@foxeltech/angular-ui 0.0.6 → 0.0.7

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.
@@ -156,6 +156,7 @@ class FxToastrService {
156
156
  appRef;
157
157
  containerRef;
158
158
  idCounter = 0;
159
+ recentMessages = new Set();
159
160
  constructor(appRef) {
160
161
  this.appRef = appRef;
161
162
  this.ensureContainerExists();
@@ -170,6 +171,12 @@ class FxToastrService {
170
171
  }
171
172
  }
172
173
  show(message, type, title) {
174
+ const dedupeKey = `${type}:${message}`;
175
+ if (this.recentMessages.has(dedupeKey)) {
176
+ return;
177
+ }
178
+ this.recentMessages.add(dedupeKey);
179
+ setTimeout(() => this.recentMessages.delete(dedupeKey), 2000);
173
180
  const toast = {
174
181
  id: ++this.idCounter,
175
182
  message,
@@ -700,7 +707,7 @@ class BaseTableComponent extends BaseComponent {
700
707
  capitalize(label) {
701
708
  return _.capitalize(label);
702
709
  }
703
- value(model, key, defaultValue) {
710
+ value(model, key, defaultValue = '---') {
704
711
  return _.get(model, key, defaultValue);
705
712
  }
706
713
  toggleExpand(element) {
@@ -1249,14 +1256,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImpor
1249
1256
 
1250
1257
  class AuthInterceptor {
1251
1258
  authState;
1259
+ handlingUnauthorized = false;
1252
1260
  constructor(authState) {
1253
1261
  this.authState = authState;
1254
1262
  }
1255
1263
  intercept(req, next) {
1256
1264
  const cloned = req.clone({ withCredentials: true });
1257
1265
  return next.handle(cloned).pipe(catchError((error) => {
1258
- if (error.status === 401) {
1266
+ if (error.status === 401 && !this.handlingUnauthorized) {
1267
+ this.handlingUnauthorized = true;
1259
1268
  this.authState.handleUnauthorized();
1269
+ setTimeout(() => {
1270
+ this.handlingUnauthorized = false;
1271
+ }, 2000);
1260
1272
  }
1261
1273
  return throwError(() => error);
1262
1274
  }));
@@ -2202,12 +2214,21 @@ class DndUploadComponent {
2202
2214
  accept = '';
2203
2215
  maxSizeMB = 10;
2204
2216
  strictSize = true;
2217
+ /** Icon name from heroicons (outline). Default: 'arrow-up-tray' */
2218
+ icon = 'arrow-up-tray';
2219
+ /** CSS color value for accent (border, icon, link). Default uses --gradient-primary-start */
2220
+ accentColor = '';
2205
2221
  fileDrop = new EventEmitter();
2206
2222
  fileClick = new EventEmitter();
2207
2223
  uploadComplete = new EventEmitter();
2208
2224
  uploadError = new EventEmitter();
2209
2225
  isDragOver = false;
2210
2226
  isOpening = false;
2227
+ get accentStyle() {
2228
+ if (!this.accentColor)
2229
+ return {};
2230
+ return { '--dnd-accent': this.accentColor };
2231
+ }
2211
2232
  onDragOver(event) {
2212
2233
  event.preventDefault();
2213
2234
  this.isDragOver = true;
@@ -2247,21 +2268,21 @@ class DndUploadComponent {
2247
2268
  const valid = [];
2248
2269
  const acceptedTypes = this.accept
2249
2270
  .split(',')
2250
- .map(t => t.trim().toLowerCase())
2251
- .filter(t => t);
2271
+ .map((t) => t.trim().toLowerCase())
2272
+ .filter((t) => t);
2252
2273
  for (const file of files) {
2253
2274
  const sizeMB = file.size / 1024 / 1024;
2254
2275
  if (this.strictSize && sizeMB > this.maxSizeMB) {
2255
2276
  this.uploadError.emit({
2256
2277
  file,
2257
- reason: `File too large: ${sizeMB.toFixed(2)} MB > ${this.maxSizeMB} MB`
2278
+ reason: `File too large: ${sizeMB.toFixed(2)} MB > ${this.maxSizeMB} MB`,
2258
2279
  });
2259
2280
  continue;
2260
2281
  }
2261
2282
  if (acceptedTypes.length && !this.isAccepted(file, acceptedTypes)) {
2262
2283
  this.uploadError.emit({
2263
2284
  file,
2264
- reason: `File type not allowed: "${file.name}"`
2285
+ reason: `File type not allowed: "${file.name}"`,
2265
2286
  });
2266
2287
  continue;
2267
2288
  }
@@ -2273,13 +2294,12 @@ class DndUploadComponent {
2273
2294
  }
2274
2295
  }
2275
2296
  onUpload(files) {
2276
- console.log('Uploading files:', files);
2277
2297
  this.uploadComplete.emit(files);
2278
2298
  }
2279
2299
  isAccepted(file, accepted) {
2280
2300
  const fileType = file.type.toLowerCase();
2281
2301
  const fileExt = '.' + file.name.split('.').pop().toLowerCase();
2282
- return accepted.some(a => {
2302
+ return accepted.some((a) => {
2283
2303
  if (a.startsWith('.')) {
2284
2304
  return a === fileExt;
2285
2305
  }
@@ -2290,11 +2310,11 @@ class DndUploadComponent {
2290
2310
  });
2291
2311
  }
2292
2312
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: DndUploadComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2293
- 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-gradient-primary-start\"\n [ngClass]=\"{\n 'border-border-default bg-bg-hover': !isDragOver,\n 'border-gradient-primary-start 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"] }] });
2313
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: DndUploadComponent, isStandalone: false, selector: "fx-ui-dnd-upload", inputs: { multiple: "multiple", accept: "accept", maxSizeMB: "maxSizeMB", strictSize: "strictSize", icon: "icon", accentColor: "accentColor" }, outputs: { fileDrop: "fileDrop", fileClick: "fileClick", uploadComplete: "uploadComplete", uploadError: "uploadError" }, ngImport: i0, template: "<div\n class=\"dnd-wrapper\"\n [ngStyle]=\"accentStyle\"\n (dragover)=\"onDragOver($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\"\n>\n <div\n role=\"button\"\n tabindex=\"0\"\n class=\"dnd-area\"\n [class.drag-over]=\"isDragOver\"\n (click)=\"onAreaClick(fileInput, $event)\"\n (keydown.enter)=\"onAreaClick(fileInput, $event)\"\n >\n <fx-ui-hero-icon\n [icon]=\"icon\"\n [size]=\"40\"\n class=\"flex dnd-icon\"\n ></fx-ui-hero-icon>\n <div class=\"dnd-text\">\n <span class=\"dnd-link\">Click to upload</span>\n <span class=\"dnd-or\">or drag and drop</span>\n </div>\n <div class=\"dnd-hint\">\n @if (accept) {\n Accepted: {{ accept }}\n } @else {\n Any file type\n }\n \u2014 Max {{ maxSizeMB }} MB\n </div>\n\n <input\n #fileInput\n type=\"file\"\n class=\"dnd-input\"\n [attr.multiple]=\"multiple ? '' : null\"\n [attr.accept]=\"accept\"\n (change)=\"onFileSelected($event)\"\n />\n </div>\n</div>\n", styles: [":host{--dnd-accent: rgba(var(--gradient-primary-start) / 1)}.dnd-wrapper{display:block;width:100%}.dnd-area{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;min-height:160px;padding:32px 24px;border:2px dashed color-mix(in srgb,var(--dnd-accent) 40%,transparent);border-radius:12px;background:color-mix(in srgb,var(--dnd-accent) 4%,transparent);cursor:pointer;outline:none;transition:all .2s ease;position:relative}.dnd-area:hover{border-color:color-mix(in srgb,var(--dnd-accent) 70%,transparent);background:color-mix(in srgb,var(--dnd-accent) 8%,transparent)}.dnd-area:focus-visible{box-shadow:0 0 0 3px color-mix(in srgb,var(--dnd-accent) 20%,transparent)}.dnd-area.drag-over{border-color:var(--dnd-accent);background:color-mix(in srgb,var(--dnd-accent) 10%,transparent)}.dnd-icon{color:var(--dnd-accent)}.dnd-text{display:flex;align-items:center;gap:4px;font-size:14px;flex-wrap:wrap;justify-content:center}.dnd-link{color:var(--dnd-accent);font-weight:600;text-decoration:underline}.dnd-or{color:rgba(var(--text-secondary)/1)}.dnd-hint{font-size:12px;color:rgba(var(--text-secondary)/.7)}.dnd-input{position:absolute;width:0;height:0;overflow:hidden;opacity:0}@media(max-width:640px){.dnd-area{min-height:120px;padding:24px 16px}.dnd-text{font-size:13px}}\n"], dependencies: [{ kind: "directive", type: i2.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: HeroIconComponent, selector: "fx-ui-hero-icon", inputs: ["stroke", "icon", "solid", "outline", "size", "color", "class"] }] });
2294
2314
  }
2295
2315
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: DndUploadComponent, decorators: [{
2296
2316
  type: Component,
2297
- 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-gradient-primary-start\"\n [ngClass]=\"{\n 'border-border-default bg-bg-hover': !isDragOver,\n 'border-gradient-primary-start 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" }]
2317
+ args: [{ selector: 'fx-ui-dnd-upload', standalone: false, template: "<div\n class=\"dnd-wrapper\"\n [ngStyle]=\"accentStyle\"\n (dragover)=\"onDragOver($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\"\n>\n <div\n role=\"button\"\n tabindex=\"0\"\n class=\"dnd-area\"\n [class.drag-over]=\"isDragOver\"\n (click)=\"onAreaClick(fileInput, $event)\"\n (keydown.enter)=\"onAreaClick(fileInput, $event)\"\n >\n <fx-ui-hero-icon\n [icon]=\"icon\"\n [size]=\"40\"\n class=\"flex dnd-icon\"\n ></fx-ui-hero-icon>\n <div class=\"dnd-text\">\n <span class=\"dnd-link\">Click to upload</span>\n <span class=\"dnd-or\">or drag and drop</span>\n </div>\n <div class=\"dnd-hint\">\n @if (accept) {\n Accepted: {{ accept }}\n } @else {\n Any file type\n }\n \u2014 Max {{ maxSizeMB }} MB\n </div>\n\n <input\n #fileInput\n type=\"file\"\n class=\"dnd-input\"\n [attr.multiple]=\"multiple ? '' : null\"\n [attr.accept]=\"accept\"\n (change)=\"onFileSelected($event)\"\n />\n </div>\n</div>\n", styles: [":host{--dnd-accent: rgba(var(--gradient-primary-start) / 1)}.dnd-wrapper{display:block;width:100%}.dnd-area{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;min-height:160px;padding:32px 24px;border:2px dashed color-mix(in srgb,var(--dnd-accent) 40%,transparent);border-radius:12px;background:color-mix(in srgb,var(--dnd-accent) 4%,transparent);cursor:pointer;outline:none;transition:all .2s ease;position:relative}.dnd-area:hover{border-color:color-mix(in srgb,var(--dnd-accent) 70%,transparent);background:color-mix(in srgb,var(--dnd-accent) 8%,transparent)}.dnd-area:focus-visible{box-shadow:0 0 0 3px color-mix(in srgb,var(--dnd-accent) 20%,transparent)}.dnd-area.drag-over{border-color:var(--dnd-accent);background:color-mix(in srgb,var(--dnd-accent) 10%,transparent)}.dnd-icon{color:var(--dnd-accent)}.dnd-text{display:flex;align-items:center;gap:4px;font-size:14px;flex-wrap:wrap;justify-content:center}.dnd-link{color:var(--dnd-accent);font-weight:600;text-decoration:underline}.dnd-or{color:rgba(var(--text-secondary)/1)}.dnd-hint{font-size:12px;color:rgba(var(--text-secondary)/.7)}.dnd-input{position:absolute;width:0;height:0;overflow:hidden;opacity:0}@media(max-width:640px){.dnd-area{min-height:120px;padding:24px 16px}.dnd-text{font-size:13px}}\n"] }]
2298
2318
  }], propDecorators: { multiple: [{
2299
2319
  type: Input
2300
2320
  }], accept: [{
@@ -2303,6 +2323,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImpor
2303
2323
  type: Input
2304
2324
  }], strictSize: [{
2305
2325
  type: Input
2326
+ }], icon: [{
2327
+ type: Input
2328
+ }], accentColor: [{
2329
+ type: Input
2306
2330
  }], fileDrop: [{
2307
2331
  type: Output
2308
2332
  }], fileClick: [{
@@ -2698,6 +2722,8 @@ class SelectComponent extends FxComponent {
2698
2722
  class;
2699
2723
  enableSearch = false;
2700
2724
  searchFn;
2725
+ /** Emits the current value when the select panel closes (useful for multiple mode). */
2726
+ closed = new EventEmitter();
2701
2727
  searchTerm = '';
2702
2728
  filteredOptions = [];
2703
2729
  search$ = new Subject();
@@ -2720,6 +2746,9 @@ class SelectComponent extends FxComponent {
2720
2746
  if (this.enableSearch)
2721
2747
  this.searchTerm = '';
2722
2748
  }
2749
+ else {
2750
+ this.closed.emit(this.value);
2751
+ }
2723
2752
  }
2724
2753
  onSearchChange(value) {
2725
2754
  if (!this.enableSearch)
@@ -2750,7 +2779,7 @@ class SelectComponent extends FxComponent {
2750
2779
  return this.value === value;
2751
2780
  }
2752
2781
  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 });
2753
- 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) {\n <span class=\"txt-required\">*</span>\n }\n </label>\n }\n\n <mat-form-field\n [attr.invalid]=\"invalid || null\"\n class=\"fx-select-field w-full shadow-sm\"\n appearance=\"fill\"\n >\n <mat-select\n [placeholder]=\"placeholder\"\n [disabled]=\"disabled\"\n [multiple]=\"multiple\"\n [value]=\"value\"\n (selectionChange)=\"onSelectionChange($event.value)\"\n (openedChange)=\"onOpened($event)\"\n (blur)=\"onTouched()\"\n >\n @if (enableSearch) {\n <div class=\"px-semi w-full\">\n <input\n type=\"text\"\n [placeholder]=\"'Search'\"\n [(ngModel)]=\"searchTerm\"\n (ngModelChange)=\"onSearchChange($event)\"\n (click)=\"$event.stopPropagation()\"\n class=\"text-text-primary placeholder:text-text-placeholder w-full bg-bg-primary border-b px-semi py-normal border-border-default focus:outline-none focus:border-gradient-primary-start text-sm text-text-primary tracking-normal ring-0 transition-colors duration-500 ease-in-out\"\n />\n </div>\n }\n <mat-option *ngFor=\"let option of filteredOptions\" [value]=\"option.value\">\n <span class=\"txt-default\">{{ option.label }}</span>\n </mat-option>\n </mat-select>\n </mat-form-field>\n\n @if (invalid) {\n <div class=\"txt-invalid text-left\">\n {{ getErrorMessage(label) }}\n </div>\n }\n</div>\n", styles: ["::ng-deep .fx-select-field{display:block!important;width:100%!important;margin:0!important;padding:0!important;border-radius:.75rem;box-shadow:0 1px 1px rgb(var(--border-default))}::ng-deep .fx-select-field .mat-mdc-form-field-wrapper,::ng-deep .fx-select-field .mat-mdc-text-field-wrapper{padding:0!important;margin:0!important;width:100%!important}::ng-deep .fx-select-field .mdc-text-field{width:100%!important;margin:0!important;padding:0!important;--mdc-shape-small: 0 !important}::ng-deep .fx-select-field .mat-mdc-form-field-flex{width:100%!important;margin:0!important;padding:0 .75rem!important;border:1px solid rgb(var(--border-default));border-radius:.375rem;height:36px!important;align-items:center;background-color:rgb(var(--bg-primary))}::ng-deep .fx-select-field .mdc-notched-outline,::ng-deep .fx-select-field .mat-mdc-form-field-subscript-wrapper{display:none!important}::ng-deep .fx-select-field.mat-focused .mat-mdc-form-field-flex{border:2px solid rgb(var(--gradient-primary-start))!important;outline:none!important;border-radius:.375rem}::ng-deep .fx-select-field.mat-form-field-invalid .mat-mdc-form-field-flex{border:1px solid rgb(var(--bg-error-bold))!important;box-shadow:none!important}::ng-deep mat-form-field[invalid] .mat-mdc-form-field-flex{border:1px solid rgb(var(--bg-error-bold))!important;box-shadow:none!important}::ng-deep .fx-select-field .mat-mdc-select-trigger{padding:0!important;margin:0!important}::ng-deep .fx-select-field .mat-mdc-select-arrow-wrapper{margin-right:0!important}::ng-deep .fx-select-field .mat-mdc-select-arrow-wrapper{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:.375rem!important;box-shadow:0 2px 4px #0000001a!important}::ng-deep .fx-select-field .mat-mdc-select-placeholder{color:rgb(var(--text-placeholder))!important;font-size:14px}::ng-deep .fx-select-field .mat-mdc-select-value{color:rgb(var(--text-primary))!important;font-size:14px;font-weight:400}.mat-select-panel .search-option-full-width{padding-left:0!important;padding-right:0!important;display:block;pointer-events:none;line-height:initial;height:auto}.mat-select-panel .search-option-full-width .search-bar{width:100%;padding:8px 16px;border:none;border-bottom:1px solid #ddd;box-sizing:border-box}\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"] }] });
2782
+ 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" }, outputs: { closed: "closed" }, 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) {\n <span class=\"txt-required\">*</span>\n }\n </label>\n }\n\n <mat-form-field\n [attr.invalid]=\"invalid || null\"\n class=\"fx-select-field w-full shadow-sm\"\n appearance=\"fill\"\n >\n <mat-select\n [placeholder]=\"placeholder\"\n [disabled]=\"disabled\"\n [multiple]=\"multiple\"\n [value]=\"value\"\n (selectionChange)=\"onSelectionChange($event.value)\"\n (openedChange)=\"onOpened($event)\"\n (blur)=\"onTouched()\"\n >\n @if (enableSearch) {\n <div class=\"px-semi w-full\">\n <input\n type=\"text\"\n [placeholder]=\"'Search'\"\n [(ngModel)]=\"searchTerm\"\n (ngModelChange)=\"onSearchChange($event)\"\n (click)=\"$event.stopPropagation()\"\n class=\"text-text-primary placeholder:text-text-placeholder w-full bg-bg-primary border-b px-semi py-normal border-border-default focus:outline-none focus:border-gradient-primary-start text-sm text-text-primary tracking-normal ring-0 transition-colors duration-500 ease-in-out\"\n />\n </div>\n }\n <mat-option *ngFor=\"let option of filteredOptions\" [value]=\"option.value\">\n <span class=\"txt-default\">{{ option.label }}</span>\n </mat-option>\n </mat-select>\n </mat-form-field>\n\n @if (invalid) {\n <div class=\"txt-invalid text-left\">\n {{ getErrorMessage(label) }}\n </div>\n }\n</div>\n", styles: ["::ng-deep .fx-select-field{display:block!important;width:100%!important;margin:0!important;padding:0!important;border-radius:.75rem;box-shadow:0 1px 1px rgb(var(--border-default))}::ng-deep .fx-select-field .mat-mdc-form-field-wrapper,::ng-deep .fx-select-field .mat-mdc-text-field-wrapper{padding:0!important;margin:0!important;width:100%!important}::ng-deep .fx-select-field .mdc-text-field{width:100%!important;margin:0!important;padding:0!important;--mdc-shape-small: 0 !important}::ng-deep .fx-select-field .mat-mdc-form-field-flex{width:100%!important;margin:0!important;padding:0 .75rem!important;border:1px solid rgb(var(--border-default));border-radius:.375rem;height:36px!important;align-items:center;background-color:rgb(var(--bg-primary))}::ng-deep .fx-select-field .mdc-notched-outline,::ng-deep .fx-select-field .mat-mdc-form-field-subscript-wrapper{display:none!important}::ng-deep .fx-select-field.mat-focused .mat-mdc-form-field-flex{border:2px solid rgb(var(--gradient-primary-start))!important;outline:none!important;border-radius:.375rem}::ng-deep .fx-select-field.mat-form-field-invalid .mat-mdc-form-field-flex{border:1px solid rgb(var(--bg-error-bold))!important;box-shadow:none!important}::ng-deep mat-form-field[invalid] .mat-mdc-form-field-flex{border:1px solid rgb(var(--bg-error-bold))!important;box-shadow:none!important}::ng-deep .fx-select-field .mat-mdc-select-trigger{padding:0!important;margin:0!important}::ng-deep .fx-select-field .mat-mdc-select-arrow-wrapper{margin-right:0!important}::ng-deep .fx-select-field .mat-mdc-select-arrow-wrapper{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:.375rem!important;box-shadow:0 2px 4px #0000001a!important}::ng-deep .fx-select-field .mat-mdc-select-placeholder{color:rgb(var(--text-placeholder))!important;font-size:14px}::ng-deep .fx-select-field .mat-mdc-select-value{color:rgb(var(--text-primary))!important;font-size:14px;font-weight:400}.mat-select-panel .search-option-full-width{padding-left:0!important;padding-right:0!important;display:block;pointer-events:none;line-height:initial;height:auto}.mat-select-panel .search-option-full-width .search-bar{width:100%;padding:8px 16px;border:none;border-bottom:1px solid #ddd;box-sizing:border-box}\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"] }] });
2754
2783
  }
