@foxeltech/angular-ui 0.0.14 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/BeVietnamPro/BeVietnamPro-Bold.woff2 +0 -0
- package/BeVietnamPro/BeVietnamPro-ExtraLight.woff2 +0 -0
- package/BeVietnamPro/BeVietnamPro-Light.woff2 +0 -0
- package/BeVietnamPro/BeVietnamPro-Medium.woff2 +0 -0
- package/BeVietnamPro/BeVietnamPro-Regular.woff2 +0 -0
- package/BeVietnamPro/BeVietnamPro-SemiBold.woff2 +0 -0
- package/BeVietnamPro/BeVietnamPro-Thin.woff2 +0 -0
- package/SpaceMono-Regular.woff2 +0 -0
- package/components.css +7 -17
- package/dm-sans/DMSans-Bold.woff2 +0 -0
- package/dm-sans/DMSans-Light.woff2 +0 -0
- package/dm-sans/DMSans-Medium.woff2 +0 -0
- package/dm-sans/DMSans-Regular.woff2 +0 -0
- package/dm-sans/DMSans-SemiBold.woff2 +0 -0
- package/dm-sans/DMSans-Thin.woff2 +0 -0
- package/fesm2022/foxeltech-angular-ui.mjs +1349 -285
- package/fesm2022/foxeltech-angular-ui.mjs.map +1 -1
- package/fonts.css +68 -34
- package/index.d.ts +624 -139
- package/orbitron/Orbitron-Bold.woff2 +0 -0
- package/orbitron/Orbitron-Medium.woff2 +0 -0
- package/orbitron/Orbitron-Regular.woff2 +0 -0
- package/orbitron/Orbitron-SemiBold.woff2 +0 -0
- package/package.json +9 -10
- package/roboto/Roboto-Black.woff2 +0 -0
- package/roboto/Roboto-BlackItalic.woff2 +0 -0
- package/roboto/Roboto-Bold.woff2 +0 -0
- package/roboto/Roboto-BoldCondensed.woff2 +0 -0
- package/roboto/Roboto-BoldCondensedItalic.woff2 +0 -0
- package/roboto/Roboto-BoldItalic.woff2 +0 -0
- package/roboto/Roboto-Condensed.woff2 +0 -0
- package/roboto/Roboto-CondensedItalic.woff2 +0 -0
- package/roboto/Roboto-Italic.woff2 +0 -0
- package/roboto/Roboto-Light.woff2 +0 -0
- package/roboto/Roboto-LightItalic.woff2 +0 -0
- package/roboto/Roboto-Medium.woff2 +0 -0
- package/roboto/Roboto-MediumItalic.woff2 +0 -0
- package/roboto/Roboto-Regular.woff2 +0 -0
- package/roboto/Roboto-Thin.woff2 +0 -0
- package/roboto/Roboto-ThinItalic.woff2 +0 -0
- package/src/lib/styles/components.css +7 -17
- package/src/lib/styles/fonts.css +68 -34
- package/src/lib/styles/tailwind.css +11 -8
- package/src/lib/styles/tokens.css +131 -0
- package/src/lib/styles/utilities.css +24 -0
- package/src/lib/ui/components/button/button.component.html +4 -1
- package/src/lib/ui/components/button/button.component.scss +0 -0
- package/src/lib/ui/components/chart/chart.component.scss +49 -0
- package/src/lib/ui/components/checkbox/checkbox.component.html +3 -2
- package/src/lib/ui/components/checkbox/checkbox.component.scss +0 -0
- package/src/lib/ui/components/circle-progress-bar/circle-progress-bar.component.scss +0 -0
- package/src/lib/ui/components/datetime-picker/datetime-picker.component.html +3 -2
- package/src/lib/ui/components/datetime-picker/datetime-picker.component.scss +147 -0
- package/src/lib/ui/components/dnd-upload/dnd-upload.component.scss +87 -0
- package/src/lib/ui/components/drawer/drawer.component.html +3 -0
- package/src/lib/ui/components/drawer/drawer.component.scss +134 -0
- package/src/lib/ui/components/empty-state/empty-state.component.html +9 -0
- package/src/lib/ui/components/filter-chips/filter-chips.component.html +12 -0
- package/src/lib/ui/components/flow-connection/flow-connection.component.scss +0 -0
- package/src/lib/ui/components/hero-icon/hero-icon.component.scss +0 -0
- package/src/lib/ui/components/info-field/info-field.component.html +4 -0
- package/src/lib/ui/components/input/input.component.html +15 -3
- package/src/lib/ui/components/kanban-board/kanban-board.component.scss +70 -0
- package/src/lib/ui/components/loading-panel/loading-panel.component.scss +0 -0
- package/src/lib/ui/components/master-detail/master-detail.component.css +11 -0
- package/src/lib/ui/components/master-detail/master-detail.component.html +70 -0
- package/src/lib/ui/components/meter/meter.component.html +22 -0
- package/src/lib/ui/components/radio-button/radio-button.component.html +10 -4
- package/src/lib/ui/components/radio-button-toggle/radio-button-toggle.component.html +8 -3
- package/src/lib/ui/components/radio-button-toggle/radio-button-toggle.component.scss +0 -0
- package/src/lib/ui/components/rich-text-area/rich-text-area.component.scss +18 -0
- package/src/lib/ui/components/search-bar/search-bar.component.scss +0 -0
- package/src/lib/ui/components/section-card/section-card.component.html +9 -0
- package/src/lib/ui/components/select/select.component.html +9 -4
- package/src/lib/ui/components/severity-badges/severity-badges.component.html +11 -0
- package/src/lib/ui/components/skeleton-detail/skeleton-detail.component.html +22 -0
- package/src/lib/ui/components/skeleton-list/skeleton-list.component.html +12 -0
- package/src/lib/ui/components/skeleton-table-loading/skeleton-table-loading.component.scss +0 -0
- package/src/lib/ui/components/slider/slider.component.html +3 -0
- package/src/lib/ui/components/slider/slider.component.scss +0 -0
- package/src/lib/ui/components/stat-cards/stat-cards.component.html +18 -0
- package/src/lib/ui/components/switch/switch.component.html +11 -6
- package/src/lib/ui/components/switch/switch.component.scss +0 -0
- package/src/lib/ui/components/tab-component/tab.component.scss +0 -0
- package/src/lib/ui/components/tab-group/tab-group.component.html +15 -1
- package/src/lib/ui/components/tab-group/tab-group.component.scss +51 -0
- package/src/lib/ui/components/table-cell/table-cell.component.scss +0 -0
- package/src/lib/ui/components/tag/tag.component.html +6 -1
- package/src/lib/ui/components/tag/tag.component.scss +0 -0
- package/src/lib/ui/components/toast/toast.component.html +2 -1
- package/src/lib/ui/components/toast/toast.component.scss +0 -0
- package/src/lib/ui/components/toast-container/toast-container.component.scss +0 -0
- package/src/lib/ui/components/tree-diagram/tree-diagram.component.scss +0 -0
- package/src/lib/ui/dialogs/confirmation/confirmation.component.html +1 -1
- package/src/lib/ui/dialogs/confirmation/confirmation.component.scss +0 -0
- package/tailwind.css +11 -8
- package/tokens.css +131 -0
- package/utilities.css +24 -0
- package/BeVietnamPro/BeVietnamPro-Bold.ttf +0 -0
- package/BeVietnamPro/BeVietnamPro-ExtraLight.ttf +0 -0
- package/BeVietnamPro/BeVietnamPro-Light.ttf +0 -0
- package/BeVietnamPro/BeVietnamPro-Medium.ttf +0 -0
- package/BeVietnamPro/BeVietnamPro-Regular.ttf +0 -0
- package/BeVietnamPro/BeVietnamPro-SemiBold.ttf +0 -0
- package/BeVietnamPro/BeVietnamPro-Thin.ttf +0 -0
- package/SpaceMono-Regular.ttf +0 -0
- package/dm-sans/DMSans-Bold.ttf +0 -0
- package/dm-sans/DMSans-Light.ttf +0 -0
- package/dm-sans/DMSans-Medium.ttf +0 -0
- package/dm-sans/DMSans-Regular.ttf +0 -0
- package/dm-sans/DMSans-SemiBold.ttf +0 -0
- package/dm-sans/DMSans-Thin.ttf +0 -0
- package/orbitron/Orbitron-Bold.ttf +0 -0
- package/orbitron/Orbitron-Medium.ttf +0 -0
- package/orbitron/Orbitron-Regular.ttf +0 -0
- package/orbitron/Orbitron-SemiBold.ttf +0 -0
- package/roboto/Roboto-Black.ttf +0 -0
- package/roboto/Roboto-BlackItalic.ttf +0 -0
- package/roboto/Roboto-Bold.ttf +0 -0
- package/roboto/Roboto-BoldCondensed.ttf +0 -0
- package/roboto/Roboto-BoldCondensedItalic.ttf +0 -0
- package/roboto/Roboto-BoldItalic.ttf +0 -0
- package/roboto/Roboto-Condensed.ttf +0 -0
- package/roboto/Roboto-CondensedItalic.ttf +0 -0
- package/roboto/Roboto-Italic.ttf +0 -0
- package/roboto/Roboto-Light.ttf +0 -0
- package/roboto/Roboto-LightItalic.ttf +0 -0
- package/roboto/Roboto-Medium.ttf +0 -0
- package/roboto/Roboto-MediumItalic.ttf +0 -0
- package/roboto/Roboto-Regular.ttf +0 -0
- package/roboto/Roboto-Thin.ttf +0 -0
- package/roboto/Roboto-ThinItalic.ttf +0 -0
- package/tailwind-config.d.ts +0 -4
- package/tailwind.config.js +0 -95
|
@@ -1,76 +1,81 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { Directive, Input, Component, createComponent, Injectable, ViewChild, Inject, EventEmitter, NgModule, HostListener, PLATFORM_ID, Output,
|
|
3
|
-
import * as
|
|
2
|
+
import { inject, DestroyRef, Injector, Directive, Input, Component, createComponent, Injectable, ViewChild, Inject, EventEmitter, NgModule, HostListener, PLATFORM_ID, Output, ChangeDetectionStrategy, TemplateRef, ContentChildren, ViewChildren, ContentChild, importProvidersFrom } from '@angular/core';
|
|
3
|
+
import * as i45 from '@angular/material/paginator';
|
|
4
4
|
import { MatPaginator, MatPaginatorModule } from '@angular/material/paginator';
|
|
5
|
-
import * as
|
|
5
|
+
import * as i46 from '@angular/material/table';
|
|
6
6
|
import { MatTableDataSource, MatTable, MatTableModule } from '@angular/material/table';
|
|
7
|
-
import * as
|
|
7
|
+
import * as i1$1 from '@angular/common';
|
|
8
8
|
import { isPlatformBrowser, CommonModule } from '@angular/common';
|
|
9
9
|
import * as i1 from '@dimaslz/ng-heroicons';
|
|
10
10
|
import { NgHeroiconsModule } from '@dimaslz/ng-heroicons';
|
|
11
11
|
import _, { clamp } from 'lodash';
|
|
12
|
-
import { Observable, firstValueFrom, Subject, debounceTime, BehaviorSubject, throwError,
|
|
13
|
-
import
|
|
12
|
+
import { Observable, firstValueFrom, Subject, debounceTime, distinctUntilChanged, BehaviorSubject, throwError, filter, takeUntil } from 'rxjs';
|
|
13
|
+
import moment from 'moment';
|
|
14
|
+
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
15
|
+
import * as i2 from '@angular/common/http';
|
|
14
16
|
import { HttpRequest, HttpClient } from '@angular/common/http';
|
|
15
|
-
import * as i1$
|
|
17
|
+
import * as i1$2 from '@angular/material/dialog';
|
|
16
18
|
import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
|
|
17
|
-
import * as i1$
|
|
19
|
+
import * as i1$3 from '@ngx-translate/core';
|
|
18
20
|
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
|
19
21
|
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
|
|
20
|
-
import * as
|
|
21
|
-
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
|
22
|
+
import * as i2$1 from '@angular/forms';
|
|
23
|
+
import { NgControl, FormsModule, ReactiveFormsModule } from '@angular/forms';
|
|
22
24
|
import * as i1$4 from '@angular/router';
|
|
23
25
|
import { NavigationEnd, RouterModule } from '@angular/router';
|
|
24
26
|
import { catchError } from 'rxjs/operators';
|
|
25
27
|
import * as signalR from '@microsoft/signalr';
|
|
26
|
-
import * as i3
|
|
28
|
+
import * as i3 from '@angular/material/select';
|
|
27
29
|
import { MatSelectModule } from '@angular/material/select';
|
|
28
|
-
import * as
|
|
30
|
+
import * as i42 from '@angular/material/radio';
|
|
29
31
|
import { MatRadioModule } from '@angular/material/radio';
|
|
30
|
-
import * as
|
|
32
|
+
import * as i43 from '@angular/material/button';
|
|
31
33
|
import { MatButtonModule } from '@angular/material/button';
|
|
32
|
-
import * as
|
|
34
|
+
import * as i44 from '@angular/material/icon';
|
|
33
35
|
import { MatIconModule } from '@angular/material/icon';
|
|
34
|
-
import * as
|
|
36
|
+
import * as i47 from '@angular/material/snack-bar';
|
|
35
37
|
import { MatSnackBarModule } from '@angular/material/snack-bar';
|
|
36
|
-
import * as
|
|
38
|
+
import * as i49 from '@angular/material/checkbox';
|
|
37
39
|
import { MatCheckboxModule } from '@angular/material/checkbox';
|
|
38
|
-
import * as
|
|
40
|
+
import * as i50 from '@angular/material/card';
|
|
39
41
|
import { MatCardModule } from '@angular/material/card';
|
|
40
|
-
import * as
|
|
42
|
+
import * as i51 from '@angular/material/datepicker';
|
|
41
43
|
import { MatDatepickerModule } from '@angular/material/datepicker';
|
|
42
|
-
import * as
|
|
44
|
+
import * as i52 from '@angular/material/timepicker';
|
|
43
45
|
import { MatTimepickerModule } from '@angular/material/timepicker';
|
|
44
|
-
import * as
|
|
46
|
+
import * as i53 from '@angular/material/badge';
|
|
45
47
|
import { MatBadgeModule } from '@angular/material/badge';
|
|
46
|
-
import * as
|
|
48
|
+
import * as i54 from '@angular/material/expansion';
|
|
47
49
|
import { MatExpansionModule } from '@angular/material/expansion';
|
|
48
|
-
import * as
|
|
50
|
+
import * as i55 from '@angular/material/form-field';
|
|
49
51
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
|
50
|
-
import * as
|
|
52
|
+
import * as i56 from '@angular/material/menu';
|
|
51
53
|
import { MatMenuModule } from '@angular/material/menu';
|
|
52
|
-
import * as
|
|
54
|
+
import * as i57 from '@angular/material/tooltip';
|
|
53
55
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
|
54
|
-
import * as i2$
|
|
56
|
+
import * as i2$3 from '@angular/cdk/drag-drop';
|
|
55
57
|
import { DragDropModule, moveItemInArray, transferArrayItem } from '@angular/cdk/drag-drop';
|
|
56
|
-
import * as
|
|
58
|
+
import * as i2$2 from '@danielmoncada/angular-datetime-picker';
|
|
57
59
|
import { OwlDateTimeModule, OwlNativeDateTimeModule, OWL_DATE_TIME_FORMATS } from '@danielmoncada/angular-datetime-picker';
|
|
58
60
|
import * as i1$5 from 'ngx-echarts';
|
|
59
61
|
import { NgxEchartsModule } from 'ngx-echarts';
|
|
60
|
-
import
|
|
61
|
-
import * as i29 from 'ngx-vflow';
|
|
62
|
+
import * as i39 from 'ngx-vflow';
|
|
62
63
|
import { Vflow } from 'ngx-vflow';
|
|
63
|
-
import { trigger, state,
|
|
64
|
+
import { trigger, state, transition, style, animate } from '@angular/animations';
|
|
64
65
|
import { OwlMomentDateTimeModule } from '@danielmoncada/angular-datetime-picker-moment-adapter';
|
|
65
66
|
|
|
66
67
|
class BaseComponent {
|
|
67
68
|
injector;
|
|
69
|
+
/** Teardown hook for subscriptions: takeUntilDestroyed(this.destroyRef). */
|
|
70
|
+
destroyRef = inject(DestroyRef);
|
|
68
71
|
constructor(injector) {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
72
|
+
// Some subclasses historically passed non-injector values (e.g. dialog
|
|
73
|
+
// data) to super(); fall back to the component's own injector so
|
|
74
|
+
// this.injector.get(...) always works.
|
|
75
|
+
this.injector = injector instanceof Injector ? injector : inject(Injector);
|
|
73
76
|
}
|
|
77
|
+
// eslint-disable-next-line @angular-eslint/no-empty-lifecycle-method -- subclasses across host apps call super.ngOnDestroy(); removing this breaks them at runtime
|
|
78
|
+
ngOnDestroy() { }
|
|
74
79
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: BaseComponent, deps: [{ token: i0.Injector }], target: i0.ɵɵFactoryTarget.Directive });
|
|
75
80
|
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.4", type: BaseComponent, isStandalone: true, ngImport: i0 });
|
|
76
81
|
}
|
|
@@ -114,11 +119,11 @@ class ToastComponent {
|
|
|
114
119
|
message = '';
|
|
115
120
|
type;
|
|
116
121
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: ToastComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
117
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: ToastComponent, isStandalone: false, selector: "fx-ui-toast", inputs: { toastId: "toastId", title: "title", message: "message", type: "type" }, ngImport: i0, template: "<div\n animate.enter=\"horizontal-fade-in-animation\"\n animate.leave=\"horizontal-fade-out-animation\"\n class=\"flex border-l-[3px] items-center bg-bg-primary gap-semi min-w-[250px] max-w-[400px] px-large py-semi rounded shadow-lg transition-all duration-300 ease-in-out transform opacity-100 translate-y-0\"\n [ngClass]=\"{\n 'border-success': type === 'success',\n 'border-critical': type === 'error',\n 'border-warning': type === 'warning',\n 'border-information': type === 'info'\n }\"\n>\n <div class=\"\">\n @switch (type) { @case('success') {\n <fx-ui-hero-icon\n icon=\"check-circle\"\n [solid]=\"true\"\n [size]=\"30\"\n class=\"text-success\"\n ></fx-ui-hero-icon>\n } @case('error') {\n <fx-ui-hero-icon\n icon=\"exclamation-circle\"\n [solid]=\"true\"\n [size]=\"30\"\n class=\"text-critical\"\n ></fx-ui-hero-icon>\n } @case('warning') {\n <fx-ui-hero-icon\n icon=\"exclamation-triangle\"\n [solid]=\"true\"\n [size]=\"30\"\n class=\"text-warning\"\n ></fx-ui-hero-icon>\n } @default {\n <fx-ui-hero-icon\n icon=\"information-circle\"\n [solid]=\"true\"\n [size]=\"30\"\n class=\"text-information\"\n ></fx-ui-hero-icon>\n } }\n </div>\n\n <div class=\"flex flex-col\">\n <strong *ngIf=\"title\" class=\"txt-field-label\">{{ title }}</strong>\n <p class=\"txt-default\">{{ message }}</p>\n </div>\n</div>\n", styles: [""], dependencies: [{ kind: "directive", type:
|
|
122
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: ToastComponent, isStandalone: false, selector: "fx-ui-toast", inputs: { toastId: "toastId", title: "title", message: "message", type: "type" }, ngImport: i0, template: "<div\n animate.enter=\"horizontal-fade-in-animation\"\n animate.leave=\"horizontal-fade-out-animation\"\n [attr.role]=\"type === 'error' ? 'alert' : 'status'\"\n class=\"flex border-l-[3px] items-center bg-bg-primary gap-semi min-w-[250px] max-w-[400px] px-large py-semi rounded shadow-lg transition-all duration-300 ease-in-out transform opacity-100 translate-y-0\"\n [ngClass]=\"{\n 'border-success': type === 'success',\n 'border-critical': type === 'error',\n 'border-warning': type === 'warning',\n 'border-information': type === 'info'\n }\"\n>\n <div class=\"\" aria-hidden=\"true\">\n @switch (type) { @case('success') {\n <fx-ui-hero-icon\n icon=\"check-circle\"\n [solid]=\"true\"\n [size]=\"30\"\n class=\"text-success\"\n ></fx-ui-hero-icon>\n } @case('error') {\n <fx-ui-hero-icon\n icon=\"exclamation-circle\"\n [solid]=\"true\"\n [size]=\"30\"\n class=\"text-critical\"\n ></fx-ui-hero-icon>\n } @case('warning') {\n <fx-ui-hero-icon\n icon=\"exclamation-triangle\"\n [solid]=\"true\"\n [size]=\"30\"\n class=\"text-warning\"\n ></fx-ui-hero-icon>\n } @default {\n <fx-ui-hero-icon\n icon=\"information-circle\"\n [solid]=\"true\"\n [size]=\"30\"\n class=\"text-information\"\n ></fx-ui-hero-icon>\n } }\n </div>\n\n <div class=\"flex flex-col\">\n <strong *ngIf=\"title\" class=\"txt-field-label\">{{ title }}</strong>\n <p class=\"txt-default\">{{ message }}</p>\n </div>\n</div>\n", styles: [""], dependencies: [{ kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: HeroIconComponent, selector: "fx-ui-hero-icon", inputs: ["stroke", "icon", "solid", "outline", "size", "color", "class"] }] });
|
|
118
123
|
}
|
|
119
124
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: ToastComponent, decorators: [{
|
|
120
125
|
type: Component,
|
|
121
|
-
args: [{ selector: 'fx-ui-toast', standalone: false, template: "<div\n animate.enter=\"horizontal-fade-in-animation\"\n animate.leave=\"horizontal-fade-out-animation\"\n class=\"flex border-l-[3px] items-center bg-bg-primary gap-semi min-w-[250px] max-w-[400px] px-large py-semi rounded shadow-lg transition-all duration-300 ease-in-out transform opacity-100 translate-y-0\"\n [ngClass]=\"{\n 'border-success': type === 'success',\n 'border-critical': type === 'error',\n 'border-warning': type === 'warning',\n 'border-information': type === 'info'\n }\"\n>\n <div class=\"\">\n @switch (type) { @case('success') {\n <fx-ui-hero-icon\n icon=\"check-circle\"\n [solid]=\"true\"\n [size]=\"30\"\n class=\"text-success\"\n ></fx-ui-hero-icon>\n } @case('error') {\n <fx-ui-hero-icon\n icon=\"exclamation-circle\"\n [solid]=\"true\"\n [size]=\"30\"\n class=\"text-critical\"\n ></fx-ui-hero-icon>\n } @case('warning') {\n <fx-ui-hero-icon\n icon=\"exclamation-triangle\"\n [solid]=\"true\"\n [size]=\"30\"\n class=\"text-warning\"\n ></fx-ui-hero-icon>\n } @default {\n <fx-ui-hero-icon\n icon=\"information-circle\"\n [solid]=\"true\"\n [size]=\"30\"\n class=\"text-information\"\n ></fx-ui-hero-icon>\n } }\n </div>\n\n <div class=\"flex flex-col\">\n <strong *ngIf=\"title\" class=\"txt-field-label\">{{ title }}</strong>\n <p class=\"txt-default\">{{ message }}</p>\n </div>\n</div>\n" }]
|
|
126
|
+
args: [{ selector: 'fx-ui-toast', standalone: false, template: "<div\n animate.enter=\"horizontal-fade-in-animation\"\n animate.leave=\"horizontal-fade-out-animation\"\n [attr.role]=\"type === 'error' ? 'alert' : 'status'\"\n class=\"flex border-l-[3px] items-center bg-bg-primary gap-semi min-w-[250px] max-w-[400px] px-large py-semi rounded shadow-lg transition-all duration-300 ease-in-out transform opacity-100 translate-y-0\"\n [ngClass]=\"{\n 'border-success': type === 'success',\n 'border-critical': type === 'error',\n 'border-warning': type === 'warning',\n 'border-information': type === 'info'\n }\"\n>\n <div class=\"\" aria-hidden=\"true\">\n @switch (type) { @case('success') {\n <fx-ui-hero-icon\n icon=\"check-circle\"\n [solid]=\"true\"\n [size]=\"30\"\n class=\"text-success\"\n ></fx-ui-hero-icon>\n } @case('error') {\n <fx-ui-hero-icon\n icon=\"exclamation-circle\"\n [solid]=\"true\"\n [size]=\"30\"\n class=\"text-critical\"\n ></fx-ui-hero-icon>\n } @case('warning') {\n <fx-ui-hero-icon\n icon=\"exclamation-triangle\"\n [solid]=\"true\"\n [size]=\"30\"\n class=\"text-warning\"\n ></fx-ui-hero-icon>\n } @default {\n <fx-ui-hero-icon\n icon=\"information-circle\"\n [solid]=\"true\"\n [size]=\"30\"\n class=\"text-information\"\n ></fx-ui-hero-icon>\n } }\n </div>\n\n <div class=\"flex flex-col\">\n <strong *ngIf=\"title\" class=\"txt-field-label\">{{ title }}</strong>\n <p class=\"txt-default\">{{ message }}</p>\n </div>\n</div>\n" }]
|
|
122
127
|
}], propDecorators: { toastId: [{
|
|
123
128
|
type: Input
|
|
124
129
|
}], title: [{
|
|
@@ -129,6 +134,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImpor
|
|
|
129
134
|
type: Input
|
|
130
135
|
}] } });
|
|
131
136
|
|
|
137
|
+
// import { ToastData } from '../../../../public-api';
|
|
132
138
|
class ToastContainerComponent {
|
|
133
139
|
ref;
|
|
134
140
|
toasts = [];
|
|
@@ -240,7 +246,7 @@ class FxUtils {
|
|
|
240
246
|
const keys = [];
|
|
241
247
|
const promises = [];
|
|
242
248
|
for (const i in value) {
|
|
243
|
-
if (
|
|
249
|
+
if (Object.prototype.hasOwnProperty.call(value, i)) {
|
|
244
250
|
keys.push(i);
|
|
245
251
|
this.preresolve(promises, value[i], i);
|
|
246
252
|
}
|
|
@@ -348,11 +354,24 @@ class FxUtils {
|
|
|
348
354
|
}
|
|
349
355
|
return `${_.get(err, 'error.code')}: ${message}`;
|
|
350
356
|
}
|
|
351
|
-
|
|
357
|
+
/**
|
|
358
|
+
* Resolve a theme channel triple (`--danger: 255 122 0`) to a CSS colour.
|
|
359
|
+
* Pass `legacyCommaSyntax: true` for canvas/ECharts consumers: canvas paints
|
|
360
|
+
* both forms, but zrender's colour parser can't read the CSS4 space syntax
|
|
361
|
+
* and renders slices black on hover when given it.
|
|
362
|
+
*/
|
|
363
|
+
static convertColorFromVariable(name, alpha = 1, options) {
|
|
352
364
|
const value = getComputedStyle(document.body)
|
|
353
365
|
.getPropertyValue(name)
|
|
354
366
|
.trim()
|
|
355
367
|
.replace(/\s+/g, ' ');
|
|
368
|
+
if (options?.legacyCommaSyntax) {
|
|
369
|
+
const [r, g, b] = value.replace(/[/,]/g, ' ').split(/\s+/).filter(Boolean);
|
|
370
|
+
if (r === undefined || g === undefined || b === undefined) {
|
|
371
|
+
return `rgba(148, 163, 184, ${alpha})`;
|
|
372
|
+
}
|
|
373
|
+
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
|
|
374
|
+
}
|
|
356
375
|
return value ? `rgba(${value} / ${alpha})` : `rgba(255 255 255 / ${alpha})`;
|
|
357
376
|
}
|
|
358
377
|
static getTagClass(key, isCriticalExist = true) {
|
|
@@ -397,6 +416,90 @@ class FxUtils {
|
|
|
397
416
|
const isEmail = emailRegex.test(value);
|
|
398
417
|
return isEmail;
|
|
399
418
|
}
|
|
419
|
+
/** Safe deep accessor with the org-wide empty placeholder. */
|
|
420
|
+
static value(model, key, defaultValue = '---') {
|
|
421
|
+
return _.get(model, key, defaultValue);
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* Severity weight for ordering — kept next to getTagClass so ordering and
|
|
425
|
+
* colouring can never drift apart.
|
|
426
|
+
*/
|
|
427
|
+
static severityWeight(level) {
|
|
428
|
+
switch (String(level ?? '').toUpperCase()) {
|
|
429
|
+
case 'CRITICAL':
|
|
430
|
+
return 5;
|
|
431
|
+
case 'HIGH':
|
|
432
|
+
return 4;
|
|
433
|
+
case 'MEDIUM':
|
|
434
|
+
return 3;
|
|
435
|
+
case 'LOW':
|
|
436
|
+
return 2;
|
|
437
|
+
case 'INFO':
|
|
438
|
+
case 'INFORMATION':
|
|
439
|
+
return 1;
|
|
440
|
+
default:
|
|
441
|
+
return 0;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
/** Org-wide date-time rendering (DD/MM/YYYY HH:mm:ss, local time). */
|
|
445
|
+
static formatDateTime(time) {
|
|
446
|
+
if (FxUtils.isNull(time))
|
|
447
|
+
return '';
|
|
448
|
+
return moment.parseZone(time).local().format('DD/MM/YYYY HH:mm:ss');
|
|
449
|
+
}
|
|
450
|
+
/** Seconds → compact duration ("2d 4h", "3h 12m", "45s"). */
|
|
451
|
+
static formatDuration(totalSeconds) {
|
|
452
|
+
const sec = Number(totalSeconds);
|
|
453
|
+
if (!_.isFinite(sec) || sec < 0)
|
|
454
|
+
return '';
|
|
455
|
+
const d = Math.floor(sec / 86400);
|
|
456
|
+
const h = Math.floor((sec % 86400) / 3600);
|
|
457
|
+
const m = Math.floor((sec % 3600) / 60);
|
|
458
|
+
const s = Math.floor(sec % 60);
|
|
459
|
+
if (d > 0)
|
|
460
|
+
return h > 0 ? `${d}d ${h}h` : `${d}d`;
|
|
461
|
+
if (h > 0)
|
|
462
|
+
return m > 0 ? `${h}h ${m}m` : `${h}h`;
|
|
463
|
+
if (m > 0)
|
|
464
|
+
return s > 0 ? `${m}m ${s}s` : `${m}m`;
|
|
465
|
+
return `${s}s`;
|
|
466
|
+
}
|
|
467
|
+
/** Relative time within the last 24h ("3 hours ago"), else DD/MM/YYYY. */
|
|
468
|
+
static formatRelative(time) {
|
|
469
|
+
if (FxUtils.isNull(time))
|
|
470
|
+
return '';
|
|
471
|
+
const m = moment.parseZone(time).local();
|
|
472
|
+
return moment().diff(m, 'hours') < 24 ? m.fromNow() : m.format('DD/MM/YYYY');
|
|
473
|
+
}
|
|
474
|
+
/**
|
|
475
|
+
* Normalize a snake_case + protobuf-Long API payload for camelCase binding:
|
|
476
|
+
* - genuine snake_case keys -> camelCase (data-as-key entries such as DLL
|
|
477
|
+
* names like "kernel32.dll" are left untouched by the strict regex);
|
|
478
|
+
* - Long-encoded numbers `{ high, low, unsigned }` -> plain number.
|
|
479
|
+
*/
|
|
480
|
+
static camelizeDeep(input) {
|
|
481
|
+
if (Array.isArray(input)) {
|
|
482
|
+
return input.map((v) => FxUtils.camelizeDeep(v));
|
|
483
|
+
}
|
|
484
|
+
if (input !== null && typeof input === 'object') {
|
|
485
|
+
const keys = Object.keys(input);
|
|
486
|
+
if (keys.length === 3 &&
|
|
487
|
+
keys.includes('high') &&
|
|
488
|
+
keys.includes('low') &&
|
|
489
|
+
keys.includes('unsigned')) {
|
|
490
|
+
const high = Number(input.high) || 0;
|
|
491
|
+
const low = Number(input.low) || 0;
|
|
492
|
+
return high * 4294967296 + (low >>> 0);
|
|
493
|
+
}
|
|
494
|
+
const out = {};
|
|
495
|
+
for (const k of keys) {
|
|
496
|
+
const nk = /^[a-z][a-z0-9]*(_[a-z0-9]+)+$/.test(k) ? _.camelCase(k) : k;
|
|
497
|
+
out[nk] = FxUtils.camelizeDeep(input[k]);
|
|
498
|
+
}
|
|
499
|
+
return out;
|
|
500
|
+
}
|
|
501
|
+
return input;
|
|
502
|
+
}
|
|
400
503
|
static formatSizeUnits(bytes) {
|
|
401
504
|
if (bytes >= 1073741824) {
|
|
402
505
|
return (bytes / 1073741824).toFixed(2) + ' GB';
|
|
@@ -424,6 +527,15 @@ class HttpWrapper {
|
|
|
424
527
|
this.http = http;
|
|
425
528
|
this.baseURL = baseURL;
|
|
426
529
|
}
|
|
530
|
+
/**
|
|
531
|
+
* Hook applied to every resolved response (get/post/put/delete and all verb
|
|
532
|
+
* helpers built on them). Default is identity; override to normalize a
|
|
533
|
+
* backend convention once (e.g. FxUtils.camelizeDeep for snake_case +
|
|
534
|
+
* protobuf-Long payloads) instead of wrapping each method.
|
|
535
|
+
*/
|
|
536
|
+
transformResponse(res) {
|
|
537
|
+
return res;
|
|
538
|
+
}
|
|
427
539
|
addCredentials(options = {}) {
|
|
428
540
|
return {
|
|
429
541
|
withCredentials: true,
|
|
@@ -437,16 +549,16 @@ class HttpWrapper {
|
|
|
437
549
|
...params,
|
|
438
550
|
},
|
|
439
551
|
};
|
|
440
|
-
return firstValueFrom(this.http.get(this.baseURL + url, options));
|
|
552
|
+
return this.transformResponse(await firstValueFrom(this.http.get(this.baseURL + url, options)));
|
|
441
553
|
}
|
|
442
554
|
async post(url, body = {}, options = {}) {
|
|
443
|
-
return firstValueFrom(this.http.post(this.baseURL + url, body, this.addCredentials(options)));
|
|
555
|
+
return this.transformResponse(await firstValueFrom(this.http.post(this.baseURL + url, body, this.addCredentials(options))));
|
|
444
556
|
}
|
|
445
557
|
async put(url, body, options = {}) {
|
|
446
|
-
return firstValueFrom(this.http.put(this.baseURL + url, body, this.addCredentials(options)));
|
|
558
|
+
return this.transformResponse(await firstValueFrom(this.http.put(this.baseURL + url, body, this.addCredentials(options))));
|
|
447
559
|
}
|
|
448
560
|
async delete(url, options = {}) {
|
|
449
|
-
return firstValueFrom(this.http.delete(this.baseURL + url, this.addCredentials(options)));
|
|
561
|
+
return this.transformResponse(await firstValueFrom(this.http.delete(this.baseURL + url, this.addCredentials(options))));
|
|
450
562
|
}
|
|
451
563
|
upload(url, body, options = {}) {
|
|
452
564
|
const req = new HttpRequest('POST', this.baseURL + url, body, {
|
|
@@ -526,7 +638,9 @@ class BaseTableComponent extends BaseComponent {
|
|
|
526
638
|
this.toastr = injector.get(FxToastrService);
|
|
527
639
|
}
|
|
528
640
|
ngOnInit() {
|
|
529
|
-
this.keywordChange
|
|
641
|
+
this.keywordChange$
|
|
642
|
+
.pipe(debounceTime(300), distinctUntilChanged(), takeUntilDestroyed(this.destroyRef))
|
|
643
|
+
.subscribe(async () => {
|
|
530
644
|
await this.refresh();
|
|
531
645
|
});
|
|
532
646
|
this.dataSourcePlaceholder = Array(this.pageSize).fill({});
|
|
@@ -535,7 +649,7 @@ class BaseTableComponent extends BaseComponent {
|
|
|
535
649
|
init() { }
|
|
536
650
|
ngAfterViewInit() {
|
|
537
651
|
if (this.paginator && this.paginator.page) {
|
|
538
|
-
this.paginator.page.subscribe((event) => {
|
|
652
|
+
this.paginator.page.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((event) => {
|
|
539
653
|
this.onPageChange(event);
|
|
540
654
|
});
|
|
541
655
|
}
|
|
@@ -548,23 +662,50 @@ class BaseTableComponent extends BaseComponent {
|
|
|
548
662
|
}
|
|
549
663
|
await this.fetch();
|
|
550
664
|
}
|
|
665
|
+
/**
|
|
666
|
+
* Where the list is fetched from. Default (null) keeps the legacy behavior
|
|
667
|
+
* of calling `api.search()` (GET /search) untouched. Subclasses whose list
|
|
668
|
+
* lives elsewhere override this instead of re-implementing fetch() — and
|
|
669
|
+
* keep the stale-response sequence guard for free.
|
|
670
|
+
*/
|
|
671
|
+
listRequest() {
|
|
672
|
+
return null;
|
|
673
|
+
}
|
|
674
|
+
/** Result keys the list/total are read from. Override alongside listRequest(). */
|
|
675
|
+
listKey = 'data';
|
|
676
|
+
totalKey = 'total';
|
|
551
677
|
setDataSource(result) {
|
|
552
|
-
this.dataSource.data = _.get(result,
|
|
553
|
-
this.total = _.get(result,
|
|
678
|
+
this.dataSource.data = _.get(result, this.listKey);
|
|
679
|
+
this.total = _.get(result, this.totalKey);
|
|
554
680
|
}
|
|
681
|
+
_fetchSeq = 0;
|
|
555
682
|
async fetch() {
|
|
683
|
+
// Sequence guard: without it a slow earlier request could resolve after
|
|
684
|
+
// a newer one and overwrite the table with stale data.
|
|
685
|
+
const seq = ++this._fetchSeq;
|
|
556
686
|
this.loading = true;
|
|
557
687
|
try {
|
|
558
688
|
const params = { page: (this.page ?? 0) + 1, pageSize: this.pageSize, ...this.filters };
|
|
559
|
-
const
|
|
689
|
+
const req = this.listRequest();
|
|
690
|
+
const result = req
|
|
691
|
+
? req.method === 'post'
|
|
692
|
+
? await this.api.post(req.url, params)
|
|
693
|
+
: await this.api.get(req.url, params)
|
|
694
|
+
: await this.api.search(params);
|
|
695
|
+
if (seq !== this._fetchSeq)
|
|
696
|
+
return;
|
|
560
697
|
this.setDataSource(result);
|
|
561
698
|
}
|
|
562
699
|
catch (err) {
|
|
700
|
+
if (seq !== this._fetchSeq)
|
|
701
|
+
return;
|
|
563
702
|
this.toastr.error(FxUtils.parseError(err));
|
|
564
703
|
}
|
|
565
704
|
finally {
|
|
566
|
-
this.
|
|
567
|
-
|
|
705
|
+
if (seq === this._fetchSeq) {
|
|
706
|
+
this.loading = false;
|
|
707
|
+
this.ref.detectChanges();
|
|
708
|
+
}
|
|
568
709
|
}
|
|
569
710
|
}
|
|
570
711
|
async onPageChange(event) {
|
|
@@ -596,11 +737,11 @@ class BaseTableComponent extends BaseComponent {
|
|
|
596
737
|
if (size == '100%') {
|
|
597
738
|
panelClass = 'fx-fullsize-dialog';
|
|
598
739
|
}
|
|
599
|
-
|
|
740
|
+
const instance = this.modal.open(dialog, {
|
|
600
741
|
panelClass: panelClass,
|
|
601
|
-
autoFocus:
|
|
742
|
+
autoFocus: 'first-tabbable',
|
|
602
743
|
width: size,
|
|
603
|
-
disableClose:
|
|
744
|
+
disableClose: conf.disableClose ?? false,
|
|
604
745
|
data: {
|
|
605
746
|
method: 'create',
|
|
606
747
|
data: this.empty(),
|
|
@@ -631,11 +772,11 @@ class BaseTableComponent extends BaseComponent {
|
|
|
631
772
|
if (size == '100%') {
|
|
632
773
|
panelClass = 'fx-fullsize-dialog';
|
|
633
774
|
}
|
|
634
|
-
|
|
775
|
+
const instance = this.modal.open(dialog, {
|
|
635
776
|
panelClass: panelClass,
|
|
636
|
-
autoFocus:
|
|
777
|
+
autoFocus: 'first-tabbable',
|
|
637
778
|
width: size,
|
|
638
|
-
disableClose:
|
|
779
|
+
disableClose: conf.disableClose ?? false,
|
|
639
780
|
data: {
|
|
640
781
|
method: 'update',
|
|
641
782
|
data: model,
|
|
@@ -666,11 +807,11 @@ class BaseTableComponent extends BaseComponent {
|
|
|
666
807
|
if (size == '100%') {
|
|
667
808
|
panelClass = 'fx-fullsize-dialog';
|
|
668
809
|
}
|
|
669
|
-
|
|
810
|
+
const instance = this.modal.open(dialog, {
|
|
670
811
|
panelClass: panelClass,
|
|
671
|
-
autoFocus:
|
|
812
|
+
autoFocus: 'first-tabbable',
|
|
672
813
|
width: size,
|
|
673
|
-
disableClose:
|
|
814
|
+
disableClose: conf.disableClose ?? false,
|
|
674
815
|
data: {
|
|
675
816
|
data: model,
|
|
676
817
|
title: conf.title,
|
|
@@ -685,16 +826,19 @@ class BaseTableComponent extends BaseComponent {
|
|
|
685
826
|
});
|
|
686
827
|
}
|
|
687
828
|
async delete(model) {
|
|
829
|
+
const conf = _.get(this.config, 'delete');
|
|
830
|
+
const dialog = _.get(conf, 'dialog');
|
|
831
|
+
if (_.isNil(conf) || _.isNil(dialog)) {
|
|
832
|
+
return;
|
|
833
|
+
}
|
|
688
834
|
if (this._lock) {
|
|
689
835
|
return;
|
|
690
836
|
}
|
|
691
837
|
this._lock = true;
|
|
692
838
|
try {
|
|
693
|
-
const conf = _.get(this.config, 'delete');
|
|
694
|
-
const dialog = _.get(conf, 'dialog');
|
|
695
839
|
const dialogRef = this.modal.open(dialog, {
|
|
696
840
|
panelClass: 'fx-dialog',
|
|
697
|
-
autoFocus:
|
|
841
|
+
autoFocus: 'first-tabbable',
|
|
698
842
|
width: '400px',
|
|
699
843
|
data: {
|
|
700
844
|
title: _.get(conf, 'title'),
|
|
@@ -746,7 +890,7 @@ class BaseTableComponent extends BaseComponent {
|
|
|
746
890
|
this.toastr.success(_.get(this.config, 'updateStatus.message', 'Update Success'));
|
|
747
891
|
}
|
|
748
892
|
catch (err) {
|
|
749
|
-
this.toastr.
|
|
893
|
+
this.toastr.error(FxUtils.parseError(err));
|
|
750
894
|
}
|
|
751
895
|
finally {
|
|
752
896
|
await this.refresh();
|
|
@@ -765,6 +909,93 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImpor
|
|
|
765
909
|
args: [MatTable]
|
|
766
910
|
}] } });
|
|
767
911
|
|
|
912
|
+
/**
|
|
913
|
+
* List + detail-pane counterpart of BaseTableComponent: the page keeps a
|
|
914
|
+
* selectable row list on one side and a lazily fetched, per-id-cached detail
|
|
915
|
+
* on the other. Pair it with `<fx-ui-master-detail>` which renders the
|
|
916
|
+
* two-column shell from this state.
|
|
917
|
+
*
|
|
918
|
+
* Subclasses declare where ids and details come from:
|
|
919
|
+
* protected override idKey = 'licenseId';
|
|
920
|
+
* protected override detailUrl(id: string) { return `/license/${id}`; }
|
|
921
|
+
* `detailUrl` returning null (the default) means the selected row itself is
|
|
922
|
+
* the detail and no request is made.
|
|
923
|
+
*/
|
|
924
|
+
class BaseMasterDetailComponent extends BaseTableComponent {
|
|
925
|
+
selectedId = null;
|
|
926
|
+
selected = null;
|
|
927
|
+
detailMap = new Map();
|
|
928
|
+
detailLoading = new Set();
|
|
929
|
+
// Explicit ctor: Ivy refuses to inherit a constructor whose `modal: any`
|
|
930
|
+
// parameter is not DI-resolvable (NG2016).
|
|
931
|
+
constructor(injector, api, modal, ref) {
|
|
932
|
+
super(injector, api, modal, ref);
|
|
933
|
+
}
|
|
934
|
+
/** Key the row id is read from. Override idOf() instead for composite ids. */
|
|
935
|
+
idKey = 'id';
|
|
936
|
+
idOf(row) {
|
|
937
|
+
return this.value(row, this.idKey, null);
|
|
938
|
+
}
|
|
939
|
+
detailUrl(id) {
|
|
940
|
+
return null;
|
|
941
|
+
}
|
|
942
|
+
/** What the detail pane shows: the fetched detail when cached, else the row. */
|
|
943
|
+
get detail() {
|
|
944
|
+
return (this.selectedId != null && this.detailMap.get(this.selectedId)) || this.selected;
|
|
945
|
+
}
|
|
946
|
+
get detailBusy() {
|
|
947
|
+
return this.selectedId != null && this.detailLoading.has(this.selectedId);
|
|
948
|
+
}
|
|
949
|
+
setDataSource(result) {
|
|
950
|
+
super.setDataSource(result);
|
|
951
|
+
this.autoSelectFirst();
|
|
952
|
+
}
|
|
953
|
+
/** Keep the previous selection when it survived the new page, else pick row 0. */
|
|
954
|
+
autoSelectFirst() {
|
|
955
|
+
const list = this.dataSource.data || [];
|
|
956
|
+
if (!list.length) {
|
|
957
|
+
this.selectedId = null;
|
|
958
|
+
this.selected = null;
|
|
959
|
+
return;
|
|
960
|
+
}
|
|
961
|
+
const still = list.find((row) => this.idOf(row) === this.selectedId);
|
|
962
|
+
this.selectItem(still ?? list[0]);
|
|
963
|
+
}
|
|
964
|
+
selectItem(row) {
|
|
965
|
+
const id = this.idOf(row);
|
|
966
|
+
this.selectedId = id;
|
|
967
|
+
this.selected = row;
|
|
968
|
+
if (id != null && !this.detailMap.has(id)) {
|
|
969
|
+
this.fetchDetail(id);
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
fetchDetail(id) {
|
|
973
|
+
const url = this.detailUrl(id);
|
|
974
|
+
if (!url || this.detailLoading.has(id)) {
|
|
975
|
+
return;
|
|
976
|
+
}
|
|
977
|
+
this.detailLoading.add(id);
|
|
978
|
+
this.api
|
|
979
|
+
.get(url)
|
|
980
|
+
.then((res) => {
|
|
981
|
+
// New Map identity so OnPush consumers of the pane see the change.
|
|
982
|
+
this.detailMap = new Map(this.detailMap).set(id, res);
|
|
983
|
+
})
|
|
984
|
+
.catch((err) => {
|
|
985
|
+
this.toastr.error(FxUtils.parseError(err));
|
|
986
|
+
})
|
|
987
|
+
.finally(() => {
|
|
988
|
+
this.detailLoading.delete(id);
|
|
989
|
+
this.ref.detectChanges();
|
|
990
|
+
});
|
|
991
|
+
}
|
|
992
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: BaseMasterDetailComponent, deps: "invalid", target: i0.ɵɵFactoryTarget.Directive });
|
|
993
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.4", type: BaseMasterDetailComponent, isStandalone: true, usesInheritance: true, ngImport: i0 });
|
|
994
|
+
}
|
|
995
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: BaseMasterDetailComponent, decorators: [{
|
|
996
|
+
type: Directive
|
|
997
|
+
}], ctorParameters: () => [{ type: i0.Injector }, { type: HttpWrapper }, { type: undefined }, { type: i0.ChangeDetectorRef }] });
|
|
998
|
+
|
|
768
999
|
class BaseDialogComponent extends BaseComponent {
|
|
769
1000
|
ref;
|
|
770
1001
|
bindings;
|
|
@@ -782,20 +1013,24 @@ class BaseDialogComponent extends BaseComponent {
|
|
|
782
1013
|
formChangesSubscription;
|
|
783
1014
|
onSubmit = true;
|
|
784
1015
|
constructor(ref, bindings, toastr, api, dialog) {
|
|
785
|
-
super(
|
|
1016
|
+
super();
|
|
786
1017
|
this.ref = ref;
|
|
787
1018
|
this.bindings = bindings;
|
|
788
1019
|
this.toastr = toastr;
|
|
789
1020
|
this.api = api;
|
|
790
1021
|
this.dialog = dialog;
|
|
791
|
-
|
|
1022
|
+
// Deep clone: a shallow clone let dialog edits mutate nested fields of
|
|
1023
|
+
// the live table row even when the user cancelled.
|
|
1024
|
+
this.model = _.cloneDeep(_.get(bindings, 'data'));
|
|
792
1025
|
this.method = _.get(bindings, 'method');
|
|
793
1026
|
this.title = _.get(bindings, 'title');
|
|
794
1027
|
this.disabled = _.get(bindings, 'disabled');
|
|
795
1028
|
this.message = _.get(bindings, 'message');
|
|
796
1029
|
this.resolve = _.get(bindings, 'resolve');
|
|
797
1030
|
}
|
|
1031
|
+
// eslint-disable-next-line @angular-eslint/no-empty-lifecycle-method -- subclasses across host apps call super lifecycle methods; removing these breaks them at runtime
|
|
798
1032
|
ngAfterViewInit() { }
|
|
1033
|
+
// eslint-disable-next-line @angular-eslint/no-empty-lifecycle-method -- see ngAfterViewInit note
|
|
799
1034
|
ngOnInit() { }
|
|
800
1035
|
async accept(form, params = null) {
|
|
801
1036
|
if (form.invalid) {
|
|
@@ -873,15 +1108,15 @@ class BaseDialogComponent extends BaseComponent {
|
|
|
873
1108
|
validate() {
|
|
874
1109
|
return true;
|
|
875
1110
|
}
|
|
876
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: BaseDialogComponent, deps: [{ token: i1$
|
|
1111
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: BaseDialogComponent, deps: [{ token: i1$2.MatDialogRef }, { token: MAT_DIALOG_DATA }, { token: FxToastrService }, { token: HttpWrapper }, { token: i1$2.MatDialog }], target: i0.ɵɵFactoryTarget.Directive });
|
|
877
1112
|
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.4", type: BaseDialogComponent, isStandalone: true, viewQueries: [{ propertyName: "ngForm", first: true, predicate: ["f"], descendants: true, static: true }], usesInheritance: true, ngImport: i0 });
|
|
878
1113
|
}
|
|
879
1114
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: BaseDialogComponent, decorators: [{
|
|
880
1115
|
type: Directive
|
|
881
|
-
}], ctorParameters: () => [{ type: i1$
|
|
1116
|
+
}], ctorParameters: () => [{ type: i1$2.MatDialogRef }, { type: undefined, decorators: [{
|
|
882
1117
|
type: Inject,
|
|
883
1118
|
args: [MAT_DIALOG_DATA]
|
|
884
|
-
}] }, { type: FxToastrService }, { type: HttpWrapper }, { type: i1$
|
|
1119
|
+
}] }, { type: FxToastrService }, { type: HttpWrapper }, { type: i1$2.MatDialog }], propDecorators: { ngForm: [{
|
|
885
1120
|
type: ViewChild,
|
|
886
1121
|
args: ['f', { static: true }]
|
|
887
1122
|
}] } });
|
|
@@ -923,6 +1158,314 @@ class BaseResolver {
|
|
|
923
1158
|
}
|
|
924
1159
|
}
|
|
925
1160
|
|
|
1161
|
+
class FxStorageService {
|
|
1162
|
+
resolve(key, value) {
|
|
1163
|
+
if (FxUtils.isNotNull(value)) {
|
|
1164
|
+
localStorage.setItem(key, JSON.stringify(value));
|
|
1165
|
+
}
|
|
1166
|
+
const loc = localStorage.getItem(key);
|
|
1167
|
+
return loc ? JSON.parse(loc) : undefined;
|
|
1168
|
+
}
|
|
1169
|
+
remove(key) {
|
|
1170
|
+
localStorage.removeItem(key);
|
|
1171
|
+
}
|
|
1172
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: FxStorageService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1173
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: FxStorageService, providedIn: 'root' });
|
|
1174
|
+
}
|
|
1175
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: FxStorageService, decorators: [{
|
|
1176
|
+
type: Injectable,
|
|
1177
|
+
args: [{
|
|
1178
|
+
providedIn: 'root',
|
|
1179
|
+
}]
|
|
1180
|
+
}] });
|
|
1181
|
+
|
|
1182
|
+
/**
|
|
1183
|
+
* Functional replacement for the copy-pasted list-resolver class:
|
|
1184
|
+
* starts the loading overlay, runs one API request, and on failure stops the
|
|
1185
|
+
* overlay and shows a parsed error toast — the exact convention every
|
|
1186
|
+
* BaseResolver subclass hand-rolls today. BaseResolver stays for existing
|
|
1187
|
+
* classes; new routes should use this factory.
|
|
1188
|
+
*
|
|
1189
|
+
* Usage:
|
|
1190
|
+
* resolve: { histories: fxListResolver(ScanManagementApi, (route, state, ctx) =>
|
|
1191
|
+
* ({ url: `/${route.paramMap.get('id')}/history`, params: { page: 1, pageSize: ctx.pageSize } })) }
|
|
1192
|
+
*/
|
|
1193
|
+
function fxListResolver(apiType, request) {
|
|
1194
|
+
return async (route, state) => {
|
|
1195
|
+
const api = inject(apiType);
|
|
1196
|
+
const loading = inject(FxLoadingService);
|
|
1197
|
+
const toastr = inject(FxToastrService);
|
|
1198
|
+
const storage = inject(FxStorageService);
|
|
1199
|
+
try {
|
|
1200
|
+
loading.start();
|
|
1201
|
+
const ctx = { pageSize: storage.resolve(state.url) || 10 };
|
|
1202
|
+
const req = request(route, state, ctx);
|
|
1203
|
+
if ((req.method ?? 'get') === 'post') {
|
|
1204
|
+
return await api.post(req.url, req.body ?? req.params ?? {});
|
|
1205
|
+
}
|
|
1206
|
+
return await api.get(req.url, req.params);
|
|
1207
|
+
}
|
|
1208
|
+
catch (err) {
|
|
1209
|
+
loading.stop();
|
|
1210
|
+
toastr.error(FxUtils.parseError(err), 'Error');
|
|
1211
|
+
return null;
|
|
1212
|
+
}
|
|
1213
|
+
};
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
/**
|
|
1217
|
+
* Factory for the 11-line HttpWrapper subclasses copy-pasted per endpoint.
|
|
1218
|
+
* Returns a distinct injectable class per call (so constructor injection by
|
|
1219
|
+
* type keeps working) without needing a decorator:
|
|
1220
|
+
*
|
|
1221
|
+
* export const ScanManagementApi = createApi(() => `${environment.baseService}/scarf/scan`);
|
|
1222
|
+
* // module: providers: [ScanManagementApi.provider]
|
|
1223
|
+
* // usage: constructor(private api: InstanceType<typeof ScanManagementApi>)
|
|
1224
|
+
*
|
|
1225
|
+
* `baseURL` accepts a string or a lazy factory (for environment-derived URLs).
|
|
1226
|
+
* An optional `transform` plugs into HttpWrapper.transformResponse — e.g.
|
|
1227
|
+
* FxUtils.camelizeDeep for snake_case/Long-encoded backends.
|
|
1228
|
+
*
|
|
1229
|
+
* IMPORTANT — when subclassing (`@Injectable() class X extends createApi(...)`),
|
|
1230
|
+
* declare an explicit `constructor(http: HttpClient) { super(http); }`.
|
|
1231
|
+
* The generated base carries no Angular factory metadata, so an inherited
|
|
1232
|
+
* constructor makes Ivy fall back to `new X()` with NO arguments: the class
|
|
1233
|
+
* silently gets `http = undefined` and every call fails at runtime while the
|
|
1234
|
+
* build stays green.
|
|
1235
|
+
*/
|
|
1236
|
+
function createApi(baseURL, transform) {
|
|
1237
|
+
class GeneratedApi extends HttpWrapper {
|
|
1238
|
+
static provider = {
|
|
1239
|
+
provide: GeneratedApi,
|
|
1240
|
+
useFactory: (http) => new GeneratedApi(http),
|
|
1241
|
+
deps: [HttpClient],
|
|
1242
|
+
};
|
|
1243
|
+
constructor(http) {
|
|
1244
|
+
super(http, typeof baseURL === 'function' ? baseURL() : baseURL);
|
|
1245
|
+
}
|
|
1246
|
+
transformResponse(res) {
|
|
1247
|
+
return transform ? transform(res) : res;
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
return GeneratedApi;
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
/**
|
|
1254
|
+
* Live-progress plumbing for server-paginated tables fed by hub events:
|
|
1255
|
+
* events matching a visible row are merged in place; events for rows that
|
|
1256
|
+
* are not in the list yet (a just-created record whose list read lags) are
|
|
1257
|
+
* buffered by key and replayed after a debounced, idle-capped background
|
|
1258
|
+
* re-fetch that merges by stable identity.
|
|
1259
|
+
*
|
|
1260
|
+
* The domain contract (event name, payload parsing, terminal semantics) is
|
|
1261
|
+
* injected via options — nothing app-specific lives here.
|
|
1262
|
+
*/
|
|
1263
|
+
class RealtimeTableSync {
|
|
1264
|
+
notifications;
|
|
1265
|
+
options;
|
|
1266
|
+
pending = new Map();
|
|
1267
|
+
syncTimer = null;
|
|
1268
|
+
idleSyncs = 0;
|
|
1269
|
+
stopped = false;
|
|
1270
|
+
subscription = null;
|
|
1271
|
+
constructor(notifications, options) {
|
|
1272
|
+
this.notifications = notifications;
|
|
1273
|
+
this.options = options;
|
|
1274
|
+
}
|
|
1275
|
+
/**
|
|
1276
|
+
* Subscribe to the hub event. Pass a DestroyRef to tie teardown to a
|
|
1277
|
+
* component lifecycle; stop() is still available for manual control.
|
|
1278
|
+
*/
|
|
1279
|
+
start(destroyRef) {
|
|
1280
|
+
const source = this.notifications.on$(this.options.event);
|
|
1281
|
+
const piped = destroyRef ? source.pipe(takeUntilDestroyed(destroyRef)) : source;
|
|
1282
|
+
this.stopped = false;
|
|
1283
|
+
this.subscription = piped.subscribe((raw) => this.handleEvent(raw));
|
|
1284
|
+
if (destroyRef) {
|
|
1285
|
+
destroyRef.onDestroy(() => this.stop());
|
|
1286
|
+
}
|
|
1287
|
+
return this;
|
|
1288
|
+
}
|
|
1289
|
+
stop() {
|
|
1290
|
+
this.stopped = true;
|
|
1291
|
+
this.subscription?.unsubscribe();
|
|
1292
|
+
this.subscription = null;
|
|
1293
|
+
if (this.syncTimer) {
|
|
1294
|
+
clearTimeout(this.syncTimer);
|
|
1295
|
+
this.syncTimer = null;
|
|
1296
|
+
}
|
|
1297
|
+
this.pending.clear();
|
|
1298
|
+
}
|
|
1299
|
+
/**
|
|
1300
|
+
* Replay buffered events onto the current rows (call from setDataSource so
|
|
1301
|
+
* a regular refresh also applies buffered progress). Returns true if
|
|
1302
|
+
* anything was applied.
|
|
1303
|
+
*/
|
|
1304
|
+
applyPending() {
|
|
1305
|
+
if (!this.pending.size)
|
|
1306
|
+
return false;
|
|
1307
|
+
let changed = false;
|
|
1308
|
+
for (const row of this.options.getRows()) {
|
|
1309
|
+
const key = this.options.matchKey(row);
|
|
1310
|
+
const event = this.pending.get(key);
|
|
1311
|
+
if (!event)
|
|
1312
|
+
continue;
|
|
1313
|
+
Object.assign(row, event.patch);
|
|
1314
|
+
this.pending.delete(key);
|
|
1315
|
+
changed = true;
|
|
1316
|
+
this.options.onApplied?.(row, event);
|
|
1317
|
+
}
|
|
1318
|
+
if (changed) {
|
|
1319
|
+
this.options.setRows([...this.options.getRows()]);
|
|
1320
|
+
}
|
|
1321
|
+
return changed;
|
|
1322
|
+
}
|
|
1323
|
+
handleEvent(raw) {
|
|
1324
|
+
const event = this.options.parse(raw);
|
|
1325
|
+
if (!event)
|
|
1326
|
+
return;
|
|
1327
|
+
const row = this.options.getRows().find((r) => this.options.matchKey(r) === event.key);
|
|
1328
|
+
if (!row) {
|
|
1329
|
+
// Not in the list yet (e.g. just-created record whose list read lags):
|
|
1330
|
+
// keep only the latest state per key and schedule a background sync.
|
|
1331
|
+
this.pending.set(event.key, event);
|
|
1332
|
+
this.scheduleSync();
|
|
1333
|
+
return;
|
|
1334
|
+
}
|
|
1335
|
+
this.idleSyncs = 0;
|
|
1336
|
+
Object.assign(row, event.patch);
|
|
1337
|
+
this.options.setRows([...this.options.getRows()]);
|
|
1338
|
+
this.options.onApplied?.(row, event);
|
|
1339
|
+
}
|
|
1340
|
+
scheduleSync() {
|
|
1341
|
+
const idleCap = this.options.idleCap ?? 3;
|
|
1342
|
+
if (this.syncTimer || this.stopped || this.idleSyncs >= idleCap)
|
|
1343
|
+
return;
|
|
1344
|
+
this.syncTimer = setTimeout(async () => {
|
|
1345
|
+
this.syncTimer = null;
|
|
1346
|
+
await this.syncFromServer();
|
|
1347
|
+
}, this.options.debounceMs ?? 1200);
|
|
1348
|
+
}
|
|
1349
|
+
async syncFromServer() {
|
|
1350
|
+
try {
|
|
1351
|
+
const result = await this.options.fetchList();
|
|
1352
|
+
if (this.stopped || !result)
|
|
1353
|
+
return;
|
|
1354
|
+
const existingByIdentity = new Map(this.options.getRows().map((r) => [this.options.identityKey(r), r]));
|
|
1355
|
+
const merged = result.rows.map((item) => {
|
|
1356
|
+
const existing = existingByIdentity.get(this.options.identityKey(item));
|
|
1357
|
+
if (existing) {
|
|
1358
|
+
Object.assign(existing, item); // reuse the object → stable identity
|
|
1359
|
+
return existing;
|
|
1360
|
+
}
|
|
1361
|
+
return item;
|
|
1362
|
+
});
|
|
1363
|
+
this.options.setRows(merged);
|
|
1364
|
+
if (result.total !== undefined) {
|
|
1365
|
+
this.options.onTotal?.(result.total);
|
|
1366
|
+
}
|
|
1367
|
+
const applied = this.applyPending();
|
|
1368
|
+
this.idleSyncs = applied ? 0 : this.idleSyncs + 1;
|
|
1369
|
+
if (this.pending.size) {
|
|
1370
|
+
this.scheduleSync();
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
catch {
|
|
1374
|
+
// Background sync is best-effort; the next event retries.
|
|
1375
|
+
this.idleSyncs += 1;
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1380
|
+
const NEUTRAL_TEXT = 'text-text-secondary';
|
|
1381
|
+
const NEUTRAL_BG = 'bg-bg-hover';
|
|
1382
|
+
const TONE_TEXT = {
|
|
1383
|
+
critical: 'text-critical',
|
|
1384
|
+
danger: 'text-danger',
|
|
1385
|
+
warning: 'text-warning',
|
|
1386
|
+
success: 'text-success',
|
|
1387
|
+
information: 'text-information',
|
|
1388
|
+
};
|
|
1389
|
+
const TONE_BG = {
|
|
1390
|
+
critical: 'bg-critical',
|
|
1391
|
+
danger: 'bg-danger',
|
|
1392
|
+
warning: 'bg-warning',
|
|
1393
|
+
success: 'bg-success',
|
|
1394
|
+
information: 'bg-information',
|
|
1395
|
+
};
|
|
1396
|
+
const TONE_BADGE = {
|
|
1397
|
+
critical: 'bg-critical/10 text-critical',
|
|
1398
|
+
danger: 'bg-danger/10 text-danger',
|
|
1399
|
+
warning: 'bg-warning/10 text-warning',
|
|
1400
|
+
success: 'bg-success/10 text-success',
|
|
1401
|
+
information: 'bg-information/10 text-information',
|
|
1402
|
+
};
|
|
1403
|
+
function hasVal(v) {
|
|
1404
|
+
if (_.isNil(v))
|
|
1405
|
+
return false;
|
|
1406
|
+
const s = String(v).trim();
|
|
1407
|
+
return s !== '' && s !== '-' && s !== '--';
|
|
1408
|
+
}
|
|
1409
|
+
/** Severity / risk-level string → tone (Critical & High → danger, Medium → warning, Low → success). */
|
|
1410
|
+
function severityTone(value) {
|
|
1411
|
+
if (!hasVal(value))
|
|
1412
|
+
return null;
|
|
1413
|
+
const t = FxUtils.getTagClass(String(value), true);
|
|
1414
|
+
return t === 'default' || t === 'discovery' ? null : t;
|
|
1415
|
+
}
|
|
1416
|
+
/**
|
|
1417
|
+
* CVSS-style 0–10 score → tone, following the CVSS v3 qualitative bands
|
|
1418
|
+
* (≥9 Critical, ≥7 High, ≥4 Medium, else Low). Backends occasionally send a
|
|
1419
|
+
* 0–100 scale, so values > 10 are normalised down by /10.
|
|
1420
|
+
*/
|
|
1421
|
+
function scoreTone(score) {
|
|
1422
|
+
let n = Number(score);
|
|
1423
|
+
if (!_.isFinite(n) || n <= 0)
|
|
1424
|
+
return null;
|
|
1425
|
+
if (n > 10)
|
|
1426
|
+
n = n / 10;
|
|
1427
|
+
if (n >= 9)
|
|
1428
|
+
return 'critical';
|
|
1429
|
+
if (n >= 7)
|
|
1430
|
+
return 'danger';
|
|
1431
|
+
if (n >= 4)
|
|
1432
|
+
return 'warning';
|
|
1433
|
+
return 'success';
|
|
1434
|
+
}
|
|
1435
|
+
/**
|
|
1436
|
+
* Count of "bad" things → tone: 0 is good (success), anything positive is bad.
|
|
1437
|
+
* `positiveTone` sets how severe a non-zero count reads (default `danger`;
|
|
1438
|
+
* pass `critical` for the worst metrics, `warning` for milder ones).
|
|
1439
|
+
* Non-numeric values (e.g. "--") return null → neutral.
|
|
1440
|
+
*/
|
|
1441
|
+
function countTone(count, positiveTone = 'danger') {
|
|
1442
|
+
const n = Number(count);
|
|
1443
|
+
if (!_.isFinite(n))
|
|
1444
|
+
return null;
|
|
1445
|
+
return n > 0 ? positiveTone : 'success';
|
|
1446
|
+
}
|
|
1447
|
+
/** Tone → text colour class, with a neutral fallback when there is no value. */
|
|
1448
|
+
function toneText(tone) {
|
|
1449
|
+
return tone ? TONE_TEXT[tone] : NEUTRAL_TEXT;
|
|
1450
|
+
}
|
|
1451
|
+
/** Tone → background colour class, with a neutral fallback when there is no value. */
|
|
1452
|
+
function toneBg(tone) {
|
|
1453
|
+
return tone ? TONE_BG[tone] : NEUTRAL_BG;
|
|
1454
|
+
}
|
|
1455
|
+
/** Tone → soft pill/badge classes (tinted bg + matching text), neutral fallback. */
|
|
1456
|
+
function toneBadge(tone) {
|
|
1457
|
+
return tone ? TONE_BADGE[tone] : 'bg-bg-hover text-text-secondary';
|
|
1458
|
+
}
|
|
1459
|
+
/**
|
|
1460
|
+
* Tone → resolved CSS colour string for ECharts / canvas where a Tailwind
|
|
1461
|
+
* class can't be used (e.g. per-slice pie `itemStyle.color`). Emits the
|
|
1462
|
+
* legacy comma syntax — see FxUtils.convertColorFromVariable.
|
|
1463
|
+
*/
|
|
1464
|
+
function toneColor(tone, alpha = 1) {
|
|
1465
|
+
const name = tone ? `--${tone}` : '--text-secondary';
|
|
1466
|
+
return FxUtils.convertColorFromVariable(name, alpha, { legacyCommaSyntax: true });
|
|
1467
|
+
}
|
|
1468
|
+
|
|
926
1469
|
class FxValidators {
|
|
927
1470
|
/**
|
|
928
1471
|
* Name fields: firstName, lastName, groupName, projectName, displayName
|
|
@@ -974,7 +1517,7 @@ class FxValidators {
|
|
|
974
1517
|
static validateCode(value) {
|
|
975
1518
|
if (!value || value.trim() === '')
|
|
976
1519
|
return null;
|
|
977
|
-
const regex = /^[a-zA-Z0-9_
|
|
1520
|
+
const regex = /^[a-zA-Z0-9_-]{1,50}$/;
|
|
978
1521
|
return regex.test(value) ? null : { invalidCode: 'Only letters, numbers, hyphens, and underscores are allowed (max 50 characters)' };
|
|
979
1522
|
}
|
|
980
1523
|
/**
|
|
@@ -984,7 +1527,7 @@ class FxValidators {
|
|
|
984
1527
|
static validateDomain(value) {
|
|
985
1528
|
if (!value || value.trim() === '')
|
|
986
1529
|
return null;
|
|
987
|
-
const regex = /^([a-zA-Z0-9]([a-zA-Z0-9
|
|
1530
|
+
const regex = /^([a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}(:\d{1,5})?$/;
|
|
988
1531
|
return regex.test(value) ? null : { invalidDomain: 'Please enter a valid domain (e.g., mail.example.com)' };
|
|
989
1532
|
}
|
|
990
1533
|
/**
|
|
@@ -1109,13 +1652,13 @@ class TranslationService {
|
|
|
1109
1652
|
onLanguageChange(callback) {
|
|
1110
1653
|
this.currentLang$.subscribe(callback);
|
|
1111
1654
|
}
|
|
1112
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: TranslationService, deps: [{ token: i1$
|
|
1655
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: TranslationService, deps: [{ token: i1$3.TranslateService }, { token: i2.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1113
1656
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: TranslationService, providedIn: 'root' });
|
|
1114
1657
|
}
|
|
1115
1658
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: TranslationService, decorators: [{
|
|
1116
1659
|
type: Injectable,
|
|
1117
1660
|
args: [{ providedIn: 'root' }]
|
|
1118
|
-
}], ctorParameters: () => [{ type: i1$
|
|
1661
|
+
}], ctorParameters: () => [{ type: i1$3.TranslateService }, { type: i2.HttpClient }] });
|
|
1119
1662
|
|
|
1120
1663
|
class PermissionService {
|
|
1121
1664
|
permissions = [];
|
|
@@ -1160,6 +1703,7 @@ class HasPermissionDirective {
|
|
|
1160
1703
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: HasPermissionDirective, decorators: [{
|
|
1161
1704
|
type: Directive,
|
|
1162
1705
|
args: [{
|
|
1706
|
+
// eslint-disable-next-line @angular-eslint/directive-selector -- renaming is a breaking change for all host-app templates
|
|
1163
1707
|
selector: '[hasPermission]'
|
|
1164
1708
|
}]
|
|
1165
1709
|
}], ctorParameters: () => [{ type: i0.TemplateRef }, { type: i0.ViewContainerRef }, { type: PermissionService }], propDecorators: { hasPermission: [{
|
|
@@ -1196,15 +1740,16 @@ class TrimOnBlurDirective {
|
|
|
1196
1740
|
native.dispatchEvent(ev);
|
|
1197
1741
|
}
|
|
1198
1742
|
}
|
|
1199
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: TrimOnBlurDirective, deps: [{ token: i0.ElementRef }, { token:
|
|
1743
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: TrimOnBlurDirective, deps: [{ token: i0.ElementRef }, { token: i2$1.NgControl }], target: i0.ɵɵFactoryTarget.Directive });
|
|
1200
1744
|
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.4", type: TrimOnBlurDirective, isStandalone: true, selector: "[appTrimOnBlur]", inputs: { collapseWhitespace: ["appTrimOnBlurCollapse", "collapseWhitespace"] }, host: { listeners: { "blur": "onBlur()" } }, ngImport: i0 });
|
|
1201
1745
|
}
|
|
1202
1746
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: TrimOnBlurDirective, decorators: [{
|
|
1203
1747
|
type: Directive,
|
|
1204
1748
|
args: [{
|
|
1749
|
+
// eslint-disable-next-line @angular-eslint/directive-selector -- renaming is a breaking change for all host-app templates
|
|
1205
1750
|
selector: '[appTrimOnBlur]'
|
|
1206
1751
|
}]
|
|
1207
|
-
}], ctorParameters: () => [{ type: i0.ElementRef }, { type:
|
|
1752
|
+
}], ctorParameters: () => [{ type: i0.ElementRef }, { type: i2$1.NgControl }], propDecorators: { collapseWhitespace: [{
|
|
1208
1753
|
type: Input,
|
|
1209
1754
|
args: ['appTrimOnBlurCollapse']
|
|
1210
1755
|
}], onBlur: [{
|
|
@@ -1237,27 +1782,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImpor
|
|
|
1237
1782
|
}]
|
|
1238
1783
|
}], ctorParameters: () => [{ type: PermissionService }, { type: i1$4.Router }] });
|
|
1239
1784
|
|
|
1240
|
-
class FxStorageService {
|
|
1241
|
-
resolve(key, value) {
|
|
1242
|
-
if (FxUtils.isNotNull(value)) {
|
|
1243
|
-
localStorage.setItem(key, JSON.stringify(value));
|
|
1244
|
-
}
|
|
1245
|
-
const loc = localStorage.getItem(key);
|
|
1246
|
-
return loc ? JSON.parse(loc) : undefined;
|
|
1247
|
-
}
|
|
1248
|
-
remove(key) {
|
|
1249
|
-
localStorage.removeItem(key);
|
|
1250
|
-
}
|
|
1251
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: FxStorageService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1252
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: FxStorageService, providedIn: 'root' });
|
|
1253
|
-
}
|
|
1254
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: FxStorageService, decorators: [{
|
|
1255
|
-
type: Injectable,
|
|
1256
|
-
args: [{
|
|
1257
|
-
providedIn: 'root',
|
|
1258
|
-
}]
|
|
1259
|
-
}] });
|
|
1260
|
-
|
|
1261
1785
|
class AuthStateService {
|
|
1262
1786
|
_isLoggedIn$ = new BehaviorSubject(null);
|
|
1263
1787
|
isLoggedIn$ = this._isLoggedIn$.asObservable();
|
|
@@ -1288,7 +1812,13 @@ class AuthInterceptor {
|
|
|
1288
1812
|
this.authState = authState;
|
|
1289
1813
|
}
|
|
1290
1814
|
intercept(req, next) {
|
|
1291
|
-
|
|
1815
|
+
// Multi-tenant: all tenant domains hit one shared API endpoint, so the
|
|
1816
|
+
// backend can no longer resolve the tenant from the Host header. Send the
|
|
1817
|
+
// originating domain explicitly for tenant resolution.
|
|
1818
|
+
const cloned = req.clone({
|
|
1819
|
+
withCredentials: true,
|
|
1820
|
+
setHeaders: { 'X-Tenant-Domain': window.location.hostname },
|
|
1821
|
+
});
|
|
1292
1822
|
return next.handle(cloned).pipe(catchError((error) => {
|
|
1293
1823
|
if (error.status === 401 && !this.handlingUnauthorized) {
|
|
1294
1824
|
this.handlingUnauthorized = true;
|
|
@@ -1388,7 +1918,7 @@ class QuillStyleLoaderService {
|
|
|
1388
1918
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: QuillStyleLoaderService, decorators: [{
|
|
1389
1919
|
type: Injectable,
|
|
1390
1920
|
args: [{ providedIn: 'root' }]
|
|
1391
|
-
}], ctorParameters: () => [{ type:
|
|
1921
|
+
}], ctorParameters: () => [{ type: undefined, decorators: [{
|
|
1392
1922
|
type: Inject,
|
|
1393
1923
|
args: [PLATFORM_ID]
|
|
1394
1924
|
}] }] });
|
|
@@ -1448,20 +1978,43 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImpor
|
|
|
1448
1978
|
class NotificationService {
|
|
1449
1979
|
hubConnection = null;
|
|
1450
1980
|
connectionStatus = 'disconnected';
|
|
1451
|
-
constructor() { }
|
|
1452
1981
|
/**
|
|
1453
|
-
*
|
|
1454
|
-
*
|
|
1455
|
-
*
|
|
1982
|
+
* Single source of truth for subscriptions. The hub connection gets exactly
|
|
1983
|
+
* one dispatcher per event, so:
|
|
1984
|
+
* - off(event, callback) can remove one subscriber without nuking the rest
|
|
1985
|
+
* (hubConnection.off(event) removes every handler for the event);
|
|
1986
|
+
* - handlers registered before connect() are attached once connected;
|
|
1987
|
+
* - handlers survive a re-connect that builds a new HubConnection.
|
|
1988
|
+
*/
|
|
1989
|
+
handlers = new Map();
|
|
1990
|
+
connectPromise = null;
|
|
1991
|
+
lastHubUrl = null;
|
|
1992
|
+
lastOptions;
|
|
1993
|
+
manualDisconnect = false;
|
|
1994
|
+
restartAttempts = 0;
|
|
1995
|
+
restartTimer = null;
|
|
1996
|
+
/**
|
|
1997
|
+
* Connect to SignalR Hub. Overlapping calls share the same in-flight
|
|
1998
|
+
* promise instead of building two competing connections.
|
|
1456
1999
|
*/
|
|
1457
2000
|
async connect(hubUrl, options) {
|
|
1458
2001
|
if (this.hubConnection && this.connectionStatus === 'connected') {
|
|
1459
|
-
console.log('Already connected to SignalR hub');
|
|
1460
2002
|
return;
|
|
1461
2003
|
}
|
|
2004
|
+
if (this.connectPromise) {
|
|
2005
|
+
return this.connectPromise;
|
|
2006
|
+
}
|
|
2007
|
+
this.connectPromise = this.doConnect(hubUrl, options).finally(() => {
|
|
2008
|
+
this.connectPromise = null;
|
|
2009
|
+
});
|
|
2010
|
+
return this.connectPromise;
|
|
2011
|
+
}
|
|
2012
|
+
async doConnect(hubUrl, options) {
|
|
1462
2013
|
try {
|
|
1463
2014
|
this.connectionStatus = 'connecting';
|
|
1464
|
-
|
|
2015
|
+
this.manualDisconnect = false;
|
|
2016
|
+
this.lastHubUrl = hubUrl;
|
|
2017
|
+
this.lastOptions = options;
|
|
1465
2018
|
const connectionOptions = {
|
|
1466
2019
|
skipNegotiation: true,
|
|
1467
2020
|
transport: signalR.HttpTransportType.WebSockets,
|
|
@@ -1473,29 +2026,33 @@ class NotificationService {
|
|
|
1473
2026
|
.withAutomaticReconnect()
|
|
1474
2027
|
.configureLogging(signalR.LogLevel.Information)
|
|
1475
2028
|
.build();
|
|
1476
|
-
//
|
|
2029
|
+
// A new HubConnection knows nothing about previous listeners:
|
|
2030
|
+
// re-attach one dispatcher per known event.
|
|
2031
|
+
for (const eventName of this.handlers.keys()) {
|
|
2032
|
+
this.attachDispatcher(eventName);
|
|
2033
|
+
}
|
|
2034
|
+
// Fires when the automatic-reconnect policy is exhausted (or the
|
|
2035
|
+
// server closes the socket). Restart with backoff unless the app
|
|
2036
|
+
// disconnected on purpose.
|
|
1477
2037
|
this.hubConnection.onclose((error) => {
|
|
1478
2038
|
this.connectionStatus = 'disconnected';
|
|
1479
2039
|
if (error) {
|
|
1480
2040
|
console.error('SignalR connection closed with error:', error);
|
|
1481
2041
|
}
|
|
1482
|
-
|
|
1483
|
-
|
|
2042
|
+
if (!this.manualDisconnect) {
|
|
2043
|
+
this.scheduleRestart();
|
|
1484
2044
|
}
|
|
1485
2045
|
});
|
|
1486
|
-
|
|
1487
|
-
this.hubConnection.onreconnecting((error) => {
|
|
2046
|
+
this.hubConnection.onreconnecting(() => {
|
|
1488
2047
|
this.connectionStatus = 'connecting';
|
|
1489
|
-
console.log('SignalR reconnecting...', error);
|
|
1490
2048
|
});
|
|
1491
|
-
|
|
1492
|
-
this.hubConnection.onreconnected((connectionId) => {
|
|
2049
|
+
this.hubConnection.onreconnected(() => {
|
|
1493
2050
|
this.connectionStatus = 'connected';
|
|
1494
|
-
|
|
2051
|
+
this.restartAttempts = 0;
|
|
1495
2052
|
});
|
|
1496
2053
|
await this.hubConnection.start();
|
|
1497
2054
|
this.connectionStatus = 'connected';
|
|
1498
|
-
|
|
2055
|
+
this.restartAttempts = 0;
|
|
1499
2056
|
}
|
|
1500
2057
|
catch (error) {
|
|
1501
2058
|
this.connectionStatus = 'disconnected';
|
|
@@ -1503,15 +2060,34 @@ class NotificationService {
|
|
|
1503
2060
|
throw error;
|
|
1504
2061
|
}
|
|
1505
2062
|
}
|
|
2063
|
+
scheduleRestart() {
|
|
2064
|
+
if (this.restartTimer || !this.lastHubUrl) {
|
|
2065
|
+
return;
|
|
2066
|
+
}
|
|
2067
|
+
const delay = Math.min(30_000, 1_000 * 2 ** this.restartAttempts++);
|
|
2068
|
+
this.restartTimer = setTimeout(async () => {
|
|
2069
|
+
this.restartTimer = null;
|
|
2070
|
+
try {
|
|
2071
|
+
await this.connect(this.lastHubUrl, this.lastOptions);
|
|
2072
|
+
}
|
|
2073
|
+
catch {
|
|
2074
|
+
this.scheduleRestart();
|
|
2075
|
+
}
|
|
2076
|
+
}, delay);
|
|
2077
|
+
}
|
|
1506
2078
|
/**
|
|
1507
|
-
* Disconnect from SignalR Hub
|
|
2079
|
+
* Disconnect from SignalR Hub. Stops any pending auto-restart.
|
|
1508
2080
|
*/
|
|
1509
2081
|
async disconnect() {
|
|
2082
|
+
this.manualDisconnect = true;
|
|
2083
|
+
if (this.restartTimer) {
|
|
2084
|
+
clearTimeout(this.restartTimer);
|
|
2085
|
+
this.restartTimer = null;
|
|
2086
|
+
}
|
|
1510
2087
|
if (this.hubConnection) {
|
|
1511
2088
|
try {
|
|
1512
2089
|
await this.hubConnection.stop();
|
|
1513
2090
|
this.connectionStatus = 'disconnected';
|
|
1514
|
-
console.log('SignalR disconnected successfully');
|
|
1515
2091
|
}
|
|
1516
2092
|
catch (error) {
|
|
1517
2093
|
console.error('Error disconnecting from SignalR hub:', error);
|
|
@@ -1519,28 +2095,73 @@ class NotificationService {
|
|
|
1519
2095
|
}
|
|
1520
2096
|
}
|
|
1521
2097
|
/**
|
|
1522
|
-
* Listen to a specific event from the server
|
|
1523
|
-
*
|
|
1524
|
-
* @param callback - Callback function to execute when event is received
|
|
2098
|
+
* Listen to a specific event from the server. Safe to call before
|
|
2099
|
+
* connect(): the handler is attached once the connection is up.
|
|
1525
2100
|
*/
|
|
1526
2101
|
on(eventName, callback) {
|
|
1527
|
-
|
|
1528
|
-
|
|
2102
|
+
let set = this.handlers.get(eventName);
|
|
2103
|
+
if (!set) {
|
|
2104
|
+
set = new Set();
|
|
2105
|
+
this.handlers.set(eventName, set);
|
|
2106
|
+
this.attachDispatcher(eventName);
|
|
2107
|
+
}
|
|
2108
|
+
set.add(callback);
|
|
2109
|
+
}
|
|
2110
|
+
/**
|
|
2111
|
+
* Remove listener(s) for an event.
|
|
2112
|
+
* Prefer passing the callback (or using on$()): calling off(event) without
|
|
2113
|
+
* one removes EVERY handler for the event, including other features'.
|
|
2114
|
+
*/
|
|
2115
|
+
off(eventName, callback) {
|
|
2116
|
+
const set = this.handlers.get(eventName);
|
|
2117
|
+
if (!set) {
|
|
1529
2118
|
return;
|
|
1530
2119
|
}
|
|
1531
|
-
|
|
1532
|
-
|
|
2120
|
+
if (callback) {
|
|
2121
|
+
set.delete(callback);
|
|
2122
|
+
}
|
|
2123
|
+
else {
|
|
2124
|
+
console.warn(`NotificationService.off('${eventName}') without a callback removes ALL handlers for this event. ` +
|
|
2125
|
+
'Pass the callback registered with on(), or migrate to on$().');
|
|
2126
|
+
set.clear();
|
|
2127
|
+
}
|
|
2128
|
+
if (set.size === 0) {
|
|
2129
|
+
this.handlers.delete(eventName);
|
|
2130
|
+
this.hubConnection?.off(eventName);
|
|
2131
|
+
}
|
|
1533
2132
|
}
|
|
1534
2133
|
/**
|
|
1535
|
-
*
|
|
1536
|
-
*
|
|
2134
|
+
* Observable stream of an event. Teardown removes exactly this
|
|
2135
|
+
* subscriber — the safe replacement for manual on()/off().
|
|
2136
|
+
* Emits the event's first argument (SignalR payloads are single-arg here).
|
|
1537
2137
|
*/
|
|
1538
|
-
|
|
2138
|
+
on$(eventName) {
|
|
2139
|
+
return new Observable((subscriber) => {
|
|
2140
|
+
const callback = (payload) => subscriber.next(payload);
|
|
2141
|
+
this.on(eventName, callback);
|
|
2142
|
+
return () => this.off(eventName, callback);
|
|
2143
|
+
});
|
|
2144
|
+
}
|
|
2145
|
+
attachDispatcher(eventName) {
|
|
1539
2146
|
if (!this.hubConnection) {
|
|
1540
2147
|
return;
|
|
1541
2148
|
}
|
|
1542
|
-
|
|
1543
|
-
|
|
2149
|
+
this.hubConnection.on(eventName, (...args) => this.dispatch(eventName, args));
|
|
2150
|
+
}
|
|
2151
|
+
dispatch(eventName, args) {
|
|
2152
|
+
const set = this.handlers.get(eventName);
|
|
2153
|
+
if (!set) {
|
|
2154
|
+
return;
|
|
2155
|
+
}
|
|
2156
|
+
// Copy: a callback may add/remove handlers while we iterate.
|
|
2157
|
+
for (const callback of [...set]) {
|
|
2158
|
+
try {
|
|
2159
|
+
callback(...args);
|
|
2160
|
+
}
|
|
2161
|
+
catch (error) {
|
|
2162
|
+
console.error(`Error in '${eventName}' notification handler:`, error);
|
|
2163
|
+
}
|
|
2164
|
+
}
|
|
1544
2165
|
}
|
|
1545
2166
|
/**
|
|
1546
2167
|
* Invoke a method on the server
|
|
@@ -1552,7 +2173,6 @@ class NotificationService {
|
|
|
1552
2173
|
throw new Error('Not connected to SignalR hub');
|
|
1553
2174
|
}
|
|
1554
2175
|
try {
|
|
1555
|
-
console.log(`Invoking method: ${methodName}`, args);
|
|
1556
2176
|
return await this.hubConnection.invoke(methodName, ...args);
|
|
1557
2177
|
}
|
|
1558
2178
|
catch (error) {
|
|
@@ -1580,7 +2200,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImpor
|
|
|
1580
2200
|
args: [{
|
|
1581
2201
|
providedIn: 'root'
|
|
1582
2202
|
}]
|
|
1583
|
-
}]
|
|
2203
|
+
}] });
|
|
1584
2204
|
|
|
1585
2205
|
const MATERIAL_MODULE = [
|
|
1586
2206
|
MatSelectModule,
|
|
@@ -1610,17 +2230,21 @@ class ButtonComponent {
|
|
|
1610
2230
|
icon;
|
|
1611
2231
|
class;
|
|
1612
2232
|
loading = false;
|
|
2233
|
+
/** Native button type. Defaults to 'button' so it never implicitly submits a form. */
|
|
2234
|
+
type = 'button';
|
|
2235
|
+
/** Accessible name for icon-only usage (icon set, no label). */
|
|
2236
|
+
ariaLabel = '';
|
|
1613
2237
|
clicked = new EventEmitter();
|
|
1614
2238
|
onClick() {
|
|
1615
2239
|
if (!this.disabled)
|
|
1616
2240
|
this.clicked.emit();
|
|
1617
2241
|
}
|
|
1618
2242
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: ButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1619
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: ButtonComponent, isStandalone: false, selector: "fx-ui-button", inputs: { label: "label", disabled: "disabled", buttonVariant: "buttonVariant", icon: "icon", class: "class", loading: "loading" }, outputs: { clicked: "clicked" }, ngImport: i0, template: "<button\n [class]=\"class\"\n [ngClass]=\"{\n 'btn-default': buttonVariant === 'default',\n 'btn-primary': buttonVariant === 'primary',\n 'btn-success': buttonVariant === 'success',\n 'btn-alternative': buttonVariant === 'alternative',\n }\"\n [disabled]=\"disabled || loading\"\n (click)=\"onClick()\"\n>\n @if (loading) {\n <div class=\"flex w-full items-center justify-center\">\n <svg\n [attr.fill]=\"\n buttonVariant !== 'alternative' ? 'rgb(var(--text-primary))' : 'rgb(var(--primary))'\n \"\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <circle cx=\"4\" cy=\"12\" r=\"0\">\n <animate\n begin=\"0;spinner_z0Or.end\"\n attributeName=\"r\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"0;3\"\n fill=\"freeze\"\n />\n <animate\n begin=\"spinner_OLMs.end\"\n attributeName=\"cx\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"4;12\"\n fill=\"freeze\"\n />\n <animate\n begin=\"spinner_UHR2.end\"\n attributeName=\"cx\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"12;20\"\n fill=\"freeze\"\n />\n <animate\n id=\"spinner_lo66\"\n begin=\"spinner_Aguh.end\"\n attributeName=\"r\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"3;0\"\n fill=\"freeze\"\n />\n <animate\n id=\"spinner_z0Or\"\n begin=\"spinner_lo66.end\"\n attributeName=\"cx\"\n dur=\"0.001s\"\n values=\"20;4\"\n fill=\"freeze\"\n />\n </circle>\n <circle cx=\"4\" cy=\"12\" r=\"3\">\n <animate\n begin=\"0;spinner_z0Or.end\"\n attributeName=\"cx\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"4;12\"\n fill=\"freeze\"\n />\n <animate\n begin=\"spinner_OLMs.end\"\n attributeName=\"cx\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"12;20\"\n fill=\"freeze\"\n />\n <animate\n id=\"spinner_JsnR\"\n begin=\"spinner_UHR2.end\"\n attributeName=\"r\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"3;0\"\n fill=\"freeze\"\n />\n <animate\n id=\"spinner_Aguh\"\n begin=\"spinner_JsnR.end\"\n attributeName=\"cx\"\n dur=\"0.001s\"\n values=\"20;4\"\n fill=\"freeze\"\n />\n <animate\n begin=\"spinner_Aguh.end\"\n attributeName=\"r\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"0;3\"\n fill=\"freeze\"\n />\n </circle>\n <circle cx=\"12\" cy=\"12\" r=\"3\">\n <animate\n begin=\"0;spinner_z0Or.end\"\n attributeName=\"cx\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"12;20\"\n fill=\"freeze\"\n />\n <animate\n id=\"spinner_hSjk\"\n begin=\"spinner_OLMs.end\"\n attributeName=\"r\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"3;0\"\n fill=\"freeze\"\n />\n <animate\n id=\"spinner_UHR2\"\n begin=\"spinner_hSjk.end\"\n attributeName=\"cx\"\n dur=\"0.001s\"\n values=\"20;4\"\n fill=\"freeze\"\n />\n <animate\n begin=\"spinner_UHR2.end\"\n attributeName=\"r\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"0;3\"\n fill=\"freeze\"\n />\n <animate\n begin=\"spinner_Aguh.end\"\n attributeName=\"cx\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"4;12\"\n fill=\"freeze\"\n />\n </circle>\n <circle cx=\"20\" cy=\"12\" r=\"3\">\n <animate\n id=\"spinner_4v5M\"\n begin=\"0;spinner_z0Or.end\"\n attributeName=\"r\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"3;0\"\n fill=\"freeze\"\n />\n <animate\n id=\"spinner_OLMs\"\n begin=\"spinner_4v5M.end\"\n attributeName=\"cx\"\n dur=\"0.001s\"\n values=\"20;4\"\n fill=\"freeze\"\n />\n <animate\n begin=\"spinner_OLMs.end\"\n attributeName=\"r\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"0;3\"\n fill=\"freeze\"\n />\n <animate\n begin=\"spinner_UHR2.end\"\n attributeName=\"cx\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"4;12\"\n fill=\"freeze\"\n />\n <animate\n begin=\"spinner_Aguh.end\"\n attributeName=\"cx\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"12;20\"\n fill=\"freeze\"\n />\n </circle>\n </svg>\n </div>\n } @else {\n <ng-container>\n <div class=\"flex items-center justify-center gap-small\">\n @if (icon) {\n <fx-ui-hero-icon\n [icon]=\"icon\"\n [size]=\"20\"\n class=\"flex\"\n [ngClass]=\"{\n 'text-text-inverse': buttonVariant === 'default',\n 'text-text-primary': buttonVariant === 'alternative',\n 'text-white': buttonVariant === 'primary' || buttonVariant === 'success',\n }\"\n ></fx-ui-hero-icon>\n }\n @if (label) {\n <div>{{ label }}</div>\n }\n </div>\n </ng-container>\n }\n</button>\n", styles: [""], dependencies: [{ kind: "directive", type:
|
|
2243
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: ButtonComponent, isStandalone: false, selector: "fx-ui-button", inputs: { label: "label", disabled: "disabled", buttonVariant: "buttonVariant", icon: "icon", class: "class", loading: "loading", type: "type", ariaLabel: "ariaLabel" }, outputs: { clicked: "clicked" }, ngImport: i0, template: "<button\n [attr.type]=\"type\"\n [attr.aria-label]=\"ariaLabel || null\"\n [attr.aria-busy]=\"loading || null\"\n [class]=\"class\"\n [ngClass]=\"{\n 'btn-default': buttonVariant === 'default',\n 'btn-primary': buttonVariant === 'primary',\n 'btn-success': buttonVariant === 'success',\n 'btn-alternative': buttonVariant === 'alternative',\n }\"\n [disabled]=\"disabled || loading\"\n (click)=\"onClick()\"\n>\n @if (loading) {\n <div class=\"flex w-full items-center justify-center\" aria-hidden=\"true\">\n <svg\n [attr.fill]=\"\n buttonVariant !== 'alternative' ? 'rgb(var(--text-primary))' : 'rgb(var(--primary))'\n \"\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <circle cx=\"4\" cy=\"12\" r=\"0\">\n <animate\n begin=\"0;spinner_z0Or.end\"\n attributeName=\"r\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"0;3\"\n fill=\"freeze\"\n />\n <animate\n begin=\"spinner_OLMs.end\"\n attributeName=\"cx\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"4;12\"\n fill=\"freeze\"\n />\n <animate\n begin=\"spinner_UHR2.end\"\n attributeName=\"cx\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"12;20\"\n fill=\"freeze\"\n />\n <animate\n id=\"spinner_lo66\"\n begin=\"spinner_Aguh.end\"\n attributeName=\"r\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"3;0\"\n fill=\"freeze\"\n />\n <animate\n id=\"spinner_z0Or\"\n begin=\"spinner_lo66.end\"\n attributeName=\"cx\"\n dur=\"0.001s\"\n values=\"20;4\"\n fill=\"freeze\"\n />\n </circle>\n <circle cx=\"4\" cy=\"12\" r=\"3\">\n <animate\n begin=\"0;spinner_z0Or.end\"\n attributeName=\"cx\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"4;12\"\n fill=\"freeze\"\n />\n <animate\n begin=\"spinner_OLMs.end\"\n attributeName=\"cx\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"12;20\"\n fill=\"freeze\"\n />\n <animate\n id=\"spinner_JsnR\"\n begin=\"spinner_UHR2.end\"\n attributeName=\"r\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"3;0\"\n fill=\"freeze\"\n />\n <animate\n id=\"spinner_Aguh\"\n begin=\"spinner_JsnR.end\"\n attributeName=\"cx\"\n dur=\"0.001s\"\n values=\"20;4\"\n fill=\"freeze\"\n />\n <animate\n begin=\"spinner_Aguh.end\"\n attributeName=\"r\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"0;3\"\n fill=\"freeze\"\n />\n </circle>\n <circle cx=\"12\" cy=\"12\" r=\"3\">\n <animate\n begin=\"0;spinner_z0Or.end\"\n attributeName=\"cx\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"12;20\"\n fill=\"freeze\"\n />\n <animate\n id=\"spinner_hSjk\"\n begin=\"spinner_OLMs.end\"\n attributeName=\"r\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"3;0\"\n fill=\"freeze\"\n />\n <animate\n id=\"spinner_UHR2\"\n begin=\"spinner_hSjk.end\"\n attributeName=\"cx\"\n dur=\"0.001s\"\n values=\"20;4\"\n fill=\"freeze\"\n />\n <animate\n begin=\"spinner_UHR2.end\"\n attributeName=\"r\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"0;3\"\n fill=\"freeze\"\n />\n <animate\n begin=\"spinner_Aguh.end\"\n attributeName=\"cx\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"4;12\"\n fill=\"freeze\"\n />\n </circle>\n <circle cx=\"20\" cy=\"12\" r=\"3\">\n <animate\n id=\"spinner_4v5M\"\n begin=\"0;spinner_z0Or.end\"\n attributeName=\"r\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"3;0\"\n fill=\"freeze\"\n />\n <animate\n id=\"spinner_OLMs\"\n begin=\"spinner_4v5M.end\"\n attributeName=\"cx\"\n dur=\"0.001s\"\n values=\"20;4\"\n fill=\"freeze\"\n />\n <animate\n begin=\"spinner_OLMs.end\"\n attributeName=\"r\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"0;3\"\n fill=\"freeze\"\n />\n <animate\n begin=\"spinner_UHR2.end\"\n attributeName=\"cx\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"4;12\"\n fill=\"freeze\"\n />\n <animate\n begin=\"spinner_Aguh.end\"\n attributeName=\"cx\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"12;20\"\n fill=\"freeze\"\n />\n </circle>\n </svg>\n </div>\n } @else {\n <ng-container>\n <div class=\"flex items-center justify-center gap-small\">\n @if (icon) {\n <fx-ui-hero-icon\n [icon]=\"icon\"\n [size]=\"20\"\n class=\"flex\"\n [ngClass]=\"{\n 'text-text-inverse': buttonVariant === 'default',\n 'text-text-primary': buttonVariant === 'alternative',\n 'text-white': buttonVariant === 'primary' || buttonVariant === 'success',\n }\"\n ></fx-ui-hero-icon>\n }\n @if (label) {\n <div>{{ label }}</div>\n }\n </div>\n </ng-container>\n }\n</button>\n", styles: [""], dependencies: [{ kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: HeroIconComponent, selector: "fx-ui-hero-icon", inputs: ["stroke", "icon", "solid", "outline", "size", "color", "class"] }] });
|
|
1620
2244
|
}
|
|
1621
2245
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: ButtonComponent, decorators: [{
|
|
1622
2246
|
type: Component,
|
|
1623
|
-
args: [{ selector: 'fx-ui-button', standalone: false, template: "<button\n [class]=\"class\"\n [ngClass]=\"{\n 'btn-default': buttonVariant === 'default',\n 'btn-primary': buttonVariant === 'primary',\n 'btn-success': buttonVariant === 'success',\n 'btn-alternative': buttonVariant === 'alternative',\n }\"\n [disabled]=\"disabled || loading\"\n (click)=\"onClick()\"\n>\n @if (loading) {\n <div class=\"flex w-full items-center justify-center\">\n <svg\n [attr.fill]=\"\n buttonVariant !== 'alternative' ? 'rgb(var(--text-primary))' : 'rgb(var(--primary))'\n \"\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <circle cx=\"4\" cy=\"12\" r=\"0\">\n <animate\n begin=\"0;spinner_z0Or.end\"\n attributeName=\"r\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"0;3\"\n fill=\"freeze\"\n />\n <animate\n begin=\"spinner_OLMs.end\"\n attributeName=\"cx\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"4;12\"\n fill=\"freeze\"\n />\n <animate\n begin=\"spinner_UHR2.end\"\n attributeName=\"cx\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"12;20\"\n fill=\"freeze\"\n />\n <animate\n id=\"spinner_lo66\"\n begin=\"spinner_Aguh.end\"\n attributeName=\"r\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"3;0\"\n fill=\"freeze\"\n />\n <animate\n id=\"spinner_z0Or\"\n begin=\"spinner_lo66.end\"\n attributeName=\"cx\"\n dur=\"0.001s\"\n values=\"20;4\"\n fill=\"freeze\"\n />\n </circle>\n <circle cx=\"4\" cy=\"12\" r=\"3\">\n <animate\n begin=\"0;spinner_z0Or.end\"\n attributeName=\"cx\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"4;12\"\n fill=\"freeze\"\n />\n <animate\n begin=\"spinner_OLMs.end\"\n attributeName=\"cx\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"12;20\"\n fill=\"freeze\"\n />\n <animate\n id=\"spinner_JsnR\"\n begin=\"spinner_UHR2.end\"\n attributeName=\"r\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"3;0\"\n fill=\"freeze\"\n />\n <animate\n id=\"spinner_Aguh\"\n begin=\"spinner_JsnR.end\"\n attributeName=\"cx\"\n dur=\"0.001s\"\n values=\"20;4\"\n fill=\"freeze\"\n />\n <animate\n begin=\"spinner_Aguh.end\"\n attributeName=\"r\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"0;3\"\n fill=\"freeze\"\n />\n </circle>\n <circle cx=\"12\" cy=\"12\" r=\"3\">\n <animate\n begin=\"0;spinner_z0Or.end\"\n attributeName=\"cx\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"12;20\"\n fill=\"freeze\"\n />\n <animate\n id=\"spinner_hSjk\"\n begin=\"spinner_OLMs.end\"\n attributeName=\"r\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"3;0\"\n fill=\"freeze\"\n />\n <animate\n id=\"spinner_UHR2\"\n begin=\"spinner_hSjk.end\"\n attributeName=\"cx\"\n dur=\"0.001s\"\n values=\"20;4\"\n fill=\"freeze\"\n />\n <animate\n begin=\"spinner_UHR2.end\"\n attributeName=\"r\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"0;3\"\n fill=\"freeze\"\n />\n <animate\n begin=\"spinner_Aguh.end\"\n attributeName=\"cx\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"4;12\"\n fill=\"freeze\"\n />\n </circle>\n <circle cx=\"20\" cy=\"12\" r=\"3\">\n <animate\n id=\"spinner_4v5M\"\n begin=\"0;spinner_z0Or.end\"\n attributeName=\"r\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"3;0\"\n fill=\"freeze\"\n />\n <animate\n id=\"spinner_OLMs\"\n begin=\"spinner_4v5M.end\"\n attributeName=\"cx\"\n dur=\"0.001s\"\n values=\"20;4\"\n fill=\"freeze\"\n />\n <animate\n begin=\"spinner_OLMs.end\"\n attributeName=\"r\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"0;3\"\n fill=\"freeze\"\n />\n <animate\n begin=\"spinner_UHR2.end\"\n attributeName=\"cx\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"4;12\"\n fill=\"freeze\"\n />\n <animate\n begin=\"spinner_Aguh.end\"\n attributeName=\"cx\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"12;20\"\n fill=\"freeze\"\n />\n </circle>\n </svg>\n </div>\n } @else {\n <ng-container>\n <div class=\"flex items-center justify-center gap-small\">\n @if (icon) {\n <fx-ui-hero-icon\n [icon]=\"icon\"\n [size]=\"20\"\n class=\"flex\"\n [ngClass]=\"{\n 'text-text-inverse': buttonVariant === 'default',\n 'text-text-primary': buttonVariant === 'alternative',\n 'text-white': buttonVariant === 'primary' || buttonVariant === 'success',\n }\"\n ></fx-ui-hero-icon>\n }\n @if (label) {\n <div>{{ label }}</div>\n }\n </div>\n </ng-container>\n }\n</button>\n" }]
|
|
2247
|
+
args: [{ selector: 'fx-ui-button', standalone: false, template: "<button\n [attr.type]=\"type\"\n [attr.aria-label]=\"ariaLabel || null\"\n [attr.aria-busy]=\"loading || null\"\n [class]=\"class\"\n [ngClass]=\"{\n 'btn-default': buttonVariant === 'default',\n 'btn-primary': buttonVariant === 'primary',\n 'btn-success': buttonVariant === 'success',\n 'btn-alternative': buttonVariant === 'alternative',\n }\"\n [disabled]=\"disabled || loading\"\n (click)=\"onClick()\"\n>\n @if (loading) {\n <div class=\"flex w-full items-center justify-center\" aria-hidden=\"true\">\n <svg\n [attr.fill]=\"\n buttonVariant !== 'alternative' ? 'rgb(var(--text-primary))' : 'rgb(var(--primary))'\n \"\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <circle cx=\"4\" cy=\"12\" r=\"0\">\n <animate\n begin=\"0;spinner_z0Or.end\"\n attributeName=\"r\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"0;3\"\n fill=\"freeze\"\n />\n <animate\n begin=\"spinner_OLMs.end\"\n attributeName=\"cx\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"4;12\"\n fill=\"freeze\"\n />\n <animate\n begin=\"spinner_UHR2.end\"\n attributeName=\"cx\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"12;20\"\n fill=\"freeze\"\n />\n <animate\n id=\"spinner_lo66\"\n begin=\"spinner_Aguh.end\"\n attributeName=\"r\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"3;0\"\n fill=\"freeze\"\n />\n <animate\n id=\"spinner_z0Or\"\n begin=\"spinner_lo66.end\"\n attributeName=\"cx\"\n dur=\"0.001s\"\n values=\"20;4\"\n fill=\"freeze\"\n />\n </circle>\n <circle cx=\"4\" cy=\"12\" r=\"3\">\n <animate\n begin=\"0;spinner_z0Or.end\"\n attributeName=\"cx\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"4;12\"\n fill=\"freeze\"\n />\n <animate\n begin=\"spinner_OLMs.end\"\n attributeName=\"cx\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"12;20\"\n fill=\"freeze\"\n />\n <animate\n id=\"spinner_JsnR\"\n begin=\"spinner_UHR2.end\"\n attributeName=\"r\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"3;0\"\n fill=\"freeze\"\n />\n <animate\n id=\"spinner_Aguh\"\n begin=\"spinner_JsnR.end\"\n attributeName=\"cx\"\n dur=\"0.001s\"\n values=\"20;4\"\n fill=\"freeze\"\n />\n <animate\n begin=\"spinner_Aguh.end\"\n attributeName=\"r\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"0;3\"\n fill=\"freeze\"\n />\n </circle>\n <circle cx=\"12\" cy=\"12\" r=\"3\">\n <animate\n begin=\"0;spinner_z0Or.end\"\n attributeName=\"cx\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"12;20\"\n fill=\"freeze\"\n />\n <animate\n id=\"spinner_hSjk\"\n begin=\"spinner_OLMs.end\"\n attributeName=\"r\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"3;0\"\n fill=\"freeze\"\n />\n <animate\n id=\"spinner_UHR2\"\n begin=\"spinner_hSjk.end\"\n attributeName=\"cx\"\n dur=\"0.001s\"\n values=\"20;4\"\n fill=\"freeze\"\n />\n <animate\n begin=\"spinner_UHR2.end\"\n attributeName=\"r\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"0;3\"\n fill=\"freeze\"\n />\n <animate\n begin=\"spinner_Aguh.end\"\n attributeName=\"cx\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"4;12\"\n fill=\"freeze\"\n />\n </circle>\n <circle cx=\"20\" cy=\"12\" r=\"3\">\n <animate\n id=\"spinner_4v5M\"\n begin=\"0;spinner_z0Or.end\"\n attributeName=\"r\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"3;0\"\n fill=\"freeze\"\n />\n <animate\n id=\"spinner_OLMs\"\n begin=\"spinner_4v5M.end\"\n attributeName=\"cx\"\n dur=\"0.001s\"\n values=\"20;4\"\n fill=\"freeze\"\n />\n <animate\n begin=\"spinner_OLMs.end\"\n attributeName=\"r\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"0;3\"\n fill=\"freeze\"\n />\n <animate\n begin=\"spinner_UHR2.end\"\n attributeName=\"cx\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"4;12\"\n fill=\"freeze\"\n />\n <animate\n begin=\"spinner_Aguh.end\"\n attributeName=\"cx\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"12;20\"\n fill=\"freeze\"\n />\n </circle>\n </svg>\n </div>\n } @else {\n <ng-container>\n <div class=\"flex items-center justify-center gap-small\">\n @if (icon) {\n <fx-ui-hero-icon\n [icon]=\"icon\"\n [size]=\"20\"\n class=\"flex\"\n [ngClass]=\"{\n 'text-text-inverse': buttonVariant === 'default',\n 'text-text-primary': buttonVariant === 'alternative',\n 'text-white': buttonVariant === 'primary' || buttonVariant === 'success',\n }\"\n ></fx-ui-hero-icon>\n }\n @if (label) {\n <div>{{ label }}</div>\n }\n </div>\n </ng-container>\n }\n</button>\n" }]
|
|
1624
2248
|
}], propDecorators: { label: [{
|
|
1625
2249
|
type: Input
|
|
1626
2250
|
}], disabled: [{
|
|
@@ -1633,6 +2257,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImpor
|
|
|
1633
2257
|
type: Input
|
|
1634
2258
|
}], loading: [{
|
|
1635
2259
|
type: Input
|
|
2260
|
+
}], type: [{
|
|
2261
|
+
type: Input
|
|
2262
|
+
}], ariaLabel: [{
|
|
2263
|
+
type: Input
|
|
1636
2264
|
}], clicked: [{
|
|
1637
2265
|
type: Output
|
|
1638
2266
|
}] } });
|
|
@@ -2041,17 +2669,23 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImpor
|
|
|
2041
2669
|
args: ['window:theme-changed']
|
|
2042
2670
|
}] } });
|
|
2043
2671
|
|
|
2044
|
-
|
|
2045
|
-
|
|
2672
|
+
class FxComponent {
|
|
2673
|
+
/**
|
|
2674
|
+
* Injected via inject() rather than a constructor parameter: parameter
|
|
2675
|
+
* decorators (@Optional/@Self) are NOT inherited by subclasses, which used
|
|
2676
|
+
* to make every control crash when used without a form binding, or silently
|
|
2677
|
+
* bind to an ancestor's NgControl.
|
|
2678
|
+
*/
|
|
2679
|
+
ngControl = inject(NgControl, { optional: true, self: true });
|
|
2680
|
+
disabled = false;
|
|
2046
2681
|
value;
|
|
2047
|
-
onChange = (
|
|
2048
|
-
onTouched = () =>
|
|
2682
|
+
onChange = () => undefined;
|
|
2683
|
+
onTouched = () => undefined;
|
|
2049
2684
|
validationError;
|
|
2050
2685
|
errorMessages;
|
|
2051
|
-
constructor(
|
|
2052
|
-
this.ngControl
|
|
2053
|
-
|
|
2054
|
-
ngControl.valueAccessor = this;
|
|
2686
|
+
constructor() {
|
|
2687
|
+
if (this.ngControl)
|
|
2688
|
+
this.ngControl.valueAccessor = this;
|
|
2055
2689
|
}
|
|
2056
2690
|
setValue(value) {
|
|
2057
2691
|
if (this.onChange) {
|
|
@@ -2064,7 +2698,9 @@ let FxComponent = class FxComponent {
|
|
|
2064
2698
|
}
|
|
2065
2699
|
}
|
|
2066
2700
|
writeValue(value) {
|
|
2067
|
-
|
|
2701
|
+
// Pass through as-is. Coercing null to '' here broke boolean/number
|
|
2702
|
+
// controls; string controls that want '' as their empty value override.
|
|
2703
|
+
this.value = value;
|
|
2068
2704
|
}
|
|
2069
2705
|
registerOnChange(fn) {
|
|
2070
2706
|
this.onChange = fn;
|
|
@@ -2072,6 +2708,9 @@ let FxComponent = class FxComponent {
|
|
|
2072
2708
|
registerOnTouched(fn) {
|
|
2073
2709
|
this.onTouched = fn;
|
|
2074
2710
|
}
|
|
2711
|
+
setDisabledState(isDisabled) {
|
|
2712
|
+
this.disabled = isDisabled;
|
|
2713
|
+
}
|
|
2075
2714
|
get invalid() {
|
|
2076
2715
|
return !!(this.ngControl?.invalid && this.ngControl?.touched);
|
|
2077
2716
|
}
|
|
@@ -2093,20 +2732,22 @@ let FxComponent = class FxComponent {
|
|
|
2093
2732
|
const control = this.ngControl?.control;
|
|
2094
2733
|
if (!control)
|
|
2095
2734
|
return;
|
|
2096
|
-
control.setErrors(error);
|
|
2735
|
+
control.setErrors({ ...control.errors, ...error });
|
|
2097
2736
|
}
|
|
2737
|
+
/**
|
|
2738
|
+
* Removes only the errors this component set imperatively via setErrors().
|
|
2739
|
+
* Errors owned by the validator pipeline (built-in or custom validators)
|
|
2740
|
+
* are left untouched.
|
|
2741
|
+
*/
|
|
2098
2742
|
clearErrors() {
|
|
2099
2743
|
const control = this.ngControl?.control;
|
|
2100
|
-
if (!control)
|
|
2744
|
+
if (!control || !this.validationError)
|
|
2101
2745
|
return;
|
|
2102
|
-
const
|
|
2103
|
-
const
|
|
2104
|
-
|
|
2105
|
-
for (const key of builtInErrorKeys) {
|
|
2106
|
-
if (errors[key] !== undefined) {
|
|
2107
|
-
remaining[key] = errors[key];
|
|
2108
|
-
}
|
|
2746
|
+
const remaining = { ...(control.errors || {}) };
|
|
2747
|
+
for (const key of Object.keys(this.validationError)) {
|
|
2748
|
+
delete remaining[key];
|
|
2109
2749
|
}
|
|
2750
|
+
this.validationError = undefined;
|
|
2110
2751
|
control.setErrors(Object.keys(remaining).length ? remaining : null);
|
|
2111
2752
|
}
|
|
2112
2753
|
getErrorMessage(label) {
|
|
@@ -2116,33 +2757,35 @@ let FxComponent = class FxComponent {
|
|
|
2116
2757
|
const defaultMessages = {
|
|
2117
2758
|
required: `${displayLabel} is required`,
|
|
2118
2759
|
minlength: `${displayLabel} minimum length is ${this.errors['minlength']?.requiredLength}`,
|
|
2119
|
-
email:
|
|
2760
|
+
email: 'Please enter a valid email',
|
|
2120
2761
|
};
|
|
2121
2762
|
const messages = { ...defaultMessages, ...this.validationError, ...this.errorMessages };
|
|
2122
2763
|
const firstKey = Object.keys(this.errors)[0];
|
|
2123
2764
|
return messages[firstKey] || `Invalid ${label ?? 'value'}`;
|
|
2124
2765
|
}
|
|
2125
|
-
};
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2766
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: FxComponent, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
2767
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.4", type: FxComponent, isStandalone: true, inputs: { disabled: "disabled" }, ngImport: i0 });
|
|
2768
|
+
}
|
|
2769
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: FxComponent, decorators: [{
|
|
2770
|
+
type: Directive
|
|
2771
|
+
}], ctorParameters: () => [], propDecorators: { disabled: [{
|
|
2772
|
+
type: Input
|
|
2773
|
+
}] } });
|
|
2130
2774
|
|
|
2131
2775
|
class CheckboxComponent extends FxComponent {
|
|
2132
2776
|
ref;
|
|
2133
2777
|
label = '';
|
|
2134
|
-
disabled = false;
|
|
2135
2778
|
rounded;
|
|
2136
2779
|
valueChange = new EventEmitter();
|
|
2137
|
-
constructor(
|
|
2138
|
-
super(
|
|
2780
|
+
constructor(ref) {
|
|
2781
|
+
super();
|
|
2139
2782
|
this.ref = ref;
|
|
2140
2783
|
}
|
|
2141
2784
|
ngAfterViewInit() {
|
|
2142
2785
|
this.ref.detectChanges();
|
|
2143
2786
|
}
|
|
2144
|
-
|
|
2145
|
-
this.
|
|
2787
|
+
writeValue(value) {
|
|
2788
|
+
this.value = value ?? false;
|
|
2146
2789
|
}
|
|
2147
2790
|
onInputChange(event) {
|
|
2148
2791
|
const checked = event.target.checked;
|
|
@@ -2153,15 +2796,13 @@ class CheckboxComponent extends FxComponent {
|
|
|
2153
2796
|
markTouched() {
|
|
2154
2797
|
this.onTouched();
|
|
2155
2798
|
}
|
|
2156
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: CheckboxComponent, deps: [{ token:
|
|
2157
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: CheckboxComponent, isStandalone: false, selector: "fx-ui-checkbox", inputs: { label: "label",
|
|
2799
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: CheckboxComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
2800
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: CheckboxComponent, isStandalone: false, selector: "fx-ui-checkbox", inputs: { label: "label", rounded: "rounded" }, outputs: { valueChange: "valueChange" }, usesInheritance: true, ngImport: i0, template: "<label\n class=\"flex items-center gap-normal cursor-pointer select-none\"\n [class.opacity-50]=\"disabled\"\n [class.cursor-not-allowed]=\"disabled\"\n>\n <!-- sr-only (not `hidden`): keeps the input focusable and in the a11y tree -->\n <input\n type=\"checkbox\"\n class=\"peer sr-only\"\n [checked]=\"value\"\n [disabled]=\"disabled\"\n (change)=\"onInputChange($event)\"\n (blur)=\"onTouched()\"\n />\n <span\n class=\"relative w-4 h-4 border rounded-full transition-colors duration-150 shrink-0 peer-focus-visible:ring-2 peer-focus-visible:ring-primary/50\"\n [ngClass]=\"{\n 'rounded-full': rounded,\n 'rounded-sm': !rounded,\n 'border-border-strong bg-transparent': !value,\n 'border-transparent bg-gradient-primary': value,\n }\"\n >\n <!-- Keep SVG space reserved always -->\n <span class=\"absolute inset-0 flex items-center justify-center\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n class=\"text-white w-4 h-4 transition-opacity duration-150\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"3\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n [style.opacity]=\"value ? 1 : 0\"\n >\n <path d=\"M5 13l4 4L19 7\" />\n </svg>\n </span>\n </span>\n\n @if (label) {\n <span class=\"mb-0 txt-field-label\">{{ label }}</span>\n }\n</label>\n", styles: [""], dependencies: [{ kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] });
|
|
2158
2801
|
}
|
|
2159
2802
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: CheckboxComponent, decorators: [{
|
|
2160
2803
|
type: Component,
|
|
2161
|
-
args: [{ selector: 'fx-ui-checkbox', standalone: false, template: "<label\n class=\"flex items-center gap-normal cursor-pointer select-none\"\n [class.opacity-50]=\"disabled\"\n [class.cursor-not-allowed]=\"disabled\"\n>\n <input\n type=\"checkbox\"\n class=\"
|
|
2162
|
-
}], ctorParameters: () => [{ type:
|
|
2163
|
-
type: Input
|
|
2164
|
-
}], disabled: [{
|
|
2804
|
+
args: [{ selector: 'fx-ui-checkbox', standalone: false, template: "<label\n class=\"flex items-center gap-normal cursor-pointer select-none\"\n [class.opacity-50]=\"disabled\"\n [class.cursor-not-allowed]=\"disabled\"\n>\n <!-- sr-only (not `hidden`): keeps the input focusable and in the a11y tree -->\n <input\n type=\"checkbox\"\n class=\"peer sr-only\"\n [checked]=\"value\"\n [disabled]=\"disabled\"\n (change)=\"onInputChange($event)\"\n (blur)=\"onTouched()\"\n />\n <span\n class=\"relative w-4 h-4 border rounded-full transition-colors duration-150 shrink-0 peer-focus-visible:ring-2 peer-focus-visible:ring-primary/50\"\n [ngClass]=\"{\n 'rounded-full': rounded,\n 'rounded-sm': !rounded,\n 'border-border-strong bg-transparent': !value,\n 'border-transparent bg-gradient-primary': value,\n }\"\n >\n <!-- Keep SVG space reserved always -->\n <span class=\"absolute inset-0 flex items-center justify-center\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n class=\"text-white w-4 h-4 transition-opacity duration-150\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"3\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n [style.opacity]=\"value ? 1 : 0\"\n >\n <path d=\"M5 13l4 4L19 7\" />\n </svg>\n </span>\n </span>\n\n @if (label) {\n <span class=\"mb-0 txt-field-label\">{{ label }}</span>\n }\n</label>\n" }]
|
|
2805
|
+
}], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { label: [{
|
|
2165
2806
|
type: Input
|
|
2166
2807
|
}], rounded: [{
|
|
2167
2808
|
type: Input
|
|
@@ -2195,31 +2836,28 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImpor
|
|
|
2195
2836
|
type: Input
|
|
2196
2837
|
}] } });
|
|
2197
2838
|
|
|
2839
|
+
let nextPickerId = 0;
|
|
2198
2840
|
class DatetimePicker extends FxComponent {
|
|
2199
2841
|
ref;
|
|
2200
2842
|
label;
|
|
2201
2843
|
placeholder = 'Select date and time';
|
|
2202
2844
|
required = false;
|
|
2203
2845
|
type = 'both';
|
|
2204
|
-
disabled = false;
|
|
2205
2846
|
minDate;
|
|
2206
2847
|
maxDate;
|
|
2207
2848
|
errorMessages = {};
|
|
2208
2849
|
validateFn;
|
|
2850
|
+
fieldId = `fx-datetime-${nextPickerId++}`;
|
|
2209
2851
|
startAt;
|
|
2210
|
-
constructor(
|
|
2211
|
-
super(
|
|
2852
|
+
constructor(ref) {
|
|
2853
|
+
super();
|
|
2212
2854
|
this.ref = ref;
|
|
2213
2855
|
}
|
|
2214
|
-
ngOnInit() { }
|
|
2215
2856
|
ngAfterViewInit() {
|
|
2216
2857
|
setTimeout(() => {
|
|
2217
2858
|
this.ref.detectChanges();
|
|
2218
2859
|
});
|
|
2219
2860
|
}
|
|
2220
|
-
setDisabledState(isDisabled) {
|
|
2221
|
-
this.disabled = isDisabled;
|
|
2222
|
-
}
|
|
2223
2861
|
onBlur() {
|
|
2224
2862
|
this.onTouched();
|
|
2225
2863
|
this.validate(this.validateFn);
|
|
@@ -2236,13 +2874,13 @@ class DatetimePicker extends FxComponent {
|
|
|
2236
2874
|
this.onChange(date);
|
|
2237
2875
|
this.onTouched();
|
|
2238
2876
|
}
|
|
2239
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: DatetimePicker, deps: [{ token:
|
|
2240
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: DatetimePicker, isStandalone: false, selector: "fx-ui-datetime-picker", inputs: { label: "label", placeholder: "placeholder", required: "required", type: "type",
|
|
2877
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: DatetimePicker, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
2878
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: DatetimePicker, isStandalone: false, selector: "fx-ui-datetime-picker", inputs: { label: "label", placeholder: "placeholder", required: "required", type: "type", minDate: "minDate", maxDate: "maxDate", errorMessages: "errorMessages", validateFn: "validateFn" }, usesInheritance: true, ngImport: i0, template: "<div class=\"flex flex-col mb-4 w-full text-left\">\n @if (label) {\n <label class=\"txt-field-label\" [attr.for]=\"fieldId\">\n {{ label }}\n <span *ngIf=\"required\" class=\"txt-required\" aria-hidden=\"true\">*</span>\n </label>\n }\n\n <div class=\"relative\">\n <input\n [id]=\"fieldId\"\n [owlDateTimeTrigger]=\"picker\"\n [owlDateTime]=\"picker\"\n [placeholder]=\"placeholder\"\n [disabled]=\"disabled\"\n [min]=\"minDate\"\n [max]=\"maxDate\"\n (blur)=\"onBlur()\"\n [value]=\"value\"\n readonly\n class=\"input-default pr-9\"\n />\n <span\n [owlDateTimeTrigger]=\"picker\"\n class=\"absolute inset-y-0 right-0 flex items-center pr-2.5 cursor-pointer text-text-placeholder hover:text-text-primary transition-colors\"\n >\n <fx-ui-hero-icon icon=\"calendar\" [size]=\"20\" class=\"text-text-primary\"></fx-ui-hero-icon>\n </span>\n </div>\n <owl-date-time\n #picker\n [startAt]=\"startAt\"\n [pickerType]=\"type\"\n (afterPickerClosed)=\"onValueChange($event)\"\n ></owl-date-time>\n <div *ngIf=\"invalid\" class=\"txt-invalid\">{{ getErrorMessage(label) }}</div>\n</div>\n", styles: ["@charset \"UTF-8\";@layer properties;::ng-deep .owl-dt-container{border-radius:.75rem;border-style:var(--tw-border-style);border-width:1px;border-color:rgb(var(--border-default));background-color:rgb(var(--bg-primary));--tw-shadow: 0 20px 25px -5px var(--tw-shadow-color, rgb(var(--shadow-color) / .1)), 0 10px 10px -5px var(--tw-shadow-color, rgb(var(--shadow-color) / .1));box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);width:320px!important}::ng-deep .owl-dt-calendar-table{width:100%;border-collapse:collapse}::ng-deep .owl-dt-calendar-table :not(.owl-dt-calendar-cell-disabled):hover>.owl-dt-calendar-cell-content:not(.owl-dt-calendar-cell-selected){background-color:rgb(var(--bg-primary-hover))}::ng-deep .owl-dt-calendar-table .owl-dt-calendar-cell-content{color:rgb(var(--text-primary))}::ng-deep .owl-dt-calendar-table .owl-dt-calendar-cell-selected{background-color:rgb(var(--primary))!important;color:#fff!important}::ng-deep .owl-dt-calendar-table .owl-dt-calendar-cell-today:not(.owl-dt-calendar-cell-selected){border-color:rgb(var(--primary))!important}::ng-deep .owl-dt-calendar-table .owl-dt-calendar-cell-disabled>.owl-dt-calendar-cell-content{color:rgb(var(--text-placeholder))!important;opacity:.6!important;cursor:not-allowed;pointer-events:none}::ng-deep .owl-dt-calendar-table .owl-dt-calendar-cell-content.owl-dt-calendar-cell-out{color:rgb(var(--text-secondary))!important;opacity:.6!important}::ng-deep .owl-dt-timer-content .owl-dt-timer-input{border-radius:.5rem;border-style:var(--tw-border-style);border-width:1px;border-color:rgb(var(--border-strong));background-color:rgb(var(--bg-primary));color:rgb(var(--text-primary))}::ng-deep .owl-dt-control-period-button .owl-dt-control-button-arrow{display:none}::ng-deep .owl-dt-container-buttons{height:auto!important;color:rgb(var(--text-primary))!important;display:flex!important;align-items:center!important;justify-content:flex-end!important;gap:8px;padding:8px 12px;border-top:1px solid rgb(var(--border-default))}::ng-deep .owl-dt-container-buttons .owl-dt-container-control-button{width:auto!important;height:32px!important;border-radius:6px!important;width:60px!important;padding-top:4px!important;padding-bottom:4px!important;font-size:13px!important;font-weight:600!important;border:1px solid rgb(var(--border-default))!important;background-color:rgb(var(--bg-primary))!important;color:rgb(var(--text-primary))!important;cursor:pointer;transition:opacity .15s ease}::ng-deep .owl-dt-container-buttons .owl-dt-container-control-button:hover{background-color:rgb(var(--bg-hover))!important}::ng-deep .owl-dt-container-buttons .owl-dt-container-control-button:last-child{background-color:rgb(var(--primary))!important;border-color:rgb(var(--primary))!important;color:#fff!important}::ng-deep .owl-dt-container-buttons .owl-dt-container-control-button:last-child:hover{opacity:.85}::ng-deep .owl-dt-container-buttons .owl-dt-container-control-button .owl-dt-control-button-content{height:100%!important;width:100%!important;padding:0!important;background-color:transparent!important;color:inherit!important;display:flex!important;align-items:center!important;justify-content:center!important}::ng-deep .owl-dt-calendar-control-content .owl-dt-control-button-content{--tw-font-weight: var(--font-weight-medium, 500);font-weight:var(--font-weight-medium, 500);color:rgb(var(--text-primary))}::ng-deep .owl-dt-control-arrow-button .owl-dt-control-button-content{color:rgb(var(--text-primary))}::ng-deep .owl-dt-calendar-table .owl-dt-calendar-header{color:rgb(var(--text-secondary))}::ng-deep .owl-dt-calendar-table .owl-dt-calendar-header .owl-dt-weekdays th{--tw-font-weight: var(--font-weight-medium, 500);font-weight:var(--font-weight-medium, 500);color:rgb(var(--text-secondary))}::ng-deep .owl-dt-inline-container,::ng-deep .owl-dt-popup-container{border-radius:.75rem;border-style:var(--tw-border-style);border-width:1px;border-color:rgb(var(--border-strong));--tw-shadow: 0 10px 15px -3px var(--tw-shadow-color, rgb(var(--shadow-color) / .1)), 0 4px 6px -4px var(--tw-shadow-color, rgb(var(--shadow-color) / .1));box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@property --tw-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-shadow-color{syntax: \"*\"; inherits: false;}@property --tw-shadow-alpha{syntax: \"<percentage>\"; inherits: false; initial-value: 100%;}@property --tw-inset-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-inset-shadow-color{syntax: \"*\"; inherits: false;}@property --tw-inset-shadow-alpha{syntax: \"<percentage>\"; inherits: false; initial-value: 100%;}@property --tw-ring-color{syntax: \"*\"; inherits: false;}@property --tw-ring-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-inset-ring-color{syntax: \"*\"; inherits: false;}@property --tw-inset-ring-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-ring-inset{syntax: \"*\"; inherits: false;}@property --tw-ring-offset-width{syntax: \"<length>\"; inherits: false; initial-value: 0px;}@property --tw-ring-offset-color{syntax: \"*\"; inherits: false; initial-value: #fff;}@property --tw-ring-offset-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-font-weight{syntax: \"*\"; inherits: false;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style: solid;--tw-shadow: 0 0 #0000;--tw-shadow-color: initial;--tw-shadow-alpha: 100%;--tw-inset-shadow: 0 0 #0000;--tw-inset-shadow-color: initial;--tw-inset-shadow-alpha: 100%;--tw-ring-color: initial;--tw-ring-shadow: 0 0 #0000;--tw-inset-ring-color: initial;--tw-inset-ring-shadow: 0 0 #0000;--tw-ring-inset: initial;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-offset-shadow: 0 0 #0000;--tw-font-weight: initial}}}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2$2.OwlDateTimeTriggerDirective, selector: "[owlDateTimeTrigger]", inputs: ["owlDateTimeTrigger", "disabled"] }, { kind: "directive", type: i2$2.OwlDateTimeInputDirective, selector: "input[owlDateTime]", inputs: ["required", "owlDateTime", "owlDateTimeFilter", "_disabled", "min", "max", "selectMode", "rangeSeparator", "value", "values"], outputs: ["dateTimeChange", "dateTimeInput"], exportAs: ["owlDateTimeInput"] }, { kind: "component", type: i2$2.OwlDateTimeComponent, selector: "owl-date-time", inputs: ["backdropClass", "panelClass", "startAt", "endAt", "pickerType", "pickerMode", "disabled", "opened", "scrollStrategy"], outputs: ["afterPickerClosed", "beforePickerOpen", "afterPickerOpen", "yearSelected", "monthSelected", "dateSelected"], exportAs: ["owlDateTime"] }, { kind: "component", type: HeroIconComponent, selector: "fx-ui-hero-icon", inputs: ["stroke", "icon", "solid", "outline", "size", "color", "class"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2241
2879
|
}
|
|
2242
2880
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: DatetimePicker, decorators: [{
|
|
2243
2881
|
type: Component,
|
|
2244
|
-
args: [{ selector: 'fx-ui-datetime-picker', standalone: false, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"flex flex-col mb-4 w-full text-left\">\n @if (label) {\n <label class=\"txt-field-label\">\n {{ label }}\n <span *ngIf=\"required\" class=\"txt-required\">*</span>\n </label>\n }\n\n <div class=\"relative\">\n <input\n [owlDateTimeTrigger]=\"picker\"\n [owlDateTime]=\"picker\"\n [placeholder]=\"placeholder\"\n [disabled]=\"disabled\"\n [min]=\"minDate\"\n [max]=\"maxDate\"\n (blur)=\"onBlur()\"\n [value]=\"value\"\n readonly\n class=\"input-default pr-9\"\n />\n <span\n [owlDateTimeTrigger]=\"picker\"\n class=\"absolute inset-y-0 right-0 flex items-center pr-2.5 cursor-pointer text-text-placeholder hover:text-text-primary transition-colors\"\n >\n <fx-ui-hero-icon icon=\"calendar\" [size]=\"20\" class=\"text-text-primary\"></fx-ui-hero-icon>\n </span>\n </div>\n <owl-date-time\n #picker\n [startAt]=\"startAt\"\n [pickerType]=\"type\"\n (afterPickerClosed)=\"onValueChange($event)\"\n ></owl-date-time>\n <div *ngIf=\"invalid\" class=\"txt-invalid\">{{ getErrorMessage(label) }}</div>\n</div>\n", styles: ["
|
|
2245
|
-
}], ctorParameters: () => [{ type:
|
|
2882
|
+
args: [{ selector: 'fx-ui-datetime-picker', standalone: false, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"flex flex-col mb-4 w-full text-left\">\n @if (label) {\n <label class=\"txt-field-label\" [attr.for]=\"fieldId\">\n {{ label }}\n <span *ngIf=\"required\" class=\"txt-required\" aria-hidden=\"true\">*</span>\n </label>\n }\n\n <div class=\"relative\">\n <input\n [id]=\"fieldId\"\n [owlDateTimeTrigger]=\"picker\"\n [owlDateTime]=\"picker\"\n [placeholder]=\"placeholder\"\n [disabled]=\"disabled\"\n [min]=\"minDate\"\n [max]=\"maxDate\"\n (blur)=\"onBlur()\"\n [value]=\"value\"\n readonly\n class=\"input-default pr-9\"\n />\n <span\n [owlDateTimeTrigger]=\"picker\"\n class=\"absolute inset-y-0 right-0 flex items-center pr-2.5 cursor-pointer text-text-placeholder hover:text-text-primary transition-colors\"\n >\n <fx-ui-hero-icon icon=\"calendar\" [size]=\"20\" class=\"text-text-primary\"></fx-ui-hero-icon>\n </span>\n </div>\n <owl-date-time\n #picker\n [startAt]=\"startAt\"\n [pickerType]=\"type\"\n (afterPickerClosed)=\"onValueChange($event)\"\n ></owl-date-time>\n <div *ngIf=\"invalid\" class=\"txt-invalid\">{{ getErrorMessage(label) }}</div>\n</div>\n", styles: ["@charset \"UTF-8\";@layer properties;::ng-deep .owl-dt-container{border-radius:.75rem;border-style:var(--tw-border-style);border-width:1px;border-color:rgb(var(--border-default));background-color:rgb(var(--bg-primary));--tw-shadow: 0 20px 25px -5px var(--tw-shadow-color, rgb(var(--shadow-color) / .1)), 0 10px 10px -5px var(--tw-shadow-color, rgb(var(--shadow-color) / .1));box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);width:320px!important}::ng-deep .owl-dt-calendar-table{width:100%;border-collapse:collapse}::ng-deep .owl-dt-calendar-table :not(.owl-dt-calendar-cell-disabled):hover>.owl-dt-calendar-cell-content:not(.owl-dt-calendar-cell-selected){background-color:rgb(var(--bg-primary-hover))}::ng-deep .owl-dt-calendar-table .owl-dt-calendar-cell-content{color:rgb(var(--text-primary))}::ng-deep .owl-dt-calendar-table .owl-dt-calendar-cell-selected{background-color:rgb(var(--primary))!important;color:#fff!important}::ng-deep .owl-dt-calendar-table .owl-dt-calendar-cell-today:not(.owl-dt-calendar-cell-selected){border-color:rgb(var(--primary))!important}::ng-deep .owl-dt-calendar-table .owl-dt-calendar-cell-disabled>.owl-dt-calendar-cell-content{color:rgb(var(--text-placeholder))!important;opacity:.6!important;cursor:not-allowed;pointer-events:none}::ng-deep .owl-dt-calendar-table .owl-dt-calendar-cell-content.owl-dt-calendar-cell-out{color:rgb(var(--text-secondary))!important;opacity:.6!important}::ng-deep .owl-dt-timer-content .owl-dt-timer-input{border-radius:.5rem;border-style:var(--tw-border-style);border-width:1px;border-color:rgb(var(--border-strong));background-color:rgb(var(--bg-primary));color:rgb(var(--text-primary))}::ng-deep .owl-dt-control-period-button .owl-dt-control-button-arrow{display:none}::ng-deep .owl-dt-container-buttons{height:auto!important;color:rgb(var(--text-primary))!important;display:flex!important;align-items:center!important;justify-content:flex-end!important;gap:8px;padding:8px 12px;border-top:1px solid rgb(var(--border-default))}::ng-deep .owl-dt-container-buttons .owl-dt-container-control-button{width:auto!important;height:32px!important;border-radius:6px!important;width:60px!important;padding-top:4px!important;padding-bottom:4px!important;font-size:13px!important;font-weight:600!important;border:1px solid rgb(var(--border-default))!important;background-color:rgb(var(--bg-primary))!important;color:rgb(var(--text-primary))!important;cursor:pointer;transition:opacity .15s ease}::ng-deep .owl-dt-container-buttons .owl-dt-container-control-button:hover{background-color:rgb(var(--bg-hover))!important}::ng-deep .owl-dt-container-buttons .owl-dt-container-control-button:last-child{background-color:rgb(var(--primary))!important;border-color:rgb(var(--primary))!important;color:#fff!important}::ng-deep .owl-dt-container-buttons .owl-dt-container-control-button:last-child:hover{opacity:.85}::ng-deep .owl-dt-container-buttons .owl-dt-container-control-button .owl-dt-control-button-content{height:100%!important;width:100%!important;padding:0!important;background-color:transparent!important;color:inherit!important;display:flex!important;align-items:center!important;justify-content:center!important}::ng-deep .owl-dt-calendar-control-content .owl-dt-control-button-content{--tw-font-weight: var(--font-weight-medium, 500);font-weight:var(--font-weight-medium, 500);color:rgb(var(--text-primary))}::ng-deep .owl-dt-control-arrow-button .owl-dt-control-button-content{color:rgb(var(--text-primary))}::ng-deep .owl-dt-calendar-table .owl-dt-calendar-header{color:rgb(var(--text-secondary))}::ng-deep .owl-dt-calendar-table .owl-dt-calendar-header .owl-dt-weekdays th{--tw-font-weight: var(--font-weight-medium, 500);font-weight:var(--font-weight-medium, 500);color:rgb(var(--text-secondary))}::ng-deep .owl-dt-inline-container,::ng-deep .owl-dt-popup-container{border-radius:.75rem;border-style:var(--tw-border-style);border-width:1px;border-color:rgb(var(--border-strong));--tw-shadow: 0 10px 15px -3px var(--tw-shadow-color, rgb(var(--shadow-color) / .1)), 0 4px 6px -4px var(--tw-shadow-color, rgb(var(--shadow-color) / .1));box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@property --tw-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-shadow-color{syntax: \"*\"; inherits: false;}@property --tw-shadow-alpha{syntax: \"<percentage>\"; inherits: false; initial-value: 100%;}@property --tw-inset-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-inset-shadow-color{syntax: \"*\"; inherits: false;}@property --tw-inset-shadow-alpha{syntax: \"<percentage>\"; inherits: false; initial-value: 100%;}@property --tw-ring-color{syntax: \"*\"; inherits: false;}@property --tw-ring-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-inset-ring-color{syntax: \"*\"; inherits: false;}@property --tw-inset-ring-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-ring-inset{syntax: \"*\"; inherits: false;}@property --tw-ring-offset-width{syntax: \"<length>\"; inherits: false; initial-value: 0px;}@property --tw-ring-offset-color{syntax: \"*\"; inherits: false; initial-value: #fff;}@property --tw-ring-offset-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-font-weight{syntax: \"*\"; inherits: false;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style: solid;--tw-shadow: 0 0 #0000;--tw-shadow-color: initial;--tw-shadow-alpha: 100%;--tw-inset-shadow: 0 0 #0000;--tw-inset-shadow-color: initial;--tw-inset-shadow-alpha: 100%;--tw-ring-color: initial;--tw-ring-shadow: 0 0 #0000;--tw-inset-ring-color: initial;--tw-inset-ring-shadow: 0 0 #0000;--tw-ring-inset: initial;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-offset-shadow: 0 0 #0000;--tw-font-weight: initial}}}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"] }]
|
|
2883
|
+
}], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { label: [{
|
|
2246
2884
|
type: Input
|
|
2247
2885
|
}], placeholder: [{
|
|
2248
2886
|
type: Input
|
|
@@ -2250,8 +2888,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImpor
|
|
|
2250
2888
|
type: Input
|
|
2251
2889
|
}], type: [{
|
|
2252
2890
|
type: Input
|
|
2253
|
-
}], disabled: [{
|
|
2254
|
-
type: Input
|
|
2255
2891
|
}], minDate: [{
|
|
2256
2892
|
type: Input
|
|
2257
2893
|
}], maxDate: [{
|
|
@@ -2363,11 +2999,11 @@ class DndUploadComponent {
|
|
|
2363
2999
|
});
|
|
2364
3000
|
}
|
|
2365
3001
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: DndUploadComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2366
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: DndUploadComponent, isStandalone: false, selector: "fx-ui-dnd-upload", inputs: { multiple: "multiple", accept: "accept", maxSizeMB: "maxSizeMB", strictSize: "strictSize", icon: "icon", accentColor: "accentColor" }, outputs: { fileDrop: "fileDrop", fileClick: "fileClick", uploadComplete: "uploadComplete", uploadError: "uploadError" }, ngImport: i0, template: "<div\n class=\"dnd-wrapper\"\n [ngStyle]=\"accentStyle\"\n (dragover)=\"onDragOver($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\"\n>\n <div\n role=\"button\"\n tabindex=\"0\"\n class=\"dnd-area\"\n [class.drag-over]=\"isDragOver\"\n (click)=\"onAreaClick(fileInput, $event)\"\n (keydown.enter)=\"onAreaClick(fileInput, $event)\"\n >\n <fx-ui-hero-icon\n [icon]=\"icon\"\n [size]=\"40\"\n class=\"flex dnd-icon\"\n ></fx-ui-hero-icon>\n <div class=\"dnd-text\">\n <span class=\"dnd-link\">Click to upload</span>\n <span class=\"dnd-or\">or drag and drop</span>\n </div>\n <div class=\"dnd-hint\">\n @if (accept) {\n Accepted: {{ accept }}\n } @else {\n Any file type\n }\n \u2014 Max {{ maxSizeMB }} MB\n </div>\n\n <input\n #fileInput\n type=\"file\"\n class=\"dnd-input\"\n [attr.multiple]=\"multiple ? '' : null\"\n [attr.accept]=\"accept\"\n (change)=\"onFileSelected($event)\"\n />\n </div>\n</div>\n", styles: [":host{--dnd-accent: rgba(var(--gradient-primary-start) / 1)}.dnd-wrapper{display:block;width:100%}.dnd-area{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;min-height:160px;padding:32px 24px;border:2px dashed color-mix(in srgb,var(--dnd-accent) 40%,transparent);border-radius:12px;background:color-mix(in srgb,var(--dnd-accent) 4%,transparent);cursor:pointer;outline:none;transition:all .2s ease;position:relative}.dnd-area:hover{border-color:color-mix(in srgb,var(--dnd-accent) 70%,transparent);background:color-mix(in srgb,var(--dnd-accent) 8%,transparent)}.dnd-area:focus-visible{box-shadow:0 0 0 3px color-mix(in srgb,var(--dnd-accent) 20%,transparent)}.dnd-area.drag-over{border-color:var(--dnd-accent);background:color-mix(in srgb,var(--dnd-accent) 10%,transparent)}.dnd-icon{color:var(--dnd-accent)}.dnd-text{display:flex;align-items:center;gap:4px;font-size:14px;flex-wrap:wrap;justify-content:center}.dnd-link{color:var(--dnd-accent);font-weight:600;text-decoration:underline}.dnd-or{color:rgba(var(--text-secondary)/1)}.dnd-hint{font-size:12px;color:rgba(var(--text-secondary)/.7)}.dnd-input{position:absolute;width:0;height:0;overflow:hidden;opacity:0}@media(max-width:640px){.dnd-area{min-height:120px;padding:24px 16px}.dnd-text{font-size:13px}}\n"], dependencies: [{ kind: "directive", type:
|
|
3002
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: DndUploadComponent, isStandalone: false, selector: "fx-ui-dnd-upload", inputs: { multiple: "multiple", accept: "accept", maxSizeMB: "maxSizeMB", strictSize: "strictSize", icon: "icon", accentColor: "accentColor" }, outputs: { fileDrop: "fileDrop", fileClick: "fileClick", uploadComplete: "uploadComplete", uploadError: "uploadError" }, ngImport: i0, template: "<div\n class=\"dnd-wrapper\"\n [ngStyle]=\"accentStyle\"\n (dragover)=\"onDragOver($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\"\n>\n <div\n role=\"button\"\n tabindex=\"0\"\n class=\"dnd-area\"\n [class.drag-over]=\"isDragOver\"\n (click)=\"onAreaClick(fileInput, $event)\"\n (keydown.enter)=\"onAreaClick(fileInput, $event)\"\n >\n <fx-ui-hero-icon\n [icon]=\"icon\"\n [size]=\"40\"\n class=\"flex dnd-icon\"\n ></fx-ui-hero-icon>\n <div class=\"dnd-text\">\n <span class=\"dnd-link\">Click to upload</span>\n <span class=\"dnd-or\">or drag and drop</span>\n </div>\n <div class=\"dnd-hint\">\n @if (accept) {\n Accepted: {{ accept }}\n } @else {\n Any file type\n }\n \u2014 Max {{ maxSizeMB }} MB\n </div>\n\n <input\n #fileInput\n type=\"file\"\n class=\"dnd-input\"\n [attr.multiple]=\"multiple ? '' : null\"\n [attr.accept]=\"accept\"\n (change)=\"onFileSelected($event)\"\n />\n </div>\n</div>\n", styles: [":host{--dnd-accent: rgba(var(--gradient-primary-start) / 1)}.dnd-wrapper{display:block;width:100%}.dnd-area{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;min-height:160px;padding:32px 24px;border:2px dashed color-mix(in srgb,var(--dnd-accent) 40%,transparent);border-radius:12px;background:color-mix(in srgb,var(--dnd-accent) 4%,transparent);cursor:pointer;outline:none;transition:all .2s ease;position:relative}.dnd-area:hover{border-color:color-mix(in srgb,var(--dnd-accent) 70%,transparent);background:color-mix(in srgb,var(--dnd-accent) 8%,transparent)}.dnd-area:focus-visible{box-shadow:0 0 0 3px color-mix(in srgb,var(--dnd-accent) 20%,transparent)}.dnd-area.drag-over{border-color:var(--dnd-accent);background:color-mix(in srgb,var(--dnd-accent) 10%,transparent)}.dnd-icon{color:var(--dnd-accent)}.dnd-text{display:flex;align-items:center;gap:4px;font-size:14px;flex-wrap:wrap;justify-content:center}.dnd-link{color:var(--dnd-accent);font-weight:600;text-decoration:underline}.dnd-or{color:rgba(var(--text-secondary)/1)}.dnd-hint{font-size:12px;color:rgba(var(--text-secondary)/.7)}.dnd-input{position:absolute;width:0;height:0;overflow:hidden;opacity:0}@media (max-width: 640px){.dnd-area{min-height:120px;padding:24px 16px}.dnd-text{font-size:13px}}\n"], dependencies: [{ kind: "directive", type: i1$1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: HeroIconComponent, selector: "fx-ui-hero-icon", inputs: ["stroke", "icon", "solid", "outline", "size", "color", "class"] }] });
|
|
2367
3003
|
}
|
|
2368
3004
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: DndUploadComponent, decorators: [{
|
|
2369
3005
|
type: Component,
|
|
2370
|
-
args: [{ selector: 'fx-ui-dnd-upload', standalone: false, template: "<div\n class=\"dnd-wrapper\"\n [ngStyle]=\"accentStyle\"\n (dragover)=\"onDragOver($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\"\n>\n <div\n role=\"button\"\n tabindex=\"0\"\n class=\"dnd-area\"\n [class.drag-over]=\"isDragOver\"\n (click)=\"onAreaClick(fileInput, $event)\"\n (keydown.enter)=\"onAreaClick(fileInput, $event)\"\n >\n <fx-ui-hero-icon\n [icon]=\"icon\"\n [size]=\"40\"\n class=\"flex dnd-icon\"\n ></fx-ui-hero-icon>\n <div class=\"dnd-text\">\n <span class=\"dnd-link\">Click to upload</span>\n <span class=\"dnd-or\">or drag and drop</span>\n </div>\n <div class=\"dnd-hint\">\n @if (accept) {\n Accepted: {{ accept }}\n } @else {\n Any file type\n }\n \u2014 Max {{ maxSizeMB }} MB\n </div>\n\n <input\n #fileInput\n type=\"file\"\n class=\"dnd-input\"\n [attr.multiple]=\"multiple ? '' : null\"\n [attr.accept]=\"accept\"\n (change)=\"onFileSelected($event)\"\n />\n </div>\n</div>\n", styles: [":host{--dnd-accent: rgba(var(--gradient-primary-start) / 1)}.dnd-wrapper{display:block;width:100%}.dnd-area{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;min-height:160px;padding:32px 24px;border:2px dashed color-mix(in srgb,var(--dnd-accent) 40%,transparent);border-radius:12px;background:color-mix(in srgb,var(--dnd-accent) 4%,transparent);cursor:pointer;outline:none;transition:all .2s ease;position:relative}.dnd-area:hover{border-color:color-mix(in srgb,var(--dnd-accent) 70%,transparent);background:color-mix(in srgb,var(--dnd-accent) 8%,transparent)}.dnd-area:focus-visible{box-shadow:0 0 0 3px color-mix(in srgb,var(--dnd-accent) 20%,transparent)}.dnd-area.drag-over{border-color:var(--dnd-accent);background:color-mix(in srgb,var(--dnd-accent) 10%,transparent)}.dnd-icon{color:var(--dnd-accent)}.dnd-text{display:flex;align-items:center;gap:4px;font-size:14px;flex-wrap:wrap;justify-content:center}.dnd-link{color:var(--dnd-accent);font-weight:600;text-decoration:underline}.dnd-or{color:rgba(var(--text-secondary)/1)}.dnd-hint{font-size:12px;color:rgba(var(--text-secondary)/.7)}.dnd-input{position:absolute;width:0;height:0;overflow:hidden;opacity:0}@media(max-width:640px){.dnd-area{min-height:120px;padding:24px 16px}.dnd-text{font-size:13px}}\n"] }]
|
|
3006
|
+
args: [{ selector: 'fx-ui-dnd-upload', standalone: false, template: "<div\n class=\"dnd-wrapper\"\n [ngStyle]=\"accentStyle\"\n (dragover)=\"onDragOver($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\"\n>\n <div\n role=\"button\"\n tabindex=\"0\"\n class=\"dnd-area\"\n [class.drag-over]=\"isDragOver\"\n (click)=\"onAreaClick(fileInput, $event)\"\n (keydown.enter)=\"onAreaClick(fileInput, $event)\"\n >\n <fx-ui-hero-icon\n [icon]=\"icon\"\n [size]=\"40\"\n class=\"flex dnd-icon\"\n ></fx-ui-hero-icon>\n <div class=\"dnd-text\">\n <span class=\"dnd-link\">Click to upload</span>\n <span class=\"dnd-or\">or drag and drop</span>\n </div>\n <div class=\"dnd-hint\">\n @if (accept) {\n Accepted: {{ accept }}\n } @else {\n Any file type\n }\n \u2014 Max {{ maxSizeMB }} MB\n </div>\n\n <input\n #fileInput\n type=\"file\"\n class=\"dnd-input\"\n [attr.multiple]=\"multiple ? '' : null\"\n [attr.accept]=\"accept\"\n (change)=\"onFileSelected($event)\"\n />\n </div>\n</div>\n", styles: [":host{--dnd-accent: rgba(var(--gradient-primary-start) / 1)}.dnd-wrapper{display:block;width:100%}.dnd-area{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;min-height:160px;padding:32px 24px;border:2px dashed color-mix(in srgb,var(--dnd-accent) 40%,transparent);border-radius:12px;background:color-mix(in srgb,var(--dnd-accent) 4%,transparent);cursor:pointer;outline:none;transition:all .2s ease;position:relative}.dnd-area:hover{border-color:color-mix(in srgb,var(--dnd-accent) 70%,transparent);background:color-mix(in srgb,var(--dnd-accent) 8%,transparent)}.dnd-area:focus-visible{box-shadow:0 0 0 3px color-mix(in srgb,var(--dnd-accent) 20%,transparent)}.dnd-area.drag-over{border-color:var(--dnd-accent);background:color-mix(in srgb,var(--dnd-accent) 10%,transparent)}.dnd-icon{color:var(--dnd-accent)}.dnd-text{display:flex;align-items:center;gap:4px;font-size:14px;flex-wrap:wrap;justify-content:center}.dnd-link{color:var(--dnd-accent);font-weight:600;text-decoration:underline}.dnd-or{color:rgba(var(--text-secondary)/1)}.dnd-hint{font-size:12px;color:rgba(var(--text-secondary)/.7)}.dnd-input{position:absolute;width:0;height:0;overflow:hidden;opacity:0}@media (max-width: 640px){.dnd-area{min-height:120px;padding:24px 16px}.dnd-text{font-size:13px}}\n"] }]
|
|
2371
3007
|
}], propDecorators: { multiple: [{
|
|
2372
3008
|
type: Input
|
|
2373
3009
|
}], accept: [{
|
|
@@ -2405,7 +3041,7 @@ class FlowConnection {
|
|
|
2405
3041
|
];
|
|
2406
3042
|
}
|
|
2407
3043
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: FlowConnection, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2408
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.4", type: FlowConnection, isStandalone: false, selector: "fx-ui-flow-connection", inputs: { nodes: "nodes", edges: "edges", backgroundColor: "backgroundColor", view: "view" }, ngImport: i0, template: "<vflow\n [background]=\"{\n type: 'solid',\n color: `rgba(var(${backgroundColor})`\n}\"\n [nodes]=\"nodes\"\n [edges]=\"edges\"\n (onConnect)=\"handleConnection($event)\"\n [view]=\"view\"\n/>\n", styles: [""], dependencies: [{ kind: "component", type:
|
|
3044
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.4", type: FlowConnection, isStandalone: false, selector: "fx-ui-flow-connection", inputs: { nodes: "nodes", edges: "edges", backgroundColor: "backgroundColor", view: "view" }, ngImport: i0, template: "<vflow\n [background]=\"{\n type: 'solid',\n color: `rgba(var(${backgroundColor})`\n}\"\n [nodes]=\"nodes\"\n [edges]=\"edges\"\n (onConnect)=\"handleConnection($event)\"\n [view]=\"view\"\n/>\n", styles: [""], dependencies: [{ kind: "component", type: i39.VflowComponent, selector: "vflow", inputs: ["view", "minZoom", "maxZoom", "background", "optimization", "entitiesSelectable", "keyboardShortcuts", "connection", "snapGrid", "elevateNodesOnSelect", "elevateEdgesOnSelect", "nodes", "alignmentHelper", "edges"], outputs: ["onComponentNodeEvent"] }, { kind: "directive", type: i39.ConnectionControllerDirective, selector: "[onConnect], [onReconnect], [connect], [reconnect]", outputs: ["onConnect", "connect", "onReconnect", "reconnect"] }] });
|
|
2409
3045
|
}
|
|
2410
3046
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: FlowConnection, decorators: [{
|
|
2411
3047
|
type: Component,
|
|
@@ -2420,6 +3056,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImpor
|
|
|
2420
3056
|
type: Input
|
|
2421
3057
|
}] } });
|
|
2422
3058
|
|
|
3059
|
+
let nextInputId = 0;
|
|
2423
3060
|
class InputComponent extends FxComponent {
|
|
2424
3061
|
ref;
|
|
2425
3062
|
label = '';
|
|
@@ -2428,7 +3065,6 @@ class InputComponent extends FxComponent {
|
|
|
2428
3065
|
placeholder = '';
|
|
2429
3066
|
errorMessages = {};
|
|
2430
3067
|
required = false;
|
|
2431
|
-
disabled = false;
|
|
2432
3068
|
suffixIcon;
|
|
2433
3069
|
class;
|
|
2434
3070
|
maxlength;
|
|
@@ -2436,17 +3072,20 @@ class InputComponent extends FxComponent {
|
|
|
2436
3072
|
blurred = new EventEmitter();
|
|
2437
3073
|
focused = new EventEmitter();
|
|
2438
3074
|
suffixClick = new EventEmitter();
|
|
3075
|
+
/** Accessible name for the suffix icon button (it renders no text). */
|
|
3076
|
+
suffixAriaLabel = '';
|
|
3077
|
+
fieldId = `fx-input-${nextInputId++}`;
|
|
2439
3078
|
showPassword = false;
|
|
2440
3079
|
validateError = null;
|
|
2441
|
-
constructor(
|
|
2442
|
-
super(
|
|
3080
|
+
constructor(ref) {
|
|
3081
|
+
super();
|
|
2443
3082
|
this.ref = ref;
|
|
2444
3083
|
}
|
|
2445
3084
|
ngAfterViewInit() {
|
|
2446
3085
|
this.ref.detectChanges();
|
|
2447
3086
|
}
|
|
2448
|
-
|
|
2449
|
-
this.
|
|
3087
|
+
writeValue(value) {
|
|
3088
|
+
this.value = value ?? '';
|
|
2450
3089
|
}
|
|
2451
3090
|
onInput(event) {
|
|
2452
3091
|
const input = event.target;
|
|
@@ -2488,13 +3127,13 @@ class InputComponent extends FxComponent {
|
|
|
2488
3127
|
return 'number';
|
|
2489
3128
|
return this.type;
|
|
2490
3129
|
}
|
|
2491
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: InputComponent, deps: [{ token:
|
|
2492
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: InputComponent, isStandalone: false, selector: "fx-ui-input", inputs: { label: "label", tooltip: "tooltip", type: "type", placeholder: "placeholder", errorMessages: "errorMessages", required: "required",
|
|
3130
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: InputComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
3131
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: InputComponent, isStandalone: false, selector: "fx-ui-input", inputs: { label: "label", tooltip: "tooltip", type: "type", placeholder: "placeholder", errorMessages: "errorMessages", required: "required", suffixIcon: "suffixIcon", class: "class", maxlength: "maxlength", validateFn: "validateFn", suffixAriaLabel: "suffixAriaLabel" }, outputs: { blurred: "blurred", focused: "focused", suffixClick: "suffixClick" }, usesInheritance: true, ngImport: i0, template: "<div class=\"mb-4 text-left w-full text-text-primary\" [class]=\"class\">\n @if (label) {\n <label class=\"block txt-field-label\" [attr.for]=\"fieldId\">\n {{ label }}\n @if (required) {\n <span class=\"txt-required\" aria-hidden=\"true\">*</span>\n }\n @if (tooltip) {\n <fx-ui-hero-icon\n icon=\"information-circle\"\n [size]=\"16\"\n class=\"inline-block align-middle ml-0.5 text-text-secondary cursor-help relative -top-px\"\n [matTooltip]=\"tooltip\"\n matTooltipPosition=\"above\"\n matTooltipClass=\"text-text-primary text-xs font-semibold\"\n ></fx-ui-hero-icon>\n }\n </label>\n }\n\n <div class=\"relative\">\n <input\n appTrimOnBlur\n [appTrimOnBlurCollapse]=\"true\"\n [id]=\"fieldId\"\n [type]=\"displayType\"\n [placeholder]=\"placeholder\"\n [disabled]=\"disabled\"\n [value]=\"value\"\n (input)=\"onInput($event)\"\n (blur)=\"onBlur()\"\n (focus)=\"onFocus()\"\n [attr.maxlength]=\"maxlength\"\n [attr.aria-invalid]=\"invalid || null\"\n [attr.aria-describedby]=\"invalid ? fieldId + '-error' : null\"\n [attr.aria-required]=\"required ? true : null\"\n [class]=\"type === 'otp' ? 'input-otp' : 'input-default'\"\n [class.input-invalid]=\"invalid\"\n [class.pr-8]=\"type === 'password' || suffixIcon\"\n [class.txt-otp-input]=\"type === 'otp'\"\n />\n @if (type === 'password' || suffixIcon) {\n <button\n type=\"button\"\n class=\"absolute inset-y-0 right-2 flex items-center text-text-primary hover:text-text-secondary\"\n (click)=\"onSuffixClick()\"\n [disabled]=\"disabled\"\n [attr.aria-label]=\"\n type === 'password'\n ? (showPassword ? 'Hide password' : 'Show password')\n : (suffixAriaLabel || null)\n \"\n [attr.aria-pressed]=\"type === 'password' ? showPassword : null\"\n >\n @if (type === 'password') {\n <fx-ui-hero-icon\n [icon]=\"showPassword ? 'eye-slash' : 'eye'\"\n [size]=\"18\"\n class=\"text-text-primary\"\n aria-hidden=\"true\"\n ></fx-ui-hero-icon>\n } @else if (suffixIcon) {\n <fx-ui-hero-icon\n [icon]=\"suffixIcon\"\n [size]=\"18\"\n class=\"text-text-primary\"\n aria-hidden=\"true\"\n ></fx-ui-hero-icon>\n }\n </button>\n }\n </div>\n\n @if (invalid) {\n <div class=\"txt-invalid\" role=\"alert\" [id]=\"fieldId + '-error'\">\n {{ getErrorMessage(label) }}\n </div>\n }\n</div>\n", styles: [""], dependencies: [{ kind: "directive", type: TrimOnBlurDirective, selector: "[appTrimOnBlur]", inputs: ["appTrimOnBlurCollapse"] }, { kind: "directive", type: i57.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: HeroIconComponent, selector: "fx-ui-hero-icon", inputs: ["stroke", "icon", "solid", "outline", "size", "color", "class"] }] });
|
|
2493
3132
|
}
|
|
2494
3133
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: InputComponent, decorators: [{
|
|
2495
3134
|
type: Component,
|
|
2496
|
-
args: [{ selector: 'fx-ui-input', standalone: false, template: "<div class=\"mb-4 text-left w-full text-text-primary\" [class]=\"class\">\n @if (label) {\n <label class=\"block txt-field-label\">\n {{ label }}\n @if (required) {\n <span class=\"txt-required\">*</span>\n }\n @if (tooltip) {\n <fx-ui-hero-icon\n icon=\"information-circle\"\n [size]=\"16\"\n class=\"inline-block align-middle ml-0.5 text-text-secondary cursor-help relative -top-px\"\n [matTooltip]=\"tooltip\"\n matTooltipPosition=\"above\"\n matTooltipClass=\"text-text-primary text-xs font-semibold\"\n ></fx-ui-hero-icon>\n }\n </label>\n }\n\n <div class=\"relative\">\n <input\n appTrimOnBlur\n [appTrimOnBlurCollapse]=\"true\"\n [type]=\"displayType\"\n [placeholder]=\"placeholder\"\n [disabled]=\"disabled\"\n [value]=\"value\"\n (input)=\"onInput($event)\"\n (blur)=\"onBlur()\"\n (focus)=\"onFocus()\"\n [attr.maxlength]=\"maxlength\"\n [class]=\"type === 'otp' ? 'input-otp' : 'input-default'\"\n [class.input-invalid]=\"invalid\"\n [class.pr-8]=\"type === 'password' || suffixIcon\"\n [class.txt-otp-input]=\"type === 'otp'\"\n />\n @if (type === 'password' || suffixIcon) {\n <button\n type=\"button\"\n class=\"absolute inset-y-0 right-2 flex items-center text-text-primary hover:text-text-secondary\"\n (click)=\"onSuffixClick()\"\n [disabled]=\"disabled\"\n >\n @if (type === 'password') {\n <fx-ui-hero-icon\n [icon]=\"showPassword ? 'eye-slash' : 'eye'\"\n [size]=\"18\"\n class=\"text-text-primary\"\n ></fx-ui-hero-icon>\n } @else if (suffixIcon) {\n <fx-ui-hero-icon\n [icon]=\"suffixIcon\"\n [size]=\"18\"\n class=\"text-text-primary\"\n ></fx-ui-hero-icon>\n }\n </button>\n }\n </div>\n\n @if (invalid) {\n <div class=\"txt-invalid\">\n {{ getErrorMessage(label) }}\n </div>\n }\n</div>\n" }]
|
|
2497
|
-
}], ctorParameters: () => [{ type:
|
|
3135
|
+
args: [{ selector: 'fx-ui-input', standalone: false, template: "<div class=\"mb-4 text-left w-full text-text-primary\" [class]=\"class\">\n @if (label) {\n <label class=\"block txt-field-label\" [attr.for]=\"fieldId\">\n {{ label }}\n @if (required) {\n <span class=\"txt-required\" aria-hidden=\"true\">*</span>\n }\n @if (tooltip) {\n <fx-ui-hero-icon\n icon=\"information-circle\"\n [size]=\"16\"\n class=\"inline-block align-middle ml-0.5 text-text-secondary cursor-help relative -top-px\"\n [matTooltip]=\"tooltip\"\n matTooltipPosition=\"above\"\n matTooltipClass=\"text-text-primary text-xs font-semibold\"\n ></fx-ui-hero-icon>\n }\n </label>\n }\n\n <div class=\"relative\">\n <input\n appTrimOnBlur\n [appTrimOnBlurCollapse]=\"true\"\n [id]=\"fieldId\"\n [type]=\"displayType\"\n [placeholder]=\"placeholder\"\n [disabled]=\"disabled\"\n [value]=\"value\"\n (input)=\"onInput($event)\"\n (blur)=\"onBlur()\"\n (focus)=\"onFocus()\"\n [attr.maxlength]=\"maxlength\"\n [attr.aria-invalid]=\"invalid || null\"\n [attr.aria-describedby]=\"invalid ? fieldId + '-error' : null\"\n [attr.aria-required]=\"required ? true : null\"\n [class]=\"type === 'otp' ? 'input-otp' : 'input-default'\"\n [class.input-invalid]=\"invalid\"\n [class.pr-8]=\"type === 'password' || suffixIcon\"\n [class.txt-otp-input]=\"type === 'otp'\"\n />\n @if (type === 'password' || suffixIcon) {\n <button\n type=\"button\"\n class=\"absolute inset-y-0 right-2 flex items-center text-text-primary hover:text-text-secondary\"\n (click)=\"onSuffixClick()\"\n [disabled]=\"disabled\"\n [attr.aria-label]=\"\n type === 'password'\n ? (showPassword ? 'Hide password' : 'Show password')\n : (suffixAriaLabel || null)\n \"\n [attr.aria-pressed]=\"type === 'password' ? showPassword : null\"\n >\n @if (type === 'password') {\n <fx-ui-hero-icon\n [icon]=\"showPassword ? 'eye-slash' : 'eye'\"\n [size]=\"18\"\n class=\"text-text-primary\"\n aria-hidden=\"true\"\n ></fx-ui-hero-icon>\n } @else if (suffixIcon) {\n <fx-ui-hero-icon\n [icon]=\"suffixIcon\"\n [size]=\"18\"\n class=\"text-text-primary\"\n aria-hidden=\"true\"\n ></fx-ui-hero-icon>\n }\n </button>\n }\n </div>\n\n @if (invalid) {\n <div class=\"txt-invalid\" role=\"alert\" [id]=\"fieldId + '-error'\">\n {{ getErrorMessage(label) }}\n </div>\n }\n</div>\n" }]
|
|
3136
|
+
}], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { label: [{
|
|
2498
3137
|
type: Input
|
|
2499
3138
|
}], tooltip: [{
|
|
2500
3139
|
type: Input
|
|
@@ -2506,8 +3145,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImpor
|
|
|
2506
3145
|
type: Input
|
|
2507
3146
|
}], required: [{
|
|
2508
3147
|
type: Input
|
|
2509
|
-
}], disabled: [{
|
|
2510
|
-
type: Input
|
|
2511
3148
|
}], suffixIcon: [{
|
|
2512
3149
|
type: Input
|
|
2513
3150
|
}], class: [{
|
|
@@ -2522,13 +3159,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImpor
|
|
|
2522
3159
|
type: Output
|
|
2523
3160
|
}], suffixClick: [{
|
|
2524
3161
|
type: Output
|
|
3162
|
+
}], suffixAriaLabel: [{
|
|
3163
|
+
type: Input
|
|
2525
3164
|
}] } });
|
|
2526
3165
|
|
|
2527
3166
|
class LoadingPanel {
|
|
2528
3167
|
show = false;
|
|
2529
3168
|
message = "Loading";
|
|
2530
3169
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: LoadingPanel, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2531
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.4", type: LoadingPanel, isStandalone: false, selector: "fx-ui-loading-panel", inputs: { show: "show", message: "message" }, ngImport: i0, template: "<!-- Full-screen glass overlay -->\n<div\n *ngIf=\"show\"\n class=\"fixed inset-0 z-50 flex items-center justify-center\"\n aria-hidden=\"false\"\n role=\"alert\"\n aria-busy=\"true\"\n>\n <div class=\"absolute inset-0 bg-bg-primary/30 backdrop-blur-xl\" aria-hidden=\"true\"></div>\n\n <div\n class=\"relative z-10 flex flex-col items-center gap-4 p-6 rounded-2xl shadow-xl bg-bg-primary/70 border border-border-strong backdrop-bg-primary/70\"\n style=\"min-width: 220px; max-width: 90%\"\n >\n <svg fill=\"url(#spinner-gradient)\" width=\"50\" height=\"50\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\">\n <defs>\n <linearGradient id=\"spinner-gradient\" x1=\"0%\" y1=\"0%\" x2=\"100%\" y2=\"0%\">\n <stop offset=\"0%\" style=\"stop-color: rgb(var(--gradient-primary-start)); stop-opacity: 1\" />\n <stop offset=\"100%\" style=\"stop-color: rgb(var(--gradient-primary-end)); stop-opacity: 1\" />\n </linearGradient>\n </defs>\n <circle cx=\"4\" cy=\"12\" r=\"0\">\n <animate begin=\"0;spinner_z0Or.end\" attributeName=\"r\" calcMode=\"spline\" dur=\"0.5s\" keySplines=\".36,.6,.31,1\" values=\"0;3\" fill=\"freeze\"/>\n <animate begin=\"spinner_OLMs.end\" attributeName=\"cx\" calcMode=\"spline\" dur=\"0.5s\" keySplines=\".36,.6,.31,1\" values=\"4;12\" fill=\"freeze\"/>\n <animate begin=\"spinner_UHR2.end\" attributeName=\"cx\" calcMode=\"spline\" dur=\"0.5s\" keySplines=\".36,.6,.31,1\" values=\"12;20\" fill=\"freeze\"/>\n <animate id=\"spinner_lo66\" begin=\"spinner_Aguh.end\" attributeName=\"r\" calcMode=\"spline\" dur=\"0.5s\" keySplines=\".36,.6,.31,1\" values=\"3;0\" fill=\"freeze\"/>\n <animate id=\"spinner_z0Or\" begin=\"spinner_lo66.end\" attributeName=\"cx\" dur=\"0.001s\" values=\"20;4\" fill=\"freeze\"/>\n </circle>\n <circle cx=\"4\" cy=\"12\" r=\"3\">\n <animate begin=\"0;spinner_z0Or.end\" attributeName=\"cx\" calcMode=\"spline\" dur=\"0.5s\" keySplines=\".36,.6,.31,1\" values=\"4;12\" fill=\"freeze\"/>\n <animate begin=\"spinner_OLMs.end\" attributeName=\"cx\" calcMode=\"spline\" dur=\"0.5s\" keySplines=\".36,.6,.31,1\" values=\"12;20\" fill=\"freeze\"/>\n <animate id=\"spinner_JsnR\" begin=\"spinner_UHR2.end\" attributeName=\"r\" calcMode=\"spline\" dur=\"0.5s\" keySplines=\".36,.6,.31,1\" values=\"3;0\" fill=\"freeze\"/>\n <animate id=\"spinner_Aguh\" begin=\"spinner_JsnR.end\" attributeName=\"cx\" dur=\"0.001s\" values=\"20;4\" fill=\"freeze\"/>\n <animate begin=\"spinner_Aguh.end\" attributeName=\"r\" calcMode=\"spline\" dur=\"0.5s\" keySplines=\".36,.6,.31,1\" values=\"0;3\" fill=\"freeze\"/>\n </circle>\n <circle cx=\"12\" cy=\"12\" r=\"3\">\n <animate begin=\"0;spinner_z0Or.end\" attributeName=\"cx\" calcMode=\"spline\" dur=\"0.5s\" keySplines=\".36,.6,.31,1\" values=\"12;20\" fill=\"freeze\"/>\n <animate id=\"spinner_hSjk\" begin=\"spinner_OLMs.end\" attributeName=\"r\" calcMode=\"spline\" dur=\"0.5s\" keySplines=\".36,.6,.31,1\" values=\"3;0\" fill=\"freeze\"/>\n <animate id=\"spinner_UHR2\" begin=\"spinner_hSjk.end\" attributeName=\"cx\" dur=\"0.001s\" values=\"20;4\" fill=\"freeze\"/>\n <animate begin=\"spinner_UHR2.end\" attributeName=\"r\" calcMode=\"spline\" dur=\"0.5s\" keySplines=\".36,.6,.31,1\" values=\"0;3\" fill=\"freeze\"/>\n <animate begin=\"spinner_Aguh.end\" attributeName=\"cx\" calcMode=\"spline\" dur=\"0.5s\" keySplines=\".36,.6,.31,1\" values=\"4;12\" fill=\"freeze\"/>\n </circle>\n <circle cx=\"20\" cy=\"12\" r=\"3\">\n <animate id=\"spinner_4v5M\" begin=\"0;spinner_z0Or.end\" attributeName=\"r\" calcMode=\"spline\" dur=\"0.5s\" keySplines=\".36,.6,.31,1\" values=\"3;0\" fill=\"freeze\"/>\n <animate id=\"spinner_OLMs\" begin=\"spinner_4v5M.end\" attributeName=\"cx\" dur=\"0.001s\" values=\"20;4\" fill=\"freeze\"/>\n <animate begin=\"spinner_OLMs.end\" attributeName=\"r\" calcMode=\"spline\" dur=\"0.5s\" keySplines=\".36,.6,.31,1\" values=\"0;3\" fill=\"freeze\"/>\n <animate begin=\"spinner_UHR2.end\" attributeName=\"cx\" calcMode=\"spline\" dur=\"0.5s\" keySplines=\".36,.6,.31,1\" values=\"4;12\" fill=\"freeze\"/>\n <animate begin=\"spinner_Aguh.end\" attributeName=\"cx\" calcMode=\"spline\" dur=\"0.5s\" keySplines=\".36,.6,.31,1\" values=\"12;20\" fill=\"freeze\"/>\n </circle>\n </svg>\n \n\n <div *ngIf=\"message\" class=\"text-sm text-text-primary text-center\">\n {{ message }}\n </div>\n </div>\n</div>\n", styles: [""], dependencies: [{ kind: "directive", type:
|
|
3170
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.4", type: LoadingPanel, isStandalone: false, selector: "fx-ui-loading-panel", inputs: { show: "show", message: "message" }, ngImport: i0, template: "<!-- Full-screen glass overlay -->\n<div\n *ngIf=\"show\"\n class=\"fixed inset-0 z-50 flex items-center justify-center\"\n aria-hidden=\"false\"\n role=\"alert\"\n aria-busy=\"true\"\n>\n <div class=\"absolute inset-0 bg-bg-primary/30 backdrop-blur-xl\" aria-hidden=\"true\"></div>\n\n <div\n class=\"relative z-10 flex flex-col items-center gap-4 p-6 rounded-2xl shadow-xl bg-bg-primary/70 border border-border-strong backdrop-bg-primary/70\"\n style=\"min-width: 220px; max-width: 90%\"\n >\n <svg fill=\"url(#spinner-gradient)\" width=\"50\" height=\"50\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\">\n <defs>\n <linearGradient id=\"spinner-gradient\" x1=\"0%\" y1=\"0%\" x2=\"100%\" y2=\"0%\">\n <stop offset=\"0%\" style=\"stop-color: rgb(var(--gradient-primary-start)); stop-opacity: 1\" />\n <stop offset=\"100%\" style=\"stop-color: rgb(var(--gradient-primary-end)); stop-opacity: 1\" />\n </linearGradient>\n </defs>\n <circle cx=\"4\" cy=\"12\" r=\"0\">\n <animate begin=\"0;spinner_z0Or.end\" attributeName=\"r\" calcMode=\"spline\" dur=\"0.5s\" keySplines=\".36,.6,.31,1\" values=\"0;3\" fill=\"freeze\"/>\n <animate begin=\"spinner_OLMs.end\" attributeName=\"cx\" calcMode=\"spline\" dur=\"0.5s\" keySplines=\".36,.6,.31,1\" values=\"4;12\" fill=\"freeze\"/>\n <animate begin=\"spinner_UHR2.end\" attributeName=\"cx\" calcMode=\"spline\" dur=\"0.5s\" keySplines=\".36,.6,.31,1\" values=\"12;20\" fill=\"freeze\"/>\n <animate id=\"spinner_lo66\" begin=\"spinner_Aguh.end\" attributeName=\"r\" calcMode=\"spline\" dur=\"0.5s\" keySplines=\".36,.6,.31,1\" values=\"3;0\" fill=\"freeze\"/>\n <animate id=\"spinner_z0Or\" begin=\"spinner_lo66.end\" attributeName=\"cx\" dur=\"0.001s\" values=\"20;4\" fill=\"freeze\"/>\n </circle>\n <circle cx=\"4\" cy=\"12\" r=\"3\">\n <animate begin=\"0;spinner_z0Or.end\" attributeName=\"cx\" calcMode=\"spline\" dur=\"0.5s\" keySplines=\".36,.6,.31,1\" values=\"4;12\" fill=\"freeze\"/>\n <animate begin=\"spinner_OLMs.end\" attributeName=\"cx\" calcMode=\"spline\" dur=\"0.5s\" keySplines=\".36,.6,.31,1\" values=\"12;20\" fill=\"freeze\"/>\n <animate id=\"spinner_JsnR\" begin=\"spinner_UHR2.end\" attributeName=\"r\" calcMode=\"spline\" dur=\"0.5s\" keySplines=\".36,.6,.31,1\" values=\"3;0\" fill=\"freeze\"/>\n <animate id=\"spinner_Aguh\" begin=\"spinner_JsnR.end\" attributeName=\"cx\" dur=\"0.001s\" values=\"20;4\" fill=\"freeze\"/>\n <animate begin=\"spinner_Aguh.end\" attributeName=\"r\" calcMode=\"spline\" dur=\"0.5s\" keySplines=\".36,.6,.31,1\" values=\"0;3\" fill=\"freeze\"/>\n </circle>\n <circle cx=\"12\" cy=\"12\" r=\"3\">\n <animate begin=\"0;spinner_z0Or.end\" attributeName=\"cx\" calcMode=\"spline\" dur=\"0.5s\" keySplines=\".36,.6,.31,1\" values=\"12;20\" fill=\"freeze\"/>\n <animate id=\"spinner_hSjk\" begin=\"spinner_OLMs.end\" attributeName=\"r\" calcMode=\"spline\" dur=\"0.5s\" keySplines=\".36,.6,.31,1\" values=\"3;0\" fill=\"freeze\"/>\n <animate id=\"spinner_UHR2\" begin=\"spinner_hSjk.end\" attributeName=\"cx\" dur=\"0.001s\" values=\"20;4\" fill=\"freeze\"/>\n <animate begin=\"spinner_UHR2.end\" attributeName=\"r\" calcMode=\"spline\" dur=\"0.5s\" keySplines=\".36,.6,.31,1\" values=\"0;3\" fill=\"freeze\"/>\n <animate begin=\"spinner_Aguh.end\" attributeName=\"cx\" calcMode=\"spline\" dur=\"0.5s\" keySplines=\".36,.6,.31,1\" values=\"4;12\" fill=\"freeze\"/>\n </circle>\n <circle cx=\"20\" cy=\"12\" r=\"3\">\n <animate id=\"spinner_4v5M\" begin=\"0;spinner_z0Or.end\" attributeName=\"r\" calcMode=\"spline\" dur=\"0.5s\" keySplines=\".36,.6,.31,1\" values=\"3;0\" fill=\"freeze\"/>\n <animate id=\"spinner_OLMs\" begin=\"spinner_4v5M.end\" attributeName=\"cx\" dur=\"0.001s\" values=\"20;4\" fill=\"freeze\"/>\n <animate begin=\"spinner_OLMs.end\" attributeName=\"r\" calcMode=\"spline\" dur=\"0.5s\" keySplines=\".36,.6,.31,1\" values=\"0;3\" fill=\"freeze\"/>\n <animate begin=\"spinner_UHR2.end\" attributeName=\"cx\" calcMode=\"spline\" dur=\"0.5s\" keySplines=\".36,.6,.31,1\" values=\"4;12\" fill=\"freeze\"/>\n <animate begin=\"spinner_Aguh.end\" attributeName=\"cx\" calcMode=\"spline\" dur=\"0.5s\" keySplines=\".36,.6,.31,1\" values=\"12;20\" fill=\"freeze\"/>\n </circle>\n </svg>\n \n\n <div *ngIf=\"message\" class=\"text-sm text-text-primary text-center\">\n {{ message }}\n </div>\n </div>\n</div>\n", styles: [""], dependencies: [{ kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
|
|
2532
3171
|
}
|
|
2533
3172
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: LoadingPanel, decorators: [{
|
|
2534
3173
|
type: Component,
|
|
@@ -2539,24 +3178,22 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImpor
|
|
|
2539
3178
|
type: Input
|
|
2540
3179
|
}] } });
|
|
2541
3180
|
|
|
3181
|
+
let nextToggleGroupId = 0;
|
|
2542
3182
|
class RadioButtonToggleComponent extends FxComponent {
|
|
2543
3183
|
ref;
|
|
3184
|
+
groupId = `fx-radio-toggle-${nextToggleGroupId++}`;
|
|
2544
3185
|
label;
|
|
2545
3186
|
required = false;
|
|
2546
3187
|
options = [];
|
|
2547
|
-
disabled = false;
|
|
2548
3188
|
errorMessages = {};
|
|
2549
3189
|
class;
|
|
2550
|
-
constructor(
|
|
2551
|
-
super(
|
|
3190
|
+
constructor(ref) {
|
|
3191
|
+
super();
|
|
2552
3192
|
this.ref = ref;
|
|
2553
3193
|
}
|
|
2554
3194
|
ngAfterViewInit() {
|
|
2555
3195
|
this.ref.detectChanges();
|
|
2556
3196
|
}
|
|
2557
|
-
setDisabledState(isDisabled) {
|
|
2558
|
-
this.disabled = isDisabled;
|
|
2559
|
-
}
|
|
2560
3197
|
onSelectValue(value) {
|
|
2561
3198
|
if (this.disabled)
|
|
2562
3199
|
return;
|
|
@@ -2564,45 +3201,41 @@ class RadioButtonToggleComponent extends FxComponent {
|
|
|
2564
3201
|
this.onChange(value);
|
|
2565
3202
|
this.onTouched();
|
|
2566
3203
|
}
|
|
2567
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: RadioButtonToggleComponent, deps: [{ token:
|
|
2568
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: RadioButtonToggleComponent, isStandalone: false, selector: "fx-ui-radio-toggle, [fx-ui-radio-toggle]", inputs: { label: "label", required: "required", options: "options",
|
|
3204
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: RadioButtonToggleComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
3205
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: RadioButtonToggleComponent, isStandalone: false, selector: "fx-ui-radio-toggle, [fx-ui-radio-toggle]", inputs: { label: "label", required: "required", options: "options", errorMessages: "errorMessages", class: "class" }, usesInheritance: true, ngImport: i0, template: "<div class=\"flex flex-col text-left mb-4 w-full\" [class]=\"class\">\n <!-- A radiogroup has no single control a <label> could point at \u2014 name it via aria-labelledby. -->\n @if(label){<span class=\"txt-field-label\" [id]=\"groupId + '-label'\">\n {{ label }}\n @if(required) {<span class=\"txt-required\" aria-hidden=\"true\">*</span>} </span\n >}\n\n <div\n class=\"inline-flex w-full rounded-lg overflow-hidden\"\n role=\"radiogroup\"\n [attr.aria-labelledby]=\"label ? groupId + '-label' : null\"\n >\n @for(opt of options; track opt){<button\n type=\"button\"\n role=\"radio\"\n [attr.aria-checked]=\"value === opt.value\"\n (click)=\"onSelectValue(opt.value)\"\n [disabled]=\"disabled\"\n [class.text-gray-900]=\"value !== opt.value\"\n class=\"flex-1 bg-bg-primary flex items-center pl-normal gap-normal h-10 rounded-md hover:bg-bg-hover transition-all duration-150 disabled:opacity-60 disabled:cursor-not-allowed\"\n >\n <span\n [ngClass]=\"{\n 'radio-checked': value === opt.value,\n 'radio-uncheck': value !== opt.value\n }\"\n class=\"radio-container\"\n >\n @if(value === opt.value) {<span class=\"radio-dot\"></span>}\n </span>\n\n <span class=\"txt-field-label mb-0\">{{ opt.label }}</span></button\n >}\n </div>\n\n @if(invalid){\n <div class=\"txt-invalid\">{{ getErrorMessage(label) }}</div>\n }\n</div>\n", styles: [""], dependencies: [{ kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] });
|
|
2569
3206
|
}
|
|
2570
3207
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: RadioButtonToggleComponent, decorators: [{
|
|
2571
3208
|
type: Component,
|
|
2572
|
-
args: [{ selector: 'fx-ui-radio-toggle, [fx-ui-radio-toggle]', standalone: false, template: "<div class=\"flex flex-col text-left mb-4 w-full\" [class]=\"class\">\n @if(label){<
|
|
2573
|
-
}], ctorParameters: () => [{ type:
|
|
3209
|
+
args: [{ selector: 'fx-ui-radio-toggle, [fx-ui-radio-toggle]', standalone: false, template: "<div class=\"flex flex-col text-left mb-4 w-full\" [class]=\"class\">\n <!-- A radiogroup has no single control a <label> could point at \u2014 name it via aria-labelledby. -->\n @if(label){<span class=\"txt-field-label\" [id]=\"groupId + '-label'\">\n {{ label }}\n @if(required) {<span class=\"txt-required\" aria-hidden=\"true\">*</span>} </span\n >}\n\n <div\n class=\"inline-flex w-full rounded-lg overflow-hidden\"\n role=\"radiogroup\"\n [attr.aria-labelledby]=\"label ? groupId + '-label' : null\"\n >\n @for(opt of options; track opt){<button\n type=\"button\"\n role=\"radio\"\n [attr.aria-checked]=\"value === opt.value\"\n (click)=\"onSelectValue(opt.value)\"\n [disabled]=\"disabled\"\n [class.text-gray-900]=\"value !== opt.value\"\n class=\"flex-1 bg-bg-primary flex items-center pl-normal gap-normal h-10 rounded-md hover:bg-bg-hover transition-all duration-150 disabled:opacity-60 disabled:cursor-not-allowed\"\n >\n <span\n [ngClass]=\"{\n 'radio-checked': value === opt.value,\n 'radio-uncheck': value !== opt.value\n }\"\n class=\"radio-container\"\n >\n @if(value === opt.value) {<span class=\"radio-dot\"></span>}\n </span>\n\n <span class=\"txt-field-label mb-0\">{{ opt.label }}</span></button\n >}\n </div>\n\n @if(invalid){\n <div class=\"txt-invalid\">{{ getErrorMessage(label) }}</div>\n }\n</div>\n" }]
|
|
3210
|
+
}], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { label: [{
|
|
2574
3211
|
type: Input
|
|
2575
3212
|
}], required: [{
|
|
2576
3213
|
type: Input
|
|
2577
3214
|
}], options: [{
|
|
2578
3215
|
type: Input
|
|
2579
|
-
}], disabled: [{
|
|
2580
|
-
type: Input
|
|
2581
3216
|
}], errorMessages: [{
|
|
2582
3217
|
type: Input
|
|
2583
3218
|
}], class: [{
|
|
2584
3219
|
type: Input
|
|
2585
3220
|
}] } });
|
|
2586
3221
|
|
|
3222
|
+
let nextRadioGroupId = 0;
|
|
2587
3223
|
class RadioButtonComponent extends FxComponent {
|
|
2588
3224
|
ref;
|
|
3225
|
+
groupId = `fx-radio-${nextRadioGroupId++}`;
|
|
2589
3226
|
label;
|
|
2590
3227
|
tooltip = '';
|
|
2591
3228
|
required = false;
|
|
2592
3229
|
options = [];
|
|
2593
|
-
disabled = false;
|
|
2594
3230
|
errorMessages = {};
|
|
2595
3231
|
class;
|
|
2596
|
-
constructor(
|
|
2597
|
-
super(
|
|
3232
|
+
constructor(ref) {
|
|
3233
|
+
super();
|
|
2598
3234
|
this.ref = ref;
|
|
2599
3235
|
}
|
|
2600
3236
|
ngAfterViewInit() {
|
|
2601
3237
|
this.ref.detectChanges();
|
|
2602
3238
|
}
|
|
2603
|
-
setDisabledState(isDisabled) {
|
|
2604
|
-
this.disabled = isDisabled;
|
|
2605
|
-
}
|
|
2606
3239
|
onSelectValue(value) {
|
|
2607
3240
|
if (this.disabled)
|
|
2608
3241
|
return;
|
|
@@ -2610,13 +3243,13 @@ class RadioButtonComponent extends FxComponent {
|
|
|
2610
3243
|
this.onChange(value);
|
|
2611
3244
|
this.onTouched();
|
|
2612
3245
|
}
|
|
2613
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: RadioButtonComponent, deps: [{ token:
|
|
2614
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: RadioButtonComponent, isStandalone: false, selector: "fx-ui-radio", inputs: { label: "label", tooltip: "tooltip", required: "required", options: "options",
|
|
3246
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: RadioButtonComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
3247
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: RadioButtonComponent, isStandalone: false, selector: "fx-ui-radio", inputs: { label: "label", tooltip: "tooltip", required: "required", options: "options", errorMessages: "errorMessages", class: "class" }, usesInheritance: true, ngImport: i0, template: "<div\n class=\"w-full rounded-lg overflow-hidden\"\n [class]=\"class\"\n role=\"radiogroup\"\n [attr.aria-labelledby]=\"label ? groupId + '-label' : null\"\n>\n <!-- A radiogroup has no single control a <label> could point at \u2014 name it via aria-labelledby. -->\n @if (label) {\n <span class=\"txt-field-label block\" [id]=\"groupId + '-label'\">\n {{ label }}\n @if (required) {\n <span class=\"txt-required\" aria-hidden=\"true\">*</span>\n }\n @if (tooltip) {\n <fx-ui-hero-icon\n icon=\"information-circle\"\n [size]=\"16\"\n class=\"inline-block align-middle ml-0.5 text-text-secondary cursor-help relative -top-px\"\n [matTooltip]=\"tooltip\"\n matTooltipPosition=\"above\"\n matTooltipClass=\"text-text-primary text-xs font-semibold\"\n ></fx-ui-hero-icon>\n }\n </span>\n }\n\n <button\n *ngFor=\"let opt of options\"\n type=\"button\"\n role=\"radio\"\n [attr.aria-checked]=\"value === opt.value\"\n (click)=\"onSelectValue(opt.value)\"\n [disabled]=\"disabled\"\n [class.text-gray-900]=\"value !== opt.value\"\n class=\"flex-1 px-normal w-full flex items-center gap-normal h-10 rounded-md hover:bg-bg-hover transition-all duration-150 disabled:opacity-60 disabled:cursor-not-allowed\"\n [matTooltip]=\"opt.tooltip || ''\"\n matTooltipPosition=\"right\"\n matTooltipClass=\"text-text-primary text-xs font-semibold\"\n >\n <span\n [ngClass]=\"{\n 'radio-checked': value === opt.value,\n 'radio-uncheck': value !== opt.value,\n }\"\n class=\"radio-container\"\n >\n @if (value === opt.value) {\n <span class=\"radio-dot\"></span>\n }\n </span>\n\n <span class=\"txt-default mb-0\">{{ opt.label }}</span>\n </button>\n @if (invalid) {\n <div class=\"txt-invalid\">\n {{ getErrorMessage(label) }}\n </div>\n }\n</div>\n", styles: [""], dependencies: [{ kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i57.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: HeroIconComponent, selector: "fx-ui-hero-icon", inputs: ["stroke", "icon", "solid", "outline", "size", "color", "class"] }] });
|
|
2615
3248
|
}
|
|
2616
3249
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: RadioButtonComponent, decorators: [{
|
|
2617
3250
|
type: Component,
|
|
2618
|
-
args: [{ selector: 'fx-ui-radio', standalone: false, template: "<div
|
|
2619
|
-
}], ctorParameters: () => [{ type:
|
|
3251
|
+
args: [{ selector: 'fx-ui-radio', standalone: false, template: "<div\n class=\"w-full rounded-lg overflow-hidden\"\n [class]=\"class\"\n role=\"radiogroup\"\n [attr.aria-labelledby]=\"label ? groupId + '-label' : null\"\n>\n <!-- A radiogroup has no single control a <label> could point at \u2014 name it via aria-labelledby. -->\n @if (label) {\n <span class=\"txt-field-label block\" [id]=\"groupId + '-label'\">\n {{ label }}\n @if (required) {\n <span class=\"txt-required\" aria-hidden=\"true\">*</span>\n }\n @if (tooltip) {\n <fx-ui-hero-icon\n icon=\"information-circle\"\n [size]=\"16\"\n class=\"inline-block align-middle ml-0.5 text-text-secondary cursor-help relative -top-px\"\n [matTooltip]=\"tooltip\"\n matTooltipPosition=\"above\"\n matTooltipClass=\"text-text-primary text-xs font-semibold\"\n ></fx-ui-hero-icon>\n }\n </span>\n }\n\n <button\n *ngFor=\"let opt of options\"\n type=\"button\"\n role=\"radio\"\n [attr.aria-checked]=\"value === opt.value\"\n (click)=\"onSelectValue(opt.value)\"\n [disabled]=\"disabled\"\n [class.text-gray-900]=\"value !== opt.value\"\n class=\"flex-1 px-normal w-full flex items-center gap-normal h-10 rounded-md hover:bg-bg-hover transition-all duration-150 disabled:opacity-60 disabled:cursor-not-allowed\"\n [matTooltip]=\"opt.tooltip || ''\"\n matTooltipPosition=\"right\"\n matTooltipClass=\"text-text-primary text-xs font-semibold\"\n >\n <span\n [ngClass]=\"{\n 'radio-checked': value === opt.value,\n 'radio-uncheck': value !== opt.value,\n }\"\n class=\"radio-container\"\n >\n @if (value === opt.value) {\n <span class=\"radio-dot\"></span>\n }\n </span>\n\n <span class=\"txt-default mb-0\">{{ opt.label }}</span>\n </button>\n @if (invalid) {\n <div class=\"txt-invalid\">\n {{ getErrorMessage(label) }}\n </div>\n }\n</div>\n" }]
|
|
3252
|
+
}], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { label: [{
|
|
2620
3253
|
type: Input
|
|
2621
3254
|
}], tooltip: [{
|
|
2622
3255
|
type: Input
|
|
@@ -2624,8 +3257,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImpor
|
|
|
2624
3257
|
type: Input
|
|
2625
3258
|
}], options: [{
|
|
2626
3259
|
type: Input
|
|
2627
|
-
}], disabled: [{
|
|
2628
|
-
type: Input
|
|
2629
3260
|
}], errorMessages: [{
|
|
2630
3261
|
type: Input
|
|
2631
3262
|
}], class: [{
|
|
@@ -2654,8 +3285,8 @@ class RichTextAreaComponent extends FxComponent {
|
|
|
2654
3285
|
];
|
|
2655
3286
|
contentChange = new EventEmitter();
|
|
2656
3287
|
quillInstance;
|
|
2657
|
-
constructor(
|
|
2658
|
-
super(
|
|
3288
|
+
constructor(cdr, styleLoader, platformId) {
|
|
3289
|
+
super();
|
|
2659
3290
|
this.cdr = cdr;
|
|
2660
3291
|
this.styleLoader = styleLoader;
|
|
2661
3292
|
this.platformId = platformId;
|
|
@@ -2698,17 +3329,18 @@ class RichTextAreaComponent extends FxComponent {
|
|
|
2698
3329
|
}
|
|
2699
3330
|
}
|
|
2700
3331
|
setDisabledState(isDisabled) {
|
|
3332
|
+
this.disabled = isDisabled;
|
|
2701
3333
|
if (this.quillInstance) {
|
|
2702
3334
|
this.quillInstance.enable(!isDisabled);
|
|
2703
3335
|
}
|
|
2704
3336
|
}
|
|
2705
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: RichTextAreaComponent, deps: [{ token:
|
|
2706
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.4", type: RichTextAreaComponent, isStandalone: false, selector: "fx-ui-rich-text-area", inputs: { placeholder: "placeholder", toolbarOptions: "toolbarOptions" }, outputs: { contentChange: "contentChange" }, viewQueries: [{ propertyName: "editorContainer", first: true, predicate: ["editorContainer"], descendants: true, static: true }], usesInheritance: true, ngImport: i0, template: "<div class=\"w-full rounded-2xl shadow-sm p-1 bg-bg-primary\">\n <div #editorContainer class=\"min-h-[200px] text-base\"></div>\n</div>", styles: ["
|
|
3337
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: RichTextAreaComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: QuillStyleLoaderService }, { token: PLATFORM_ID }], target: i0.ɵɵFactoryTarget.Component });
|
|
3338
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.4", type: RichTextAreaComponent, isStandalone: false, selector: "fx-ui-rich-text-area", inputs: { placeholder: "placeholder", toolbarOptions: "toolbarOptions" }, outputs: { contentChange: "contentChange" }, viewQueries: [{ propertyName: "editorContainer", first: true, predicate: ["editorContainer"], descendants: true, static: true }], usesInheritance: true, ngImport: i0, template: "<div class=\"w-full rounded-2xl shadow-sm p-1 bg-bg-primary\">\n <div #editorContainer class=\"min-h-[200px] text-base\"></div>\n</div>", styles: ["@charset \"UTF-8\";@layer properties;:host{display:block}.ql-toolbar{border-top-left-radius:.25rem;border-top-right-radius:.25rem;border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:rgb(var(--border-default));padding:calc(var(--spacing, .25rem) * 2)}.ql-container{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.ql-editor{min-height:200px;padding:calc(var(--spacing, .25rem) * 3);font-size:1rem;line-height:var(--tw-leading, var(--text-base--line-height, 1.5 ))}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style: solid}}}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"] });
|
|
2707
3339
|
}
|
|
2708
3340
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: RichTextAreaComponent, decorators: [{
|
|
2709
3341
|
type: Component,
|
|
2710
|
-
args: [{ selector: 'fx-ui-rich-text-area', standalone: false, template: "<div class=\"w-full rounded-2xl shadow-sm p-1 bg-bg-primary\">\n <div #editorContainer class=\"min-h-[200px] text-base\"></div>\n</div>", styles: ["
|
|
2711
|
-
}], ctorParameters: () => [{ type:
|
|
3342
|
+
args: [{ selector: 'fx-ui-rich-text-area', standalone: false, template: "<div class=\"w-full rounded-2xl shadow-sm p-1 bg-bg-primary\">\n <div #editorContainer class=\"min-h-[200px] text-base\"></div>\n</div>", styles: ["@charset \"UTF-8\";@layer properties;:host{display:block}.ql-toolbar{border-top-left-radius:.25rem;border-top-right-radius:.25rem;border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:rgb(var(--border-default));padding:calc(var(--spacing, .25rem) * 2)}.ql-container{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.ql-editor{min-height:200px;padding:calc(var(--spacing, .25rem) * 3);font-size:1rem;line-height:var(--tw-leading, var(--text-base--line-height, 1.5 ))}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style: solid}}}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"] }]
|
|
3343
|
+
}], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: QuillStyleLoaderService }, { type: undefined, decorators: [{
|
|
2712
3344
|
type: Inject,
|
|
2713
3345
|
args: [PLATFORM_ID]
|
|
2714
3346
|
}] }], propDecorators: { editorContainer: [{
|
|
@@ -2725,16 +3357,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImpor
|
|
|
2725
3357
|
class SearchBarComponent extends FxComponent {
|
|
2726
3358
|
ref;
|
|
2727
3359
|
placeholder = 'Search...';
|
|
2728
|
-
disabled = false;
|
|
2729
3360
|
search = new EventEmitter();
|
|
2730
3361
|
valueChange = new EventEmitter();
|
|
2731
|
-
constructor(
|
|
2732
|
-
super(
|
|
3362
|
+
constructor(ref) {
|
|
3363
|
+
super();
|
|
2733
3364
|
this.ref = ref;
|
|
2734
3365
|
}
|
|
2735
3366
|
ngAfterViewInit() {
|
|
2736
3367
|
this.ref.detectChanges();
|
|
2737
3368
|
}
|
|
3369
|
+
writeValue(value) {
|
|
3370
|
+
this.value = value ?? '';
|
|
3371
|
+
}
|
|
2738
3372
|
onValueChange(value) {
|
|
2739
3373
|
this.value = value;
|
|
2740
3374
|
this.setValue(value);
|
|
@@ -2750,15 +3384,13 @@ class SearchBarComponent extends FxComponent {
|
|
|
2750
3384
|
this.valueChange.emit('');
|
|
2751
3385
|
this.search.emit('');
|
|
2752
3386
|
}
|
|
2753
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: SearchBarComponent, deps: [{ token:
|
|
2754
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.4", type: SearchBarComponent, isStandalone: false, selector: "fx-ui-search-bar", inputs: { placeholder: "placeholder"
|
|
3387
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: SearchBarComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
3388
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.4", type: SearchBarComponent, isStandalone: false, selector: "fx-ui-search-bar", inputs: { placeholder: "placeholder" }, outputs: { search: "search", valueChange: "valueChange" }, usesInheritance: true, ngImport: i0, template: "<div class=\"relative\">\n <fx-ui-hero-icon icon=\"magnifying-glass\" [size]=\"18\" class=\"absolute left-1 top-[3px] text-text-primary\"></fx-ui-hero-icon>\n <input\n type=\"text\"\n class=\"input-default px-9\"\n [attr.placeholder]=\"placeholder\"\n [disabled]=\"disabled\"\n [value]=\"value\"\n (input)=\"onValueChange($any($event.target).value)\"\n (keydown.enter)=\"onEnterKey()\"\n />\n\n <button\n *ngIf=\"value && !disabled\"\n type=\"button\"\n class=\"absolute inset-y-0 right-2 flex items-center text-text-primary hover:text-text-secondary\"\n (click)=\"clear()\"\n aria-label=\"Clear\"\n >\n <i class=\"material-symbols-outlined text-lg select-none\">close</i>\n </button>\n</div>\n", styles: [""], dependencies: [{ kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: HeroIconComponent, selector: "fx-ui-hero-icon", inputs: ["stroke", "icon", "solid", "outline", "size", "color", "class"] }] });
|
|
2755
3389
|
}
|
|
2756
3390
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: SearchBarComponent, decorators: [{
|
|
2757
3391
|
type: Component,
|
|
2758
3392
|
args: [{ selector: 'fx-ui-search-bar', standalone: false, template: "<div class=\"relative\">\n <fx-ui-hero-icon icon=\"magnifying-glass\" [size]=\"18\" class=\"absolute left-1 top-[3px] text-text-primary\"></fx-ui-hero-icon>\n <input\n type=\"text\"\n class=\"input-default px-9\"\n [attr.placeholder]=\"placeholder\"\n [disabled]=\"disabled\"\n [value]=\"value\"\n (input)=\"onValueChange($any($event.target).value)\"\n (keydown.enter)=\"onEnterKey()\"\n />\n\n <button\n *ngIf=\"value && !disabled\"\n type=\"button\"\n class=\"absolute inset-y-0 right-2 flex items-center text-text-primary hover:text-text-secondary\"\n (click)=\"clear()\"\n aria-label=\"Clear\"\n >\n <i class=\"material-symbols-outlined text-lg select-none\">close</i>\n </button>\n</div>\n" }]
|
|
2759
|
-
}], ctorParameters: () => [{ type:
|
|
2760
|
-
type: Input
|
|
2761
|
-
}], disabled: [{
|
|
3393
|
+
}], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { placeholder: [{
|
|
2762
3394
|
type: Input
|
|
2763
3395
|
}], search: [{
|
|
2764
3396
|
type: Output
|
|
@@ -2766,6 +3398,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImpor
|
|
|
2766
3398
|
type: Output
|
|
2767
3399
|
}] } });
|
|
2768
3400
|
|
|
3401
|
+
let nextSelectId = 0;
|
|
2769
3402
|
class SelectComponent extends FxComponent {
|
|
2770
3403
|
ref;
|
|
2771
3404
|
label = '';
|
|
@@ -2773,7 +3406,6 @@ class SelectComponent extends FxComponent {
|
|
|
2773
3406
|
placeholder = '';
|
|
2774
3407
|
options = [];
|
|
2775
3408
|
multiple = false;
|
|
2776
|
-
disabled = false;
|
|
2777
3409
|
errorMessages = {};
|
|
2778
3410
|
required = false;
|
|
2779
3411
|
class;
|
|
@@ -2781,17 +3413,23 @@ class SelectComponent extends FxComponent {
|
|
|
2781
3413
|
searchFn;
|
|
2782
3414
|
/** Emits the current value when the select panel closes (useful for multiple mode). */
|
|
2783
3415
|
closed = new EventEmitter();
|
|
3416
|
+
fieldId = `fx-select-${nextSelectId++}`;
|
|
2784
3417
|
searchTerm = '';
|
|
2785
3418
|
filteredOptions = [];
|
|
2786
3419
|
search$ = new Subject();
|
|
2787
|
-
|
|
2788
|
-
|
|
3420
|
+
destroyRef = inject(DestroyRef);
|
|
3421
|
+
constructor(ref) {
|
|
3422
|
+
super();
|
|
2789
3423
|
this.ref = ref;
|
|
2790
3424
|
}
|
|
3425
|
+
// mat-select treats '' as "nothing selected"; keep the legacy empty value.
|
|
3426
|
+
writeValue(value) {
|
|
3427
|
+
this.value = value ?? '';
|
|
3428
|
+
}
|
|
2791
3429
|
ngOnInit() {
|
|
2792
3430
|
this.filteredOptions = this.options;
|
|
2793
3431
|
this.search$
|
|
2794
|
-
.pipe(debounceTime(300), distinctUntilChanged())
|
|
3432
|
+
.pipe(debounceTime(300), distinctUntilChanged(), takeUntilDestroyed(this.destroyRef))
|
|
2795
3433
|
.subscribe((term) => this.performSearch(term));
|
|
2796
3434
|
}
|
|
2797
3435
|
ngOnChanges(changes) {
|
|
@@ -2830,7 +3468,6 @@ class SelectComponent extends FxComponent {
|
|
|
2830
3468
|
}
|
|
2831
3469
|
const result = await this.searchFn(term);
|
|
2832
3470
|
this.filteredOptions = result;
|
|
2833
|
-
console.log(this.filteredOptions);
|
|
2834
3471
|
Promise.resolve().then(() => this.ref.detectChanges());
|
|
2835
3472
|
}
|
|
2836
3473
|
onSelectionChange(value) {
|
|
@@ -2843,13 +3480,13 @@ class SelectComponent extends FxComponent {
|
|
|
2843
3480
|
}
|
|
2844
3481
|
return this.value === value;
|
|
2845
3482
|
}
|
|
2846
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: SelectComponent, deps: [{ token:
|
|
2847
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: SelectComponent, isStandalone: false, selector: "fx-ui-select", inputs: { label: "label", tooltip: "tooltip", placeholder: "placeholder", options: "options", multiple: "multiple",
|
|
3483
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: SelectComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
3484
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: SelectComponent, isStandalone: false, selector: "fx-ui-select", inputs: { label: "label", tooltip: "tooltip", placeholder: "placeholder", options: "options", multiple: "multiple", errorMessages: "errorMessages", required: "required", class: "class", enableSearch: "enableSearch", searchFn: "searchFn" }, outputs: { closed: "closed" }, usesInheritance: true, usesOnChanges: true, ngImport: i0, template: "<div class=\"mb-4 text-left w-full\" [class]=\"class\">\n @if (label) {\n <label class=\"block txt-field-label\" [attr.for]=\"fieldId\">\n {{ label }}\n @if (required) {\n <span class=\"txt-required\" aria-hidden=\"true\">*</span>\n }\n @if (tooltip) {\n <fx-ui-hero-icon\n icon=\"information-circle\"\n [size]=\"16\"\n class=\"inline-block align-middle ml-0.5 text-text-secondary cursor-help relative -top-px\"\n [matTooltip]=\"tooltip\"\n matTooltipPosition=\"above\"\n matTooltipClass=\"text-text-primary text-xs font-semibold\"\n ></fx-ui-hero-icon>\n }\n </label>\n }\n\n <mat-form-field\n [attr.invalid]=\"invalid || null\"\n class=\"fx-select-field w-full shadow-sm\"\n appearance=\"fill\"\n >\n <mat-select\n [id]=\"fieldId\"\n [required]=\"!!required\"\n [attr.aria-invalid]=\"invalid || null\"\n [attr.aria-describedby]=\"invalid ? fieldId + '-error' : null\"\n [placeholder]=\"placeholder\"\n [disabled]=\"disabled\"\n [multiple]=\"multiple\"\n [value]=\"value\"\n (selectionChange)=\"onSelectionChange($event.value)\"\n (openedChange)=\"onOpened($event)\"\n (blur)=\"onTouched()\"\n >\n @if (enableSearch) {\n <div class=\"px-semi w-full\">\n <input\n type=\"text\"\n aria-label=\"Search\"\n [placeholder]=\"'Search'\"\n [(ngModel)]=\"searchTerm\"\n (ngModelChange)=\"onSearchChange($event)\"\n (click)=\"$event.stopPropagation()\"\n class=\"text-text-primary placeholder:text-text-placeholder w-full bg-bg-primary border-b px-semi py-normal border-border-default focus:outline-none focus:border-gradient-primary-start focus-visible:ring-2 focus-visible:ring-primary/50 text-sm text-text-primary tracking-normal ring-0 transition-colors duration-500 ease-in-out\"\n />\n </div>\n }\n <mat-option *ngFor=\"let option of filteredOptions\" [value]=\"option.value\">\n <span class=\"txt-default\">{{ option.label }}</span>\n </mat-option>\n </mat-select>\n </mat-form-field>\n\n @if (invalid) {\n <div class=\"txt-invalid text-left\" role=\"alert\" [id]=\"fieldId + '-error'\">\n {{ getErrorMessage(label) }}\n </div>\n }\n</div>\n", styles: ["::ng-deep .fx-select-field{display:block!important;width:100%!important;margin:0!important;padding:0!important;border-radius:.75rem;box-shadow:0 1px 1px rgb(var(--border-default))}::ng-deep .fx-select-field .mat-mdc-form-field-wrapper,::ng-deep .fx-select-field .mat-mdc-text-field-wrapper{padding:0!important;margin:0!important;width:100%!important}::ng-deep .fx-select-field .mdc-text-field{width:100%!important;margin:0!important;padding:0!important;--mdc-shape-small: 0 !important}::ng-deep .fx-select-field .mat-mdc-form-field-flex{width:100%!important;margin:0!important;padding:0 .75rem!important;border:1px solid rgb(var(--border-default));border-radius:.375rem;height:36px!important;align-items:center;background-color:rgb(var(--bg-primary))}::ng-deep .fx-select-field .mdc-notched-outline,::ng-deep .fx-select-field .mat-mdc-form-field-subscript-wrapper{display:none!important}::ng-deep .fx-select-field.mat-focused .mat-mdc-form-field-flex{border:2px solid rgb(var(--gradient-primary-start))!important;outline:none!important;border-radius:.375rem}::ng-deep .fx-select-field.mat-form-field-invalid .mat-mdc-form-field-flex{border:1px solid rgb(var(--bg-error-bold))!important;box-shadow:none!important}::ng-deep mat-form-field[invalid] .mat-mdc-form-field-flex{border:1px solid rgb(var(--bg-error-bold))!important;box-shadow:none!important}::ng-deep .fx-select-field .mat-mdc-select-trigger{padding:0!important;margin:0!important}::ng-deep .fx-select-field .mat-mdc-select-arrow-wrapper{margin-right:0!important}::ng-deep .fx-select-field .mat-mdc-select-arrow-wrapper .mat-mdc-select-arrow svg{display:none!important}::ng-deep .fx-select-field .mat-mdc-select-arrow-wrapper .mat-mdc-select-arrow{width:8px!important;height:8px!important;border-style:solid;border-color:currentColor;border-width:0 1.5px 1.5px 0;transform:rotate(45deg);margin-top:-3px;color:rgb(var(--text-secondary))!important}::ng-deep .mat-mdc-select-panel{background-color:rgb(var(--bg-primary))!important;border:1px solid rgb(var(--border-default))!important;border-radius:.375rem!important;box-shadow:0 2px 4px #0000001a!important}::ng-deep .fx-select-field .mat-mdc-select-placeholder{color:rgb(var(--text-placeholder))!important;font-size:14px}::ng-deep .fx-select-field .mat-mdc-select-value{color:rgb(var(--text-primary))!important;font-size:14px;font-weight:400}.mat-select-panel .search-option-full-width{padding-left:0!important;padding-right:0!important;display:block;pointer-events:none;line-height:initial;height:auto}.mat-select-panel .search-option-full-width .search-bar{width:100%;padding:8px 16px;border:none;border-bottom:1px solid #ddd;box-sizing:border-box}::ng-deep .fx-select-field.mat-form-field-disabled .mat-mdc-form-field-flex,::ng-deep .fx-select-field:has(.mat-mdc-select-disabled) .mat-mdc-form-field-flex{background-color:rgb(var(--bg-primary-hover))!important;border-color:rgb(var(--border-default))!important;cursor:not-allowed!important;opacity:.6;box-shadow:none!important}::ng-deep .fx-select-field.mat-form-field-disabled .mat-mdc-select-value,::ng-deep .fx-select-field.mat-form-field-disabled .mat-mdc-select-placeholder,::ng-deep .fx-select-field.mat-form-field-disabled .mat-mdc-select-arrow,::ng-deep .fx-select-field:has(.mat-mdc-select-disabled) .mat-mdc-select-value,::ng-deep .fx-select-field:has(.mat-mdc-select-disabled) .mat-mdc-select-placeholder,::ng-deep .fx-select-field:has(.mat-mdc-select-disabled) .mat-mdc-select-arrow{color:rgb(var(--text-placeholder))!important}::ng-deep .fx-select-field.mat-form-field-disabled .mat-mdc-select,::ng-deep .fx-select-field:has(.mat-mdc-select-disabled) .mat-mdc-select{cursor:not-allowed!important}\n"], dependencies: [{ kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "component", type: i3.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i3.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "directive", type: i57.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: HeroIconComponent, selector: "fx-ui-hero-icon", inputs: ["stroke", "icon", "solid", "outline", "size", "color", "class"] }] });
|
|
2848
3485
|
}
|
|
2849
3486
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: SelectComponent, decorators: [{
|
|
2850
3487
|
type: Component,
|
|
2851
|
-
args: [{ selector: 'fx-ui-select', standalone: false, template: "<div class=\"mb-4 text-left w-full\" [class]=\"class\">\n @if (label) {\n <label class=\"block txt-field-label\">\n {{ label }}\n @if (required) {\n <span class=\"txt-required\">*</span>\n }\n @if (tooltip) {\n <fx-ui-hero-icon\n icon=\"information-circle\"\n [size]=\"16\"\n class=\"inline-block align-middle ml-0.5 text-text-secondary cursor-help relative -top-px\"\n [matTooltip]=\"tooltip\"\n matTooltipPosition=\"above\"\n matTooltipClass=\"text-text-primary text-xs font-semibold\"\n ></fx-ui-hero-icon>\n }\n </label>\n }\n\n <mat-form-field\n [attr.invalid]=\"invalid || null\"\n class=\"fx-select-field w-full shadow-sm\"\n appearance=\"fill\"\n >\n <mat-select\n [placeholder]=\"placeholder\"\n [disabled]=\"disabled\"\n [multiple]=\"multiple\"\n [value]=\"value\"\n (selectionChange)=\"onSelectionChange($event.value)\"\n (openedChange)=\"onOpened($event)\"\n (blur)=\"onTouched()\"\n >\n @if (enableSearch) {\n <div class=\"px-semi w-full\">\n <input\n type=\"text\"\n [placeholder]=\"'Search'\"\n [(ngModel)]=\"searchTerm\"\n (ngModelChange)=\"onSearchChange($event)\"\n (click)=\"$event.stopPropagation()\"\n class=\"text-text-primary placeholder:text-text-placeholder w-full bg-bg-primary border-b px-semi py-normal border-border-default focus:outline-none focus:border-gradient-primary-start text-sm text-text-primary tracking-normal ring-0 transition-colors duration-500 ease-in-out\"\n />\n </div>\n }\n <mat-option *ngFor=\"let option of filteredOptions\" [value]=\"option.value\">\n <span class=\"txt-default\">{{ option.label }}</span>\n </mat-option>\n </mat-select>\n </mat-form-field>\n\n @if (invalid) {\n <div class=\"txt-invalid text-left\">\n {{ getErrorMessage(label) }}\n </div>\n }\n</div>\n", styles: ["::ng-deep .fx-select-field{display:block!important;width:100%!important;margin:0!important;padding:0!important;border-radius:.75rem;box-shadow:0 1px 1px rgb(var(--border-default))}::ng-deep .fx-select-field .mat-mdc-form-field-wrapper,::ng-deep .fx-select-field .mat-mdc-text-field-wrapper{padding:0!important;margin:0!important;width:100%!important}::ng-deep .fx-select-field .mdc-text-field{width:100%!important;margin:0!important;padding:0!important;--mdc-shape-small: 0 !important}::ng-deep .fx-select-field .mat-mdc-form-field-flex{width:100%!important;margin:0!important;padding:0 .75rem!important;border:1px solid rgb(var(--border-default));border-radius:.375rem;height:36px!important;align-items:center;background-color:rgb(var(--bg-primary))}::ng-deep .fx-select-field .mdc-notched-outline,::ng-deep .fx-select-field .mat-mdc-form-field-subscript-wrapper{display:none!important}::ng-deep .fx-select-field.mat-focused .mat-mdc-form-field-flex{border:2px solid rgb(var(--gradient-primary-start))!important;outline:none!important;border-radius:.375rem}::ng-deep .fx-select-field.mat-form-field-invalid .mat-mdc-form-field-flex{border:1px solid rgb(var(--bg-error-bold))!important;box-shadow:none!important}::ng-deep mat-form-field[invalid] .mat-mdc-form-field-flex{border:1px solid rgb(var(--bg-error-bold))!important;box-shadow:none!important}::ng-deep .fx-select-field .mat-mdc-select-trigger{padding:0!important;margin:0!important}::ng-deep .fx-select-field .mat-mdc-select-arrow-wrapper{margin-right:0!important}::ng-deep .fx-select-field .mat-mdc-select-arrow-wrapper .mat-mdc-select-arrow svg{display:none!important}::ng-deep .fx-select-field .mat-mdc-select-arrow-wrapper .mat-mdc-select-arrow{width:8px!important;height:8px!important;border-style:solid;border-color:currentColor;border-width:0 1.5px 1.5px 0;transform:rotate(45deg);margin-top:-3px;color:rgb(var(--text-secondary))!important}::ng-deep .mat-mdc-select-panel{background-color:rgb(var(--bg-primary))!important;border:1px solid rgb(var(--border-default))!important;border-radius:.375rem!important;box-shadow:0 2px 4px #0000001a!important}::ng-deep .fx-select-field .mat-mdc-select-placeholder{color:rgb(var(--text-placeholder))!important;font-size:14px}::ng-deep .fx-select-field .mat-mdc-select-value{color:rgb(var(--text-primary))!important;font-size:14px;font-weight:400}.mat-select-panel .search-option-full-width{padding-left:0!important;padding-right:0!important;display:block;pointer-events:none;line-height:initial;height:auto}.mat-select-panel .search-option-full-width .search-bar{width:100%;padding:8px 16px;border:none;border-bottom:1px solid #ddd;box-sizing:border-box}::ng-deep .fx-select-field.mat-form-field-disabled .mat-mdc-form-field-flex,::ng-deep .fx-select-field:has(.mat-mdc-select-disabled) .mat-mdc-form-field-flex{background-color:rgb(var(--bg-primary-hover))!important;border-color:rgb(var(--border-default))!important;cursor:not-allowed!important;opacity:.6;box-shadow:none!important}::ng-deep .fx-select-field.mat-form-field-disabled .mat-mdc-select-value,::ng-deep .fx-select-field.mat-form-field-disabled .mat-mdc-select-placeholder,::ng-deep .fx-select-field.mat-form-field-disabled .mat-mdc-select-arrow,::ng-deep .fx-select-field:has(.mat-mdc-select-disabled) .mat-mdc-select-value,::ng-deep .fx-select-field:has(.mat-mdc-select-disabled) .mat-mdc-select-placeholder,::ng-deep .fx-select-field:has(.mat-mdc-select-disabled) .mat-mdc-select-arrow{color:rgb(var(--text-placeholder))!important}::ng-deep .fx-select-field.mat-form-field-disabled .mat-mdc-select,::ng-deep .fx-select-field:has(.mat-mdc-select-disabled) .mat-mdc-select{cursor:not-allowed!important}\n"] }]
|
|
2852
|
-
}], ctorParameters: () => [{ type:
|
|
3488
|
+
args: [{ selector: 'fx-ui-select', standalone: false, template: "<div class=\"mb-4 text-left w-full\" [class]=\"class\">\n @if (label) {\n <label class=\"block txt-field-label\" [attr.for]=\"fieldId\">\n {{ label }}\n @if (required) {\n <span class=\"txt-required\" aria-hidden=\"true\">*</span>\n }\n @if (tooltip) {\n <fx-ui-hero-icon\n icon=\"information-circle\"\n [size]=\"16\"\n class=\"inline-block align-middle ml-0.5 text-text-secondary cursor-help relative -top-px\"\n [matTooltip]=\"tooltip\"\n matTooltipPosition=\"above\"\n matTooltipClass=\"text-text-primary text-xs font-semibold\"\n ></fx-ui-hero-icon>\n }\n </label>\n }\n\n <mat-form-field\n [attr.invalid]=\"invalid || null\"\n class=\"fx-select-field w-full shadow-sm\"\n appearance=\"fill\"\n >\n <mat-select\n [id]=\"fieldId\"\n [required]=\"!!required\"\n [attr.aria-invalid]=\"invalid || null\"\n [attr.aria-describedby]=\"invalid ? fieldId + '-error' : null\"\n [placeholder]=\"placeholder\"\n [disabled]=\"disabled\"\n [multiple]=\"multiple\"\n [value]=\"value\"\n (selectionChange)=\"onSelectionChange($event.value)\"\n (openedChange)=\"onOpened($event)\"\n (blur)=\"onTouched()\"\n >\n @if (enableSearch) {\n <div class=\"px-semi w-full\">\n <input\n type=\"text\"\n aria-label=\"Search\"\n [placeholder]=\"'Search'\"\n [(ngModel)]=\"searchTerm\"\n (ngModelChange)=\"onSearchChange($event)\"\n (click)=\"$event.stopPropagation()\"\n class=\"text-text-primary placeholder:text-text-placeholder w-full bg-bg-primary border-b px-semi py-normal border-border-default focus:outline-none focus:border-gradient-primary-start focus-visible:ring-2 focus-visible:ring-primary/50 text-sm text-text-primary tracking-normal ring-0 transition-colors duration-500 ease-in-out\"\n />\n </div>\n }\n <mat-option *ngFor=\"let option of filteredOptions\" [value]=\"option.value\">\n <span class=\"txt-default\">{{ option.label }}</span>\n </mat-option>\n </mat-select>\n </mat-form-field>\n\n @if (invalid) {\n <div class=\"txt-invalid text-left\" role=\"alert\" [id]=\"fieldId + '-error'\">\n {{ getErrorMessage(label) }}\n </div>\n }\n</div>\n", styles: ["::ng-deep .fx-select-field{display:block!important;width:100%!important;margin:0!important;padding:0!important;border-radius:.75rem;box-shadow:0 1px 1px rgb(var(--border-default))}::ng-deep .fx-select-field .mat-mdc-form-field-wrapper,::ng-deep .fx-select-field .mat-mdc-text-field-wrapper{padding:0!important;margin:0!important;width:100%!important}::ng-deep .fx-select-field .mdc-text-field{width:100%!important;margin:0!important;padding:0!important;--mdc-shape-small: 0 !important}::ng-deep .fx-select-field .mat-mdc-form-field-flex{width:100%!important;margin:0!important;padding:0 .75rem!important;border:1px solid rgb(var(--border-default));border-radius:.375rem;height:36px!important;align-items:center;background-color:rgb(var(--bg-primary))}::ng-deep .fx-select-field .mdc-notched-outline,::ng-deep .fx-select-field .mat-mdc-form-field-subscript-wrapper{display:none!important}::ng-deep .fx-select-field.mat-focused .mat-mdc-form-field-flex{border:2px solid rgb(var(--gradient-primary-start))!important;outline:none!important;border-radius:.375rem}::ng-deep .fx-select-field.mat-form-field-invalid .mat-mdc-form-field-flex{border:1px solid rgb(var(--bg-error-bold))!important;box-shadow:none!important}::ng-deep mat-form-field[invalid] .mat-mdc-form-field-flex{border:1px solid rgb(var(--bg-error-bold))!important;box-shadow:none!important}::ng-deep .fx-select-field .mat-mdc-select-trigger{padding:0!important;margin:0!important}::ng-deep .fx-select-field .mat-mdc-select-arrow-wrapper{margin-right:0!important}::ng-deep .fx-select-field .mat-mdc-select-arrow-wrapper .mat-mdc-select-arrow svg{display:none!important}::ng-deep .fx-select-field .mat-mdc-select-arrow-wrapper .mat-mdc-select-arrow{width:8px!important;height:8px!important;border-style:solid;border-color:currentColor;border-width:0 1.5px 1.5px 0;transform:rotate(45deg);margin-top:-3px;color:rgb(var(--text-secondary))!important}::ng-deep .mat-mdc-select-panel{background-color:rgb(var(--bg-primary))!important;border:1px solid rgb(var(--border-default))!important;border-radius:.375rem!important;box-shadow:0 2px 4px #0000001a!important}::ng-deep .fx-select-field .mat-mdc-select-placeholder{color:rgb(var(--text-placeholder))!important;font-size:14px}::ng-deep .fx-select-field .mat-mdc-select-value{color:rgb(var(--text-primary))!important;font-size:14px;font-weight:400}.mat-select-panel .search-option-full-width{padding-left:0!important;padding-right:0!important;display:block;pointer-events:none;line-height:initial;height:auto}.mat-select-panel .search-option-full-width .search-bar{width:100%;padding:8px 16px;border:none;border-bottom:1px solid #ddd;box-sizing:border-box}::ng-deep .fx-select-field.mat-form-field-disabled .mat-mdc-form-field-flex,::ng-deep .fx-select-field:has(.mat-mdc-select-disabled) .mat-mdc-form-field-flex{background-color:rgb(var(--bg-primary-hover))!important;border-color:rgb(var(--border-default))!important;cursor:not-allowed!important;opacity:.6;box-shadow:none!important}::ng-deep .fx-select-field.mat-form-field-disabled .mat-mdc-select-value,::ng-deep .fx-select-field.mat-form-field-disabled .mat-mdc-select-placeholder,::ng-deep .fx-select-field.mat-form-field-disabled .mat-mdc-select-arrow,::ng-deep .fx-select-field:has(.mat-mdc-select-disabled) .mat-mdc-select-value,::ng-deep .fx-select-field:has(.mat-mdc-select-disabled) .mat-mdc-select-placeholder,::ng-deep .fx-select-field:has(.mat-mdc-select-disabled) .mat-mdc-select-arrow{color:rgb(var(--text-placeholder))!important}::ng-deep .fx-select-field.mat-form-field-disabled .mat-mdc-select,::ng-deep .fx-select-field:has(.mat-mdc-select-disabled) .mat-mdc-select{cursor:not-allowed!important}\n"] }]
|
|
3489
|
+
}], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { label: [{
|
|
2853
3490
|
type: Input
|
|
2854
3491
|
}], tooltip: [{
|
|
2855
3492
|
type: Input
|
|
@@ -2859,8 +3496,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImpor
|
|
|
2859
3496
|
type: Input
|
|
2860
3497
|
}], multiple: [{
|
|
2861
3498
|
type: Input
|
|
2862
|
-
}], disabled: [{
|
|
2863
|
-
type: Input
|
|
2864
3499
|
}], errorMessages: [{
|
|
2865
3500
|
type: Input
|
|
2866
3501
|
}], required: [{
|
|
@@ -2879,7 +3514,7 @@ class SkeletonTableLoadingComponent {
|
|
|
2879
3514
|
columns = 5;
|
|
2880
3515
|
rows = 5;
|
|
2881
3516
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: SkeletonTableLoadingComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2882
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: SkeletonTableLoadingComponent, isStandalone: false, selector: "fx-ui-skeleton-table-loading", inputs: { columns: "columns", rows: "rows" }, ngImport: i0, template: "<div class=\"w-full animate-pulse space-y-normal\">\n <div\n class=\"grid gap-small\"\n [ngStyle]=\"{ 'grid-template-columns': 'repeat(' + columns + ', minmax(0, 1fr))' }\"\n >\n @for(col of [].constructor(columns); track col) {\n <div class=\"h-6 bg-bg-hover rounded\"></div>\n }\n </div>\n\n @for(row of [].constructor(rows); track row) {\n <div\n class=\"grid gap-small\"\n [ngStyle]=\"{ 'grid-template-columns': 'repeat(' + columns + ', minmax(0, 1fr))' }\"\n >\n @for(col of [].constructor(columns); track col) {\n <div class=\"h-7 bg-bg-hover rounded\"></div>\n }\n </div>\n }\n</div>\n", styles: [""], dependencies: [{ kind: "directive", type:
|
|
3517
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: SkeletonTableLoadingComponent, isStandalone: false, selector: "fx-ui-skeleton-table-loading", inputs: { columns: "columns", rows: "rows" }, ngImport: i0, template: "<div class=\"w-full animate-pulse space-y-normal\">\n <div\n class=\"grid gap-small\"\n [ngStyle]=\"{ 'grid-template-columns': 'repeat(' + columns + ', minmax(0, 1fr))' }\"\n >\n @for(col of [].constructor(columns); track col) {\n <div class=\"h-6 bg-bg-hover rounded\"></div>\n }\n </div>\n\n @for(row of [].constructor(rows); track row) {\n <div\n class=\"grid gap-small\"\n [ngStyle]=\"{ 'grid-template-columns': 'repeat(' + columns + ', minmax(0, 1fr))' }\"\n >\n @for(col of [].constructor(columns); track col) {\n <div class=\"h-7 bg-bg-hover rounded\"></div>\n }\n </div>\n }\n</div>\n", styles: [""], dependencies: [{ kind: "directive", type: i1$1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }] });
|
|
2883
3518
|
}
|
|
2884
3519
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: SkeletonTableLoadingComponent, decorators: [{
|
|
2885
3520
|
type: Component,
|
|
@@ -2898,11 +3533,10 @@ class SliderComponent extends FxComponent {
|
|
|
2898
3533
|
max = 100;
|
|
2899
3534
|
step = 1;
|
|
2900
3535
|
vertical = false;
|
|
2901
|
-
disabled = false;
|
|
2902
3536
|
valueChange = new EventEmitter();
|
|
2903
3537
|
dragging = false;
|
|
2904
|
-
constructor(
|
|
2905
|
-
super(
|
|
3538
|
+
constructor(ref) {
|
|
3539
|
+
super();
|
|
2906
3540
|
this.ref = ref;
|
|
2907
3541
|
}
|
|
2908
3542
|
ngAfterViewInit() {
|
|
@@ -2950,7 +3584,9 @@ class SliderComponent extends FxComponent {
|
|
|
2950
3584
|
try {
|
|
2951
3585
|
event.target.releasePointerCapture(event.pointerId);
|
|
2952
3586
|
}
|
|
2953
|
-
catch {
|
|
3587
|
+
catch {
|
|
3588
|
+
// pointer capture may already be released
|
|
3589
|
+
}
|
|
2954
3590
|
this.dragging = false;
|
|
2955
3591
|
this.onTouched();
|
|
2956
3592
|
}
|
|
@@ -3016,13 +3652,13 @@ class SliderComponent extends FxComponent {
|
|
|
3016
3652
|
? { bottom: `${this.percent}%`, transform: 'translateY(50%)' }
|
|
3017
3653
|
: { left: `${this.percent}%`, transform: 'translateX(-50%)' };
|
|
3018
3654
|
}
|
|
3019
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: SliderComponent, deps: [{ token:
|
|
3020
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.4", type: SliderComponent, isStandalone: false, selector: "fx-ui-slider", inputs: { min: "min", max: "max", step: "step", vertical: "vertical"
|
|
3655
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: SliderComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
3656
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.4", type: SliderComponent, isStandalone: false, selector: "fx-ui-slider", inputs: { min: "min", max: "max", step: "step", vertical: "vertical" }, outputs: { valueChange: "valueChange" }, host: { listeners: { "keydown": "onKeydown($event)" } }, viewQueries: [{ propertyName: "trackRef", first: true, predicate: ["track"], descendants: true, static: true }, { propertyName: "thumbRef", first: true, predicate: ["thumb"], descendants: true, static: true }], usesInheritance: true, ngImport: i0, template: "<div\n class=\"w-full select-none\"\n [class.cursor-not-allowed]=\"disabled\"\n [class.opacity-60]=\"disabled\"\n role=\"group\"\n>\n <!-- Track click is a pointer shortcut; the thumb button below owns keyboard/focus. -->\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <div\n #track\n class=\"relative touch-none py-4\"\n (click)=\"onTrackClick($event)\"\n [attr.aria-disabled]=\"disabled\"\n [attr.aria-orientation]=\"vertical ? 'vertical' : 'horizontal'\"\n >\n <div\n class=\"relative flex items-center\"\n [ngClass]=\"{ 'flex-col h-48': vertical }\"\n >\n <div\n class=\"relative flex-1 rounded-full bg-gray-600 h-2\"\n [ngClass]=\"{ 'w-1 rotate-0 h-full w-auto bg-border-gray-700': vertical }\"\n >\n <div\n class=\"absolute left-0 top-0 h-full rounded-full bg-primary\"\n *ngIf=\"!vertical\"\n [style.width.%]=\"percent\"\n ></div>\n\n <div\n class=\"absolute bottom-0 left-0 w-full rounded-full bg-primary\"\n *ngIf=\"vertical\"\n [style.height.%]=\"percent\"\n style=\"transform-origin: bottom;\"\n ></div>\n\n <button\n #thumb\n type=\"button\"\n class=\"absolute -mt-1 -ml-1.5 w-4 h-4 rounded-full shadow focus:ring-primary bg-primary border-2 border-text-primary\"\n [ngStyle]=\"thumbStyle()\"\n role=\"slider\"\n aria-label=\"Slider\"\n [attr.aria-valuemin]=\"min\"\n [attr.aria-valuemax]=\"max\"\n [attr.aria-valuenow]=\"value\"\n [attr.aria-disabled]=\"disabled\"\n tabindex=\"0\"\n (pointerdown)=\"onPointerDown($event)\"\n (pointermove)=\"onPointerMove($event)\"\n (pointerup)=\"onPointerUp($event)\"\n [disabled]=\"disabled\"\n ></button>\n </div>\n </div>\n </div>\n</div>\n", styles: [""], dependencies: [{ kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }] });
|
|
3021
3657
|
}
|
|
3022
3658
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: SliderComponent, decorators: [{
|
|
3023
3659
|
type: Component,
|
|
3024
|
-
args: [{ selector: 'fx-ui-slider', standalone: false, template: "<div\n class=\"w-full select-none\"\n [class.cursor-not-allowed]=\"disabled\"\n [class.opacity-60]=\"disabled\"\n role=\"group\"\n>\n <div\n #track\n class=\"relative touch-none py-4\"\n (click)=\"onTrackClick($event)\"\n [attr.aria-disabled]=\"disabled\"\n [attr.aria-orientation]=\"vertical ? 'vertical' : 'horizontal'\"\n >\n <div\n class=\"relative flex items-center\"\n [ngClass]=\"{ 'flex-col h-48': vertical }\"\n >\n <div\n class=\"relative flex-1 rounded-full bg-gray-600 h-2\"\n [ngClass]=\"{ 'w-1 rotate-0 h-full w-auto bg-border-gray-700': vertical }\"\n >\n <div\n class=\"absolute left-0 top-0 h-full rounded-full bg-primary\"\n *ngIf=\"!vertical\"\n [style.width.%]=\"percent\"\n ></div>\n\n <div\n class=\"absolute bottom-0 left-0 w-full rounded-full bg-primary\"\n *ngIf=\"vertical\"\n [style.height.%]=\"percent\"\n style=\"transform-origin: bottom;\"\n ></div>\n\n <button\n #thumb\n type=\"button\"\n class=\"absolute -mt-1 -ml-1.5 w-4 h-4 rounded-full shadow focus:ring-primary bg-primary border-2 border-text-primary\"\n [ngStyle]=\"thumbStyle()\"\n role=\"slider\"\n [attr.aria-valuemin]=\"min\"\n [attr.aria-valuemax]=\"max\"\n [attr.aria-valuenow]=\"value\"\n [attr.aria-disabled]=\"disabled\"\n tabindex=\"0\"\n (pointerdown)=\"onPointerDown($event)\"\n (pointermove)=\"onPointerMove($event)\"\n (pointerup)=\"onPointerUp($event)\"\n [disabled]=\"disabled\"\n ></button>\n </div>\n </div>\n </div>\n</div>\n" }]
|
|
3025
|
-
}], ctorParameters: () => [{ type:
|
|
3660
|
+
args: [{ selector: 'fx-ui-slider', standalone: false, template: "<div\n class=\"w-full select-none\"\n [class.cursor-not-allowed]=\"disabled\"\n [class.opacity-60]=\"disabled\"\n role=\"group\"\n>\n <!-- Track click is a pointer shortcut; the thumb button below owns keyboard/focus. -->\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <div\n #track\n class=\"relative touch-none py-4\"\n (click)=\"onTrackClick($event)\"\n [attr.aria-disabled]=\"disabled\"\n [attr.aria-orientation]=\"vertical ? 'vertical' : 'horizontal'\"\n >\n <div\n class=\"relative flex items-center\"\n [ngClass]=\"{ 'flex-col h-48': vertical }\"\n >\n <div\n class=\"relative flex-1 rounded-full bg-gray-600 h-2\"\n [ngClass]=\"{ 'w-1 rotate-0 h-full w-auto bg-border-gray-700': vertical }\"\n >\n <div\n class=\"absolute left-0 top-0 h-full rounded-full bg-primary\"\n *ngIf=\"!vertical\"\n [style.width.%]=\"percent\"\n ></div>\n\n <div\n class=\"absolute bottom-0 left-0 w-full rounded-full bg-primary\"\n *ngIf=\"vertical\"\n [style.height.%]=\"percent\"\n style=\"transform-origin: bottom;\"\n ></div>\n\n <button\n #thumb\n type=\"button\"\n class=\"absolute -mt-1 -ml-1.5 w-4 h-4 rounded-full shadow focus:ring-primary bg-primary border-2 border-text-primary\"\n [ngStyle]=\"thumbStyle()\"\n role=\"slider\"\n aria-label=\"Slider\"\n [attr.aria-valuemin]=\"min\"\n [attr.aria-valuemax]=\"max\"\n [attr.aria-valuenow]=\"value\"\n [attr.aria-disabled]=\"disabled\"\n tabindex=\"0\"\n (pointerdown)=\"onPointerDown($event)\"\n (pointermove)=\"onPointerMove($event)\"\n (pointerup)=\"onPointerUp($event)\"\n [disabled]=\"disabled\"\n ></button>\n </div>\n </div>\n </div>\n</div>\n" }]
|
|
3661
|
+
}], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { trackRef: [{
|
|
3026
3662
|
type: ViewChild,
|
|
3027
3663
|
args: ['track', { static: true }]
|
|
3028
3664
|
}], thumbRef: [{
|
|
@@ -3036,8 +3672,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImpor
|
|
|
3036
3672
|
type: Input
|
|
3037
3673
|
}], vertical: [{
|
|
3038
3674
|
type: Input
|
|
3039
|
-
}], disabled: [{
|
|
3040
|
-
type: Input
|
|
3041
3675
|
}], valueChange: [{
|
|
3042
3676
|
type: Output
|
|
3043
3677
|
}], onKeydown: [{
|
|
@@ -3047,17 +3681,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImpor
|
|
|
3047
3681
|
|
|
3048
3682
|
class SwitchComponent extends FxComponent {
|
|
3049
3683
|
cdr;
|
|
3050
|
-
disabled = false;
|
|
3051
3684
|
onSwitch = new EventEmitter();
|
|
3685
|
+
/** Accessible name — the switch renders no visible text of its own. */
|
|
3686
|
+
ariaLabel = '';
|
|
3052
3687
|
ngAfterViewInit() {
|
|
3053
3688
|
this.cdr.detectChanges();
|
|
3054
3689
|
}
|
|
3055
|
-
constructor(
|
|
3056
|
-
super(
|
|
3690
|
+
constructor(cdr) {
|
|
3691
|
+
super();
|
|
3057
3692
|
this.cdr = cdr;
|
|
3058
3693
|
}
|
|
3059
|
-
|
|
3060
|
-
this.
|
|
3694
|
+
writeValue(value) {
|
|
3695
|
+
this.value = value ?? false;
|
|
3061
3696
|
}
|
|
3062
3697
|
toggle() {
|
|
3063
3698
|
if (this.disabled)
|
|
@@ -3067,16 +3702,16 @@ class SwitchComponent extends FxComponent {
|
|
|
3067
3702
|
this.onChange(this.value);
|
|
3068
3703
|
this.onTouched();
|
|
3069
3704
|
}
|
|
3070
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: SwitchComponent, deps: [{ token:
|
|
3071
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: SwitchComponent, isStandalone: false, selector: "fx-ui-switch", inputs: {
|
|
3705
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: SwitchComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
3706
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: SwitchComponent, isStandalone: false, selector: "fx-ui-switch", inputs: { ariaLabel: "ariaLabel" }, outputs: { onSwitch: "onSwitch" }, usesInheritance: true, ngImport: i0, template: "<button\n type=\"button\"\n role=\"switch\"\n [attr.aria-checked]=\"!!value\"\n [attr.aria-label]=\"ariaLabel || null\"\n [disabled]=\"disabled\"\n class=\"relative flex items-center w-10 h-5 rounded-full transition-colors duration-300 cursor-pointer disabled:cursor-not-allowed focus-visible:ring-2 focus-visible:ring-primary/50\"\n [ngClass]=\"{\n 'bg-success': value && !disabled,\n 'bg-button-default': !value && !disabled,\n 'bg-bg-hover': disabled\n }\"\n (click)=\"toggle()\"\n>\n @if(value) {\n <i style=\"font-size: 20px;\" class=\"material-symbols-outlined absolute left-0.5 top-0 text-text-inverse\" aria-hidden=\"true\">check</i>\n\n }\n @if(!value) {\n <i style=\"font-size: 20px;\" class=\"material-symbols-outlined absolute right-0.5 top-0 text-text-inverse\" aria-hidden=\"true\">close</i>\n }\n <div\n class=\"absolute top-0.5 left-0.5 w-4 h-4 bg-text-inverse rounded-full shadow-md transform transition-transform duration-300\"\n [ngClass]=\"{ 'translate-x-5': value }\"\n ></div>\n</button>\n", styles: [""], dependencies: [{ kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] });
|
|
3072
3707
|
}
|
|
3073
3708
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: SwitchComponent, decorators: [{
|
|
3074
3709
|
type: Component,
|
|
3075
|
-
args: [{ selector: 'fx-ui-switch', standalone: false, template: "<
|
|
3076
|
-
}], ctorParameters: () => [{ type:
|
|
3077
|
-
type: Input
|
|
3078
|
-
}], onSwitch: [{
|
|
3710
|
+
args: [{ selector: 'fx-ui-switch', standalone: false, template: "<button\n type=\"button\"\n role=\"switch\"\n [attr.aria-checked]=\"!!value\"\n [attr.aria-label]=\"ariaLabel || null\"\n [disabled]=\"disabled\"\n class=\"relative flex items-center w-10 h-5 rounded-full transition-colors duration-300 cursor-pointer disabled:cursor-not-allowed focus-visible:ring-2 focus-visible:ring-primary/50\"\n [ngClass]=\"{\n 'bg-success': value && !disabled,\n 'bg-button-default': !value && !disabled,\n 'bg-bg-hover': disabled\n }\"\n (click)=\"toggle()\"\n>\n @if(value) {\n <i style=\"font-size: 20px;\" class=\"material-symbols-outlined absolute left-0.5 top-0 text-text-inverse\" aria-hidden=\"true\">check</i>\n\n }\n @if(!value) {\n <i style=\"font-size: 20px;\" class=\"material-symbols-outlined absolute right-0.5 top-0 text-text-inverse\" aria-hidden=\"true\">close</i>\n }\n <div\n class=\"absolute top-0.5 left-0.5 w-4 h-4 bg-text-inverse rounded-full shadow-md transform transition-transform duration-300\"\n [ngClass]=\"{ 'translate-x-5': value }\"\n ></div>\n</button>\n" }]
|
|
3711
|
+
}], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { onSwitch: [{
|
|
3079
3712
|
type: Output
|
|
3713
|
+
}], ariaLabel: [{
|
|
3714
|
+
type: Input
|
|
3080
3715
|
}] } });
|
|
3081
3716
|
|
|
3082
3717
|
class TabComponent {
|
|
@@ -3123,6 +3758,33 @@ class TabGroupComponent {
|
|
|
3123
3758
|
this.activeIndex = index;
|
|
3124
3759
|
this.scrollToActiveTab(true);
|
|
3125
3760
|
}
|
|
3761
|
+
/** Roving tablist keyboard nav: arrows move AND activate (selection follows focus). */
|
|
3762
|
+
onTabKeydown(event, index) {
|
|
3763
|
+
const count = this.tabs.length;
|
|
3764
|
+
if (!count)
|
|
3765
|
+
return;
|
|
3766
|
+
let next;
|
|
3767
|
+
switch (event.key) {
|
|
3768
|
+
case 'ArrowRight':
|
|
3769
|
+
next = (index + 1) % count;
|
|
3770
|
+
break;
|
|
3771
|
+
case 'ArrowLeft':
|
|
3772
|
+
next = (index - 1 + count) % count;
|
|
3773
|
+
break;
|
|
3774
|
+
case 'Home':
|
|
3775
|
+
next = 0;
|
|
3776
|
+
break;
|
|
3777
|
+
case 'End':
|
|
3778
|
+
next = count - 1;
|
|
3779
|
+
break;
|
|
3780
|
+
default:
|
|
3781
|
+
return;
|
|
3782
|
+
}
|
|
3783
|
+
event.preventDefault();
|
|
3784
|
+
this.selectTab(next);
|
|
3785
|
+
const buttons = this.tabListRef.nativeElement.querySelectorAll('.tab-btn');
|
|
3786
|
+
buttons[next]?.focus();
|
|
3787
|
+
}
|
|
3126
3788
|
updateUnderlinePosition() {
|
|
3127
3789
|
const tabListEl = this.tabListRef.nativeElement;
|
|
3128
3790
|
const underlineEl = this.underlineRef.nativeElement;
|
|
@@ -3183,11 +3845,11 @@ class TabGroupComponent {
|
|
|
3183
3845
|
this.updateUnderlinePosition();
|
|
3184
3846
|
}
|
|
3185
3847
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: TabGroupComponent, deps: [{ token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Component });
|
|
3186
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: TabGroupComponent, isStandalone: false, selector: "fx-ui-tab-group", inputs: { activeIndex: "activeIndex" }, host: { listeners: { "window:resize": "onResize()" } }, queries: [{ propertyName: "tabs", predicate: TabComponent }], viewQueries: [{ propertyName: "tabListRef", first: true, predicate: ["tabList"], descendants: true, static: true }, { propertyName: "underlineRef", first: true, predicate: ["underline"], descendants: true, static: true }], ngImport: i0, template: "<div class=\"relative\">\n <!-- Left scroll button -->\n <button\n class=\"left-scroll-button\"\n type=\"button\"\n (click)=\"scroll('left')\"\n [style.opacity]=\"canScrollLeft ? 1 : 0\"\n [style.pointerEvents]=\"canScrollLeft ? 'auto' : 'none'\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n class=\"h-5 w-5 text-text-primary\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M15 19l-7-7 7-7\" />\n </svg>\n </button>\n\n <div\n #tabList\n class=\"relative flex overflow-x-auto no-scrollbar scroll-smooth\"\n (scroll)=\"onScroll()\"\n >\n <button\n *ngFor=\"let tab of tabs.toArray(); let i = index\"\n (click)=\"selectTab(i)\"\n class=\"tab-btn tab-bar-btn\"\n >\n <div class=\"flex flex-row items-center justify-center\">\n @if (tab.icon) {\n <fx-ui-hero-icon\n [icon]=\"tab.icon\"\n [size]=\"20\"\n class=\"flex mr-[2px]\"\n [ngClass]=\"{\n 'text-gradient-primary-start mb-small': i === activeIndex,\n 'text-text-placeholder': i !== activeIndex,\n }\"\n ></fx-ui-hero-icon>\n }\n <div\n [ngClass]=\"\n i === activeIndex ? 'txt-field-label text-gradient-primary-start' : 'txt-placeholder'\n \"\n >\n {{ tab.label }}\n </div>\n </div>\n </button>\n\n <span #underline class=\"underline\" style=\"width: 0; transform: translateX(0)\"></span>\n </div>\n\n <button\n class=\"right-scroll-button\"\n type=\"button\"\n (click)=\"scroll('right')\"\n [style.opacity]=\"canScrollRight ? 1 : 0\"\n [style.pointerEvents]=\"canScrollRight ? 'auto' : 'none'\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n class=\"h-5 w-5 text-text-primary\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 5l7 7-7 7\" />\n </svg>\n </button>\n</div>\n\n<!-- Tab content -->\n<div class=\"transition-all duration-300 ease-in-out mt-4\">\n <ng-container *ngFor=\"let tab of tabs.toArray(); let i = index\">\n <div *ngIf=\"i === activeIndex\" class=\"animate-fadeIn\">\n <ng-container *ngTemplateOutlet=\"tab.content\"></ng-container>\n </div>\n </ng-container>\n</div>\n", styles: ["@keyframes fadeIn{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}.animate-fadeIn{animation:fadeIn .28s ease-in-out}.no-scrollbar::-webkit-scrollbar{display:none}.no-scrollbar{-ms-overflow-style:none;scrollbar-width:none}.underline{position:absolute;bottom:0;height:2px;background-color:rgb(var(--gradient-primary-start));transition:transform .25s cubic-bezier(.2,.9,.2,1),width .25s cubic-bezier(.2,.9,.2,1);will-change:transform,width;transform:translate(0);width:0;margin-left:32px;margin-right:32px}.left-scroll-button{position:absolute;
|
|
3848
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: TabGroupComponent, isStandalone: false, selector: "fx-ui-tab-group", inputs: { activeIndex: "activeIndex" }, host: { listeners: { "window:resize": "onResize()" } }, queries: [{ propertyName: "tabs", predicate: TabComponent }], viewQueries: [{ propertyName: "tabListRef", first: true, predicate: ["tabList"], descendants: true, static: true }, { propertyName: "underlineRef", first: true, predicate: ["underline"], descendants: true, static: true }], ngImport: i0, template: "<div class=\"relative\">\n <!-- Left scroll button -->\n <button\n class=\"left-scroll-button\"\n type=\"button\"\n aria-label=\"Scroll tabs left\"\n (click)=\"scroll('left')\"\n [style.opacity]=\"canScrollLeft ? 1 : 0\"\n [style.pointerEvents]=\"canScrollLeft ? 'auto' : 'none'\"\n [attr.tabindex]=\"canScrollLeft ? null : -1\"\n [attr.aria-hidden]=\"!canScrollLeft\"\n >\n <svg\n aria-hidden=\"true\"\n xmlns=\"http://www.w3.org/2000/svg\"\n class=\"h-5 w-5 text-text-primary\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M15 19l-7-7 7-7\" />\n </svg>\n </button>\n\n <div\n #tabList\n role=\"tablist\"\n class=\"relative flex overflow-x-auto no-scrollbar scroll-smooth\"\n (scroll)=\"onScroll()\"\n >\n <button\n *ngFor=\"let tab of tabs.toArray(); let i = index\"\n type=\"button\"\n role=\"tab\"\n [attr.aria-selected]=\"i === activeIndex\"\n [tabindex]=\"i === activeIndex ? 0 : -1\"\n (click)=\"selectTab(i)\"\n (keydown)=\"onTabKeydown($event, i)\"\n class=\"tab-btn tab-bar-btn\"\n >\n <div class=\"flex flex-row items-center justify-center\">\n @if (tab.icon) {\n <fx-ui-hero-icon\n [icon]=\"tab.icon\"\n [size]=\"20\"\n class=\"flex mr-[2px]\"\n [ngClass]=\"{\n 'text-gradient-primary-start mb-small': i === activeIndex,\n 'text-text-placeholder': i !== activeIndex,\n }\"\n ></fx-ui-hero-icon>\n }\n <div\n [ngClass]=\"\n i === activeIndex ? 'txt-field-label text-gradient-primary-start' : 'txt-placeholder'\n \"\n >\n {{ tab.label }}\n </div>\n </div>\n </button>\n\n <span #underline class=\"underline\" style=\"width: 0; transform: translateX(0)\"></span>\n </div>\n\n <button\n class=\"right-scroll-button\"\n type=\"button\"\n aria-label=\"Scroll tabs right\"\n (click)=\"scroll('right')\"\n [style.opacity]=\"canScrollRight ? 1 : 0\"\n [style.pointerEvents]=\"canScrollRight ? 'auto' : 'none'\"\n [attr.tabindex]=\"canScrollRight ? null : -1\"\n [attr.aria-hidden]=\"!canScrollRight\"\n >\n <svg\n aria-hidden=\"true\"\n xmlns=\"http://www.w3.org/2000/svg\"\n class=\"h-5 w-5 text-text-primary\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 5l7 7-7 7\" />\n </svg>\n </button>\n</div>\n\n<!-- Tab content -->\n<div class=\"transition-all duration-300 ease-in-out mt-4\">\n <ng-container *ngFor=\"let tab of tabs.toArray(); let i = index\">\n <div *ngIf=\"i === activeIndex\" role=\"tabpanel\" class=\"animate-fadeIn\">\n <ng-container *ngTemplateOutlet=\"tab.content\"></ng-container>\n </div>\n </ng-container>\n</div>\n", styles: ["@charset \"UTF-8\";@layer properties;@keyframes fadeIn{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}.animate-fadeIn{animation:fadeIn .28s ease-in-out}.no-scrollbar::-webkit-scrollbar{display:none}.no-scrollbar{-ms-overflow-style:none;scrollbar-width:none}.underline{position:absolute;bottom:0;height:2px;background-color:rgb(var(--gradient-primary-start));transition:transform .25s cubic-bezier(.2,.9,.2,1),width .25s cubic-bezier(.2,.9,.2,1);will-change:transform,width;transform:translate(0);width:0;margin-left:32px;margin-right:32px}.left-scroll-button{position:absolute;top:0;left:0;z-index:10;display:flex;height:100%;align-items:center;background-color:rgb(var(--bg-primary));padding-inline:.5rem;transition-property:opacity;transition-timing-function:var(--tw-ease, var(--default-transition-timing-function, cubic-bezier(.4, 0, .2, 1)));transition-duration:var(--tw-duration, var(--default-transition-duration, .15s));--tw-duration: .2s;transition-duration:.2s}.right-scroll-button{position:absolute;top:0;right:0;z-index:10;display:flex;height:100%;align-items:center;background-color:rgb(var(--bg-primary));padding-inline:.5rem;transition-property:opacity;transition-timing-function:var(--tw-ease, var(--default-transition-timing-function, cubic-bezier(.4, 0, .2, 1)));transition-duration:var(--tw-duration, var(--default-transition-duration, .15s));--tw-duration: .2s;transition-duration:.2s}.tab-bar-btn{position:relative;padding-inline:1rem;padding-block:.5rem;font-size:.875rem;line-height:var(--tw-leading, var(--text-sm--line-height, calc(1.25 / .875)));white-space:nowrap;transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease, var(--default-transition-timing-function, cubic-bezier(.4, 0, .2, 1)));transition-duration:var(--tw-duration, var(--default-transition-duration, .15s));--tw-duration: .2s;transition-duration:.2s}@property --tw-duration{syntax: \"*\"; inherits: false;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-duration: initial}}}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: HeroIconComponent, selector: "fx-ui-hero-icon", inputs: ["stroke", "icon", "solid", "outline", "size", "color", "class"] }] });
|
|
3187
3849
|
}
|
|
3188
3850
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: TabGroupComponent, decorators: [{
|
|
3189
3851
|
type: Component,
|
|
3190
|
-
args: [{ selector: 'fx-ui-tab-group', standalone: false, template: "<div class=\"relative\">\n <!-- Left scroll button -->\n <button\n class=\"left-scroll-button\"\n type=\"button\"\n (click)=\"scroll('left')\"\n [style.opacity]=\"canScrollLeft ? 1 : 0\"\n [style.pointerEvents]=\"canScrollLeft ? 'auto' : 'none'\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n class=\"h-5 w-5 text-text-primary\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M15 19l-7-7 7-7\" />\n </svg>\n </button>\n\n <div\n #tabList\n class=\"relative flex overflow-x-auto no-scrollbar scroll-smooth\"\n (scroll)=\"onScroll()\"\n >\n <button\n *ngFor=\"let tab of tabs.toArray(); let i = index\"\n (click)=\"selectTab(i)\"\n class=\"tab-btn tab-bar-btn\"\n >\n <div class=\"flex flex-row items-center justify-center\">\n @if (tab.icon) {\n <fx-ui-hero-icon\n [icon]=\"tab.icon\"\n [size]=\"20\"\n class=\"flex mr-[2px]\"\n [ngClass]=\"{\n 'text-gradient-primary-start mb-small': i === activeIndex,\n 'text-text-placeholder': i !== activeIndex,\n }\"\n ></fx-ui-hero-icon>\n }\n <div\n [ngClass]=\"\n i === activeIndex ? 'txt-field-label text-gradient-primary-start' : 'txt-placeholder'\n \"\n >\n {{ tab.label }}\n </div>\n </div>\n </button>\n\n <span #underline class=\"underline\" style=\"width: 0; transform: translateX(0)\"></span>\n </div>\n\n <button\n class=\"right-scroll-button\"\n type=\"button\"\n (click)=\"scroll('right')\"\n [style.opacity]=\"canScrollRight ? 1 : 0\"\n [style.pointerEvents]=\"canScrollRight ? 'auto' : 'none'\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n class=\"h-5 w-5 text-text-primary\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 5l7 7-7 7\" />\n </svg>\n </button>\n</div>\n\n<!-- Tab content -->\n<div class=\"transition-all duration-300 ease-in-out mt-4\">\n <ng-container *ngFor=\"let tab of tabs.toArray(); let i = index\">\n <div *ngIf=\"i === activeIndex\" class=\"animate-fadeIn\">\n <ng-container *ngTemplateOutlet=\"tab.content\"></ng-container>\n </div>\n </ng-container>\n</div>\n", styles: ["@keyframes fadeIn{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}.animate-fadeIn{animation:fadeIn .28s ease-in-out}.no-scrollbar::-webkit-scrollbar{display:none}.no-scrollbar{-ms-overflow-style:none;scrollbar-width:none}.underline{position:absolute;bottom:0;height:2px;background-color:rgb(var(--gradient-primary-start));transition:transform .25s cubic-bezier(.2,.9,.2,1),width .25s cubic-bezier(.2,.9,.2,1);will-change:transform,width;transform:translate(0);width:0;margin-left:32px;margin-right:32px}.left-scroll-button{position:absolute;
|
|
3852
|
+
args: [{ selector: 'fx-ui-tab-group', standalone: false, template: "<div class=\"relative\">\n <!-- Left scroll button -->\n <button\n class=\"left-scroll-button\"\n type=\"button\"\n aria-label=\"Scroll tabs left\"\n (click)=\"scroll('left')\"\n [style.opacity]=\"canScrollLeft ? 1 : 0\"\n [style.pointerEvents]=\"canScrollLeft ? 'auto' : 'none'\"\n [attr.tabindex]=\"canScrollLeft ? null : -1\"\n [attr.aria-hidden]=\"!canScrollLeft\"\n >\n <svg\n aria-hidden=\"true\"\n xmlns=\"http://www.w3.org/2000/svg\"\n class=\"h-5 w-5 text-text-primary\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M15 19l-7-7 7-7\" />\n </svg>\n </button>\n\n <div\n #tabList\n role=\"tablist\"\n class=\"relative flex overflow-x-auto no-scrollbar scroll-smooth\"\n (scroll)=\"onScroll()\"\n >\n <button\n *ngFor=\"let tab of tabs.toArray(); let i = index\"\n type=\"button\"\n role=\"tab\"\n [attr.aria-selected]=\"i === activeIndex\"\n [tabindex]=\"i === activeIndex ? 0 : -1\"\n (click)=\"selectTab(i)\"\n (keydown)=\"onTabKeydown($event, i)\"\n class=\"tab-btn tab-bar-btn\"\n >\n <div class=\"flex flex-row items-center justify-center\">\n @if (tab.icon) {\n <fx-ui-hero-icon\n [icon]=\"tab.icon\"\n [size]=\"20\"\n class=\"flex mr-[2px]\"\n [ngClass]=\"{\n 'text-gradient-primary-start mb-small': i === activeIndex,\n 'text-text-placeholder': i !== activeIndex,\n }\"\n ></fx-ui-hero-icon>\n }\n <div\n [ngClass]=\"\n i === activeIndex ? 'txt-field-label text-gradient-primary-start' : 'txt-placeholder'\n \"\n >\n {{ tab.label }}\n </div>\n </div>\n </button>\n\n <span #underline class=\"underline\" style=\"width: 0; transform: translateX(0)\"></span>\n </div>\n\n <button\n class=\"right-scroll-button\"\n type=\"button\"\n aria-label=\"Scroll tabs right\"\n (click)=\"scroll('right')\"\n [style.opacity]=\"canScrollRight ? 1 : 0\"\n [style.pointerEvents]=\"canScrollRight ? 'auto' : 'none'\"\n [attr.tabindex]=\"canScrollRight ? null : -1\"\n [attr.aria-hidden]=\"!canScrollRight\"\n >\n <svg\n aria-hidden=\"true\"\n xmlns=\"http://www.w3.org/2000/svg\"\n class=\"h-5 w-5 text-text-primary\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 5l7 7-7 7\" />\n </svg>\n </button>\n</div>\n\n<!-- Tab content -->\n<div class=\"transition-all duration-300 ease-in-out mt-4\">\n <ng-container *ngFor=\"let tab of tabs.toArray(); let i = index\">\n <div *ngIf=\"i === activeIndex\" role=\"tabpanel\" class=\"animate-fadeIn\">\n <ng-container *ngTemplateOutlet=\"tab.content\"></ng-container>\n </div>\n </ng-container>\n</div>\n", styles: ["@charset \"UTF-8\";@layer properties;@keyframes fadeIn{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}.animate-fadeIn{animation:fadeIn .28s ease-in-out}.no-scrollbar::-webkit-scrollbar{display:none}.no-scrollbar{-ms-overflow-style:none;scrollbar-width:none}.underline{position:absolute;bottom:0;height:2px;background-color:rgb(var(--gradient-primary-start));transition:transform .25s cubic-bezier(.2,.9,.2,1),width .25s cubic-bezier(.2,.9,.2,1);will-change:transform,width;transform:translate(0);width:0;margin-left:32px;margin-right:32px}.left-scroll-button{position:absolute;top:0;left:0;z-index:10;display:flex;height:100%;align-items:center;background-color:rgb(var(--bg-primary));padding-inline:.5rem;transition-property:opacity;transition-timing-function:var(--tw-ease, var(--default-transition-timing-function, cubic-bezier(.4, 0, .2, 1)));transition-duration:var(--tw-duration, var(--default-transition-duration, .15s));--tw-duration: .2s;transition-duration:.2s}.right-scroll-button{position:absolute;top:0;right:0;z-index:10;display:flex;height:100%;align-items:center;background-color:rgb(var(--bg-primary));padding-inline:.5rem;transition-property:opacity;transition-timing-function:var(--tw-ease, var(--default-transition-timing-function, cubic-bezier(.4, 0, .2, 1)));transition-duration:var(--tw-duration, var(--default-transition-duration, .15s));--tw-duration: .2s;transition-duration:.2s}.tab-bar-btn{position:relative;padding-inline:1rem;padding-block:.5rem;font-size:.875rem;line-height:var(--tw-leading, var(--text-sm--line-height, calc(1.25 / .875)));white-space:nowrap;transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease, var(--default-transition-timing-function, cubic-bezier(.4, 0, .2, 1)));transition-duration:var(--tw-duration, var(--default-transition-duration, .15s));--tw-duration: .2s;transition-duration:.2s}@property --tw-duration{syntax: \"*\"; inherits: false;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-duration: initial}}}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"] }]
|
|
3191
3853
|
}], ctorParameters: () => [{ type: i0.Renderer2 }], propDecorators: { tabs: [{
|
|
3192
3854
|
type: ContentChildren,
|
|
3193
3855
|
args: [TabComponent]
|
|
@@ -3226,16 +3888,16 @@ class TagComponent {
|
|
|
3226
3888
|
rounded = true;
|
|
3227
3889
|
icon;
|
|
3228
3890
|
solid = false;
|
|
3229
|
-
|
|
3891
|
+
clicked = new EventEmitter();
|
|
3230
3892
|
onClick() {
|
|
3231
|
-
this.
|
|
3893
|
+
this.clicked.emit();
|
|
3232
3894
|
}
|
|
3233
3895
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: TagComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3234
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: TagComponent, isStandalone: false, selector: "fx-ui-tag", inputs: { label: "label", type: "type", rounded: "rounded", icon: "icon", solid: "solid" }, outputs: {
|
|
3896
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: TagComponent, isStandalone: false, selector: "fx-ui-tag", inputs: { label: "label", type: "type", rounded: "rounded", icon: "icon", solid: "solid" }, outputs: { clicked: "clicked" }, ngImport: i0, template: "<!-- Interactive semantics only when a (clicked) listener is attached. -->\n<div\n (click)=\"onClick()\"\n (keydown.enter)=\"onClick()\"\n (keydown.space)=\"$event.preventDefault(); onClick()\"\n [attr.role]=\"clicked.observed ? 'button' : null\"\n [attr.tabindex]=\"clicked.observed ? 0 : null\"\n [class]=\"\n `flex flex-row gap-small ${rounded ? 'tag-round' : 'tag-square'} tag-${type}${solid ? '-solid' : ''}${clicked.observed ? ' cursor-pointer focus-visible:ring-2 focus-visible:ring-primary/50' : ''}`\n \"\n>\n @if (icon) {\n <fx-ui-hero-icon\n [class]=\"`flex txt-tag-${type} ${solid && type !== 'default' ? 'text-white' : ''}`\"\n [icon]=\"icon\"\n [size]=\"18\"\n ></fx-ui-hero-icon>\n }\n\n <div [class]=\"`txt-tag-${type} ${solid && type !== 'default' ? 'text-white font-medium' : ''}`\">\n {{ label }}\n </div>\n</div>\n", styles: [""], dependencies: [{ kind: "component", type: HeroIconComponent, selector: "fx-ui-hero-icon", inputs: ["stroke", "icon", "solid", "outline", "size", "color", "class"] }] });
|
|
3235
3897
|
}
|
|
3236
3898
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: TagComponent, decorators: [{
|
|
3237
3899
|
type: Component,
|
|
3238
|
-
args: [{ selector: 'fx-ui-tag', standalone: false, template: "<div\n (click)=\"onClick()\"\n [class]=\"\n `flex flex-row gap-small ${rounded ? 'tag-round' : 'tag-square'} tag-${type}${solid ? '-solid' : ''}`\n \"\n>\n @if (icon) {\n <fx-ui-hero-icon\n [class]=\"`flex txt-tag-${type} ${solid && type !== 'default' ? 'text-white' : ''}`\"\n [icon]=\"icon\"\n [size]=\"18\"\n ></fx-ui-hero-icon>\n }\n\n <div [class]=\"`txt-tag-${type} ${solid && type !== 'default' ? 'text-white font-medium' : ''}`\">\n {{ label }}\n </div>\n</div>\n" }]
|
|
3900
|
+
args: [{ selector: 'fx-ui-tag', standalone: false, template: "<!-- Interactive semantics only when a (clicked) listener is attached. -->\n<div\n (click)=\"onClick()\"\n (keydown.enter)=\"onClick()\"\n (keydown.space)=\"$event.preventDefault(); onClick()\"\n [attr.role]=\"clicked.observed ? 'button' : null\"\n [attr.tabindex]=\"clicked.observed ? 0 : null\"\n [class]=\"\n `flex flex-row gap-small ${rounded ? 'tag-round' : 'tag-square'} tag-${type}${solid ? '-solid' : ''}${clicked.observed ? ' cursor-pointer focus-visible:ring-2 focus-visible:ring-primary/50' : ''}`\n \"\n>\n @if (icon) {\n <fx-ui-hero-icon\n [class]=\"`flex txt-tag-${type} ${solid && type !== 'default' ? 'text-white' : ''}`\"\n [icon]=\"icon\"\n [size]=\"18\"\n ></fx-ui-hero-icon>\n }\n\n <div [class]=\"`txt-tag-${type} ${solid && type !== 'default' ? 'text-white font-medium' : ''}`\">\n {{ label }}\n </div>\n</div>\n" }]
|
|
3239
3901
|
}], propDecorators: { label: [{
|
|
3240
3902
|
type: Input
|
|
3241
3903
|
}], type: [{
|
|
@@ -3246,7 +3908,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImpor
|
|
|
3246
3908
|
type: Input
|
|
3247
3909
|
}], solid: [{
|
|
3248
3910
|
type: Input
|
|
3249
|
-
}],
|
|
3911
|
+
}], clicked: [{
|
|
3250
3912
|
type: Output
|
|
3251
3913
|
}] } });
|
|
3252
3914
|
|
|
@@ -3335,7 +3997,7 @@ class TreeDiagram {
|
|
|
3335
3997
|
this.cdr.detectChanges();
|
|
3336
3998
|
}
|
|
3337
3999
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: TreeDiagram, deps: [{ token: i0.NgZone }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
3338
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.4", type: TreeDiagram, isStandalone: false, selector: "fx-ui-tree-diagram", inputs: { data: "data" }, viewQueries: [{ propertyName: "container", first: true, predicate: ["container"], descendants: true }, { propertyName: "root", first: true, predicate: ["root"], descendants: true }, { propertyName: "children", predicate: ["child"], descendants: true }], ngImport: i0, template: "<div class=\"relative p-8\" #container>\n\n <!-- ROOT -->\n <div\n #root\n class=\"mx-auto w-fit bg-blue-600 text-white px-7 py-3 rounded-md font-semibold text-center\"\n >\n {{ data.label }}\n </div>\n\n <!-- CHILDREN -->\n <div class=\"mt-16 flex justify-center gap-16\">\n <div\n *ngFor=\"let child of data.children\"\n #child\n class=\"w-48 bg-rose-600 text-white px-4 py-4 rounded-lg text-center\"\n >\n <div class=\"font-semibold text-lg\">{{ child.label }}</div>\n <div class=\"mt-1 text-sm opacity-90\">{{ child.status }}</div>\n </div>\n </div>\n\n <!-- SVG CONNECTORS -->\n <svg\n class=\"absolute inset-0 pointer-events-none\"\n [attr.width]=\"svgWidth\"\n [attr.height]=\"svgHeight\"\n >\n <line\n *ngFor=\"let line of lines\"\n [attr.x1]=\"line.x1\"\n [attr.y1]=\"line.y1\"\n [attr.x2]=\"line.x2\"\n [attr.y2]=\"line.y2\"\n stroke=\"#1f2937\"\n stroke-width=\"2\"\n />\n </svg>\n\n</div>\n", styles: [""], dependencies: [{ kind: "directive", type:
|
|
4000
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.4", type: TreeDiagram, isStandalone: false, selector: "fx-ui-tree-diagram", inputs: { data: "data" }, viewQueries: [{ propertyName: "container", first: true, predicate: ["container"], descendants: true }, { propertyName: "root", first: true, predicate: ["root"], descendants: true }, { propertyName: "children", predicate: ["child"], descendants: true }], ngImport: i0, template: "<div class=\"relative p-8\" #container>\n\n <!-- ROOT -->\n <div\n #root\n class=\"mx-auto w-fit bg-blue-600 text-white px-7 py-3 rounded-md font-semibold text-center\"\n >\n {{ data.label }}\n </div>\n\n <!-- CHILDREN -->\n <div class=\"mt-16 flex justify-center gap-16\">\n <div\n *ngFor=\"let child of data.children\"\n #child\n class=\"w-48 bg-rose-600 text-white px-4 py-4 rounded-lg text-center\"\n >\n <div class=\"font-semibold text-lg\">{{ child.label }}</div>\n <div class=\"mt-1 text-sm opacity-90\">{{ child.status }}</div>\n </div>\n </div>\n\n <!-- SVG CONNECTORS -->\n <svg\n class=\"absolute inset-0 pointer-events-none\"\n [attr.width]=\"svgWidth\"\n [attr.height]=\"svgHeight\"\n >\n <line\n *ngFor=\"let line of lines\"\n [attr.x1]=\"line.x1\"\n [attr.y1]=\"line.y1\"\n [attr.x2]=\"line.x2\"\n [attr.y2]=\"line.y2\"\n stroke=\"#1f2937\"\n stroke-width=\"2\"\n />\n </svg>\n\n</div>\n", styles: [""], dependencies: [{ kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }] });
|
|
3339
4001
|
}
|
|
3340
4002
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: TreeDiagram, decorators: [{
|
|
3341
4003
|
type: Component,
|
|
@@ -3388,11 +4050,11 @@ class KanbanBoardComponent {
|
|
|
3388
4050
|
});
|
|
3389
4051
|
}
|
|
3390
4052
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: KanbanBoardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3391
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: KanbanBoardComponent, isStandalone: false, selector: "fx-ui-kanban-board", inputs: { columns: "columns", itemTemplate: "itemTemplate" }, outputs: { columnChanged: "columnChanged" }, ngImport: i0, template: "<div class=\"kanban-board\">\n <div class=\"kanban-columns\">\n @for (column of columns; track column.id) {\n <div class=\"kanban-column\">\n <div class=\"kanban-column-header\">\n <h3 class=\"kanban-column-title\">{{ column.title }}</h3>\n <span class=\"kanban-column-count\">{{ column.items.length }}</span>\n </div>\n \n <div\n class=\"kanban-column-content\"\n cdkDropList\n [id]=\"column.id\"\n [cdkDropListData]=\"column.items\"\n [cdkDropListConnectedTo]=\"getColumnIds()\"\n (cdkDropListDropped)=\"onDrop($event, column.id)\"\n >\n @for (item of column.items; track $index) {\n <div class=\"kanban-item\" cdkDrag>\n <div class=\"kanban-item-placeholder\" *cdkDragPlaceholder></div>\n \n @if (itemTemplate) {\n <ng-container\n *ngTemplateOutlet=\"itemTemplate; context: { $implicit: item, index: $index }\"\n ></ng-container>\n } @else {\n <div class=\"kanban-item-default\">\n {{ item }}\n </div>\n }\n </div>\n }\n \n @if (column.items.length === 0) {\n <div class=\"kanban-column-empty\">\n Drop items here\n </div>\n }\n </div>\n </div>\n }\n </div>\n</div>\n", styles: [".kanban-board{height:100%;width:100%;overflow-x:auto}.kanban-columns{display:flex;min-height:100%;gap:1rem;padding:1rem}.kanban-column{display:flex;min-width:300px;flex-direction:column;border-radius:.5rem;--tw-bg-opacity: 1;background-color:rgb(249 250 251 / var(--tw-bg-opacity, 1))}@media(prefers-color-scheme:dark){.kanban-column{--tw-bg-opacity: 1;background-color:rgb(31 41 55 / var(--tw-bg-opacity, 1))}}.kanban-column{flex-shrink:0}.kanban-column-header{display:flex;align-items:center;justify-content:space-between;border-bottom-width:1px;--tw-border-opacity: 1;border-color:rgb(229 231 235 / var(--tw-border-opacity, 1));padding:1rem}@media(prefers-color-scheme:dark){.kanban-column-header{--tw-border-opacity: 1;border-color:rgb(55 65 81 / var(--tw-border-opacity, 1))}}.kanban-column-title{margin:0;font-size:1.125rem;line-height:1.75rem;font-weight:600;--tw-text-opacity: 1;color:rgb(17 24 39 / var(--tw-text-opacity, 1))}@media(prefers-color-scheme:dark){.kanban-column-title{--tw-text-opacity: 1;color:rgb(243 244 246 / var(--tw-text-opacity, 1))}}.kanban-column-count{border-radius:9999px;--tw-bg-opacity: 1;background-color:rgb(229 231 235 / var(--tw-bg-opacity, 1));padding:.25rem .5rem;font-size:.875rem;font-weight:500;--tw-text-opacity: 1;color:rgb(55 65 81 / var(--tw-text-opacity, 1))}@media(prefers-color-scheme:dark){.kanban-column-count{--tw-bg-opacity: 1;background-color:rgb(55 65 81 / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(209 213 219 / var(--tw-text-opacity, 1))}}.kanban-column-content{flex:1 1 0%}.kanban-column-content>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.75rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem * var(--tw-space-y-reverse))}.kanban-column-content{overflow-y:auto;padding:1rem;min-height:200px}.kanban-item{border-radius:.5rem;border-width:1px;--tw-border-opacity: 1;border-color:rgb(229 231 235 / var(--tw-border-opacity, 1));--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1));--tw-shadow: 0 1px 2px 0 rgb(var(--shadow-color) / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}@media(prefers-color-scheme:dark){.kanban-item{--tw-border-opacity: 1;border-color:rgb(75 85 99 / var(--tw-border-opacity, 1));--tw-bg-opacity: 1;background-color:rgb(55 65 81 / var(--tw-bg-opacity, 1))}}.kanban-item{transition-property:box-shadow;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s;cursor:move}.kanban-item:hover{--tw-shadow: 0 4px 6px -1px rgb(var(--shadow-color) / .1), 0 2px 4px -2px rgb(var(--shadow-color) / .1);--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.kanban-item-default{padding:1rem;--tw-text-opacity: 1;color:rgb(17 24 39 / var(--tw-text-opacity, 1))}@media(prefers-color-scheme:dark){.kanban-item-default{--tw-text-opacity: 1;color:rgb(243 244 246 / var(--tw-text-opacity, 1))}}.kanban-item-placeholder{border-radius:.5rem;border-width:2px;border-style:dashed;--tw-border-opacity: 1;border-color:rgb(156 163 175 / var(--tw-border-opacity, 1));--tw-bg-opacity: 1;background-color:rgb(229 231 235 / var(--tw-bg-opacity, 1))}@media(prefers-color-scheme:dark){.kanban-item-placeholder{--tw-border-opacity: 1;border-color:rgb(107 114 128 / var(--tw-border-opacity, 1));--tw-bg-opacity: 1;background-color:rgb(75 85 99 / var(--tw-bg-opacity, 1))}}.kanban-item-placeholder{opacity:.5;min-height:60px}.kanban-column-empty{display:flex;height:8rem;align-items:center;justify-content:center;font-size:.875rem;--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity, 1))}@media(prefers-color-scheme:dark){.kanban-column-empty{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity, 1))}}.kanban-column-empty{border-radius:.5rem;border-width:2px;border-style:dashed;--tw-border-opacity: 1;border-color:rgb(209 213 219 / var(--tw-border-opacity, 1))}@media(prefers-color-scheme:dark){.kanban-column-empty{--tw-border-opacity: 1;border-color:rgb(75 85 99 / var(--tw-border-opacity, 1))}}.cdk-drag-preview{border-radius:.5rem;opacity:.8;--tw-shadow: 0 10px 15px -3px rgb(var(--shadow-color) / .1), 0 4px 6px -4px rgb(var(--shadow-color) / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.cdk-drag-animating{transition:transform .25s cubic-bezier(0,0,.2,1)}.kanban-column-content.cdk-drop-list-dragging .kanban-item:not(.cdk-drag-placeholder){transition:transform .25s cubic-bezier(0,0,.2,1)}\n"], dependencies: [{ kind: "directive", type: i2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i2$2.CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: i2$2.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: i2$2.CdkDragPlaceholder, selector: "ng-template[cdkDragPlaceholder]", inputs: ["data"] }] });
|
|
4053
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: KanbanBoardComponent, isStandalone: false, selector: "fx-ui-kanban-board", inputs: { columns: "columns", itemTemplate: "itemTemplate" }, outputs: { columnChanged: "columnChanged" }, ngImport: i0, template: "<div class=\"kanban-board\">\n <div class=\"kanban-columns\">\n @for (column of columns; track column.id) {\n <div class=\"kanban-column\">\n <div class=\"kanban-column-header\">\n <h3 class=\"kanban-column-title\">{{ column.title }}</h3>\n <span class=\"kanban-column-count\">{{ column.items.length }}</span>\n </div>\n \n <div\n class=\"kanban-column-content\"\n cdkDropList\n [id]=\"column.id\"\n [cdkDropListData]=\"column.items\"\n [cdkDropListConnectedTo]=\"getColumnIds()\"\n (cdkDropListDropped)=\"onDrop($event, column.id)\"\n >\n @for (item of column.items; track $index) {\n <div class=\"kanban-item\" cdkDrag>\n <div class=\"kanban-item-placeholder\" *cdkDragPlaceholder></div>\n \n @if (itemTemplate) {\n <ng-container\n *ngTemplateOutlet=\"itemTemplate; context: { $implicit: item, index: $index }\"\n ></ng-container>\n } @else {\n <div class=\"kanban-item-default\">\n {{ item }}\n </div>\n }\n </div>\n }\n \n @if (column.items.length === 0) {\n <div class=\"kanban-column-empty\">\n Drop items here\n </div>\n }\n </div>\n </div>\n }\n </div>\n</div>\n", styles: ["@charset \"UTF-8\";@layer properties;.kanban-board{height:100%;width:100%;overflow-x:auto}.kanban-columns{display:flex;min-height:100%;gap:calc(var(--spacing, .25rem) * 4);padding:calc(var(--spacing, .25rem) * 4)}.kanban-column{display:flex;min-width:300px;flex-direction:column;border-radius:.5rem;background-color:var(--color-gray-50, oklch(98.5% .002 247.839));flex-shrink:0}@media (prefers-color-scheme: dark){.kanban-column{background-color:var(--color-gray-800, oklch(27.8% .033 256.848))}}.kanban-column-header{display:flex;align-items:center;justify-content:space-between;border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:var(--color-gray-200, oklch(92.8% .006 264.531));padding:calc(var(--spacing, .25rem) * 4)}@media (prefers-color-scheme: dark){.kanban-column-header{border-color:var(--color-gray-700, oklch(37.3% .034 259.733))}}.kanban-column-title{margin:0;font-size:var(--text-lg, 1.125rem);line-height:var(--tw-leading, var(--text-lg--line-height, calc(1.75 / 1.125)));--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);color:var(--color-gray-900, oklch(21% .034 264.665))}@media (prefers-color-scheme: dark){.kanban-column-title{color:var(--color-gray-100, oklch(96.7% .003 264.542))}}.kanban-column-count{border-radius:calc(infinity * 1px);background-color:var(--color-gray-200, oklch(92.8% .006 264.531));padding-inline:calc(var(--spacing, .25rem) * 2);padding-block:var(--spacing, .25rem);font-size:.875rem;line-height:var(--tw-leading, var(--text-sm--line-height, calc(1.25 / .875)));--tw-font-weight: var(--font-weight-medium, 500);font-weight:var(--font-weight-medium, 500);color:var(--color-gray-700, oklch(37.3% .034 259.733))}@media (prefers-color-scheme: dark){.kanban-column-count{background-color:var(--color-gray-700, oklch(37.3% .034 259.733))}}@media (prefers-color-scheme: dark){.kanban-column-count{color:var(--color-gray-300, oklch(87.2% .01 258.338))}}.kanban-column-content{flex:1;overflow-y:auto;padding:calc(var(--spacing, .25rem) * 4);min-height:200px}:where(.kanban-column-content>:not(:last-child)){--tw-space-y-reverse: 0;margin-block-start:calc(calc(var(--spacing, .25rem) * 3) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing, .25rem) * 3) * calc(1 - var(--tw-space-y-reverse)))}.kanban-item{border-radius:.5rem;border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200, oklch(92.8% .006 264.531));background-color:var(--color-white, #fff);--tw-shadow: 0 1px 2px 0 var(--tw-shadow-color, rgb(var(--shadow-color) / .05));box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);transition-property:box-shadow;transition-timing-function:var(--tw-ease, var(--default-transition-timing-function, cubic-bezier(.4, 0, .2, 1)));transition-duration:var(--tw-duration, var(--default-transition-duration, .15s));--tw-duration: .2s;transition-duration:.2s;cursor:move}@media (prefers-color-scheme: dark){.kanban-item{border-color:var(--color-gray-600, oklch(44.6% .03 256.802))}}@media (prefers-color-scheme: dark){.kanban-item{background-color:var(--color-gray-700, oklch(37.3% .034 259.733))}}.kanban-item:hover{--tw-shadow: 0 4px 6px -1px var(--tw-shadow-color, rgb(var(--shadow-color) / .1)), 0 2px 4px -2px var(--tw-shadow-color, rgb(var(--shadow-color) / .1));box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.kanban-item-default{padding:calc(var(--spacing, .25rem) * 4);color:var(--color-gray-900, oklch(21% .034 264.665))}@media (prefers-color-scheme: dark){.kanban-item-default{color:var(--color-gray-100, oklch(96.7% .003 264.542))}}.kanban-item-placeholder{border-radius:.5rem;border-style:var(--tw-border-style);border-width:2px;--tw-border-style: dashed;border-style:dashed;border-color:var(--color-gray-400, oklch(70.7% .022 261.325));background-color:var(--color-gray-200, oklch(92.8% .006 264.531));opacity:50%;min-height:60px}@media (prefers-color-scheme: dark){.kanban-item-placeholder{border-color:var(--color-gray-500, oklch(55.1% .027 264.364))}}@media (prefers-color-scheme: dark){.kanban-item-placeholder{background-color:var(--color-gray-600, oklch(44.6% .03 256.802))}}.kanban-column-empty{display:flex;height:calc(var(--spacing, .25rem) * 32);align-items:center;justify-content:center;font-size:.875rem;line-height:var(--tw-leading, var(--text-sm--line-height, calc(1.25 / .875)));color:var(--color-gray-400, oklch(70.7% .022 261.325));border-radius:.5rem;border-style:var(--tw-border-style);border-width:2px;--tw-border-style: dashed;border-style:dashed;border-color:var(--color-gray-300, oklch(87.2% .01 258.338))}@media (prefers-color-scheme: dark){.kanban-column-empty{color:var(--color-gray-500, oklch(55.1% .027 264.364))}}@media (prefers-color-scheme: dark){.kanban-column-empty{border-color:var(--color-gray-600, oklch(44.6% .03 256.802))}}.cdk-drag-preview{border-radius:.5rem;opacity:80%;--tw-shadow: 0 10px 15px -3px var(--tw-shadow-color, rgb(var(--shadow-color) / .1)), 0 4px 6px -4px var(--tw-shadow-color, rgb(var(--shadow-color) / .1));box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.cdk-drag-animating{transition:transform .25s cubic-bezier(0,0,.2,1)}.kanban-column-content.cdk-drop-list-dragging .kanban-item:not(.cdk-drag-placeholder){transition:transform .25s cubic-bezier(0,0,.2,1)}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@property --tw-font-weight{syntax: \"*\"; inherits: false;}@property --tw-space-y-reverse{syntax: \"*\"; inherits: false; initial-value: 0;}@property --tw-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-shadow-color{syntax: \"*\"; inherits: false;}@property --tw-shadow-alpha{syntax: \"<percentage>\"; inherits: false; initial-value: 100%;}@property --tw-inset-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-inset-shadow-color{syntax: \"*\"; inherits: false;}@property --tw-inset-shadow-alpha{syntax: \"<percentage>\"; inherits: false; initial-value: 100%;}@property --tw-ring-color{syntax: \"*\"; inherits: false;}@property --tw-ring-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-inset-ring-color{syntax: \"*\"; inherits: false;}@property --tw-inset-ring-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-ring-inset{syntax: \"*\"; inherits: false;}@property --tw-ring-offset-width{syntax: \"<length>\"; inherits: false; initial-value: 0px;}@property --tw-ring-offset-color{syntax: \"*\"; inherits: false; initial-value: #fff;}@property --tw-ring-offset-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-duration{syntax: \"*\"; inherits: false;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style: solid;--tw-font-weight: initial;--tw-space-y-reverse: 0;--tw-shadow: 0 0 #0000;--tw-shadow-color: initial;--tw-shadow-alpha: 100%;--tw-inset-shadow: 0 0 #0000;--tw-inset-shadow-color: initial;--tw-inset-shadow-alpha: 100%;--tw-ring-color: initial;--tw-ring-shadow: 0 0 #0000;--tw-inset-ring-color: initial;--tw-inset-ring-shadow: 0 0 #0000;--tw-ring-inset: initial;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-offset-shadow: 0 0 #0000;--tw-duration: initial}}}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i2$3.CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: i2$3.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: i2$3.CdkDragPlaceholder, selector: "ng-template[cdkDragPlaceholder]", inputs: ["data"] }] });
|
|
3392
4054
|
}
|
|
3393
4055
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: KanbanBoardComponent, decorators: [{
|
|
3394
4056
|
type: Component,
|
|
3395
|
-
args: [{ selector: 'fx-ui-kanban-board', standalone: false, template: "<div class=\"kanban-board\">\n <div class=\"kanban-columns\">\n @for (column of columns; track column.id) {\n <div class=\"kanban-column\">\n <div class=\"kanban-column-header\">\n <h3 class=\"kanban-column-title\">{{ column.title }}</h3>\n <span class=\"kanban-column-count\">{{ column.items.length }}</span>\n </div>\n \n <div\n class=\"kanban-column-content\"\n cdkDropList\n [id]=\"column.id\"\n [cdkDropListData]=\"column.items\"\n [cdkDropListConnectedTo]=\"getColumnIds()\"\n (cdkDropListDropped)=\"onDrop($event, column.id)\"\n >\n @for (item of column.items; track $index) {\n <div class=\"kanban-item\" cdkDrag>\n <div class=\"kanban-item-placeholder\" *cdkDragPlaceholder></div>\n \n @if (itemTemplate) {\n <ng-container\n *ngTemplateOutlet=\"itemTemplate; context: { $implicit: item, index: $index }\"\n ></ng-container>\n } @else {\n <div class=\"kanban-item-default\">\n {{ item }}\n </div>\n }\n </div>\n }\n \n @if (column.items.length === 0) {\n <div class=\"kanban-column-empty\">\n Drop items here\n </div>\n }\n </div>\n </div>\n }\n </div>\n</div>\n", styles: ["
|
|
4057
|
+
args: [{ selector: 'fx-ui-kanban-board', standalone: false, template: "<div class=\"kanban-board\">\n <div class=\"kanban-columns\">\n @for (column of columns; track column.id) {\n <div class=\"kanban-column\">\n <div class=\"kanban-column-header\">\n <h3 class=\"kanban-column-title\">{{ column.title }}</h3>\n <span class=\"kanban-column-count\">{{ column.items.length }}</span>\n </div>\n \n <div\n class=\"kanban-column-content\"\n cdkDropList\n [id]=\"column.id\"\n [cdkDropListData]=\"column.items\"\n [cdkDropListConnectedTo]=\"getColumnIds()\"\n (cdkDropListDropped)=\"onDrop($event, column.id)\"\n >\n @for (item of column.items; track $index) {\n <div class=\"kanban-item\" cdkDrag>\n <div class=\"kanban-item-placeholder\" *cdkDragPlaceholder></div>\n \n @if (itemTemplate) {\n <ng-container\n *ngTemplateOutlet=\"itemTemplate; context: { $implicit: item, index: $index }\"\n ></ng-container>\n } @else {\n <div class=\"kanban-item-default\">\n {{ item }}\n </div>\n }\n </div>\n }\n \n @if (column.items.length === 0) {\n <div class=\"kanban-column-empty\">\n Drop items here\n </div>\n }\n </div>\n </div>\n }\n </div>\n</div>\n", styles: ["@charset \"UTF-8\";@layer properties;.kanban-board{height:100%;width:100%;overflow-x:auto}.kanban-columns{display:flex;min-height:100%;gap:calc(var(--spacing, .25rem) * 4);padding:calc(var(--spacing, .25rem) * 4)}.kanban-column{display:flex;min-width:300px;flex-direction:column;border-radius:.5rem;background-color:var(--color-gray-50, oklch(98.5% .002 247.839));flex-shrink:0}@media (prefers-color-scheme: dark){.kanban-column{background-color:var(--color-gray-800, oklch(27.8% .033 256.848))}}.kanban-column-header{display:flex;align-items:center;justify-content:space-between;border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:var(--color-gray-200, oklch(92.8% .006 264.531));padding:calc(var(--spacing, .25rem) * 4)}@media (prefers-color-scheme: dark){.kanban-column-header{border-color:var(--color-gray-700, oklch(37.3% .034 259.733))}}.kanban-column-title{margin:0;font-size:var(--text-lg, 1.125rem);line-height:var(--tw-leading, var(--text-lg--line-height, calc(1.75 / 1.125)));--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);color:var(--color-gray-900, oklch(21% .034 264.665))}@media (prefers-color-scheme: dark){.kanban-column-title{color:var(--color-gray-100, oklch(96.7% .003 264.542))}}.kanban-column-count{border-radius:calc(infinity * 1px);background-color:var(--color-gray-200, oklch(92.8% .006 264.531));padding-inline:calc(var(--spacing, .25rem) * 2);padding-block:var(--spacing, .25rem);font-size:.875rem;line-height:var(--tw-leading, var(--text-sm--line-height, calc(1.25 / .875)));--tw-font-weight: var(--font-weight-medium, 500);font-weight:var(--font-weight-medium, 500);color:var(--color-gray-700, oklch(37.3% .034 259.733))}@media (prefers-color-scheme: dark){.kanban-column-count{background-color:var(--color-gray-700, oklch(37.3% .034 259.733))}}@media (prefers-color-scheme: dark){.kanban-column-count{color:var(--color-gray-300, oklch(87.2% .01 258.338))}}.kanban-column-content{flex:1;overflow-y:auto;padding:calc(var(--spacing, .25rem) * 4);min-height:200px}:where(.kanban-column-content>:not(:last-child)){--tw-space-y-reverse: 0;margin-block-start:calc(calc(var(--spacing, .25rem) * 3) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing, .25rem) * 3) * calc(1 - var(--tw-space-y-reverse)))}.kanban-item{border-radius:.5rem;border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200, oklch(92.8% .006 264.531));background-color:var(--color-white, #fff);--tw-shadow: 0 1px 2px 0 var(--tw-shadow-color, rgb(var(--shadow-color) / .05));box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);transition-property:box-shadow;transition-timing-function:var(--tw-ease, var(--default-transition-timing-function, cubic-bezier(.4, 0, .2, 1)));transition-duration:var(--tw-duration, var(--default-transition-duration, .15s));--tw-duration: .2s;transition-duration:.2s;cursor:move}@media (prefers-color-scheme: dark){.kanban-item{border-color:var(--color-gray-600, oklch(44.6% .03 256.802))}}@media (prefers-color-scheme: dark){.kanban-item{background-color:var(--color-gray-700, oklch(37.3% .034 259.733))}}.kanban-item:hover{--tw-shadow: 0 4px 6px -1px var(--tw-shadow-color, rgb(var(--shadow-color) / .1)), 0 2px 4px -2px var(--tw-shadow-color, rgb(var(--shadow-color) / .1));box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.kanban-item-default{padding:calc(var(--spacing, .25rem) * 4);color:var(--color-gray-900, oklch(21% .034 264.665))}@media (prefers-color-scheme: dark){.kanban-item-default{color:var(--color-gray-100, oklch(96.7% .003 264.542))}}.kanban-item-placeholder{border-radius:.5rem;border-style:var(--tw-border-style);border-width:2px;--tw-border-style: dashed;border-style:dashed;border-color:var(--color-gray-400, oklch(70.7% .022 261.325));background-color:var(--color-gray-200, oklch(92.8% .006 264.531));opacity:50%;min-height:60px}@media (prefers-color-scheme: dark){.kanban-item-placeholder{border-color:var(--color-gray-500, oklch(55.1% .027 264.364))}}@media (prefers-color-scheme: dark){.kanban-item-placeholder{background-color:var(--color-gray-600, oklch(44.6% .03 256.802))}}.kanban-column-empty{display:flex;height:calc(var(--spacing, .25rem) * 32);align-items:center;justify-content:center;font-size:.875rem;line-height:var(--tw-leading, var(--text-sm--line-height, calc(1.25 / .875)));color:var(--color-gray-400, oklch(70.7% .022 261.325));border-radius:.5rem;border-style:var(--tw-border-style);border-width:2px;--tw-border-style: dashed;border-style:dashed;border-color:var(--color-gray-300, oklch(87.2% .01 258.338))}@media (prefers-color-scheme: dark){.kanban-column-empty{color:var(--color-gray-500, oklch(55.1% .027 264.364))}}@media (prefers-color-scheme: dark){.kanban-column-empty{border-color:var(--color-gray-600, oklch(44.6% .03 256.802))}}.cdk-drag-preview{border-radius:.5rem;opacity:80%;--tw-shadow: 0 10px 15px -3px var(--tw-shadow-color, rgb(var(--shadow-color) / .1)), 0 4px 6px -4px var(--tw-shadow-color, rgb(var(--shadow-color) / .1));box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.cdk-drag-animating{transition:transform .25s cubic-bezier(0,0,.2,1)}.kanban-column-content.cdk-drop-list-dragging .kanban-item:not(.cdk-drag-placeholder){transition:transform .25s cubic-bezier(0,0,.2,1)}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@property --tw-font-weight{syntax: \"*\"; inherits: false;}@property --tw-space-y-reverse{syntax: \"*\"; inherits: false; initial-value: 0;}@property --tw-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-shadow-color{syntax: \"*\"; inherits: false;}@property --tw-shadow-alpha{syntax: \"<percentage>\"; inherits: false; initial-value: 100%;}@property --tw-inset-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-inset-shadow-color{syntax: \"*\"; inherits: false;}@property --tw-inset-shadow-alpha{syntax: \"<percentage>\"; inherits: false; initial-value: 100%;}@property --tw-ring-color{syntax: \"*\"; inherits: false;}@property --tw-ring-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-inset-ring-color{syntax: \"*\"; inherits: false;}@property --tw-inset-ring-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-ring-inset{syntax: \"*\"; inherits: false;}@property --tw-ring-offset-width{syntax: \"<length>\"; inherits: false; initial-value: 0px;}@property --tw-ring-offset-color{syntax: \"*\"; inherits: false; initial-value: #fff;}@property --tw-ring-offset-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-duration{syntax: \"*\"; inherits: false;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style: solid;--tw-font-weight: initial;--tw-space-y-reverse: 0;--tw-shadow: 0 0 #0000;--tw-shadow-color: initial;--tw-shadow-alpha: 100%;--tw-inset-shadow: 0 0 #0000;--tw-inset-shadow-color: initial;--tw-inset-shadow-alpha: 100%;--tw-ring-color: initial;--tw-ring-shadow: 0 0 #0000;--tw-inset-ring-color: initial;--tw-inset-ring-shadow: 0 0 #0000;--tw-ring-inset: initial;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-offset-shadow: 0 0 #0000;--tw-duration: initial}}}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"] }]
|
|
3396
4058
|
}], propDecorators: { columns: [{
|
|
3397
4059
|
type: Input
|
|
3398
4060
|
}], itemTemplate: [{
|
|
@@ -3504,7 +4166,7 @@ class DrawerComponent {
|
|
|
3504
4166
|
return this.isExpanded ? 'expanded' : 'collapsed';
|
|
3505
4167
|
}
|
|
3506
4168
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: DrawerComponent, deps: [{ token: i1$4.Router }], target: i0.ɵɵFactoryTarget.Component });
|
|
3507
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: DrawerComponent, isStandalone: false, selector: "fx-ui-drawer", inputs: { items: "items", isExpanded: "isExpanded", logo: "logo", version: "version" }, outputs: { toggleExpanded: "toggleExpanded", itemClicked: "itemClicked" }, ngImport: i0, template: "<div class=\"drawer-host-wrapper\">\n <a\n href=\"/\"\n class=\"h-[60px]\"\n [ngClass]=\"{ 'flex items-center justify-center': !isExpanded, 'p-4': isExpanded }\"\n >\n @if (logo) {\n <img\n [src]=\"logo\"\n class=\"h-[20px] w-auto transition-all duration-300 ease-in-out\"\n [ngClass]=\"{ 'scale-90': !isExpanded, 'ml-5': isExpanded }\"\n />\n } @else {\n <div class=\"h-[30px] w-auto transition-all duration-300 ease-in-out\"></div>\n }\n </a>\n <div class=\"drawer-container\" [@drawerExpand]=\"getDrawerState()\">\n <!-- Navigation Items -->\n <nav class=\"drawer-nav\">\n <ng-container *ngFor=\"let item of items\">\n <ng-container\n *ngTemplateOutlet=\"drawerItem; context: { $implicit: item, level: 0 }\"\n ></ng-container>\n </ng-container>\n </nav>\n\n <!-- Version -->\n <div *ngIf=\"version\" class=\"drawer-version\" [matTooltip]=\"!isExpanded ? version : ''\" matTooltipPosition=\"right\">\n <span *ngIf=\"isExpanded\">{{ version }}</span>\n <span *ngIf=\"!isExpanded\" class=\"drawer-version-dot\"></span>\n </div>\n </div>\n\n <!-- Floating Toggle Button on right border -->\n <button\n type=\"button\"\n class=\"toggle-btn\"\n (click)=\"toggleDrawer()\"\n [matTooltip]=\"isExpanded ? 'Collapse' : 'Expand'\"\n matTooltipClass=\"text-text-primary text-xs font-semibold\"\n matTooltipPosition=\"right\"\n >\n <fx-ui-hero-icon\n [icon]=\"isExpanded ? 'chevron-left' : 'chevron-right'\"\n [size]=\"16\"\n class=\"text-text-primary\"\n ></fx-ui-hero-icon>\n </button>\n</div>\n\n<!-- Recursive Template for Drawer Items -->\n<ng-template #drawerItem let-item let-level=\"level\">\n <div class=\"drawer-item-wrapper\">\n <!-- Item Button/Link -->\n <a\n *ngIf=\"item.route && !item.children\"\n [routerLink]=\"item.route\"\n class=\"drawer-item\"\n [class.active]=\"isActiveRoute(item.route)\"\n [class.has-children]=\"item.children && item.children.length > 0\"\n [style.paddingLeft.px]=\"isExpanded ? 16 + level * 16 : 16\"\n [matTooltip]=\"!isExpanded ? item.label : ''\"\n matTooltipPosition=\"right\"\n matTooltipClass=\"text-text-primary text-xs font-semibold\"\n (click)=\"onItemClick(item, $event)\"\n >\n <div class=\"item-content\">\n <fx-ui-hero-icon [icon]=\"item.icon\" [size]=\"20\" class=\"item-icon\"></fx-ui-hero-icon>\n <span *ngIf=\"isExpanded\" class=\"item-label\">{{ item.label }}</span>\n </div>\n </a>\n\n <button\n *ngIf=\"!item.route || item.children\"\n type=\"button\"\n class=\"drawer-item\"\n [class.active]=\"isActiveRoute(item.route)\"\n [class.has-children]=\"item.children && item.children.length > 0\"\n [style.paddingLeft.px]=\"isExpanded ? 16 + level * 16 : 16\"\n [matTooltip]=\"!isExpanded ? item.label : ''\"\n matTooltipPosition=\"right\"\n matTooltipClass=\"text-text-primary text-sm font-semibold\"\n (click)=\"onItemClick(item, $event)\"\n >\n <div class=\"item-content\">\n <fx-ui-hero-icon [icon]=\"item.icon\" [size]=\"20\" class=\"item-icon\"></fx-ui-hero-icon>\n <span *ngIf=\"isExpanded\" class=\"item-label\">{{ item.label }}</span>\n <fx-ui-hero-icon\n *ngIf=\"isExpanded && item.children && item.children.length > 0\"\n [icon]=\"'chevron-down'\"\n [size]=\"16\"\n class=\"chevron-icon\"\n [class.rotated]=\"isAccordionExpanded(item.id)\"\n ></fx-ui-hero-icon>\n </div>\n </button>\n\n <!-- Accordion Children -->\n <div\n *ngIf=\"item.children && item.children.length > 0\"\n class=\"accordion-children\"\n [@accordionExpand]=\"getAccordionState(item.id)\"\n >\n <ng-container *ngFor=\"let child of item.children\">\n <ng-container\n *ngTemplateOutlet=\"drawerItem; context: { $implicit: child, level: level + 1 }\"\n ></ng-container>\n </ng-container>\n </div>\n </div>\n</ng-template>\n", styles: ["@charset \"UTF-8\";:host{display:block;height:100%}.drawer-host-wrapper{position:relative;height:100%;border-right-width:1px;--tw-border-opacity: 1;border-color:rgb(var(--border-default) / var(--tw-border-opacity, 1))}.drawer-container{display:flex;height:100%;flex-direction:column;overflow:auto;--tw-bg-opacity: 1;background-color:rgb(var(--bg-primary) / var(--tw-bg-opacity, 1));padding-left:.5rem;padding-right:.5rem;transition:width .3s cubic-bezier(.4,0,.2,1)}.toggle-btn{position:absolute;top:50%;z-index:50;display:flex;height:1.5rem;width:1.5rem;--tw-translate-y: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));cursor:pointer;align-items:center;justify-content:center;border-radius:9999px;border-width:1px;--tw-border-opacity: 1;border-color:rgb(var(--border-default) / var(--tw-border-opacity, 1));--tw-bg-opacity: 1;background-color:rgb(var(--bg-primary) / var(--tw-bg-opacity, 1));--tw-shadow: 0 1px 2px 0 rgb(var(--shadow-color) / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow);right:-12px;transition:all .2s ease}.toggle-btn:hover{--tw-bg-opacity: 1;background-color:rgb(var(--bg-primary-hover) / var(--tw-bg-opacity, 1));--tw-shadow: 0 4px 6px -1px rgb(var(--shadow-color) / .1), 0 2px 4px -2px rgb(var(--shadow-color) / .1);--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.toggle-btn:active{transform:translateY(-50%) scale(.9)}.toggle-btn:focus-visible{outline-width:2px;outline-offset:2px;outline-color:rgb(var(--primary) / 1)}.drawer-nav{flex:1 1 0%;overflow-y:auto;overflow-x:hidden;padding-top:.5rem;padding-bottom:.5rem}.drawer-nav::-webkit-scrollbar{width:.375rem}.drawer-nav::-webkit-scrollbar-track{background-color:transparent}.drawer-nav::-webkit-scrollbar-thumb{border-radius:.125rem;--tw-bg-opacity: 1;background-color:rgb(var(--border-default) / var(--tw-bg-opacity, 1))}.drawer-nav::-webkit-scrollbar-thumb:hover{--tw-bg-opacity: 1;background-color:rgb(var(--text-placeholder) / var(--tw-bg-opacity, 1))}.drawer-item-wrapper{position:relative}.drawer-item{position:relative;margin-top:.25rem;display:flex;min-height:2rem;width:100%;cursor:pointer;align-items:center;border-radius:.25rem;border-style:none;background-color:transparent;padding:.5rem 1rem;--tw-text-opacity: 1;color:rgb(var(--text-primary) / var(--tw-text-opacity, 1));text-decoration-line:none;transition:all .2s ease}.drawer-item:hover{--tw-scale-x: .95;--tw-scale-y: .95;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));--tw-bg-opacity: 1;background-color:rgb(var(--bg-primary-hover) / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(var(--text-primary) / var(--tw-text-opacity, 1))}.drawer-item.active{background-image:linear-gradient(135deg,rgb(var(--gradient-primary-start)),rgb(var(--gradient-primary-end)));font-weight:500;--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.drawer-item.active .item-icon{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.drawer-item:active{transform:scale(.98)}.drawer-item:focus-visible{outline-width:2px;outline-offset:2px;outline-color:rgb(var(--primary) / 1)}.drawer-item{-webkit-tap-highlight-color:transparent;outline:2px solid transparent;outline-offset:2px}.item-content{display:flex;width:100%;align-items:center;gap:.75rem;overflow:hidden}.item-icon{flex-shrink:0;color:inherit}.item-label{flex:1 1 0%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-align:left;font-size:13px;line-height:1.25rem}.chevron-icon{margin-left:auto;flex-shrink:0;--tw-text-opacity: 1;color:rgb(var(--text-primary) / var(--tw-text-opacity, 1));transition:transform .3s cubic-bezier(.4,0,.2,1)}.chevron-icon.rotated{transform:rotate(180deg)}.accordion-children{overflow:hidden}.drawer-version{display:flex;-webkit-user-select:none;user-select:none;align-items:center;justify-content:center;border-top-width:1px;--tw-border-opacity: 1;border-color:rgb(var(--border-default) / var(--tw-border-opacity, 1));padding:.75rem 1rem;font-size:11px;--tw-text-opacity: 1;color:rgb(var(--text-placeholder) / var(--tw-text-opacity, 1));font-family:Roboto,monospace}.drawer-version-dot{display:block;height:.375rem;width:.375rem;border-radius:9999px;--tw-bg-opacity: 1;background-color:rgb(var(--text-placeholder) / var(--tw-bg-opacity, 1))}.drawer-container[style*=\"width: 64px\"] .drawer-item{justify-content:center;padding-left:16px!important}.drawer-container[style*=\"width: 64px\"] .item-content{justify-content:center}\n"], dependencies: [{ kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i1$4.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: i3$1.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: HeroIconComponent, selector: "fx-ui-hero-icon", inputs: ["stroke", "icon", "solid", "outline", "size", "color", "class"] }], animations: [
|
|
4169
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: DrawerComponent, isStandalone: false, selector: "fx-ui-drawer", inputs: { items: "items", isExpanded: "isExpanded", logo: "logo", version: "version" }, outputs: { toggleExpanded: "toggleExpanded", itemClicked: "itemClicked" }, ngImport: i0, template: "<div class=\"drawer-host-wrapper\">\n <a\n href=\"/\"\n class=\"h-[60px]\"\n [ngClass]=\"{ 'flex items-center justify-center': !isExpanded, 'p-4': isExpanded }\"\n >\n @if (logo) {\n <img\n [src]=\"logo\"\n alt=\"Home\"\n class=\"h-[20px] w-auto transition-all duration-300 ease-in-out\"\n [ngClass]=\"{ 'scale-90': !isExpanded, 'ml-5': isExpanded }\"\n />\n } @else {\n <div class=\"h-[30px] w-auto transition-all duration-300 ease-in-out\"></div>\n }\n </a>\n <div class=\"drawer-container\" [@drawerExpand]=\"getDrawerState()\">\n <!-- Navigation Items -->\n <nav class=\"drawer-nav\">\n <ng-container *ngFor=\"let item of items\">\n <ng-container\n *ngTemplateOutlet=\"drawerItem; context: { $implicit: item, level: 0 }\"\n ></ng-container>\n </ng-container>\n </nav>\n\n <!-- Version -->\n <div *ngIf=\"version\" class=\"drawer-version\" [matTooltip]=\"!isExpanded ? version : ''\" matTooltipPosition=\"right\">\n <span *ngIf=\"isExpanded\">{{ version }}</span>\n <span *ngIf=\"!isExpanded\" class=\"drawer-version-dot\"></span>\n </div>\n </div>\n\n <!-- Floating Toggle Button on right border -->\n <button\n type=\"button\"\n class=\"toggle-btn\"\n [attr.aria-label]=\"isExpanded ? 'Collapse navigation' : 'Expand navigation'\"\n [attr.aria-expanded]=\"isExpanded\"\n (click)=\"toggleDrawer()\"\n [matTooltip]=\"isExpanded ? 'Collapse' : 'Expand'\"\n matTooltipClass=\"text-text-primary text-xs font-semibold\"\n matTooltipPosition=\"right\"\n >\n <fx-ui-hero-icon\n [icon]=\"isExpanded ? 'chevron-left' : 'chevron-right'\"\n [size]=\"16\"\n class=\"text-text-primary\"\n ></fx-ui-hero-icon>\n </button>\n</div>\n\n<!-- Recursive Template for Drawer Items -->\n<ng-template #drawerItem let-item let-level=\"level\">\n <div class=\"drawer-item-wrapper\">\n <!-- Item Button/Link -->\n <a\n *ngIf=\"item.route && !item.children\"\n [routerLink]=\"item.route\"\n class=\"drawer-item\"\n [class.active]=\"isActiveRoute(item.route)\"\n [class.has-children]=\"item.children && item.children.length > 0\"\n [style.paddingLeft.px]=\"isExpanded ? 16 + level * 16 : 16\"\n [matTooltip]=\"!isExpanded ? item.label : ''\"\n matTooltipPosition=\"right\"\n matTooltipClass=\"text-text-primary text-xs font-semibold\"\n (click)=\"onItemClick(item, $event)\"\n >\n <div class=\"item-content\">\n <fx-ui-hero-icon [icon]=\"item.icon\" [size]=\"20\" class=\"item-icon\"></fx-ui-hero-icon>\n <span *ngIf=\"isExpanded\" class=\"item-label\">{{ item.label }}</span>\n </div>\n </a>\n\n <button\n *ngIf=\"!item.route || item.children\"\n type=\"button\"\n class=\"drawer-item\"\n [class.active]=\"isActiveRoute(item.route)\"\n [class.has-children]=\"item.children && item.children.length > 0\"\n [style.paddingLeft.px]=\"isExpanded ? 16 + level * 16 : 16\"\n [matTooltip]=\"!isExpanded ? item.label : ''\"\n matTooltipPosition=\"right\"\n matTooltipClass=\"text-text-primary text-sm font-semibold\"\n (click)=\"onItemClick(item, $event)\"\n >\n <div class=\"item-content\">\n <fx-ui-hero-icon [icon]=\"item.icon\" [size]=\"20\" class=\"item-icon\"></fx-ui-hero-icon>\n <span *ngIf=\"isExpanded\" class=\"item-label\">{{ item.label }}</span>\n <fx-ui-hero-icon\n *ngIf=\"isExpanded && item.children && item.children.length > 0\"\n [icon]=\"'chevron-down'\"\n [size]=\"16\"\n class=\"chevron-icon\"\n [class.rotated]=\"isAccordionExpanded(item.id)\"\n ></fx-ui-hero-icon>\n </div>\n </button>\n\n <!-- Accordion Children -->\n <div\n *ngIf=\"item.children && item.children.length > 0\"\n class=\"accordion-children\"\n [@accordionExpand]=\"getAccordionState(item.id)\"\n >\n <ng-container *ngFor=\"let child of item.children\">\n <ng-container\n *ngTemplateOutlet=\"drawerItem; context: { $implicit: child, level: level + 1 }\"\n ></ng-container>\n </ng-container>\n </div>\n </div>\n</ng-template>\n", styles: ["@charset \"UTF-8\";@layer properties;:host{display:block;height:100%}.drawer-host-wrapper{position:relative;height:100%;border-right-style:var(--tw-border-style);border-right-width:1px;border-color:rgb(var(--border-default))}.drawer-container{display:flex;height:100%;flex-direction:column;overflow:auto;background-color:rgb(var(--bg-primary));padding-inline:calc(var(--spacing, .25rem) * 2);transition:width .3s cubic-bezier(.4,0,.2,1)}.toggle-btn{position:absolute;top:50%;z-index:50;display:flex;height:calc(var(--spacing, .25rem) * 6);width:calc(var(--spacing, .25rem) * 6);--tw-translate-y: -50% ;translate:var(--tw-translate-x) var(--tw-translate-y);cursor:pointer;align-items:center;justify-content:center;border-radius:calc(infinity * 1px);border-style:var(--tw-border-style);border-width:1px;border-color:rgb(var(--border-default));background-color:rgb(var(--bg-primary));--tw-shadow: 0 1px 2px 0 var(--tw-shadow-color, rgb(var(--shadow-color) / .05));box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);right:-12px;transition:all .2s ease}.toggle-btn:hover{background-color:rgb(var(--bg-primary-hover));--tw-shadow: 0 4px 6px -1px var(--tw-shadow-color, rgb(var(--shadow-color) / .1)), 0 2px 4px -2px var(--tw-shadow-color, rgb(var(--shadow-color) / .1));box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.toggle-btn:active{transform:translateY(-50%) scale(.9)}.toggle-btn:focus-visible{outline-style:var(--tw-outline-style);outline-width:2px;outline-offset:2px;outline-color:rgb(var(--primary))}.drawer-nav{flex:1;overflow-x:hidden;overflow-y:auto;padding-block:calc(var(--spacing, .25rem) * 2)}.drawer-nav::-webkit-scrollbar{width:calc(var(--spacing, .25rem) * 1.5)}.drawer-nav::-webkit-scrollbar-track{background-color:transparent}.drawer-nav::-webkit-scrollbar-thumb{border-radius:.125rem;background-color:rgb(var(--border-default))}.drawer-nav::-webkit-scrollbar-thumb:hover{background-color:rgb(var(--text-placeholder))}.drawer-item-wrapper{position:relative}.drawer-item{position:relative;margin-top:var(--spacing, .25rem);display:flex;min-height:calc(var(--spacing, .25rem) * 8);width:100%;cursor:pointer;align-items:center;border-radius:.25rem;--tw-border-style: none;border-style:none;background-color:transparent;padding-inline:1rem;padding-block:calc(var(--spacing, .25rem) * 2);color:rgb(var(--text-primary));text-decoration-line:none;transition:all .2s ease;-webkit-tap-highlight-color:transparent;--tw-outline-style: none;outline-style:none}.drawer-item:hover{--tw-scale-x: 95%;--tw-scale-y: 95%;--tw-scale-z: 95%;scale:var(--tw-scale-x) var(--tw-scale-y);background-color:rgb(var(--bg-primary-hover));color:rgb(var(--text-primary))}.drawer-item.active{background-image:linear-gradient(135deg,rgb(var(--gradient-primary-start)),rgb(var(--gradient-primary-end)));--tw-font-weight: var(--font-weight-medium, 500);font-weight:var(--font-weight-medium, 500);color:var(--color-white, #fff)}.drawer-item.active .item-icon{color:var(--color-white, #fff)}.drawer-item:active{transform:scale(.98)}.drawer-item:focus-visible{outline-style:var(--tw-outline-style);outline-width:2px;outline-offset:2px;outline-color:rgb(var(--primary))}.item-content{display:flex;width:100%;align-items:center;gap:calc(var(--spacing, .25rem) * 3);overflow:hidden}.item-icon{flex-shrink:0;color:inherit}.item-label{flex:1;overflow:hidden;text-align:left;font-size:13px;--tw-leading: calc(var(--spacing, .25rem) * 5);line-height:calc(var(--spacing, .25rem) * 5);text-overflow:ellipsis;white-space:nowrap}.chevron-icon{margin-left:auto;flex-shrink:0;color:rgb(var(--text-primary));transition:transform .3s cubic-bezier(.4,0,.2,1)}.chevron-icon.rotated{transform:rotate(180deg)}.accordion-children{overflow:hidden}.drawer-version{display:flex;align-items:center;justify-content:center;border-top-style:var(--tw-border-style);border-top-width:1px;border-color:rgb(var(--border-default));padding-inline:calc(var(--spacing, .25rem) * 4);padding-block:calc(var(--spacing, .25rem) * 3);font-size:11px;color:rgb(var(--text-placeholder));-webkit-user-select:none;user-select:none;font-family:Roboto,monospace}.drawer-version-dot{display:block;height:calc(var(--spacing, .25rem) * 1.5);width:calc(var(--spacing, .25rem) * 1.5);border-radius:calc(infinity * 1px);background-color:rgb(var(--text-placeholder))}.drawer-container[style*=\"width: 64px\"] .drawer-item{justify-content:center;padding-left:16px!important}.drawer-container[style*=\"width: 64px\"] .item-content{justify-content:center}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@property --tw-translate-x{syntax: \"*\"; inherits: false; initial-value: 0;}@property --tw-translate-y{syntax: \"*\"; inherits: false; initial-value: 0;}@property --tw-translate-z{syntax: \"*\"; inherits: false; initial-value: 0;}@property --tw-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-shadow-color{syntax: \"*\"; inherits: false;}@property --tw-shadow-alpha{syntax: \"<percentage>\"; inherits: false; initial-value: 100%;}@property --tw-inset-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-inset-shadow-color{syntax: \"*\"; inherits: false;}@property --tw-inset-shadow-alpha{syntax: \"<percentage>\"; inherits: false; initial-value: 100%;}@property --tw-ring-color{syntax: \"*\"; inherits: false;}@property --tw-ring-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-inset-ring-color{syntax: \"*\"; inherits: false;}@property --tw-inset-ring-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-ring-inset{syntax: \"*\"; inherits: false;}@property --tw-ring-offset-width{syntax: \"<length>\"; inherits: false; initial-value: 0px;}@property --tw-ring-offset-color{syntax: \"*\"; inherits: false; initial-value: #fff;}@property --tw-ring-offset-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-outline-style{syntax: \"*\"; inherits: false; initial-value: solid;}@property --tw-scale-x{syntax: \"*\"; inherits: false; initial-value: 1;}@property --tw-scale-y{syntax: \"*\"; inherits: false; initial-value: 1;}@property --tw-scale-z{syntax: \"*\"; inherits: false; initial-value: 1;}@property --tw-font-weight{syntax: \"*\"; inherits: false;}@property --tw-leading{syntax: \"*\"; inherits: false;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style: solid;--tw-translate-x: 0;--tw-translate-y: 0;--tw-translate-z: 0;--tw-shadow: 0 0 #0000;--tw-shadow-color: initial;--tw-shadow-alpha: 100%;--tw-inset-shadow: 0 0 #0000;--tw-inset-shadow-color: initial;--tw-inset-shadow-alpha: 100%;--tw-ring-color: initial;--tw-ring-shadow: 0 0 #0000;--tw-inset-ring-color: initial;--tw-inset-ring-shadow: 0 0 #0000;--tw-ring-inset: initial;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-offset-shadow: 0 0 #0000;--tw-outline-style: solid;--tw-scale-x: 1;--tw-scale-y: 1;--tw-scale-z: 1;--tw-font-weight: initial;--tw-leading: initial}}}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i1$4.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: i57.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: HeroIconComponent, selector: "fx-ui-hero-icon", inputs: ["stroke", "icon", "solid", "outline", "size", "color", "class"] }], animations: [
|
|
3508
4170
|
trigger('drawerExpand', [
|
|
3509
4171
|
state('collapsed', style({ width: '70px' })),
|
|
3510
4172
|
state('expanded', style({ width: '260px' })),
|
|
@@ -3530,7 +4192,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImpor
|
|
|
3530
4192
|
state('expanded', style({ height: '*', overflow: 'visible', opacity: 1 })),
|
|
3531
4193
|
transition('collapsed <=> expanded', animate('300ms cubic-bezier(0.4, 0, 0.2, 1)')),
|
|
3532
4194
|
]),
|
|
3533
|
-
], template: "<div class=\"drawer-host-wrapper\">\n <a\n href=\"/\"\n class=\"h-[60px]\"\n [ngClass]=\"{ 'flex items-center justify-center': !isExpanded, 'p-4': isExpanded }\"\n >\n @if (logo) {\n <img\n [src]=\"logo\"\n class=\"h-[20px] w-auto transition-all duration-300 ease-in-out\"\n [ngClass]=\"{ 'scale-90': !isExpanded, 'ml-5': isExpanded }\"\n />\n } @else {\n <div class=\"h-[30px] w-auto transition-all duration-300 ease-in-out\"></div>\n }\n </a>\n <div class=\"drawer-container\" [@drawerExpand]=\"getDrawerState()\">\n <!-- Navigation Items -->\n <nav class=\"drawer-nav\">\n <ng-container *ngFor=\"let item of items\">\n <ng-container\n *ngTemplateOutlet=\"drawerItem; context: { $implicit: item, level: 0 }\"\n ></ng-container>\n </ng-container>\n </nav>\n\n <!-- Version -->\n <div *ngIf=\"version\" class=\"drawer-version\" [matTooltip]=\"!isExpanded ? version : ''\" matTooltipPosition=\"right\">\n <span *ngIf=\"isExpanded\">{{ version }}</span>\n <span *ngIf=\"!isExpanded\" class=\"drawer-version-dot\"></span>\n </div>\n </div>\n\n <!-- Floating Toggle Button on right border -->\n <button\n type=\"button\"\n class=\"toggle-btn\"\n (click)=\"toggleDrawer()\"\n [matTooltip]=\"isExpanded ? 'Collapse' : 'Expand'\"\n matTooltipClass=\"text-text-primary text-xs font-semibold\"\n matTooltipPosition=\"right\"\n >\n <fx-ui-hero-icon\n [icon]=\"isExpanded ? 'chevron-left' : 'chevron-right'\"\n [size]=\"16\"\n class=\"text-text-primary\"\n ></fx-ui-hero-icon>\n </button>\n</div>\n\n<!-- Recursive Template for Drawer Items -->\n<ng-template #drawerItem let-item let-level=\"level\">\n <div class=\"drawer-item-wrapper\">\n <!-- Item Button/Link -->\n <a\n *ngIf=\"item.route && !item.children\"\n [routerLink]=\"item.route\"\n class=\"drawer-item\"\n [class.active]=\"isActiveRoute(item.route)\"\n [class.has-children]=\"item.children && item.children.length > 0\"\n [style.paddingLeft.px]=\"isExpanded ? 16 + level * 16 : 16\"\n [matTooltip]=\"!isExpanded ? item.label : ''\"\n matTooltipPosition=\"right\"\n matTooltipClass=\"text-text-primary text-xs font-semibold\"\n (click)=\"onItemClick(item, $event)\"\n >\n <div class=\"item-content\">\n <fx-ui-hero-icon [icon]=\"item.icon\" [size]=\"20\" class=\"item-icon\"></fx-ui-hero-icon>\n <span *ngIf=\"isExpanded\" class=\"item-label\">{{ item.label }}</span>\n </div>\n </a>\n\n <button\n *ngIf=\"!item.route || item.children\"\n type=\"button\"\n class=\"drawer-item\"\n [class.active]=\"isActiveRoute(item.route)\"\n [class.has-children]=\"item.children && item.children.length > 0\"\n [style.paddingLeft.px]=\"isExpanded ? 16 + level * 16 : 16\"\n [matTooltip]=\"!isExpanded ? item.label : ''\"\n matTooltipPosition=\"right\"\n matTooltipClass=\"text-text-primary text-sm font-semibold\"\n (click)=\"onItemClick(item, $event)\"\n >\n <div class=\"item-content\">\n <fx-ui-hero-icon [icon]=\"item.icon\" [size]=\"20\" class=\"item-icon\"></fx-ui-hero-icon>\n <span *ngIf=\"isExpanded\" class=\"item-label\">{{ item.label }}</span>\n <fx-ui-hero-icon\n *ngIf=\"isExpanded && item.children && item.children.length > 0\"\n [icon]=\"'chevron-down'\"\n [size]=\"16\"\n class=\"chevron-icon\"\n [class.rotated]=\"isAccordionExpanded(item.id)\"\n ></fx-ui-hero-icon>\n </div>\n </button>\n\n <!-- Accordion Children -->\n <div\n *ngIf=\"item.children && item.children.length > 0\"\n class=\"accordion-children\"\n [@accordionExpand]=\"getAccordionState(item.id)\"\n >\n <ng-container *ngFor=\"let child of item.children\">\n <ng-container\n *ngTemplateOutlet=\"drawerItem; context: { $implicit: child, level: level + 1 }\"\n ></ng-container>\n </ng-container>\n </div>\n </div>\n</ng-template>\n", styles: ["@charset \"UTF-8\";:host{display:block;height:100%}.drawer-host-wrapper{position:relative;height:100%;border-right-width:1px;--tw-border-opacity: 1;border-color:rgb(var(--border-default) / var(--tw-border-opacity, 1))}.drawer-container{display:flex;height:100%;flex-direction:column;overflow:auto;--tw-bg-opacity: 1;background-color:rgb(var(--bg-primary) / var(--tw-bg-opacity, 1));padding-left:.5rem;padding-right:.5rem;transition:width .3s cubic-bezier(.4,0,.2,1)}.toggle-btn{position:absolute;top:50%;z-index:50;display:flex;height:1.5rem;width:1.5rem;--tw-translate-y: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));cursor:pointer;align-items:center;justify-content:center;border-radius:9999px;border-width:1px;--tw-border-opacity: 1;border-color:rgb(var(--border-default) / var(--tw-border-opacity, 1));--tw-bg-opacity: 1;background-color:rgb(var(--bg-primary) / var(--tw-bg-opacity, 1));--tw-shadow: 0 1px 2px 0 rgb(var(--shadow-color) / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow);right:-12px;transition:all .2s ease}.toggle-btn:hover{--tw-bg-opacity: 1;background-color:rgb(var(--bg-primary-hover) / var(--tw-bg-opacity, 1));--tw-shadow: 0 4px 6px -1px rgb(var(--shadow-color) / .1), 0 2px 4px -2px rgb(var(--shadow-color) / .1);--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.toggle-btn:active{transform:translateY(-50%) scale(.9)}.toggle-btn:focus-visible{outline-width:2px;outline-offset:2px;outline-color:rgb(var(--primary) / 1)}.drawer-nav{flex:1 1 0%;overflow-y:auto;overflow-x:hidden;padding-top:.5rem;padding-bottom:.5rem}.drawer-nav::-webkit-scrollbar{width:.375rem}.drawer-nav::-webkit-scrollbar-track{background-color:transparent}.drawer-nav::-webkit-scrollbar-thumb{border-radius:.125rem;--tw-bg-opacity: 1;background-color:rgb(var(--border-default) / var(--tw-bg-opacity, 1))}.drawer-nav::-webkit-scrollbar-thumb:hover{--tw-bg-opacity: 1;background-color:rgb(var(--text-placeholder) / var(--tw-bg-opacity, 1))}.drawer-item-wrapper{position:relative}.drawer-item{position:relative;margin-top:.25rem;display:flex;min-height:2rem;width:100%;cursor:pointer;align-items:center;border-radius:.25rem;border-style:none;background-color:transparent;padding:.5rem 1rem;--tw-text-opacity: 1;color:rgb(var(--text-primary) / var(--tw-text-opacity, 1));text-decoration-line:none;transition:all .2s ease}.drawer-item:hover{--tw-scale-x: .95;--tw-scale-y: .95;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));--tw-bg-opacity: 1;background-color:rgb(var(--bg-primary-hover) / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(var(--text-primary) / var(--tw-text-opacity, 1))}.drawer-item.active{background-image:linear-gradient(135deg,rgb(var(--gradient-primary-start)),rgb(var(--gradient-primary-end)));font-weight:500;--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.drawer-item.active .item-icon{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.drawer-item:active{transform:scale(.98)}.drawer-item:focus-visible{outline-width:2px;outline-offset:2px;outline-color:rgb(var(--primary) / 1)}.drawer-item{-webkit-tap-highlight-color:transparent;outline:2px solid transparent;outline-offset:2px}.item-content{display:flex;width:100%;align-items:center;gap:.75rem;overflow:hidden}.item-icon{flex-shrink:0;color:inherit}.item-label{flex:1 1 0%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-align:left;font-size:13px;line-height:1.25rem}.chevron-icon{margin-left:auto;flex-shrink:0;--tw-text-opacity: 1;color:rgb(var(--text-primary) / var(--tw-text-opacity, 1));transition:transform .3s cubic-bezier(.4,0,.2,1)}.chevron-icon.rotated{transform:rotate(180deg)}.accordion-children{overflow:hidden}.drawer-version{display:flex;-webkit-user-select:none;user-select:none;align-items:center;justify-content:center;border-top-width:1px;--tw-border-opacity: 1;border-color:rgb(var(--border-default) / var(--tw-border-opacity, 1));padding:.75rem 1rem;font-size:11px;--tw-text-opacity: 1;color:rgb(var(--text-placeholder) / var(--tw-text-opacity, 1));font-family:Roboto,monospace}.drawer-version-dot{display:block;height:.375rem;width:.375rem;border-radius:9999px;--tw-bg-opacity: 1;background-color:rgb(var(--text-placeholder) / var(--tw-bg-opacity, 1))}.drawer-container[style*=\"width: 64px\"] .drawer-item{justify-content:center;padding-left:16px!important}.drawer-container[style*=\"width: 64px\"] .item-content{justify-content:center}\n"] }]
|
|
4195
|
+
], template: "<div class=\"drawer-host-wrapper\">\n <a\n href=\"/\"\n class=\"h-[60px]\"\n [ngClass]=\"{ 'flex items-center justify-center': !isExpanded, 'p-4': isExpanded }\"\n >\n @if (logo) {\n <img\n [src]=\"logo\"\n alt=\"Home\"\n class=\"h-[20px] w-auto transition-all duration-300 ease-in-out\"\n [ngClass]=\"{ 'scale-90': !isExpanded, 'ml-5': isExpanded }\"\n />\n } @else {\n <div class=\"h-[30px] w-auto transition-all duration-300 ease-in-out\"></div>\n }\n </a>\n <div class=\"drawer-container\" [@drawerExpand]=\"getDrawerState()\">\n <!-- Navigation Items -->\n <nav class=\"drawer-nav\">\n <ng-container *ngFor=\"let item of items\">\n <ng-container\n *ngTemplateOutlet=\"drawerItem; context: { $implicit: item, level: 0 }\"\n ></ng-container>\n </ng-container>\n </nav>\n\n <!-- Version -->\n <div *ngIf=\"version\" class=\"drawer-version\" [matTooltip]=\"!isExpanded ? version : ''\" matTooltipPosition=\"right\">\n <span *ngIf=\"isExpanded\">{{ version }}</span>\n <span *ngIf=\"!isExpanded\" class=\"drawer-version-dot\"></span>\n </div>\n </div>\n\n <!-- Floating Toggle Button on right border -->\n <button\n type=\"button\"\n class=\"toggle-btn\"\n [attr.aria-label]=\"isExpanded ? 'Collapse navigation' : 'Expand navigation'\"\n [attr.aria-expanded]=\"isExpanded\"\n (click)=\"toggleDrawer()\"\n [matTooltip]=\"isExpanded ? 'Collapse' : 'Expand'\"\n matTooltipClass=\"text-text-primary text-xs font-semibold\"\n matTooltipPosition=\"right\"\n >\n <fx-ui-hero-icon\n [icon]=\"isExpanded ? 'chevron-left' : 'chevron-right'\"\n [size]=\"16\"\n class=\"text-text-primary\"\n ></fx-ui-hero-icon>\n </button>\n</div>\n\n<!-- Recursive Template for Drawer Items -->\n<ng-template #drawerItem let-item let-level=\"level\">\n <div class=\"drawer-item-wrapper\">\n <!-- Item Button/Link -->\n <a\n *ngIf=\"item.route && !item.children\"\n [routerLink]=\"item.route\"\n class=\"drawer-item\"\n [class.active]=\"isActiveRoute(item.route)\"\n [class.has-children]=\"item.children && item.children.length > 0\"\n [style.paddingLeft.px]=\"isExpanded ? 16 + level * 16 : 16\"\n [matTooltip]=\"!isExpanded ? item.label : ''\"\n matTooltipPosition=\"right\"\n matTooltipClass=\"text-text-primary text-xs font-semibold\"\n (click)=\"onItemClick(item, $event)\"\n >\n <div class=\"item-content\">\n <fx-ui-hero-icon [icon]=\"item.icon\" [size]=\"20\" class=\"item-icon\"></fx-ui-hero-icon>\n <span *ngIf=\"isExpanded\" class=\"item-label\">{{ item.label }}</span>\n </div>\n </a>\n\n <button\n *ngIf=\"!item.route || item.children\"\n type=\"button\"\n class=\"drawer-item\"\n [class.active]=\"isActiveRoute(item.route)\"\n [class.has-children]=\"item.children && item.children.length > 0\"\n [style.paddingLeft.px]=\"isExpanded ? 16 + level * 16 : 16\"\n [matTooltip]=\"!isExpanded ? item.label : ''\"\n matTooltipPosition=\"right\"\n matTooltipClass=\"text-text-primary text-sm font-semibold\"\n (click)=\"onItemClick(item, $event)\"\n >\n <div class=\"item-content\">\n <fx-ui-hero-icon [icon]=\"item.icon\" [size]=\"20\" class=\"item-icon\"></fx-ui-hero-icon>\n <span *ngIf=\"isExpanded\" class=\"item-label\">{{ item.label }}</span>\n <fx-ui-hero-icon\n *ngIf=\"isExpanded && item.children && item.children.length > 0\"\n [icon]=\"'chevron-down'\"\n [size]=\"16\"\n class=\"chevron-icon\"\n [class.rotated]=\"isAccordionExpanded(item.id)\"\n ></fx-ui-hero-icon>\n </div>\n </button>\n\n <!-- Accordion Children -->\n <div\n *ngIf=\"item.children && item.children.length > 0\"\n class=\"accordion-children\"\n [@accordionExpand]=\"getAccordionState(item.id)\"\n >\n <ng-container *ngFor=\"let child of item.children\">\n <ng-container\n *ngTemplateOutlet=\"drawerItem; context: { $implicit: child, level: level + 1 }\"\n ></ng-container>\n </ng-container>\n </div>\n </div>\n</ng-template>\n", styles: ["@charset \"UTF-8\";@layer properties;:host{display:block;height:100%}.drawer-host-wrapper{position:relative;height:100%;border-right-style:var(--tw-border-style);border-right-width:1px;border-color:rgb(var(--border-default))}.drawer-container{display:flex;height:100%;flex-direction:column;overflow:auto;background-color:rgb(var(--bg-primary));padding-inline:calc(var(--spacing, .25rem) * 2);transition:width .3s cubic-bezier(.4,0,.2,1)}.toggle-btn{position:absolute;top:50%;z-index:50;display:flex;height:calc(var(--spacing, .25rem) * 6);width:calc(var(--spacing, .25rem) * 6);--tw-translate-y: -50% ;translate:var(--tw-translate-x) var(--tw-translate-y);cursor:pointer;align-items:center;justify-content:center;border-radius:calc(infinity * 1px);border-style:var(--tw-border-style);border-width:1px;border-color:rgb(var(--border-default));background-color:rgb(var(--bg-primary));--tw-shadow: 0 1px 2px 0 var(--tw-shadow-color, rgb(var(--shadow-color) / .05));box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);right:-12px;transition:all .2s ease}.toggle-btn:hover{background-color:rgb(var(--bg-primary-hover));--tw-shadow: 0 4px 6px -1px var(--tw-shadow-color, rgb(var(--shadow-color) / .1)), 0 2px 4px -2px var(--tw-shadow-color, rgb(var(--shadow-color) / .1));box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.toggle-btn:active{transform:translateY(-50%) scale(.9)}.toggle-btn:focus-visible{outline-style:var(--tw-outline-style);outline-width:2px;outline-offset:2px;outline-color:rgb(var(--primary))}.drawer-nav{flex:1;overflow-x:hidden;overflow-y:auto;padding-block:calc(var(--spacing, .25rem) * 2)}.drawer-nav::-webkit-scrollbar{width:calc(var(--spacing, .25rem) * 1.5)}.drawer-nav::-webkit-scrollbar-track{background-color:transparent}.drawer-nav::-webkit-scrollbar-thumb{border-radius:.125rem;background-color:rgb(var(--border-default))}.drawer-nav::-webkit-scrollbar-thumb:hover{background-color:rgb(var(--text-placeholder))}.drawer-item-wrapper{position:relative}.drawer-item{position:relative;margin-top:var(--spacing, .25rem);display:flex;min-height:calc(var(--spacing, .25rem) * 8);width:100%;cursor:pointer;align-items:center;border-radius:.25rem;--tw-border-style: none;border-style:none;background-color:transparent;padding-inline:1rem;padding-block:calc(var(--spacing, .25rem) * 2);color:rgb(var(--text-primary));text-decoration-line:none;transition:all .2s ease;-webkit-tap-highlight-color:transparent;--tw-outline-style: none;outline-style:none}.drawer-item:hover{--tw-scale-x: 95%;--tw-scale-y: 95%;--tw-scale-z: 95%;scale:var(--tw-scale-x) var(--tw-scale-y);background-color:rgb(var(--bg-primary-hover));color:rgb(var(--text-primary))}.drawer-item.active{background-image:linear-gradient(135deg,rgb(var(--gradient-primary-start)),rgb(var(--gradient-primary-end)));--tw-font-weight: var(--font-weight-medium, 500);font-weight:var(--font-weight-medium, 500);color:var(--color-white, #fff)}.drawer-item.active .item-icon{color:var(--color-white, #fff)}.drawer-item:active{transform:scale(.98)}.drawer-item:focus-visible{outline-style:var(--tw-outline-style);outline-width:2px;outline-offset:2px;outline-color:rgb(var(--primary))}.item-content{display:flex;width:100%;align-items:center;gap:calc(var(--spacing, .25rem) * 3);overflow:hidden}.item-icon{flex-shrink:0;color:inherit}.item-label{flex:1;overflow:hidden;text-align:left;font-size:13px;--tw-leading: calc(var(--spacing, .25rem) * 5);line-height:calc(var(--spacing, .25rem) * 5);text-overflow:ellipsis;white-space:nowrap}.chevron-icon{margin-left:auto;flex-shrink:0;color:rgb(var(--text-primary));transition:transform .3s cubic-bezier(.4,0,.2,1)}.chevron-icon.rotated{transform:rotate(180deg)}.accordion-children{overflow:hidden}.drawer-version{display:flex;align-items:center;justify-content:center;border-top-style:var(--tw-border-style);border-top-width:1px;border-color:rgb(var(--border-default));padding-inline:calc(var(--spacing, .25rem) * 4);padding-block:calc(var(--spacing, .25rem) * 3);font-size:11px;color:rgb(var(--text-placeholder));-webkit-user-select:none;user-select:none;font-family:Roboto,monospace}.drawer-version-dot{display:block;height:calc(var(--spacing, .25rem) * 1.5);width:calc(var(--spacing, .25rem) * 1.5);border-radius:calc(infinity * 1px);background-color:rgb(var(--text-placeholder))}.drawer-container[style*=\"width: 64px\"] .drawer-item{justify-content:center;padding-left:16px!important}.drawer-container[style*=\"width: 64px\"] .item-content{justify-content:center}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@property --tw-translate-x{syntax: \"*\"; inherits: false; initial-value: 0;}@property --tw-translate-y{syntax: \"*\"; inherits: false; initial-value: 0;}@property --tw-translate-z{syntax: \"*\"; inherits: false; initial-value: 0;}@property --tw-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-shadow-color{syntax: \"*\"; inherits: false;}@property --tw-shadow-alpha{syntax: \"<percentage>\"; inherits: false; initial-value: 100%;}@property --tw-inset-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-inset-shadow-color{syntax: \"*\"; inherits: false;}@property --tw-inset-shadow-alpha{syntax: \"<percentage>\"; inherits: false; initial-value: 100%;}@property --tw-ring-color{syntax: \"*\"; inherits: false;}@property --tw-ring-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-inset-ring-color{syntax: \"*\"; inherits: false;}@property --tw-inset-ring-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-ring-inset{syntax: \"*\"; inherits: false;}@property --tw-ring-offset-width{syntax: \"<length>\"; inherits: false; initial-value: 0px;}@property --tw-ring-offset-color{syntax: \"*\"; inherits: false; initial-value: #fff;}@property --tw-ring-offset-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-outline-style{syntax: \"*\"; inherits: false; initial-value: solid;}@property --tw-scale-x{syntax: \"*\"; inherits: false; initial-value: 1;}@property --tw-scale-y{syntax: \"*\"; inherits: false; initial-value: 1;}@property --tw-scale-z{syntax: \"*\"; inherits: false; initial-value: 1;}@property --tw-font-weight{syntax: \"*\"; inherits: false;}@property --tw-leading{syntax: \"*\"; inherits: false;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style: solid;--tw-translate-x: 0;--tw-translate-y: 0;--tw-translate-z: 0;--tw-shadow: 0 0 #0000;--tw-shadow-color: initial;--tw-shadow-alpha: 100%;--tw-inset-shadow: 0 0 #0000;--tw-inset-shadow-color: initial;--tw-inset-shadow-alpha: 100%;--tw-ring-color: initial;--tw-ring-shadow: 0 0 #0000;--tw-inset-ring-color: initial;--tw-inset-ring-shadow: 0 0 #0000;--tw-ring-inset: initial;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-offset-shadow: 0 0 #0000;--tw-outline-style: solid;--tw-scale-x: 1;--tw-scale-y: 1;--tw-scale-z: 1;--tw-font-weight: initial;--tw-leading: initial}}}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"] }]
|
|
3534
4196
|
}], ctorParameters: () => [{ type: i1$4.Router }], propDecorators: { items: [{
|
|
3535
4197
|
type: Input
|
|
3536
4198
|
}], isExpanded: [{
|
|
@@ -3545,6 +4207,398 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImpor
|
|
|
3545
4207
|
type: Output
|
|
3546
4208
|
}] } });
|
|
3547
4209
|
|
|
4210
|
+
/** Row-card skeleton placeholder for master-detail list panes. */
|
|
4211
|
+
class SkeletonListComponent {
|
|
4212
|
+
rows = 5;
|
|
4213
|
+
range(n) {
|
|
4214
|
+
return Array.from({ length: Number(n) || 0 }, (_, i) => i);
|
|
4215
|
+
}
|
|
4216
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: SkeletonListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4217
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: SkeletonListComponent, isStandalone: false, selector: "fx-ui-skeleton-list", inputs: { rows: "rows" }, ngImport: i0, template: "<div class=\"flex flex-col gap-normal animate-pulse\">\n @for (r of range(rows); track r) {\n <div class=\"rounded-lg border border-border-default p-semi\">\n <div class=\"flex items-center gap-normal mb-small\">\n <div class=\"w-8 h-8 rounded-lg bg-bg-hover shrink-0\"></div>\n <div class=\"h-4 flex-1 rounded bg-bg-hover\"></div>\n <div class=\"h-5 w-12 rounded-full bg-bg-hover shrink-0\"></div>\n </div>\n <div class=\"h-3 w-3/4 rounded bg-bg-hover\"></div>\n </div>\n }\n</div>\n" });
|
|
4218
|
+
}
|
|
4219
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: SkeletonListComponent, decorators: [{
|
|
4220
|
+
type: Component,
|
|
4221
|
+
args: [{ selector: 'fx-ui-skeleton-list', standalone: false, template: "<div class=\"flex flex-col gap-normal animate-pulse\">\n @for (r of range(rows); track r) {\n <div class=\"rounded-lg border border-border-default p-semi\">\n <div class=\"flex items-center gap-normal mb-small\">\n <div class=\"w-8 h-8 rounded-lg bg-bg-hover shrink-0\"></div>\n <div class=\"h-4 flex-1 rounded bg-bg-hover\"></div>\n <div class=\"h-5 w-12 rounded-full bg-bg-hover shrink-0\"></div>\n </div>\n <div class=\"h-3 w-3/4 rounded bg-bg-hover\"></div>\n </div>\n }\n</div>\n" }]
|
|
4222
|
+
}], propDecorators: { rows: [{
|
|
4223
|
+
type: Input
|
|
4224
|
+
}] } });
|
|
4225
|
+
|
|
4226
|
+
/** Card-shaped skeleton placeholder for detail panels. */
|
|
4227
|
+
class SkeletonDetailComponent {
|
|
4228
|
+
/** Number of content cards rendered below the header card. */
|
|
4229
|
+
cards = 3;
|
|
4230
|
+
/** Rows per content card. */
|
|
4231
|
+
rows = 4;
|
|
4232
|
+
range(n) {
|
|
4233
|
+
return Array.from({ length: Number(n) || 0 }, (_, i) => i);
|
|
4234
|
+
}
|
|
4235
|
+
rowWidth(i) {
|
|
4236
|
+
return [90, 70, 80, 60][i % 4];
|
|
4237
|
+
}
|
|
4238
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: SkeletonDetailComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4239
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: SkeletonDetailComponent, isStandalone: false, selector: "fx-ui-skeleton-detail", inputs: { cards: "cards", rows: "rows" }, ngImport: i0, template: "<div class=\"flex flex-col gap-xl animate-pulse\">\n <div class=\"card-common\">\n <div class=\"flex items-start gap-normal\">\n <div class=\"w-10 h-10 rounded-lg bg-bg-hover shrink-0\"></div>\n <div class=\"flex-1 min-w-0 flex flex-col gap-normal\">\n <div class=\"h-5 w-1/2 rounded bg-bg-hover\"></div>\n <div class=\"h-3 w-3/4 rounded bg-bg-hover\"></div>\n </div>\n </div>\n </div>\n\n @for (c of range(cards); track c) {\n <div class=\"card-common flex flex-col gap-large\">\n <div class=\"h-4 w-1/3 rounded bg-bg-hover\"></div>\n <div class=\"flex flex-col gap-normal\">\n @for (r of range(rows); track r) {\n <div class=\"h-3 rounded bg-bg-hover\" [style.width.%]=\"rowWidth(r)\"></div>\n }\n </div>\n </div>\n }\n</div>\n" });
|
|
4240
|
+
}
|
|
4241
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: SkeletonDetailComponent, decorators: [{
|
|
4242
|
+
type: Component,
|
|
4243
|
+
args: [{ selector: 'fx-ui-skeleton-detail', standalone: false, template: "<div class=\"flex flex-col gap-xl animate-pulse\">\n <div class=\"card-common\">\n <div class=\"flex items-start gap-normal\">\n <div class=\"w-10 h-10 rounded-lg bg-bg-hover shrink-0\"></div>\n <div class=\"flex-1 min-w-0 flex flex-col gap-normal\">\n <div class=\"h-5 w-1/2 rounded bg-bg-hover\"></div>\n <div class=\"h-3 w-3/4 rounded bg-bg-hover\"></div>\n </div>\n </div>\n </div>\n\n @for (c of range(cards); track c) {\n <div class=\"card-common flex flex-col gap-large\">\n <div class=\"h-4 w-1/3 rounded bg-bg-hover\"></div>\n <div class=\"flex flex-col gap-normal\">\n @for (r of range(rows); track r) {\n <div class=\"h-3 rounded bg-bg-hover\" [style.width.%]=\"rowWidth(r)\"></div>\n }\n </div>\n </div>\n }\n</div>\n" }]
|
|
4244
|
+
}], propDecorators: { cards: [{
|
|
4245
|
+
type: Input
|
|
4246
|
+
}], rows: [{
|
|
4247
|
+
type: Input
|
|
4248
|
+
}] } });
|
|
4249
|
+
|
|
4250
|
+
/** KPI tile row: big number + tinted icon box + caption, in a responsive grid. */
|
|
4251
|
+
class StatCardsComponent {
|
|
4252
|
+
cards = [];
|
|
4253
|
+
/** Grid columns; defaults to one column per card. */
|
|
4254
|
+
cols;
|
|
4255
|
+
get gridColumns() {
|
|
4256
|
+
// No explicit cols → responsive auto-fit so tiles stack on narrow viewports.
|
|
4257
|
+
if (this.cols === undefined) {
|
|
4258
|
+
return 'repeat(auto-fit, minmax(180px, 1fr))';
|
|
4259
|
+
}
|
|
4260
|
+
return `repeat(${this.cols}, minmax(0, 1fr))`;
|
|
4261
|
+
}
|
|
4262
|
+
numClass(card) {
|
|
4263
|
+
return card.tone ? toneText(card.tone) : 'text-text-primary';
|
|
4264
|
+
}
|
|
4265
|
+
boxClass(card) {
|
|
4266
|
+
return card.tone ? toneBadge(card.tone) : 'bg-bg-hover text-text-secondary';
|
|
4267
|
+
}
|
|
4268
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: StatCardsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4269
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: StatCardsComponent, isStandalone: false, selector: "fx-ui-stat-cards", inputs: { cards: "cards", cols: "cols" }, ngImport: i0, template: "<div class=\"grid gap-normal\" [ngStyle]=\"{ 'grid-template-columns': gridColumns }\">\n @for (card of cards; track card.label) {\n <div class=\"card-common flex items-center gap-normal\">\n @if (card.icon) {\n <div\n class=\"w-10 h-10 rounded-lg flex items-center justify-center shrink-0\"\n [ngClass]=\"boxClass(card)\"\n >\n <fx-ui-hero-icon [icon]=\"card.icon\" [size]=\"20\"></fx-ui-hero-icon>\n </div>\n }\n <div class=\"flex flex-col min-w-0\">\n <span class=\"txt-heading-04\" [ngClass]=\"numClass(card)\">{{ card.value }}</span>\n <span class=\"txt-utility-caption text-text-secondary truncate\">{{ card.label }}</span>\n </div>\n </div>\n }\n</div>\n", dependencies: [{ kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: HeroIconComponent, selector: "fx-ui-hero-icon", inputs: ["stroke", "icon", "solid", "outline", "size", "color", "class"] }] });
|
|
4270
|
+
}
|
|
4271
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: StatCardsComponent, decorators: [{
|
|
4272
|
+
type: Component,
|
|
4273
|
+
args: [{ selector: 'fx-ui-stat-cards', standalone: false, template: "<div class=\"grid gap-normal\" [ngStyle]=\"{ 'grid-template-columns': gridColumns }\">\n @for (card of cards; track card.label) {\n <div class=\"card-common flex items-center gap-normal\">\n @if (card.icon) {\n <div\n class=\"w-10 h-10 rounded-lg flex items-center justify-center shrink-0\"\n [ngClass]=\"boxClass(card)\"\n >\n <fx-ui-hero-icon [icon]=\"card.icon\" [size]=\"20\"></fx-ui-hero-icon>\n </div>\n }\n <div class=\"flex flex-col min-w-0\">\n <span class=\"txt-heading-04\" [ngClass]=\"numClass(card)\">{{ card.value }}</span>\n <span class=\"txt-utility-caption text-text-secondary truncate\">{{ card.label }}</span>\n </div>\n </div>\n }\n</div>\n" }]
|
|
4274
|
+
}], propDecorators: { cards: [{
|
|
4275
|
+
type: Input
|
|
4276
|
+
}], cols: [{
|
|
4277
|
+
type: Input
|
|
4278
|
+
}] } });
|
|
4279
|
+
|
|
4280
|
+
/** Centered "no data" block. The message is caller-translated (no i18n in lib). */
|
|
4281
|
+
class EmptyStateComponent {
|
|
4282
|
+
message = 'No data';
|
|
4283
|
+
/** Optional icon (hero-icon name). */
|
|
4284
|
+
icon;
|
|
4285
|
+
/** Min height of the block, CSS length. */
|
|
4286
|
+
height = '120px';
|
|
4287
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: EmptyStateComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4288
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: EmptyStateComponent, isStandalone: false, selector: "fx-ui-empty-state", inputs: { message: "message", icon: "icon", height: "height" }, ngImport: i0, template: "<div\n class=\"flex flex-col items-center justify-center gap-small w-full\"\n [style.minHeight]=\"height\"\n>\n @if (icon) {\n <fx-ui-hero-icon class=\"text-text-placeholder\" [icon]=\"icon\" [size]=\"24\"></fx-ui-hero-icon>\n }\n <div class=\"txt-default text-text-placeholder\">{{ message }}</div>\n</div>\n", dependencies: [{ kind: "component", type: HeroIconComponent, selector: "fx-ui-hero-icon", inputs: ["stroke", "icon", "solid", "outline", "size", "color", "class"] }] });
|
|
4289
|
+
}
|
|
4290
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: EmptyStateComponent, decorators: [{
|
|
4291
|
+
type: Component,
|
|
4292
|
+
args: [{ selector: 'fx-ui-empty-state', standalone: false, template: "<div\n class=\"flex flex-col items-center justify-center gap-small w-full\"\n [style.minHeight]=\"height\"\n>\n @if (icon) {\n <fx-ui-hero-icon class=\"text-text-placeholder\" [icon]=\"icon\" [size]=\"24\"></fx-ui-hero-icon>\n }\n <div class=\"txt-default text-text-placeholder\">{{ message }}</div>\n</div>\n" }]
|
|
4293
|
+
}], propDecorators: { message: [{
|
|
4294
|
+
type: Input
|
|
4295
|
+
}], icon: [{
|
|
4296
|
+
type: Input
|
|
4297
|
+
}], height: [{
|
|
4298
|
+
type: Input
|
|
4299
|
+
}] } });
|
|
4300
|
+
|
|
4301
|
+
/**
|
|
4302
|
+
* Tone-tinted filter chip row. Replaces the per-tab chipClass() copies —
|
|
4303
|
+
* including their classic bug of comparing a hardcoded filter key: the active
|
|
4304
|
+
* chip here is always derived from [value].
|
|
4305
|
+
*/
|
|
4306
|
+
class FilterChipsComponent {
|
|
4307
|
+
options = [];
|
|
4308
|
+
value = '';
|
|
4309
|
+
valueChange = new EventEmitter();
|
|
4310
|
+
/** Per-value tone overrides (e.g. { Safe: 'information', Protected: 'discovery' as any }). */
|
|
4311
|
+
toneOverrides = {};
|
|
4312
|
+
select(option) {
|
|
4313
|
+
this.value = option.value;
|
|
4314
|
+
this.valueChange.emit(option.value);
|
|
4315
|
+
}
|
|
4316
|
+
chipClass(option) {
|
|
4317
|
+
const active = this.value === option.value;
|
|
4318
|
+
if (!option.value) {
|
|
4319
|
+
return active
|
|
4320
|
+
? 'bg-text-primary text-text-focus'
|
|
4321
|
+
: 'bg-bg-hover text-text-secondary hover:bg-gradient-primary-start/10 hover:text-gradient-primary-start';
|
|
4322
|
+
}
|
|
4323
|
+
const tone = option.value in this.toneOverrides
|
|
4324
|
+
? this.toneOverrides[option.value]
|
|
4325
|
+
: severityTone(option.value);
|
|
4326
|
+
if (!tone) {
|
|
4327
|
+
return active ? 'bg-text-primary text-text-focus' : `${toneBadge(null)} hover:opacity-50`;
|
|
4328
|
+
}
|
|
4329
|
+
return active ? `${toneBg(tone)} text-text-focus` : `${toneBadge(tone)} hover:opacity-50`;
|
|
4330
|
+
}
|
|
4331
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: FilterChipsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4332
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: FilterChipsComponent, isStandalone: false, selector: "fx-ui-filter-chips", inputs: { options: "options", value: "value", toneOverrides: "toneOverrides" }, outputs: { valueChange: "valueChange" }, ngImport: i0, template: "<div class=\"flex flex-wrap items-center gap-small\">\n @for (option of options; track option.value) {\n <button\n type=\"button\"\n class=\"px-3 py-1 rounded-full txt-utility-caption font-medium transition-colors cursor-pointer\"\n [ngClass]=\"chipClass(option)\"\n (click)=\"select(option)\"\n >\n {{ option.label }}\n </button>\n }\n</div>\n", dependencies: [{ kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] });
|
|
4333
|
+
}
|
|
4334
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: FilterChipsComponent, decorators: [{
|
|
4335
|
+
type: Component,
|
|
4336
|
+
args: [{ selector: 'fx-ui-filter-chips', standalone: false, template: "<div class=\"flex flex-wrap items-center gap-small\">\n @for (option of options; track option.value) {\n <button\n type=\"button\"\n class=\"px-3 py-1 rounded-full txt-utility-caption font-medium transition-colors cursor-pointer\"\n [ngClass]=\"chipClass(option)\"\n (click)=\"select(option)\"\n >\n {{ option.label }}\n </button>\n }\n</div>\n" }]
|
|
4337
|
+
}], propDecorators: { options: [{
|
|
4338
|
+
type: Input
|
|
4339
|
+
}], value: [{
|
|
4340
|
+
type: Input
|
|
4341
|
+
}], valueChange: [{
|
|
4342
|
+
type: Output
|
|
4343
|
+
}], toneOverrides: [{
|
|
4344
|
+
type: Input
|
|
4345
|
+
}] } });
|
|
4346
|
+
|
|
4347
|
+
/** Thin tone-coloured progress/score bar with optional label and caption. */
|
|
4348
|
+
class MeterComponent {
|
|
4349
|
+
value = 0;
|
|
4350
|
+
max = 10;
|
|
4351
|
+
tone = null;
|
|
4352
|
+
/** Caller-translated texts; omitted → bar only. */
|
|
4353
|
+
label;
|
|
4354
|
+
caption;
|
|
4355
|
+
get percent() {
|
|
4356
|
+
const max = Number(this.max) || 1;
|
|
4357
|
+
const pct = (Number(this.value) / max) * 100;
|
|
4358
|
+
return Math.min(100, Math.max(0, pct));
|
|
4359
|
+
}
|
|
4360
|
+
get fillClass() {
|
|
4361
|
+
return toneBg(this.tone);
|
|
4362
|
+
}
|
|
4363
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: MeterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4364
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: MeterComponent, isStandalone: false, selector: "fx-ui-meter", inputs: { value: "value", max: "max", tone: "tone", label: "label", caption: "caption" }, ngImport: i0, template: "<div class=\"flex flex-col gap-small w-full\">\n @if (label || caption) {\n <div class=\"flex items-center justify-between gap-normal\">\n @if (label) {\n <span class=\"txt-field-label !mb-0\">{{ label }}</span>\n }\n @if (caption) {\n <span class=\"txt-utility-caption text-text-secondary\">{{ caption }}</span>\n }\n </div>\n }\n <div\n class=\"h-2 rounded-full bg-bg-hover overflow-hidden\"\n role=\"progressbar\"\n [attr.aria-valuemin]=\"0\"\n [attr.aria-valuemax]=\"max\"\n [attr.aria-valuenow]=\"value\"\n [attr.aria-label]=\"label || null\"\n >\n <div class=\"h-full rounded-full\" [ngClass]=\"fillClass\" [style.width.%]=\"percent\"></div>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] });
|
|
4365
|
+
}
|
|
4366
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: MeterComponent, decorators: [{
|
|
4367
|
+
type: Component,
|
|
4368
|
+
args: [{ selector: 'fx-ui-meter', standalone: false, template: "<div class=\"flex flex-col gap-small w-full\">\n @if (label || caption) {\n <div class=\"flex items-center justify-between gap-normal\">\n @if (label) {\n <span class=\"txt-field-label !mb-0\">{{ label }}</span>\n }\n @if (caption) {\n <span class=\"txt-utility-caption text-text-secondary\">{{ caption }}</span>\n }\n </div>\n }\n <div\n class=\"h-2 rounded-full bg-bg-hover overflow-hidden\"\n role=\"progressbar\"\n [attr.aria-valuemin]=\"0\"\n [attr.aria-valuemax]=\"max\"\n [attr.aria-valuenow]=\"value\"\n [attr.aria-label]=\"label || null\"\n >\n <div class=\"h-full rounded-full\" [ngClass]=\"fillClass\" [style.width.%]=\"percent\"></div>\n </div>\n</div>\n" }]
|
|
4369
|
+
}], propDecorators: { value: [{
|
|
4370
|
+
type: Input
|
|
4371
|
+
}], max: [{
|
|
4372
|
+
type: Input
|
|
4373
|
+
}], tone: [{
|
|
4374
|
+
type: Input
|
|
4375
|
+
}], label: [{
|
|
4376
|
+
type: Input
|
|
4377
|
+
}], caption: [{
|
|
4378
|
+
type: Input
|
|
4379
|
+
}] } });
|
|
4380
|
+
|
|
4381
|
+
/** Detail-panel section: card with an icon + heading header and projected body. */
|
|
4382
|
+
class SectionCardComponent {
|
|
4383
|
+
/** Caller-translated heading. */
|
|
4384
|
+
title = '';
|
|
4385
|
+
/** hero-icon name; omitted → text-only header. */
|
|
4386
|
+
icon;
|
|
4387
|
+
iconTone = null;
|
|
4388
|
+
get iconClass() {
|
|
4389
|
+
return this.iconTone ? toneText(this.iconTone) : 'text-text-secondary';
|
|
4390
|
+
}
|
|
4391
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: SectionCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4392
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: SectionCardComponent, isStandalone: false, selector: "fx-ui-section-card", inputs: { title: "title", icon: "icon", iconTone: "iconTone" }, ngImport: i0, template: "<div class=\"card-common\">\n <div class=\"flex items-center gap-normal mb-large\">\n @if (icon) {\n <fx-ui-hero-icon [ngClass]=\"iconClass\" [icon]=\"icon\" [size]=\"18\"></fx-ui-hero-icon>\n }\n <span class=\"txt-heading-06\">{{ title }}</span>\n </div>\n <ng-content></ng-content>\n</div>\n", dependencies: [{ kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: HeroIconComponent, selector: "fx-ui-hero-icon", inputs: ["stroke", "icon", "solid", "outline", "size", "color", "class"] }] });
|
|
4393
|
+
}
|
|
4394
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: SectionCardComponent, decorators: [{
|
|
4395
|
+
type: Component,
|
|
4396
|
+
args: [{ selector: 'fx-ui-section-card', standalone: false, template: "<div class=\"card-common\">\n <div class=\"flex items-center gap-normal mb-large\">\n @if (icon) {\n <fx-ui-hero-icon [ngClass]=\"iconClass\" [icon]=\"icon\" [size]=\"18\"></fx-ui-hero-icon>\n }\n <span class=\"txt-heading-06\">{{ title }}</span>\n </div>\n <ng-content></ng-content>\n</div>\n" }]
|
|
4397
|
+
}], propDecorators: { title: [{
|
|
4398
|
+
type: Input
|
|
4399
|
+
}], icon: [{
|
|
4400
|
+
type: Input
|
|
4401
|
+
}], iconTone: [{
|
|
4402
|
+
type: Input
|
|
4403
|
+
}] } });
|
|
4404
|
+
|
|
4405
|
+
/**
|
|
4406
|
+
* Label-over-value field for detail panels. The label uses txt-field-label
|
|
4407
|
+
* with its baked-in margin neutralised (the known mb-1 alignment gotcha) —
|
|
4408
|
+
* spacing is owned by this component.
|
|
4409
|
+
*/
|
|
4410
|
+
class InfoFieldComponent {
|
|
4411
|
+
/** Caller-translated label. */
|
|
4412
|
+
label = '';
|
|
4413
|
+
value;
|
|
4414
|
+
/** Colours the value; null/undefined renders the primary text colour. */
|
|
4415
|
+
tone = null;
|
|
4416
|
+
/** Monospace value (hashes, paths). */
|
|
4417
|
+
mono = false;
|
|
4418
|
+
/** Placeholder when value is empty. */
|
|
4419
|
+
placeholder = '---';
|
|
4420
|
+
get displayValue() {
|
|
4421
|
+
if (this.value === null || this.value === undefined || this.value === '') {
|
|
4422
|
+
return this.placeholder;
|
|
4423
|
+
}
|
|
4424
|
+
return String(this.value);
|
|
4425
|
+
}
|
|
4426
|
+
get valueClass() {
|
|
4427
|
+
const toneClass = this.tone ? toneText(this.tone) : 'text-text-primary';
|
|
4428
|
+
return this.mono ? `${toneClass} font-mono break-all` : toneClass;
|
|
4429
|
+
}
|
|
4430
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: InfoFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4431
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.4", type: InfoFieldComponent, isStandalone: false, selector: "fx-ui-info-field", inputs: { label: "label", value: "value", tone: "tone", mono: "mono", placeholder: "placeholder" }, ngImport: i0, template: "<div class=\"flex flex-col gap-small min-w-0\">\n <span class=\"txt-field-label !mb-0\">{{ label }}</span>\n <span class=\"txt-default\" [ngClass]=\"valueClass\">{{ displayValue }}</span>\n</div>\n", dependencies: [{ kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] });
|
|
4432
|
+
}
|
|
4433
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: InfoFieldComponent, decorators: [{
|
|
4434
|
+
type: Component,
|
|
4435
|
+
args: [{ selector: 'fx-ui-info-field', standalone: false, template: "<div class=\"flex flex-col gap-small min-w-0\">\n <span class=\"txt-field-label !mb-0\">{{ label }}</span>\n <span class=\"txt-default\" [ngClass]=\"valueClass\">{{ displayValue }}</span>\n</div>\n" }]
|
|
4436
|
+
}], propDecorators: { label: [{
|
|
4437
|
+
type: Input
|
|
4438
|
+
}], value: [{
|
|
4439
|
+
type: Input
|
|
4440
|
+
}], tone: [{
|
|
4441
|
+
type: Input
|
|
4442
|
+
}], mono: [{
|
|
4443
|
+
type: Input
|
|
4444
|
+
}], placeholder: [{
|
|
4445
|
+
type: Input
|
|
4446
|
+
}] } });
|
|
4447
|
+
|
|
4448
|
+
/**
|
|
4449
|
+
* Compact total/critical/high/medium(/low) badge cluster used in table cells.
|
|
4450
|
+
* Pass counts via [stats]; missing fields are skipped. Labels are numbers, so
|
|
4451
|
+
* no i18n is involved; the total badge renders neutral.
|
|
4452
|
+
*/
|
|
4453
|
+
class SeverityBadgesComponent {
|
|
4454
|
+
stats = null;
|
|
4455
|
+
/** Show zero-count badges (default true, mirrors the table-cell usage). */
|
|
4456
|
+
showZero = true;
|
|
4457
|
+
/** Append the severity letter to counts ("3C", "5H", "2M") per the report design. */
|
|
4458
|
+
suffixed = false;
|
|
4459
|
+
badgeClick = new EventEmitter();
|
|
4460
|
+
static TONES = {
|
|
4461
|
+
total: null,
|
|
4462
|
+
critical: 'critical',
|
|
4463
|
+
high: 'danger',
|
|
4464
|
+
medium: 'warning',
|
|
4465
|
+
low: 'success',
|
|
4466
|
+
};
|
|
4467
|
+
get badges() {
|
|
4468
|
+
const s = this.stats ?? {};
|
|
4469
|
+
return ['total', 'critical', 'high', 'medium', 'low']
|
|
4470
|
+
.filter((key) => s[key] !== undefined && (this.showZero || Number(s[key]) > 0))
|
|
4471
|
+
.map((key) => {
|
|
4472
|
+
const count = Number(s[key]) || 0;
|
|
4473
|
+
const label = this.suffixed && key !== 'total' ? `${count}${key.charAt(0).toUpperCase()}` : count;
|
|
4474
|
+
return { key, label, tone: SeverityBadgesComponent.TONES[key] };
|
|
4475
|
+
});
|
|
4476
|
+
}
|
|
4477
|
+
badgeClass(badge) {
|
|
4478
|
+
return badge.tone ? toneBadge(badge.tone) : 'bg-bg-hover text-text-primary';
|
|
4479
|
+
}
|
|
4480
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: SeverityBadgesComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4481
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: SeverityBadgesComponent, isStandalone: false, selector: "fx-ui-severity-badges", inputs: { stats: "stats", showZero: "showZero", suffixed: "suffixed" }, outputs: { badgeClick: "badgeClick" }, ngImport: i0, template: "<div class=\"flex items-center gap-small\">\n @for (badge of badges; track badge.key) {\n <button\n type=\"button\"\n class=\"px-2 py-0.5 rounded-full txt-utility-caption font-medium cursor-pointer focus-visible:ring-2 focus-visible:ring-primary/50\"\n [ngClass]=\"badgeClass(badge)\"\n [attr.aria-label]=\"badge.key + ': ' + badge.label\"\n (click)=\"badgeClick.emit(badge.key)\"\n >{{ badge.label }}</button>\n }\n</div>\n", dependencies: [{ kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] });
|
|
4482
|
+
}
|
|
4483
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: SeverityBadgesComponent, decorators: [{
|
|
4484
|
+
type: Component,
|
|
4485
|
+
args: [{ selector: 'fx-ui-severity-badges', standalone: false, template: "<div class=\"flex items-center gap-small\">\n @for (badge of badges; track badge.key) {\n <button\n type=\"button\"\n class=\"px-2 py-0.5 rounded-full txt-utility-caption font-medium cursor-pointer focus-visible:ring-2 focus-visible:ring-primary/50\"\n [ngClass]=\"badgeClass(badge)\"\n [attr.aria-label]=\"badge.key + ': ' + badge.label\"\n (click)=\"badgeClick.emit(badge.key)\"\n >{{ badge.label }}</button>\n }\n</div>\n" }]
|
|
4486
|
+
}], propDecorators: { stats: [{
|
|
4487
|
+
type: Input
|
|
4488
|
+
}], showZero: [{
|
|
4489
|
+
type: Input
|
|
4490
|
+
}], suffixed: [{
|
|
4491
|
+
type: Input
|
|
4492
|
+
}], badgeClick: [{
|
|
4493
|
+
type: Output
|
|
4494
|
+
}] } });
|
|
4495
|
+
|
|
4496
|
+
let nextUid = 0;
|
|
4497
|
+
/**
|
|
4498
|
+
* Two-column master-detail shell driven by a BaseMasterDetailComponent.
|
|
4499
|
+
* The host page projects its own header/filters and supplies templates for
|
|
4500
|
+
* the parts that vary per entity:
|
|
4501
|
+
*
|
|
4502
|
+
* <fx-ui-master-detail [table]="this" [emptyMessage]="'noData' | translate">
|
|
4503
|
+
* <div mdHeader>…title / stat cards…</div>
|
|
4504
|
+
* <div mdFilters>…search bar / filter chips…</div>
|
|
4505
|
+
* <ng-template #row let-item let-selected="selected">…row body…</ng-template>
|
|
4506
|
+
* <ng-template #detail let-detail>…detail pane…</ng-template>
|
|
4507
|
+
* </fx-ui-master-detail>
|
|
4508
|
+
*
|
|
4509
|
+
* The list behaves as a single-select listbox: the selected option is the
|
|
4510
|
+
* tab stop, ArrowUp/Down/Home/End move the selection.
|
|
4511
|
+
*/
|
|
4512
|
+
class MasterDetailComponent {
|
|
4513
|
+
table;
|
|
4514
|
+
/** Fixed list-pane width on lg+ viewports (stacked full-width below). */
|
|
4515
|
+
listWidth = '420px';
|
|
4516
|
+
maxListHeight = '640px';
|
|
4517
|
+
pageSizeOptions = [5, 10, 20, 50];
|
|
4518
|
+
/** Labels come in via inputs — the lib carries no i18n. */
|
|
4519
|
+
emptyMessage = 'No data';
|
|
4520
|
+
listLabel = '';
|
|
4521
|
+
skeletonCards = 3;
|
|
4522
|
+
rowTemplate;
|
|
4523
|
+
detailTemplate;
|
|
4524
|
+
paginator;
|
|
4525
|
+
uid = `fx-md-${nextUid++}`;
|
|
4526
|
+
ngAfterViewInit() {
|
|
4527
|
+
// The paginator lives in this view, not the host page's, so the host's
|
|
4528
|
+
// @ViewChild(MatPaginator) never finds it. Hand it to the table so
|
|
4529
|
+
// refresh() keeps resetting pageIndex.
|
|
4530
|
+
if (this.table) {
|
|
4531
|
+
this.table.paginator = this.paginator;
|
|
4532
|
+
}
|
|
4533
|
+
}
|
|
4534
|
+
isSelected(row) {
|
|
4535
|
+
return this.table.idOf(row) === this.table.selectedId;
|
|
4536
|
+
}
|
|
4537
|
+
optionId(index) {
|
|
4538
|
+
return `${this.uid}-opt-${index}`;
|
|
4539
|
+
}
|
|
4540
|
+
onKeydown(event, index) {
|
|
4541
|
+
const rows = this.table.dataSource.data || [];
|
|
4542
|
+
if (!rows.length) {
|
|
4543
|
+
return;
|
|
4544
|
+
}
|
|
4545
|
+
let next;
|
|
4546
|
+
switch (event.key) {
|
|
4547
|
+
case 'ArrowDown':
|
|
4548
|
+
next = Math.min(index + 1, rows.length - 1);
|
|
4549
|
+
break;
|
|
4550
|
+
case 'ArrowUp':
|
|
4551
|
+
next = Math.max(index - 1, 0);
|
|
4552
|
+
break;
|
|
4553
|
+
case 'Home':
|
|
4554
|
+
next = 0;
|
|
4555
|
+
break;
|
|
4556
|
+
case 'End':
|
|
4557
|
+
next = rows.length - 1;
|
|
4558
|
+
break;
|
|
4559
|
+
default:
|
|
4560
|
+
return;
|
|
4561
|
+
}
|
|
4562
|
+
event.preventDefault();
|
|
4563
|
+
if (next === index) {
|
|
4564
|
+
return;
|
|
4565
|
+
}
|
|
4566
|
+
this.table.selectItem(rows[next]);
|
|
4567
|
+
const el = document.getElementById(this.optionId(next));
|
|
4568
|
+
el?.focus();
|
|
4569
|
+
el?.scrollIntoView({ block: 'nearest' });
|
|
4570
|
+
}
|
|
4571
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: MasterDetailComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4572
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: MasterDetailComponent, isStandalone: false, selector: "fx-ui-master-detail", inputs: { table: "table", listWidth: "listWidth", maxListHeight: "maxListHeight", pageSizeOptions: "pageSizeOptions", emptyMessage: "emptyMessage", listLabel: "listLabel", skeletonCards: "skeletonCards" }, queries: [{ propertyName: "rowTemplate", first: true, predicate: ["row"], descendants: true }, { propertyName: "detailTemplate", first: true, predicate: ["detail"], descendants: true }], viewQueries: [{ propertyName: "paginator", first: true, predicate: MatPaginator, descendants: true }], ngImport: i0, template: "<div class=\"flex flex-col lg:flex-row items-start gap-xl\" [style.--fx-md-list-width]=\"listWidth\">\n <!-- Master: list pane -->\n <div class=\"fx-md-list card-common\">\n <ng-content select=\"[mdHeader]\"></ng-content>\n <ng-content select=\"[mdFilters]\"></ng-content>\n <div\n class=\"flex flex-col gap-normal overflow-auto pr-xs\"\n [style.maxHeight]=\"maxListHeight\"\n role=\"listbox\"\n [attr.aria-label]=\"listLabel || null\"\n >\n @if (table.loading) {\n <fx-ui-skeleton-list [rows]=\"table.pageSize\"></fx-ui-skeleton-list>\n } @else {\n @for (row of table.dataSource.data; track table.idOf(row); let i = $index) {\n <button\n type=\"button\"\n role=\"option\"\n [id]=\"optionId(i)\"\n [attr.aria-selected]=\"isSelected(row)\"\n [tabindex]=\"isSelected(row) ? 0 : -1\"\n (click)=\"table.selectItem(row)\"\n (keydown)=\"onKeydown($event, i)\"\n class=\"text-left rounded-lg border p-semi transition-colors\"\n [ngClass]=\"\n isSelected(row)\n ? 'border-gradient-primary-start bg-gradient-primary-start/5'\n : 'border-border-default hover:border-border-interactive bg-bg-primary'\n \"\n >\n <ng-container\n [ngTemplateOutlet]=\"rowTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: row, selected: isSelected(row) }\"\n ></ng-container>\n </button>\n }\n @if (!table.dataSource.data.length) {\n <fx-ui-empty-state [message]=\"emptyMessage\"></fx-ui-empty-state>\n }\n }\n </div>\n <!-- No (page) binding: ngAfterViewInit hands this paginator to the table,\n whose own BaseTable subscription (running after this child's hook)\n wires page events \u2014 a binding here would double-fetch every change. -->\n <mat-paginator\n class=\"mt-normal\"\n [length]=\"table.total\"\n [pageSize]=\"table.pageSize\"\n [pageSizeOptions]=\"pageSizeOptions\"\n ></mat-paginator>\n </div>\n\n <!-- Detail pane -->\n <div class=\"w-full lg:flex-1 min-w-0\">\n @if (table.selected) {\n @if (table.detailBusy) {\n <fx-ui-skeleton-detail [cards]=\"skeletonCards\"></fx-ui-skeleton-detail>\n } @else {\n <ng-container\n [ngTemplateOutlet]=\"detailTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: table.detail, selected: table.selected }\"\n ></ng-container>\n }\n } @else {\n <div class=\"card-common flex items-center justify-center h-[300px]\">\n <fx-ui-empty-state [message]=\"emptyMessage\"></fx-ui-empty-state>\n </div>\n }\n </div>\n</div>\n", styles: [".fx-md-list{width:100%}@media (min-width: 1080px){.fx-md-list{width:var(--fx-md-list-width, 420px);flex-shrink:0}}\n"], dependencies: [{ kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: i45.MatPaginator, selector: "mat-paginator", inputs: ["color", "pageIndex", "length", "pageSize", "pageSizeOptions", "hidePageSize", "showFirstLastButtons", "selectConfig", "disabled"], outputs: ["page"], exportAs: ["matPaginator"] }, { kind: "component", type: SkeletonListComponent, selector: "fx-ui-skeleton-list", inputs: ["rows"] }, { kind: "component", type: SkeletonDetailComponent, selector: "fx-ui-skeleton-detail", inputs: ["cards", "rows"] }, { kind: "component", type: EmptyStateComponent, selector: "fx-ui-empty-state", inputs: ["message", "icon", "height"] }] });
|
|
4573
|
+
}
|
|
4574
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: MasterDetailComponent, decorators: [{
|
|
4575
|
+
type: Component,
|
|
4576
|
+
args: [{ selector: 'fx-ui-master-detail', standalone: false, template: "<div class=\"flex flex-col lg:flex-row items-start gap-xl\" [style.--fx-md-list-width]=\"listWidth\">\n <!-- Master: list pane -->\n <div class=\"fx-md-list card-common\">\n <ng-content select=\"[mdHeader]\"></ng-content>\n <ng-content select=\"[mdFilters]\"></ng-content>\n <div\n class=\"flex flex-col gap-normal overflow-auto pr-xs\"\n [style.maxHeight]=\"maxListHeight\"\n role=\"listbox\"\n [attr.aria-label]=\"listLabel || null\"\n >\n @if (table.loading) {\n <fx-ui-skeleton-list [rows]=\"table.pageSize\"></fx-ui-skeleton-list>\n } @else {\n @for (row of table.dataSource.data; track table.idOf(row); let i = $index) {\n <button\n type=\"button\"\n role=\"option\"\n [id]=\"optionId(i)\"\n [attr.aria-selected]=\"isSelected(row)\"\n [tabindex]=\"isSelected(row) ? 0 : -1\"\n (click)=\"table.selectItem(row)\"\n (keydown)=\"onKeydown($event, i)\"\n class=\"text-left rounded-lg border p-semi transition-colors\"\n [ngClass]=\"\n isSelected(row)\n ? 'border-gradient-primary-start bg-gradient-primary-start/5'\n : 'border-border-default hover:border-border-interactive bg-bg-primary'\n \"\n >\n <ng-container\n [ngTemplateOutlet]=\"rowTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: row, selected: isSelected(row) }\"\n ></ng-container>\n </button>\n }\n @if (!table.dataSource.data.length) {\n <fx-ui-empty-state [message]=\"emptyMessage\"></fx-ui-empty-state>\n }\n }\n </div>\n <!-- No (page) binding: ngAfterViewInit hands this paginator to the table,\n whose own BaseTable subscription (running after this child's hook)\n wires page events \u2014 a binding here would double-fetch every change. -->\n <mat-paginator\n class=\"mt-normal\"\n [length]=\"table.total\"\n [pageSize]=\"table.pageSize\"\n [pageSizeOptions]=\"pageSizeOptions\"\n ></mat-paginator>\n </div>\n\n <!-- Detail pane -->\n <div class=\"w-full lg:flex-1 min-w-0\">\n @if (table.selected) {\n @if (table.detailBusy) {\n <fx-ui-skeleton-detail [cards]=\"skeletonCards\"></fx-ui-skeleton-detail>\n } @else {\n <ng-container\n [ngTemplateOutlet]=\"detailTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: table.detail, selected: table.selected }\"\n ></ng-container>\n }\n } @else {\n <div class=\"card-common flex items-center justify-center h-[300px]\">\n <fx-ui-empty-state [message]=\"emptyMessage\"></fx-ui-empty-state>\n </div>\n }\n </div>\n</div>\n", styles: [".fx-md-list{width:100%}@media (min-width: 1080px){.fx-md-list{width:var(--fx-md-list-width, 420px);flex-shrink:0}}\n"] }]
|
|
4577
|
+
}], propDecorators: { table: [{
|
|
4578
|
+
type: Input
|
|
4579
|
+
}], listWidth: [{
|
|
4580
|
+
type: Input
|
|
4581
|
+
}], maxListHeight: [{
|
|
4582
|
+
type: Input
|
|
4583
|
+
}], pageSizeOptions: [{
|
|
4584
|
+
type: Input
|
|
4585
|
+
}], emptyMessage: [{
|
|
4586
|
+
type: Input
|
|
4587
|
+
}], listLabel: [{
|
|
4588
|
+
type: Input
|
|
4589
|
+
}], skeletonCards: [{
|
|
4590
|
+
type: Input
|
|
4591
|
+
}], rowTemplate: [{
|
|
4592
|
+
type: ContentChild,
|
|
4593
|
+
args: ['row']
|
|
4594
|
+
}], detailTemplate: [{
|
|
4595
|
+
type: ContentChild,
|
|
4596
|
+
args: ['detail']
|
|
4597
|
+
}], paginator: [{
|
|
4598
|
+
type: ViewChild,
|
|
4599
|
+
args: [MatPaginator]
|
|
4600
|
+
}] } });
|
|
4601
|
+
|
|
3548
4602
|
const FX_COMPONENTS = [
|
|
3549
4603
|
InputComponent,
|
|
3550
4604
|
SelectComponent,
|
|
@@ -3573,6 +4627,16 @@ const FX_COMPONENTS = [
|
|
|
3573
4627
|
TableCell,
|
|
3574
4628
|
KanbanBoardComponent,
|
|
3575
4629
|
DrawerComponent,
|
|
4630
|
+
SkeletonListComponent,
|
|
4631
|
+
SkeletonDetailComponent,
|
|
4632
|
+
StatCardsComponent,
|
|
4633
|
+
EmptyStateComponent,
|
|
4634
|
+
FilterChipsComponent,
|
|
4635
|
+
MeterComponent,
|
|
4636
|
+
SectionCardComponent,
|
|
4637
|
+
InfoFieldComponent,
|
|
4638
|
+
SeverityBadgesComponent,
|
|
4639
|
+
MasterDetailComponent,
|
|
3576
4640
|
];
|
|
3577
4641
|
|
|
3578
4642
|
class ConfirmationDialogComponent {
|
|
@@ -3602,13 +4666,13 @@ class ConfirmationDialogComponent {
|
|
|
3602
4666
|
cancel() {
|
|
3603
4667
|
this.ref.close(false);
|
|
3604
4668
|
}
|
|
3605
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: ConfirmationDialogComponent, deps: [{ token: i1$
|
|
3606
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.4", type: ConfirmationDialogComponent, isStandalone: false, selector: "fx-ui-confirmation-dialog", ngImport: i0, template: "<div animate.enter=\"fade-in-animation\" class=\"dialog-container\">\n <h1 class=\"txt-heading-05 dialog-header\">{{ title }}</h1>\n <mat-dialog-content>\n <div class=\"txt-default\">{{ message }}</div>\n </mat-dialog-content>\n <mat-dialog-actions align=\"end\">\n <fx-ui-button class=\"mr-1\" [label]=\"cancelLabel\" (clicked)=\"cancel()\"></fx-ui-button>\n <fx-ui-button\n buttonVariant=\"primary\"\n class=\"mr-1\"\n [label]=\"confirmLabel\"\n (clicked)=\"accept()\"\n [loading]=\"loading\"\n class=\"min-w-[100px]\"\n ></fx-ui-button>\n </mat-dialog-actions>\n</div>\n", styles: [""], dependencies: [{ kind: "directive", type: i1$
|
|
4669
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: ConfirmationDialogComponent, deps: [{ token: i1$2.MatDialogRef }, { token: MAT_DIALOG_DATA }], target: i0.ɵɵFactoryTarget.Component });
|
|
4670
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.4", type: ConfirmationDialogComponent, isStandalone: false, selector: "fx-ui-confirmation-dialog", ngImport: i0, template: "<div animate.enter=\"fade-in-animation\" class=\"dialog-container\">\n <h1 mat-dialog-title class=\"txt-heading-05 dialog-header\">{{ title }}</h1>\n <mat-dialog-content>\n <div class=\"txt-default\">{{ message }}</div>\n </mat-dialog-content>\n <mat-dialog-actions align=\"end\">\n <fx-ui-button class=\"mr-1\" [label]=\"cancelLabel\" (clicked)=\"cancel()\"></fx-ui-button>\n <fx-ui-button\n buttonVariant=\"primary\"\n class=\"mr-1\"\n [label]=\"confirmLabel\"\n (clicked)=\"accept()\"\n [loading]=\"loading\"\n class=\"min-w-[100px]\"\n ></fx-ui-button>\n </mat-dialog-actions>\n</div>\n", styles: [""], dependencies: [{ kind: "directive", type: i1$2.MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: i1$2.MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "directive", type: i1$2.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "component", type: ButtonComponent, selector: "fx-ui-button", inputs: ["label", "disabled", "buttonVariant", "icon", "class", "loading", "type", "ariaLabel"], outputs: ["clicked"] }] });
|
|
3607
4671
|
}
|
|
3608
4672
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: ConfirmationDialogComponent, decorators: [{
|
|
3609
4673
|
type: Component,
|
|
3610
|
-
args: [{ selector: 'fx-ui-confirmation-dialog', standalone: false, template: "<div animate.enter=\"fade-in-animation\" class=\"dialog-container\">\n <h1 class=\"txt-heading-05 dialog-header\">{{ title }}</h1>\n <mat-dialog-content>\n <div class=\"txt-default\">{{ message }}</div>\n </mat-dialog-content>\n <mat-dialog-actions align=\"end\">\n <fx-ui-button class=\"mr-1\" [label]=\"cancelLabel\" (clicked)=\"cancel()\"></fx-ui-button>\n <fx-ui-button\n buttonVariant=\"primary\"\n class=\"mr-1\"\n [label]=\"confirmLabel\"\n (clicked)=\"accept()\"\n [loading]=\"loading\"\n class=\"min-w-[100px]\"\n ></fx-ui-button>\n </mat-dialog-actions>\n</div>\n" }]
|
|
3611
|
-
}], ctorParameters: () => [{ type: i1$
|
|
4674
|
+
args: [{ selector: 'fx-ui-confirmation-dialog', standalone: false, template: "<div animate.enter=\"fade-in-animation\" class=\"dialog-container\">\n <h1 mat-dialog-title class=\"txt-heading-05 dialog-header\">{{ title }}</h1>\n <mat-dialog-content>\n <div class=\"txt-default\">{{ message }}</div>\n </mat-dialog-content>\n <mat-dialog-actions align=\"end\">\n <fx-ui-button class=\"mr-1\" [label]=\"cancelLabel\" (clicked)=\"cancel()\"></fx-ui-button>\n <fx-ui-button\n buttonVariant=\"primary\"\n class=\"mr-1\"\n [label]=\"confirmLabel\"\n (clicked)=\"accept()\"\n [loading]=\"loading\"\n class=\"min-w-[100px]\"\n ></fx-ui-button>\n </mat-dialog-actions>\n</div>\n" }]
|
|
4675
|
+
}], ctorParameters: () => [{ type: i1$2.MatDialogRef }, { type: undefined, decorators: [{
|
|
3612
4676
|
type: Inject,
|
|
3613
4677
|
args: [MAT_DIALOG_DATA]
|
|
3614
4678
|
}] }] });
|
|
@@ -3628,7 +4692,7 @@ const MY_MOMENT_FORMATS = {
|
|
|
3628
4692
|
};
|
|
3629
4693
|
class UiModule {
|
|
3630
4694
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: UiModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
3631
|
-
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.4", ngImport: i0, type: UiModule, declarations: [InputComponent, SelectComponent, RadioButtonComponent, CheckboxComponent, DndUploadComponent, ButtonComponent, RadioButtonToggleComponent, DatetimePicker, LoadingPanel, SearchBarComponent, TabGroupComponent, TabComponent, HeroIconComponent, ToastComponent, ToastContainerComponent, TagComponent, ChartComponent, SliderComponent, SwitchComponent, RichTextAreaComponent, SkeletonTableLoadingComponent, FlowConnection, CircleProgressBar, TreeDiagram, TableCell, KanbanBoardComponent, DrawerComponent, ConfirmationDialogComponent], imports: [CommonModule,
|
|
4695
|
+
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.4", ngImport: i0, type: UiModule, declarations: [InputComponent, SelectComponent, RadioButtonComponent, CheckboxComponent, DndUploadComponent, ButtonComponent, RadioButtonToggleComponent, DatetimePicker, LoadingPanel, SearchBarComponent, TabGroupComponent, TabComponent, HeroIconComponent, ToastComponent, ToastContainerComponent, TagComponent, ChartComponent, SliderComponent, SwitchComponent, RichTextAreaComponent, SkeletonTableLoadingComponent, FlowConnection, CircleProgressBar, TreeDiagram, TableCell, KanbanBoardComponent, DrawerComponent, SkeletonListComponent, SkeletonDetailComponent, StatCardsComponent, EmptyStateComponent, FilterChipsComponent, MeterComponent, SectionCardComponent, InfoFieldComponent, SeverityBadgesComponent, MasterDetailComponent, ConfirmationDialogComponent], imports: [CommonModule,
|
|
3632
4696
|
FormsModule,
|
|
3633
4697
|
ReactiveFormsModule,
|
|
3634
4698
|
RouterModule,
|
|
@@ -3637,7 +4701,7 @@ class UiModule {
|
|
|
3637
4701
|
OwlMomentDateTimeModule,
|
|
3638
4702
|
HasPermissionDirective,
|
|
3639
4703
|
TrimOnBlurDirective,
|
|
3640
|
-
NgHeroiconsModule,
|
|
4704
|
+
NgHeroiconsModule, i39.VflowComponent, i39.HandleComponent, i39.ResizableComponent, i39.SelectableDirective, i39.MiniMapComponent, i39.NodeToolbarComponent, i39.CustomTemplateEdgeComponent, i39.DragHandleDirective, i39.ConnectionControllerDirective, i39.NodeHtmlTemplateDirective, i39.NodeSvgTemplateDirective, i39.GroupNodeTemplateDirective, i39.EdgeLabelHtmlTemplateDirective, i39.EdgeTemplateDirective, i39.ConnectionTemplateDirective, i39.HandleTemplateDirective, i1$5.NgxEchartsModule, i3.MatSelectModule, i42.MatRadioModule, i43.MatButtonModule, i44.MatIconModule, i45.MatPaginatorModule, i46.MatTableModule, i47.MatSnackBarModule, i1$2.MatDialogModule, i49.MatCheckboxModule, i50.MatCardModule, i51.MatDatepickerModule, i52.MatTimepickerModule, i53.MatBadgeModule, i54.MatExpansionModule, i55.MatFormFieldModule, i56.MatMenuModule, i57.MatTooltipModule, i2$3.DragDropModule], exports: [InputComponent, SelectComponent, RadioButtonComponent, CheckboxComponent, DndUploadComponent, ButtonComponent, RadioButtonToggleComponent, DatetimePicker, LoadingPanel, SearchBarComponent, TabGroupComponent, TabComponent, HeroIconComponent, ToastComponent, ToastContainerComponent, TagComponent, ChartComponent, SliderComponent, SwitchComponent, RichTextAreaComponent, SkeletonTableLoadingComponent, FlowConnection, CircleProgressBar, TreeDiagram, TableCell, KanbanBoardComponent, DrawerComponent, SkeletonListComponent, SkeletonDetailComponent, StatCardsComponent, EmptyStateComponent, FilterChipsComponent, MeterComponent, SectionCardComponent, InfoFieldComponent, SeverityBadgesComponent, MasterDetailComponent, ConfirmationDialogComponent,
|
|
3641
4705
|
// Module
|
|
3642
4706
|
FormsModule,
|
|
3643
4707
|
ReactiveFormsModule,
|
|
@@ -3645,7 +4709,7 @@ class UiModule {
|
|
|
3645
4709
|
OwlDateTimeModule,
|
|
3646
4710
|
OwlNativeDateTimeModule,
|
|
3647
4711
|
HasPermissionDirective,
|
|
3648
|
-
NgHeroiconsModule, i3
|
|
4712
|
+
NgHeroiconsModule, i3.MatSelectModule, i42.MatRadioModule, i43.MatButtonModule, i44.MatIconModule, i45.MatPaginatorModule, i46.MatTableModule, i47.MatSnackBarModule, i1$2.MatDialogModule, i49.MatCheckboxModule, i50.MatCardModule, i51.MatDatepickerModule, i52.MatTimepickerModule, i53.MatBadgeModule, i54.MatExpansionModule, i55.MatFormFieldModule, i56.MatMenuModule, i57.MatTooltipModule, i2$3.DragDropModule] });
|
|
3649
4713
|
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: UiModule, providers: [
|
|
3650
4714
|
importProvidersFrom(NgHeroiconsModule.forRoot()),
|
|
3651
4715
|
{ provide: OWL_DATE_TIME_FORMATS, useValue: MY_MOMENT_FORMATS },
|
|
@@ -3656,14 +4720,14 @@ class UiModule {
|
|
|
3656
4720
|
OwlDateTimeModule,
|
|
3657
4721
|
OwlNativeDateTimeModule,
|
|
3658
4722
|
OwlMomentDateTimeModule,
|
|
3659
|
-
NgHeroiconsModule,
|
|
4723
|
+
NgHeroiconsModule, i39.VflowComponent, i39.HandleComponent, i39.ResizableComponent, i39.MiniMapComponent, i39.NodeToolbarComponent, i39.CustomTemplateEdgeComponent, NgxEchartsModule.forRoot({ echarts: () => import('echarts') }), MATERIAL_MODULE,
|
|
3660
4724
|
// Module
|
|
3661
4725
|
FormsModule,
|
|
3662
4726
|
ReactiveFormsModule,
|
|
3663
4727
|
RouterModule,
|
|
3664
4728
|
OwlDateTimeModule,
|
|
3665
4729
|
OwlNativeDateTimeModule,
|
|
3666
|
-
NgHeroiconsModule, i3
|
|
4730
|
+
NgHeroiconsModule, i3.MatSelectModule, i42.MatRadioModule, i43.MatButtonModule, i44.MatIconModule, i45.MatPaginatorModule, i46.MatTableModule, i47.MatSnackBarModule, i1$2.MatDialogModule, i49.MatCheckboxModule, i50.MatCardModule, i51.MatDatepickerModule, i52.MatTimepickerModule, i53.MatBadgeModule, i54.MatExpansionModule, i55.MatFormFieldModule, i56.MatMenuModule, i57.MatTooltipModule, i2$3.DragDropModule] });
|
|
3667
4731
|
}
|
|
3668
4732
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: UiModule, decorators: [{
|
|
3669
4733
|
type: NgModule,
|
|
@@ -3713,5 +4777,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImpor
|
|
|
3713
4777
|
* Generated bundle index. Do not edit.
|
|
3714
4778
|
*/
|
|
3715
4779
|
|
|
3716
|
-
export { AuthInterceptor, AuthStateService, BaseComponent, BaseDialogComponent, BaseResolver, BaseTableComponent, BreadcrumbService, ButtonComponent, ChartComponent, CheckboxComponent, CircleProgressBar, ConfirmationDialogComponent, DatetimePicker, DndUploadComponent, DrawerComponent, FlowConnection, FxLoadingService, FxStorageService, FxToastrService, FxUtils, FxValidators, HasPermissionDirective, HeroIconComponent, HttpLoaderFactory, HttpWrapper, InputComponent, KanbanBoardComponent, LoadingPanel, MY_MOMENT_FORMATS, NotificationService, PermissionGuard, PermissionService, QuillStyleLoaderService, RadioButtonComponent, RadioButtonToggleComponent, RichTextAreaComponent, SearchBarComponent, SelectComponent, SkeletonTableLoadingComponent, SliderComponent, SwitchComponent, TabComponent, TabGroupComponent, TableCell, TagComponent, ThemeService, ToastComponent, ToastContainerComponent, TranslationModule, TranslationService, TreeDiagram, TrimOnBlurDirective, UiModule };
|
|
4780
|
+
export { AuthInterceptor, AuthStateService, BaseComponent, BaseDialogComponent, BaseMasterDetailComponent, BaseResolver, BaseTableComponent, BreadcrumbService, ButtonComponent, ChartComponent, CheckboxComponent, CircleProgressBar, ConfirmationDialogComponent, DatetimePicker, DndUploadComponent, DrawerComponent, EmptyStateComponent, FilterChipsComponent, FlowConnection, FxLoadingService, FxStorageService, FxToastrService, FxUtils, FxValidators, HasPermissionDirective, HeroIconComponent, HttpLoaderFactory, HttpWrapper, InfoFieldComponent, InputComponent, KanbanBoardComponent, LoadingPanel, MY_MOMENT_FORMATS, MasterDetailComponent, MeterComponent, NotificationService, PermissionGuard, PermissionService, QuillStyleLoaderService, RadioButtonComponent, RadioButtonToggleComponent, RealtimeTableSync, RichTextAreaComponent, SearchBarComponent, SectionCardComponent, SelectComponent, SeverityBadgesComponent, SkeletonDetailComponent, SkeletonListComponent, SkeletonTableLoadingComponent, SliderComponent, StatCardsComponent, SwitchComponent, TabComponent, TabGroupComponent, TableCell, TagComponent, ThemeService, ToastComponent, ToastContainerComponent, TranslationModule, TranslationService, TreeDiagram, TrimOnBlurDirective, UiModule, countTone, createApi, fxListResolver, scoreTone, severityTone, toneBadge, toneBg, toneColor, toneText };
|
|
3717
4781
|
//# sourceMappingURL=foxeltech-angular-ui.mjs.map
|