@foxeltech/angular-ui 0.0.1

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.
Files changed (68) hide show
  1. package/BeVietnamPro/BeVietnamPro-Bold.ttf +0 -0
  2. package/BeVietnamPro/BeVietnamPro-ExtraLight.ttf +0 -0
  3. package/BeVietnamPro/BeVietnamPro-Light.ttf +0 -0
  4. package/BeVietnamPro/BeVietnamPro-Medium.ttf +0 -0
  5. package/BeVietnamPro/BeVietnamPro-Regular.ttf +0 -0
  6. package/BeVietnamPro/BeVietnamPro-SemiBold.ttf +0 -0
  7. package/BeVietnamPro/BeVietnamPro-Thin.ttf +0 -0
  8. package/README.md +3 -0
  9. package/SpaceMono-Regular.ttf +0 -0
  10. package/animations.css +130 -0
  11. package/components.css +480 -0
  12. package/dialogs.css +70 -0
  13. package/dm-sans/DMSans-Bold.ttf +0 -0
  14. package/dm-sans/DMSans-Light.ttf +0 -0
  15. package/dm-sans/DMSans-Medium.ttf +0 -0
  16. package/dm-sans/DMSans-Regular.ttf +0 -0
  17. package/dm-sans/DMSans-SemiBold.ttf +0 -0
  18. package/dm-sans/DMSans-Thin.ttf +0 -0
  19. package/fesm2022/foxeltech-angular-ui.mjs +3450 -0
  20. package/fesm2022/foxeltech-angular-ui.mjs.map +1 -0
  21. package/fonts.css +125 -0
  22. package/index.d.ts +1022 -0
  23. package/orbitron/Orbitron-Bold.ttf +0 -0
  24. package/orbitron/Orbitron-Medium.ttf +0 -0
  25. package/orbitron/Orbitron-Regular.ttf +0 -0
  26. package/orbitron/Orbitron-SemiBold.ttf +0 -0
  27. package/package.json +56 -0
  28. package/src/lib/styles/animations.css +130 -0
  29. package/src/lib/styles/components.css +480 -0
  30. package/src/lib/styles/dialogs.css +70 -0
  31. package/src/lib/styles/fonts.css +125 -0
  32. package/src/lib/styles/tailwind.css +18 -0
  33. package/src/lib/styles/theme.css +67 -0
  34. package/src/lib/ui/components/button/button.component.html +223 -0
  35. package/src/lib/ui/components/chart/chart.component.html +11 -0
  36. package/src/lib/ui/components/checkbox/checkbox.component.html +42 -0
  37. package/src/lib/ui/components/circle-progress-bar/circle-progress-bar.component.html +44 -0
  38. package/src/lib/ui/components/datetime-picker/datetime-picker.component.html +36 -0
  39. package/src/lib/ui/components/dnd-upload/dnd-upload.component.html +34 -0
  40. package/src/lib/ui/components/drawer/drawer.component.html +101 -0
  41. package/src/lib/ui/components/flow-connection/flow-connection.component.html +10 -0
  42. package/src/lib/ui/components/hero-icon/hero-icon.component.html +8 -0
  43. package/src/lib/ui/components/input/input.component.css +0 -0
  44. package/src/lib/ui/components/input/input.component.html +48 -0
  45. package/src/lib/ui/components/kanban-board/kanban-board.component.html +43 -0
  46. package/src/lib/ui/components/loading-panel/loading-panel.component.html +51 -0
  47. package/src/lib/ui/components/radio-button/radio-button.component.css +0 -0
  48. package/src/lib/ui/components/radio-button/radio-button.component.html +35 -0
  49. package/src/lib/ui/components/radio-button-toggle/radio-button-toggle.component.html +34 -0
  50. package/src/lib/ui/components/rich-text-area/rich-text-area.component.html +3 -0
  51. package/src/lib/ui/components/search-bar/search-bar.component.html +22 -0
  52. package/src/lib/ui/components/select/select.component.css +102 -0
  53. package/src/lib/ui/components/select/select.component.html +42 -0
  54. package/src/lib/ui/components/skeleton-table-loading/skeleton-table-loading.component.html +21 -0
  55. package/src/lib/ui/components/slider/slider.component.html +54 -0
  56. package/src/lib/ui/components/switch/switch.component.html +21 -0
  57. package/src/lib/ui/components/tab-component/tab.component.html +1 -0
  58. package/src/lib/ui/components/tab-group/tab-group.component.html +75 -0
  59. package/src/lib/ui/components/table-cell/table-cell.component.html +7 -0
  60. package/src/lib/ui/components/tag/tag.component.html +15 -0
  61. package/src/lib/ui/components/toast/toast.component.html +48 -0
  62. package/src/lib/ui/components/toast-container/toast-container.component.html +12 -0
  63. package/src/lib/ui/components/tree-diagram/tree-diagram.component.html +40 -0
  64. package/src/lib/ui/dialogs/confirmation/confirmation.component.html +17 -0
  65. package/tailwind-config.d.ts +4 -0
  66. package/tailwind.config.js +88 -0
  67. package/tailwind.css +18 -0
  68. package/theme.css +67 -0