2755
2784
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: SelectComponent, decorators: [{
2756
2785
  type: Component,
@@ -2775,6 +2804,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImpor
2775
2804
  type: Input
2776
2805
  }], searchFn: [{
2777
2806
  type: Input
2807
+ }], closed: [{
2808
+ type: Output
2778
2809
  }] } });
2779
2810
 
2780
2811
  class SkeletonTableLoadingComponent {
@@ -3128,12 +3159,16 @@ class TagComponent {
3128
3159
  rounded = true;
3129
3160
  icon;
3130
3161
  solid = false;
3162
+ click = new EventEmitter();
3163
+ onClick() {
3164
+ this.click.emit();
3165
+ }
3131
3166
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: TagComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3132
- 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]=\"\n `flex flex-row gap-small ${rounded ? 'tag-round' : 'tag-square'} tag-${type}${solid ? '-solid' : ''}`\n \"\n>\n @if (icon) {\n <fx-ui-hero-icon\n [class]=\"`flex txt-tag-${type} ${solid && type !== 'default' ? 'text-white' : ''}`\"\n [icon]=\"icon\"\n [size]=\"18\"\n ></fx-ui-hero-icon>\n }\n\n <div [class]=\"`txt-tag-${type} ${solid && type !== 'default' ? 'text-white font-medium' : ''}`\">\n {{ label }}\n </div>\n</div>\n", styles: [""], dependencies: [{ kind: "component", type: HeroIconComponent, selector: "fx-ui-hero-icon", inputs: ["stroke", "icon", "solid", "outline", "size", "color", "class"] }] });
3167
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: TagComponent, isStandalone: false, selector: "fx-ui-tag", inputs: { label: "label", type: "type", rounded: "rounded", icon: "icon", solid: "solid" }, outputs: { click: "click" }, ngImport: i0, template: "<div\n (click)=\"onClick()\"\n [class]=\"\n `flex flex-row gap-small ${rounded ? 'tag-round' : 'tag-square'} tag-${type}${solid ? '-solid' : ''}`\n \"\n>\n @if (icon) {\n <fx-ui-hero-icon\n [class]=\"`flex txt-tag-${type} ${solid && type !== 'default' ? 'text-white' : ''}`\"\n [icon]=\"icon\"\n [size]=\"18\"\n ></fx-ui-hero-icon>\n }\n\n <div [class]=\"`txt-tag-${type} ${solid && type !== 'default' ? 'text-white font-medium' : ''}`\">\n {{ label }}\n </div>\n</div>\n", styles: [""], dependencies: [{ kind: "component", type: HeroIconComponent, selector: "fx-ui-hero-icon", inputs: ["stroke", "icon", "solid", "outline", "size", "color", "class"] }] });
3133
3168
  }
