mirror of
https://gitlab.com/openstapps/openstapps.git
synced 2025-12-22 22:26:17 +00:00
Compare commits
1 Commits
@openstapp
...
159-e2e-te
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1214b31cfc
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -102,6 +102,7 @@ typings/
|
||||
|
||||
# ignore lib
|
||||
lib
|
||||
dist
|
||||
|
||||
# ignore docs
|
||||
docs
|
||||
|
||||
@@ -11,6 +11,7 @@ log.txt
|
||||
*.sublime-workspace
|
||||
.vscode/*
|
||||
npm-debug.log*
|
||||
.browser-data
|
||||
|
||||
# This file is sometimes created automatically, even though
|
||||
# we actually use the capacitor.config.ts
|
||||
|
||||
@@ -21,10 +21,10 @@ as usual.
|
||||
|
||||
The modified `ion-icon` comes with a few extra features:
|
||||
|
||||
- `[fill]` controls the fill color of the icon.
|
||||
- `[weight]` controls the font weight of the icon.
|
||||
- `[size]` controls the font size of the icon.
|
||||
- `[grade]` controls the font grade of the icon.
|
||||
- `[style.--fill]` controls the fill color of the icon.
|
||||
- `[style.--weight]` controls the font weight of the icon.
|
||||
- `[style.--size]` controls the font size of the icon.
|
||||
- `[style.--grade]` controls the font grade of the icon.
|
||||
|
||||
All of these attributes are animated as described
|
||||
[here](https://developers.google.com/fonts/docs/material_symbols).
|
||||
@@ -50,11 +50,10 @@ the config file.
|
||||
Icon font minification is done automatically, but requires you to
|
||||
follow a few simple rules:
|
||||
|
||||
1. Use the tagged template literal for referencing icon names in
|
||||
TypeScript files and code
|
||||
1. Use the Proxy object to reference icon names in TypeScript files and code
|
||||
|
||||
```ts
|
||||
SCIcon`icon_name`;
|
||||
SCIcon.icon_name;
|
||||
```
|
||||
|
||||
2. When using `ion-icon` in HTML, reference either icons that went through
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
describe('favorites', function () {
|
||||
beforeEach(() => {
|
||||
cy.interceptSearch({
|
||||
extends: {query: 'test'},
|
||||
extends: {query: 'a'},
|
||||
fixture: 'search/generic',
|
||||
alias: 'search',
|
||||
});
|
||||
@@ -29,7 +29,7 @@ describe('favorites', function () {
|
||||
it('should add a favorite', function () {
|
||||
cy.visit('/search');
|
||||
cy.patchSearchPage();
|
||||
cy.get('ion-searchbar').type('test');
|
||||
cy.get('ion-searchbar').type('a');
|
||||
let text!: string;
|
||||
cy.get('stapps-data-list-item')
|
||||
.first()
|
||||
@@ -40,9 +40,7 @@ describe('favorites', function () {
|
||||
text = it;
|
||||
});
|
||||
cy.get('stapps-favorite-button').click();
|
||||
cy.get('stapps-favorite-button > ion-button > ion-icon')
|
||||
.invoke('attr', 'ng-reflect-fill')
|
||||
.should('eq', 'true');
|
||||
cy.get('stapps-favorite-button > ion-button > ion-icon').should('have.class', 'selected');
|
||||
});
|
||||
cy.visit('/favorites');
|
||||
cy.get('stapps-data-list-item').within(() => {
|
||||
|
||||
@@ -96,6 +96,7 @@
|
||||
"form-data": "4.0.0",
|
||||
"geojson": "0.5.0",
|
||||
"ionic-appauth": "0.9.0",
|
||||
"ionicons": "7.2.1",
|
||||
"jsonpath-plus": "6.0.1",
|
||||
"leaflet": "1.9.4",
|
||||
"leaflet.markercluster": "1.5.3",
|
||||
|
||||
@@ -25,7 +25,6 @@ import moment from 'moment';
|
||||
import 'moment/min/locales';
|
||||
import {LoggerModule, NGXLogger, NgxLoggerLevel} from 'ngx-logger';
|
||||
import SwiperCore, {FreeMode, Navigation} from 'swiper';
|
||||
|
||||
import {environment} from '../environments/environment';
|
||||
import {AppRoutingModule} from './app-routing.module';
|
||||
import {AppComponent} from './app.component';
|
||||
@@ -62,12 +61,12 @@ import {RoutingStackService} from './util/routing-stack.service';
|
||||
import {SCLanguageCode, SCSettingValue} from '@openstapps/core';
|
||||
import {DefaultAuthService} from './modules/auth/default-auth.service';
|
||||
import {PAIAAuthService} from './modules/auth/paia/paia-auth.service';
|
||||
import {IonIconModule} from './util/ion-icon/ion-icon.module';
|
||||
import {NavigationModule} from './modules/menu/navigation/navigation.module';
|
||||
import {browserFactory, SimpleBrowser} from './util/browser.factory';
|
||||
import {getDateFnsLocale} from './translation/dfns-locale';
|
||||
import {setDefaultOptions} from 'date-fns';
|
||||
import {DateFnsConfigurationService} from 'ngx-date-fns';
|
||||
import {SCIcon} from './util/ion-icon/icon';
|
||||
import {Capacitor} from '@capacitor/core';
|
||||
import {SplashScreen} from '@capacitor/splash-screen';
|
||||
|
||||
@@ -155,8 +154,9 @@ export function createTranslateLoader(http: HttpClient) {
|
||||
DashboardModule,
|
||||
DataModule,
|
||||
HebisModule,
|
||||
IonicModule.forRoot(),
|
||||
IonIconModule,
|
||||
IonicModule.forRoot({
|
||||
backButtonIcon: SCIcon.arrow_back,
|
||||
}),
|
||||
JobModule,
|
||||
FavoritesModule,
|
||||
LibraryModule,
|
||||
|
||||
@@ -29,7 +29,12 @@
|
||||
<ion-card-header>
|
||||
<ion-card-title>
|
||||
{{ license.name }}
|
||||
<ion-icon [size]="16" [weight]="300" class="supertext-icon" name="open_in_browser"></ion-icon>
|
||||
<ion-icon
|
||||
[size]="16"
|
||||
[style.--weight]="300"
|
||||
class="supertext-icon"
|
||||
name="open_in_browser"
|
||||
></ion-icon>
|
||||
</ion-card-title>
|
||||
@if (license.authors || license.publisher) {
|
||||
<ion-card-subtitle> {{ license.authors || license.publisher }} </ion-card-subtitle>
|
||||
|
||||
@@ -29,7 +29,6 @@ import {ScrollingModule} from '@angular/cdk/scrolling';
|
||||
import {AboutLicenseModalComponent} from './about-license-modal.component';
|
||||
import {AboutChangelogComponent} from './about-changelog.component';
|
||||
import {UtilModule} from '../../util/util.module';
|
||||
import {IonIconModule} from '../../util/ion-icon/ion-icon.module';
|
||||
|
||||
const settingsRoutes: Routes = [
|
||||
{path: 'about', component: AboutPageComponent},
|
||||
@@ -53,9 +52,8 @@ const settingsRoutes: Routes = [
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
IonIconModule,
|
||||
FormsModule,
|
||||
IonicModule.forRoot(),
|
||||
IonicModule,
|
||||
TranslateModule.forChild(),
|
||||
ThingTranslateModule.forChild(),
|
||||
RouterModule.forChild(settingsRoutes),
|
||||
|
||||
@@ -35,7 +35,6 @@ import {AssessmentsProvider} from './assessments.provider';
|
||||
import {AssessmentsSimpleDataListComponent} from './list/assessments-simple-data-list.component';
|
||||
import {ProtectedRoutes} from '../auth/protected.routes';
|
||||
import {AssessmentsTreeListComponent} from './list/assessments-tree-list.component';
|
||||
import {IonIconModule} from '../../util/ion-icon/ion-icon.module';
|
||||
import {UtilModule} from '../../util/util.module';
|
||||
|
||||
const routes: ProtectedRoutes = [
|
||||
@@ -69,7 +68,6 @@ const routes: ProtectedRoutes = [
|
||||
imports: [
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
IonIconModule,
|
||||
IonicModule,
|
||||
RouterModule.forChild(routes),
|
||||
TranslateModule,
|
||||
|
||||
@@ -67,7 +67,7 @@
|
||||
<div class="horizontal-flex">
|
||||
<ion-button fill="clear" (click)="export()">
|
||||
{{ 'share' | translate }}
|
||||
<ion-icon slot="end" md="share" ios="ios_share"></ion-icon>
|
||||
<ion-icon slot="end" name="share"></ion-icon>
|
||||
</ion-button>
|
||||
@if (isWeb) {
|
||||
<ion-button fill="outline" (click)="download()">
|
||||
|
||||
@@ -25,15 +25,13 @@ import {FormsModule} from '@angular/forms';
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {MomentModule} from 'ngx-moment';
|
||||
import {UtilModule} from '../../util/util.module';
|
||||
import {IonIconModule} from '../../util/ion-icon/ion-icon.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [AddEventReviewModalComponent],
|
||||
imports: [
|
||||
IonicModule.forRoot(),
|
||||
IonicModule,
|
||||
TranslateModule.forChild(),
|
||||
ThingTranslateModule.forChild(),
|
||||
IonIconModule,
|
||||
FormsModule,
|
||||
CommonModule,
|
||||
MomentModule,
|
||||
|
||||
@@ -23,7 +23,6 @@ import {DataModule} from '../data/data.module';
|
||||
import {SettingsProvider} from '../settings/settings.provider';
|
||||
import {CatalogComponent} from './catalog.component';
|
||||
import {UtilModule} from '../../util/util.module';
|
||||
import {IonIconModule} from '../../util/ion-icon/ion-icon.module';
|
||||
|
||||
const catalogRoutes: Routes = [
|
||||
{path: 'catalog', component: CatalogComponent},
|
||||
@@ -36,11 +35,10 @@ const catalogRoutes: Routes = [
|
||||
@NgModule({
|
||||
declarations: [CatalogComponent],
|
||||
imports: [
|
||||
IonicModule.forRoot(),
|
||||
IonicModule,
|
||||
FormsModule,
|
||||
TranslateModule.forChild(),
|
||||
RouterModule.forChild(catalogRoutes),
|
||||
IonIconModule,
|
||||
CommonModule,
|
||||
MomentModule,
|
||||
DataModule,
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
</ion-header>
|
||||
<div #schedule class="schedule">
|
||||
<a [routerLink]="['/schedule/week-overview']">
|
||||
<ion-icon [size]="36" [weight]="300" name="calendar_month"></ion-icon>
|
||||
<ion-icon [size]="36" [style.--weight]="300" name="calendar_month"></ion-icon>
|
||||
<ion-label [innerHTML]="'schedule.recurring' | translate"></ion-label>
|
||||
</a>
|
||||
<!-- Avoid structural directives here, they might interfere with the collapse animation -->
|
||||
|
||||
@@ -30,7 +30,6 @@ import {MensaSectionContentComponent} from './sections/mensa-section/mensa-secti
|
||||
import {FavoritesSectionComponent} from './sections/favorites-section/favorites-section.component';
|
||||
import {ThingTranslateModule} from '../../translation/thing-translate.module';
|
||||
import {UtilModule} from '../../util/util.module';
|
||||
import {IonIconModule} from '../../util/ion-icon/ion-icon.module';
|
||||
import {NewsModule} from '../news/news.module';
|
||||
import {JobSectionComponent} from './sections/jobs-section/job-section.component';
|
||||
import {JobModule} from '../jobs/jobs.module';
|
||||
@@ -56,8 +55,7 @@ const catalogRoutes: Routes = [
|
||||
JobSectionComponent,
|
||||
],
|
||||
imports: [
|
||||
IonicModule.forRoot(),
|
||||
IonIconModule,
|
||||
IonicModule,
|
||||
FormsModule,
|
||||
TranslateModule.forChild(),
|
||||
RouterModule.forChild(catalogRoutes),
|
||||
|
||||
@@ -60,7 +60,7 @@ export class AddEventActionChipComponent {
|
||||
/**
|
||||
* Current state of icon fill
|
||||
*/
|
||||
iconFill: boolean;
|
||||
iconFill: 1 | 0;
|
||||
|
||||
/**
|
||||
* Label
|
||||
|
||||
@@ -24,31 +24,31 @@ export enum AddEventStates {
|
||||
|
||||
export const AddEventStatesMap = {
|
||||
[AddEventStates.ADDED_ALL]: {
|
||||
icon: SCIcon`event_available`,
|
||||
fill: true,
|
||||
icon: SCIcon.event_available,
|
||||
fill: 1,
|
||||
label: 'data.chips.add_events.ADDED_ALL',
|
||||
disabled: false,
|
||||
color: 'success',
|
||||
},
|
||||
[AddEventStates.ADDED_SOME]: {
|
||||
icon: SCIcon`event`,
|
||||
fill: true,
|
||||
icon: SCIcon.event,
|
||||
fill: 1,
|
||||
label: 'data.chips.add_events.ADDED_SOME',
|
||||
disabled: false,
|
||||
color: 'success',
|
||||
},
|
||||
[AddEventStates.REMOVED_ALL]: {
|
||||
icon: SCIcon`calendar_today`,
|
||||
fill: false,
|
||||
icon: SCIcon.calendar_today,
|
||||
fill: 0,
|
||||
label: 'data.chips.add_events.REMOVED_ALL',
|
||||
disabled: false,
|
||||
color: 'primary',
|
||||
},
|
||||
[AddEventStates.UNAVAILABLE]: {
|
||||
icon: SCIcon`event_busy`,
|
||||
fill: false,
|
||||
icon: SCIcon.event_busy,
|
||||
fill: 0,
|
||||
label: 'data.chips.add_events.UNAVAILABLE',
|
||||
disabled: true,
|
||||
color: 'dark',
|
||||
},
|
||||
};
|
||||
} as const;
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
[color]="color"
|
||||
[outline]="true"
|
||||
>
|
||||
<ion-icon [name]="icon" [fill]="iconFill"></ion-icon>
|
||||
<ion-icon [name]="icon" [style.--fill]="iconFill"></ion-icon>
|
||||
<ion-label>{{ label | translate }}</ion-label>
|
||||
<stapps-edit-modal #editModal (save)="selection.save()">
|
||||
<ng-template>
|
||||
|
||||
@@ -16,35 +16,35 @@ import {SCThingType} from '@openstapps/core';
|
||||
import {SCIcon} from '../../util/ion-icon/icon';
|
||||
|
||||
export const DataIcons: Record<SCThingType, string> = {
|
||||
'academic event': SCIcon`school`,
|
||||
'assessment': SCIcon`fact_check`,
|
||||
'article': SCIcon`article`,
|
||||
'book': SCIcon`book`,
|
||||
'building': SCIcon`location_city`,
|
||||
'certification': SCIcon`contract`,
|
||||
'catalog': SCIcon`inventory_2`,
|
||||
'contact point': SCIcon`contact_page`,
|
||||
'course of study': SCIcon`school`,
|
||||
'date series': SCIcon`event`,
|
||||
'dish': SCIcon`lunch_dining`,
|
||||
'favorite': SCIcon`favorite`,
|
||||
'floor': SCIcon`foundation`,
|
||||
'id card': SCIcon`badge`,
|
||||
'message': SCIcon`newspaper`,
|
||||
'organization': SCIcon`business_center`,
|
||||
'periodical': SCIcon`feed`,
|
||||
'person': SCIcon`person`,
|
||||
'point of interest': SCIcon`pin_drop`,
|
||||
'publication event': SCIcon`campaign`,
|
||||
'room': SCIcon`meeting_room`,
|
||||
'semester': SCIcon`date_range`,
|
||||
'setting': SCIcon`settings`,
|
||||
'sport course': SCIcon`sports_soccer`,
|
||||
'study module': SCIcon`view_module`,
|
||||
'ticket': SCIcon`confirmation_number`,
|
||||
'todo': SCIcon`task`,
|
||||
'tour': SCIcon`tour`,
|
||||
'video': SCIcon`movie`,
|
||||
'diff': SCIcon`difference`,
|
||||
'job posting': SCIcon`work`,
|
||||
'academic event': SCIcon.school,
|
||||
'assessment': SCIcon.fact_check,
|
||||
'article': SCIcon.article,
|
||||
'book': SCIcon.book,
|
||||
'building': SCIcon.location_city,
|
||||
'certification': SCIcon.contract,
|
||||
'catalog': SCIcon.inventory_2,
|
||||
'contact point': SCIcon.contact_page,
|
||||
'course of study': SCIcon.school,
|
||||
'date series': SCIcon.event,
|
||||
'dish': SCIcon.lunch_dining,
|
||||
'favorite': SCIcon.favorite,
|
||||
'floor': SCIcon.foundation,
|
||||
'id card': SCIcon.badge,
|
||||
'message': SCIcon.newspaper,
|
||||
'organization': SCIcon.business_center,
|
||||
'periodical': SCIcon.feed,
|
||||
'person': SCIcon.person,
|
||||
'point of interest': SCIcon.pin_drop,
|
||||
'publication event': SCIcon.campaign,
|
||||
'room': SCIcon.meeting_room,
|
||||
'semester': SCIcon.date_range,
|
||||
'setting': SCIcon.settings,
|
||||
'sport course': SCIcon.sports_soccer,
|
||||
'study module': SCIcon.view_module,
|
||||
'ticket': SCIcon.confirmation_number,
|
||||
'todo': SCIcon.task,
|
||||
'tour': SCIcon.tour,
|
||||
'video': SCIcon.movie,
|
||||
'diff': SCIcon.difference,
|
||||
'job posting': SCIcon.work,
|
||||
};
|
||||
|
||||
@@ -24,7 +24,6 @@ import {MarkdownModule} from 'ngx-markdown';
|
||||
import {MomentModule} from 'ngx-moment';
|
||||
import {ThingTranslateModule} from '../../translation/thing-translate.module';
|
||||
import {SimpleBrowser, browserFactory} from '../../util/browser.factory';
|
||||
import {IonIconModule} from '../../util/ion-icon/ion-icon.module';
|
||||
import {RoutingStackService} from '../../util/routing-stack.service';
|
||||
import {UtilModule} from '../../util/util.module';
|
||||
import {CalendarService} from '../calendar/calendar.service';
|
||||
@@ -188,11 +187,10 @@ import {ShareButtonComponent} from './elements/share-button.component';
|
||||
DataRoutingModule,
|
||||
FormsModule,
|
||||
HttpClientModule,
|
||||
IonicModule.forRoot(),
|
||||
IonicModule,
|
||||
LeafletModule,
|
||||
MarkdownModule.forRoot(),
|
||||
MenuModule,
|
||||
IonIconModule,
|
||||
MomentModule.forRoot({
|
||||
relativeTimeThresholdOptions: {
|
||||
m: 59,
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<ion-button (click)="toggle($event)" color="medium" size="small" fill="clear">
|
||||
<ion-icon
|
||||
slot="icon-only"
|
||||
[fill]="(isFavorite$ | async) ?? false"
|
||||
[style.--fill]="(isFavorite$ | async) ? 1 : 0"
|
||||
[class.selected]="isFavorite$ | async"
|
||||
name="grade"
|
||||
></ion-icon>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<ion-button size="small" fill="clear" (click)="share() && toast.present()">
|
||||
<ion-icon [size]="24" slot="icon-only" name="share" ios="ios_share"></ion-icon>
|
||||
<ion-icon [size]="24" slot="icon-only" name="share"></ion-icon>
|
||||
</ion-button>
|
||||
<ion-toast [message]="'toast.TITLE_COPIED' | translate" #toast [duration]="2000"></ion-toast>
|
||||
|
||||
@@ -16,10 +16,11 @@
|
||||
import {Component, ElementRef, HostListener, Input, OnChanges, OnInit, ViewChild} from '@angular/core';
|
||||
import {SCThings} from '@openstapps/core';
|
||||
import {SCIcon} from '../../../util/ion-icon/icon';
|
||||
import {MaterialSymbol} from 'material-symbols';
|
||||
|
||||
const AccordionButtonState = {
|
||||
collapsed: SCIcon`expand_more`,
|
||||
expanded: SCIcon`expand_less`,
|
||||
collapsed: SCIcon.expand_more,
|
||||
expanded: SCIcon.expand_less,
|
||||
};
|
||||
|
||||
@Component({
|
||||
@@ -35,7 +36,7 @@ export class TitleCardComponent implements OnInit, OnChanges {
|
||||
|
||||
@ViewChild('accordionTextArea') accordionTextArea: ElementRef;
|
||||
|
||||
buttonState = AccordionButtonState.collapsed;
|
||||
buttonState: MaterialSymbol = AccordionButtonState.collapsed;
|
||||
|
||||
buttonShown = true;
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
-->
|
||||
@if (isInCalendar | async) {
|
||||
<ion-chip outline="true" color="success" (click)="removeFromCalendar()">
|
||||
<ion-icon name="event_available" [fill]="true"></ion-icon>
|
||||
<ion-icon name="event_available" [style.--fill]="1"></ion-icon>
|
||||
<ion-label>{{ 'chips.addEvent.addedToEvents' | translate }}</ion-label>
|
||||
</ion-chip>
|
||||
} @else {
|
||||
|
||||
@@ -22,7 +22,6 @@ import {MenuModule} from '../menu/menu.module';
|
||||
import {TranslateModule} from '@ngx-translate/core';
|
||||
import {DataModule} from '../data/data.module';
|
||||
import {UtilModule} from '../../util/util.module';
|
||||
import {IonIconModule} from '../../util/ion-icon/ion-icon.module';
|
||||
|
||||
const favoritesRoutes: Routes = [
|
||||
{
|
||||
@@ -40,7 +39,6 @@ const favoritesRoutes: Routes = [
|
||||
MenuModule,
|
||||
TranslateModule,
|
||||
DataModule,
|
||||
IonIconModule,
|
||||
UtilModule,
|
||||
],
|
||||
declarations: [FavoritesPageComponent],
|
||||
|
||||
@@ -21,7 +21,6 @@ import {RouterModule, Routes} from '@angular/router';
|
||||
import {TranslateModule} from '@ngx-translate/core';
|
||||
import {MarkdownModule} from 'ngx-markdown';
|
||||
import {UtilModule} from '../../util/util.module';
|
||||
import {IonIconModule} from '../../util/ion-icon/ion-icon.module';
|
||||
|
||||
const feedbackRoutes: Routes = [
|
||||
{
|
||||
@@ -35,7 +34,6 @@ const feedbackRoutes: Routes = [
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
IonicModule,
|
||||
IonIconModule,
|
||||
RouterModule.forChild(feedbackRoutes),
|
||||
TranslateModule,
|
||||
MarkdownModule,
|
||||
|
||||
@@ -27,7 +27,6 @@ import {HebisDetailComponent} from './hebis-detail.component';
|
||||
import {Observable, of} from 'rxjs';
|
||||
import {StorageProvider} from '../../storage/storage.provider';
|
||||
import {IonicModule} from '@ionic/angular';
|
||||
import {IonIconModule} from '../../../util/ion-icon/ion-icon.module';
|
||||
import {LoggerModule, NgxLoggerLevel} from 'ngx-logger';
|
||||
|
||||
const translations: any = {data: {detail: {TITLE: 'Foo'}}};
|
||||
@@ -69,7 +68,6 @@ describe('HebisDetailComponent', () => {
|
||||
HebisRoutingModule,
|
||||
HebisModule,
|
||||
IonicModule,
|
||||
IonIconModule,
|
||||
TranslateModule.forRoot({
|
||||
loader: {provide: TranslateLoader, useClass: TranslateFakeLoader},
|
||||
}),
|
||||
|
||||
@@ -34,7 +34,6 @@ import {HebisRoutingModule} from './hebis-routing.module';
|
||||
import {DataModule} from '../data/data.module';
|
||||
import {DaiaAvailabilityComponent} from './daia-availability/daia-availability.component';
|
||||
import {UtilModule} from '../../util/util.module';
|
||||
import {IonIconModule} from '../../util/ion-icon/ion-icon.module';
|
||||
import {DaiaHoldingComponent} from './daia-availability/daia-holding.component';
|
||||
|
||||
/**
|
||||
@@ -53,9 +52,8 @@ import {DaiaHoldingComponent} from './daia-availability/daia-holding.component';
|
||||
DataModule,
|
||||
FormsModule,
|
||||
HebisRoutingModule,
|
||||
IonIconModule,
|
||||
HttpClientModule,
|
||||
IonicModule.forRoot(),
|
||||
IonicModule,
|
||||
MarkdownModule.forRoot(),
|
||||
MenuModule,
|
||||
MomentModule.forRoot({
|
||||
|
||||
@@ -5,7 +5,6 @@ import {TranslateModule} from '@ngx-translate/core';
|
||||
import {MomentModule} from 'ngx-moment';
|
||||
import {DataModule} from '../data/data.module';
|
||||
import {UtilModule} from '../../util/util.module';
|
||||
import {IonIconModule} from '../../util/ion-icon/ion-icon.module';
|
||||
import {ConfigProvider} from '../config/config.provider';
|
||||
import {ThingTranslateModule} from '../../translation/thing-translate.module';
|
||||
import {RouterModule, Routes} from '@angular/router';
|
||||
@@ -16,11 +15,10 @@ const jobsRoutes: Routes = [{path: 'jobs', component: JobsPageComponent}];
|
||||
@NgModule({
|
||||
declarations: [JobsPageComponent],
|
||||
imports: [
|
||||
IonicModule.forRoot(),
|
||||
IonicModule,
|
||||
ThingTranslateModule.forChild(),
|
||||
TranslateModule.forChild(),
|
||||
RouterModule.forChild(jobsRoutes),
|
||||
IonIconModule,
|
||||
CommonModule,
|
||||
MomentModule,
|
||||
DataModule,
|
||||
|
||||
@@ -32,7 +32,6 @@ import {MomentModule} from 'ngx-moment';
|
||||
import {FeeItemComponent} from './account/elements/fee-item/fee-item.component';
|
||||
import {DataModule} from '../data/data.module';
|
||||
import {UtilModule} from '../../util/util.module';
|
||||
import {IonIconModule} from '../../util/ion-icon/ion-icon.module';
|
||||
|
||||
const routes: ProtectedRoutes | Routes = [
|
||||
{
|
||||
@@ -72,7 +71,6 @@ const routes: ProtectedRoutes | Routes = [
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
IonicModule,
|
||||
IonIconModule,
|
||||
RouterModule.forChild(routes),
|
||||
TranslateModule,
|
||||
MomentModule,
|
||||
|
||||
@@ -32,7 +32,6 @@ import {MapPageComponent} from './page/map-page.component';
|
||||
import {MapListModalComponent} from './page/map-list-modal.component';
|
||||
import {NgModule} from '@angular/core';
|
||||
import {UtilModule} from '../../util/util.module';
|
||||
import {IonIconModule} from '../../util/ion-icon/ion-icon.module';
|
||||
import {GeoNavigationDirective} from './geo-navigation.directive';
|
||||
|
||||
/**
|
||||
@@ -59,9 +58,8 @@ const mapRoutes: Routes = [
|
||||
exports: [],
|
||||
imports: [
|
||||
CommonModule,
|
||||
IonicModule.forRoot(),
|
||||
IonicModule,
|
||||
LeafletModule,
|
||||
IonIconModule,
|
||||
LeafletMarkerClusterModule,
|
||||
RouterModule.forChild(mapRoutes),
|
||||
TranslateModule.forChild(),
|
||||
|
||||
@@ -52,10 +52,10 @@ export class MapProvider {
|
||||
icon: divIcon({
|
||||
className: className,
|
||||
html: `<span
|
||||
name="${SCIcon`location_on`}"
|
||||
name="${SCIcon.location_on}"
|
||||
class="material-symbols-rounded map-location-pin"
|
||||
style="font-size: ${iconSize}px;"
|
||||
>${SCIcon`location_on`}</span>`,
|
||||
>${SCIcon.location_on}</span>`,
|
||||
iconSize: [iconSize, iconSize],
|
||||
iconAnchor: [iconSize / 2, iconSize],
|
||||
}),
|
||||
@@ -75,10 +75,10 @@ export class MapProvider {
|
||||
html:
|
||||
position.heading === undefined
|
||||
? `<span
|
||||
name="${SCIcon`person_pin_circle`}"
|
||||
name="${SCIcon.person_pin_circle}"
|
||||
class="material-symbols-rounded map-location-pin"
|
||||
style="font-size: ${iconSize}px; color: var(--ion-color-primary);"
|
||||
>${SCIcon`person_pin_circle`}</span>`
|
||||
>${SCIcon.person_pin_circle}</span>`
|
||||
: `<span
|
||||
class="material-symbols-rounded map-location-pin"
|
||||
style="
|
||||
@@ -87,7 +87,7 @@ export class MapProvider {
|
||||
font-size: ${iconSize}px;
|
||||
color: var(--ion-color-primary);
|
||||
"
|
||||
>${SCIcon`navigation`}</span>`,
|
||||
>${SCIcon.navigation}</span>`,
|
||||
iconSize: [iconSize, iconSize],
|
||||
}),
|
||||
zIndexOffset: 1000,
|
||||
|
||||
@@ -52,7 +52,7 @@ describe('ContextMenuComponent', async () => {
|
||||
],
|
||||
imports: [
|
||||
FormsModule,
|
||||
IonicModule.forRoot(),
|
||||
IonicModule,
|
||||
TranslateModule.forRoot(),
|
||||
CommonModule,
|
||||
SettingsModule,
|
||||
|
||||
@@ -22,7 +22,6 @@ import {TranslateModule} from '@ngx-translate/core';
|
||||
import {SettingsModule} from '../settings/settings.module';
|
||||
import {ContextMenuComponent} from './context/context-menu.component';
|
||||
import {ContextMenuService} from './context/context-menu.service';
|
||||
import {IonIconModule} from '../../util/ion-icon/ion-icon.module';
|
||||
|
||||
/**
|
||||
* Menu module
|
||||
@@ -32,9 +31,8 @@ import {IonIconModule} from '../../util/ion-icon/ion-icon.module';
|
||||
exports: [ContextMenuComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
IonIconModule,
|
||||
FormsModule,
|
||||
IonicModule.forRoot(),
|
||||
IonicModule,
|
||||
RouterModule,
|
||||
SettingsModule,
|
||||
TranslateModule.forChild(),
|
||||
|
||||
@@ -18,14 +18,13 @@ import {NavigationComponent} from './navigation.component';
|
||||
import {TabsComponent} from './tabs.component';
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {IonicModule} from '@ionic/angular';
|
||||
import {IonIconModule} from '../../../util/ion-icon/ion-icon.module';
|
||||
import {TranslateModule} from '@ngx-translate/core';
|
||||
import {RouterModule} from '@angular/router';
|
||||
import {OfflineNoticeComponent} from './offline-notice.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [RootLinkDirective, NavigationComponent, TabsComponent, OfflineNoticeComponent],
|
||||
imports: [CommonModule, IonicModule, IonIconModule, TranslateModule, RouterModule],
|
||||
imports: [CommonModule, IonicModule, TranslateModule, RouterModule],
|
||||
exports: [TabsComponent, RootLinkDirective, NavigationComponent],
|
||||
})
|
||||
export class NavigationModule {}
|
||||
|
||||
@@ -77,9 +77,7 @@ ion-router-outlet {
|
||||
}
|
||||
|
||||
.link-active > * {
|
||||
color: var(--ion-color-primary);
|
||||
--fill: 1;
|
||||
|
||||
::ng-deep stapps-icon {
|
||||
--fill: 1;
|
||||
}
|
||||
color: var(--ion-color-primary);
|
||||
}
|
||||
|
||||
@@ -13,13 +13,13 @@
|
||||
~ this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<ion-button class="offline-button" color="warning">
|
||||
<ion-icon slot="start" [size]="16" [weight]="800" name="cloud_off"></ion-icon>
|
||||
<ion-icon slot="start" [size]="16" [style.--weight]="800" name="cloud_off"></ion-icon>
|
||||
<ion-label>{{ 'app.errors.OFFLINE' | translate }}</ion-label>
|
||||
</ion-button>
|
||||
<ion-button class="error-button" color="danger" (click)="retry()">
|
||||
<ion-icon #spinIcon slot="start" [size]="16" [weight]="800" name="refresh"></ion-icon>
|
||||
<ion-icon #spinIcon slot="start" [size]="16" [style.--weight]="800" name="refresh"></ion-icon>
|
||||
<ion-label>{{ 'app.errors.CONNECTION_ERROR' | translate }}</ion-label>
|
||||
</ion-button>
|
||||
<ion-button class="close" fill="clear" color="light" (click)="offlineProvider.dismissError()"
|
||||
><ion-icon [size]="16" [weight]="800" name="close" slot="icon-only"></ion-icon
|
||||
><ion-icon [size]="16" [style.--weight]="800" name="close" slot="icon-only"></ion-icon
|
||||
></ion-button>
|
||||
|
||||
@@ -60,6 +60,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.tab-selected ::ng-deep stapps-icon {
|
||||
.tab-selected ion-icon {
|
||||
--fill: 1;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
@if (displayValue) {
|
||||
<ion-chip [class.active]="active" (click)="emitToggle(value)">
|
||||
@if (active) {
|
||||
<ion-icon class="ion-color" name="check_circle" [fill]="true"></ion-icon>
|
||||
<ion-icon class="ion-color" name="check_circle" [style.--fill]="1"></ion-icon>
|
||||
}
|
||||
<ion-label>{{ displayValue }}</ion-label>
|
||||
</ion-chip>
|
||||
@@ -24,11 +24,10 @@ import {SettingsProvider} from '../settings/settings.provider';
|
||||
import {NewsItemComponent} from './item/news-item.component';
|
||||
import {NewsPageComponent} from './page/news-page.component';
|
||||
import {SkeletonNewsItemComponent} from './item/skeleton-news-item.component';
|
||||
import {ChipFilterComponent} from '../data/chips/filter/chip-filter.component';
|
||||
import {ChipFilterComponent} from './elements/news-filter-settings/chip-filter.component';
|
||||
import {SettingsModule} from '../settings/settings.module';
|
||||
import {NewsSettingsFilterComponent} from './elements/news-filter-settings/news-settings-filter.component';
|
||||
import {UtilModule} from '../../util/util.module';
|
||||
import {IonIconModule} from '../../util/ion-icon/ion-icon.module';
|
||||
|
||||
const newsRoutes: Routes = [{path: 'news', component: NewsPageComponent}];
|
||||
|
||||
@@ -44,11 +43,10 @@ const newsRoutes: Routes = [{path: 'news', component: NewsPageComponent}];
|
||||
NewsSettingsFilterComponent,
|
||||
],
|
||||
imports: [
|
||||
IonicModule.forRoot(),
|
||||
IonicModule,
|
||||
ThingTranslateModule.forChild(),
|
||||
TranslateModule.forChild(),
|
||||
RouterModule.forChild(newsRoutes),
|
||||
IonIconModule,
|
||||
CommonModule,
|
||||
MomentModule,
|
||||
DataModule,
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<div>
|
||||
<div class="log-in">
|
||||
{{ 'profile.userInfo.logInPrompt' | translate | sentencecase }}
|
||||
<ion-icon name="person" [fill]="true"></ion-icon>
|
||||
<ion-icon name="person" [style.--fill]="1"></ion-icon>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ import {ProfilePageComponent} from './page/profile-page.component';
|
||||
import {TranslateModule} from '@ngx-translate/core';
|
||||
import {SwiperModule} from 'swiper/angular';
|
||||
import {UtilModule} from '../../util/util.module';
|
||||
import {IonIconModule} from '../../util/ion-icon/ion-icon.module';
|
||||
import {ProfilePageSectionComponent} from './page/profile-page-section.component';
|
||||
import {ThingTranslateModule} from '../../translation/thing-translate.module';
|
||||
import {DataModule} from '../data/data.module';
|
||||
@@ -41,7 +40,6 @@ const routes: Routes = [
|
||||
imports: [
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
IonIconModule,
|
||||
IonicModule,
|
||||
RouterModule.forChild(routes),
|
||||
TranslateModule,
|
||||
|
||||
@@ -35,7 +35,6 @@ import {ScheduleDayComponent} from './page/grid/schedule-day.component';
|
||||
import {ThingTranslateModule} from '../../translation/thing-translate.module';
|
||||
import {InfiniteSwiperComponent} from './page/grid/infinite-swiper.component';
|
||||
import {CalendarComponent} from './page/components/calendar.component';
|
||||
import {IonIconModule} from '../../util/ion-icon/ion-icon.module';
|
||||
import {ChooseEventsPageComponent} from './page/choose-events-page.component';
|
||||
|
||||
const settingsRoutes: Routes = [
|
||||
@@ -69,8 +68,7 @@ const settingsRoutes: Routes = [
|
||||
CommonModule,
|
||||
DataModule,
|
||||
FormsModule,
|
||||
IonicModule.forRoot(),
|
||||
IonIconModule,
|
||||
IonicModule,
|
||||
MomentModule,
|
||||
RouterModule.forChild(settingsRoutes),
|
||||
SwiperModule,
|
||||
|
||||
@@ -33,7 +33,6 @@ import {CalendarService} from '../calendar/calendar.service';
|
||||
import {CalendarModule} from '../calendar/calendar.module';
|
||||
import {BackgroundModule} from '../background/background.module';
|
||||
import {UtilModule} from '../../util/util.module';
|
||||
import {IonIconModule} from '../../util/ion-icon/ion-icon.module';
|
||||
|
||||
const settingsRoutes: Routes = [{path: 'settings', component: SettingsPageComponent}];
|
||||
|
||||
@@ -52,9 +51,8 @@ const settingsRoutes: Routes = [{path: 'settings', component: SettingsPageCompon
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
CalendarModule,
|
||||
IonIconModule,
|
||||
BackgroundModule,
|
||||
IonicModule.forRoot(),
|
||||
IonicModule,
|
||||
TranslateModule.forChild(),
|
||||
ThingTranslateModule.forChild(),
|
||||
RouterModule.forChild(settingsRoutes),
|
||||
|
||||
@@ -29,7 +29,6 @@ import {
|
||||
import {ThingTranslateDefaultParser, ThingTranslateParser} from './thing-translate.parser';
|
||||
import {ThingTranslatePipe} from './thing-translate.pipe';
|
||||
import {ThingTranslateService} from './thing-translate.service';
|
||||
import {IonIconModule} from '../util/ion-icon/ion-icon.module';
|
||||
import {TranslateSimplePipe} from './translate-simple.pipe';
|
||||
import {PropertyNameTranslatePipe} from './property-name-translate.pipe';
|
||||
|
||||
@@ -38,7 +37,6 @@ export interface ThingTranslateModuleConfig {
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
imports: [IonIconModule],
|
||||
declarations: [
|
||||
ArrayJoinPipe,
|
||||
DurationLocalizedPipe,
|
||||
@@ -56,7 +54,6 @@ export interface ThingTranslateModuleConfig {
|
||||
IsNumericPipe,
|
||||
],
|
||||
exports: [
|
||||
IonIconModule,
|
||||
ArrayJoinPipe,
|
||||
DurationLocalizedPipe,
|
||||
NumberLocalizedPipe,
|
||||
|
||||
@@ -61,7 +61,6 @@ export class InternetConnectionService {
|
||||
private doRetry(error: unknown, retryCount: number): ObservableInput<unknown> {
|
||||
return race(
|
||||
this.offline$.pipe(
|
||||
tap(it => console.log(it)),
|
||||
filter(it => !it),
|
||||
take(1),
|
||||
delay(Math.min(retryCount ** 4 + 100, 10_000)),
|
||||
|
||||
5
frontend/app/src/app/util/ion-icon/icon-match.d.mts
Normal file
5
frontend/app/src/app/util/ion-icon/icon-match.d.mts
Normal file
@@ -0,0 +1,5 @@
|
||||
export function matchTagProperties(tag: string): RegExp;
|
||||
|
||||
export function matchPropertyContent(properties: string[]): RegExp;
|
||||
|
||||
export function matchPropertyAccess(objectName: string): RegExp;
|
||||
@@ -14,17 +14,24 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} tag
|
||||
*/
|
||||
export function matchTagProperties(tag: string) {
|
||||
export function matchTagProperties(tag) {
|
||||
return new RegExp(`(?<=<${tag})[\\s\\S]*?(?=>\\s*<\\/${tag}\\s*>)`, 'g');
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string[]} properties
|
||||
*/
|
||||
export function matchPropertyContent(properties: string[]) {
|
||||
export function matchPropertyContent(properties) {
|
||||
const names = properties.join('|');
|
||||
|
||||
return new RegExp(`((?<=(${names})=")[\\w-]+(?="))|((?<=\\[(${names})]="')[\\w-]+(?='"))`, 'g');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} objectName
|
||||
*/
|
||||
export function matchPropertyAccess(objectName) {
|
||||
return new RegExp(`(?<=${objectName}\\s*\\.\\s*)\\w+`, 'g');
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
*/
|
||||
/* eslint-disable unicorn/no-null */
|
||||
|
||||
import {matchPropertyContent, matchTagProperties} from './icon-match';
|
||||
import {matchPropertyContent, matchTagProperties, matchPropertyAccess} from './icon-match.mjs';
|
||||
|
||||
describe('matchTagProperties', function () {
|
||||
const regex = matchTagProperties('test');
|
||||
@@ -59,3 +59,30 @@ describe('matchPropertyContent', function () {
|
||||
expect(`no="content" [no]="'content'"`.match(regex)).toEqual(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('matchPropertyAccess', function () {
|
||||
const property = '0_20a_boninAo0_';
|
||||
const object = 'test';
|
||||
const regex = matchPropertyAccess(object);
|
||||
|
||||
it('should match property access', function () {
|
||||
expect(`${object}.${property}`.match(regex)).toEqual([property]);
|
||||
});
|
||||
|
||||
it('should respect whitespace', function () {
|
||||
expect(`${object}. ${property}`.match(regex)).toEqual([property]);
|
||||
expect(`${object} .${property}`.match(regex)).toEqual([property]);
|
||||
expect(`${object} . ${property}`.match(regex)).toEqual([property]);
|
||||
expect(`${object} \n . \n ${property}`.match(regex)).toEqual([property]);
|
||||
});
|
||||
|
||||
it('should not include invalid trailing stuff', function () {
|
||||
expect(`${object}.${property}!`.match(regex)).toEqual([property]);
|
||||
expect(`${object}.${property}.`.match(regex)).toEqual([property]);
|
||||
expect(`${object}.${property}-`.match(regex)).toEqual([property]);
|
||||
expect(`${object}.${property}]`.match(regex)).toEqual([property]);
|
||||
expect(`${object}.${property}}`.match(regex)).toEqual([property]);
|
||||
expect(`${object}.${property};`.match(regex)).toEqual([property]);
|
||||
expect(`${object}.${property}:`.match(regex)).toEqual([property]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {ChangeDetectionStrategy, Component, HostBinding, Input} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'stapps-icon',
|
||||
templateUrl: 'icon.html',
|
||||
styleUrls: ['icon.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class IconComponent {
|
||||
@HostBinding('style.--size')
|
||||
@Input()
|
||||
size?: number;
|
||||
|
||||
@HostBinding('style.--weight')
|
||||
@Input()
|
||||
weight?: number;
|
||||
|
||||
@HostBinding('style.--grade')
|
||||
@Input()
|
||||
grade?: number;
|
||||
|
||||
@Input()
|
||||
fill: boolean;
|
||||
|
||||
@HostBinding('innerHtml')
|
||||
@Input()
|
||||
name: string;
|
||||
|
||||
@HostBinding('style.--fill') get fillStyle(): number | undefined {
|
||||
return this.fill ? 1 : undefined;
|
||||
}
|
||||
|
||||
@HostBinding('class.material-symbols-rounded') hostClass = true;
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
<!--
|
||||
~ Copyright (C) 2022 StApps
|
||||
~ This program is free software: you can redistribute it and/or modify it
|
||||
~ under the terms of the GNU General Public License as published by the Free
|
||||
~ Software Foundation, version 3.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
~ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
~ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
~ more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License along with
|
||||
~ this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<ng-content></ng-content>
|
||||
@@ -1,31 +0,0 @@
|
||||
/*!
|
||||
* Copyright (C) 2022 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
:host {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
font-size: inherit;
|
||||
font-variation-settings:
|
||||
'wght' var(--weight),
|
||||
'GRAD' var(--grade),
|
||||
'FILL' var(--fill);
|
||||
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
@@ -12,10 +12,8 @@
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import {MaterialSymbol} from 'material-symbols';
|
||||
|
||||
/**
|
||||
* A noop function to aid parsing icon names
|
||||
*/
|
||||
export function SCIcon(strings: TemplateStringsArray, ..._keys: string[]): string {
|
||||
return strings.join('');
|
||||
}
|
||||
export const SCIcon = new Proxy({} as {[key in MaterialSymbol]: key}, {
|
||||
get: (_target, property: string) => property as MaterialSymbol,
|
||||
});
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import {
|
||||
DestroyRef,
|
||||
Directive,
|
||||
ElementRef,
|
||||
Host,
|
||||
inject,
|
||||
OnInit,
|
||||
Optional,
|
||||
Self,
|
||||
ViewContainerRef,
|
||||
} from '@angular/core';
|
||||
import {SCIcon} from './icon';
|
||||
import {IconReplacer} from './replace-util';
|
||||
import {TranslateService} from '@ngx-translate/core';
|
||||
import {IonBackButton} from '@ionic/angular';
|
||||
import {TitleCasePipe} from '@angular/common';
|
||||
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
|
||||
|
||||
@Directive({
|
||||
selector: 'ion-back-button',
|
||||
})
|
||||
export class IonBackButtonDirective extends IconReplacer implements OnInit {
|
||||
destroy$ = inject(DestroyRef);
|
||||
|
||||
constructor(
|
||||
element: ElementRef,
|
||||
viewContainerRef: ViewContainerRef,
|
||||
@Host() @Self() @Optional() private ionBackButton: IonBackButton,
|
||||
private translateService: TranslateService,
|
||||
private titleCasePipe: TitleCasePipe,
|
||||
) {
|
||||
super(element, viewContainerRef, 'shadow');
|
||||
}
|
||||
|
||||
replace() {
|
||||
this.replaceIcon(this.host.querySelector('.button-inner'), {
|
||||
md: SCIcon`arrow_back`,
|
||||
ios: SCIcon`arrow_back_ios`,
|
||||
size: 24,
|
||||
});
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
await super.ngOnInit();
|
||||
this.translateService
|
||||
.stream('back')
|
||||
.pipe(takeUntilDestroyed(this.destroy$))
|
||||
.subscribe((value: string) => {
|
||||
this.ionBackButton.text = this.titleCasePipe.transform(value);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2022 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import {Directive, ElementRef, ViewContainerRef} from '@angular/core';
|
||||
import {SCIcon} from './icon';
|
||||
import {IconReplacer} from './replace-util';
|
||||
|
||||
@Directive({
|
||||
selector: 'ion-breadcrumb',
|
||||
})
|
||||
export class IonBreadcrumbDirective extends IconReplacer {
|
||||
constructor(element: ElementRef, viewContainerRef: ViewContainerRef) {
|
||||
super(element, viewContainerRef, 'shadow');
|
||||
}
|
||||
|
||||
replace() {
|
||||
this.replaceIcon(
|
||||
this.host.querySelector('span[part="separator"]'),
|
||||
{
|
||||
name: SCIcon`arrow_forward_ios`,
|
||||
size: 16,
|
||||
style: `color: var(--ion-color-tint);`,
|
||||
},
|
||||
'-separator',
|
||||
);
|
||||
this.replaceIcon(
|
||||
this.host.querySelector('button[part="collapsed-indicator"]'),
|
||||
{
|
||||
name: SCIcon`more_horiz`,
|
||||
size: 24,
|
||||
},
|
||||
'-collapsed',
|
||||
);
|
||||
}
|
||||
}
|
||||
126
frontend/app/src/app/util/ion-icon/ion-icon-custom-element.ts
Normal file
126
frontend/app/src/app/util/ion-icon/ion-icon-custom-element.ts
Normal file
@@ -0,0 +1,126 @@
|
||||
import {MaterialSymbol} from 'material-symbols';
|
||||
import {SCIcon} from './icon';
|
||||
import {
|
||||
arrowBackOutline,
|
||||
caretDownSharp,
|
||||
chevronBackOutline,
|
||||
chevronExpand,
|
||||
chevronForwardOutline,
|
||||
close,
|
||||
closeCircle,
|
||||
closeSharp,
|
||||
ellipsisHorizontal,
|
||||
menu,
|
||||
menuSharp,
|
||||
searchOutline,
|
||||
searchSharp,
|
||||
} from 'ionicons/icons';
|
||||
|
||||
const styles = new CSSStyleSheet();
|
||||
styles.replaceSync(/* css */ `
|
||||
:host {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
|
||||
font-size: inherit;
|
||||
font-variation-settings: 'wght' var(--weight, 400), 'GRAD' var(--grade, 1), 'FILL' var(--fill, 0);
|
||||
|
||||
transition: font-variation-settings 0.2s ease-in-out;
|
||||
|
||||
direction: ltr;
|
||||
font-family: 'Material Symbols Rounded';
|
||||
font-feature-settings: 'liga';
|
||||
font-style: normal;
|
||||
line-height: 1;
|
||||
text-transform: none;
|
||||
text-rendering: optimizelegibility;
|
||||
letter-spacing: normal;
|
||||
word-wrap: normal;
|
||||
white-space: nowrap;
|
||||
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
`);
|
||||
|
||||
export class IonIconCustomElement extends HTMLElement {
|
||||
static observedAttributes = ['name', 'size', 'color'];
|
||||
|
||||
set name(value: MaterialSymbol) {
|
||||
this.shadowRoot!.textContent =
|
||||
document.querySelector('ion-app.ios') && iosAlias.has(value) ? iosAlias.get(value)! : value;
|
||||
}
|
||||
|
||||
set icon(value: string) {
|
||||
if (iconMap.has(value)) {
|
||||
this.name = iconMap.get(value)!;
|
||||
} else {
|
||||
this.style.setProperty('color', 'red');
|
||||
// @ts-expect-error not assignable
|
||||
this.name = '??';
|
||||
console.error(
|
||||
`The icon "${value}" is missing from the icon map (did Ionic change something?)`,
|
||||
'\n\n',
|
||||
new Error('...')!.stack,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
set size(value: string) {
|
||||
switch (value) {
|
||||
case 'small': {
|
||||
this.style.setProperty('font-size', '1.125rem');
|
||||
break;
|
||||
}
|
||||
case 'large': {
|
||||
this.style.setProperty('font-size', '2rem');
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
this.style.setProperty('font-size', Number.isNaN(Number(value)) ? value : `${value}px`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
set color(value: string) {
|
||||
this.style.setProperty('color', `var(--ion-color-${value}`);
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
const shadow = this.attachShadow({mode: 'open'});
|
||||
shadow.adoptedStyleSheets = [styles];
|
||||
}
|
||||
}
|
||||
|
||||
const iosAlias = new Map<MaterialSymbol, MaterialSymbol>([
|
||||
[SCIcon.arrow_back, SCIcon.arrow_back_ios],
|
||||
[SCIcon.arrow_forward, SCIcon.arrow_forward_ios],
|
||||
[SCIcon.share, SCIcon.ios_share],
|
||||
]);
|
||||
|
||||
/**
|
||||
* Maps hardcoded icon SVGs to our icon names.
|
||||
* Thanks, Ionic.
|
||||
*/
|
||||
const iconMap = new Map<string, MaterialSymbol>([
|
||||
[arrowBackOutline, SCIcon.arrow_back],
|
||||
[chevronBackOutline, SCIcon.arrow_back],
|
||||
[chevronForwardOutline, SCIcon.arrow_forward],
|
||||
[menu, SCIcon.menu],
|
||||
[menuSharp, SCIcon.menu],
|
||||
[searchOutline, SCIcon.search],
|
||||
[searchSharp, SCIcon.search],
|
||||
[chevronExpand, SCIcon.expand_more],
|
||||
[caretDownSharp, SCIcon.expand_more],
|
||||
[close, SCIcon.close],
|
||||
[closeSharp, SCIcon.close],
|
||||
[closeCircle, SCIcon.cancel],
|
||||
[ellipsisHorizontal, SCIcon.more_horiz],
|
||||
['chevron-down-outline', SCIcon.expand_more],
|
||||
['arrow_back', SCIcon.arrow_back],
|
||||
]);
|
||||
@@ -1,132 +1,14 @@
|
||||
/*
|
||||
* Copyright (C) 2022 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {
|
||||
ComponentRef,
|
||||
Directive,
|
||||
ElementRef,
|
||||
Host,
|
||||
Input,
|
||||
OnChanges,
|
||||
OnDestroy,
|
||||
OnInit,
|
||||
Optional,
|
||||
Self,
|
||||
ViewContainerRef,
|
||||
} from '@angular/core';
|
||||
import {IconComponent} from './icon.component';
|
||||
import {IonIcon} from '@ionic/angular';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
const noop = () => {};
|
||||
const noopProperty = {
|
||||
set: noop,
|
||||
get: noop,
|
||||
};
|
||||
import {Directive, Input} from '@angular/core';
|
||||
import {MaterialSymbol} from 'material-symbols';
|
||||
|
||||
@Directive({
|
||||
selector: 'ion-icon',
|
||||
standalone: true,
|
||||
})
|
||||
export class IonIconDirective implements OnInit, OnDestroy, OnChanges {
|
||||
@Input() name: string;
|
||||
export class IonIconDirective {
|
||||
@Input({required: true}) name: MaterialSymbol;
|
||||
|
||||
@Input() md: string;
|
||||
@Input() size: 'large' | 'small' | number | string;
|
||||
|
||||
@Input() ios: string;
|
||||
|
||||
@Input() fill = false;
|
||||
|
||||
@Input() weight: number;
|
||||
|
||||
@Input() size: number | 'small' | 'large';
|
||||
|
||||
@Input() grade: number;
|
||||
|
||||
@Input() style: string;
|
||||
|
||||
private mutationObserver: MutationObserver;
|
||||
|
||||
iconComponent?: ComponentRef<IconComponent>;
|
||||
|
||||
private static get mode(): 'md' | 'ios' {
|
||||
return document.querySelector(':root')?.getAttribute('mode') as 'md' | 'ios';
|
||||
}
|
||||
|
||||
constructor(
|
||||
private element: ElementRef,
|
||||
private viewContainerRef: ViewContainerRef,
|
||||
@Host() @Self() @Optional() private ionIcon: IonIcon,
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.iconComponent = this.viewContainerRef.createComponent(IconComponent, {});
|
||||
|
||||
this.element.nativeElement.insertBefore(
|
||||
this.iconComponent.location.nativeElement,
|
||||
this.element.nativeElement.firstChild,
|
||||
);
|
||||
|
||||
this.mutationObserver = new MutationObserver(() => {
|
||||
const inner = this.element.nativeElement.shadowRoot.querySelector('.icon-inner');
|
||||
if (!inner) return;
|
||||
|
||||
inner.insertBefore(document.createElement('slot'), inner.firstChild);
|
||||
});
|
||||
this.mutationObserver.observe(this.element.nativeElement.shadowRoot, {
|
||||
childList: true,
|
||||
});
|
||||
|
||||
this.ngOnChanges();
|
||||
// this will effectively completely disable the ion-icon component
|
||||
for (const name of ['src', 'name', 'icon', 'md', 'ios']) {
|
||||
this.disableProperty(name);
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.mutationObserver.disconnect();
|
||||
}
|
||||
|
||||
ngOnChanges() {
|
||||
if (!this.iconComponent) return;
|
||||
for (const key of ['name', 'weight', 'fill', 'size', 'grade'] as Array<
|
||||
keyof IconComponent & keyof IonIconDirective
|
||||
>) {
|
||||
// @ts-expect-error type mismatch
|
||||
this.iconComponent.instance[key] = this[key];
|
||||
}
|
||||
|
||||
for (const mode of ['md', 'ios'] as Array<'md' | 'ios'>) {
|
||||
if (this[mode] && IonIconDirective.mode === mode) {
|
||||
this.iconComponent.instance.name = this[mode];
|
||||
}
|
||||
}
|
||||
|
||||
if (this.size) {
|
||||
this.element.nativeElement.style.cssText = `font-size: ${this.size}px;`;
|
||||
}
|
||||
if (this.style) {
|
||||
this.element.nativeElement.style.cssText += this.style;
|
||||
}
|
||||
}
|
||||
|
||||
disableProperty(name: string) {
|
||||
Object.defineProperty(
|
||||
Object.getPrototypeOf((this.ionIcon as unknown as {el: HTMLElement}).el),
|
||||
name,
|
||||
noopProperty,
|
||||
);
|
||||
}
|
||||
@Input() color: 'primary' | 'secondary' | 'light' | 'medium' | 'dark' | string;
|
||||
}
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2022 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {NgModule} from '@angular/core';
|
||||
import {IconComponent} from './icon.component';
|
||||
import {IonIconDirective} from './ion-icon.directive';
|
||||
import {IonBackButtonDirective} from './ion-back-button.directive';
|
||||
import {IonSearchbarDirective} from './ion-searchbar.directive';
|
||||
import {IonBreadcrumbDirective} from './ion-breadcrumb.directive';
|
||||
import {IonReorderDirective} from './ion-reorder.directive';
|
||||
import {TranslateModule, TranslateService} from '@ngx-translate/core';
|
||||
import {CommonModule, TitleCasePipe} from '@angular/common';
|
||||
|
||||
@NgModule({
|
||||
imports: [TranslateModule, CommonModule],
|
||||
declarations: [
|
||||
IconComponent,
|
||||
IonIconDirective,
|
||||
IonBackButtonDirective,
|
||||
IonSearchbarDirective,
|
||||
IonBreadcrumbDirective,
|
||||
IonReorderDirective,
|
||||
],
|
||||
exports: [
|
||||
IonIconDirective,
|
||||
IonReorderDirective,
|
||||
IonBackButtonDirective,
|
||||
IonSearchbarDirective,
|
||||
IonBreadcrumbDirective,
|
||||
],
|
||||
providers: [TranslateService, TitleCasePipe],
|
||||
})
|
||||
export class IonIconModule {}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2022 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import {Directive, ElementRef, ViewContainerRef} from '@angular/core';
|
||||
import {SCIcon} from './icon';
|
||||
import {IconReplacer} from './replace-util';
|
||||
|
||||
@Directive({
|
||||
selector: 'ion-reorder',
|
||||
})
|
||||
export class IonReorderDirective extends IconReplacer {
|
||||
constructor(element: ElementRef, viewContainerRef: ViewContainerRef) {
|
||||
super(element, viewContainerRef, 'shadow');
|
||||
}
|
||||
|
||||
replace() {
|
||||
this.replaceIcon(this.host, {
|
||||
name: SCIcon`reorder`,
|
||||
size: 24,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2022 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import {Directive, ElementRef, ViewContainerRef} from '@angular/core';
|
||||
import {SCIcon} from './icon';
|
||||
import {IconReplacer} from './replace-util';
|
||||
|
||||
@Directive({
|
||||
selector: 'ion-searchbar',
|
||||
})
|
||||
export class IonSearchbarDirective extends IconReplacer {
|
||||
constructor(element: ElementRef, viewContainerRef: ViewContainerRef) {
|
||||
super(element, viewContainerRef, 'light');
|
||||
}
|
||||
|
||||
replace() {
|
||||
this.replaceIcon(this.host.querySelector('.searchbar-input-container'), {
|
||||
name: SCIcon`search`,
|
||||
size: 24,
|
||||
});
|
||||
this.replaceIcon(this.host.querySelector('.searchbar-clear-button'), {
|
||||
name: SCIcon`close`,
|
||||
size: 24,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2022 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {ComponentRef, Directive, ElementRef, OnDestroy, OnInit, ViewContainerRef} from '@angular/core';
|
||||
import {IonIcon} from '@ionic/angular';
|
||||
import {IonIconDirective} from './ion-icon.directive';
|
||||
import {waitForElement} from './shadow-attacher';
|
||||
|
||||
export type IconData = Omit<
|
||||
Partial<IonIconDirective>,
|
||||
'ngOnChanges' | 'ngOnInit' | 'viewContainerRef' | 'ngOnDestroy' | 'element' | 'ionIcon' | 'disableProperty'
|
||||
>;
|
||||
|
||||
/**
|
||||
* A utility class to replace ion-icons in other ionic components.
|
||||
*/
|
||||
@Directive()
|
||||
export abstract class IconReplacer implements OnInit, OnDestroy {
|
||||
private mutationObserver: MutationObserver;
|
||||
|
||||
protected slotName = 'sc-icon';
|
||||
|
||||
protected maxAttempts = 10;
|
||||
|
||||
protected retryAfterMs = 10;
|
||||
|
||||
/**
|
||||
* The host element
|
||||
*
|
||||
* This will be either element.nativeElement.shadowRoot or element.nativeElement
|
||||
* depending on the iconDomLocation
|
||||
*/
|
||||
protected get host() {
|
||||
return this.iconDomLocation === 'shadow'
|
||||
? this.element.nativeElement.shadowRoot
|
||||
: this.element.nativeElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param element The host element
|
||||
* @param viewContainerRef The view container ref
|
||||
* @param iconDomLocation If the icon is placed inside the shadow dom or not
|
||||
* @protected
|
||||
*/
|
||||
protected constructor(
|
||||
private readonly element: ElementRef,
|
||||
private readonly viewContainerRef: ViewContainerRef,
|
||||
private readonly iconDomLocation: 'shadow' | 'light',
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Replace the icons here
|
||||
*/
|
||||
abstract replace(): void;
|
||||
|
||||
async ngOnInit() {
|
||||
if (this.host) {
|
||||
this.attachObserver();
|
||||
} else {
|
||||
console.warn("Shadow root didn't exist for ion icon replacer, retrying...");
|
||||
await waitForElement(() => this.host);
|
||||
this.replace();
|
||||
}
|
||||
}
|
||||
|
||||
private attachObserver() {
|
||||
this.mutationObserver = new MutationObserver(() => this.replace());
|
||||
this.mutationObserver.observe(this.host, {
|
||||
childList: true,
|
||||
});
|
||||
}
|
||||
|
||||
replaceIcon(parent: HTMLElement | null, iconData: IconData, slotName = '') {
|
||||
if (!parent) return;
|
||||
|
||||
const icon = parent.querySelector('ion-icon');
|
||||
if (!icon) return;
|
||||
|
||||
const scIcon = this.createIcon(iconData);
|
||||
// @ts-expect-error can be spread
|
||||
scIcon.location.nativeElement.classList.add(...icon.classList);
|
||||
|
||||
if (this.iconDomLocation === 'shadow') {
|
||||
// shadow dom needs to utilize slotting, to put it outside the shadow dom
|
||||
// otherwise it won't receive any css data
|
||||
const slot = document.createElement('slot');
|
||||
slot.name = this.slotName + slotName;
|
||||
icon.replaceWith(slot);
|
||||
|
||||
scIcon.location.nativeElement.slot = this.slotName + slotName;
|
||||
this.element.nativeElement.append(scIcon.location.nativeElement);
|
||||
} else {
|
||||
icon.replaceWith(scIcon.location.nativeElement);
|
||||
}
|
||||
}
|
||||
|
||||
private createIcon(iconData: IconData): ComponentRef<IonIcon> {
|
||||
const ionIcon = this.viewContainerRef.createComponent(IonIcon, {});
|
||||
const iconDirective = new IonIconDirective(ionIcon.location, this.viewContainerRef, ionIcon.instance);
|
||||
for (const key in iconData) {
|
||||
// @ts-expect-error type mismatch
|
||||
iconDirective[key] = iconData[key];
|
||||
}
|
||||
iconDirective.ngOnInit();
|
||||
|
||||
return ionIcon;
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.mutationObserver?.disconnect();
|
||||
}
|
||||
}
|
||||
@@ -62,7 +62,7 @@ export const profilePageSections: SCSection[] = [
|
||||
links: [
|
||||
{
|
||||
name: 'Favorites',
|
||||
icon: SCIcon`grade`,
|
||||
icon: SCIcon.grade,
|
||||
link: ['/favorites'],
|
||||
translations: {
|
||||
de: {
|
||||
@@ -73,7 +73,7 @@ export const profilePageSections: SCSection[] = [
|
||||
},
|
||||
{
|
||||
name: 'Schedule',
|
||||
icon: SCIcon`calendar_today`,
|
||||
icon: SCIcon.calendar_today,
|
||||
link: ['/schedule'],
|
||||
translations: {
|
||||
de: {
|
||||
@@ -84,7 +84,7 @@ export const profilePageSections: SCSection[] = [
|
||||
},
|
||||
{
|
||||
name: 'Course Catalog',
|
||||
icon: SCIcon`inventory_2`,
|
||||
icon: SCIcon.inventory_2,
|
||||
link: ['/catalog'],
|
||||
translations: {
|
||||
de: {
|
||||
@@ -95,7 +95,7 @@ export const profilePageSections: SCSection[] = [
|
||||
},
|
||||
{
|
||||
name: 'Settings',
|
||||
icon: SCIcon`settings`,
|
||||
icon: SCIcon.settings,
|
||||
link: ['/settings'],
|
||||
translations: {
|
||||
de: {
|
||||
@@ -106,7 +106,7 @@ export const profilePageSections: SCSection[] = [
|
||||
},
|
||||
{
|
||||
name: 'Feedback',
|
||||
icon: SCIcon`rate_review`,
|
||||
icon: SCIcon.rate_review,
|
||||
link: ['/feedback'],
|
||||
translations: {
|
||||
de: {
|
||||
@@ -117,7 +117,7 @@ export const profilePageSections: SCSection[] = [
|
||||
},
|
||||
{
|
||||
name: 'About',
|
||||
icon: SCIcon`info`,
|
||||
icon: SCIcon.info,
|
||||
link: ['/about'],
|
||||
translations: {
|
||||
de: {
|
||||
@@ -140,7 +140,7 @@ export const profilePageSections: SCSection[] = [
|
||||
links: [
|
||||
{
|
||||
name: 'Assessments',
|
||||
icon: SCIcon`fact_check`,
|
||||
icon: SCIcon.fact_check,
|
||||
link: ['/assessments'],
|
||||
needsAuth: true,
|
||||
translations: {
|
||||
@@ -164,7 +164,7 @@ export const profilePageSections: SCSection[] = [
|
||||
links: [
|
||||
{
|
||||
name: 'Library Catalog',
|
||||
icon: SCIcon`local_library`,
|
||||
icon: SCIcon.local_library,
|
||||
link: ['/hebis-search'],
|
||||
translations: {
|
||||
de: {
|
||||
@@ -175,7 +175,7 @@ export const profilePageSections: SCSection[] = [
|
||||
},
|
||||
{
|
||||
name: 'Library Account',
|
||||
icon: SCIcon`badge`,
|
||||
icon: SCIcon.badge,
|
||||
needsAuth: true,
|
||||
link: ['/library-account/profile'],
|
||||
translations: {
|
||||
@@ -187,7 +187,7 @@ export const profilePageSections: SCSection[] = [
|
||||
},
|
||||
{
|
||||
name: 'Orders & Reservations',
|
||||
icon: SCIcon`collections_bookmark`,
|
||||
icon: SCIcon.collections_bookmark,
|
||||
needsAuth: true,
|
||||
link: ['/library-account/holds'],
|
||||
translations: {
|
||||
@@ -199,7 +199,7 @@ export const profilePageSections: SCSection[] = [
|
||||
},
|
||||
{
|
||||
name: 'Checked out items',
|
||||
icon: SCIcon`library_books`,
|
||||
icon: SCIcon.library_books,
|
||||
needsAuth: true,
|
||||
link: ['/library-account/checked-out'],
|
||||
translations: {
|
||||
@@ -211,7 +211,7 @@ export const profilePageSections: SCSection[] = [
|
||||
},
|
||||
{
|
||||
name: 'Fines',
|
||||
icon: SCIcon`request_page`,
|
||||
icon: SCIcon.request_page,
|
||||
needsAuth: true,
|
||||
link: ['/library-account/fines'],
|
||||
translations: {
|
||||
|
||||
@@ -36,13 +36,6 @@
|
||||
|
||||
/* StApps */
|
||||
|
||||
stapps-icon {
|
||||
--size-unit: 1px;
|
||||
--weight: 400;
|
||||
--grade: 0;
|
||||
--fill: 0;
|
||||
}
|
||||
|
||||
.map-location-pin {
|
||||
font-variation-settings: 'FILL' 1;
|
||||
color: var(--ion-color-tertiary);
|
||||
|
||||
@@ -12,10 +12,14 @@
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import {enableProdMode} from '@angular/core';
|
||||
// override the ion-icon before everything else
|
||||
customElements.define('ion-icon', IonIconCustomElement);
|
||||
|
||||
import {platformBrowser} from '@angular/platform-browser';
|
||||
import {AppModule} from './app/app.module';
|
||||
import {environment} from './environments/environment';
|
||||
import {enableProdMode} from '@angular/core';
|
||||
import {AppModule} from './app/app.module';
|
||||
import {IonIconCustomElement} from './app/util/ion-icon/ion-icon-custom-element';
|
||||
|
||||
if (environment.production) {
|
||||
enableProdMode();
|
||||
|
||||
@@ -33,9 +33,14 @@ app-root ion-searchbar[class*='sc-ion-searchbar-'] {
|
||||
width: $icon-size;
|
||||
height: $icon-size;
|
||||
|
||||
font-size: $icon-size;
|
||||
color: var(--ion-color-medium-shade);
|
||||
}
|
||||
|
||||
ion-icon.searchbar-clear-icon {
|
||||
font-size: $icon-size;
|
||||
}
|
||||
|
||||
input.searchbar-input {
|
||||
padding-top: var(--spacing-xs);
|
||||
padding-bottom: var(--spacing-xs);
|
||||
|
||||
@@ -68,6 +68,7 @@ app-root ion-toolbar.in-toolbar {
|
||||
|
||||
ion-back-button {
|
||||
--icon-margin-end: var(--spacing-xs);
|
||||
--icon-margin-start: 0;
|
||||
|
||||
height: 42px; // this prevents the back button to become a .x px value
|
||||
}
|
||||
|
||||
@@ -30,12 +30,12 @@
|
||||
"devDependencies": {
|
||||
"@changesets/changelog-git": "0.1.14",
|
||||
"@changesets/cli": "2.26.1",
|
||||
"prettier": "3.1.1",
|
||||
"cobertura-merge": "1.0.4",
|
||||
"deepmerge": "4.3.1",
|
||||
"dotenv-cli": "7.2.1",
|
||||
"glob": "10.3.10",
|
||||
"junit-report-merger": "6.0.3",
|
||||
"prettier": "3.1.1",
|
||||
"syncpack": "12.3.0",
|
||||
"turbo": "1.10.16",
|
||||
"turbo-ignore": "1.10.16",
|
||||
|
||||
44
pnpm-lock.yaml
generated
44
pnpm-lock.yaml
generated
@@ -830,6 +830,9 @@ importers:
|
||||
ionic-appauth:
|
||||
specifier: 0.9.0
|
||||
version: 0.9.0(rxjs@7.8.1)
|
||||
ionicons:
|
||||
specifier: 7.2.1
|
||||
version: 7.2.1
|
||||
jsonpath-plus:
|
||||
specifier: 6.0.1
|
||||
version: 6.0.1
|
||||
@@ -4546,7 +4549,7 @@ packages:
|
||||
object-assign: 4.1.1
|
||||
open: 8.4.0
|
||||
proxy-middleware: 0.15.0
|
||||
send: 1.0.0-beta.2
|
||||
send: 0.18.0
|
||||
serve-index: 1.9.1
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
@@ -6528,12 +6531,6 @@ packages:
|
||||
hasBin: true
|
||||
dev: false
|
||||
|
||||
/@stencil/core@4.5.0:
|
||||
resolution: {integrity: sha512-XRbHdb9t4SQzCCbF9qsh0dexvnlArEzCDJl19BJzxzazVBM398SeJUKCBh4p91AZIWveN0gHuZSIGMhLWR7qSA==}
|
||||
engines: {node: '>=16.0.0', npm: '>=7.10.0'}
|
||||
hasBin: true
|
||||
dev: false
|
||||
|
||||
/@swc/helpers@0.4.14:
|
||||
resolution: {integrity: sha512-4C7nX/dvpzB7za4Ql9K81xK3HPxCpHMgwTZVyf+9JQ6VUbn9jjZVN7/Nkdz/Ugzs2CSjqnL/UPXroiVBVHUWUw==}
|
||||
dependencies:
|
||||
@@ -10089,17 +10086,6 @@ packages:
|
||||
dependencies:
|
||||
ms: 2.0.0
|
||||
|
||||
/debug@3.1.0:
|
||||
resolution: {integrity: sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==}
|
||||
peerDependencies:
|
||||
supports-color: '*'
|
||||
peerDependenciesMeta:
|
||||
supports-color:
|
||||
optional: true
|
||||
dependencies:
|
||||
ms: 2.0.0
|
||||
dev: true
|
||||
|
||||
/debug@3.2.7(supports-color@5.5.0):
|
||||
resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
|
||||
peerDependencies:
|
||||
@@ -12872,7 +12858,7 @@ packages:
|
||||
/ionicons@7.2.1:
|
||||
resolution: {integrity: sha512-2pvCM7DGVEtbbj48PJzQrCADCQrqjU1nUYX9l9PyEWz3ZfdnLdAouqwPxLdl8tbaF9cE7OZRSlyQD7oLOLnGoQ==}
|
||||
dependencies:
|
||||
'@stencil/core': 4.5.0
|
||||
'@stencil/core': 4.12.6
|
||||
dev: false
|
||||
|
||||
/ionicons@7.2.2:
|
||||
@@ -17306,26 +17292,6 @@ packages:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
/send@1.0.0-beta.2:
|
||||
resolution: {integrity: sha512-k1yHu/FNK745PULKdsGpQ+bVSXYNwSk+bWnYzbxGZbt5obZc0JKDVANsCRuJD1X/EG15JtP9eZpwxkhUxIYEcg==}
|
||||
engines: {node: '>= 0.10'}
|
||||
dependencies:
|
||||
debug: 3.1.0
|
||||
destroy: 1.2.0
|
||||
encodeurl: 1.0.2
|
||||
escape-html: 1.0.3
|
||||
etag: 1.8.1
|
||||
fresh: 0.5.2
|
||||
http-errors: 2.0.0
|
||||
mime-types: 2.1.35
|
||||
ms: 2.1.3
|
||||
on-finished: 2.4.1
|
||||
range-parser: 1.2.1
|
||||
statuses: 2.0.1
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/serialize-javascript@6.0.0:
|
||||
resolution: {integrity: sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==}
|
||||
dependencies:
|
||||
|
||||
Reference in New Issue
Block a user