@@ -0,0 +1,3450 @@
1
+ import * as i0 from '@angular/core';
2
+ import { Directive, Input, Component, createComponent, Injectable, ViewChild, Inject, EventEmitter, NgModule, HostListener, PLATFORM_ID, Output, Optional, Self, ChangeDetectionStrategy, TemplateRef, ContentChildren, ViewChildren, importProvidersFrom } from '@angular/core';
3
+ import * as i35 from '@angular/material/paginator';
4
+ import { MatPaginator, MatPaginatorModule } from '@angular/material/paginator';
5
+ import * as i36 from '@angular/material/table';
6
+ import { MatTableDataSource, MatTable, MatTableModule } from '@angular/material/table';
7
+ import * as i2 from '@angular/common';
8
+ import { isPlatformBrowser, CommonModule } from '@angular/common';
9
+ import * as i1 from '@dimaslz/ng-heroicons';
10
+ import { NgHeroiconsModule } from '@dimaslz/ng-heroicons';
11
+ import _, { clamp } from 'lodash';
12
+ import { Observable, firstValueFrom, Subject, debounceTime, BehaviorSubject, throwError, distinctUntilChanged, filter, takeUntil } from 'rxjs';
13
+ import * as i2$1 from '@angular/common/http';
14
+ import { HttpRequest, HttpClient } from '@angular/common/http';
15
+ import * as i1$1 from '@angular/material/dialog';
16
+ import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
17
+ import * as i1$2 from '@ngx-translate/core';
18
+ import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
19
+ import { TranslateHttpLoader } from '@ngx-translate/http-loader';
20
+ import * as i1$3 from '@angular/forms';
21
+ import { FormsModule, ReactiveFormsModule } from '@angular/forms';
22
+ import * as i1$4 from '@angular/router';
23
+ import { NavigationEnd, RouterModule } from '@angular/router';
24
+ import { catchError } from 'rxjs/operators';
25
+ import * as signalR from '@microsoft/signalr';
26
+ import * as i3$1 from '@angular/material/select';
27
+ import { MatSelectModule } from '@angular/material/select';
28
+ import * as i32 from '@angular/material/radio';
29
+ import { MatRadioModule } from '@angular/material/radio';
30
+ import * as i33 from '@angular/material/button';
31
+ import { MatButtonModule } from '@angular/material/button';
32
+ import * as i34 from '@angular/material/icon';
33
+ import { MatIconModule } from '@angular/material/icon';
34
+ import * as i37 from '@angular/material/snack-bar';
35
+ import { MatSnackBarModule } from '@angular/material/snack-bar';
36
+ import * as i39 from '@angular/material/checkbox';
37
+ import { MatCheckboxModule } from '@angular/material/checkbox';
38
+ import * as i40 from '@angular/material/card';
39
+ import { MatCardModule } from '@angular/material/card';
40
+ import * as i41 from '@angular/material/datepicker';
41
+ import { MatDatepickerModule } from '@angular/material/datepicker';
42
+ import * as i42 from '@angular/material/timepicker';
43
+ import { MatTimepickerModule } from '@angular/material/timepicker';
44
+ import * as i43 from '@angular/material/badge';
45
+ import { MatBadgeModule } from '@angular/material/badge';
46
+ import * as i44 from '@angular/material/expansion';
47
+ import { MatExpansionModule } from '@angular/material/expansion';
48
+ import * as i45 from '@angular/material/form-field';
49
+ import { MatFormFieldModule } from '@angular/material/form-field';
50
+ import * as i46 from '@angular/material/menu';
51
+ import { MatMenuModule } from '@angular/material/menu';
52
+ import * as i47 from '@angular/material/tooltip';
53
+ import { MatTooltipModule } from '@angular/material/tooltip';
54
+ import * as i2$2 from '@angular/cdk/drag-drop';
55
+ import { DragDropModule, moveItemInArray, transferArrayItem } from '@angular/cdk/drag-drop';
56
+ import * as i3 from '@danielmoncada/angular-datetime-picker';
57
+ import { OwlDateTimeModule, OwlNativeDateTimeModule, OWL_DATE_TIME_FORMATS } from '@danielmoncada/angular-datetime-picker';
58
+ import * as i1$5 from 'ngx-echarts';
59
+ import { NgxEchartsModule } from 'ngx-echarts';
60
+ import { __decorate, __param } from 'tslib';
61
+ import * as i29 from 'ngx-vflow';
62
+ import { Vflow } from 'ngx-vflow';
63
+ import { trigger, state, style, transition, animate } from '@angular/animations';
64
+ import { OwlMomentDateTimeModule } from '@danielmoncada/angular-datetime-picker-moment-adapter';
65
+
66
+ class BaseComponent {
67
+ injector;
68
+ constructor(injector) {
69
+ this.injector = injector;
70
+ }
71
+ ngOnDestroy() {
72
+ // clean up if needed later
73
+ }
74
+ 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
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.4", type: BaseComponent, isStandalone: true, ngImport: i0 });
76
+ }
77
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: BaseComponent, decorators: [{
78
+ type: Directive
79
+ }], ctorParameters: () => [{ type: i0.Injector }] });
80
+
81
+ class HeroIconComponent {
82
+ icon;
83
+ solid;
84
+ outline;
85
+ size;
86
+ color;
87
+ class;
88
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: HeroIconComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
89
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.4", type: HeroIconComponent, isStandalone: false, selector: "fx-ui-hero-icon", inputs: { icon: "icon", solid: "solid", outline: "outline", size: "size", color: "color", class: "class" }, ngImport: i0, template: "<ng-heroicons\n [icon]=\"icon\"\n [size]=\"size\"\n [solid]=\"solid\"\n [outline]=\"outline\"\n [color]=\"color\"\n [class]=\"class\"\n/>\n", styles: [""], dependencies: [{ kind: "component", type: i1.DynamicComponent, selector: "ng-heroicons", inputs: ["icon", "size", "color", "stroke", "outline", "solid", "class", "style"] }] });
90
+ }
91
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: HeroIconComponent, decorators: [{
92
+ type: Component,
93
+ args: [{ selector: 'fx-ui-hero-icon', standalone: false, template: "<ng-heroicons\n [icon]=\"icon\"\n [size]=\"size\"\n [solid]=\"solid\"\n [outline]=\"outline\"\n [color]=\"color\"\n [class]=\"class\"\n/>\n" }]
94
+ }], propDecorators: { icon: [{
95
+ type: Input
96
+ }], solid: [{
97
+ type: Input
98
+ }], outline: [{
99
+ type: Input
100
+ }], size: [{
101
+ type: Input
102
+ }], color: [{
103
+ type: Input
104
+ }], class: [{
105
+ type: Input
106
+ }] } });
107
+
108
+ class ToastComponent {
109
+ toastId;
110
+ title = '';
111
+ message = '';
112
+ type;
113
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: ToastComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
114
+ 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: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: HeroIconComponent, selector: "fx-ui-hero-icon", inputs: ["icon", "solid", "outline", "size", "color", "class"] }] });
115
+ }
116
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: ToastComponent, decorators: [{
117
+ type: Component,
118
+ 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" }]
119
+ }], propDecorators: { toastId: [{
120
+ type: Input
121
+ }], title: [{
122
+ type: Input
123
+ }], message: [{
124
+ type: Input
125
+ }], type: [{
126
+ type: Input
127
+ }] } });
128
+
129
+ class ToastContainerComponent {
130
+ ref;
131
+ toasts = [];
132
+ constructor(ref) {
133
+ this.ref = ref;
134
+ }
135
+ addToast(toast) {
136
+ this.toasts.push(toast);
137
+ this.ref.detectChanges();
138
+ setTimeout(() => this.removeToast(toast.id), 3000);
139
+ }
140
+ removeToast(id) {
141
+ this.toasts = this.toasts.filter(t => t.id !== id);
142
+ this.ref.detectChanges();
143
+ }
144
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: ToastContainerComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
145
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: ToastContainerComponent, isStandalone: false, selector: "fx-ui-toast-container", ngImport: i0, template: "<div\n class=\"fixed top-5 right-5 flex flex-col items-end gap-3 z-[10000] pointer-events-none\"\n>\n @for (toast of toasts; track toast) {\n <fx-ui-toast\n class=\"pointer-events-auto\"\n [message]=\"toast.message\"\n [title]=\"toast.title\"\n [type]=\"toast.type\"\n ></fx-ui-toast>\n }\n</div>", styles: [""], dependencies: [{ kind: "component", type: ToastComponent, selector: "fx-ui-toast", inputs: ["toastId", "title", "message", "type"] }] });
146
+ }
147
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: ToastContainerComponent, decorators: [{
148
+ type: Component,
149
+ args: [{ selector: 'fx-ui-toast-container', standalone: false, template: "<div\n class=\"fixed top-5 right-5 flex flex-col items-end gap-3 z-[10000] pointer-events-none\"\n>\n @for (toast of toasts; track toast) {\n <fx-ui-toast\n class=\"pointer-events-auto\"\n [message]=\"toast.message\"\n [title]=\"toast.title\"\n [type]=\"toast.type\"\n ></fx-ui-toast>\n }\n</div>" }]
150
+ }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }] });
151
+
152
+ class FxToastrService {
153
+ appRef;
154
+ containerRef;
155
+ idCounter = 0;
156
+ constructor(appRef) {
157
+ this.appRef = appRef;
158
+ this.ensureContainerExists();
159
+ }
160
+ ensureContainerExists() {
161
+ if (!this.containerRef) {
162
+ this.containerRef = createComponent(ToastContainerComponent, {
163
+ environmentInjector: this.appRef.injector,
164
+ });
165
+ this.appRef.attachView(this.containerRef.hostView);
166
+ document.body.appendChild(this.containerRef.location.nativeElement);
167
+ }
168
+ }
169
+ show(message, type, title) {
170
+ const toast = {
171
+ id: ++this.idCounter,
172
+ message,
173
+ title,
174
+ type,
175
+ };
176
+ this.containerRef?.instance.addToast(toast);
177
+ }
178
+ success(message, title) {
179
+ this.show(message, 'success', title);
180
+ }
181
+ error(message, title) {
182
+ this.show(message, 'error', title);
183
+ }
184
+ info(message, title) {
185
+ this.show(message, 'info', title);
186
+ }
187
+ warning(message, title) {
188
+ this.show(message, 'warning', title);
189
+ }
190
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: FxToastrService, deps: [{ token: i0.ApplicationRef }], target: i0.ɵɵFactoryTarget.Injectable });
191
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: FxToastrService, providedIn: 'root' });
192
+ }
193
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: FxToastrService, decorators: [{
194
+ type: Injectable,
195
+ args: [{
196
+ providedIn: 'root',
197
+ }]
198
+ }], ctorParameters: () => [{ type: i0.ApplicationRef }] });
199
+
200
+ class FxUtils {
201
+ static async preresolve(promises, value, key) {
202
+ if (key === 'callback' || key === 'socket' || key === 'listener') {
203
+ promises.push(value);
204
+ return;
205
+ }
206
+ if (value instanceof Function) {
207
+ this.preresolve(promises, value());
208
+ }
209
+ else if (value instanceof Array) {
210
+ const inpromises = [];
211
+ value.forEach((v) => {
212
+ this.preresolve(inpromises, v);
213
+ });
214
+ promises.push(Promise.all(inpromises));
215
+ }
216
+ else if (value instanceof Observable) {
217
+ promises.push(firstValueFrom(value));
218
+ }
219
+ else if (value instanceof Promise) {
220
+ promises.push(value);
221
+ }
222
+ else if (value instanceof Object) {
223
+ promises.push(this.resolve(value));
224
+ }
225
+ else {
226
+ promises.push(value);
227
+ }
228
+ }
229
+ static async resolve(value) {
230
+ const keys = [];
231
+ const promises = [];
232
+ for (const i in value) {
233
+ if (value.hasOwnProperty(i)) {
234
+ keys.push(i);
235
+ this.preresolve(promises, value[i], i);
236
+ }
237
+ }
238
+ const values = await Promise.all(promises);
239
+ const raw = {};
240
+ keys.forEach((key, index) => {
241
+ _.set(raw, key, values[index]);
242
+ });
243
+ return raw;
244
+ }
245
+ static isNotNull(obj) {
246
+ return !this.isNull(obj);
247
+ }
248
+ static isNull(obj) {
249
+ return obj === undefined || obj === null;
250
+ }
251
+ static isFunction(obj) {
252
+ return this.isNotNull(obj) && obj instanceof Function;
253
+ }
254
+ static isNotFunction(obj) {
255
+ return !this.isFunction(obj);
256
+ }
257
+ static isStringEmpty(obj) {
258
+ return this.isNull(obj) || obj === '';
259
+ }
260
+ static isStringNotEmpty(obj) {
261
+ return !this.isStringEmpty(obj);
262
+ }
263
+ static isArrayEmpty(obj) {
264
+ return this.isNull(obj) || obj.length === 0;
265
+ }
266
+ static isArrayNotEmpty(obj) {
267
+ return !this.isArrayEmpty(obj);
268
+ }
269
+ static replaceStartWithAsterisk(inputString, numToReplace) {
270
+ if (numToReplace >= inputString.length) {
271
+ return '*'.repeat(inputString.length);
272
+ }
273
+ else if (numToReplace <= 0) {
274
+ return inputString;
275
+ }
276
+ else {
277
+ const asterisks = '*'.repeat(numToReplace);
278
+ const remainingString = inputString.substring(numToReplace);
279
+ return asterisks + remainingString;
280
+ }
281
+ }
282
+ static checkInvalidField(form) {
283
+ Object.keys(form.controls).forEach((field) => {
284
+ const control = form.controls[field];
285
+ control.markAsTouched({ onlySelf: true });
286
+ });
287
+ }
288
+ static checkInlineErrorField(err, f) {
289
+ if (err) {
290
+ Object.keys(err).forEach((field) => {
291
+ const formControl = f.form.get(field);
292
+ if (formControl) {
293
+ const error = { apiError: err[field] };
294
+ formControl.setErrors(error);
295
+ }
296
+ });
297
+ }
298
+ }
299
+ static isSameCollection(collection, compareCollection) {
300
+ return _.isEmpty(_.xor(collection, compareCollection));
301
+ }
302
+ static isSameValue(model, compareModel) {
303
+ return _.isEqual(model, compareModel);
304
+ }
305
+ static parseError(err) {
306
+ if (_.get(err, 'status') === 401) {
307
+ return 'Session timeout. Please login again.';
308
+ }
309
+ if (this.isStringEmpty(_.get(err, 'error.code'))) {
310
+ return _.get(err, 'error.message', 'Unknown Error');
311
+ }
312
+ return `${_.get(err, 'error.code')}: ${_.get(err, 'error.message', 'Unknown Error')}`;
313
+ }
314
+ static convertColorFromVariable(name, alpha = 1) {
315
+ const value = getComputedStyle(document.body)
316
+ .getPropertyValue(name)
317
+ .trim()
318
+ .replace(/\s+/g, ' ');
319
+ return value ? `rgba(${value} / ${alpha})` : `rgba(255 255 255 / ${alpha})`;
320
+ }
321
+ static getTagClass(key, isCriticalExist = true) {
322
+ switch (key.toLowerCase()) {
323
+ case 'error':
324
+ case 'failed':
325
+ case 'inactive':
326
+ case 'critical':
327
+ case 'stopped':
328
+ return 'critical';
329
+ case 'high':
330
+ if (!isCriticalExist) {
331
+ return 'critical';
332
+ }
333
+ return 'danger';
334
+ case 'inprogress':
335
+ case 'processing':
336
+ case 'paused':
337
+ case 'medium':
338
+ case 'warning':
339
+ return 'warning';
340
+ case 'completed':
341
+ case 'success':
342
+ case 'active':
343
+ case 'low':
344
+ case 'running':
345
+ return 'success';
346
+ case 'queue':
347
+ case 'waiting':
348
+ case 'info':
349
+ return 'information';
350
+ case 'discovery':
351
+ return 'discovery';
352
+ case 'disabled':
353
+ default:
354
+ return 'default';
355
+ }
356
+ }
357
+ static validateEmail(value) {
358
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
359
+ const isEmail = emailRegex.test(value);
360
+ return isEmail;
361
+ }
362
+ static isStrongPassword(password) {
363
+ const regex = /^(?=.{6,}$)(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[^A-Za-z0-9]).*$/;
364
+ return regex.test(password);
365
+ }
366
+ static formatSizeUnits(bytes) {
367
+ if (bytes >= 1073741824) {
368
+ return (bytes / 1073741824).toFixed(2) + ' GB';
369
+ }
370
+ else if (bytes >= 1048576) {
371
+ return (bytes / 1048576).toFixed(2) + ' MB';
372
+ }
373
+ else if (bytes >= 1024) {
374
+ return (bytes / 1024).toFixed(2) + ' KB';
375
+ }
376
+ else if (bytes > 1) {
377
+ return bytes + ' bytes';
378
+ }
379
+ else if (bytes == 1) {
380
+ return bytes + ' byte';
381
+ }
382
+ return '0 bytes';
383
+ }
384
+ }
385
+
386
+ class HttpWrapper {
387
+ http;
388
+ baseURL;
389
+ constructor(http, baseURL) {
390
+ this.http = http;
391
+ this.baseURL = baseURL;
392
+ }
393
+ addCredentials(options = {}) {
394
+ return {
395
+ withCredentials: true,
396
+ ...options,
397
+ };
398
+ }
399
+ async get(url = '', params) {
400
+ const options = {
401
+ withCredentials: true,
402
+ params: {
403
+ ...params,
404
+ },
405
+ };
406
+ return firstValueFrom(this.http.get(this.baseURL + url, options));
407
+ }
408
+ async post(url, body = {}, options = {}) {
409
+ return firstValueFrom(this.http.post(this.baseURL + url, body, this.addCredentials(options)));
410
+ }
411
+ async put(url, body, options = {}) {
412
+ return firstValueFrom(this.http.put(this.baseURL + url, body, this.addCredentials(options)));
413
+ }
414
+ async delete(url, options = {}) {
415
+ return firstValueFrom(this.http.delete(this.baseURL + url, this.addCredentials(options)));
416
+ }
417
+ upload(url, body, options = {}) {
418
+ const req = new HttpRequest('POST', this.baseURL + url, body, {
419
+ reportProgress: true,
420
+ ...this.addCredentials(options)
421
+ });
422
+ return this.http.request(req);
423
+ }
424
+ async search(params = {}) {
425
+ return await this.get('/search', params);
426
+ }
427
+ async detail(params) {
428
+ return await this.get(`/detail`, params);
429
+ }
430
+ async remove(params = {}) {
431
+ return await this.delete(`/remove`, params);
432
+ }
433
+ async create(data) {
434
+ return await this.post(`/create`, data);
435
+ }
436
+ async updateInfo(data, params) {
437
+ return await this.put(`/update-info`, data, { params: params });
438
+ }
439
+ async updateStatus(data, params) {
440
+ return await this.put(`/update-status`, data, { params: params });
441
+ }
442
+ async update(data, params) {
443
+ return await this.put(`/update`, data, { params: params });
444
+ }
445
+ async single(code) {
446
+ return await this.post(`/${code}`, {});
447
+ }
448
+ async selectize() {
449
+ return await this.post('/search', {});
450
+ }
451
+ async fetch(filter = {}, params = {}) {
452
+ return await this.post('', filter, { params: params });
453
+ }
454
+ async published(idOrCode) {
455
+ return await this.post(`/published/${idOrCode}`);
456
+ }
457
+ async unpublished(idOrCode) {
458
+ return await this.post(`/unpublished/${idOrCode}`);
459
+ }
460
+ async terminated(idOrCode) {
461
+ return await this.post(`/terminated/${idOrCode}`);
462
+ }
463
+ async disable(params) {
464
+ return await this.post(`/disable`, params);
465
+ }
466
+ }
467
+
468
+ class BaseTableComponent extends BaseComponent {
469
+ api;
470
+ modal;
471
+ ref;
472
+ dataSource = new MatTableDataSource();
473
+ cols = [];
474
+ total = 0;
475
+ page = 0;
476
+ pageSize = 10;
477
+ filters = {};
478
+ loading = false;
479
+ expandedElement;
480
+ toastr;
481
+ _lock = false;
482
+ keywordChange$ = new Subject();
483
+ dataSourcePlaceholder;
484
+ config;
485
+ paginator;
486
+ table;
487
+ constructor(injector, api, modal, ref) {
488
+ super(injector);
489
+ this.api = api;
490
+ this.modal = modal;
491
+ this.ref = ref;
492
+ this.toastr = injector.get(FxToastrService);
493
+ }
494
+ ngOnInit() {
495
+ this.keywordChange$.pipe(debounceTime(300)).subscribe(async (keyword) => {
496
+ await this.refresh();
497
+ });
498
+ this.dataSourcePlaceholder = Array(this.pageSize).fill({});
499
+ this.init();
500
+ }
501
+ init() { }
502
+ ngAfterViewInit() {
503
+ if (this.paginator && this.paginator.page) {
504
+ this.paginator.page.subscribe(event => {
505
+ this.onPageChange(event);
506
+ });
507
+ }
508
+ this.ref.detectChanges();
509
+ }
510
+ async refresh() {
511
+ this.page = 0;
512
+ await this.fetch();
513
+ }
514
+ setDataSource(result) {
515
+ this.dataSource.data = _.get(result, 'data');
516
+ this.total = _.get(result, 'total');
517
+ }
518
+ async fetch() {
519
+ this.loading = true;
520
+ try {
521
+ const params = { page: (this.page ?? 0) + 1, pageSize: this.pageSize, ...this.filters };
522
+ const result = await this.api.search(params);
523
+ this.setDataSource(result);
524
+ }
525
+ catch (err) {
526
+ this.toastr.error(FxUtils.parseError(err));
527
+ }
528
+ finally {
529
+ this.loading = false;
530
+ this.ref.detectChanges();
531
+ }
532
+ }
533
+ async onPageChange(event) {
534
+ this.page = event.pageIndex;
535
+ this.pageSize = event.pageSize;
536
+ await this.fetch();
537
+ }
538
+ resolve() {
539
+ return {};
540
+ }
541
+ empty() {
542
+ return {};
543
+ }
544
+ onKeywordChange(keyword) {
545
+ this.keywordChange$.next(keyword);
546
+ }
547
+ async create() {
548
+ const conf = _.get(this.config, 'create');
549
+ const dialog = _.get(conf, 'dialog');
550
+ const size = _.get(conf, 'size');
551
+ if (_.isNil(conf) || _.isNil(dialog)) {
552
+ return;
553
+ }
554
+ if (this._lock) {
555
+ return;
556
+ }
557
+ this._lock = true;
558
+ let panelClass = 'fx-dialog';
559
+ if (size == '100%') {
560
+ panelClass = 'fx-fullsize-dialog';
561
+ }
562
+ let instance = this.modal.open(dialog, {
563
+ panelClass: panelClass,
564
+ autoFocus: false,
565
+ width: size,
566
+ disableClose: true,
567
+ data: {
568
+ method: 'create',
569
+ data: this.empty(),
570
+ title: conf.title,
571
+ message: conf.message,
572
+ resolve: this.resolve(),
573
+ },
574
+ });
575
+ instance.afterClosed().subscribe((result) => {
576
+ if (result) {
577
+ this.refresh();
578
+ }
579
+ this._lock = false;
580
+ });
581
+ }
582
+ async update(model) {
583
+ const conf = _.get(this.config, 'update');
584
+ const dialog = _.get(conf, 'dialog');
585
+ const size = _.get(conf, 'size');
586
+ if (_.isNil(dialog) || _.isNil(conf)) {
587
+ return;
588
+ }
589
+ if (this._lock) {
590
+ return;
591
+ }
592
+ this._lock = true;
593
+ let panelClass = 'fx-dialog';
594
+ if (size == '100%') {
595
+ panelClass = 'fx-fullsize-dialog';
596
+ }
597
+ let instance = this.modal.open(dialog, {
598
+ panelClass: panelClass,
599
+ autoFocus: false,
600
+ width: size,
601
+ disableClose: true,
602
+ data: {
603
+ method: 'update',
604
+ data: model,
605
+ title: conf.title,
606
+ message: conf.message,
607
+ resolve: this.resolve(),
608
+ },
609
+ });
610
+ instance.afterClosed().subscribe((result) => {
611
+ if (result) {
612
+ this.refresh();
613
+ }
614
+ this._lock = false;
615
+ });
616
+ }
617
+ async view(model) {
618
+ const conf = _.get(this.config, 'view');
619
+ const dialog = _.get(conf, 'dialog');
620
+ const size = _.get(conf, 'size');
621
+ if (_.isNil(dialog) || _.isNil(conf)) {
622
+ return;
623
+ }
624
+ if (this._lock) {
625
+ return;
626
+ }
627
+ this._lock = true;
628
+ let panelClass = 'fx-dialog';
629
+ if (size == '100%') {
630
+ panelClass = 'fx-fullsize-dialog';
631
+ }
632
+ let instance = this.modal.open(dialog, {
633
+ panelClass: panelClass,
634
+ autoFocus: false,
635
+ width: size,
636
+ disableClose: true,
637
+ data: {
638
+ data: model,
639
+ title: conf.title,
640
+ message: conf.message,
641
+ resolve: this.resolve(),
642
+ disabled: true,
643
+ method: 'view',
644
+ },
645
+ });
646
+ instance.afterClosed().subscribe(() => {
647
+ this._lock = false;
648
+ });
649
+ }
650
+ async delete(model) {
651
+ if (this._lock) {
652
+ return;
653
+ }
654
+ this._lock = true;
655
+ try {
656
+ const conf = _.get(this.config, 'delete');
657
+ const dialog = _.get(conf, 'dialog');
658
+ const dialogRef = this.modal.open(dialog, {
659
+ panelClass: 'fx-dialog',
660
+ autoFocus: false,
661
+ width: '400px',
662
+ data: {
663
+ title: _.get(conf, 'title'),
664
+ message: _.get(conf, 'message'),
665
+ cancelLabel: _.get(conf, 'cancelTitle'),
666
+ confirmLabel: _.get(conf, 'confirmTitle'),
667
+ callback: async () => {
668
+ await this.remove(model);
669
+ },
670
+ },
671
+ });
672
+ const state = await firstValueFrom(dialogRef.afterClosed());
673
+ if (state) {
674
+ try {
675
+ await this.refresh();
676
+ }
677
+ catch (err) {
678
+ this.toastr.error(FxUtils.parseError(err));
679
+ }
680
+ }
681
+ }
682
+ finally {
683
+ this._lock = false;
684
+ }
685
+ }
686
+ async remove(model) {
687
+ try {
688
+ return await this.api.remove({ ...model });
689
+ }
690
+ catch (err) {
691
+ throw err;
692
+ }
693
+ }
694
+ tagClass(tag) {
695
+ return FxUtils.getTagClass(tag, true);
696
+ }
697
+ capitalize(label) {
698
+ return _.capitalize(label);
699
+ }
700
+ value(model, key, defaultValue) {
701
+ return _.get(model, key, defaultValue);
702
+ }
703
+ toggleExpand(element) {
704
+ this.expandedElement = this.expandedElement === element ? null : element;
705
+ }
706
+ async updateStatus(params) {
707
+ try {
708
+ await this.api.updateStatus(params);
709
+ this.toastr.success(_.get(this.config, 'updateStatus.message', 'Update Success'));
710
+ }
711
+ catch (err) {
712
+ this.toastr.success(FxUtils.parseError(err));
713
+ }
714
+ finally {
715
+ await this.refresh();
716
+ }
717
+ }
718
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: BaseTableComponent, deps: "invalid", target: i0.ɵɵFactoryTarget.Directive });
719
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.4", type: BaseTableComponent, isStandalone: true, viewQueries: [{ propertyName: "paginator", first: true, predicate: MatPaginator, descendants: true }, { propertyName: "table", first: true, predicate: MatTable, descendants: true }], usesInheritance: true, ngImport: i0 });
720
+ }
721
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: BaseTableComponent, decorators: [{
722
+ type: Directive
723
+ }], ctorParameters: () => [{ type: i0.Injector }, { type: HttpWrapper }, { type: undefined }, { type: i0.ChangeDetectorRef }], propDecorators: { paginator: [{
724
+ type: ViewChild,
725
+ args: [MatPaginator]
726
+ }], table: [{
727
+ type: ViewChild,
728
+ args: [MatTable]
729
+ }] } });
730
+
731
+ class BaseDialogComponent extends BaseComponent {
732
+ ref;
733
+ bindings;
734
+ toastr;
735
+ api;
736
+ dialog;
737
+ ngForm;
738
+ method;
739
+ title;
740
+ model;
741
+ disabled;
742
+ loading;
743
+ message;
744
+ resolve;
745
+ formChangesSubscription;
746
+ onSubmit = true;
747
+ constructor(ref, bindings, toastr, api, dialog) {
748
+ super(bindings);
749
+ this.ref = ref;
750
+ this.bindings = bindings;
751
+ this.toastr = toastr;
752
+ this.api = api;
753
+ this.dialog = dialog;
754
+ this.model = _.clone(_.get(bindings, 'data'));
755
+ this.method = _.get(bindings, 'method');
756
+ this.title = _.get(bindings, 'title');
757
+ this.disabled = _.get(bindings, 'disabled');
758
+ this.message = _.get(bindings, 'message');
759
+ this.resolve = _.get(bindings, 'resolve');
760
+ }
761
+ ngAfterViewInit() { }
762
+ ngOnInit() { }
763
+ accept(form, params = null) {
764
+ if (form.invalid) {
765
+ FxUtils.checkInvalidField(form);
766
+ return;
767
+ }
768
+ if (!this.disabled) {
769
+ if (!this.validate())
770
+ return;
771
+ if (this.method === 'create') {
772
+ this.create(params);
773
+ return;
774
+ }
775
+ else {
776
+ this.update(params);
777
+ return;
778
+ }
779
+ }
780
+ this.cancel();
781
+ }
782
+ create(params = null) {
783
+ if (this.loading) {
784
+ return;
785
+ }
786
+ this.loading = true;
787
+ this.api
788
+ .create({ ...this.model, ...params })
789
+ .then(() => {
790
+ this.toastr.success(this.message);
791
+ this.ref.close(true);
792
+ }, (error) => {
793
+ this.toastr.error(FxUtils.parseError(error));
794
+ })
795
+ .then(() => {
796
+ this.loading = false;
797
+ });
798
+ }
799
+ update(params = null) {
800
+ if (this.loading) {
801
+ return;
802
+ }
803
+ this.loading = true;
804
+ this.api
805
+ .update({ ...this.model, ...params })
806
+ .then(() => {
807
+ this.toastr.success(this.message);
808
+ this.ref.close(true);
809
+ }, (error) => {
810
+ this.toastr.error(FxUtils.parseError(error));
811
+ })
812
+ .then(() => {
813
+ this.loading = false;
814
+ });
815
+ }
816
+ updateInfo(params = null) {
817
+ if (this.loading) {
818
+ return;
819
+ }
820
+ this.loading = true;
821
+ this.api
822
+ .updateInfo({ ...this.model, ...params })
823
+ .then(() => {
824
+ this.toastr.success(this.message);
825
+ this.ref.close(true);
826
+ }, (error) => {
827
+ this.toastr.error(error);
828
+ })
829
+ .then(() => {
830
+ this.loading = false;
831
+ });
832
+ }
833
+ cancel() {
834
+ this.ref.close(false);
835
+ }
836
+ validate() {
837
+ return true;
838
+ }
839
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: BaseDialogComponent, deps: [{ token: i1$1.MatDialogRef }, { token: MAT_DIALOG_DATA }, { token: FxToastrService }, { token: HttpWrapper }, { token: i1$1.MatDialog }], target: i0.ɵɵFactoryTarget.Directive });
840
+ 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 });
841
+ }
842
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: BaseDialogComponent, decorators: [{
843
+ type: Directive
844
+ }], ctorParameters: () => [{ type: i1$1.MatDialogRef }, { type: undefined, decorators: [{
845
+ type: Inject,
846
+ args: [MAT_DIALOG_DATA]
847
+ }] }, { type: FxToastrService }, { type: HttpWrapper }, { type: i1$1.MatDialog }], propDecorators: { ngForm: [{
848
+ type: ViewChild,
849
+ args: ['f', { static: true }]
850
+ }] } });
851
+
852
+ class FxLoadingService {
853
+ listener = new EventEmitter();
854
+ listenerAction = new EventEmitter();
855
+ start() {
856
+ this.listener.emit(true);
857
+ }
858
+ stop() {
859
+ this.listener.emit(false);
860
+ }
861
+ startAction() {
862
+ this.listenerAction.emit(true);
863
+ }
864
+ stopAction() {
865
+ this.listenerAction.emit(false);
866
+ }
867
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: FxLoadingService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
868
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: FxLoadingService, providedIn: 'root' });
869
+ }
870
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: FxLoadingService, decorators: [{
871
+ type: Injectable,
872
+ args: [{
873
+ providedIn: 'root'
874
+ }]
875
+ }] });
876
+
877
+ class BaseResolver {
878
+ loadingService;
879
+ injector;
880
+ constructor(loadingService, injector) {
881
+ this.loadingService = loadingService;
882
+ this.injector = injector;
883
+ }
884
+ baseResolverInit() {
885
+ this.loadingService.start();
886
+ }
887
+ }
888
+
889
+ function HttpLoaderFactory(http) {
890
+ return new TranslateHttpLoader(http, '/assets/i18n/', '.json');
891
+ }
892
+ class TranslationModule {
893
+ static forRoot() {
894
+ return {
895
+ ngModule: TranslationModule,
896
+ providers: [
897
+ {
898
+ provide: TranslateLoader,
899
+ useFactory: HttpLoaderFactory,
900
+ deps: [HttpClient]
901
+ }
902
+ ]
903
+ };
904
+ }
905
+ static forChild() {
906
+ return {
907
+ ngModule: TranslationModule,
908
+ providers: []
909
+ };
910
+ }
911
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: TranslationModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
912
+ static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.4", ngImport: i0, type: TranslationModule, imports: [TranslateModule], exports: [TranslateModule] });
913
+ static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: TranslationModule, imports: [TranslateModule, TranslateModule] });
914
+ }
915
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: TranslationModule, decorators: [{
916
+ type: NgModule,
917
+ args: [{
918
+ imports: [TranslateModule],
919
+ exports: [TranslateModule]
920
+ }]
921
+ }] });
922
+
923
+ class TranslationService {
924
+ translate;
925
+ http;
926
+ currentLangSubject = new BehaviorSubject('en');
927
+ currentLang$ = this.currentLangSubject.asObservable();
928
+ constructor(translate, http) {
929
+ this.translate = translate;
930
+ this.http = http;
931
+ // Default fallback
932
+ this.translate.addLangs(['en', 'vi']);
933
+ this.translate.setDefaultLang('en');
934
+ }
935
+ init(lang) {
936
+ this.setLanguage(lang);
937
+ }
938
+ currentLang() {
939
+ return this.translate.currentLang || this.translate.defaultLang;
940
+ }
941
+ setLanguage(lang) {
942
+ this.translate.use(lang);
943
+ this.currentLangSubject.next(lang);
944
+ }
945
+ loadTranslations(lang, path) {
946
+ const url = `${path}${lang}.json`;
947
+ this.http.get(url).subscribe({
948
+ next: (translations) => {
949
+ this.translate.setTranslation(lang, translations, true);
950
+ },
951
+ error: (err) => {
952
+ console.warn(`[TranslationService] Failed to load ${url}`, err);
953
+ }
954
+ });
955
+ }
956
+ onLanguageChange(callback) {
957
+ this.currentLang$.subscribe(callback);
958
+ }
959
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: TranslationService, deps: [{ token: i1$2.TranslateService }, { token: i2$1.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable });
960
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: TranslationService, providedIn: 'root' });
961
+ }
962
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: TranslationService, decorators: [{
963
+ type: Injectable,
964
+ args: [{ providedIn: 'root' }]
965
+ }], ctorParameters: () => [{ type: i1$2.TranslateService }, { type: i2$1.HttpClient }] });
966
+
967
+ class PermissionService {
968
+ permissions = [];
969
+ setPermissions(perms) {
970
+ this.permissions = perms;
971
+ }
972
+ hasPermission(requiredPermissions) {
973
+ return _.some(requiredPermissions, permission => this.permissions.includes(permission));
974
+ }
975
+ getPermissions() {
976
+ return [...this.permissions];
977
+ }
978
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: PermissionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
979
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: PermissionService, providedIn: 'root' });
980
+ }
981
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: PermissionService, decorators: [{
982
+ type: Injectable,
983
+ args: [{
984
+ providedIn: 'root'
985
+ }]
986
+ }] });
987
+
988
+ class HasPermissionDirective {
989
+ templateRef;
990
+ viewContainerRef;
991
+ permissionService;
992
+ constructor(templateRef, viewContainerRef, permissionService) {
993
+ this.templateRef = templateRef;
994
+ this.viewContainerRef = viewContainerRef;
995
+ this.permissionService = permissionService;
996
+ }
997
+ set hasPermission(required) {
998
+ const requiredPermissions = _.isArray(required) ? required : [required];
999
+ this.viewContainerRef.clear();
1000
+ if (this.permissionService.hasPermission(requiredPermissions)) {
1001
+ this.viewContainerRef.createEmbeddedView(this.templateRef);
1002
+ }
1003
+ }
1004
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: HasPermissionDirective, deps: [{ token: i0.TemplateRef }, { token: i0.ViewContainerRef }, { token: PermissionService }], target: i0.ɵɵFactoryTarget.Directive });
1005
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.4", type: HasPermissionDirective, isStandalone: true, selector: "[hasPermission]", inputs: { hasPermission: "hasPermission" }, ngImport: i0 });
1006
+ }
1007
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: HasPermissionDirective, decorators: [{
1008
+ type: Directive,
1009
+ args: [{
1010
+ selector: '[hasPermission]'
1011
+ }]
1012
+ }], ctorParameters: () => [{ type: i0.TemplateRef }, { type: i0.ViewContainerRef }, { type: PermissionService }], propDecorators: { hasPermission: [{
1013
+ type: Input
1014
+ }] } });
1015
+
1016
+ class TrimOnBlurDirective {
1017
+ el;
1018
+ ngControl;
1019
+ collapseWhitespace = false;
1020
+ constructor(el, ngControl) {
1021
+ this.el = el;
1022
+ this.ngControl = ngControl;
1023
+ }
1024
+ onBlur() {
1025
+ const native = this.el.nativeElement;
1026
+ if (!native)
1027
+ return;
1028
+ let value = native.value ?? '';
1029
+ if (this.collapseWhitespace) {
1030
+ value = value.replace(/\s+/g, ' ').trim();
1031
+ }
1032
+ else {
1033
+ value = value.trim();
1034
+ }
1035
+ if (native.value === value)
1036
+ return;
1037
+ native.value = value;
1038
+ if (this.ngControl && this.ngControl.control) {
1039
+ this.ngControl.control.setValue(value);
1040
+ }
1041
+ else {
1042
+ const ev = new Event('input', { bubbles: true });
1043
+ native.dispatchEvent(ev);
1044
+ }
1045
+ }
1046
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: TrimOnBlurDirective, deps: [{ token: i0.ElementRef }, { token: i1$3.NgControl }], target: i0.ɵɵFactoryTarget.Directive });
1047
+ 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 });
1048
+ }
1049
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: TrimOnBlurDirective, decorators: [{
1050
+ type: Directive,
1051
+ args: [{
1052
+ selector: '[appTrimOnBlur]'
1053
+ }]
1054
+ }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i1$3.NgControl }], propDecorators: { collapseWhitespace: [{
1055
+ type: Input,
1056
+ args: ['appTrimOnBlurCollapse']
1057
+ }], onBlur: [{
1058
+ type: HostListener,
1059
+ args: ['blur']
1060
+ }] } });
1061
+
1062
+ class PermissionGuard {
1063
+ permissionService;
1064
+ router;
1065
+ constructor(permissionService, router) {
1066
+ this.permissionService = permissionService;
1067
+ this.router = router;
1068
+ }
1069
+ canActivate(route) {
1070
+ const requiredPermissions = route.data?.['permissions'];
1071
+ if (!requiredPermissions || this.permissionService.hasPermission(requiredPermissions)) {
1072
+ return true;
1073
+ }
1074
+ this.router.navigate(['/unauthorized']);
1075
+ return false;
1076
+ }
1077
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: PermissionGuard, deps: [{ token: PermissionService }, { token: i1$4.Router }], target: i0.ɵɵFactoryTarget.Injectable });
1078
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: PermissionGuard, providedIn: 'root' });
1079
+ }
1080
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: PermissionGuard, decorators: [{
1081
+ type: Injectable,
1082
+ args: [{
1083
+ providedIn: 'root',
1084
+ }]
1085
+ }], ctorParameters: () => [{ type: PermissionService }, { type: i1$4.Router }] });
1086
+
1087
+ class FxStorageService {
1088
+ resolve(key, value) {
1089
+ if (FxUtils.isNotNull(value)) {
1090
+ localStorage.setItem(key, JSON.stringify(value));
1091
+ }
1092
+ const loc = localStorage.getItem(key);
1093
+ return loc ? JSON.parse(loc) : undefined;
1094
+ }
1095
+ remove(key) {
1096
+ localStorage.removeItem(key);
1097
+ }
1098
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: FxStorageService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1099
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: FxStorageService, providedIn: 'root' });
1100
+ }
1101
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: FxStorageService, decorators: [{
1102
+ type: Injectable,
1103
+ args: [{
1104
+ providedIn: 'root',
1105
+ }]
1106
+ }] });
1107
+
1108
+ class AuthStateService {
1109
+ _isLoggedIn$ = new BehaviorSubject(null);
1110
+ isLoggedIn$ = this._isLoggedIn$.asObservable();
1111
+ unauthorized$ = new Subject();
1112
+ constructor() { }
1113
+ setLoggedIn(value) {
1114
+ this._isLoggedIn$.next(value);
1115
+ }
1116
+ get isLoggedIn() {
1117
+ return this._isLoggedIn$.value === true;
1118
+ }
1119
+ handleUnauthorized() {
1120
+ this.setLoggedIn(false);
1121
+ this.unauthorized$.next();
1122
+ }
1123
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: AuthStateService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1124
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: AuthStateService, providedIn: 'root' });
1125
+ }
1126
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: AuthStateService, decorators: [{
1127
+ type: Injectable,
1128
+ args: [{ providedIn: 'root' }]
1129
+ }], ctorParameters: () => [] });
1130
+
1131
+ class AuthInterceptor {
1132
+ authState;
1133
+ constructor(authState) {
1134
+ this.authState = authState;
1135
+ }
1136
+ intercept(req, next) {
1137
+ const cloned = req.clone({ withCredentials: true });
1138
+ return next.handle(cloned).pipe(catchError((error) => {
1139
+ if (error.status === 401) {
1140
+ this.authState.handleUnauthorized();
1141
+ }
1142
+ return throwError(() => error);
1143
+ }));
1144
+ }
1145
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: AuthInterceptor, deps: [{ token: AuthStateService }], target: i0.ɵɵFactoryTarget.Injectable });
1146
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: AuthInterceptor });
1147
+ }
1148
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: AuthInterceptor, decorators: [{
1149
+ type: Injectable
1150
+ }], ctorParameters: () => [{ type: AuthStateService }] });
1151
+
1152
+ class BreadcrumbService {
1153
+ breadcrumbs$ = new BehaviorSubject([]);
1154
+ set(breadcrumbs) {
1155
+ this.breadcrumbs$.next(breadcrumbs);
1156
+ }
1157
+ get() {
1158
+ return this.breadcrumbs$.asObservable();
1159
+ }
1160
+ clear() {
1161
+ this.breadcrumbs$.next([]);
1162
+ }
1163
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: BreadcrumbService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1164
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: BreadcrumbService, providedIn: 'root' });
1165
+ }
1166
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: BreadcrumbService, decorators: [{
1167
+ type: Injectable,
1168
+ args: [{
1169
+ providedIn: 'root'
1170
+ }]
1171
+ }] });
1172
+
1173
+ class QuillStyleLoaderService {
1174
+ platformId;
1175
+ loaded = false;
1176
+ loadingPromise = null;
1177
+ constructor(platformId) {
1178
+ this.platformId = platformId;
1179
+ }
1180
+ /**
1181
+ * Load Quill CSS (only in browser). Returns a promise that resolves when
1182
+ * the CSS links have been appended.
1183
+ */
1184
+ load() {
1185
+ if (!isPlatformBrowser(this.platformId)) {
1186
+ // Server: do nothing and resolve immediately.
1187
+ return Promise.resolve();
1188
+ }
1189
+ if (this.loaded)
1190
+ return Promise.resolve();
1191
+ if (this.loadingPromise)
1192
+ return this.loadingPromise;
1193
+ this.loadingPromise = new Promise((resolve) => {
1194
+ const cssUrls = [
1195
+ // Using CDN is simplest — no consumer angular.json edits needed.
1196
+ 'https://cdn.jsdelivr.net/npm/quill/dist/quill.core.css',
1197
+ 'https://cdn.jsdelivr.net/npm/quill/dist/quill.snow.css'
1198
+ ];
1199
+ let remaining = cssUrls.length;
1200
+ const onLoad = () => {
1201
+ remaining -= 1;
1202
+ if (remaining === 0) {
1203
+ this.loaded = true;
1204
+ resolve();
1205
+ }
1206
+ };
1207
+ cssUrls.forEach((href) => {
1208
+ // Do not add duplicate links
1209
+ if (document.querySelector(`link[href="${href}"]`)) {
1210
+ onLoad();
1211
+ return;
1212
+ }
1213
+ const link = document.createElement('link');
1214
+ link.rel = 'stylesheet';
1215
+ link.href = href;
1216
+ link.onload = () => onLoad();
1217
+ link.onerror = () => {
1218
+ // still count it as loaded to avoid hanging if CDN fails
1219
+ console.warn(`[QuillStyleLoader] failed to load: ${href}`);
1220
+ onLoad();
1221
+ };
1222
+ document.head.appendChild(link);
1223
+ });
1224
+ });
1225
+ return this.loadingPromise;
1226
+ }
1227
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: QuillStyleLoaderService, deps: [{ token: PLATFORM_ID }], target: i0.ɵɵFactoryTarget.Injectable });
1228
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: QuillStyleLoaderService, providedIn: 'root' });
1229
+ }
1230
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: QuillStyleLoaderService, decorators: [{
1231
+ type: Injectable,
1232
+ args: [{ providedIn: 'root' }]
1233
+ }], ctorParameters: () => [{ type: Object, decorators: [{
1234
+ type: Inject,
1235
+ args: [PLATFORM_ID]
1236
+ }] }] });
1237
+
1238
+ class ThemeService {
1239
+ renderer;
1240
+ lightClass = 'light_theme';
1241
+ theme$ = new BehaviorSubject('dark');
1242
+ themeStorageKey = 'host-theme';
1243
+ constructor(rendererFactory) {
1244
+ this.renderer = rendererFactory.createRenderer(null, null);
1245
+ const savedTheme = localStorage.getItem(this.themeStorageKey);
1246
+ if (savedTheme) {
1247
+ this.setLightMode(savedTheme === 'light');
1248
+ }
1249
+ else {
1250
+ const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
1251
+ const isDark = document.body.classList.contains(this.lightClass) || prefersDark;
1252
+ this.setLightMode(isDark);
1253
+ }
1254
+ }
1255
+ setLightMode(enable) {
1256
+ if (enable) {
1257
+ this.renderer.addClass(document.body, this.lightClass);
1258
+ this.theme$.next('light');
1259
+ localStorage.setItem(this.themeStorageKey, 'light');
1260
+ }
1261
+ else {
1262
+ this.renderer.removeClass(document.body, this.lightClass);
1263
+ this.theme$.next('dark');
1264
+ localStorage.setItem(this.themeStorageKey, 'dark');
1265
+ }
1266
+ window.dispatchEvent(new CustomEvent('theme-changed', { detail: { dark: !enable } }));
1267
+ }
1268
+ toggleTheme() {
1269
+ const isLight = this.theme$.value === 'light';
1270
+ this.setLightMode(!isLight);
1271
+ }
1272
+ themeChanges() {
1273
+ return this.theme$.asObservable();
1274
+ }
1275
+ getCurrentTheme() {
1276
+ return this.theme$.value;
1277
+ }
1278
+ isLight() {
1279
+ return this.theme$.value === 'light';
1280
+ }
1281
+ getLogoPath() {
1282
+ return 'assets/images/logo.png';
1283
+ }
1284
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: ThemeService, deps: [{ token: i0.RendererFactory2 }], target: i0.ɵɵFactoryTarget.Injectable });
1285
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: ThemeService, providedIn: 'root' });
1286
+ }
1287
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: ThemeService, decorators: [{
1288
+ type: Injectable,
1289
+ args: [{ providedIn: 'root' }]
1290
+ }], ctorParameters: () => [{ type: i0.RendererFactory2 }] });
1291
+
1292
+ class NotificationService {
1293
+ hubConnection = null;
1294
+ connectionStatus = 'disconnected';
1295
+ constructor() { }
1296
+ /**
1297
+ * Connect to SignalR Hub
1298
+ * @param hubUrl - The URL of the SignalR hub
1299
+ * @returns Promise that resolves when connected
1300
+ */
1301
+ async connect(hubUrl, options) {
1302
+ if (this.hubConnection && this.connectionStatus === 'connected') {
1303
+ console.log('Already connected to SignalR hub');
1304
+ return;
1305
+ }
1306
+ try {
1307
+ this.connectionStatus = 'connecting';
1308
+ console.log(`Connecting to SignalR hub: ${hubUrl}`);
1309
+ const connectionOptions = {
1310
+ skipNegotiation: true,
1311
+ transport: signalR.HttpTransportType.WebSockets,
1312
+ withCredentials: true, // Include cookies if any
1313
+ ...options
1314
+ };
1315
+ this.hubConnection = new signalR.HubConnectionBuilder()
1316
+ .withUrl(hubUrl, connectionOptions)
1317
+ .withAutomaticReconnect()
1318
+ .configureLogging(signalR.LogLevel.Information)
1319
+ .build();
1320
+ // Handle connection closed
1321
+ this.hubConnection.onclose((error) => {
1322
+ this.connectionStatus = 'disconnected';
1323
+ if (error) {
1324
+ console.error('SignalR connection closed with error:', error);
1325
+ }
1326
+ else {
1327
+ console.log('SignalR connection closed');
1328
+ }
1329
+ });
1330
+ // Handle reconnecting
1331
+ this.hubConnection.onreconnecting((error) => {
1332
+ this.connectionStatus = 'connecting';
1333
+ console.log('SignalR reconnecting...', error);
1334
+ });
1335
+ // Handle reconnected
1336
+ this.hubConnection.onreconnected((connectionId) => {
1337
+ this.connectionStatus = 'connected';
1338
+ console.log('SignalR reconnected. Connection ID:', connectionId);
1339
+ });
1340
+ await this.hubConnection.start();
1341
+ this.connectionStatus = 'connected';
1342
+ console.log('SignalR connected successfully. Connection ID:', this.hubConnection.connectionId);
1343
+ }
1344
+ catch (error) {
1345
+ this.connectionStatus = 'disconnected';
1346
+ console.error('Error connecting to SignalR hub:', error);
1347
+ throw error;
1348
+ }
1349
+ }
1350
+ /**
1351
+ * Disconnect from SignalR Hub
1352
+ */
1353
+ async disconnect() {
1354
+ if (this.hubConnection) {
1355
+ try {
1356
+ await this.hubConnection.stop();
1357
+ this.connectionStatus = 'disconnected';
1358
+ console.log('SignalR disconnected successfully');
1359
+ }
1360
+ catch (error) {
1361
+ console.error('Error disconnecting from SignalR hub:', error);
1362
+ }
1363
+ }
1364
+ }
1365
+ /**
1366
+ * Listen to a specific event from the server
1367
+ * @param eventName - Name of the event to listen to
1368
+ * @param callback - Callback function to execute when event is received
1369
+ */
1370
+ on(eventName, callback) {
1371
+ if (!this.hubConnection) {
1372
+ console.error('Cannot listen to events: not connected to hub');
1373
+ return;
1374
+ }
1375
+ console.log(`Registering listener for event: ${eventName}`);
1376
+ this.hubConnection.on(eventName, callback);
1377
+ }
1378
+ /**
1379
+ * Remove listener for a specific event
1380
+ * @param eventName - Name of the event to stop listening to
1381
+ */
1382
+ off(eventName) {
1383
+ if (!this.hubConnection) {
1384
+ return;
1385
+ }
1386
+ console.log(`Removing listener for event: ${eventName}`);
1387
+ this.hubConnection.off(eventName);
1388
+ }
1389
+ /**
1390
+ * Invoke a method on the server
1391
+ * @param methodName - Name of the method to invoke
1392
+ * @param args - Arguments to pass to the method
1393
+ */
1394
+ async invoke(methodName, ...args) {
1395
+ if (!this.hubConnection || this.connectionStatus !== 'connected') {
1396
+ throw new Error('Not connected to SignalR hub');
1397
+ }
1398
+ try {
1399
+ console.log(`Invoking method: ${methodName}`, args);
1400
+ return await this.hubConnection.invoke(methodName, ...args);
1401
+ }
1402
+ catch (error) {
1403
+ console.error(`Error invoking method ${methodName}:`, error);
1404
+ throw error;
1405
+ }
1406
+ }
1407
+ /**
1408
+ * Get current connection status
1409
+ */
1410
+ getConnectionStatus() {
1411
+ return this.connectionStatus;
1412
+ }
1413
+ /**
1414
+ * Get connection ID
1415
+ */
1416
+ getConnectionId() {
1417
+ return this.hubConnection?.connectionId ?? null;
1418
+ }
1419
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: NotificationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1420
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: NotificationService, providedIn: 'root' });
1421
+ }
1422
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: NotificationService, decorators: [{
1423
+ type: Injectable,
1424
+ args: [{
1425
+ providedIn: 'root'
1426
+ }]
1427
+ }], ctorParameters: () => [] });
1428
+
1429
+ const MATERIAL_MODULE = [
1430
+ MatSelectModule,
1431
+ MatRadioModule,
1432
+ MatButtonModule,
1433
+ MatIconModule,
1434
+ MatPaginatorModule,
1435
+ MatTableModule,
1436
+ MatSnackBarModule,
1437
+ MatDialogModule,
1438
+ MatCheckboxModule,
1439
+ MatCardModule,
1440
+ MatDatepickerModule,
1441
+ MatTimepickerModule,
1442
+ MatBadgeModule,
1443
+ MatExpansionModule,
1444
+ MatFormFieldModule,
1445
+ MatMenuModule,
1446
+ MatTooltipModule,
1447
+ DragDropModule,
1448
+ ];
1449
+
1450
+ class ButtonComponent {
1451
+ label;
1452
+ disabled = false;
1453
+ buttonVariant = 'alternative';
1454
+ icon;
1455
+ class;
1456
+ loading = false;
1457
+ clicked = new EventEmitter();
1458
+ onClick() {
1459
+ if (!this.disabled)
1460
+ this.clicked.emit();
1461
+ }
1462
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: ButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1463
+ 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-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) {<fx-ui-hero-icon [icon]=\"icon\" [size]=\"20\" class=\"flex\" [ngClass]=\"{\n 'text-text-inverse': buttonVariant !== 'default',\n 'text-text-primary': buttonVariant === 'alternative'\n }\"></fx-ui-hero-icon>}\n @if(label){<div>{{ label }}</div>}\n </div>\n </ng-container>\n }\n</button>\n", styles: [""], dependencies: [{ kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: HeroIconComponent, selector: "fx-ui-hero-icon", inputs: ["icon", "solid", "outline", "size", "color", "class"] }] });
1464
+ }
1465
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: ButtonComponent, decorators: [{
1466
+ type: Component,
1467
+ 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-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) {<fx-ui-hero-icon [icon]=\"icon\" [size]=\"20\" class=\"flex\" [ngClass]=\"{\n 'text-text-inverse': buttonVariant !== 'default',\n 'text-text-primary': buttonVariant === 'alternative'\n }\"></fx-ui-hero-icon>}\n @if(label){<div>{{ label }}</div>}\n </div>\n </ng-container>\n }\n</button>\n" }]
1468
+ }], propDecorators: { label: [{
1469
+ type: Input
1470
+ }], disabled: [{
1471
+ type: Input
1472
+ }], buttonVariant: [{
1473
+ type: Input
1474
+ }], icon: [{
1475
+ type: Input
1476
+ }], class: [{
1477
+ type: Input
1478
+ }], loading: [{
1479
+ type: Input
1480
+ }], clicked: [{
1481
+ type: Output
1482
+ }] } });
1483
+
1484
+ class ChartComponent {
1485
+ ref;
1486
+ cdr;
1487
+ /* ================= INPUTS ================= */
1488
+ type = 'auto';
1489
+ data;
1490
+ options;
1491
+ palette;
1492
+ height = '400px';
1493
+ loading = false;
1494
+ // pie controls
1495
+ pieLegendAlign = '10%';
1496
+ pieLegendPosition = 'middle';
1497
+ showPieLabel = true;
1498
+ mobileWidth = 350;
1499
+ labelFontFamily = 'BeVietnamPro';
1500
+ /* ================= OUTPUTS ================= */
1501
+ chartClick = new EventEmitter();
1502
+ chartInit = new EventEmitter();
1503
+ /* ================= INTERNAL ================= */
1504
+ chartOptions = {};
1505
+ labelFontSize = 12;
1506
+ chartInstance;
1507
+ resizeObserver;
1508
+ intersectionObserver;
1509
+ canInit = false;
1510
+ isMobile = false;
1511
+ constructor(ref, cdr) {
1512
+ this.ref = ref;
1513
+ this.cdr = cdr;
1514
+ }
1515
+ /* ================= LIFECYCLE ================= */
1516
+ ngAfterViewInit() {
1517
+ // Only initialize when visible (tab-safe)
1518
+ this.intersectionObserver = new IntersectionObserver(([entry]) => {
1519
+ if (entry.isIntersecting && !this.canInit) {
1520
+ this.canInit = true;
1521
+ this.updateResponsiveState();
1522
+ this.rebuildChart();
1523
+ this.cdr.detectChanges();
1524
+ }
1525
+ }, { threshold: 0.1 });
1526
+ this.intersectionObserver.observe(this.ref.nativeElement);
1527
+ // Resize-safe (tabs, flex, async layout)
1528
+ this.resizeObserver = new ResizeObserver(() => {
1529
+ if (!this.canInit)
1530
+ return;
1531
+ this.updateResponsiveState();
1532
+ this.forceMediaRecalc();
1533
+ });
1534
+ this.resizeObserver.observe(this.ref.nativeElement);
1535
+ }
1536
+ ngOnChanges() {
1537
+ if (this.canInit) {
1538
+ this.updateResponsiveState();
1539
+ this.rebuildChart();
1540
+ }
1541
+ }
1542
+ ngOnDestroy() {
1543
+ this.resizeObserver?.disconnect();
1544
+ this.intersectionObserver?.disconnect();
1545
+ }
1546
+ onThemeChange() {
1547
+ if (!this.canInit)
1548
+ return;
1549
+ requestAnimationFrame(() => this.rebuildChart());
1550
+ }
1551
+ /* ================= EVENTS ================= */
1552
+ onChartClick(event) {
1553
+ this.chartClick.emit(event);
1554
+ }
1555
+ onChartInit(chart) {
1556
+ this.chartInstance = chart;
1557
+ this.chartInit.emit(chart);
1558
+ requestAnimationFrame(() => this.forceMediaRecalc());
1559
+ }
1560
+ /* ================= CORE FIX ================= */
1561
+ updateResponsiveState() {
1562
+ const { width } = this.ref.nativeElement.getBoundingClientRect();
1563
+ if (width > 0) {
1564
+ this.isMobile = width < this.mobileWidth;
1565
+ }
1566
+ }
1567
+ forceMediaRecalc(retry = 0) {
1568
+ if (!this.chartInstance)
1569
+ return;
1570
+ const { width, height } = this.ref.nativeElement.getBoundingClientRect();
1571
+ if ((width === 0 || height === 0) && retry < 10) {
1572
+ requestAnimationFrame(() => this.forceMediaRecalc(retry + 1));
1573
+ return;
1574
+ }
1575
+ this.chartInstance.setOption(this.chartOptions, true);
1576
+ this.chartInstance.resize();
1577
+ }
1578
+ rebuildChart() {
1579
+ this.chartOptions = this.buildOptions();
1580
+ if (this.chartInstance) {
1581
+ this.chartInstance.clear();
1582
+ this.chartInstance.setOption(this.chartOptions, true);
1583
+ }
1584
+ }
1585
+ /* ================= REGISTRY ================= */
1586
+ registry = {
1587
+ bar: (data, colors) => ({
1588
+ color: colors,
1589
+ tooltip: { trigger: 'axis' },
1590
+ grid: { left: '5%', right: '5%', bottom: '10%', containLabel: true },
1591
+ xAxis: {
1592
+ type: 'category',
1593
+ data: data?.categories || [],
1594
+ axisLabel: {
1595
+ interval: 0,
1596
+ width: 80,
1597
+ overflow: 'truncate',
1598
+ ellipsis: '…',
1599
+ fontFamily: this.labelFontFamily,
1600
+ fontSize: this.labelFontSize,
1601
+ color: FxUtils.convertColorFromVariable('--text-secondary'),
1602
+ },
1603
+ axisLine: {
1604
+ lineStyle: {
1605
+ color: FxUtils.convertColorFromVariable('--border-strong'),
1606
+ },
1607
+ },
1608
+ },
1609
+ yAxis: {
1610
+ type: 'value',
1611
+ axisLabel: {
1612
+ fontFamily: this.labelFontFamily,
1613
+ fontSize: this.labelFontSize,
1614
+ color: FxUtils.convertColorFromVariable('--text-secondary'),
1615
+ },
1616
+ axisLine: {
1617
+ lineStyle: {
1618
+ color: FxUtils.convertColorFromVariable('--border-strong'),
1619
+ },
1620
+ },
1621
+ splitLine: {
1622
+ lineStyle: {
1623
+ color: FxUtils.convertColorFromVariable('--border-default'),
1624
+ },
1625
+ },
1626
+ },
1627
+ series: [
1628
+ {
1629
+ type: 'bar',
1630
+ data: data?.values || [],
1631
+ itemStyle: {
1632
+ color: (p) => colors[p.dataIndex % colors.length],
1633
+ },
1634
+ label: {
1635
+ show: true,
1636
+ position: 'top',
1637
+ fontFamily: this.labelFontFamily,
1638
+ fontSize: this.labelFontSize,
1639
+ color: FxUtils.convertColorFromVariable('--text-primary'),
1640
+ },
1641
+ },
1642
+ ],
1643
+ }),
1644
+ pie: (data, colors) => {
1645
+ const total = data.reduce((sum, d) => sum + d.value, 0);
1646
+ const isMobile = this.isMobile;
1647
+ return {
1648
+ color: colors,
1649
+ tooltip: { trigger: 'item' },
1650
+ legend: {
1651
+ orient: isMobile ? 'horizontal' : 'vertical',
1652
+ icon: 'circle',
1653
+ itemGap: 12,
1654
+ width: 160,
1655
+ // 🔑 layout switch
1656
+ right: isMobile ? undefined : this.pieLegendAlign,
1657
+ top: isMobile ? undefined : this.pieLegendPosition,
1658
+ left: isMobile ? 'center' : undefined,
1659
+ bottom: isMobile ? 0 : undefined,
1660
+ formatter: (name) => {
1661
+ const item = data.find((d) => d.name === name);
1662
+ return item ? `${name}: ${item.value}` : name;
1663
+ },
1664
+ textStyle: {
1665
+ fontFamily: this.labelFontFamily,
1666
+ fontSize: 13,
1667
+ fontWeight: 500,
1668
+ color: FxUtils.convertColorFromVariable('--text-primary'),
1669
+ overflow: 'truncate',
1670
+ },
1671
+ },
1672
+ series: [
1673
+ {
1674
+ type: 'pie',
1675
+ radius: ['40%', '70%'],
1676
+ center: isMobile ? ['50%', '40%'] : ['35%', '50%'],
1677
+ data,
1678
+ label: {
1679
+ show: this.showPieLabel,
1680
+ position: 'outside',
1681
+ formatter: '{d}%',
1682
+ fontSize: this.labelFontSize,
1683
+ fontFamily: this.labelFontFamily,
1684
+ color: FxUtils.convertColorFromVariable('--text-primary'),
1685
+ textBorderWidth: 0,
1686
+ textBorderColor: 'transparent',
1687
+ },
1688
+ labelLine: {
1689
+ show: this.showPieLabel,
1690
+ length: 7,
1691
+ length2: 7,
1692
+ },
1693
+ },
1694
+ ],
1695
+ graphic: {
1696
+ type: 'group',
1697
+ left: isMobile ? '50%' : '35%',
1698
+ top: isMobile ? '40%' : '50%',
1699
+ bounding: 'raw',
1700
+ children: [
1701
+ {
1702
+ type: 'text',
1703
+ style: {
1704
+ text: total.toString(),
1705
+ fontSize: 24,
1706
+ fontWeight: 600,
1707
+ fontFamily: this.labelFontFamily,
1708
+ fill: FxUtils.convertColorFromVariable('--text-primary'),
1709
+ align: 'center',
1710
+ verticalAlign: 'middle',
1711
+ },
1712
+ },
1713
+ ],
1714
+ },
1715
+ };
1716
+ },
1717
+ sunburst: (data, colors) => ({
1718
+ color: colors,
1719
+ series: [
1720
+ {
1721
+ type: 'sunburst',
1722
+ data,
1723
+ radius: ['15%', '90%'],
1724
+ itemStyle: {
1725
+ borderWidth: 1,
1726
+ borderColor: FxUtils.convertColorFromVariable('--border-default'),
1727
+ },
1728
+ label: {
1729
+ rotate: 'tangential',
1730
+ fontSize: this.labelFontSize,
1731
+ fontFamily: this.labelFontFamily,
1732
+ color: FxUtils.convertColorFromVariable('--text-primary'),
1733
+ textBorderWidth: 0,
1734
+ textBorderColor: 'transparent',
1735
+ },
1736
+ },
1737
+ ],
1738
+ }),
1739
+ };
1740
+ /* ================= BUILD ================= */
1741
+ detectType() {
1742
+ if (this.type !== 'auto')
1743
+ return this.type;
1744
+ if (this.data?.children)
1745
+ return 'sunburst';
1746
+ if (this.data?.categories && this.data?.values)
1747
+ return 'bar';
1748
+ if (Array.isArray(this.data))
1749
+ return 'pie';
1750
+ return 'bar';
1751
+ }
1752
+ buildOptions() {
1753
+ const type = this.detectType();
1754
+ const palette = this.resolvePalette();
1755
+ const base = this.registry[type]?.(this.data, palette) || {};
1756
+ return {
1757
+ ...base,
1758
+ ...this.options,
1759
+ };
1760
+ }
1761
+ resolvePalette() {
1762
+ const defaults = [
1763
+ 'rgba(var(--success) / 1)',
1764
+ 'rgba(var(--information) / 1)',
1765
+ 'rgba(var(--critical) / 1)',
1766
+ 'rgba(var(--danger) / 1)',
1767
+ 'rgba(var(--warning) / 1)',
1768
+ 'rgba(var(--discovery) / 1)',
1769
+ ];
1770
+ const palette = this.palette?.length ? this.palette : defaults;
1771
+ return palette.map((raw) => {
1772
+ if (!raw.includes('var('))
1773
+ return raw;
1774
+ const cssVar = raw.match(/var\((.*?)\)/)?.[1];
1775
+ const rgb = getComputedStyle(document.body)
1776
+ .getPropertyValue(cssVar || '')
1777
+ .trim()
1778
+ .replace(/\s+/g, ',');
1779
+ return `rgba(${rgb},1)`;
1780
+ });
1781
+ }
1782
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: ChartComponent, deps: [{ token: i0.ElementRef }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
1783
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.4", type: ChartComponent, isStandalone: false, selector: "fx-ui-chart", inputs: { type: "type", data: "data", options: "options", palette: "palette", height: "height", loading: "loading", pieLegendAlign: "pieLegendAlign", pieLegendPosition: "pieLegendPosition", showPieLabel: "showPieLabel", mobileWidth: "mobileWidth", labelFontFamily: "labelFontFamily" }, outputs: { chartClick: "chartClick", chartInit: "chartInit" }, host: { listeners: { "window:theme-changed": "onThemeChange()" } }, usesOnChanges: true, ngImport: i0, template: "<div class=\"chart-wrapper\" [style.height]=\"height\">\n <div\n echarts\n [options]=\"chartOptions\"\n [loading]=\"loading\"\n (chartInit)=\"onChartInit($event)\"\n (chartClick)=\"onChartClick($event)\"\n class=\"fx-chart\"\n [style.height]=\"height\"\n ></div>\n</div>\n", styles: [":host{display:block;width:100%}.chart-wrapper{position:relative;width:100%;height:100%}.chart-container{width:100%;height:100%}.chart-loader{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:rgba(var(--bg-primary-hover)/.6);font-weight:500;font-size:1.1rem;z-index:2;border-radius:4px}\n"], dependencies: [{ kind: "directive", type: i1$5.NgxEchartsDirective, selector: "echarts, [echarts]", inputs: ["options", "theme", "initOpts", "merge", "autoResize", "loading", "loadingType", "loadingOpts"], outputs: ["chartInit", "optionsError", "chartClick", "chartDblClick", "chartMouseDown", "chartMouseMove", "chartMouseUp", "chartMouseOver", "chartMouseOut", "chartGlobalOut", "chartContextMenu", "chartHighlight", "chartDownplay", "chartSelectChanged", "chartLegendSelectChanged", "chartLegendSelected", "chartLegendUnselected", "chartLegendLegendSelectAll", "chartLegendLegendInverseSelect", "chartLegendScroll", "chartDataZoom", "chartDataRangeSelected", "chartGraphRoam", "chartGeoRoam", "chartTreeRoam", "chartTimelineChanged", "chartTimelinePlayChanged", "chartRestore", "chartDataViewChanged", "chartMagicTypeChanged", "chartGeoSelectChanged", "chartGeoSelected", "chartGeoUnselected", "chartAxisAreaSelected", "chartBrush", "chartBrushEnd", "chartBrushSelected", "chartGlobalCursorTaken", "chartRendered", "chartFinished"], exportAs: ["echarts"] }] });
1784
+ }
1785
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: ChartComponent, decorators: [{
1786
+ type: Component,
1787
+ args: [{ selector: 'fx-ui-chart', standalone: false, template: "<div class=\"chart-wrapper\" [style.height]=\"height\">\n <div\n echarts\n [options]=\"chartOptions\"\n [loading]=\"loading\"\n (chartInit)=\"onChartInit($event)\"\n (chartClick)=\"onChartClick($event)\"\n class=\"fx-chart\"\n [style.height]=\"height\"\n ></div>\n</div>\n", styles: [":host{display:block;width:100%}.chart-wrapper{position:relative;width:100%;height:100%}.chart-container{width:100%;height:100%}.chart-loader{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:rgba(var(--bg-primary-hover)/.6);font-weight:500;font-size:1.1rem;z-index:2;border-radius:4px}\n"] }]
1788
+ }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.ChangeDetectorRef }], propDecorators: { type: [{
1789
+ type: Input
1790
+ }], data: [{
1791
+ type: Input
1792
+ }], options: [{
1793
+ type: Input
1794
+ }], palette: [{
1795
+ type: Input
1796
+ }], height: [{
1797
+ type: Input
1798
+ }], loading: [{
1799
+ type: Input
1800
+ }], pieLegendAlign: [{
1801
+ type: Input
1802
+ }], pieLegendPosition: [{
1803
+ type: Input
1804
+ }], showPieLabel: [{
1805
+ type: Input
1806
+ }], mobileWidth: [{
1807
+ type: Input
1808
+ }], labelFontFamily: [{
1809
+ type: Input
1810
+ }], chartClick: [{
1811
+ type: Output
1812
+ }], chartInit: [{
1813
+ type: Output
1814
+ }], onThemeChange: [{
1815
+ type: HostListener,
1816
+ args: ['window:theme-changed']
1817
+ }] } });
1818
+
1819
+ let FxComponent = class FxComponent {
1820
+ ngControl;
1821
+ value;
1822
+ onChange = (value) => { };
1823
+ onTouched = () => { };
1824
+ validationError;
1825
+ errorMessages;
1826
+ constructor(ngControl) {
1827
+ this.ngControl = ngControl;
1828
+ if (ngControl)
1829
+ ngControl.valueAccessor = this;
1830
+ }
1831
+ setValue(value) {
1832
+ if (this.onChange) {
1833
+ this.onChange(value);
1834
+ }
1835
+ }
1836
+ onEnter(value) {
1837
+ if (this.onChange) {
1838
+ this.onChange(value);
1839
+ }
1840
+ }
1841
+ writeValue(value) {
1842
+ this.value = value ?? '';
1843
+ }
1844
+ registerOnChange(fn) {
1845
+ this.onChange = fn;
1846
+ }
1847
+ registerOnTouched(fn) {
1848
+ this.onTouched = fn;
1849
+ }
1850
+ get invalid() {
1851
+ return !!(this.ngControl?.invalid && this.ngControl?.touched);
1852
+ }
1853
+ get errors() {
1854
+ return this.ngControl?.errors || {};
1855
+ }
1856
+ validate(validateFn) {
1857
+ if (this.invalid)
1858
+ return;
1859
+ if (validateFn) {
1860
+ const error = validateFn(this.value);
1861
+ if (FxUtils.isNotNull(error)) {
1862
+ this.setErrors(error ?? {});
1863
+ }
1864
+ }
1865
+ }
1866
+ setErrors(error) {
1867
+ this.validationError = error;
1868
+ const control = this.ngControl?.control;
1869
+ if (!control)
1870
+ return;
1871
+ control.setErrors(error);
1872
+ }
1873
+ clearErrors() {
1874
+ const control = this.ngControl?.control;
1875
+ if (!control)
1876
+ return;
1877
+ const errors = control.errors || {};
1878
+ const builtInErrorKeys = ['required', 'minlength', 'maxlength', 'email', 'pattern'];
1879
+ const remaining = {};
1880
+ for (const key of builtInErrorKeys) {
1881
+ if (errors[key] !== undefined) {
1882
+ remaining[key] = errors[key];
1883
+ }
1884
+ }
1885
+ control.setErrors(Object.keys(remaining).length ? remaining : null);
1886
+ }
1887
+ getErrorMessage(label) {
1888
+ if (!this.errors)
1889
+ return null;
1890
+ const displayLabel = FxUtils.isStringEmpty(label ?? '') ? 'This field' : label;
1891
+ const defaultMessages = {
1892
+ required: `${displayLabel} is required`,
1893
+ minlength: `${displayLabel} minimum length is ${this.errors['minlength']?.requiredLength}`,
1894
+ email: "Please enter a valid email"
1895
+ };
1896
+ const messages = { ...defaultMessages, ...this.validationError, ...this.errorMessages };
1897
+ const firstKey = Object.keys(this.errors)[0];
1898
+ return messages[firstKey] || `Invalid ${label ?? 'value'}`;
1899
+ }
1900
+ };
1901
+ FxComponent = __decorate([
1902
+ __param(0, Optional()),
1903
+ __param(0, Self())
1904
+ ], FxComponent);
1905
+
1906
+ class CheckboxComponent extends FxComponent {
1907
+ ref;
1908
+ label = '';
1909
+ disabled = false;
1910
+ rounded;
1911
+ valueChange = new EventEmitter();
1912
+ constructor(ngControl, ref) {
1913
+ super(ngControl);
1914
+ this.ref = ref;
1915
+ }
1916
+ ngAfterViewInit() {
1917
+ this.ref.detectChanges();
1918
+ }
1919
+ setDisabledState(isDisabled) {
1920
+ this.disabled = isDisabled;
1921
+ }
1922
+ onInputChange(event) {
1923
+ const checked = event.target.checked;
1924
+ this.value = checked;
1925
+ this.onChange(checked);
1926
+ this.valueChange.emit(checked);
1927
+ }
1928
+ markTouched() {
1929
+ this.onTouched();
1930
+ }
1931
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: CheckboxComponent, deps: [{ token: i1$3.NgControl }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
1932
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: CheckboxComponent, isStandalone: false, selector: "fx-ui-checkbox", inputs: { label: "label", disabled: "disabled", 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 <input\n type=\"checkbox\"\n class=\"hidden\"\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\"\n [ngClass]=\"{\n 'rounded-full': rounded,\n 'rounded-sm': !rounded,\n 'border-border-strong bg-transparent': !value,\n 'border-border-selected bg-border-selected': 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-bg-primary 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){<span class=\"mb-0 txt-field-label\">{{ label }}</span>}\n</label>\n", styles: [""], dependencies: [{ kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] });
1933
+ }
1934
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: CheckboxComponent, decorators: [{
1935
+ type: Component,
1936
+ 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=\"hidden\"\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\"\n [ngClass]=\"{\n 'rounded-full': rounded,\n 'rounded-sm': !rounded,\n 'border-border-strong bg-transparent': !value,\n 'border-border-selected bg-border-selected': 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-bg-primary 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){<span class=\"mb-0 txt-field-label\">{{ label }}</span>}\n</label>\n" }]
1937
+ }], ctorParameters: () => [{ type: i1$3.NgControl }, { type: i0.ChangeDetectorRef }], propDecorators: { label: [{
1938
+ type: Input
1939
+ }], disabled: [{
1940
+ type: Input
1941
+ }], rounded: [{
1942
+ type: Input
1943
+ }], valueChange: [{
1944
+ type: Output
1945
+ }] } });
1946
+
1947
+ class CircleProgressBar {
1948
+ percent = 0;
1949
+ showPercent = false;
1950
+ radius = 35;
1951
+ stroke = 10;
1952
+ normalizedRadius = this.radius - this.stroke / 2;
1953
+ circumference = 2 * Math.PI * this.normalizedRadius;
1954
+ get dashOffset() {
1955
+ const clamped = Math.max(0, Math.min(100, this.percent));
1956
+ return this.circumference - (clamped / 100) * this.circumference;
1957
+ }
1958
+ get ariaLabel() {
1959
+ return `${Math.round(this.percent)}%`;
1960
+ }
1961
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: CircleProgressBar, deps: [], target: i0.ɵɵFactoryTarget.Component });
1962
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: CircleProgressBar, isStandalone: false, selector: "fx-ui-circle-progress-bar", inputs: { percent: "percent", showPercent: "showPercent" }, ngImport: i0, template: "<div class=\"flex items-center justify-center w-full h-full\">\n <svg\n class=\"transform -rotate-90 block\"\n [attr.viewBox]=\"'0 0 ' + radius * 2 + ' ' + radius * 2\"\n width=\"100%\"\n height=\"100%\"\n >\n <circle\n class=\"opacity-20\"\n [attr.cx]=\"radius\"\n [attr.cy]=\"radius\"\n [attr.r]=\"normalizedRadius\"\n [attr.stroke-width]=\"stroke\"\n fill=\"transparent\"\n stroke=\"currentColor\"\n ></circle>\n\n <circle\n class=\"stroke-current\"\n [attr.cx]=\"radius\"\n [attr.cy]=\"radius\"\n [attr.r]=\"normalizedRadius\"\n [attr.stroke-width]=\"stroke\"\n fill=\"transparent\"\n stroke-linecap=\"round\"\n [attr.stroke-dasharray]=\"circumference\"\n [attr.stroke-dashoffset]=\"dashOffset\"\n style=\"transition: stroke-dashoffset 300ms linear\"\n ></circle>\n\n @if(showPercent){\n <text\n [attr.x]=\"radius\"\n [attr.y]=\"radius\"\n dominant-baseline=\"central\"\n text-anchor=\"middle\"\n class=\"text-xs font-medium\"\n fill=\"currentColor\"\n >\n {{ percent }}%\n </text>\n }\n </svg>\n</div>\n", styles: [""] });
1963
+ }
1964
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: CircleProgressBar, decorators: [{
1965
+ type: Component,
1966
+ args: [{ selector: 'fx-ui-circle-progress-bar', standalone: false, template: "<div class=\"flex items-center justify-center w-full h-full\">\n <svg\n class=\"transform -rotate-90 block\"\n [attr.viewBox]=\"'0 0 ' + radius * 2 + ' ' + radius * 2\"\n width=\"100%\"\n height=\"100%\"\n >\n <circle\n class=\"opacity-20\"\n [attr.cx]=\"radius\"\n [attr.cy]=\"radius\"\n [attr.r]=\"normalizedRadius\"\n [attr.stroke-width]=\"stroke\"\n fill=\"transparent\"\n stroke=\"currentColor\"\n ></circle>\n\n <circle\n class=\"stroke-current\"\n [attr.cx]=\"radius\"\n [attr.cy]=\"radius\"\n [attr.r]=\"normalizedRadius\"\n [attr.stroke-width]=\"stroke\"\n fill=\"transparent\"\n stroke-linecap=\"round\"\n [attr.stroke-dasharray]=\"circumference\"\n [attr.stroke-dashoffset]=\"dashOffset\"\n style=\"transition: stroke-dashoffset 300ms linear\"\n ></circle>\n\n @if(showPercent){\n <text\n [attr.x]=\"radius\"\n [attr.y]=\"radius\"\n dominant-baseline=\"central\"\n text-anchor=\"middle\"\n class=\"text-xs font-medium\"\n fill=\"currentColor\"\n >\n {{ percent }}%\n </text>\n }\n </svg>\n</div>\n" }]
1967
+ }], propDecorators: { percent: [{
1968
+ type: Input
1969
+ }], showPercent: [{
1970
+ type: Input
1971
+ }] } });
1972
+
1973
+ class DatetimePicker extends FxComponent {
1974
+ ref;
1975
+ label;
1976
+ placeholder = 'Select date and time';
1977
+ required = false;
1978
+ type = 'both';
1979
+ disabled = false;
1980
+ minDate;
1981
+ maxDate;
1982
+ errorMessages = {};
1983
+ validateFn;
1984
+ startAt;
1985
+ constructor(ngControl, ref) {
1986
+ super(ngControl);
1987
+ this.ref = ref;
1988
+ }
1989
+ ngOnInit() { }
1990
+ ngAfterViewInit() {
1991
+ setTimeout(() => {
1992
+ this.ref.detectChanges();
1993
+ });
1994
+ }
1995
+ setDisabledState(isDisabled) {
1996
+ this.disabled = isDisabled;
1997
+ }
1998
+ onBlur() {
1999
+ this.onTouched();
2000
+ this.validate(this.validateFn);
2001
+ }
2002
+ writeValue(value) {
2003
+ this.value = value ?? '';
2004
+ this.startAt = value ?? '';
2005
+ this.ref.markForCheck();
2006
+ }
2007
+ onValueChange(date) {
2008
+ if (this.disabled)
2009
+ return;
2010
+ this.value = date;
2011
+ this.onChange(date);
2012
+ this.onTouched();
2013
+ }
2014
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: DatetimePicker, deps: [{ token: i1$3.NgControl }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
2015
+ 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", disabled: "disabled", 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\">\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: ["::ng-deep .owl-dt-container{border-radius:.75rem;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 20px 25px -5px rgb(var(--shadow-color) / .1), 0 10px 10px -5px rgb(var(--shadow-color) / .1);--tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 10px 10px -5px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),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){--tw-bg-opacity: 1;background-color:rgb(var(--bg-primary-hover) / var(--tw-bg-opacity, 1))}::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-width:1px;--tw-border-opacity: 1;border-color:rgb(var(--border-strong) / var(--tw-border-opacity, 1));--tw-bg-opacity: 1;background-color:rgb(var(--bg-primary) / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(var(--text-primary) / var(--tw-text-opacity, 1))}::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{font-weight:500;--tw-text-opacity: 1;color:rgb(var(--text-primary) / var(--tw-text-opacity, 1))}::ng-deep .owl-dt-control-arrow-button .owl-dt-control-button-content{--tw-text-opacity: 1;color:rgb(var(--text-primary) / var(--tw-text-opacity, 1))}::ng-deep .owl-dt-calendar-table .owl-dt-calendar-header{--tw-text-opacity: 1;color:rgb(var(--text-secondary) / var(--tw-text-opacity, 1))}::ng-deep .owl-dt-calendar-table .owl-dt-calendar-header .owl-dt-weekdays th{font-weight:500;--tw-text-opacity: 1;color:rgb(var(--text-secondary) / var(--tw-text-opacity, 1))}::ng-deep .owl-dt-inline-container,::ng-deep .owl-dt-popup-container{border-radius:.75rem;border-width:1px;--tw-border-opacity: 1;border-color:rgb(var(--border-strong) / var(--tw-border-opacity, 1));--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)}\n"], dependencies: [{ kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i3.OwlDateTimeTriggerDirective, selector: "[owlDateTimeTrigger]", inputs: ["owlDateTimeTrigger", "disabled"] }, { kind: "directive", type: i3.OwlDateTimeInputDirective, selector: "input[owlDateTime]", inputs: ["required", "owlDateTime", "owlDateTimeFilter", "_disabled", "min", "max", "selectMode", "rangeSeparator", "value", "values"], outputs: ["dateTimeChange", "dateTimeInput"], exportAs: ["owlDateTimeInput"] }, { kind: "component", type: i3.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: ["icon", "solid", "outline", "size", "color", "class"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2016
+ }
2017
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: DatetimePicker, decorators: [{
2018
+ type: Component,
2019
+ 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: ["::ng-deep .owl-dt-container{border-radius:.75rem;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 20px 25px -5px rgb(var(--shadow-color) / .1), 0 10px 10px -5px rgb(var(--shadow-color) / .1);--tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 10px 10px -5px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),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){--tw-bg-opacity: 1;background-color:rgb(var(--bg-primary-hover) / var(--tw-bg-opacity, 1))}::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-width:1px;--tw-border-opacity: 1;border-color:rgb(var(--border-strong) / var(--tw-border-opacity, 1));--tw-bg-opacity: 1;background-color:rgb(var(--bg-primary) / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(var(--text-primary) / var(--tw-text-opacity, 1))}::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{font-weight:500;--tw-text-opacity: 1;color:rgb(var(--text-primary) / var(--tw-text-opacity, 1))}::ng-deep .owl-dt-control-arrow-button .owl-dt-control-button-content{--tw-text-opacity: 1;color:rgb(var(--text-primary) / var(--tw-text-opacity, 1))}::ng-deep .owl-dt-calendar-table .owl-dt-calendar-header{--tw-text-opacity: 1;color:rgb(var(--text-secondary) / var(--tw-text-opacity, 1))}::ng-deep .owl-dt-calendar-table .owl-dt-calendar-header .owl-dt-weekdays th{font-weight:500;--tw-text-opacity: 1;color:rgb(var(--text-secondary) / var(--tw-text-opacity, 1))}::ng-deep .owl-dt-inline-container,::ng-deep .owl-dt-popup-container{border-radius:.75rem;border-width:1px;--tw-border-opacity: 1;border-color:rgb(var(--border-strong) / var(--tw-border-opacity, 1));--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)}\n"] }]
2020
+ }], ctorParameters: () => [{ type: i1$3.NgControl }, { type: i0.ChangeDetectorRef }], propDecorators: { label: [{
2021
+ type: Input
2022
+ }], placeholder: [{
2023
+ type: Input
2024
+ }], required: [{
2025
+ type: Input
2026
+ }], type: [{
2027
+ type: Input
2028
+ }], disabled: [{
2029
+ type: Input
2030
+ }], minDate: [{
2031
+ type: Input
2032
+ }], maxDate: [{
2033
+ type: Input
2034
+ }], errorMessages: [{
2035
+ type: Input
2036
+ }], validateFn: [{
2037
+ type: Input
2038
+ }] } });
2039
+
2040
+ class DndUploadComponent {
2041
+ multiple = false;
2042
+ accept = '';
2043
+ maxSizeMB = 10;
2044
+ strictSize = true;
2045
+ fileDrop = new EventEmitter();
2046
+ fileClick = new EventEmitter();
2047
+ uploadComplete = new EventEmitter();
2048
+ uploadError = new EventEmitter();
2049
+ isDragOver = false;
2050
+ isOpening = false;
2051
+ onDragOver(event) {
2052
+ event.preventDefault();
2053
+ this.isDragOver = true;
2054
+ }
2055
+ onDragLeave(event) {
2056
+ event.preventDefault();
2057
+ this.isDragOver = false;
2058
+ }
2059
+ onDrop(event) {
2060
+ event.preventDefault();
2061
+ this.isDragOver = false;
2062
+ if (!event.dataTransfer?.files?.length)
2063
+ return;
2064
+ const files = Array.from(event.dataTransfer.files);
2065
+ this.processFiles(files);
2066
+ }
2067
+ onAreaClick(inputEl, event) {
2068
+ if (this.isOpening)
2069
+ return;
2070
+ this.isOpening = true;
2071
+ this.fileClick.emit();
2072
+ event?.currentTarget?.blur?.();
2073
+ setTimeout(() => {
2074
+ inputEl?.click();
2075
+ this.isOpening = false;
2076
+ }, 0);
2077
+ }
2078
+ onFileSelected(event) {
2079
+ const input = event.target;
2080
+ if (!input.files?.length)
2081
+ return;
2082
+ const files = Array.from(input.files);
2083
+ this.processFiles(files);
2084
+ input.value = '';
2085
+ }
2086
+ processFiles(files) {
2087
+ const valid = [];
2088
+ const acceptedTypes = this.accept
2089
+ .split(',')
2090
+ .map(t => t.trim().toLowerCase())
2091
+ .filter(t => t);
2092
+ for (const file of files) {
2093
+ const sizeMB = file.size / 1024 / 1024;
2094
+ if (this.strictSize && sizeMB > this.maxSizeMB) {
2095
+ this.uploadError.emit({
2096
+ file,
2097
+ reason: `File too large: ${sizeMB.toFixed(2)} MB > ${this.maxSizeMB} MB`
2098
+ });
2099
+ continue;
2100
+ }
2101
+ if (acceptedTypes.length && !this.isAccepted(file, acceptedTypes)) {
2102
+ this.uploadError.emit({
2103
+ file,
2104
+ reason: `File type not allowed: "${file.name}"`
2105
+ });
2106
+ continue;
2107
+ }
2108
+ valid.push(file);
2109
+ }
2110
+ if (valid.length) {
2111
+ this.fileDrop.emit(valid);
2112
+ this.onUpload(valid);
2113
+ }
2114
+ }
2115
+ onUpload(files) {
2116
+ console.log('Uploading files:', files);
2117
+ this.uploadComplete.emit(files);
2118
+ }
2119
+ isAccepted(file, accepted) {
2120
+ const fileType = file.type.toLowerCase();
2121
+ const fileExt = '.' + file.name.split('.').pop().toLowerCase();
2122
+ return accepted.some(a => {
2123
+ if (a.startsWith('.')) {
2124
+ return a === fileExt;
2125
+ }
2126
+ if (a.endsWith('/*')) {
2127
+ return fileType.startsWith(a.replace('/*', ''));
2128
+ }
2129
+ return a === fileType;
2130
+ });
2131
+ }
2132
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: DndUploadComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2133
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.4", type: DndUploadComponent, isStandalone: false, selector: "fx-ui-dnd-upload", inputs: { multiple: "multiple", accept: "accept", maxSizeMB: "maxSizeMB", strictSize: "strictSize" }, outputs: { fileDrop: "fileDrop", fileClick: "fileClick", uploadComplete: "uploadComplete", uploadError: "uploadError" }, ngImport: i0, template: "<div\n class=\"block w-full\"\n (dragover)=\"onDragOver($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\"\n>\n <div\n role=\"button\"\n tabindex=\"0\"\n class=\"flex flex-col items-center justify-center gap-1 border-2 border-dashed rounded-md p-4 cursor-pointer select-none outline-none focus:ring-2 focus:ring-primary\"\n [ngClass]=\"{\n 'border-border-default bg-bg-hover': !isDragOver,\n 'border-border-interactive bg-secondary': isDragOver\n }\"\n (click)=\"onAreaClick(fileInput, $event)\"\n (keydown.enter)=\"onAreaClick(fileInput, $event)\"\n >\n <div class=\"text-sm text-text-link font-medium\">\n Click to upload <span class=\"text-text-secondary opacity-50\">or drag and drop</span>\n </div>\n <div class=\"text-xs text-text-secondary\">\n Allowed: {{ accept || 'any file type' }} (max {{ maxSizeMB }} MB)\n </div>\n\n <input\n #fileInput\n type=\"file\"\n class=\"hidden\"\n [attr.multiple]=\"multiple ? '' : null\"\n [attr.accept]=\"accept\"\n (change)=\"onFileSelected($event)\"\n />\n </div>\n</div>\n", styles: [""], dependencies: [{ kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] });
2134
+ }
2135
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: DndUploadComponent, decorators: [{
2136
+ type: Component,
2137
+ args: [{ selector: 'fx-ui-dnd-upload', standalone: false, template: "<div\n class=\"block w-full\"\n (dragover)=\"onDragOver($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\"\n>\n <div\n role=\"button\"\n tabindex=\"0\"\n class=\"flex flex-col items-center justify-center gap-1 border-2 border-dashed rounded-md p-4 cursor-pointer select-none outline-none focus:ring-2 focus:ring-primary\"\n [ngClass]=\"{\n 'border-border-default bg-bg-hover': !isDragOver,\n 'border-border-interactive bg-secondary': isDragOver\n }\"\n (click)=\"onAreaClick(fileInput, $event)\"\n (keydown.enter)=\"onAreaClick(fileInput, $event)\"\n >\n <div class=\"text-sm text-text-link font-medium\">\n Click to upload <span class=\"text-text-secondary opacity-50\">or drag and drop</span>\n </div>\n <div class=\"text-xs text-text-secondary\">\n Allowed: {{ accept || 'any file type' }} (max {{ maxSizeMB }} MB)\n </div>\n\n <input\n #fileInput\n type=\"file\"\n class=\"hidden\"\n [attr.multiple]=\"multiple ? '' : null\"\n [attr.accept]=\"accept\"\n (change)=\"onFileSelected($event)\"\n />\n </div>\n</div>\n" }]
2138
+ }], propDecorators: { multiple: [{
2139
+ type: Input
2140
+ }], accept: [{
2141
+ type: Input
2142
+ }], maxSizeMB: [{
2143
+ type: Input
2144
+ }], strictSize: [{
2145
+ type: Input
2146
+ }], fileDrop: [{
2147
+ type: Output
2148
+ }], fileClick: [{
2149
+ type: Output
2150
+ }], uploadComplete: [{
2151
+ type: Output
2152
+ }], uploadError: [{
2153
+ type: Output
2154
+ }] } });
2155
+
2156
+ class FlowConnection {
2157
+ nodes = [];
2158
+ edges = [];
2159
+ backgroundColor = '--bg-hover-primary';
2160
+ view = ['100%', '400px'];
2161
+ handleConnection(connection) {
2162
+ this.edges = [
2163
+ ...this.edges,
2164
+ {
2165
+ id: `${connection.source} -> ${connection.target}`,
2166
+ ...connection,
2167
+ },
2168
+ ];
2169
+ }
2170
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: FlowConnection, deps: [], target: i0.ɵɵFactoryTarget.Component });
2171
+ 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: i29.VflowComponent, selector: "vflow", inputs: ["view", "minZoom", "maxZoom", "background", "optimization", "entitiesSelectable", "keyboardShortcuts", "connection", "snapGrid", "elevateNodesOnSelect", "elevateEdgesOnSelect", "nodes", "alignmentHelper", "edges"], outputs: ["onComponentNodeEvent"] }, { kind: "directive", type: i29.ConnectionControllerDirective, selector: "[onConnect], [onReconnect], [connect], [reconnect]", outputs: ["onConnect", "connect", "onReconnect", "reconnect"] }] });
2172
+ }
2173
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: FlowConnection, decorators: [{
2174
+ type: Component,
2175
+ args: [{ selector: 'fx-ui-flow-connection', standalone: false, 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" }]
2176
+ }], propDecorators: { nodes: [{
2177
+ type: Input
2178
+ }], edges: [{
2179
+ type: Input
2180
+ }], backgroundColor: [{
2181
+ type: Input
2182
+ }], view: [{
2183
+ type: Input
2184
+ }] } });
2185
+
2186
+ class InputComponent extends FxComponent {
2187
+ ref;
2188
+ label = '';
2189
+ type = 'text';
2190
+ placeholder = '';
2191
+ errorMessages = {};
2192
+ required = false;
2193
+ disabled = false;
2194
+ iconClass = 'material-symbols-outlined';
2195
+ suffixIcon;
2196
+ class;
2197
+ maxlength;
2198
+ validateFn;
2199
+ blurred = new EventEmitter();
2200
+ focused = new EventEmitter();
2201
+ suffixClick = new EventEmitter();
2202
+ showPassword = false;
2203
+ validateError = null;
2204
+ constructor(ngControl, ref) {
2205
+ super(ngControl);
2206
+ this.ref = ref;
2207
+ }
2208
+ ngAfterViewInit() {
2209
+ this.ref.detectChanges();
2210
+ }
2211
+ setDisabledState(isDisabled) {
2212
+ this.disabled = isDisabled;
2213
+ }
2214
+ onInput(event) {
2215
+ const input = event.target;
2216
+ let newValue = input.value;
2217
+ if (this.maxlength && newValue.length > this.maxlength) {
2218
+ newValue = newValue.slice(0, this.maxlength);
2219
+ input.value = newValue;
2220
+ }
2221
+ this.value = newValue;
2222
+ this.onChange(this.value);
2223
+ }
2224
+ onFocus() {
2225
+ this.clearErrors();
2226
+ this.focused.emit();
2227
+ }
2228
+ onBlur() {
2229
+ this.onTouched();
2230
+ this.blurred.emit();
2231
+ if (this.validateFn) {
2232
+ this.validateError = this.validateFn(this.value);
2233
+ this.validate(this.validateFn);
2234
+ }
2235
+ }
2236
+ togglePassword() {
2237
+ this.showPassword = !this.showPassword;
2238
+ }
2239
+ onSuffixClick() {
2240
+ if (this.type === 'password') {
2241
+ this.togglePassword();
2242
+ }
2243
+ else {
2244
+ this.suffixClick.emit();
2245
+ }
2246
+ }
2247
+ get displayType() {
2248
+ if (this.type === 'password' && this.showPassword)
2249
+ return 'text';
2250
+ if (this.type === 'otp')
2251
+ return 'number';
2252
+ return this.type;
2253
+ }
2254
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: InputComponent, deps: [{ token: i1$3.NgControl }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
2255
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: InputComponent, isStandalone: false, selector: "fx-ui-input", inputs: { label: "label", type: "type", placeholder: "placeholder", errorMessages: "errorMessages", required: "required", disabled: "disabled", iconClass: "iconClass", suffixIcon: "suffixIcon", class: "class", maxlength: "maxlength", validateFn: "validateFn" }, 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){<label class=\"block txt-field-label\">\n {{ label }}\n @if (required) {<span class=\"txt-required\">*</span>}\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 [ngClass]=\"{\n 'input-invalid': invalid,\n 'pr-8': type === 'password' || suffixIcon,\n 'txt-otp-input': type === 'otp'\n }\"\n />\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 <i class=\"material-symbols-outlined text-lg select-none\">{{\n showPassword ? 'visibility_off' : 'visibility'\n }}</i>\n } @else if (suffixIcon) {\n <i [class]=\"iconClass\" class=\"text-lg select-none\">{{ suffixIcon }}</i>\n }\n </button>\n </div>\n\n @if (invalid){\n <div class=\"txt-invalid\">\n {{ getErrorMessage(label) }}\n </div>\n }\n</div>\n", styles: [""], dependencies: [{ kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: TrimOnBlurDirective, selector: "[appTrimOnBlur]", inputs: ["appTrimOnBlurCollapse"] }] });
2256
+ }
2257
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: InputComponent, decorators: [{
2258
+ type: Component,
2259
+ args: [{ selector: 'fx-ui-input', standalone: false, template: "<div class=\"mb-4 text-left w-full text-text-primary\" [class]=\"class\">\n @if(label){<label class=\"block txt-field-label\">\n {{ label }}\n @if (required) {<span class=\"txt-required\">*</span>}\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 [ngClass]=\"{\n 'input-invalid': invalid,\n 'pr-8': type === 'password' || suffixIcon,\n 'txt-otp-input': type === 'otp'\n }\"\n />\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 <i class=\"material-symbols-outlined text-lg select-none\">{{\n showPassword ? 'visibility_off' : 'visibility'\n }}</i>\n } @else if (suffixIcon) {\n <i [class]=\"iconClass\" class=\"text-lg select-none\">{{ suffixIcon }}</i>\n }\n </button>\n </div>\n\n @if (invalid){\n <div class=\"txt-invalid\">\n {{ getErrorMessage(label) }}\n </div>\n }\n</div>\n" }]
2260
+ }], ctorParameters: () => [{ type: i1$3.NgControl }, { type: i0.ChangeDetectorRef }], propDecorators: { label: [{
2261
+ type: Input
2262
+ }], type: [{
2263
+ type: Input
2264
+ }], placeholder: [{
2265
+ type: Input
2266
+ }], errorMessages: [{
2267
+ type: Input
2268
+ }], required: [{
2269
+ type: Input
2270
+ }], disabled: [{
2271
+ type: Input
2272
+ }], iconClass: [{
2273
+ type: Input
2274
+ }], suffixIcon: [{
2275
+ type: Input
2276
+ }], class: [{
2277
+ type: Input
2278
+ }], maxlength: [{
2279
+ type: Input
2280
+ }], validateFn: [{
2281
+ type: Input
2282
+ }], blurred: [{
2283
+ type: Output
2284
+ }], focused: [{
2285
+ type: Output
2286
+ }], suffixClick: [{
2287
+ type: Output
2288
+ }] } });
2289
+
2290
+ class LoadingPanel {
2291
+ show = false;
2292
+ message = "Loading";
2293
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: LoadingPanel, deps: [], target: i0.ɵɵFactoryTarget.Component });
2294
+ 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=\"rgb(var(--primary))\" width=\"50\" height=\"50\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\">\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: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
2295
+ }
2296
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: LoadingPanel, decorators: [{
2297
+ type: Component,
2298
+ args: [{ selector: 'fx-ui-loading-panel', standalone: false, 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=\"rgb(var(--primary))\" width=\"50\" height=\"50\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\">\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" }]
2299
+ }], propDecorators: { show: [{
2300
+ type: Input
2301
+ }], message: [{
2302
+ type: Input
2303
+ }] } });
2304
+
2305
+ class RadioButtonToggleComponent extends FxComponent {
2306
+ ref;
2307
+ label;
2308
+ required = false;
2309
+ options = [];
2310
+ disabled = false;
2311
+ errorMessages = {};
2312
+ class;
2313
+ constructor(ngControl, ref) {
2314
+ super(ngControl);
2315
+ this.ref = ref;
2316
+ }
2317
+ ngAfterViewInit() {
2318
+ this.ref.detectChanges();
2319
+ }
2320
+ setDisabledState(isDisabled) {
2321
+ this.disabled = isDisabled;
2322
+ }
2323
+ onSelectValue(value) {
2324
+ if (this.disabled)
2325
+ return;
2326
+ this.value = value;
2327
+ this.onChange(value);
2328
+ this.onTouched();
2329
+ }
2330
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: RadioButtonToggleComponent, deps: [{ token: i1$3.NgControl }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
2331
+ 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", disabled: "disabled", errorMessages: "errorMessages", class: "class" }, usesInheritance: true, ngImport: i0, template: "<div class=\"flex flex-col text-left mb-4 w-full\" [class]=\"class\">\n @if(label){<label class=\"txt-field-label\">\n {{ label }}\n @if(required) {<span class=\"txt-required\">*</span>} </label\n >}\n\n <div class=\"inline-flex w-full rounded-lg overflow-hidden\" role=\"radiogroup\">\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: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] });
2332
+ }
2333
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: RadioButtonToggleComponent, decorators: [{
2334
+ type: Component,
2335
+ 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){<label class=\"txt-field-label\">\n {{ label }}\n @if(required) {<span class=\"txt-required\">*</span>} </label\n >}\n\n <div class=\"inline-flex w-full rounded-lg overflow-hidden\" role=\"radiogroup\">\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" }]
2336
+ }], ctorParameters: () => [{ type: i1$3.NgControl }, { type: i0.ChangeDetectorRef }], propDecorators: { label: [{
2337
+ type: Input
2338
+ }], required: [{
2339
+ type: Input
2340
+ }], options: [{
2341
+ type: Input
2342
+ }], disabled: [{
2343
+ type: Input
2344
+ }], errorMessages: [{
2345
+ type: Input
2346
+ }], class: [{
2347
+ type: Input
2348
+ }] } });
2349
+
2350
+ class RadioButtonComponent extends FxComponent {
2351
+ ref;
2352
+ label;
2353
+ required = false;
2354
+ options = [];
2355
+ disabled = false;
2356
+ errorMessages = {};
2357
+ class;
2358
+ constructor(ngControl, ref) {
2359
+ super(ngControl);
2360
+ this.ref = ref;
2361
+ }
2362
+ ngAfterViewInit() {
2363
+ this.ref.detectChanges();
2364
+ }
2365
+ setDisabledState(isDisabled) {
2366
+ this.disabled = isDisabled;
2367
+ }
2368
+ onSelectValue(value) {
2369
+ if (this.disabled)
2370
+ return;
2371
+ this.value = value;
2372
+ this.onChange(value);
2373
+ this.onTouched();
2374
+ }
2375
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: RadioButtonComponent, deps: [{ token: i1$3.NgControl }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
2376
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: RadioButtonComponent, isStandalone: false, selector: "fx-ui-radio", inputs: { label: "label", required: "required", options: "options", disabled: "disabled", errorMessages: "errorMessages", class: "class" }, usesInheritance: true, ngImport: i0, template: "<div class=\"w-full rounded-lg overflow-hidden\" [class]=\"class\" role=\"radiogroup\">\n @if (label){\n <label class=\"txt-field-label\">\n {{ label }}\n @if(required){<span class=\"txt-required\">*</span>} </label\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 >\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-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: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }] });
2377
+ }
2378
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: RadioButtonComponent, decorators: [{
2379
+ type: Component,
2380
+ args: [{ selector: 'fx-ui-radio', standalone: false, template: "<div class=\"w-full rounded-lg overflow-hidden\" [class]=\"class\" role=\"radiogroup\">\n @if (label){\n <label class=\"txt-field-label\">\n {{ label }}\n @if(required){<span class=\"txt-required\">*</span>} </label\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 >\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-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" }]
2381
+ }], ctorParameters: () => [{ type: i1$3.NgControl }, { type: i0.ChangeDetectorRef }], propDecorators: { label: [{
2382
+ type: Input
2383
+ }], required: [{
2384
+ type: Input
2385
+ }], options: [{
2386
+ type: Input
2387
+ }], disabled: [{
2388
+ type: Input
2389
+ }], errorMessages: [{
2390
+ type: Input
2391
+ }], class: [{
2392
+ type: Input
2393
+ }] } });
2394
+
2395
+ class RichTextAreaComponent extends FxComponent {
2396
+ cdr;
2397
+ styleLoader;
2398
+ platformId;
2399
+ editorContainer;
2400
+ placeholder = 'Write something...';
2401
+ toolbarOptions = [
2402
+ // ['undo', 'redo'],
2403
+ [{ 'header': [1, 2, 3, 4, 5, 6, false] }],
2404
+ [{ 'font': [] }],
2405
+ [{ 'size': ['small', false, 'large', 'huge'] }],
2406
+ [{ 'align': [] }],
2407
+ [{ 'color': [] }, { 'background': [] }],
2408
+ ['bold', 'italic', 'underline', 'strike'],
2409
+ ['blockquote', 'code-block'],
2410
+ [{ 'list': 'ordered' }, { 'list': 'bullet' }],
2411
+ [{ 'indent': '-1' }, { 'indent': '+1' }],
2412
+ ['link', 'image'],
2413
+ ['clean']
2414
+ ];
2415
+ contentChange = new EventEmitter();
2416
+ quillInstance;
2417
+ constructor(ngControl, cdr, styleLoader, platformId) {
2418
+ super(ngControl);
2419
+ this.cdr = cdr;
2420
+ this.styleLoader = styleLoader;
2421
+ this.platformId = platformId;
2422
+ }
2423
+ async ngAfterViewInit() {
2424
+ if (!isPlatformBrowser(this.platformId))
2425
+ return;
2426
+ await this.styleLoader.load();
2427
+ const QuillModule = await import('quill');
2428
+ const Quill = QuillModule.default || QuillModule;
2429
+ const icons = Quill.import('ui/icons');
2430
+ // icons['undo'] = `<svg viewBox="0 0 18 18"><path d="M6.4 5.2H3L6.8 1l3.8 4.2H7.9C10.2 5.4 12 7.4 12 9.8c0 2.7-2.2 4.9-4.9 4.9S2.2 12.5 2.2 9.8H0c0 3.8 3.1 6.9 6.9 6.9s6.9-3.1 6.9-6.9S10.7 3 7 3v2.2z"></path></svg>`;
2431
+ // icons['redo'] = `<svg viewBox="0 0 18 18"><path d="M11.6 5.2H15L11.2 1 7.4 5.2h2.7C7.8 5.4 6 7.4 6 9.8c0 2.7 2.2 4.9 4.9 4.9s4.9-2.2 4.9-4.9H18c0 3.8-3.1 6.9-6.9 6.9S4.2 13.6 4.2 9.8 7.3 3 11 3v2.2z"></path></svg>`;
2432
+ this.quillInstance = new Quill(this.editorContainer.nativeElement, {
2433
+ theme: 'snow',
2434
+ placeholder: this.placeholder,
2435
+ modules: {
2436
+ history: true,
2437
+ toolbar: this.toolbarOptions,
2438
+ }
2439
+ });
2440
+ this.quillInstance.root.style.color = FxUtils.convertColorFromVariable('--text-primary');
2441
+ const toolbar = this.quillInstance.getModule('toolbar');
2442
+ toolbar.addHandler('undo', () => this.quillInstance.history.undo());
2443
+ toolbar.addHandler('redo', () => this.quillInstance.history.redo());
2444
+ if (this.value) {
2445
+ this.quillInstance.root.innerHTML = this.value;
2446
+ }
2447
+ this.quillInstance.on('text-change', () => {
2448
+ const html = this.quillInstance.root.innerHTML || '';
2449
+ this.value = html;
2450
+ this.onChange(html);
2451
+ this.contentChange.emit(html);
2452
+ });
2453
+ }
2454
+ writeValue(value) {
2455
+ this.value = value || '';
2456
+ if (this.quillInstance) {
2457
+ this.quillInstance.root.innerHTML = this.value;
2458
+ }
2459
+ }
2460
+ setDisabledState(isDisabled) {
2461
+ if (this.quillInstance) {
2462
+ this.quillInstance.enable(!isDisabled);
2463
+ }
2464
+ }
2465
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: RichTextAreaComponent, deps: [{ token: i1$3.NgControl }, { token: i0.ChangeDetectorRef }, { token: QuillStyleLoaderService }, { token: PLATFORM_ID }], target: i0.ɵɵFactoryTarget.Component });
2466
+ 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: [":host{display:block}.ql-toolbar{border-top-left-radius:.25rem;border-top-right-radius:.25rem;border-bottom-width:1px;--tw-border-opacity: 1;border-color:rgb(var(--border-default) / var(--tw-border-opacity, 1));padding:.5rem}.ql-container{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.ql-editor{min-height:200px;padding:.75rem;font-size:1rem}\n"] });
2467
+ }
2468
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: RichTextAreaComponent, decorators: [{
2469
+ type: Component,
2470
+ 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: [":host{display:block}.ql-toolbar{border-top-left-radius:.25rem;border-top-right-radius:.25rem;border-bottom-width:1px;--tw-border-opacity: 1;border-color:rgb(var(--border-default) / var(--tw-border-opacity, 1));padding:.5rem}.ql-container{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.ql-editor{min-height:200px;padding:.75rem;font-size:1rem}\n"] }]
2471
+ }], ctorParameters: () => [{ type: i1$3.NgControl }, { type: i0.ChangeDetectorRef }, { type: QuillStyleLoaderService }, { type: Object, decorators: [{
2472
+ type: Inject,
2473
+ args: [PLATFORM_ID]
2474
+ }] }], propDecorators: { editorContainer: [{
2475
+ type: ViewChild,
2476
+ args: ['editorContainer', { static: true }]
2477
+ }], placeholder: [{
2478
+ type: Input
2479
+ }], toolbarOptions: [{
2480
+ type: Input
2481
+ }], contentChange: [{
2482
+ type: Output
2483
+ }] } });
2484
+
2485
+ class SearchBarComponent extends FxComponent {
2486
+ ref;
2487
+ placeholder = 'Search...';
2488
+ disabled = false;
2489
+ search = new EventEmitter();
2490
+ valueChange = new EventEmitter();
2491
+ constructor(ngControl, ref) {
2492
+ super(ngControl);
2493
+ this.ref = ref;
2494
+ }
2495
+ ngAfterViewInit() {
2496
+ this.ref.detectChanges();
2497
+ }
2498
+ onValueChange(value) {
2499
+ this.value = value;
2500
+ this.setValue(value);
2501
+ this.valueChange.emit(value);
2502
+ }
2503
+ onEnterKey() {
2504
+ this.onEnter(this.value);
2505
+ this.search.emit(this.value.trim());
2506
+ }
2507
+ clear() {
2508
+ this.value = '';
2509
+ this.setValue(this.value);
2510
+ this.valueChange.emit('');
2511
+ this.search.emit('');
2512
+ }
2513
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: SearchBarComponent, deps: [{ token: i1$3.NgControl }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
2514
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.4", type: SearchBarComponent, isStandalone: false, selector: "fx-ui-search-bar", inputs: { placeholder: "placeholder", disabled: "disabled" }, 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: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: HeroIconComponent, selector: "fx-ui-hero-icon", inputs: ["icon", "solid", "outline", "size", "color", "class"] }] });
2515
+ }
2516
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: SearchBarComponent, decorators: [{
2517
+ type: Component,
2518
+ 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" }]
2519
+ }], ctorParameters: () => [{ type: i1$3.NgControl }, { type: i0.ChangeDetectorRef }], propDecorators: { placeholder: [{
2520
+ type: Input
2521
+ }], disabled: [{
2522
+ type: Input
2523
+ }], search: [{
2524
+ type: Output
2525
+ }], valueChange: [{
2526
+ type: Output
2527
+ }] } });
2528
+
2529
+ class SelectComponent extends FxComponent {
2530
+ ref;
2531
+ label = '';
2532
+ placeholder = '';
2533
+ options = [];
2534
+ multiple = false;
2535
+ disabled = false;
2536
+ errorMessages = {};
2537
+ required = false;
2538
+ class;
2539
+ enableSearch = false;
2540
+ searchFn;
2541
+ searchTerm = '';
2542
+ filteredOptions = [];
2543
+ search$ = new Subject();
2544
+ constructor(ngControl, ref) {
2545
+ super(ngControl);
2546
+ this.ref = ref;
2547
+ }
2548
+ ngOnInit() {
2549
+ this.filteredOptions = this.options;
2550
+ this.search$
2551
+ .pipe(debounceTime(300), distinctUntilChanged())
2552
+ .subscribe((term) => this.performSearch(term));
2553
+ }
2554
+ ngAfterViewInit() {
2555
+ this.ref.detectChanges();
2556
+ }
2557
+ onOpened(open) {
2558
+ if (open) {
2559
+ this.filteredOptions = this.options;
2560
+ if (this.enableSearch)
2561
+ this.searchTerm = '';
2562
+ }
2563
+ }
2564
+ onSearchChange(value) {
2565
+ if (!this.enableSearch)
2566
+ return;
2567
+ this.search$.next(value);
2568
+ }
2569
+ async performSearch(term) {
2570
+ if (!this.enableSearch)
2571
+ return;
2572
+ if (!this.searchFn) {
2573
+ this.filteredOptions = this.options.filter((o) => o.label?.toLowerCase().includes(term.toLowerCase()));
2574
+ this.ref.detectChanges();
2575
+ return;
2576
+ }
2577
+ const result = await this.searchFn(term);
2578
+ this.filteredOptions = result;
2579
+ console.log(this.filteredOptions);
2580
+ Promise.resolve().then(() => this.ref.detectChanges());
2581
+ }
2582
+ onSelectionChange(value) {
2583
+ this.value = value;
2584
+ this.onChange(this.value);
2585
+ }
2586
+ isSelected(value) {
2587
+ if (Array.isArray(this.value)) {
2588
+ return this.value.includes(value);
2589
+ }
2590
+ return this.value === value;
2591
+ }
2592
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: SelectComponent, deps: [{ token: i1$3.NgControl }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
2593
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: SelectComponent, isStandalone: false, selector: "fx-ui-select", inputs: { label: "label", placeholder: "placeholder", options: "options", multiple: "multiple", disabled: "disabled", errorMessages: "errorMessages", required: "required", class: "class", enableSearch: "enableSearch", searchFn: "searchFn" }, usesInheritance: true, ngImport: i0, 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) {<span class=\"txt-required\">*</span>}\n </label>\n }\n\n <mat-form-field class=\"fx-select-field w-full shadow-sm\" appearance=\"fill\">\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-border-interactive 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:.5rem;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(--border-interactive))!important;outline:none!important;border-radius:.5rem}::ng-deep .fx-select-field.mat-form-field-invalid .mat-mdc-form-field-flex{border-color:#ef4444!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{color:rgb(var(--text-primary))}::ng-deep .mat-mdc-select-panel{background-color:rgb(var(--bg-primary))!important;border:1px solid rgb(var(--border-default))!important;border-radius:.5rem!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}\n"], dependencies: [{ kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$3.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: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i3$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "component", type: i3$1.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$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }] });
2594
+ }
2595
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: SelectComponent, decorators: [{
2596
+ type: Component,
2597
+ 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) {<span class=\"txt-required\">*</span>}\n </label>\n }\n\n <mat-form-field class=\"fx-select-field w-full shadow-sm\" appearance=\"fill\">\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-border-interactive 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:.5rem;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(--border-interactive))!important;outline:none!important;border-radius:.5rem}::ng-deep .fx-select-field.mat-form-field-invalid .mat-mdc-form-field-flex{border-color:#ef4444!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{color:rgb(var(--text-primary))}::ng-deep .mat-mdc-select-panel{background-color:rgb(var(--bg-primary))!important;border:1px solid rgb(var(--border-default))!important;border-radius:.5rem!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}\n"] }]
2598
+ }], ctorParameters: () => [{ type: i1$3.NgControl }, { type: i0.ChangeDetectorRef }], propDecorators: { label: [{
2599
+ type: Input
2600
+ }], placeholder: [{
2601
+ type: Input
2602
+ }], options: [{
2603
+ type: Input
2604
+ }], multiple: [{
2605
+ type: Input
2606
+ }], disabled: [{
2607
+ type: Input
2608
+ }], errorMessages: [{
2609
+ type: Input
2610
+ }], required: [{
2611
+ type: Input
2612
+ }], class: [{
2613
+ type: Input
2614
+ }], enableSearch: [{
2615
+ type: Input
2616
+ }], searchFn: [{
2617
+ type: Input
2618
+ }] } });
2619
+
2620
+ class SkeletonTableLoadingComponent {
2621
+ columns = 5;
2622
+ rows = 5;
2623
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: SkeletonTableLoadingComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2624
+ 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: i2.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }] });
2625
+ }
2626
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: SkeletonTableLoadingComponent, decorators: [{
2627
+ type: Component,
2628
+ args: [{ selector: 'fx-ui-skeleton-table-loading', standalone: false, 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" }]
2629
+ }], propDecorators: { columns: [{
2630
+ type: Input
2631
+ }], rows: [{
2632
+ type: Input
2633
+ }] } });
2634
+
2635
+ class SliderComponent extends FxComponent {
2636
+ ref;
2637
+ trackRef;
2638
+ thumbRef;
2639
+ min = 0;
2640
+ max = 100;
2641
+ step = 1;
2642
+ vertical = false;
2643
+ disabled = false;
2644
+ valueChange = new EventEmitter();
2645
+ dragging = false;
2646
+ constructor(ngControl, ref) {
2647
+ super(ngControl);
2648
+ this.ref = ref;
2649
+ }
2650
+ ngAfterViewInit() {
2651
+ this._normalizeValue(this.value);
2652
+ this.ref.markForCheck();
2653
+ }
2654
+ setDisabledState(isDisabled) {
2655
+ this.disabled = isDisabled;
2656
+ this.ref.markForCheck();
2657
+ }
2658
+ writeValue(value) {
2659
+ this.value = this._normalizeValue(value ?? this.min);
2660
+ this.ref.markForCheck();
2661
+ }
2662
+ _normalizeValue(v) {
2663
+ if (!Number.isFinite(v))
2664
+ v = this.min;
2665
+ const clamped = clamp(v, this.min, this.max);
2666
+ const stepped = Math.round((clamped - this.min) / this.step) * this.step + this.min;
2667
+ const precision = Math.max(0, this._decimalPlaces(this.step));
2668
+ return parseFloat(stepped.toFixed(precision));
2669
+ }
2670
+ _decimalPlaces(n) {
2671
+ const s = String(n);
2672
+ if (s.indexOf('.') === -1)
2673
+ return 0;
2674
+ return s.length - s.indexOf('.') - 1;
2675
+ }
2676
+ /** Pointer events */
2677
+ onPointerDown(event) {
2678
+ if (this.disabled)
2679
+ return;
2680
+ event.target.setPointerCapture(event.pointerId);
2681
+ this.dragging = true;
2682
+ this._setFromPointer(event);
2683
+ }
2684
+ onPointerMove(event) {
2685
+ if (!this.dragging || this.disabled)
2686
+ return;
2687
+ this._setFromPointer(event);
2688
+ }
2689
+ onPointerUp(event) {
2690
+ if (this.disabled)
2691
+ return;
2692
+ try {
2693
+ event.target.releasePointerCapture(event.pointerId);
2694
+ }
2695
+ catch { }
2696
+ this.dragging = false;
2697
+ this.onTouched();
2698
+ }
2699
+ _setFromPointer(event) {
2700
+ const newVal = this._coordToValue(event.clientX, event.clientY);
2701
+ this._applyValue(newVal);
2702
+ }
2703
+ _coordToValue(clientX, clientY) {
2704
+ const rect = this.trackRef.nativeElement.getBoundingClientRect();
2705
+ let ratio = this.vertical
2706
+ ? 1 - (clientY - rect.top) / rect.height
2707
+ : (clientX - rect.left) / rect.width;
2708
+ ratio = clamp(ratio, 0, 1);
2709
+ const raw = this.min + ratio * (this.max - this.min);
2710
+ return this._normalizeValue(raw);
2711
+ }
2712
+ onTrackClick(event) {
2713
+ if (this.disabled)
2714
+ return;
2715
+ const newVal = this._coordToValue(event.clientX, event.clientY);
2716
+ this._applyValue(newVal);
2717
+ this.onTouched();
2718
+ }
2719
+ onKeydown(ev) {
2720
+ if (this.disabled)
2721
+ return;
2722
+ const key = ev.key;
2723
+ const largeStep = (this.max - this.min) / 10;
2724
+ let delta = 0;
2725
+ if (key === 'ArrowLeft' || key === 'ArrowDown')
2726
+ delta = -this.step;
2727
+ else if (key === 'ArrowRight' || key === 'ArrowUp')
2728
+ delta = this.step;
2729
+ else if (key === 'PageDown')
2730
+ delta = -largeStep;
2731
+ else if (key === 'PageUp')
2732
+ delta = largeStep;
2733
+ else if (key === 'Home')
2734
+ return this._applyValue(this.min);
2735
+ else if (key === 'End')
2736
+ return this._applyValue(this.max);
2737
+ if (delta !== 0) {
2738
+ this._applyValue(this.value + delta);
2739
+ ev.preventDefault();
2740
+ }
2741
+ }
2742
+ _applyValue(newVal) {
2743
+ const v = this._normalizeValue(newVal);
2744
+ if (v !== this.value) {
2745
+ this.value = v;
2746
+ this.ref.markForCheck();
2747
+ this.valueChange.emit(v);
2748
+ this.onChange(v);
2749
+ }
2750
+ }
2751
+ get percent() {
2752
+ if (this.max === this.min)
2753
+ return 0;
2754
+ return ((this.value - this.min) / (this.max - this.min)) * 100;
2755
+ }
2756
+ thumbStyle() {
2757
+ return this.vertical
2758
+ ? { bottom: `${this.percent}%`, transform: 'translateY(50%)' }
2759
+ : { left: `${this.percent}%`, transform: 'translateX(-50%)' };
2760
+ }
2761
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: SliderComponent, deps: [{ token: i1$3.NgControl }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
2762
+ 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", disabled: "disabled" }, 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 <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", styles: [""], dependencies: [{ kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }] });
2763
+ }
2764
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: SliderComponent, decorators: [{
2765
+ type: Component,
2766
+ 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" }]
2767
+ }], ctorParameters: () => [{ type: i1$3.NgControl }, { type: i0.ChangeDetectorRef }], propDecorators: { trackRef: [{
2768
+ type: ViewChild,
2769
+ args: ['track', { static: true }]
2770
+ }], thumbRef: [{
2771
+ type: ViewChild,
2772
+ args: ['thumb', { static: true }]
2773
+ }], min: [{
2774
+ type: Input
2775
+ }], max: [{
2776
+ type: Input
2777
+ }], step: [{
2778
+ type: Input
2779
+ }], vertical: [{
2780
+ type: Input
2781
+ }], disabled: [{
2782
+ type: Input
2783
+ }], valueChange: [{
2784
+ type: Output
2785
+ }], onKeydown: [{
2786
+ type: HostListener,
2787
+ args: ['keydown', ['$event']]
2788
+ }] } });
2789
+
2790
+ class SwitchComponent extends FxComponent {
2791
+ cdr;
2792
+ disabled = false;
2793
+ onSwitch = new EventEmitter();
2794
+ ngAfterViewInit() {
2795
+ this.cdr.detectChanges();
2796
+ }
2797
+ constructor(ngControl, cdr) {
2798
+ super(ngControl);
2799
+ this.cdr = cdr;
2800
+ }
2801
+ setDisabledState(isDisabled) {
2802
+ this.disabled = isDisabled;
2803
+ }
2804
+ toggle() {
2805
+ if (this.disabled)
2806
+ return;
2807
+ this.value = !this.value;
2808
+ this.onSwitch.emit(this.value);
2809
+ this.onChange(this.value);
2810
+ this.onTouched();
2811
+ }
2812
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: SwitchComponent, deps: [{ token: i1$3.NgControl }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
2813
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: SwitchComponent, isStandalone: false, selector: "fx-ui-switch", inputs: { disabled: "disabled" }, outputs: { onSwitch: "onSwitch" }, usesInheritance: true, ngImport: i0, template: "<div\n class=\"relative flex items-center w-10 h-5 rounded-full transition-colors duration-300 cursor-pointer\"\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\">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\">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</div>\n", styles: [""], dependencies: [{ kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] });
2814
+ }
2815
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: SwitchComponent, decorators: [{
2816
+ type: Component,
2817
+ args: [{ selector: 'fx-ui-switch', standalone: false, template: "<div\n class=\"relative flex items-center w-10 h-5 rounded-full transition-colors duration-300 cursor-pointer\"\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\">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\">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</div>\n" }]
2818
+ }], ctorParameters: () => [{ type: i1$3.NgControl }, { type: i0.ChangeDetectorRef }], propDecorators: { disabled: [{
2819
+ type: Input
2820
+ }], onSwitch: [{
2821
+ type: Output
2822
+ }] } });
2823
+
2824
+ class TabComponent {
2825
+ label;
2826
+ icon;
2827
+ content;
2828
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: TabComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2829
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.4", type: TabComponent, isStandalone: false, selector: "fx-ui-tab", inputs: { label: "label", icon: "icon" }, viewQueries: [{ propertyName: "content", first: true, predicate: TemplateRef, descendants: true, static: true }], ngImport: i0, template: "<ng-template><ng-content></ng-content></ng-template>\n" });
2830
+ }
2831
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: TabComponent, decorators: [{
2832
+ type: Component,
2833
+ args: [{ selector: 'fx-ui-tab', standalone: false, template: "<ng-template><ng-content></ng-content></ng-template>\n" }]
2834
+ }], propDecorators: { label: [{
2835
+ type: Input
2836
+ }], icon: [{
2837
+ type: Input
2838
+ }], content: [{
2839
+ type: ViewChild,
2840
+ args: [TemplateRef, { static: true }]
2841
+ }] } });
2842
+
2843
+ class TabGroupComponent {
2844
+ renderer;
2845
+ tabs;
2846
+ activeIndex = 0;
2847
+ tabListRef;
2848
+ underlineRef;
2849
+ canScrollLeft = false;
2850
+ canScrollRight = false;
2851
+ animateDelay = 50;
2852
+ scrollAmount = 160;
2853
+ sidePadding = 32; // same as button width space
2854
+ constructor(renderer) {
2855
+ this.renderer = renderer;
2856
+ }
2857
+ ngAfterContentInit() {
2858
+ setTimeout(() => {
2859
+ this.updateScrollButtons();
2860
+ this.updateUnderlinePosition();
2861
+ this.scrollToActiveTab(false);
2862
+ }, 0);
2863
+ }
2864
+ selectTab(index) {
2865
+ this.activeIndex = index;
2866
+ this.scrollToActiveTab(true);
2867
+ }
2868
+ updateUnderlinePosition() {
2869
+ const tabListEl = this.tabListRef.nativeElement;
2870
+ const underlineEl = this.underlineRef.nativeElement;
2871
+ const tabButtons = tabListEl.querySelectorAll('.tab-btn');
2872
+ const activeTab = tabButtons[this.activeIndex];
2873
+ if (!activeTab || !underlineEl)
2874
+ return;
2875
+ const tabRect = activeTab.getBoundingClientRect();
2876
+ const containerRect = tabListEl.getBoundingClientRect();
2877
+ // Calculate X position relative to scrollable area (not viewport)
2878
+ const translateX = tabRect.left - containerRect.left + tabListEl.scrollLeft - this.sidePadding;
2879
+ this.renderer.setStyle(underlineEl, 'width', `${activeTab.offsetWidth}px`);
2880
+ this.renderer.setStyle(underlineEl, 'transform', `translateX(${translateX}px)`);
2881
+ }
2882
+ scrollToActiveTab(updateUnderlineAfter = true) {
2883
+ const tabListEl = this.tabListRef.nativeElement;
2884
+ const tabButtons = tabListEl.querySelectorAll('.tab-btn');
2885
+ const activeTab = tabButtons[this.activeIndex];
2886
+ if (!activeTab)
2887
+ return;
2888
+ const elLeft = activeTab.offsetLeft;
2889
+ const elRight = elLeft + activeTab.offsetWidth;
2890
+ const containerLeft = tabListEl.scrollLeft;
2891
+ const containerRight = containerLeft + tabListEl.clientWidth;
2892
+ if (elLeft < containerLeft) {
2893
+ tabListEl.scrollTo({ left: Math.max(elLeft - 32, 0), behavior: 'smooth' });
2894
+ }
2895
+ else if (elRight > containerRight) {
2896
+ const target = elRight - tabListEl.clientWidth + 32;
2897
+ tabListEl.scrollTo({ left: target, behavior: 'smooth' });
2898
+ }
2899
+ setTimeout(() => {
2900
+ this.updateScrollButtons();
2901
+ this.updateUnderlinePosition();
2902
+ }, updateUnderlineAfter ? this.animateDelay : 0);
2903
+ }
2904
+ scroll(direction) {
2905
+ const tabListEl = this.tabListRef.nativeElement;
2906
+ const delta = direction === 'left' ? -this.scrollAmount : this.scrollAmount;
2907
+ tabListEl.scrollBy({ left: delta, behavior: 'smooth' });
2908
+ setTimeout(() => {
2909
+ this.updateScrollButtons();
2910
+ this.updateUnderlinePosition();
2911
+ }, this.animateDelay);
2912
+ }
2913
+ onScroll() {
2914
+ this.updateScrollButtons();
2915
+ this.updateUnderlinePosition();
2916
+ }
2917
+ updateScrollButtons() {
2918
+ const el = this.tabListRef.nativeElement;
2919
+ const scrollLeft = el.scrollLeft;
2920
+ const maxScrollLeft = el.scrollWidth - el.clientWidth;
2921
+ this.canScrollLeft = scrollLeft > 2;
2922
+ this.canScrollRight = scrollLeft < maxScrollLeft - 2;
2923
+ }
2924
+ onResize() {
2925
+ this.updateUnderlinePosition();
2926
+ }
2927
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: TabGroupComponent, deps: [{ token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Component });
2928
+ 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) {<fx-ui-hero-icon\n [icon]=\"tab.icon\"\n [size]=\"20\"\n class=\"flex mr-[2px]\"\n [ngClass]=\"{\n 'text-primary mb-small': i === activeIndex,\n 'text-text-placeholder': i !== activeIndex\n }\"\n ></fx-ui-hero-icon\n >}\n <div [ngClass]=\"i === activeIndex ? 'txt-field-label text-primary' : 'txt-placeholder'\">{{ tab.label }}</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(--primary));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;left:0;top:0;z-index:10;display:flex;height:100%;align-items:center;--tw-bg-opacity: 1;background-color:rgb(var(--bg-primary) / var(--tw-bg-opacity, 1));padding-left:.5rem;padding-right:.5rem;transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s}.right-scroll-button{position:absolute;right:0;top:0;z-index:10;display:flex;height:100%;align-items:center;--tw-bg-opacity: 1;background-color:rgb(var(--bg-primary) / var(--tw-bg-opacity, 1));padding-left:.5rem;padding-right:.5rem;transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s}.tab-bar-btn{position:relative;white-space:nowrap;padding:.5rem 1rem;font-size:.875rem;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s}\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: "component", type: HeroIconComponent, selector: "fx-ui-hero-icon", inputs: ["icon", "solid", "outline", "size", "color", "class"] }] });
2929
+ }
2930
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: TabGroupComponent, decorators: [{
2931
+ type: Component,
2932
+ 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) {<fx-ui-hero-icon\n [icon]=\"tab.icon\"\n [size]=\"20\"\n class=\"flex mr-[2px]\"\n [ngClass]=\"{\n 'text-primary mb-small': i === activeIndex,\n 'text-text-placeholder': i !== activeIndex\n }\"\n ></fx-ui-hero-icon\n >}\n <div [ngClass]=\"i === activeIndex ? 'txt-field-label text-primary' : 'txt-placeholder'\">{{ tab.label }}</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(--primary));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;left:0;top:0;z-index:10;display:flex;height:100%;align-items:center;--tw-bg-opacity: 1;background-color:rgb(var(--bg-primary) / var(--tw-bg-opacity, 1));padding-left:.5rem;padding-right:.5rem;transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s}.right-scroll-button{position:absolute;right:0;top:0;z-index:10;display:flex;height:100%;align-items:center;--tw-bg-opacity: 1;background-color:rgb(var(--bg-primary) / var(--tw-bg-opacity, 1));padding-left:.5rem;padding-right:.5rem;transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s}.tab-bar-btn{position:relative;white-space:nowrap;padding:.5rem 1rem;font-size:.875rem;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s}\n"] }]
2933
+ }], ctorParameters: () => [{ type: i0.Renderer2 }], propDecorators: { tabs: [{
2934
+ type: ContentChildren,
2935
+ args: [TabComponent]
2936
+ }], activeIndex: [{
2937
+ type: Input
2938
+ }], tabListRef: [{
2939
+ type: ViewChild,
2940
+ args: ['tabList', { static: true }]
2941
+ }], underlineRef: [{
2942
+ type: ViewChild,
2943
+ args: ['underline', { static: true }]
2944
+ }], onResize: [{
2945
+ type: HostListener,
2946
+ args: ['window:resize']
2947
+ }] } });
2948
+
2949
+ class TableCell {
2950
+ loading;
2951
+ skeletonClass = 'h-4 w-full';
2952
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: TableCell, deps: [], target: i0.ɵɵFactoryTarget.Component });
2953
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: TableCell, isStandalone: false, selector: "fx-ui-table-cell", inputs: { loading: "loading", skeletonClass: "skeletonClass" }, ngImport: i0, template: "@if (!loading) {\n <div animate.enter=\"fade-in-animation\" class=\"inline-block my-normal\">\n <ng-content />\n </div>\n} @else {\n <div animate.enter=\"fade-in-animation\" class=\"skeleton-load my-semi\" [class]=\"skeletonClass\"></div>\n}\n", styles: [""] });
2954
+ }
2955
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: TableCell, decorators: [{
2956
+ type: Component,
2957
+ args: [{ selector: 'fx-ui-table-cell', standalone: false, template: "@if (!loading) {\n <div animate.enter=\"fade-in-animation\" class=\"inline-block my-normal\">\n <ng-content />\n </div>\n} @else {\n <div animate.enter=\"fade-in-animation\" class=\"skeleton-load my-semi\" [class]=\"skeletonClass\"></div>\n}\n" }]
2958
+ }], propDecorators: { loading: [{
2959
+ type: Input,
2960
+ args: [{ required: true }]
2961
+ }], skeletonClass: [{
2962
+ type: Input
2963
+ }] } });
2964
+
2965
+ class TagComponent {
2966
+ label;
2967
+ type = 'default';
2968
+ rounded = true;
2969
+ icon;
2970
+ solid = false;
2971
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: TagComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2972
+ 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" }, ngImport: i0, template: "<div\n [class]=\"`flex flex-row gap-small ${rounded ? 'tag-round' : 'tag-square'} tag-${type}${solid ? '-solid' : ''}`\"\n>\n @if(icon){\n <fx-ui-hero-icon\n [class]=\"`flex txt-tag-${type} ${solid ? 'text-white': ''}`\"\n [icon]=\"icon\"\n [size]=\"18\"\n ></fx-ui-hero-icon>\n }\n\n <div [class]=\"`txt-tag-${type} ${solid ? 'text-white': ''}`\">\n {{ label }}\n </div>\n</div>\n", styles: [""], dependencies: [{ kind: "component", type: HeroIconComponent, selector: "fx-ui-hero-icon", inputs: ["icon", "solid", "outline", "size", "color", "class"] }] });
2973
+ }
2974
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: TagComponent, decorators: [{
2975
+ type: Component,
2976
+ args: [{ selector: 'fx-ui-tag', standalone: false, template: "<div\n [class]=\"`flex flex-row gap-small ${rounded ? 'tag-round' : 'tag-square'} tag-${type}${solid ? '-solid' : ''}`\"\n>\n @if(icon){\n <fx-ui-hero-icon\n [class]=\"`flex txt-tag-${type} ${solid ? 'text-white': ''}`\"\n [icon]=\"icon\"\n [size]=\"18\"\n ></fx-ui-hero-icon>\n }\n\n <div [class]=\"`txt-tag-${type} ${solid ? 'text-white': ''}`\">\n {{ label }}\n </div>\n</div>\n" }]
2977
+ }], propDecorators: { label: [{
2978
+ type: Input
2979
+ }], type: [{
2980
+ type: Input
2981
+ }], rounded: [{
2982
+ type: Input
2983
+ }], icon: [{
2984
+ type: Input
2985
+ }], solid: [{
2986
+ type: Input
2987
+ }] } });
2988
+
2989
+ class TreeDiagram {
2990
+ zone;
2991
+ cdr;
2992
+ data;
2993
+ container;
2994
+ root;
2995
+ children;
2996
+ lines = [];
2997
+ svgWidth = 0;
2998
+ svgHeight = 0;
2999
+ resizeObserver;
3000
+ rafId;
3001
+ constructor(zone, cdr) {
3002
+ this.zone = zone;
3003
+ this.cdr = cdr;
3004
+ }
3005
+ ngAfterViewInit() {
3006
+ this.zone.runOutsideAngular(() => {
3007
+ this.resizeObserver = new ResizeObserver((entries) => {
3008
+ const rect = entries[0].contentRect;
3009
+ if (rect.width === 0 || rect.height === 0)
3010
+ return;
3011
+ this.scheduleDraw();
3012
+ });
3013
+ this.resizeObserver.observe(this.container.nativeElement);
3014
+ setTimeout(() => this.scheduleDraw(), 50);
3015
+ setTimeout(() => this.scheduleDraw(), 150);
3016
+ });
3017
+ this.children.changes.subscribe(() => this.scheduleDraw());
3018
+ }
3019
+ ngOnDestroy() {
3020
+ this.resizeObserver?.disconnect();
3021
+ if (this.rafId)
3022
+ cancelAnimationFrame(this.rafId);
3023
+ }
3024
+ scheduleDraw() {
3025
+ if (this.rafId)
3026
+ cancelAnimationFrame(this.rafId);
3027
+ this.rafId = requestAnimationFrame(() => {
3028
+ this.drawLines();
3029
+ });
3030
+ }
3031
+ drawLines() {
3032
+ if (!this.root || !this.children.length)
3033
+ return;
3034
+ const containerEl = this.container.nativeElement;
3035
+ const rect = containerEl.getBoundingClientRect();
3036
+ if (rect.width === 0 || rect.height === 0)
3037
+ return;
3038
+ const rootRect = this.root.nativeElement.getBoundingClientRect();
3039
+ const rootX = rootRect.left + rootRect.width / 2 - rect.left;
3040
+ const rootBottomY = rootRect.bottom - rect.top;
3041
+ const childRects = this.children.map(c => c.nativeElement.getBoundingClientRect());
3042
+ const childCenters = childRects.map(r => ({
3043
+ x: r.left + r.width / 2 - rect.left,
3044
+ y: r.top - rect.top
3045
+ }));
3046
+ const horizontalY = childCenters[0].y - 24;
3047
+ this.zone.run(() => {
3048
+ this.svgWidth = rect.width;
3049
+ this.svgHeight = rect.height;
3050
+ this.lines = [
3051
+ {
3052
+ x1: rootX,
3053
+ y1: rootBottomY,
3054
+ x2: rootX,
3055
+ y2: horizontalY
3056
+ },
3057
+ {
3058
+ x1: childCenters[0].x,
3059
+ y1: horizontalY,
3060
+ x2: childCenters[childCenters.length - 1].x,
3061
+ y2: horizontalY
3062
+ },
3063
+ ...childCenters.map(c => ({
3064
+ x1: c.x,
3065
+ y1: horizontalY,
3066
+ x2: c.x,
3067
+ y2: c.y
3068
+ }))
3069
+ ];
3070
+ });
3071
+ this.cdr.detectChanges();
3072
+ }
3073
+ 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 });
3074
+ 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: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }] });
3075
+ }
3076
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: TreeDiagram, decorators: [{
3077
+ type: Component,
3078
+ args: [{ selector: 'fx-ui-tree-diagram', standalone: false, 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" }]
3079
+ }], ctorParameters: () => [{ type: i0.NgZone }, { type: i0.ChangeDetectorRef }], propDecorators: { data: [{
3080
+ type: Input
3081
+ }], container: [{
3082
+ type: ViewChild,
3083
+ args: ['container']
3084
+ }], root: [{
3085
+ type: ViewChild,
3086
+ args: ['root']
3087
+ }], children: [{
3088
+ type: ViewChildren,
3089
+ args: ['child']
3090
+ }] } });
3091
+
3092
+ class KanbanBoardComponent {
3093
+ columns = [];
3094
+ itemTemplate;
3095
+ columnChanged = new EventEmitter();
3096
+ /**
3097
+ * Get all column IDs for connecting drop lists
3098
+ */
3099
+ getColumnIds() {
3100
+ return this.columns.map((col) => col.id);
3101
+ }
3102
+ /**
3103
+ * Handle drop event when item is moved
3104
+ */
3105
+ onDrop(event, columnId) {
3106
+ const previousColumnId = event.previousContainer.id;
3107
+ const currentColumnId = columnId;
3108
+ if (event.previousContainer === event.container) {
3109
+ // Reorder within the same column
3110
+ moveItemInArray(event.container.data, event.previousIndex, event.currentIndex);
3111
+ }
3112
+ else {
3113
+ // Transfer between columns
3114
+ transferArrayItem(event.previousContainer.data, event.container.data, event.previousIndex, event.currentIndex);
3115
+ }
3116
+ // Emit the change event
3117
+ const item = event.container.data[event.currentIndex];
3118
+ this.columnChanged.emit({
3119
+ previousColumnId,
3120
+ currentColumnId,
3121
+ item,
3122
+ previousIndex: event.previousIndex,
3123
+ currentIndex: event.currentIndex,
3124
+ });
3125
+ }
3126
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: KanbanBoardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3127
+ 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"] }] });
3128
+ }
3129
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: KanbanBoardComponent, decorators: [{
3130
+ type: Component,
3131
+ 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: [".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"] }]
3132
+ }], propDecorators: { columns: [{
3133
+ type: Input
3134
+ }], itemTemplate: [{
3135
+ type: Input
3136
+ }], columnChanged: [{
3137
+ type: Output
3138
+ }] } });
3139
+
3140
+ class DrawerComponent {
3141
+ router;
3142
+ items = [];
3143
+ isExpanded = true;
3144
+ logo;
3145
+ toggleExpanded = new EventEmitter();
3146
+ itemClicked = new EventEmitter();
3147
+ expandedItems = new Set();
3148
+ currentRoute = '';
3149
+ destroy$ = new Subject();
3150
+ constructor(router) {
3151
+ this.router = router;
3152
+ }
3153
+ ngOnInit() {
3154
+ // Track current route
3155
+ this.currentRoute = this.router.url;
3156
+ // Subscribe to route changes
3157
+ this.router.events
3158
+ .pipe(filter((event) => event instanceof NavigationEnd), takeUntil(this.destroy$))
3159
+ .subscribe((event) => {
3160
+ this.currentRoute = event.urlAfterRedirects || event.url;
3161
+ this.autoExpandActiveParents();
3162
+ });
3163
+ // Auto-expand parents of active route on init
3164
+ this.autoExpandActiveParents();
3165
+ }
3166
+ ngOnDestroy() {
3167
+ this.destroy$.next();
3168
+ this.destroy$.complete();
3169
+ }
3170
+ toggleDrawer() {
3171
+ this.isExpanded = !this.isExpanded;
3172
+ this.toggleExpanded.emit(this.isExpanded);
3173
+ }
3174
+ toggleAccordion(itemId) {
3175
+ if (this.expandedItems.has(itemId)) {
3176
+ this.expandedItems.delete(itemId);
3177
+ }
3178
+ else {
3179
+ this.expandedItems.add(itemId);
3180
+ }
3181
+ }
3182
+ isAccordionExpanded(itemId) {
3183
+ return this.expandedItems.has(itemId);
3184
+ }
3185
+ onItemClick(item, event) {
3186
+ // If item has children, toggle accordion
3187
+ if (item.children && item.children.length > 0) {
3188
+ event?.preventDefault();
3189
+ this.toggleAccordion(item.id);
3190
+ return;
3191
+ }
3192
+ // Emit click event
3193
+ this.itemClicked.emit(item);
3194
+ // If item has action, execute it
3195
+ if (item.action) {
3196
+ event?.preventDefault();
3197
+ item.action();
3198
+ }
3199
+ // Navigation is handled by routerLink in template
3200
+ }
3201
+ isActiveRoute(route) {
3202
+ if (!route)
3203
+ return false;
3204
+ return this.currentRoute === route || this.currentRoute.startsWith(route + '/');
3205
+ }
3206
+ hasActiveChild(item) {
3207
+ if (!item.children)
3208
+ return false;
3209
+ return item.children.some((child) => {
3210
+ if (this.isActiveRoute(child.route))
3211
+ return true;
3212
+ if (child.children)
3213
+ return this.hasActiveChild(child);
3214
+ return false;
3215
+ });
3216
+ }
3217
+ autoExpandActiveParents() {
3218
+ const expandParents = (items) => {
3219
+ for (const item of items) {
3220
+ if (item.route && this.isActiveRoute(item.route)) {
3221
+ return true;
3222
+ }
3223
+ if (item.children) {
3224
+ const hasActiveChild = expandParents(item.children);
3225
+ if (hasActiveChild) {
3226
+ this.expandedItems.add(item.id);
3227
+ return true;
3228
+ }
3229
+ }
3230
+ }
3231
+ return false;
3232
+ };
3233
+ expandParents(this.items);
3234
+ }
3235
+ getAccordionState(itemId) {
3236
+ return this.isAccordionExpanded(itemId) ? 'expanded' : 'collapsed';
3237
+ }
3238
+ getDrawerState() {
3239
+ return this.isExpanded ? 'expanded' : 'collapsed';
3240
+ }
3241
+ 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 });
3242
+ 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" }, outputs: { toggleExpanded: "toggleExpanded", itemClicked: "itemClicked" }, ngImport: i0, template: "<div class=\"drawer-host-wrapper\">\n <a href=\"/\" class=\"h-[60px] p-4\">\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 ml-2': !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 </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{--tw-bg-opacity: 1;background-color:rgb(var(--primary) / var(--tw-bg-opacity, 1));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-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: i47.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: HeroIconComponent, selector: "fx-ui-hero-icon", inputs: ["icon", "solid", "outline", "size", "color", "class"] }], animations: [
3243
+ trigger('drawerExpand', [
3244
+ state('collapsed', style({ width: '70px' })),
3245
+ state('expanded', style({ width: '240px' })),
3246
+ transition('collapsed <=> expanded', animate('300ms cubic-bezier(0.4, 0, 0.2, 1)')),
3247
+ ]),
3248
+ trigger('accordionExpand', [
3249
+ state('collapsed', style({ height: '0', overflow: 'hidden', opacity: 0 })),
3250
+ state('expanded', style({ height: '*', overflow: 'visible', opacity: 1 })),
3251
+ transition('collapsed <=> expanded', animate('300ms cubic-bezier(0.4, 0, 0.2, 1)')),
3252
+ ]),
3253
+ ] });
3254
+ }
3255
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: DrawerComponent, decorators: [{
3256
+ type: Component,
3257
+ args: [{ selector: 'fx-ui-drawer', standalone: false, animations: [
3258
+ trigger('drawerExpand', [
3259
+ state('collapsed', style({ width: '70px' })),
3260
+ state('expanded', style({ width: '240px' })),
3261
+ transition('collapsed <=> expanded', animate('300ms cubic-bezier(0.4, 0, 0.2, 1)')),
3262
+ ]),
3263
+ trigger('accordionExpand', [
3264
+ state('collapsed', style({ height: '0', overflow: 'hidden', opacity: 0 })),
3265
+ state('expanded', style({ height: '*', overflow: 'visible', opacity: 1 })),
3266
+ transition('collapsed <=> expanded', animate('300ms cubic-bezier(0.4, 0, 0.2, 1)')),
3267
+ ]),
3268
+ ], template: "<div class=\"drawer-host-wrapper\">\n <a href=\"/\" class=\"h-[60px] p-4\">\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 ml-2': !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 </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{--tw-bg-opacity: 1;background-color:rgb(var(--primary) / var(--tw-bg-opacity, 1));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-container[style*=\"width: 64px\"] .drawer-item{justify-content:center;padding-left:16px!important}.drawer-container[style*=\"width: 64px\"] .item-content{justify-content:center}\n"] }]
3269
+ }], ctorParameters: () => [{ type: i1$4.Router }], propDecorators: { items: [{
3270
+ type: Input
3271
+ }], isExpanded: [{
3272
+ type: Input
3273
+ }], logo: [{
3274
+ type: Input
3275
+ }], toggleExpanded: [{
3276
+ type: Output
3277
+ }], itemClicked: [{
3278
+ type: Output
3279
+ }] } });
3280
+
3281
+ const FX_COMPONENTS = [
3282
+ InputComponent,
3283
+ SelectComponent,
3284
+ RadioButtonComponent,
3285
+ CheckboxComponent,
3286
+ DndUploadComponent,
3287
+ ButtonComponent,
3288
+ RadioButtonToggleComponent,
3289
+ DatetimePicker,
3290
+ LoadingPanel,
3291
+ SearchBarComponent,
3292
+ TabGroupComponent,
3293
+ TabComponent,
3294
+ HeroIconComponent,
3295
+ ToastComponent,
3296
+ ToastContainerComponent,
3297
+ TagComponent,
3298
+ ChartComponent,
3299
+ SliderComponent,
3300
+ SwitchComponent,
3301
+ RichTextAreaComponent,
3302
+ SkeletonTableLoadingComponent,
3303
+ FlowConnection,
3304
+ CircleProgressBar,
3305
+ TreeDiagram,
3306
+ TableCell,
3307
+ KanbanBoardComponent,
3308
+ DrawerComponent,
3309
+ ];
3310
+
3311
+ class ConfirmationDialogComponent {
3312
+ ref;
3313
+ bindings;
3314
+ loading = false;
3315
+ title = '';
3316
+ message = '';
3317
+ cancelLabel = 'Cancel';
3318
+ confirmLabel = 'Submit';
3319
+ constructor(ref, bindings) {
3320
+ this.ref = ref;
3321
+ this.bindings = bindings;
3322
+ this.title = _.get(bindings, 'title');
3323
+ this.message = _.get(bindings, 'message');
3324
+ this.cancelLabel = _.get(bindings, 'cancelLabel');
3325
+ this.confirmLabel = _.get(bindings, 'confirmLabel');
3326
+ }
3327
+ async accept() {
3328
+ this.loading = true;
3329
+ if (_.get(this.bindings, 'callback')) {
3330
+ await this.bindings.callback();
3331
+ this.loading = false;
3332
+ }
3333
+ this.ref.close(true);
3334
+ }
3335
+ cancel() {
3336
+ this.ref.close(false);
3337
+ }
3338
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: ConfirmationDialogComponent, deps: [{ token: i1$1.MatDialogRef }, { token: MAT_DIALOG_DATA }], target: i0.ɵɵFactoryTarget.Component });
3339
+ 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$1.MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "directive", type: i1$1.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "component", type: ButtonComponent, selector: "fx-ui-button", inputs: ["label", "disabled", "buttonVariant", "icon", "class", "loading"], outputs: ["clicked"] }] });
3340
+ }
3341
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: ConfirmationDialogComponent, decorators: [{
3342
+ type: Component,
3343
+ 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" }]
3344
+ }], ctorParameters: () => [{ type: i1$1.MatDialogRef }, { type: undefined, decorators: [{
3345
+ type: Inject,
3346
+ args: [MAT_DIALOG_DATA]
3347
+ }] }] });
3348
+
3349
+ const DIALOGS_COMPONENT = [
3350
+ ConfirmationDialogComponent
3351
+ ];
3352
+
3353
+ const MY_MOMENT_FORMATS = {
3354
+ parseInput: 'YYYY-MM-DD',
3355
+ fullPickerInput: 'YYYY-MM-DD HH:mm',
3356
+ datePickerInput: 'YYYY-MM-DD',
3357
+ timePickerInput: 'HH:mm',
3358
+ monthYearLabel: 'MMM YYYY',
3359
+ dateA11yLabel: 'LL',
3360
+ monthYearA11yLabel: 'MMMM YYYY',
3361
+ };
3362
+ class UiModule {
3363
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: UiModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
3364
+ 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,
3365
+ FormsModule,
3366
+ ReactiveFormsModule,
3367
+ RouterModule,
3368
+ OwlDateTimeModule,
3369
+ OwlNativeDateTimeModule,
3370
+ OwlMomentDateTimeModule,
3371
+ HasPermissionDirective,
3372
+ TrimOnBlurDirective,
3373
+ NgHeroiconsModule, i29.VflowComponent, i29.HandleComponent, i29.ResizableComponent, i29.SelectableDirective, i29.MiniMapComponent, i29.NodeToolbarComponent, i29.CustomTemplateEdgeComponent, i29.DragHandleDirective, i29.ConnectionControllerDirective, i29.NodeHtmlTemplateDirective, i29.NodeSvgTemplateDirective, i29.GroupNodeTemplateDirective, i29.EdgeLabelHtmlTemplateDirective, i29.EdgeTemplateDirective, i29.ConnectionTemplateDirective, i29.HandleTemplateDirective, i1$5.NgxEchartsModule, i3$1.MatSelectModule, i32.MatRadioModule, i33.MatButtonModule, i34.MatIconModule, i35.MatPaginatorModule, i36.MatTableModule, i37.MatSnackBarModule, i1$1.MatDialogModule, i39.MatCheckboxModule, i40.MatCardModule, i41.MatDatepickerModule, i42.MatTimepickerModule, i43.MatBadgeModule, i44.MatExpansionModule, i45.MatFormFieldModule, i46.MatMenuModule, i47.MatTooltipModule, i2$2.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, ConfirmationDialogComponent,
3374
+ // Module
3375
+ FormsModule,
3376
+ ReactiveFormsModule,
3377
+ RouterModule,
3378
+ OwlDateTimeModule,
3379
+ OwlNativeDateTimeModule,
3380
+ HasPermissionDirective,
3381
+ NgHeroiconsModule, i3$1.MatSelectModule, i32.MatRadioModule, i33.MatButtonModule, i34.MatIconModule, i35.MatPaginatorModule, i36.MatTableModule, i37.MatSnackBarModule, i1$1.MatDialogModule, i39.MatCheckboxModule, i40.MatCardModule, i41.MatDatepickerModule, i42.MatTimepickerModule, i43.MatBadgeModule, i44.MatExpansionModule, i45.MatFormFieldModule, i46.MatMenuModule, i47.MatTooltipModule, i2$2.DragDropModule] });
3382
+ static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: UiModule, providers: [
3383
+ importProvidersFrom(NgHeroiconsModule.forRoot()),
3384
+ { provide: OWL_DATE_TIME_FORMATS, useValue: MY_MOMENT_FORMATS },
3385
+ ], imports: [CommonModule,
3386
+ FormsModule,
3387
+ ReactiveFormsModule,
3388
+ RouterModule,
3389
+ OwlDateTimeModule,
3390
+ OwlNativeDateTimeModule,
3391
+ OwlMomentDateTimeModule,
3392
+ NgHeroiconsModule, i29.VflowComponent, i29.HandleComponent, i29.ResizableComponent, i29.MiniMapComponent, i29.NodeToolbarComponent, i29.CustomTemplateEdgeComponent, NgxEchartsModule.forRoot({ echarts: () => import('echarts') }), MATERIAL_MODULE,
3393
+ // Module
3394
+ FormsModule,
3395
+ ReactiveFormsModule,
3396
+ RouterModule,
3397
+ OwlDateTimeModule,
3398
+ OwlNativeDateTimeModule,
3399
+ NgHeroiconsModule, i3$1.MatSelectModule, i32.MatRadioModule, i33.MatButtonModule, i34.MatIconModule, i35.MatPaginatorModule, i36.MatTableModule, i37.MatSnackBarModule, i1$1.MatDialogModule, i39.MatCheckboxModule, i40.MatCardModule, i41.MatDatepickerModule, i42.MatTimepickerModule, i43.MatBadgeModule, i44.MatExpansionModule, i45.MatFormFieldModule, i46.MatMenuModule, i47.MatTooltipModule, i2$2.DragDropModule] });
3400
+ }
3401
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: UiModule, decorators: [{
3402
+ type: NgModule,
3403
+ args: [{
3404
+ declarations: [...FX_COMPONENTS, ...DIALOGS_COMPONENT],
3405
+ imports: [
3406
+ CommonModule,
3407
+ FormsModule,
3408
+ ReactiveFormsModule,
3409
+ RouterModule,
3410
+ OwlDateTimeModule,
3411
+ OwlNativeDateTimeModule,
3412
+ OwlMomentDateTimeModule,
3413
+ HasPermissionDirective,
3414
+ TrimOnBlurDirective,
3415
+ NgHeroiconsModule,
3416
+ ...Vflow,
3417
+ NgxEchartsModule.forRoot({ echarts: () => import('echarts') }),
3418
+ ...MATERIAL_MODULE,
3419
+ ],
3420
+ exports: [
3421
+ // Component
3422
+ ...FX_COMPONENTS,
3423
+ ...DIALOGS_COMPONENT,
3424
+ // Module
3425
+ FormsModule,
3426
+ ReactiveFormsModule,
3427
+ RouterModule,
3428
+ OwlDateTimeModule,
3429
+ OwlNativeDateTimeModule,
3430
+ HasPermissionDirective,
3431
+ NgHeroiconsModule,
3432
+ ...MATERIAL_MODULE,
3433
+ ],
3434
+ providers: [
3435
+ importProvidersFrom(NgHeroiconsModule.forRoot()),
3436
+ { provide: OWL_DATE_TIME_FORMATS, useValue: MY_MOMENT_FORMATS },
3437
+ ],
3438
+ }]
3439
+ }] });
3440
+
3441
+ /*
3442
+ * Public API Surface of ui
3443
+ */
3444
+
3445
+ /**
3446
+ * Generated bundle index. Do not edit.
3447
+ */
3448
+
3449
+ export { AuthInterceptor, AuthStateService, BaseComponent, BaseDialogComponent, BaseResolver, BaseTableComponent, BreadcrumbService, ButtonComponent, ChartComponent, CheckboxComponent, CircleProgressBar, ConfirmationDialogComponent, DatetimePicker, DndUploadComponent, DrawerComponent, FlowConnection, FxLoadingService, FxStorageService, FxToastrService, FxUtils, 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 };
3450
+ //# sourceMappingURL=foxeltech-angular-ui.mjs.map