3134
3169
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: TagComponent, decorators: [{
3135
3170
  type: Component,
3136
- args: [{ selector: 'fx-ui-tag', standalone: false, template: "<div\n [class]=\"\n `flex flex-row gap-small ${rounded ? 'tag-round' : 'tag-square'} tag-${type}${solid ? '-solid' : ''}`\n \"\n>\n @if (icon) {\n <fx-ui-hero-icon\n [class]=\"`flex txt-tag-${type} ${solid && type !== 'default' ? 'text-white' : ''}`\"\n [icon]=\"icon\"\n [size]=\"18\"\n ></fx-ui-hero-icon>\n }\n\n <div [class]=\"`txt-tag-${type} ${solid && type !== 'default' ? 'text-white font-medium' : ''}`\">\n {{ label }}\n </div>\n</div>\n" }]
3171
+ args: [{ selector: 'fx-ui-tag', standalone: false, template: "<div\n (click)=\"onClick()\"\n [class]=\"\n `flex flex-row gap-small ${rounded ? 'tag-round' : 'tag-square'} tag-${type}${solid ? '-solid' : ''}`\n \"\n>\n @if (icon) {\n <fx-ui-hero-icon\n [class]=\"`flex txt-tag-${type} ${solid && type !== 'default' ? 'text-white' : ''}`\"\n [icon]=\"icon\"\n [size]=\"18\"\n ></fx-ui-hero-icon>\n }\n\n <div [class]=\"`txt-tag-${type} ${solid && type !== 'default' ? 'text-white font-medium' : ''}`\">\n {{ label }}\n </div>\n</div>\n" }]
3137
3172
  }], propDecorators: { label: [{
3138
3173
  type: Input
3139
3174
  }], type: [{
@@ -3144,6 +3179,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImpor
3144
3179
  type: Input
3145
3180
  }], solid: [{
3146
3181
  type: Input
3182
+ }], click: [{
3183
+ type: Output
3147
3184
  }] } });
3148
3185
 
3149
3186
  class TreeDiagram {