@zambon-dev/library 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/fesm2022/zambon-dev-library.mjs +3795 -0
  2. package/fesm2022/zambon-dev-library.mjs.map +1 -0
  3. package/index.d.ts +10 -0
  4. package/lib/components/base.component.d.ts +9 -0
  5. package/lib/components/catalog-select/catalog-select.component.d.ts +112 -0
  6. package/lib/components/data-grid/data-grid.component.d.ts +49 -0
  7. package/lib/components/data-grid-row/data-grid-row.component.d.ts +30 -0
  8. package/lib/components/form-group/form-group.component.d.ts +8 -0
  9. package/lib/components/form-input/form-input.component.d.ts +47 -0
  10. package/lib/components/form-input-group/form-input-group.component.d.ts +40 -0
  11. package/lib/components/group-accordion/group-accordion.component.d.ts +13 -0
  12. package/lib/components/group-container/group-container.component.d.ts +16 -0
  13. package/lib/components/group-scroll-spy/group-scroll-spy.component.d.ts +14 -0
  14. package/lib/components/index.d.ts +18 -0
  15. package/lib/components/modal/modal.component.d.ts +26 -0
  16. package/lib/components/multi-editor/multi-editor.component.d.ts +30 -0
  17. package/lib/components/multi-select/multi-select.component.d.ts +25 -0
  18. package/lib/components/multi-select/result-grid/result-grid.component.d.ts +18 -0
  19. package/lib/components/ribbon/ribbon.component.d.ts +5 -0
  20. package/lib/components/ribbon-button/ribbon-button.component.d.ts +31 -0
  21. package/lib/components/ribbon-group/ribbon-group.component.d.ts +10 -0
  22. package/lib/components/sidebar/sidebar.component.d.ts +34 -0
  23. package/lib/components/sidebar-item/sidebar-item.component.d.ts +30 -0
  24. package/lib/directives/index.d.ts +1 -0
  25. package/lib/directives/scroll-spy.directive.d.ts +16 -0
  26. package/lib/helpers/date-helpers.d.ts +3 -0
  27. package/lib/helpers/guid-helper.d.ts +3 -0
  28. package/lib/helpers/index.d.ts +3 -0
  29. package/lib/helpers/route-formatter.d.ts +4 -0
  30. package/lib/library.module.d.ts +27 -0
  31. package/lib/models/batch-update.d.ts +4 -0
  32. package/lib/models/catalog-entry.d.ts +4 -0
  33. package/lib/models/catalog-result.d.ts +5 -0
  34. package/lib/models/configs/data-grid-configs.d.ts +9 -0
  35. package/lib/models/configs/grid-configs.d.ts +9 -0
  36. package/lib/models/configs/index.d.ts +2 -0
  37. package/lib/models/data-grid-parameters.d.ts +12 -0
  38. package/lib/models/grid-column.d.ts +9 -0
  39. package/lib/models/index.d.ts +13 -0
  40. package/lib/models/modal.interface.d.ts +7 -0
  41. package/lib/models/multi-editor-changes.d.ts +6 -0
  42. package/lib/models/multi-selector-changes.d.ts +4 -0
  43. package/lib/models/ribbon-button-option.d.ts +13 -0
  44. package/lib/models/ribbon-group-child.d.ts +3 -0
  45. package/lib/models/sidebar/index.d.ts +3 -0
  46. package/lib/models/sidebar/sidebar-configs.d.ts +9 -0
  47. package/lib/models/sidebar/sidebar-menu.d.ts +12 -0
  48. package/lib/models/sidebar/sidebar-profile.d.ts +5 -0
  49. package/lib/models/tab-view.d.ts +5 -0
  50. package/lib/pipes/enum.d.ts +12 -0
  51. package/lib/pipes/index.d.ts +3 -0
  52. package/lib/pipes/replace-many.pipe.d.ts +9 -0
  53. package/lib/pipes/replace.pipe.d.ts +7 -0
  54. package/lib/services/catalog.service.d.ts +11 -0
  55. package/lib/services/configs-providers/data-grid-configs.provider.d.ts +8 -0
  56. package/lib/services/configs-providers/grid-configs.provider.d.ts +9 -0
  57. package/lib/services/configs-providers/index.d.ts +2 -0
  58. package/lib/services/data-providers/data-provider.service.d.ts +29 -0
  59. package/lib/services/data-providers/index.d.ts +1 -0
  60. package/lib/services/datasets/base.dataset.d.ts +15 -0
  61. package/lib/services/datasets/data-grid.dataset.d.ts +33 -0
  62. package/lib/services/datasets/grid.dataset.d.ts +55 -0
  63. package/lib/services/datasets/index.d.ts +5 -0
  64. package/lib/services/datasets/multi-editor.dataset.d.ts +22 -0
  65. package/lib/services/datasets/multi-select-result.dataset.d.ts +26 -0
  66. package/lib/services/form-group.service.d.ts +5 -0
  67. package/lib/services/form.service.d.ts +39 -0
  68. package/lib/services/index.d.ts +6 -0
  69. package/lib/services/sidebar.service.d.ts +27 -0
  70. package/lib/validators/date.validators.d.ts +13 -0
  71. package/lib/validators/index.d.ts +1 -0
  72. package/package.json +49 -0
@@ -0,0 +1,3795 @@
1
+ import * as i0 from '@angular/core';
2
+ import { Component, inject, Injectable, EventEmitter, InjectionToken, Input, Output, KeyValueDiffers, ViewContainerRef, TemplateRef, ElementRef, HostListener, ViewChild, ChangeDetectorRef, ChangeDetectionStrategy, NgZone, Directive, ContentChildren, Pipe, NgModule } from '@angular/core';
3
+ import { Subject, ReplaySubject, take, tap, map, takeUntil, filter, debounceTime, startWith, pairwise, Observable, forkJoin, switchMap, of, delay } from 'rxjs';
4
+ import { Overlay, OverlayPositionBuilder } from '@angular/cdk/overlay';
5
+ import { TemplatePortal } from '@angular/cdk/portal';
6
+ import * as i1 from '@angular/common';
7
+ import { formatDate, CommonModule, NgIf, NgSwitch, NgSwitchCase, KeyValuePipe, NgFor, NgStyle, NgTemplateOutlet, NgClass } from '@angular/common';
8
+ import * as i1$1 from '@angular/forms';
9
+ import { FormGroupDirective, FormGroupName, FormsModule, ReactiveFormsModule, ControlContainer, FormControl, FormArray, FormGroup } from '@angular/forms';
10
+ import { TranslatePipe, TranslateService } from '@ngx-translate/core';
11
+ import { HttpClient } from '@angular/common/http';
12
+ import { ActivatedRoute, Router, NavigationStart, NavigationEnd } from '@angular/router';
13
+ import * as i1$2 from '@angular/cdk/scrolling';
14
+ import { CdkVirtualScrollViewport, ScrollingModule } from '@angular/cdk/scrolling';
15
+ import * as i2 from 'ngx-resize-observer';
16
+ import { NgxResizeObserverModule } from 'ngx-resize-observer';
17
+
18
+ class BaseComponent {
19
+ //#region ViewChilds, Inputs, Outputs
20
+ //#endregion
21
+ //#region Host listeners
22
+ //#endregion
23
+ //#region Variables
24
+ destroy$ = new Subject();
25
+ //#endregion
26
+ //#region Properties
27
+ //#endregion
28
+ //#region Constructor and Angular life cycle methods
29
+ ngOnDestroy() {
30
+ this.destroy$.next(true);
31
+ this.destroy$.complete();
32
+ }
33
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: BaseComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
34
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.6", type: BaseComponent, isStandalone: true, selector: "ng-component", ngImport: i0, template: '', isInline: true });
35
+ }
36
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: BaseComponent, decorators: [{
37
+ type: Component,
38
+ args: [{ template: '' }]
39
+ }] });
40
+
41
+ class CatalogService {
42
+ //#region ViewChilds, Inputs, Outputs
43
+ //#endregion
44
+ //#region Variables
45
+ http = inject(HttpClient);
46
+ //#endregion
47
+ //#region Properties
48
+ //#endregion
49
+ //#region Constructor and Angular life cycle methods
50
+ //#endregion
51
+ //#region Event handlers
52
+ //#endregion
53
+ //#region Public methods
54
+ search(endpoint, maxResults, criteria, filters = null) {
55
+ return this.http.post(`${endpoint}`, { maxResults, criteria, filters });
56
+ }
57
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: CatalogService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
58
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: CatalogService, providedIn: 'root' });
59
+ }
60
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: CatalogService, decorators: [{
61
+ type: Injectable,
62
+ args: [{
63
+ providedIn: 'root'
64
+ }]
65
+ }] });
66
+
67
+ class DataProviderService {
68
+ //#region ViewChilds, Inputs, Outputs
69
+ //#endregion
70
+ //#region Variables
71
+ activatedRoute = inject(ActivatedRoute);
72
+ destroy$ = new Subject();
73
+ shouldLazyLoad = false;
74
+ _entityID;
75
+ errorSubject = new Subject();
76
+ isLoading = false;
77
+ isModelLoaded = false;
78
+ modelCache = new ReplaySubject(1);
79
+ //#endregion
80
+ //#region Properties
81
+ get entityID() {
82
+ return this._entityID;
83
+ }
84
+ get hasEntityID() {
85
+ return !!this.entityID;
86
+ }
87
+ //#endregion
88
+ //#region Constructor and Angular life cycle methods
89
+ constructor() {
90
+ this.activatedRoute.paramMap
91
+ .pipe(take(1))
92
+ .subscribe((paramMap) => {
93
+ const newID = Number(paramMap.get('id'));
94
+ if (newID !== this.entityID) {
95
+ this._entityID = newID;
96
+ // Need to wait for the data-provider to be initialized.
97
+ queueMicrotask(() => {
98
+ // When lazy loading, the model should be loaded by the consuming component.
99
+ if (!this.shouldLazyLoad) {
100
+ this.refreshModel();
101
+ }
102
+ });
103
+ }
104
+ });
105
+ }
106
+ ngOnDestroy() {
107
+ this.destroy$.next(true);
108
+ this.destroy$.complete();
109
+ }
110
+ //#endregion
111
+ //#region Event handlers
112
+ //#endregion
113
+ //#region Public methods
114
+ getError$() {
115
+ return this.errorSubject.asObservable();
116
+ }
117
+ getModel$() {
118
+ if (!this.isModelLoaded && !this.shouldLazyLoad) {
119
+ this.refreshModel();
120
+ }
121
+ return this.modelCache.asObservable();
122
+ }
123
+ refreshModel() {
124
+ if (this.isLoading) {
125
+ return;
126
+ }
127
+ this.isLoading = true;
128
+ this.loadModel(this.entityID)
129
+ .pipe(take(1))
130
+ .subscribe({
131
+ next: (model) => {
132
+ this.isLoading = false;
133
+ this.updateModel(model);
134
+ },
135
+ error: (error) => {
136
+ this.isLoading = false;
137
+ this.isModelLoaded = true;
138
+ this.modelCache.next(null);
139
+ this.errorSubject.next(error);
140
+ },
141
+ });
142
+ }
143
+ resetForNewEntity() {
144
+ this._entityID = undefined;
145
+ this.isModelLoaded = false;
146
+ this.modelCache.next(null);
147
+ }
148
+ updateModel(model) {
149
+ this.isModelLoaded = true;
150
+ // Update entity ID if changed.
151
+ if (model && model.id !== this.entityID) {
152
+ this._entityID = model.id;
153
+ }
154
+ this.modelCache.next(model);
155
+ }
156
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: DataProviderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
157
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: DataProviderService });
158
+ }
159
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: DataProviderService, decorators: [{
160
+ type: Injectable
161
+ }], ctorParameters: () => [] });
162
+
163
+ class BaseDataset {
164
+ //#region ViewChilds, Inputs, Outputs
165
+ //#endregion
166
+ //#region Variables
167
+ dataProvider;
168
+ destroy$ = new Subject();
169
+ // TODO: Remove.
170
+ _parentEntityId;
171
+ //#endregion
172
+ //#region Properties
173
+ // TODO: Remove.
174
+ get parentEntityId() {
175
+ return this._parentEntityId;
176
+ }
177
+ set parentEntityId(value) {
178
+ this._parentEntityId = value;
179
+ }
180
+ //#endregion
181
+ //#region Constructor and Angular life cycle methods
182
+ constructor() {
183
+ this.dataProvider = inject(DataProviderService, { optional: true });
184
+ }
185
+ ngOnDestroy() {
186
+ this.destroy$.next(true);
187
+ this.destroy$.complete();
188
+ }
189
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: BaseDataset, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
190
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: BaseDataset });
191
+ }
192
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: BaseDataset, decorators: [{
193
+ type: Injectable
194
+ }], ctorParameters: () => [] });
195
+
196
+ class DateHelpers {
197
+ //#region Public methods
198
+ static diffDays(initialDate, finalDate) {
199
+ const initialDateOnly = new Date(initialDate.getFullYear(), initialDate.getMonth(), initialDate.getDate());
200
+ const finalDateOnly = new Date(finalDate.getFullYear(), finalDate.getMonth(), finalDate.getDate());
201
+ const diff = initialDateOnly.getTime() - finalDateOnly.getTime();
202
+ return Math.floor(diff / (1000 * 60 * 60 * 24));
203
+ }
204
+ }
205
+
206
+ class GuidHelper {
207
+ static generateGUID() {
208
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
209
+ const r = (Math.random() * 16) | 0;
210
+ const v = c === 'x' ? r : (r & 0x3) | 0x8;
211
+ return v.toString(16);
212
+ });
213
+ }
214
+ }
215
+
216
+ class RouterFormatter {
217
+ //#region ViewChilds, Inputs, Outputs
218
+ //#endregion
219
+ //#region Variables
220
+ //#endregion
221
+ //#region Properties
222
+ //#endregion
223
+ //#region Constructor and Angular life cycle methods
224
+ //#endregion
225
+ //#region Event handlers
226
+ //#endregion
227
+ //#region Public methods
228
+ static getURL(route) {
229
+ let parentRoute = '';
230
+ if (route.parent) {
231
+ parentRoute = this.getURL(route.parent);
232
+ }
233
+ let path = route.routeConfig?.path ?? '';
234
+ route.paramMap.keys
235
+ .forEach(k => {
236
+ path = path.replace(':' + k, k === 'view'
237
+ ? ''
238
+ : (route.paramMap.get(k) ?? ''));
239
+ });
240
+ if (!route.parent || (parentRoute !== '' && parentRoute !== '/' && route.routeConfig?.path && path)) {
241
+ parentRoute += '/';
242
+ }
243
+ return parentRoute + path;
244
+ }
245
+ }
246
+
247
+ class GridConfigsProvider {
248
+ //#region ViewChilds, Inputs, Outputs
249
+ //#endregion
250
+ //#region Variables
251
+ _configs;
252
+ //#endregion
253
+ //#region Properties
254
+ get configs() {
255
+ return this._configs;
256
+ }
257
+ //#endregion
258
+ //#region Constructor and Angular life cycle methods
259
+ constructor() {
260
+ this._configs = {
261
+ loadingDisplayText: 'Loading...',
262
+ messageOnEmpty: 'No results',
263
+ recordBlockSize: 100,
264
+ rowHeight: 41.6,
265
+ rowsToDisplay: 6,
266
+ };
267
+ }
268
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: GridConfigsProvider, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
269
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: GridConfigsProvider });
270
+ }
271
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: GridConfigsProvider, decorators: [{
272
+ type: Injectable
273
+ }], ctorParameters: () => [] });
274
+
275
+ const KEY_FIELD = '_key';
276
+ class GridDataset extends BaseDataset {
277
+ //#region ViewChilds, Inputs, Outputs
278
+ //#endregion
279
+ //#region Variables
280
+ configs;
281
+ filtersChanged = new EventEmitter();
282
+ isLoading = false;
283
+ loadedLastRow = false;
284
+ loadFinished = new EventEmitter();
285
+ loadStarted = new EventEmitter();
286
+ compareProperty = 'id';
287
+ configsProvider;
288
+ focusedRow = null;
289
+ recordBlock = 0;
290
+ _hasBeenLoaded = false;
291
+ _loadedKeys;
292
+ _loadedRows;
293
+ queryFilters;
294
+ //#endregion
295
+ //#region Properties
296
+ get filters() {
297
+ return this.queryFilters;
298
+ }
299
+ get hasBeenLoaded() {
300
+ return this._hasBeenLoaded;
301
+ }
302
+ get hasLoadedRows() {
303
+ return !!this._loadedRows && this._loadedRows.length > 0;
304
+ }
305
+ get keyProperty() {
306
+ return this.compareProperty;
307
+ }
308
+ get loadedKeys() {
309
+ return this._loadedKeys;
310
+ }
311
+ get loadedRows() {
312
+ return this._loadedRows;
313
+ }
314
+ get parentEntityId() {
315
+ return super.parentEntityId;
316
+ }
317
+ set parentEntityId(value) {
318
+ super.parentEntityId = value;
319
+ if (!!this.loadedRows) {
320
+ this.refresh();
321
+ }
322
+ }
323
+ //#endregion
324
+ //#region Constructor and Angular life cycle methods
325
+ constructor() {
326
+ super();
327
+ this.configsProvider = inject(GridConfigsProvider);
328
+ this.configs = JSON.parse(JSON.stringify(this.configsProvider.configs));
329
+ }
330
+ //#endregion
331
+ //#region Event handlers
332
+ //#endregion
333
+ //#region Public methods
334
+ addNewRow(row) {
335
+ this.updateLoadedRows([row]);
336
+ }
337
+ clearFocusedRow() {
338
+ this.focusedRow = null;
339
+ }
340
+ getRowData(key) {
341
+ if (this.hasLoadedRows) {
342
+ const index = this._loadedKeys.indexOf(key);
343
+ return this.loadedRows[index];
344
+ }
345
+ return null;
346
+ }
347
+ getRowID(key) {
348
+ const row = this.getRowData(key);
349
+ return this.compareProperty.split('.')
350
+ .reduce((row, property) => row[property], row);
351
+ }
352
+ getRowInternalKey(row) {
353
+ return row[KEY_FIELD];
354
+ }
355
+ hasRowWithID(id) {
356
+ if (!this.hasLoadedRows) {
357
+ return false;
358
+ }
359
+ const loadedIDs = this.compareProperty.split('.')
360
+ .reduce((loadedRows, property) => loadedRows.map((row) => row[property]), this._loadedRows ?? [])
361
+ .map((row) => typeof id === 'string' ? `${row}`
362
+ : typeof id === 'number' ? parseInt(`${row}`)
363
+ : row);
364
+ return loadedIDs.indexOf(id) >= 0;
365
+ }
366
+ isKeyExists(key) {
367
+ return this.hasLoadedRows && this._loadedKeys.some((x) => x === key);
368
+ }
369
+ loadRows() {
370
+ if (this.isLoading) {
371
+ return;
372
+ }
373
+ this.isLoading = true;
374
+ this.loadStarted.emit();
375
+ const parameters = {
376
+ endRow: this.configsProvider.configs.recordBlockSize,
377
+ filters: this.queryFilters,
378
+ startRow: this.recordBlock,
379
+ };
380
+ this.getData(parameters)
381
+ .pipe(take(1))
382
+ .subscribe({
383
+ next: (rows) => {
384
+ this.updateLoadedRows(rows ?? []);
385
+ this.isLoading = false;
386
+ this._hasBeenLoaded = true;
387
+ this.loadFinished.emit(true);
388
+ },
389
+ error: (_) => {
390
+ this.isLoading = false;
391
+ this.loadFinished.emit(false);
392
+ }
393
+ });
394
+ }
395
+ refresh() {
396
+ if (this.focusedRow) {
397
+ this.clearFocusedRow();
398
+ }
399
+ this.clearLoadedRows();
400
+ this.loadRows();
401
+ }
402
+ removeKeys(keys) {
403
+ if (!this.hasLoadedRows) {
404
+ return;
405
+ }
406
+ keys.forEach((key) => {
407
+ if (this.focusedRow === key) {
408
+ this.clearFocusedRow();
409
+ }
410
+ const indexToRemove = this._loadedKeys.indexOf(key);
411
+ if (indexToRemove >= 0) {
412
+ // Need to set again the array values to trigger the change detection.
413
+ this._loadedKeys = this._loadedKeys.filter((_, index) => index !== indexToRemove);
414
+ this._loadedRows = this._loadedRows.filter((_, index) => index !== indexToRemove);
415
+ }
416
+ });
417
+ }
418
+ setFilters(filters) {
419
+ this.queryFilters = filters;
420
+ this.filtersChanged.emit(filters);
421
+ this.refresh();
422
+ }
423
+ setFocusedRow(key) {
424
+ this.focusedRow = key;
425
+ }
426
+ updateRow(key, newRowData) {
427
+ if (!this.hasLoadedRows) {
428
+ return;
429
+ }
430
+ const index = this._loadedKeys.indexOf(key);
431
+ if (index >= 0) {
432
+ // Need to set the KEY_FIELD value into the new data, to make sure this information is not lost.
433
+ newRowData[KEY_FIELD] = key;
434
+ this._loadedRows[index] = newRowData;
435
+ // Need to update loaded rows to force displayed rows to refresh.
436
+ this.updateLoadedRows([]);
437
+ }
438
+ }
439
+ //#endregion
440
+ //#region Private methods
441
+ clearLoadedRows() {
442
+ this._hasBeenLoaded = false;
443
+ this._loadedRows = undefined;
444
+ this._loadedKeys = undefined;
445
+ this.recordBlock = 0;
446
+ this.loadedLastRow = false;
447
+ }
448
+ updateLoadedRows(rows) {
449
+ // Assign individual key to each row.
450
+ rows.forEach((row) => {
451
+ row[KEY_FIELD] = GuidHelper.generateGUID();
452
+ });
453
+ this._loadedRows = [...this._loadedRows ?? [], ...rows];
454
+ this._loadedKeys = [...this._loadedKeys ?? [], ...rows.map((row) => row[KEY_FIELD])];
455
+ this.recordBlock += rows.length;
456
+ if (rows.length < this.configsProvider.configs.recordBlockSize) {
457
+ this.loadedLastRow = true;
458
+ }
459
+ }
460
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: GridDataset, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
461
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: GridDataset });
462
+ }
463
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: GridDataset, decorators: [{
464
+ type: Injectable
465
+ }], ctorParameters: () => [] });
466
+
467
+ class DataGridDataset extends GridDataset {
468
+ //#region ViewChilds, Inputs, Outputs
469
+ selectedRowsChanged = new EventEmitter();
470
+ //#endregion
471
+ //#region Variables
472
+ columns;
473
+ configs;
474
+ selectedRowKeys = [];
475
+ //#endregion
476
+ //#region Properties
477
+ get columnsSize() {
478
+ const sizes = [];
479
+ if (this.configs.multiSelect) {
480
+ sizes.push(this.configs.multiSelectSize ?? 'minmax(1.5rem,min-content)');
481
+ }
482
+ sizes.push(...this.columns.map((col) => col.size ?? 'minmax(0,1fr)'));
483
+ return sizes.join(' ');
484
+ }
485
+ get hasSelectedRows() {
486
+ return this.selectedRowKeys.length > 0;
487
+ }
488
+ //#endregion
489
+ //#region Constructor and Angular life cycle methods
490
+ constructor() {
491
+ super();
492
+ this.configs = JSON.parse(JSON.stringify(this.configsProvider.configs));
493
+ }
494
+ //#endregion
495
+ //#region Event handlers
496
+ //#endregion
497
+ //#region Public methods
498
+ clearSelection() {
499
+ if (!this.hasLoadedRows || !this.hasSelectedRows) {
500
+ return;
501
+ }
502
+ const selectedKeys = this.selectedRowKeys;
503
+ this.selectedRowKeys = [];
504
+ this.clearFocusedRow();
505
+ this.selectedRowsChanged.emit(selectedKeys.reduce((keys, key) => {
506
+ keys[key] = {
507
+ rowData: this.getRowData(key),
508
+ selected: false
509
+ };
510
+ return keys;
511
+ }, {}));
512
+ }
513
+ deselectRow(key) {
514
+ if (!this.hasLoadedRows || !this.isKeySelected(key)) {
515
+ return;
516
+ }
517
+ this.removeSelectedRow(key);
518
+ this.selectedRowsChanged.emit({
519
+ [key]: {
520
+ rowData: this.getRowData(key),
521
+ selected: false,
522
+ }
523
+ });
524
+ }
525
+ deselectRowID(id) {
526
+ if (!this.hasLoadedRows) {
527
+ return;
528
+ }
529
+ const index = this.loadedKeys.map((key) => this.getRowID(key))
530
+ .indexOf(id);
531
+ const key = this.loadedKeys[index];
532
+ this.deselectRow(key);
533
+ }
534
+ getSelectedRowsData() {
535
+ if (!this.hasLoadedRows || !this.hasSelectedRows) {
536
+ return [];
537
+ }
538
+ return this.selectedRowKeys.map((key) => this.loadedKeys.indexOf(key))
539
+ .filter((index) => index >= 0)
540
+ .map((index) => this.loadedRows[index]);
541
+ }
542
+ isKeySelected(key) {
543
+ return this.selectedRowKeys.indexOf(key) >= 0;
544
+ }
545
+ refresh() {
546
+ if (this.hasSelectedRows) {
547
+ this.clearSelection();
548
+ }
549
+ super.refresh();
550
+ }
551
+ removeKeys(keys) {
552
+ if (!this.hasLoadedRows) {
553
+ return;
554
+ }
555
+ if (this.hasSelectedRows) {
556
+ keys.filter((key) => this.isKeySelected(key))
557
+ .forEach((key) => {
558
+ this.removeSelectedRow(key);
559
+ });
560
+ }
561
+ super.removeKeys(keys);
562
+ }
563
+ selectRow(key) {
564
+ if (!this.hasLoadedRows) {
565
+ return;
566
+ }
567
+ if (!this.configs.multiSelect) {
568
+ if (this.isKeySelected(key)) {
569
+ return;
570
+ }
571
+ // Clear current selected row.
572
+ this.removeSelectedRow(this.selectedRowKeys[0]);
573
+ }
574
+ if (!this.isKeySelected(key)) {
575
+ this.selectedRowKeys.push(key);
576
+ this.selectedRowsChanged.emit({
577
+ [key]: {
578
+ rowData: this.getRowData(key),
579
+ selected: true,
580
+ }
581
+ });
582
+ }
583
+ }
584
+ selectRowID(id) {
585
+ if (!this.hasLoadedRows) {
586
+ return;
587
+ }
588
+ if (this.hasRowWithID(id)) {
589
+ const rowIndex = this.loadedRows.findIndex((row) => row[this.compareProperty] === id);
590
+ if (rowIndex < 0) {
591
+ return;
592
+ }
593
+ const key = this.loadedKeys[rowIndex];
594
+ if (!!key) {
595
+ this.selectRow(key);
596
+ }
597
+ }
598
+ }
599
+ selectRows(keys) {
600
+ if (!this.hasLoadedRows || !this.configs.multiSelect) {
601
+ return;
602
+ }
603
+ // Filter only the keys that are not selected yet.
604
+ const keysNotSelected = keys.filter((key) => !this.isKeySelected(key));
605
+ if (keysNotSelected.length > 0) {
606
+ this.selectedRowKeys.push(...keysNotSelected);
607
+ this.selectedRowsChanged.emit(keys.reduce((keys, key) => {
608
+ keys[key] = {
609
+ rowData: this.getRowData(key),
610
+ selected: true,
611
+ };
612
+ return keys;
613
+ }, {}));
614
+ }
615
+ }
616
+ setFocusedRow(key) {
617
+ super.setFocusedRow(key);
618
+ if (!this.configs.multiSelect) {
619
+ this.selectRow(key);
620
+ }
621
+ else if (this.configs.selectOnClick ?? true) {
622
+ if (this.isKeySelected(key)) {
623
+ this.deselectRow(key);
624
+ }
625
+ else {
626
+ this.selectRow(key);
627
+ }
628
+ }
629
+ }
630
+ //#endregion
631
+ //#region Private methods
632
+ removeSelectedRow(key) {
633
+ const index = this.selectedRowKeys.indexOf(key);
634
+ if (index >= 0) {
635
+ this.selectedRowKeys.splice(index, 1);
636
+ }
637
+ }
638
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: DataGridDataset, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
639
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: DataGridDataset });
640
+ }
641
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: DataGridDataset, decorators: [{
642
+ type: Injectable
643
+ }], ctorParameters: () => [] });
644
+
645
+ class MultiEditorDataset extends BaseDataset {
646
+ //#region ViewChilds, Inputs, Outputs
647
+ savedChanges = new EventEmitter();
648
+ //#endregion
649
+ //#region Variables
650
+ dataGridDataset;
651
+ fakeIDsGenerated = [];
652
+ modifiedValues = {
653
+ changed: {},
654
+ removed: [],
655
+ };
656
+ //#endregion
657
+ //#region Properties
658
+ //#endregion
659
+ //#region Constructor and Angular life cycle methods
660
+ constructor() {
661
+ super();
662
+ this.dataGridDataset = inject(DataGridDataset);
663
+ }
664
+ //#endregion
665
+ //#region Event handlers
666
+ //#endregion
667
+ //#region Public methods
668
+ clearChangedValues() {
669
+ this.fakeIDsGenerated = [];
670
+ this.modifiedValues = {
671
+ changed: {},
672
+ removed: [],
673
+ };
674
+ }
675
+ removeValue(key) {
676
+ if (this.modifiedValues.changed[key]) {
677
+ delete this.modifiedValues.changed[key];
678
+ }
679
+ const id = this.dataGridDataset.getRowID(key);
680
+ this.dataGridDataset.removeKeys([key]);
681
+ if (!id) {
682
+ return;
683
+ }
684
+ if (this.fakeIDsGenerated.indexOf(id) < 0) {
685
+ this.modifiedValues.removed.push(id);
686
+ }
687
+ else {
688
+ this.fakeIDsGenerated.splice(this.fakeIDsGenerated.indexOf(id), 1);
689
+ }
690
+ }
691
+ saveChanges() {
692
+ const batchUpdate = {
693
+ entitiesToInsertOrUpdate: Object.entries(this.modifiedValues.changed).map(([_, v]) => v),
694
+ entitiesToDelete: this.modifiedValues.removed,
695
+ };
696
+ return this.saveData(batchUpdate)
697
+ .pipe(tap(() => this.savedChanges.emit()));
698
+ }
699
+ storeFakeIDGenerated(id) {
700
+ if (this.fakeIDsGenerated.indexOf(id) >= 0) {
701
+ return;
702
+ }
703
+ this.fakeIDsGenerated.push(id);
704
+ }
705
+ updateValues(key, newRowData) {
706
+ this.modifiedValues.changed[key] = newRowData;
707
+ this.dataGridDataset.updateRow(key, newRowData);
708
+ }
709
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: MultiEditorDataset, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
710
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: MultiEditorDataset });
711
+ }
712
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: MultiEditorDataset, decorators: [{
713
+ type: Injectable
714
+ }], ctorParameters: () => [] });
715
+
716
+ class MultiSelectResultDataset extends GridDataset {
717
+ //#region ViewChilds, Inputs, Outputs
718
+ savedChanges = new EventEmitter();
719
+ //#endregion
720
+ //#region Variables
721
+ displayedIDs = [];
722
+ displayedRows = [];
723
+ _idsToAdd = [];
724
+ _idsToRemove = [];
725
+ addedRowData = [];
726
+ //#endregion
727
+ //#region Properties
728
+ get idsToAdd() {
729
+ return this._idsToAdd;
730
+ }
731
+ get idsToRemove() {
732
+ return this._idsToRemove;
733
+ }
734
+ //#endregion
735
+ //#region Constructor and Angular life cycle methods
736
+ //#endregion
737
+ //#region Event handlers
738
+ //#endregion
739
+ //#region Public methods
740
+ refresh() {
741
+ this.clearSelection();
742
+ super.refresh();
743
+ }
744
+ saveChanges() {
745
+ const changes = {
746
+ idsToAdd: this.idsToAdd,
747
+ idsToRemove: this.idsToRemove,
748
+ };
749
+ return this.saveData(changes)
750
+ .pipe(tap(() => {
751
+ this.savedChanges.emit();
752
+ this.clearSelection();
753
+ this.clearLoadedRows();
754
+ }));
755
+ }
756
+ setIDToAdd(idToAdd, rowData) {
757
+ const index = this._idsToRemove.indexOf(idToAdd);
758
+ if (index >= 0) {
759
+ this._idsToRemove.splice(index, 1);
760
+ }
761
+ else if (!this.isExistingID(idToAdd)) {
762
+ this._idsToAdd.push(idToAdd);
763
+ this.addedRowData.push(rowData);
764
+ }
765
+ this.updateDisplayedRows();
766
+ }
767
+ setIDToRemove(idToRemove) {
768
+ const index = this._idsToAdd.indexOf(idToRemove);
769
+ if (index >= 0) {
770
+ this._idsToAdd.splice(index, 1);
771
+ this.addedRowData.splice(index, 1);
772
+ }
773
+ else if (this.isExistingID(idToRemove)) {
774
+ this._idsToRemove.push(idToRemove);
775
+ }
776
+ this.updateDisplayedRows();
777
+ }
778
+ updateLoadedRows(rows) {
779
+ super.updateLoadedRows(rows);
780
+ this.updateDisplayedRows();
781
+ }
782
+ //#endregion
783
+ //#region Private methods
784
+ clearSelection() {
785
+ this._idsToAdd = [];
786
+ this._idsToRemove = [];
787
+ this.addedRowData = [];
788
+ this.updateDisplayedRows();
789
+ }
790
+ isExistingID(id) {
791
+ return (this.loadedKeys ?? [])
792
+ .map((key) => this.getRowID(key))
793
+ .indexOf(id) >= 0;
794
+ }
795
+ updateDisplayedRows() {
796
+ const loadedKeys = (this.loadedKeys ?? [])
797
+ .filter((key) => this.idsToRemove.indexOf(this.getRowID(key)) < 0);
798
+ this.displayedIDs = loadedKeys.map((key) => this.getRowID(key));
799
+ this.displayedIDs.push(...this._idsToAdd);
800
+ this.displayedRows = loadedKeys.map((key) => this.getRowData(key));
801
+ this.displayedRows.push(...this.addedRowData);
802
+ }
803
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: MultiSelectResultDataset, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
804
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: MultiSelectResultDataset });
805
+ }
806
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: MultiSelectResultDataset, decorators: [{
807
+ type: Injectable
808
+ }] });
809
+
810
+ class FormGroupService {
811
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: FormGroupService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
812
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: FormGroupService });
813
+ }
814
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: FormGroupService, decorators: [{
815
+ type: Injectable
816
+ }] });
817
+
818
+ class FormService {
819
+ //#region ViewChilds, Inputs, Outputs
820
+ fieldRefreshed = new EventEmitter();
821
+ modelRefreshed = new EventEmitter;
822
+ //#endregion
823
+ //#region Variables
824
+ _loading = false;
825
+ _model = null;
826
+ currentMode = 'view';
827
+ editCanceled$ = new Subject();
828
+ form;
829
+ initialValue;
830
+ //#endregion
831
+ //#region Properties
832
+ get editCanceled() {
833
+ return this.editCanceled$.asObservable();
834
+ }
835
+ get isEditMode() {
836
+ return this.currentMode === 'edit';
837
+ }
838
+ get isValid() {
839
+ return this.form?.valid ?? false;
840
+ }
841
+ get loading() {
842
+ return this._loading;
843
+ }
844
+ set loading(value) {
845
+ if (this._loading !== value) {
846
+ this._loading = value;
847
+ if (value) {
848
+ this.form?.disable();
849
+ }
850
+ else if (this.isEditMode) {
851
+ this.form?.enable();
852
+ }
853
+ }
854
+ }
855
+ get model() {
856
+ return this._model;
857
+ }
858
+ set model(value) {
859
+ if (this._model !== value) {
860
+ this._model = value;
861
+ this.modelRefreshed.emit(value);
862
+ }
863
+ }
864
+ //#endregion
865
+ //#region Constructor and Angular life cycle methods
866
+ //#endregion
867
+ //#region Event handlers
868
+ //#endregion
869
+ //#region Public methods
870
+ beginEdit() {
871
+ this.currentMode = 'edit';
872
+ this.form?.enable();
873
+ }
874
+ beginFieldLoading(fieldName) {
875
+ this.fieldRefreshed.emit({
876
+ fieldName,
877
+ loading: true
878
+ });
879
+ }
880
+ cancelEdit() {
881
+ this.currentMode = 'view';
882
+ this.form?.disable();
883
+ this.resetForm();
884
+ this.editCanceled$.next();
885
+ }
886
+ disableForm() {
887
+ this.form?.disable();
888
+ }
889
+ enableForm() {
890
+ if (this.isEditMode) {
891
+ this.form?.enable();
892
+ }
893
+ }
894
+ getModelFromForm() {
895
+ return this.form?.getRawValue();
896
+ }
897
+ initializeForm(form) {
898
+ this.form = form;
899
+ this.initialValue = form.getRawValue();
900
+ if (this.currentMode === 'view') {
901
+ this.form.disable();
902
+ }
903
+ }
904
+ markAllAsTouched() {
905
+ if (!this.form) {
906
+ return;
907
+ }
908
+ this.form.markAllAsTouched();
909
+ }
910
+ resetForm() {
911
+ if (!this.form) {
912
+ return;
913
+ }
914
+ this.form.reset();
915
+ if (!!this.model) {
916
+ this.form.patchValue(this.model);
917
+ }
918
+ else if (!!this.initialValue) {
919
+ this.form.patchValue(this.initialValue);
920
+ }
921
+ this.form.updateValueAndValidity();
922
+ }
923
+ setFieldValue(fieldName, value) {
924
+ this.fieldRefreshed.emit({
925
+ fieldName,
926
+ loading: false,
927
+ value,
928
+ });
929
+ }
930
+ setValidationErrorsFromHttpResponse(errorResponse) {
931
+ if (!errorResponse || !this.form) {
932
+ return;
933
+ }
934
+ // Error 400 = Validation issues
935
+ if (errorResponse.status === 400 && errorResponse.error && errorResponse.error.errors) {
936
+ const errors = errorResponse.error.errors;
937
+ Object.keys(errors)
938
+ .map((x) => { return { key: x, field: x.substring(0, 1).toLocaleLowerCase() + x.substring(1, x.length) }; })
939
+ .forEach((err) => {
940
+ setTimeout(() => {
941
+ const field = this.form.get(err.field);
942
+ field?.setErrors({ [errors[err.key][0]]: true });
943
+ field?.markAsTouched();
944
+ });
945
+ });
946
+ }
947
+ }
948
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: FormService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
949
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: FormService });
950
+ }
951
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: FormService, decorators: [{
952
+ type: Injectable
953
+ }] });
954
+
955
+ class RibbonGroupChild {
956
+ }
957
+
958
+ const SIDEBAR_CONFIGS = new InjectionToken('', {
959
+ providedIn: 'root',
960
+ factory: () => new SidebarConfigs(),
961
+ });
962
+ class SidebarConfigs {
963
+ errorText = 'Error';
964
+ loadingText = 'Loading';
965
+ logoCollapsedPath;
966
+ logoExpandedPath;
967
+ constructor(options = {}) {
968
+ Object.assign(this, options);
969
+ }
970
+ }
971
+
972
+ class SidebarMenu {
973
+ childCount = 0;
974
+ children = [];
975
+ height;
976
+ icon;
977
+ id = 0;
978
+ label = '';
979
+ isSelected = false;
980
+ parent = null;
981
+ url;
982
+ constructor(options = {}) {
983
+ Object.assign(this, options);
984
+ }
985
+ }
986
+
987
+ class SidebarService {
988
+ //#region ViewChilds, Inputs, Outputs
989
+ childrenFailed = new EventEmitter();
990
+ childrenLoading = new EventEmitter();
991
+ childrenInitialized = new EventEmitter();
992
+ menuUrlSelected = new EventEmitter();
993
+ selectionChanged = new EventEmitter();
994
+ //#endregion
995
+ //#region Variables
996
+ isActive = false;
997
+ isCollapsed = false;
998
+ shouldActivate = false;
999
+ menus = [];
1000
+ selectedMenu = null;
1001
+ //#endregion
1002
+ //#region Properties
1003
+ //#endregion
1004
+ //#region Constructor and Angular life cycle methods
1005
+ //#endregion
1006
+ //#region Public methods
1007
+ deselectAll() {
1008
+ this.menus
1009
+ .filter((menu) => menu.isSelected)
1010
+ .forEach((menu) => {
1011
+ this.deselectMenu(menu);
1012
+ });
1013
+ }
1014
+ loadChildren(parentMenu) {
1015
+ this.childrenLoading.emit(parentMenu);
1016
+ this.loadMenus(parentMenu)
1017
+ .pipe(take(1), map((menus) => menus.map((menu) => new SidebarMenu(menu))))
1018
+ .subscribe({
1019
+ next: (childrenMenus) => {
1020
+ parentMenu.children = childrenMenus;
1021
+ childrenMenus.forEach((childMenu) => childMenu.parent = parentMenu);
1022
+ },
1023
+ error: (exception) => {
1024
+ this.childrenFailed.emit(parentMenu);
1025
+ throw exception;
1026
+ }
1027
+ });
1028
+ }
1029
+ loadRoot() {
1030
+ return this.loadMenus(null)
1031
+ .pipe(take(1), map((menus) => menus.map((menu) => new SidebarMenu(menu))), tap((menus) => this.menus = menus));
1032
+ }
1033
+ select(menu) {
1034
+ if (menu.childCount > 0 && (menu.children?.length ?? 0) === 0) {
1035
+ this.loadChildren(menu);
1036
+ }
1037
+ const hasUrl = (menu.url?.length ?? 0) > 0;
1038
+ // In case we're deselecting the menu because a child was selected, we need to keep track of the original selection state.
1039
+ const isSelected = menu.isSelected;
1040
+ if (!!this.selectedMenu && this.selectedMenu !== menu) {
1041
+ this.deselectAll();
1042
+ }
1043
+ if (!isSelected) {
1044
+ this.selectedMenu = menu;
1045
+ this.selectMenu(menu);
1046
+ if (hasUrl) {
1047
+ this.menuUrlSelected.emit(menu);
1048
+ }
1049
+ }
1050
+ else if (hasUrl) {
1051
+ // Menu with a URL is already selected — navigate again instead of toggling off.
1052
+ this.menuUrlSelected.emit(menu);
1053
+ }
1054
+ else {
1055
+ this.selectedMenu = null;
1056
+ this.deselectMenu(menu);
1057
+ }
1058
+ }
1059
+ //#endregion
1060
+ //#region Private methods
1061
+ deselectMenu(menu, shouldUpdateParents = true) {
1062
+ if (!menu.isSelected) {
1063
+ return;
1064
+ }
1065
+ menu.isSelected = false;
1066
+ this.selectionChanged.emit(menu);
1067
+ if ((menu.children?.length ?? 0) > 0) {
1068
+ menu.children
1069
+ .filter((child) => child.isSelected)
1070
+ .forEach((child) => {
1071
+ const shouldUpdateParents = false;
1072
+ this.deselectMenu(child, shouldUpdateParents);
1073
+ });
1074
+ }
1075
+ if (!!menu.parent && shouldUpdateParents) {
1076
+ while (!!menu.parent) {
1077
+ this.selectionChanged.emit(menu.parent);
1078
+ menu = menu.parent;
1079
+ }
1080
+ }
1081
+ }
1082
+ selectMenu(menu) {
1083
+ menu.isSelected = true;
1084
+ this.selectionChanged.emit(menu);
1085
+ if (!!menu.parent) {
1086
+ this.selectMenu(menu.parent);
1087
+ }
1088
+ }
1089
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: SidebarService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1090
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: SidebarService, providedIn: 'root' });
1091
+ }
1092
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: SidebarService, decorators: [{
1093
+ type: Injectable,
1094
+ args: [{
1095
+ providedIn: 'root'
1096
+ }]
1097
+ }] });
1098
+
1099
+ class FormInputComponent extends BaseComponent {
1100
+ //#region ViewChilds, Inputs, Outputs
1101
+ autofocus = false;
1102
+ controlName = '';
1103
+ disabledControlName;
1104
+ displayReadOnlyAsDisabled = true;
1105
+ fixedValue;
1106
+ format;
1107
+ invalid = false;
1108
+ isDisabled = true;
1109
+ isFullHeight = false;
1110
+ maxLength;
1111
+ min;
1112
+ readOnly = false;
1113
+ rows;
1114
+ step = 1;
1115
+ type = 'text';
1116
+ blur = new EventEmitter();
1117
+ change = new EventEmitter();
1118
+ fixedValueChanged = new EventEmitter();
1119
+ input = new EventEmitter();
1120
+ focus = new EventEmitter();
1121
+ //#endregion
1122
+ //#region Host listeners
1123
+ //#endregion
1124
+ //#region Variables
1125
+ formGroupDirective = inject(FormGroupDirective);
1126
+ formGroupName = inject(FormGroupName, { optional: true });
1127
+ formService = inject(FormService);
1128
+ //#endregion
1129
+ //#region Properties
1130
+ get formControl() {
1131
+ return this.controlName
1132
+ ? this.formGroup.get(this.controlName)
1133
+ : null;
1134
+ }
1135
+ get formControlName() {
1136
+ return [
1137
+ ...this.parentGroups,
1138
+ this.controlName,
1139
+ ].join('.');
1140
+ }
1141
+ get formGroup() {
1142
+ let formGroup = this.formGroupDirective.form;
1143
+ for (let group of this.parentGroups) {
1144
+ formGroup = formGroup.get(group);
1145
+ }
1146
+ return formGroup;
1147
+ }
1148
+ get isFormControl() {
1149
+ return !!this.controlName && this.controlName.length > 0;
1150
+ }
1151
+ get isFormEditMode() {
1152
+ return this.formService.isEditMode;
1153
+ }
1154
+ get parentGroups() {
1155
+ return this.formGroupName?.path ?? [];
1156
+ }
1157
+ get stepAttributeValue() {
1158
+ if (this.type === 'number') {
1159
+ return this.step;
1160
+ }
1161
+ return null;
1162
+ }
1163
+ get minAttributeValue() {
1164
+ if (this.type === 'number') {
1165
+ return this.min ?? null;
1166
+ }
1167
+ return null;
1168
+ }
1169
+ //#endregion
1170
+ //#region Constructor and Angular life cycle methods
1171
+ constructor() {
1172
+ super();
1173
+ }
1174
+ ngOnInit() {
1175
+ switch (this.type) {
1176
+ case 'number':
1177
+ if (!!this.formControl) {
1178
+ this.formControl.valueChanges
1179
+ .pipe(takeUntil(this.destroy$))
1180
+ .subscribe((value) => {
1181
+ if (value !== null && value === '') {
1182
+ this.formControl?.setValue(null);
1183
+ }
1184
+ });
1185
+ }
1186
+ break;
1187
+ case 'date':
1188
+ if (this.format === undefined) {
1189
+ this.format = 'yyyy-MM-dd';
1190
+ }
1191
+ if (!!this.formControl) {
1192
+ this.formControl.valueChanges
1193
+ .pipe(takeUntil(this.destroy$))
1194
+ .subscribe((value) => {
1195
+ if (value !== null) {
1196
+ try {
1197
+ if (value !== formatDate(value, 'yyyy-MM-dd', 'en')) {
1198
+ this.formControl?.setValue(formatDate(value, 'yyyy-MM-dd', 'en'), { emitEvent: false });
1199
+ }
1200
+ }
1201
+ catch {
1202
+ this.formControl?.setValue(null, { emitEvent: false });
1203
+ }
1204
+ }
1205
+ });
1206
+ }
1207
+ break;
1208
+ }
1209
+ }
1210
+ //#endregion
1211
+ //#region Event handlers
1212
+ onBlur() {
1213
+ this.blur.emit();
1214
+ }
1215
+ onChange(value) {
1216
+ this.change.emit(value);
1217
+ }
1218
+ onFocus() {
1219
+ this.focus.emit();
1220
+ }
1221
+ onInput() {
1222
+ this.input.emit();
1223
+ }
1224
+ onValueChanged(newText) {
1225
+ this.fixedValue = newText;
1226
+ this.fixedValueChanged.emit(this.fixedValue);
1227
+ }
1228
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: FormInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1229
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.6", type: FormInputComponent, isStandalone: true, selector: "lib-form-input", inputs: { autofocus: "autofocus", controlName: "controlName", disabledControlName: "disabledControlName", displayReadOnlyAsDisabled: "displayReadOnlyAsDisabled", fixedValue: "fixedValue", format: "format", invalid: "invalid", isDisabled: "isDisabled", isFullHeight: "isFullHeight", maxLength: "maxLength", min: "min", readOnly: "readOnly", rows: "rows", step: "step", type: "type" }, outputs: { blur: "blur", change: "change", fixedValueChanged: "fixedValueChanged", input: "input", focus: "focus" }, host: { properties: { "class.full-height": "isFullHeight" } }, usesInheritance: true, ngImport: i0, template: "<ng-container *ngIf=\"formGroupDirective.form?.enabled || isFormEditMode;else disabledControl\">\n <ng-container [ngSwitch]=\"type\">\n \n <textarea *ngSwitchCase=\"'textarea'\"\n [attr.autofocus]=\"autofocus ? true : null\"\n [attr.maxLength]=\"maxLength ?? null\"\n [attr.rows]=\"rows\"\n [class.invalid]=\"invalid\"\n [formControlName]=\"controlName\"\n (blur)=\"onBlur()\"\n (focus)=\"onFocus()\"\n (input)=\"onInput()\">\n </textarea>\n\n <ng-container *ngSwitchCase=\"'checkbox'\">\n <input *ngIf=\"isFormControl; else fixedCheckbox\"\n class=\"switch\"\n type=\"checkbox\"\n [attr.autofocus]=\"autofocus ? true : null\"\n [class.invalid]=\"invalid\"\n [formControlName]=\"controlName\"\n (blur)=\"onBlur()\"\n (focus)=\"onFocus()\"\n (input)=\"onInput()\" />\n\n <ng-template #fixedCheckbox>\n <input\n class=\"switch\"\n type=\"checkbox\"\n [(ngModel)]=\"fixedValue\"\n [attr.readonly]=\"true\"\n [class.invalid]=\"invalid\"\n [class.readonly-as-disabled]=\"displayReadOnlyAsDisabled\"\n [disabled]=\"isDisabled\"\n [ngModelOptions]=\"{ standalone: true }\"\n (blur)=\"onBlur()\"\n (change)=\"onChange($event)\"\n (focus)=\"onFocus()\"\n (input)=\"onInput()\"\n (ngModelChange)=\"onValueChanged($event)\" />\n </ng-template>\n </ng-container>\n\n\n <ng-container *ngSwitchDefault>\n <input *ngIf=\"isFormControl; else fixedTextBox\"\n [attr.autofocus]=\"autofocus ? true : null\"\n [attr.maxLength]=\"maxLength ?? null\"\n [attr.min]=\"minAttributeValue\"\n [attr.readonly]=\"readOnly ? true : null\"\n [attr.step]=\"stepAttributeValue\"\n [class.invalid]=\"invalid\"\n [class.readonly-as-disabled]=\"displayReadOnlyAsDisabled\"\n [formControlName]=\"controlName\"\n [type]=\"type\"\n (blur)=\"onBlur()\"\n (focus)=\"onFocus()\"\n (input)=\"onInput()\" />\n\n <ng-template #fixedTextBox>\n <input\n type=\"text\"\n [attr.maxLength]=\"maxLength ?? null\"\n [attr.min]=\"minAttributeValue\"\n [attr.readonly]=\"true\"\n [attr.step]=\"stepAttributeValue\"\n [class.invalid]=\"invalid\"\n [class.readonly-as-disabled]=\"displayReadOnlyAsDisabled\"\n [disabled]=\"isDisabled\"\n [ngModel]=\"type === 'date' || type === 'datetime-local' ? (fixedValue | date: format) : ( type === 'number' ? (fixedValue | number) : fixedValue )\" \n [ngModelOptions]=\"{ standalone: true }\"\n (blur)=\"onBlur()\"\n (focus)=\"onFocus()\"\n (input)=\"onInput()\"\n (ngModelChange)=\"onValueChanged($event)\" />\n </ng-template>\n </ng-container>\n\n </ng-container>\n</ng-container>\n\n<ng-template #disabledControl>\n <ng-container [ngSwitch]=\"type\">\n <label *ngSwitchCase=\"'checkbox'\">\n <i [ngClass]=\"fixedValue ?? formGroupDirective.form.get(disabledControlName ?? controlName)?.value ? 'fa-solid fa-square-check' : 'fa-regular fa-square'\"></i>\n </label>\n\n <label *ngSwitchCase=\"'date'\">\n {{ fixedValue ?? formGroupDirective.form.get(disabledControlName ?? controlName)?.value | date: format }}\n </label>\n\n <label *ngSwitchCase=\"'datetime-local'\">\n {{ fixedValue ?? formGroupDirective.form.get(disabledControlName ?? controlName)?.value | date: format }}\n </label>\n\n <label *ngSwitchCase=\"'number'\">\n {{ fixedValue ?? formGroupDirective.form.get(disabledControlName ?? controlName)?.value | number: format }}\n </label>\n\n <label class=\"text\" *ngSwitchDefault>{{ fixedValue ?? formGroupDirective.form.get(disabledControlName ?? controlName)?.value }}</label>\n </ng-container>\n</ng-template>\n\n<ng-template #fixedTextbox let-fixedValue=\"fixedValue\">\n \n</ng-template>\n", styles: ["@charset \"UTF-8\";:host{display:block}:host.full-height{height:100%}:host.full-height textarea{height:100%}input,textarea{--tw-border-opacity: 1;border-color:rgb(156 163 175 / var(--tw-border-opacity, 1))}input:focus,textarea:focus{--tw-border-opacity: 1;border-color:rgb(156 163 175 / var(--tw-border-opacity, 1));--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000);--tw-ring-color: rgb(0 107 182 / .3) }input,textarea{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}input:invalid,textarea:invalid{--tw-border-opacity: 1;border-color:rgb(248 113 113 / var(--tw-border-opacity, 1))}input:disabled,input.readonly-as-disabled[readonly],textarea:disabled,textarea.readonly-as-disabled[readonly]{--tw-bg-opacity: 1;background-color:rgb(241 245 249 / var(--tw-bg-opacity, 1))}input.ng-invalid.ng-touched,input.invalid,textarea.ng-invalid.ng-touched,textarea.invalid{--tw-border-opacity: 1;border-color:rgb(220 38 38 / var(--tw-border-opacity, 1))}input[type=text],input[type=number],input[type=password],input[type=date],input[type=datetime-local],input[type=email],input[type=file],input[type=month],input[type=tel],input[type=time],input[type=url],input[type=week],textarea{display:block;width:100%;border-radius:.375rem;padding:.25rem .5rem;--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .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)}input[type=text].ng-invalid.ng-touched,input[type=text].invalid,input[type=number].ng-invalid.ng-touched,input[type=number].invalid,input[type=password].ng-invalid.ng-touched,input[type=password].invalid,input[type=date].ng-invalid.ng-touched,input[type=date].invalid,input[type=datetime-local].ng-invalid.ng-touched,input[type=datetime-local].invalid,input[type=email].ng-invalid.ng-touched,input[type=email].invalid,input[type=file].ng-invalid.ng-touched,input[type=file].invalid,input[type=month].ng-invalid.ng-touched,input[type=month].invalid,input[type=tel].ng-invalid.ng-touched,input[type=tel].invalid,input[type=time].ng-invalid.ng-touched,input[type=time].invalid,input[type=url].ng-invalid.ng-touched,input[type=url].invalid,input[type=week].ng-invalid.ng-touched,input[type=week].invalid,textarea.ng-invalid.ng-touched,textarea.invalid{margin-right:-2.25rem;padding-right:2.25rem;transition-property:none}input[type=text].ng-invalid.ng-touched:focus,input[type=text].invalid:focus,input[type=number].ng-invalid.ng-touched:focus,input[type=number].invalid:focus,input[type=password].ng-invalid.ng-touched:focus,input[type=password].invalid:focus,input[type=date].ng-invalid.ng-touched:focus,input[type=date].invalid:focus,input[type=datetime-local].ng-invalid.ng-touched:focus,input[type=datetime-local].invalid:focus,input[type=email].ng-invalid.ng-touched:focus,input[type=email].invalid:focus,input[type=file].ng-invalid.ng-touched:focus,input[type=file].invalid:focus,input[type=month].ng-invalid.ng-touched:focus,input[type=month].invalid:focus,input[type=tel].ng-invalid.ng-touched:focus,input[type=tel].invalid:focus,input[type=time].ng-invalid.ng-touched:focus,input[type=time].invalid:focus,input[type=url].ng-invalid.ng-touched:focus,input[type=url].invalid:focus,input[type=week].ng-invalid.ng-touched:focus,input[type=week].invalid:focus,textarea.ng-invalid.ng-touched:focus,textarea.invalid:focus{--tw-ring-color: rgb(248 113 113 / .3) }input[type=text].ng-invalid.ng-touched,input[type=text].invalid,input[type=number].ng-invalid.ng-touched,input[type=number].invalid,input[type=password].ng-invalid.ng-touched,input[type=password].invalid,input[type=date].ng-invalid.ng-touched,input[type=date].invalid,input[type=datetime-local].ng-invalid.ng-touched,input[type=datetime-local].invalid,input[type=email].ng-invalid.ng-touched,input[type=email].invalid,input[type=file].ng-invalid.ng-touched,input[type=file].invalid,input[type=month].ng-invalid.ng-touched,input[type=month].invalid,input[type=tel].ng-invalid.ng-touched,input[type=tel].invalid,input[type=time].ng-invalid.ng-touched,input[type=time].invalid,input[type=url].ng-invalid.ng-touched,input[type=url].invalid,input[type=week].ng-invalid.ng-touched,input[type=week].invalid,textarea.ng-invalid.ng-touched,textarea.invalid{background-size:1.125rem 1.125rem;background-position:right .5625rem center;background-repeat:no-repeat;background-image:url(\"data:image/svg+xml,%3csvg class='text-white' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e\")}input[type=checkbox]{position:relative;margin-top:.25rem;margin-bottom:.25rem;height:1.25rem;width:1.25rem;border-radius:.25rem;padding:.125rem;display:inline-flex;flex-direction:row;justify-content:flex-end;appearance:none;--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}input[type=checkbox]:disabled{pointer-events:none;opacity:.5}input[type=checkbox]:not(.switch){border-width:2px;--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .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)}input[type=checkbox]:not(.switch):focus{--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow);--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}input[type=checkbox]:not(.switch):before{border-radius:.25rem;--tw-content: \"\";content:var(--tw-content);position:absolute;inset:0;display:flex;align-items:center;justify-content:center;padding:.25rem;font-family:\"Font Awesome 6 Free\";font-weight:900}input[type=checkbox]:checked{border-color:#006bb6cc;--tw-bg-opacity: 1;background-color:rgb(0 107 182 / var(--tw-bg-opacity, 1));background-image:none}input[type=checkbox]:checked:before{--tw-content: \"\\f00c\";content:var(--tw-content);--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}input[type=checkbox]:disabled~label{opacity:.5}input[type=checkbox]~label{margin-left:.5rem;-webkit-user-select:none;user-select:none}input[type=checkbox].switch{height:1.5rem;width:3rem;border-radius:2rem;appearance:none;--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}input[type=checkbox].switch:before{aspect-ratio:1 / 1;height:100%;border-radius:9999px;--tw-content: \"\";content:var(--tw-content);background-color:#6b728099}input[type=checkbox].switch:after{flex-grow:1;--tw-content: \"\";content:var(--tw-content);transition:flex-grow .15s ease-in-out}input[type=checkbox].switch:focus:before{background-color:#3b82f699}input[type=checkbox].switch:checked{border-color:#006bb6cc;--tw-bg-opacity: 1;background-color:rgb(0 107 182 / var(--tw-bg-opacity, 1));background-image:none}input[type=checkbox].switch:checked:before{--tw-content: \"\";content:var(--tw-content);--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}input[type=checkbox].switch:checked:after{flex-grow:0}label{display:block;overflow-wrap:break-word;padding-top:.25rem;padding-bottom:.25rem}label.text{white-space:pre-wrap}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i1.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: i1.NgSwitchDefault, selector: "[ngSwitchDefault]" }, { kind: "pipe", type: i1.DecimalPipe, name: "number" }, { kind: "pipe", type: i1.DatePipe, name: "date" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }], viewProviders: [{ provide: ControlContainer, useExisting: FormGroupDirective }] });
1230
+ }
1231
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: FormInputComponent, decorators: [{
1232
+ type: Component,
1233
+ args: [{ selector: 'lib-form-input', imports: [
1234
+ CommonModule,
1235
+ FormsModule,
1236
+ NgIf,
1237
+ NgSwitch,
1238
+ NgSwitchCase,
1239
+ ReactiveFormsModule,
1240
+ ], viewProviders: [{ provide: ControlContainer, useExisting: FormGroupDirective }], host: {
1241
+ '[class.full-height]': 'isFullHeight'
1242
+ }, template: "<ng-container *ngIf=\"formGroupDirective.form?.enabled || isFormEditMode;else disabledControl\">\n <ng-container [ngSwitch]=\"type\">\n \n <textarea *ngSwitchCase=\"'textarea'\"\n [attr.autofocus]=\"autofocus ? true : null\"\n [attr.maxLength]=\"maxLength ?? null\"\n [attr.rows]=\"rows\"\n [class.invalid]=\"invalid\"\n [formControlName]=\"controlName\"\n (blur)=\"onBlur()\"\n (focus)=\"onFocus()\"\n (input)=\"onInput()\">\n </textarea>\n\n <ng-container *ngSwitchCase=\"'checkbox'\">\n <input *ngIf=\"isFormControl; else fixedCheckbox\"\n class=\"switch\"\n type=\"checkbox\"\n [attr.autofocus]=\"autofocus ? true : null\"\n [class.invalid]=\"invalid\"\n [formControlName]=\"controlName\"\n (blur)=\"onBlur()\"\n (focus)=\"onFocus()\"\n (input)=\"onInput()\" />\n\n <ng-template #fixedCheckbox>\n <input\n class=\"switch\"\n type=\"checkbox\"\n [(ngModel)]=\"fixedValue\"\n [attr.readonly]=\"true\"\n [class.invalid]=\"invalid\"\n [class.readonly-as-disabled]=\"displayReadOnlyAsDisabled\"\n [disabled]=\"isDisabled\"\n [ngModelOptions]=\"{ standalone: true }\"\n (blur)=\"onBlur()\"\n (change)=\"onChange($event)\"\n (focus)=\"onFocus()\"\n (input)=\"onInput()\"\n (ngModelChange)=\"onValueChanged($event)\" />\n </ng-template>\n </ng-container>\n\n\n <ng-container *ngSwitchDefault>\n <input *ngIf=\"isFormControl; else fixedTextBox\"\n [attr.autofocus]=\"autofocus ? true : null\"\n [attr.maxLength]=\"maxLength ?? null\"\n [attr.min]=\"minAttributeValue\"\n [attr.readonly]=\"readOnly ? true : null\"\n [attr.step]=\"stepAttributeValue\"\n [class.invalid]=\"invalid\"\n [class.readonly-as-disabled]=\"displayReadOnlyAsDisabled\"\n [formControlName]=\"controlName\"\n [type]=\"type\"\n (blur)=\"onBlur()\"\n (focus)=\"onFocus()\"\n (input)=\"onInput()\" />\n\n <ng-template #fixedTextBox>\n <input\n type=\"text\"\n [attr.maxLength]=\"maxLength ?? null\"\n [attr.min]=\"minAttributeValue\"\n [attr.readonly]=\"true\"\n [attr.step]=\"stepAttributeValue\"\n [class.invalid]=\"invalid\"\n [class.readonly-as-disabled]=\"displayReadOnlyAsDisabled\"\n [disabled]=\"isDisabled\"\n [ngModel]=\"type === 'date' || type === 'datetime-local' ? (fixedValue | date: format) : ( type === 'number' ? (fixedValue | number) : fixedValue )\" \n [ngModelOptions]=\"{ standalone: true }\"\n (blur)=\"onBlur()\"\n (focus)=\"onFocus()\"\n (input)=\"onInput()\"\n (ngModelChange)=\"onValueChanged($event)\" />\n </ng-template>\n </ng-container>\n\n </ng-container>\n</ng-container>\n\n<ng-template #disabledControl>\n <ng-container [ngSwitch]=\"type\">\n <label *ngSwitchCase=\"'checkbox'\">\n <i [ngClass]=\"fixedValue ?? formGroupDirective.form.get(disabledControlName ?? controlName)?.value ? 'fa-solid fa-square-check' : 'fa-regular fa-square'\"></i>\n </label>\n\n <label *ngSwitchCase=\"'date'\">\n {{ fixedValue ?? formGroupDirective.form.get(disabledControlName ?? controlName)?.value | date: format }}\n </label>\n\n <label *ngSwitchCase=\"'datetime-local'\">\n {{ fixedValue ?? formGroupDirective.form.get(disabledControlName ?? controlName)?.value | date: format }}\n </label>\n\n <label *ngSwitchCase=\"'number'\">\n {{ fixedValue ?? formGroupDirective.form.get(disabledControlName ?? controlName)?.value | number: format }}\n </label>\n\n <label class=\"text\" *ngSwitchDefault>{{ fixedValue ?? formGroupDirective.form.get(disabledControlName ?? controlName)?.value }}</label>\n </ng-container>\n</ng-template>\n\n<ng-template #fixedTextbox let-fixedValue=\"fixedValue\">\n \n</ng-template>\n", styles: ["@charset \"UTF-8\";:host{display:block}:host.full-height{height:100%}:host.full-height textarea{height:100%}input,textarea{--tw-border-opacity: 1;border-color:rgb(156 163 175 / var(--tw-border-opacity, 1))}input:focus,textarea:focus{--tw-border-opacity: 1;border-color:rgb(156 163 175 / var(--tw-border-opacity, 1));--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000);--tw-ring-color: rgb(0 107 182 / .3) }input,textarea{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}input:invalid,textarea:invalid{--tw-border-opacity: 1;border-color:rgb(248 113 113 / var(--tw-border-opacity, 1))}input:disabled,input.readonly-as-disabled[readonly],textarea:disabled,textarea.readonly-as-disabled[readonly]{--tw-bg-opacity: 1;background-color:rgb(241 245 249 / var(--tw-bg-opacity, 1))}input.ng-invalid.ng-touched,input.invalid,textarea.ng-invalid.ng-touched,textarea.invalid{--tw-border-opacity: 1;border-color:rgb(220 38 38 / var(--tw-border-opacity, 1))}input[type=text],input[type=number],input[type=password],input[type=date],input[type=datetime-local],input[type=email],input[type=file],input[type=month],input[type=tel],input[type=time],input[type=url],input[type=week],textarea{display:block;width:100%;border-radius:.375rem;padding:.25rem .5rem;--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .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)}input[type=text].ng-invalid.ng-touched,input[type=text].invalid,input[type=number].ng-invalid.ng-touched,input[type=number].invalid,input[type=password].ng-invalid.ng-touched,input[type=password].invalid,input[type=date].ng-invalid.ng-touched,input[type=date].invalid,input[type=datetime-local].ng-invalid.ng-touched,input[type=datetime-local].invalid,input[type=email].ng-invalid.ng-touched,input[type=email].invalid,input[type=file].ng-invalid.ng-touched,input[type=file].invalid,input[type=month].ng-invalid.ng-touched,input[type=month].invalid,input[type=tel].ng-invalid.ng-touched,input[type=tel].invalid,input[type=time].ng-invalid.ng-touched,input[type=time].invalid,input[type=url].ng-invalid.ng-touched,input[type=url].invalid,input[type=week].ng-invalid.ng-touched,input[type=week].invalid,textarea.ng-invalid.ng-touched,textarea.invalid{margin-right:-2.25rem;padding-right:2.25rem;transition-property:none}input[type=text].ng-invalid.ng-touched:focus,input[type=text].invalid:focus,input[type=number].ng-invalid.ng-touched:focus,input[type=number].invalid:focus,input[type=password].ng-invalid.ng-touched:focus,input[type=password].invalid:focus,input[type=date].ng-invalid.ng-touched:focus,input[type=date].invalid:focus,input[type=datetime-local].ng-invalid.ng-touched:focus,input[type=datetime-local].invalid:focus,input[type=email].ng-invalid.ng-touched:focus,input[type=email].invalid:focus,input[type=file].ng-invalid.ng-touched:focus,input[type=file].invalid:focus,input[type=month].ng-invalid.ng-touched:focus,input[type=month].invalid:focus,input[type=tel].ng-invalid.ng-touched:focus,input[type=tel].invalid:focus,input[type=time].ng-invalid.ng-touched:focus,input[type=time].invalid:focus,input[type=url].ng-invalid.ng-touched:focus,input[type=url].invalid:focus,input[type=week].ng-invalid.ng-touched:focus,input[type=week].invalid:focus,textarea.ng-invalid.ng-touched:focus,textarea.invalid:focus{--tw-ring-color: rgb(248 113 113 / .3) }input[type=text].ng-invalid.ng-touched,input[type=text].invalid,input[type=number].ng-invalid.ng-touched,input[type=number].invalid,input[type=password].ng-invalid.ng-touched,input[type=password].invalid,input[type=date].ng-invalid.ng-touched,input[type=date].invalid,input[type=datetime-local].ng-invalid.ng-touched,input[type=datetime-local].invalid,input[type=email].ng-invalid.ng-touched,input[type=email].invalid,input[type=file].ng-invalid.ng-touched,input[type=file].invalid,input[type=month].ng-invalid.ng-touched,input[type=month].invalid,input[type=tel].ng-invalid.ng-touched,input[type=tel].invalid,input[type=time].ng-invalid.ng-touched,input[type=time].invalid,input[type=url].ng-invalid.ng-touched,input[type=url].invalid,input[type=week].ng-invalid.ng-touched,input[type=week].invalid,textarea.ng-invalid.ng-touched,textarea.invalid{background-size:1.125rem 1.125rem;background-position:right .5625rem center;background-repeat:no-repeat;background-image:url(\"data:image/svg+xml,%3csvg class='text-white' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e\")}input[type=checkbox]{position:relative;margin-top:.25rem;margin-bottom:.25rem;height:1.25rem;width:1.25rem;border-radius:.25rem;padding:.125rem;display:inline-flex;flex-direction:row;justify-content:flex-end;appearance:none;--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}input[type=checkbox]:disabled{pointer-events:none;opacity:.5}input[type=checkbox]:not(.switch){border-width:2px;--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .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)}input[type=checkbox]:not(.switch):focus{--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow);--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}input[type=checkbox]:not(.switch):before{border-radius:.25rem;--tw-content: \"\";content:var(--tw-content);position:absolute;inset:0;display:flex;align-items:center;justify-content:center;padding:.25rem;font-family:\"Font Awesome 6 Free\";font-weight:900}input[type=checkbox]:checked{border-color:#006bb6cc;--tw-bg-opacity: 1;background-color:rgb(0 107 182 / var(--tw-bg-opacity, 1));background-image:none}input[type=checkbox]:checked:before{--tw-content: \"\\f00c\";content:var(--tw-content);--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}input[type=checkbox]:disabled~label{opacity:.5}input[type=checkbox]~label{margin-left:.5rem;-webkit-user-select:none;user-select:none}input[type=checkbox].switch{height:1.5rem;width:3rem;border-radius:2rem;appearance:none;--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}input[type=checkbox].switch:before{aspect-ratio:1 / 1;height:100%;border-radius:9999px;--tw-content: \"\";content:var(--tw-content);background-color:#6b728099}input[type=checkbox].switch:after{flex-grow:1;--tw-content: \"\";content:var(--tw-content);transition:flex-grow .15s ease-in-out}input[type=checkbox].switch:focus:before{background-color:#3b82f699}input[type=checkbox].switch:checked{border-color:#006bb6cc;--tw-bg-opacity: 1;background-color:rgb(0 107 182 / var(--tw-bg-opacity, 1));background-image:none}input[type=checkbox].switch:checked:before{--tw-content: \"\";content:var(--tw-content);--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}input[type=checkbox].switch:checked:after{flex-grow:0}label{display:block;overflow-wrap:break-word;padding-top:.25rem;padding-bottom:.25rem}label.text{white-space:pre-wrap}\n"] }]
1243
+ }], ctorParameters: () => [], propDecorators: { autofocus: [{
1244
+ type: Input
1245
+ }], controlName: [{
1246
+ type: Input
1247
+ }], disabledControlName: [{
1248
+ type: Input
1249
+ }], displayReadOnlyAsDisabled: [{
1250
+ type: Input
1251
+ }], fixedValue: [{
1252
+ type: Input
1253
+ }], format: [{
1254
+ type: Input
1255
+ }], invalid: [{
1256
+ type: Input
1257
+ }], isDisabled: [{
1258
+ type: Input
1259
+ }], isFullHeight: [{
1260
+ type: Input
1261
+ }], maxLength: [{
1262
+ type: Input
1263
+ }], min: [{
1264
+ type: Input
1265
+ }], readOnly: [{
1266
+ type: Input
1267
+ }], rows: [{
1268
+ type: Input
1269
+ }], step: [{
1270
+ type: Input
1271
+ }], type: [{
1272
+ type: Input
1273
+ }], blur: [{
1274
+ type: Output
1275
+ }], change: [{
1276
+ type: Output
1277
+ }], fixedValueChanged: [{
1278
+ type: Output
1279
+ }], input: [{
1280
+ type: Output
1281
+ }], focus: [{
1282
+ type: Output
1283
+ }] } });
1284
+
1285
+ class FormInputGroupComponent extends BaseComponent {
1286
+ //#region ViewChilds, Inputs, Outputs
1287
+ autofocus = false;
1288
+ alignContent = 'center';
1289
+ controlName;
1290
+ fixedValue;
1291
+ format;
1292
+ label;
1293
+ isDisabled = true;
1294
+ isFullHeight = false;
1295
+ maxLength;
1296
+ min;
1297
+ notes = '';
1298
+ readOnly = false;
1299
+ rows;
1300
+ showLabel = true;
1301
+ step = 1;
1302
+ template;
1303
+ type = 'text';
1304
+ validations = {};
1305
+ //#endregion
1306
+ //#region Host listeners
1307
+ //#endregion
1308
+ //#region Variables
1309
+ formGroupDirective = inject(FormGroupDirective);
1310
+ formGroupName = inject(FormGroupName, { optional: true });
1311
+ formService = inject(FormService);
1312
+ loadingField = false;
1313
+ //#endregion
1314
+ //#region Properties
1315
+ get formControl() {
1316
+ return this.controlName
1317
+ ? this.formGroup.get(this.controlName)
1318
+ : null;
1319
+ }
1320
+ get formGroup() {
1321
+ let formGroup = this.formGroupDirective.form;
1322
+ for (let group of this.parentGroups) {
1323
+ formGroup = formGroup.get(group);
1324
+ }
1325
+ return formGroup;
1326
+ }
1327
+ get hasNotes() {
1328
+ return !!this.notes && this.notes.length > 0;
1329
+ }
1330
+ get parentGroups() {
1331
+ return this.formGroupName?.path ?? [];
1332
+ }
1333
+ //#endregion
1334
+ //#region Constructor and Angular life cycle methods
1335
+ constructor() {
1336
+ super();
1337
+ }
1338
+ ngOnInit() {
1339
+ this.formControl?.statusChanges
1340
+ .pipe(takeUntil(this.destroy$))
1341
+ .subscribe(() => {
1342
+ this.loadingField = false;
1343
+ });
1344
+ this.formControl?.valueChanges
1345
+ .pipe(takeUntil(this.destroy$))
1346
+ .subscribe(() => {
1347
+ this.loadingField = false;
1348
+ });
1349
+ this.formService.fieldRefreshed
1350
+ .pipe(takeUntil(this.destroy$), filter((field) => field.fieldName === this.controlName))
1351
+ .subscribe((field) => {
1352
+ this.loadingField = field.loading ?? false;
1353
+ });
1354
+ }
1355
+ //#endregion
1356
+ //#region Event handlers
1357
+ //#endregion
1358
+ //#region Public methods
1359
+ hasValidation(errorKey) {
1360
+ return Object.keys(this.validations).indexOf(errorKey) >= 0;
1361
+ }
1362
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: FormInputGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1363
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.6", type: FormInputGroupComponent, isStandalone: true, selector: "lib-form-input-group", inputs: { autofocus: "autofocus", alignContent: "alignContent", controlName: "controlName", fixedValue: "fixedValue", format: "format", label: "label", isDisabled: "isDisabled", isFullHeight: "isFullHeight", maxLength: "maxLength", min: "min", notes: "notes", readOnly: "readOnly", rows: "rows", showLabel: "showLabel", step: "step", template: "template", type: "type", validations: "validations" }, host: { properties: { "class.full-height": "isFullHeight" } }, usesInheritance: true, ngImport: i0, template: "<label *ngIf=\"showLabel\"\n[attr.for]=\"controlName\">\n {{ label | translate }}:\n</label>\n\n<div class=\"field\"\n[class.col-span-2]=\"!showLabel\"\n[ngStyle]=\"{ 'align-content': alignContent }\" >\n <div class=\"loading\" *ngIf=\"(loadingField || (formService.loading && formService.model === null)); else content\">\n <div></div>\n </div>\n\n <ng-template #content>\n <ng-container *ngTemplateOutlet=\"template ? template : defaultTemplate\">\n </ng-container>\n \n <div class=\"validations\" [class.show]=\"formControl?.invalid\" *ngIf=\"formControl?.touched\">\n <ng-container *ngFor=\"let error of formControl?.errors | keyvalue\">\n <span *ngIf=\"hasValidation(error.key)\">{{ validations[error.key] | translate }}</span>\n <ng-template #defaultValidationError>{{ error.key }}</ng-template>\n </ng-container>\n </div>\n \n <ng-template #defaultTemplate>\n <lib-form-input #formInput\n [autofocus]=\"autofocus\"\n [controlName]=\"controlName\"\n [fixedValue]=\"fixedValue\"\n [format]=\"format\"\n [isDisabled]=\"isDisabled\"\n [isFullHeight]=\"isFullHeight\"\n [min]=\"min\"\n [maxLength]=\"maxLength\"\n [readOnly]=\"readOnly\"\n [rows]=\"rows\"\n [step]=\"step\"\n [type]=\"type\">\n </lib-form-input>\n </ng-template>\n \n <span class=\"text-sm italic\" *ngIf=\"hasNotes\">\n *{{ notes | translate }}\n </span>\n </ng-template>\n\n</div>\n", styles: [":host{grid-column:span 2 / span 2;display:grid;grid-template-columns:subgrid;gap:.75rem}:host.full-height{max-height:100%;overflow:auto}label{white-space:nowrap;padding-top:.25rem;padding-bottom:.25rem;text-align:right;font-size:.75rem;font-weight:700;text-transform:uppercase;line-height:1.5rem;letter-spacing:.025em;--tw-text-opacity: 1;color:rgb(55 65 81 / var(--tw-text-opacity, 1))}.field .validations{flex-basis:100%;font-size:.875rem;line-height:1.25rem;--tw-text-opacity: 1;color:rgb(220 38 38 / var(--tw-text-opacity, 1))}.field .validations.show{margin-top:.25rem}.field .validations span{display:block}.field .loading{max-width:24rem}@keyframes pulse{50%{opacity:.5}}.field .loading{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}.field .loading *{height:.625rem;width:100%;border-radius:9999px;--tw-bg-opacity: 1;background-color:rgb(229 231 235 / var(--tw-bg-opacity, 1))}@media (prefers-color-scheme: dark){.field .loading *{--tw-bg-opacity: 1;background-color:rgb(55 65 81 / var(--tw-bg-opacity, 1))}}\n"], dependencies: [{ kind: "component", type: FormInputComponent, selector: "lib-form-input", inputs: ["autofocus", "controlName", "disabledControlName", "displayReadOnlyAsDisabled", "fixedValue", "format", "invalid", "isDisabled", "isFullHeight", "maxLength", "min", "readOnly", "rows", "step", "type"], outputs: ["blur", "change", "fixedValueChanged", "input", "focus"] }, { kind: "pipe", type: KeyValuePipe, name: "keyvalue" }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
1364
+ }
1365
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: FormInputGroupComponent, decorators: [{
1366
+ type: Component,
1367
+ args: [{ selector: 'lib-form-input-group', imports: [
1368
+ FormInputComponent,
1369
+ KeyValuePipe,
1370
+ NgFor,
1371
+ NgIf,
1372
+ NgStyle,
1373
+ NgTemplateOutlet,
1374
+ TranslatePipe,
1375
+ ], host: {
1376
+ '[class.full-height]': 'isFullHeight'
1377
+ }, template: "<label *ngIf=\"showLabel\"\n[attr.for]=\"controlName\">\n {{ label | translate }}:\n</label>\n\n<div class=\"field\"\n[class.col-span-2]=\"!showLabel\"\n[ngStyle]=\"{ 'align-content': alignContent }\" >\n <div class=\"loading\" *ngIf=\"(loadingField || (formService.loading && formService.model === null)); else content\">\n <div></div>\n </div>\n\n <ng-template #content>\n <ng-container *ngTemplateOutlet=\"template ? template : defaultTemplate\">\n </ng-container>\n \n <div class=\"validations\" [class.show]=\"formControl?.invalid\" *ngIf=\"formControl?.touched\">\n <ng-container *ngFor=\"let error of formControl?.errors | keyvalue\">\n <span *ngIf=\"hasValidation(error.key)\">{{ validations[error.key] | translate }}</span>\n <ng-template #defaultValidationError>{{ error.key }}</ng-template>\n </ng-container>\n </div>\n \n <ng-template #defaultTemplate>\n <lib-form-input #formInput\n [autofocus]=\"autofocus\"\n [controlName]=\"controlName\"\n [fixedValue]=\"fixedValue\"\n [format]=\"format\"\n [isDisabled]=\"isDisabled\"\n [isFullHeight]=\"isFullHeight\"\n [min]=\"min\"\n [maxLength]=\"maxLength\"\n [readOnly]=\"readOnly\"\n [rows]=\"rows\"\n [step]=\"step\"\n [type]=\"type\">\n </lib-form-input>\n </ng-template>\n \n <span class=\"text-sm italic\" *ngIf=\"hasNotes\">\n *{{ notes | translate }}\n </span>\n </ng-template>\n\n</div>\n", styles: [":host{grid-column:span 2 / span 2;display:grid;grid-template-columns:subgrid;gap:.75rem}:host.full-height{max-height:100%;overflow:auto}label{white-space:nowrap;padding-top:.25rem;padding-bottom:.25rem;text-align:right;font-size:.75rem;font-weight:700;text-transform:uppercase;line-height:1.5rem;letter-spacing:.025em;--tw-text-opacity: 1;color:rgb(55 65 81 / var(--tw-text-opacity, 1))}.field .validations{flex-basis:100%;font-size:.875rem;line-height:1.25rem;--tw-text-opacity: 1;color:rgb(220 38 38 / var(--tw-text-opacity, 1))}.field .validations.show{margin-top:.25rem}.field .validations span{display:block}.field .loading{max-width:24rem}@keyframes pulse{50%{opacity:.5}}.field .loading{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}.field .loading *{height:.625rem;width:100%;border-radius:9999px;--tw-bg-opacity: 1;background-color:rgb(229 231 235 / var(--tw-bg-opacity, 1))}@media (prefers-color-scheme: dark){.field .loading *{--tw-bg-opacity: 1;background-color:rgb(55 65 81 / var(--tw-bg-opacity, 1))}}\n"] }]
1378
+ }], ctorParameters: () => [], propDecorators: { autofocus: [{
1379
+ type: Input
1380
+ }], alignContent: [{
1381
+ type: Input
1382
+ }], controlName: [{
1383
+ type: Input
1384
+ }], fixedValue: [{
1385
+ type: Input
1386
+ }], format: [{
1387
+ type: Input
1388
+ }], label: [{
1389
+ type: Input
1390
+ }], isDisabled: [{
1391
+ type: Input
1392
+ }], isFullHeight: [{
1393
+ type: Input
1394
+ }], maxLength: [{
1395
+ type: Input
1396
+ }], min: [{
1397
+ type: Input
1398
+ }], notes: [{
1399
+ type: Input
1400
+ }], readOnly: [{
1401
+ type: Input
1402
+ }], rows: [{
1403
+ type: Input
1404
+ }], showLabel: [{
1405
+ type: Input
1406
+ }], step: [{
1407
+ type: Input
1408
+ }], template: [{
1409
+ type: Input
1410
+ }], type: [{
1411
+ type: Input
1412
+ }], validations: [{
1413
+ type: Input
1414
+ }] } });
1415
+
1416
+ class CatalogSelectComponent extends BaseComponent {
1417
+ //#region HostListeners
1418
+ bodyMouseDown(event) {
1419
+ const target = event.target;
1420
+ if (this.isDropDownShown && !target.closest('.catalog-container.show')) {
1421
+ this.wasClickedOutside = (event.button === 0 && !target.closest('.dropdown-container')) ?? false;
1422
+ }
1423
+ }
1424
+ bodyMouseUp(event) {
1425
+ if (this.isDropDownShown && this.wasClickedOutside) {
1426
+ this.closeDropdownOverlay();
1427
+ this.wasClickedOutside = false;
1428
+ }
1429
+ }
1430
+ //#endregion
1431
+ //#region ViewChilds, Inputs, Outputs
1432
+ dropdownTemplate;
1433
+ inputElement;
1434
+ autofocus = false;
1435
+ controlName;
1436
+ displayControlName;
1437
+ displayProperty = 'display';
1438
+ set entriesList(value) {
1439
+ if (this._entriesList !== value) {
1440
+ this._entriesList = value;
1441
+ if (this.isSubscriptionInitialized) {
1442
+ this.entriesDataSource = [];
1443
+ this.initializeEntriesDataSource();
1444
+ this.refreshSearch();
1445
+ }
1446
+ }
1447
+ }
1448
+ set filters(value) {
1449
+ if (this._filters !== value) {
1450
+ this._filters = value;
1451
+ this.updateFilters();
1452
+ }
1453
+ }
1454
+ label;
1455
+ maxEntries = 100;
1456
+ minimumLengthSearch = 3;
1457
+ notes = '';
1458
+ readOnly = false;
1459
+ searchEndpoint;
1460
+ validations = {};
1461
+ valueProperty = 'value';
1462
+ //#endregion
1463
+ //#region Variables
1464
+ displayedEntries = [];
1465
+ focusedIndex = -1;
1466
+ isDropDownShown = false;
1467
+ isFocused = false;
1468
+ isLoading = false;
1469
+ selectedValue;
1470
+ shouldUseCriteria = false;
1471
+ showFailureMessage = false;
1472
+ showMinimumCharactersMessage = false;
1473
+ showNoResultsMessage = false;
1474
+ static instanceCounter = 0;
1475
+ _entriesList;
1476
+ _filters = {};
1477
+ catalogService = inject(CatalogService);
1478
+ dataGridDataset = inject(DataGridDataset, { optional: true });
1479
+ entriesDataSource = [];
1480
+ filterDiffer;
1481
+ formGroup = inject(FormGroupDirective);
1482
+ formGroupName = inject(FormGroupName, { optional: true });
1483
+ instanceId;
1484
+ isDataInitialized = false;
1485
+ isSubscriptionInitialized = false;
1486
+ keyValueDiffers = inject(KeyValueDiffers);
1487
+ lastCriteriaUsed = null;
1488
+ overlay = inject(Overlay);
1489
+ overlayRef;
1490
+ positionBuilder = inject(OverlayPositionBuilder);
1491
+ searchSubject = new Subject();
1492
+ viewContainerRef = inject(ViewContainerRef);
1493
+ wasClickedOutside = false;
1494
+ //#endregion
1495
+ //#region Properties
1496
+ get entriesList() {
1497
+ return this._entriesList;
1498
+ }
1499
+ get filters() {
1500
+ return this._filters;
1501
+ }
1502
+ get activeDescendantId() {
1503
+ if (this.focusedIndex >= 0 && this.focusedIndex < this.displayedEntries.length) {
1504
+ return `catalog-select-${this.instanceId}-option-${this.focusedIndex}`;
1505
+ }
1506
+ return null;
1507
+ }
1508
+ get displayControl() {
1509
+ return this.formGroup.form.get(this.formDisplayControlName);
1510
+ }
1511
+ get formControl() {
1512
+ return this.formGroup.form.get(this.formControlName);
1513
+ }
1514
+ get formControlName() {
1515
+ return [
1516
+ ...this.formGroupName?.path ?? [],
1517
+ this.controlName
1518
+ ].join('.');
1519
+ }
1520
+ get formDisplayControlName() {
1521
+ return [
1522
+ ...this.formGroupName?.path ?? [],
1523
+ this.displayControlName
1524
+ ].join('.');
1525
+ }
1526
+ get isInvalid() {
1527
+ return this.formControl.invalid;
1528
+ }
1529
+ get isTouched() {
1530
+ return this.formControl.touched;
1531
+ }
1532
+ get listboxId() {
1533
+ return `catalog-select-${this.instanceId}-listbox`;
1534
+ }
1535
+ get showClearButton() {
1536
+ return this.selectedValue != null && !this.readOnly && (this.formControl?.enabled ?? false);
1537
+ }
1538
+ get hasSearchEndpoint() {
1539
+ return !!this.searchEndpoint && this.searchEndpoint.length > 0;
1540
+ }
1541
+ //#endregion
1542
+ //#region Constructor and Angular life cycle methods
1543
+ constructor() {
1544
+ super();
1545
+ this.instanceId = CatalogSelectComponent.instanceCounter++;
1546
+ }
1547
+ ngAfterViewInit() {
1548
+ this.setInputAriaAttributes();
1549
+ }
1550
+ ngOnDestroy() {
1551
+ this.closeDropdownOverlay();
1552
+ super.ngOnDestroy();
1553
+ }
1554
+ ngOnInit() {
1555
+ // Check and add the display control, if it does not exist.
1556
+ const wasFormDisabled = this.formGroup.form.disabled;
1557
+ this.checkAndAddDisplayControl();
1558
+ if (wasFormDisabled) {
1559
+ this.formGroup.form.disable();
1560
+ }
1561
+ // Initialize the entries data source from the entries list, if provided.
1562
+ this.initializeEntriesDataSource();
1563
+ // Initialize the search functionality.
1564
+ this.initializeSearch();
1565
+ // Subscribe to changes in the display control to apply search criteria.
1566
+ this.displayControl?.valueChanges
1567
+ .pipe(takeUntil(this.destroy$), tap(() => {
1568
+ if (!this.isFocused) {
1569
+ return;
1570
+ }
1571
+ this.isLoading = true;
1572
+ this.resetMessages();
1573
+ }), debounceTime(500), startWith(null), pairwise())
1574
+ .subscribe(([previous, current]) => {
1575
+ if (!this.isFocused || previous === current) {
1576
+ this.isLoading = false;
1577
+ return;
1578
+ }
1579
+ this.applySearchCriteria(current);
1580
+ });
1581
+ this.filterDiffer = this.keyValueDiffers.find(this._filters).create();
1582
+ // Subscribe to changes in the form control to update the selected value and display control states.
1583
+ this.syncFormControlsEnabledDisabled();
1584
+ this.formControl?.valueChanges
1585
+ .pipe(takeUntil(this.destroy$))
1586
+ .subscribe((value) => {
1587
+ this.syncFormControlsEnabledDisabled();
1588
+ if (this.selectedValue === value) {
1589
+ return;
1590
+ }
1591
+ this.selectedValue = value;
1592
+ if (this.hasSearchEndpoint) {
1593
+ if (!value) {
1594
+ this.displayControl?.reset(null, { emitEvent: false });
1595
+ }
1596
+ }
1597
+ else {
1598
+ const display = this.entriesDataSource.filter((entry) => entry.value === value)[0]?.display ?? '';
1599
+ this.displayControl?.setValue(display, { emitEvent: false });
1600
+ }
1601
+ });
1602
+ // Initial sync: if the form control already has a value (e.g., the model was loaded
1603
+ // before this component initialized), sync the display control immediately.
1604
+ this.syncDisplayFromCurrentValue();
1605
+ }
1606
+ //#endregion
1607
+ //#region Event handlers
1608
+ onContainerBlur() {
1609
+ this.isFocused = false;
1610
+ }
1611
+ onContainerClick() {
1612
+ if ((!this.isDropDownShown || this.readOnly)
1613
+ && (this.formGroup.form?.enabled ?? false)
1614
+ && (this.formGroup.form?.get(this.formControlName)?.enabled ?? false)) {
1615
+ this.openDropdownOverlay();
1616
+ }
1617
+ }
1618
+ onContainerFocus() {
1619
+ this.isFocused = true;
1620
+ if (!this.isDropDownShown && !this.readOnly
1621
+ && (this.formGroup.form?.enabled ?? false)
1622
+ && (this.formGroup.form?.get(this.formControlName)?.enabled ?? false)) {
1623
+ this.openDropdownOverlay();
1624
+ }
1625
+ }
1626
+ onClearClick(event) {
1627
+ event.stopPropagation();
1628
+ if (this.formControl) {
1629
+ this.formControl.setValue(null);
1630
+ this.formControl.markAsDirty();
1631
+ this.formControl.markAsTouched();
1632
+ }
1633
+ if (this.displayControl) {
1634
+ this.displayControl.setValue('', { emitEvent: false });
1635
+ this.displayControl.markAsDirty();
1636
+ this.displayControl.markAsTouched();
1637
+ }
1638
+ this.selectedValue = null;
1639
+ }
1640
+ onKeyDown(event) {
1641
+ switch (event.key) {
1642
+ case 'ArrowDown':
1643
+ event.preventDefault();
1644
+ if (!this.isDropDownShown) {
1645
+ this.openDropdownOverlay();
1646
+ }
1647
+ if (this.displayedEntries.length > 0) {
1648
+ this.focusedIndex = Math.min(this.focusedIndex + 1, this.displayedEntries.length - 1);
1649
+ this.scrollFocusedItemIntoView();
1650
+ }
1651
+ this.updateInputAriaActiveDescendant();
1652
+ break;
1653
+ case 'ArrowUp':
1654
+ event.preventDefault();
1655
+ if (this.isDropDownShown && this.displayedEntries.length > 0) {
1656
+ this.focusedIndex = Math.max(this.focusedIndex - 1, 0);
1657
+ this.scrollFocusedItemIntoView();
1658
+ }
1659
+ this.updateInputAriaActiveDescendant();
1660
+ break;
1661
+ case 'Enter':
1662
+ event.preventDefault();
1663
+ if (this.isDropDownShown && this.focusedIndex >= 0 && this.focusedIndex < this.displayedEntries.length) {
1664
+ this.onDropDownItemClick(this.displayedEntries[this.focusedIndex]);
1665
+ }
1666
+ break;
1667
+ case 'Escape':
1668
+ if (this.isDropDownShown) {
1669
+ event.preventDefault();
1670
+ this.closeDropdownOverlay();
1671
+ }
1672
+ break;
1673
+ case 'Tab':
1674
+ if (this.isDropDownShown) {
1675
+ this.closeDropdownOverlay();
1676
+ }
1677
+ break;
1678
+ }
1679
+ }
1680
+ onDropDownErrorClick() {
1681
+ this.refreshSearch();
1682
+ }
1683
+ onDropDownItemClick(entry) {
1684
+ if (this.displayControl) {
1685
+ this.displayControl.markAsDirty();
1686
+ this.displayControl.markAllAsTouched();
1687
+ this.displayControl.setValue(entry.display, { emitEvent: false });
1688
+ }
1689
+ if (this.formControl) {
1690
+ this.formControl.markAsDirty();
1691
+ this.formControl.markAsTouched();
1692
+ this.formControl.setValue(entry.value);
1693
+ }
1694
+ this.closeDropdownOverlay();
1695
+ }
1696
+ onDropDownMessageClick() {
1697
+ this.closeDropdownOverlay();
1698
+ }
1699
+ //#endregion
1700
+ //#region Public methods
1701
+ getOptionId(index) {
1702
+ return `catalog-select-${this.instanceId}-option-${index}`;
1703
+ }
1704
+ //#endregion
1705
+ //#region Private methods
1706
+ applySearchCriteria(criteria) {
1707
+ this.isLoading = true;
1708
+ this.resetMessages();
1709
+ this.searchSubject.next(criteria);
1710
+ }
1711
+ checkAndAddDisplayControl() {
1712
+ let parent = this.formGroup.form;
1713
+ const controlNames = this.formDisplayControlName.split('.');
1714
+ controlNames.forEach((name, index) => {
1715
+ if (index < controlNames.length - 1) {
1716
+ parent = parent.get(name);
1717
+ }
1718
+ else {
1719
+ if (!parent.get(name)) {
1720
+ parent.addControl(name, new FormControl(''));
1721
+ }
1722
+ }
1723
+ });
1724
+ }
1725
+ closeDropdownOverlay() {
1726
+ this.isDropDownShown = false;
1727
+ this.focusedIndex = -1;
1728
+ this.updateInputAriaActiveDescendant();
1729
+ if (this.overlayRef) {
1730
+ this.overlayRef.detach();
1731
+ this.overlayRef.dispose();
1732
+ this.overlayRef = undefined;
1733
+ }
1734
+ }
1735
+ initializeEntriesDataSource() {
1736
+ if (this.hasSearchEndpoint || !this._entriesList || this._entriesList.length === 0) {
1737
+ return;
1738
+ }
1739
+ const observables = [];
1740
+ this._entriesList.forEach((entry) => {
1741
+ const value = entry[this.valueProperty];
1742
+ const display = entry[this.displayProperty];
1743
+ if (display instanceof Observable) {
1744
+ this.entriesDataSource.push({ value: value, display: 'Loading...' });
1745
+ observables.push(display
1746
+ .pipe(take(1), tap(v => {
1747
+ this.entriesDataSource.filter(x => x.value === value)[0].display = v;
1748
+ if (this.selectedValue === value) {
1749
+ this.displayControl.setValue(v);
1750
+ }
1751
+ })));
1752
+ }
1753
+ else {
1754
+ this.entriesDataSource.push({ value, display });
1755
+ }
1756
+ });
1757
+ forkJoin(observables)
1758
+ .pipe(take(1))
1759
+ .subscribe(() => {
1760
+ });
1761
+ }
1762
+ initializeSearch() {
1763
+ this.searchSubject
1764
+ .pipe(takeUntil(this.destroy$), switchMap((criteria) => {
1765
+ this.lastCriteriaUsed = criteria;
1766
+ if (!this.hasSearchEndpoint) {
1767
+ if (this.entriesDataSource.length === 0) {
1768
+ return of(null).pipe(take(1));
1769
+ }
1770
+ return of(this.entriesDataSource.filter((entry) => entry.display.toLowerCase().indexOf(criteria?.toLowerCase() ?? '') > -1)).pipe(take(1));
1771
+ }
1772
+ this.updateMinimumCharactersMessageVisibility(criteria ?? '');
1773
+ if (this.showMinimumCharactersMessage) {
1774
+ return of(null).pipe(take(1));
1775
+ }
1776
+ return this.catalogService.search(this.searchEndpoint, this.maxEntries, criteria ?? '', this.filters)
1777
+ .pipe(take(1));
1778
+ })).subscribe({
1779
+ next: (result) => {
1780
+ this.isLoading = false;
1781
+ this.displayedEntries = [];
1782
+ if (Array.isArray(result) && result.length > 0) {
1783
+ this.displayedEntries = result;
1784
+ }
1785
+ else if (result?.entries && Array.isArray(result.entries)) {
1786
+ const catalogResult = result;
1787
+ if (!catalogResult) {
1788
+ return;
1789
+ }
1790
+ this.displayedEntries = catalogResult.entries;
1791
+ if (!this.isDataInitialized) {
1792
+ // The shouldUseCriteria is only set from the initialization.
1793
+ this.isDataInitialized = true;
1794
+ this.shouldUseCriteria = catalogResult.shouldUseCriteria ?? false;
1795
+ this.showMinimumCharactersMessage = this.displayedEntries.length === 0;
1796
+ }
1797
+ }
1798
+ // Try to filter out already loaded rows from the data grid dataset.
1799
+ if (this.displayedEntries.length > 0 && this.dataGridDataset) {
1800
+ this.displayedEntries = this.tryFilterFromDataset(this.displayedEntries);
1801
+ }
1802
+ if (!this.showMinimumCharactersMessage && this.displayedEntries.length === 0) {
1803
+ this.showNoResultsMessage = true;
1804
+ }
1805
+ },
1806
+ error: (_) => {
1807
+ this.isLoading = false;
1808
+ this.displayedEntries = [];
1809
+ this.showFailureMessage = true;
1810
+ // Set a new subscription to allow retrying the search later.
1811
+ this.initializeSearch();
1812
+ }
1813
+ });
1814
+ if (!this.isSubscriptionInitialized) {
1815
+ this.isSubscriptionInitialized = true;
1816
+ this.applySearchCriteria(null);
1817
+ }
1818
+ }
1819
+ openDropdownOverlay() {
1820
+ // Defensive cleanup of any stale overlay before creating a new one.
1821
+ if (this.overlayRef) {
1822
+ this.overlayRef.detach();
1823
+ this.overlayRef.dispose();
1824
+ this.overlayRef = undefined;
1825
+ }
1826
+ const positionStrategy = this.positionBuilder
1827
+ .flexibleConnectedTo(this.inputElement)
1828
+ .withPositions([
1829
+ {
1830
+ originX: 'start',
1831
+ originY: 'bottom',
1832
+ overlayX: 'start',
1833
+ overlayY: 'top',
1834
+ },
1835
+ {
1836
+ originX: 'start',
1837
+ originY: 'top',
1838
+ overlayX: 'start',
1839
+ overlayY: 'bottom',
1840
+ }
1841
+ ]);
1842
+ this.overlayRef = this.overlay.create({
1843
+ positionStrategy,
1844
+ hasBackdrop: false,
1845
+ scrollStrategy: this.overlay.scrollStrategies.reposition(),
1846
+ });
1847
+ // Set overlay width to match input field
1848
+ const inputRect = this.inputElement.nativeElement.getBoundingClientRect();
1849
+ this.overlayRef.updateSize({ width: inputRect.width });
1850
+ const portal = new TemplatePortal(this.dropdownTemplate, this.viewContainerRef);
1851
+ this.overlayRef.attach(portal);
1852
+ this.isDropDownShown = true;
1853
+ this.focusedIndex = this.selectedValue != null
1854
+ ? this.displayedEntries.findIndex((entry) => entry.value === this.selectedValue)
1855
+ : -1;
1856
+ this.updateInputAriaActiveDescendant();
1857
+ // Re-evaluate minimum characters message visibility when opening the dropdown,
1858
+ // in case it was incorrectly reset by a programmatic value change.
1859
+ if (this.shouldUseCriteria && !this.isLoading) {
1860
+ this.updateMinimumCharactersMessageVisibility(this.displayControl?.value ?? '');
1861
+ }
1862
+ // Scroll the selected item into view after the overlay renders.
1863
+ if (this.focusedIndex >= 0) {
1864
+ requestAnimationFrame(() => this.scrollFocusedItemIntoView());
1865
+ }
1866
+ }
1867
+ refreshSearch(reInitialize = false) {
1868
+ if (reInitialize) {
1869
+ this.isDataInitialized = false;
1870
+ }
1871
+ this.applySearchCriteria(this.lastCriteriaUsed);
1872
+ }
1873
+ resetMessages() {
1874
+ this.showFailureMessage = false;
1875
+ this.showNoResultsMessage = false;
1876
+ this.showMinimumCharactersMessage = false;
1877
+ }
1878
+ syncDisplayFromCurrentValue() {
1879
+ const currentValue = this.formControl?.value;
1880
+ if (currentValue == null || this.selectedValue === currentValue) {
1881
+ return;
1882
+ }
1883
+ this.selectedValue = currentValue;
1884
+ if (!this.hasSearchEndpoint) {
1885
+ const display = this.entriesDataSource.filter((entry) => entry.value === currentValue)[0]?.display ?? '';
1886
+ this.displayControl?.setValue(display, { emitEvent: false });
1887
+ }
1888
+ }
1889
+ syncFormControlsEnabledDisabled() {
1890
+ if (this.formControl.enabled !== this.displayControl.enabled) {
1891
+ if (this.formControl.disabled) {
1892
+ this.displayControl.disable({ emitEvent: false });
1893
+ }
1894
+ else {
1895
+ this.displayControl.enable({ emitEvent: false });
1896
+ }
1897
+ }
1898
+ }
1899
+ tryFilterFromDataset(results) {
1900
+ if (this.dataGridDataset
1901
+ && this.controlName === this.dataGridDataset?.keyProperty
1902
+ && this.dataGridDataset.hasLoadedRows) {
1903
+ // If the control name is matching the ID property of the data grid dataset, filter out the loaded rows to remove duplicated IDs.
1904
+ const loadedIDs = this.dataGridDataset.loadedKeys.map((key) => this.dataGridDataset.getRowID(key));
1905
+ return results.filter((result) => loadedIDs.indexOf(result.value) < 0);
1906
+ }
1907
+ return results;
1908
+ }
1909
+ updateFilters() {
1910
+ if (!this.filterDiffer || !this.filters) {
1911
+ return;
1912
+ }
1913
+ const changes = this.filterDiffer.diff(this.filters);
1914
+ if (changes) {
1915
+ const reInitialize = true;
1916
+ this.refreshSearch(reInitialize);
1917
+ }
1918
+ }
1919
+ updateMinimumCharactersMessageVisibility(criteria) {
1920
+ this.showMinimumCharactersMessage =
1921
+ this.shouldUseCriteria
1922
+ && this.minimumLengthSearch > 0
1923
+ && (criteria?.length ?? 0) < this.minimumLengthSearch;
1924
+ }
1925
+ getNativeInput() {
1926
+ return this.inputElement?.nativeElement?.querySelector('input') ?? null;
1927
+ }
1928
+ scrollFocusedItemIntoView() {
1929
+ if (!this.overlayRef || this.focusedIndex < 0) {
1930
+ return;
1931
+ }
1932
+ const optionId = this.getOptionId(this.focusedIndex);
1933
+ const element = this.overlayRef.overlayElement.querySelector('#' + optionId);
1934
+ if (element) {
1935
+ element.scrollIntoView({ block: 'nearest' });
1936
+ }
1937
+ }
1938
+ setInputAriaAttributes() {
1939
+ const input = this.getNativeInput();
1940
+ if (!input) {
1941
+ return;
1942
+ }
1943
+ input.setAttribute('role', 'combobox');
1944
+ input.setAttribute('aria-autocomplete', 'list');
1945
+ input.setAttribute('aria-expanded', String(this.isDropDownShown));
1946
+ input.setAttribute('aria-haspopup', 'listbox');
1947
+ input.setAttribute('aria-owns', this.listboxId);
1948
+ }
1949
+ updateInputAriaActiveDescendant() {
1950
+ const input = this.getNativeInput();
1951
+ if (!input) {
1952
+ return;
1953
+ }
1954
+ input.setAttribute('aria-expanded', String(this.isDropDownShown));
1955
+ const descendantId = this.activeDescendantId;
1956
+ if (descendantId) {
1957
+ input.setAttribute('aria-activedescendant', descendantId);
1958
+ }
1959
+ else {
1960
+ input.removeAttribute('aria-activedescendant');
1961
+ }
1962
+ }
1963
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: CatalogSelectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1964
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.6", type: CatalogSelectComponent, isStandalone: true, selector: "lib-catalog-select", inputs: { autofocus: "autofocus", controlName: "controlName", displayControlName: "displayControlName", displayProperty: "displayProperty", entriesList: "entriesList", filters: "filters", label: "label", maxEntries: "maxEntries", minimumLengthSearch: "minimumLengthSearch", notes: "notes", readOnly: "readOnly", searchEndpoint: "searchEndpoint", validations: "validations", valueProperty: "valueProperty" }, host: { listeners: { "body:mousedown": "bodyMouseDown($event)", "body:mouseup": "bodyMouseUp($event)" } }, viewQueries: [{ propertyName: "dropdownTemplate", first: true, predicate: ["dropdownTemplate"], descendants: true, read: TemplateRef }, { propertyName: "inputElement", first: true, predicate: ["input"], descendants: true, read: ElementRef }], usesInheritance: true, ngImport: i0, template: "<lib-form-input-group\n[controlName]=\"controlName\"\n[label]=\"label\"\n[notes]=\"notes\"\n[template]=\"template\"\n[validations]=\"validations\">\n</lib-form-input-group>\n\n<ng-template #template>\n <div class=\"catalog-container\"\n [class.show]=\"isDropDownShown\">\n <lib-form-input #input\n [class.fixed-content]=\"readOnly && !shouldUseCriteria\"\n [autofocus]=\"autofocus\"\n [controlName]=\"displayControlName\"\n [invalid]=\"isInvalid && isTouched\"\n [readOnly]=\"readOnly\"\n (blur)=\"onContainerBlur()\"\n (click)=\"onContainerClick()\"\n (focus)=\"onContainerFocus()\"\n (keydown)=\"onKeyDown($event)\">\n </lib-form-input>\n <button *ngIf=\"showClearButton\"\n class=\"clear-button\"\n type=\"button\"\n tabindex=\"-1\"\n (click)=\"onClearClick($event)\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n </div>\n</ng-template>\n\n<ng-template #dropdownTemplate>\n <div class=\"dropdown-container\">\n <ul class=\"dropdown\"\n role=\"listbox\"\n [attr.id]=\"listboxId\">\n <li *ngIf=\"isLoading\"\n (click)=\"onDropDownMessageClick()\">\n {{ 'CatalogSelect-Loading' | translate }}\n </li>\n\n <li *ngIf=\"shouldUseCriteria && showMinimumCharactersMessage\"\n title=\"{{ 'CatalogSelect-MinimumLengthSearch' | translate: { minimumLengthSearch: minimumLengthSearch } }}\"\n (click)=\"onDropDownMessageClick()\">\n {{ 'CatalogSelect-MinimumLengthSearch' | translate: { minimumLengthSearch: minimumLengthSearch } }}\n </li>\n\n <li *ngIf=\"showNoResultsMessage\"\n title=\"{{ 'CatalogSelect-NoResultsFound' | translate }}\"\n (click)=\"onDropDownMessageClick()\">\n {{ 'CatalogSelect-NoResultsFound' | translate }}\n </li>\n\n <li *ngIf=\"showFailureMessage\"\n class=\"error\"\n title=\"{{ 'CatalogSelect-FailureMessage' | translate }}\"\n (click)=\"onDropDownErrorClick()\">\n {{ 'CatalogSelect-FailureMessage' | translate }}\n </li>\n\n <li class=\"dropdown-item\" *ngFor=\"let entry of displayedEntries; let i = index\"\n role=\"option\"\n [attr.id]=\"getOptionId(i)\"\n [attr.aria-selected]=\"selectedValue === entry.value\"\n [class.focused]=\"focusedIndex === i\"\n [class.selected]=\"selectedValue === entry.value\"\n (click)=\"onDropDownItemClick(entry)\">\n {{ entry.display }}\n </li>\n </ul>\n </div>\n</ng-template>", styles: [":host-context{grid-column:span 2 / span 2;display:grid;grid-template-columns:subgrid}.catalog-container{position:relative}.catalog-container .clear-button{position:absolute;right:1.75rem;top:50%;display:flex;--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));align-items:center;justify-content:center;height:1.25rem;width:1.25rem;border-radius:9999px;--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity, 1))}.catalog-container .clear-button:hover{--tw-bg-opacity: 1;background-color:rgb(229 231 235 / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(75 85 99 / var(--tw-text-opacity, 1))}.catalog-container .clear-button{z-index:10;cursor:pointer;border-style:none;background-color:transparent;padding:0;font-size:.875rem;line-height:1.25rem;line-height:1}.catalog-container:has(.clear-button) lib-form-input ::ng-deep input{padding-right:3.5rem}div lib-form-input ::ng-deep input{cursor:pointer;text-overflow:ellipsis;background-size:1.125rem 1.125rem;background-position:right .5625rem center;background-repeat:no-repeat;padding-right:2.25rem;background-image:url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'><path fill='%239ca3af' d='M233.4 406.6c12.5 12.5 32.8 12.5 45.3 0l192-192c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L256 338.7 86.6 169.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l192 192z'/%3e%3c/svg%3e\")}.dropdown-container{width:100%;-webkit-user-select:none;user-select:none}.dropdown{overflow-y:auto;overscroll-behavior:none;border-radius:.375rem;border-width:1px;--tw-border-opacity: 1;border-color:rgb(156 163 175 / var(--tw-border-opacity, 1));--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1));--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / .1), 0 1px 2px -1px rgb(0 0 0 / .1);--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow);max-height:13rem}.dropdown li{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;padding:.25rem .5rem}.dropdown li:not(.dropdown-item){font-size:.875rem;line-height:1.25rem}.dropdown li.dropdown-item{cursor:pointer}.dropdown li.dropdown-item:nth-child(2n){background-color:#f5f5f4b3}.dropdown li.dropdown-item.selected{background-color:#59a5db33;font-weight:700}.dropdown li.dropdown-item.focused{background-color:#59a5db4d;outline:2px solid transparent;outline-offset:2px}.dropdown li.dropdown-item:hover,.dropdown li.dropdown-item:nth-child(2n):hover,.dropdown li.dropdown-item.selected:hover,.dropdown li.dropdown-item.focused:hover{background-color:#59a5db80}lib-form-input-group{flex-basis:100%}\n"], dependencies: [{ kind: "component", type: FormInputComponent, selector: "lib-form-input", inputs: ["autofocus", "controlName", "disabledControlName", "displayReadOnlyAsDisabled", "fixedValue", "format", "invalid", "isDisabled", "isFullHeight", "maxLength", "min", "readOnly", "rows", "step", "type"], outputs: ["blur", "change", "fixedValueChanged", "input", "focus"] }, { kind: "component", type: FormInputGroupComponent, selector: "lib-form-input-group", inputs: ["autofocus", "alignContent", "controlName", "fixedValue", "format", "label", "isDisabled", "isFullHeight", "maxLength", "min", "notes", "readOnly", "rows", "showLabel", "step", "template", "type", "validations"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
1965
+ }
1966
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: CatalogSelectComponent, decorators: [{
1967
+ type: Component,
1968
+ args: [{ selector: 'lib-catalog-select', imports: [
1969
+ FormInputComponent,
1970
+ FormInputGroupComponent,
1971
+ NgFor,
1972
+ NgIf,
1973
+ TranslatePipe,
1974
+ ], template: "<lib-form-input-group\n[controlName]=\"controlName\"\n[label]=\"label\"\n[notes]=\"notes\"\n[template]=\"template\"\n[validations]=\"validations\">\n</lib-form-input-group>\n\n<ng-template #template>\n <div class=\"catalog-container\"\n [class.show]=\"isDropDownShown\">\n <lib-form-input #input\n [class.fixed-content]=\"readOnly && !shouldUseCriteria\"\n [autofocus]=\"autofocus\"\n [controlName]=\"displayControlName\"\n [invalid]=\"isInvalid && isTouched\"\n [readOnly]=\"readOnly\"\n (blur)=\"onContainerBlur()\"\n (click)=\"onContainerClick()\"\n (focus)=\"onContainerFocus()\"\n (keydown)=\"onKeyDown($event)\">\n </lib-form-input>\n <button *ngIf=\"showClearButton\"\n class=\"clear-button\"\n type=\"button\"\n tabindex=\"-1\"\n (click)=\"onClearClick($event)\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n </div>\n</ng-template>\n\n<ng-template #dropdownTemplate>\n <div class=\"dropdown-container\">\n <ul class=\"dropdown\"\n role=\"listbox\"\n [attr.id]=\"listboxId\">\n <li *ngIf=\"isLoading\"\n (click)=\"onDropDownMessageClick()\">\n {{ 'CatalogSelect-Loading' | translate }}\n </li>\n\n <li *ngIf=\"shouldUseCriteria && showMinimumCharactersMessage\"\n title=\"{{ 'CatalogSelect-MinimumLengthSearch' | translate: { minimumLengthSearch: minimumLengthSearch } }}\"\n (click)=\"onDropDownMessageClick()\">\n {{ 'CatalogSelect-MinimumLengthSearch' | translate: { minimumLengthSearch: minimumLengthSearch } }}\n </li>\n\n <li *ngIf=\"showNoResultsMessage\"\n title=\"{{ 'CatalogSelect-NoResultsFound' | translate }}\"\n (click)=\"onDropDownMessageClick()\">\n {{ 'CatalogSelect-NoResultsFound' | translate }}\n </li>\n\n <li *ngIf=\"showFailureMessage\"\n class=\"error\"\n title=\"{{ 'CatalogSelect-FailureMessage' | translate }}\"\n (click)=\"onDropDownErrorClick()\">\n {{ 'CatalogSelect-FailureMessage' | translate }}\n </li>\n\n <li class=\"dropdown-item\" *ngFor=\"let entry of displayedEntries; let i = index\"\n role=\"option\"\n [attr.id]=\"getOptionId(i)\"\n [attr.aria-selected]=\"selectedValue === entry.value\"\n [class.focused]=\"focusedIndex === i\"\n [class.selected]=\"selectedValue === entry.value\"\n (click)=\"onDropDownItemClick(entry)\">\n {{ entry.display }}\n </li>\n </ul>\n </div>\n</ng-template>", styles: [":host-context{grid-column:span 2 / span 2;display:grid;grid-template-columns:subgrid}.catalog-container{position:relative}.catalog-container .clear-button{position:absolute;right:1.75rem;top:50%;display:flex;--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));align-items:center;justify-content:center;height:1.25rem;width:1.25rem;border-radius:9999px;--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity, 1))}.catalog-container .clear-button:hover{--tw-bg-opacity: 1;background-color:rgb(229 231 235 / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(75 85 99 / var(--tw-text-opacity, 1))}.catalog-container .clear-button{z-index:10;cursor:pointer;border-style:none;background-color:transparent;padding:0;font-size:.875rem;line-height:1.25rem;line-height:1}.catalog-container:has(.clear-button) lib-form-input ::ng-deep input{padding-right:3.5rem}div lib-form-input ::ng-deep input{cursor:pointer;text-overflow:ellipsis;background-size:1.125rem 1.125rem;background-position:right .5625rem center;background-repeat:no-repeat;padding-right:2.25rem;background-image:url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'><path fill='%239ca3af' d='M233.4 406.6c12.5 12.5 32.8 12.5 45.3 0l192-192c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L256 338.7 86.6 169.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l192 192z'/%3e%3c/svg%3e\")}.dropdown-container{width:100%;-webkit-user-select:none;user-select:none}.dropdown{overflow-y:auto;overscroll-behavior:none;border-radius:.375rem;border-width:1px;--tw-border-opacity: 1;border-color:rgb(156 163 175 / var(--tw-border-opacity, 1));--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1));--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / .1), 0 1px 2px -1px rgb(0 0 0 / .1);--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow);max-height:13rem}.dropdown li{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;padding:.25rem .5rem}.dropdown li:not(.dropdown-item){font-size:.875rem;line-height:1.25rem}.dropdown li.dropdown-item{cursor:pointer}.dropdown li.dropdown-item:nth-child(2n){background-color:#f5f5f4b3}.dropdown li.dropdown-item.selected{background-color:#59a5db33;font-weight:700}.dropdown li.dropdown-item.focused{background-color:#59a5db4d;outline:2px solid transparent;outline-offset:2px}.dropdown li.dropdown-item:hover,.dropdown li.dropdown-item:nth-child(2n):hover,.dropdown li.dropdown-item.selected:hover,.dropdown li.dropdown-item.focused:hover{background-color:#59a5db80}lib-form-input-group{flex-basis:100%}\n"] }]
1975
+ }], ctorParameters: () => [], propDecorators: { bodyMouseDown: [{
1976
+ type: HostListener,
1977
+ args: ['body:mousedown', ['$event']]
1978
+ }], bodyMouseUp: [{
1979
+ type: HostListener,
1980
+ args: ['body:mouseup', ['$event']]
1981
+ }], dropdownTemplate: [{
1982
+ type: ViewChild,
1983
+ args: ['dropdownTemplate', { read: TemplateRef }]
1984
+ }], inputElement: [{
1985
+ type: ViewChild,
1986
+ args: ['input', { read: ElementRef }]
1987
+ }], autofocus: [{
1988
+ type: Input
1989
+ }], controlName: [{
1990
+ type: Input
1991
+ }], displayControlName: [{
1992
+ type: Input
1993
+ }], displayProperty: [{
1994
+ type: Input
1995
+ }], entriesList: [{
1996
+ type: Input
1997
+ }], filters: [{
1998
+ type: Input
1999
+ }], label: [{
2000
+ type: Input
2001
+ }], maxEntries: [{
2002
+ type: Input
2003
+ }], minimumLengthSearch: [{
2004
+ type: Input
2005
+ }], notes: [{
2006
+ type: Input
2007
+ }], readOnly: [{
2008
+ type: Input
2009
+ }], searchEndpoint: [{
2010
+ type: Input
2011
+ }], validations: [{
2012
+ type: Input
2013
+ }], valueProperty: [{
2014
+ type: Input
2015
+ }] } });
2016
+
2017
+ class DataGridRowComponent extends BaseComponent {
2018
+ //#region ViewChilds, Inputs, Outputs
2019
+ disabled = false;
2020
+ isFirstRow = false;
2021
+ set rowData(value) {
2022
+ this._rowData = value;
2023
+ this.refreshSelection();
2024
+ }
2025
+ selectionResized = new EventEmitter();
2026
+ //#endregion
2027
+ //#region Host listeners
2028
+ setFocus() {
2029
+ if (!this.disabled) {
2030
+ this.dataGridDataset.setFocusedRow(this.rowKey);
2031
+ if (this.dataGridDataset.configs.multiSelect && (this.dataGridDataset.configs.selectOnClick ?? true)) {
2032
+ this.selected = this.isRowDataSelected;
2033
+ }
2034
+ }
2035
+ }
2036
+ //#endregion
2037
+ //#region Variables
2038
+ changeDetectorRef = inject(ChangeDetectorRef);
2039
+ dataGridDataset = inject(DataGridDataset);
2040
+ selected = false;
2041
+ _rowData;
2042
+ isWidthSet = {};
2043
+ //#endregion
2044
+ //#region Properties
2045
+ get rowData() {
2046
+ return this._rowData;
2047
+ }
2048
+ get columns() {
2049
+ return this.dataGridDataset.columns;
2050
+ }
2051
+ get isRowDataSelected() {
2052
+ return this.dataGridDataset.isKeySelected(this.rowKey);
2053
+ }
2054
+ get rowKey() {
2055
+ return this.dataGridDataset.getRowInternalKey(this.rowData);
2056
+ }
2057
+ //#endregion
2058
+ //#region Constructor and Angular life cycle methods
2059
+ constructor() {
2060
+ super();
2061
+ }
2062
+ ngOnInit() {
2063
+ this.selected = this.isRowDataSelected;
2064
+ this.dataGridDataset.selectedRowsChanged
2065
+ .pipe(takeUntil(this.destroy$))
2066
+ .subscribe((selectionChanges) => {
2067
+ if (this.rowKey in selectionChanges) {
2068
+ this.selected = selectionChanges[this.rowKey].selected;
2069
+ this.changeDetectorRef.detectChanges();
2070
+ }
2071
+ });
2072
+ }
2073
+ //#endregion
2074
+ //#region Event handlers
2075
+ onResize(colIndex, event) {
2076
+ if (this.isWidthSet[colIndex]) {
2077
+ return;
2078
+ }
2079
+ const width = event.contentRect.width;
2080
+ if (width <= 0) {
2081
+ return;
2082
+ }
2083
+ if (this.columns[colIndex].realSize != width) {
2084
+ this.columns[colIndex].realSize = width;
2085
+ this.isWidthSet[colIndex] = true;
2086
+ }
2087
+ }
2088
+ onResizeSelection(event) {
2089
+ const width = event.contentRect.width;
2090
+ this.selectionResized.emit(width);
2091
+ }
2092
+ onToggleSelection() {
2093
+ if (this.disabled || (this.dataGridDataset.configs.multiSelect && (this.dataGridDataset.configs.selectOnClick ?? true))) {
2094
+ return;
2095
+ }
2096
+ this.selected = !this.selected;
2097
+ if (this.selected) {
2098
+ this.dataGridDataset.selectRow(this.rowKey);
2099
+ }
2100
+ else {
2101
+ this.dataGridDataset.deselectRow(this.rowKey);
2102
+ }
2103
+ }
2104
+ //#endregion
2105
+ //#region Public methods
2106
+ getValue(field) {
2107
+ return field.split('.').reduce((value, field) => value[field], this.rowData);
2108
+ }
2109
+ //#endregion
2110
+ //#region Private methods
2111
+ refreshSelection() {
2112
+ this.selected = this.isRowDataSelected;
2113
+ }
2114
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: DataGridRowComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2115
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.6", type: DataGridRowComponent, isStandalone: true, selector: "lib-data-grid-row", inputs: { disabled: "disabled", isFirstRow: "isFirstRow", rowData: "rowData" }, outputs: { selectionResized: "selectionResized" }, host: { listeners: { "click": "setFocus()" }, properties: { "class.focused": "dataGridDataset.focusedRow === rowData", "class.selected": "dataGridDataset.isKeySelected(rowKey)" } }, usesInheritance: true, ngImport: i0, template: "<div *ngIf=\"dataGridDataset.configs.multiSelect\"\nclass=\"cell\"\n(click)=\"onToggleSelection()\"\n(onResize)=\"onResizeSelection($event)\">\n <input class=\"form-input\"\n type=\"checkbox\"\n [attr.disabled]=\"disabled\"\n [(ngModel)]=\"selected\" />\n</div> \n\n<div #cell\n*ngFor=\"let column of columns; index as i\"\nclass=\"cell\"\n[class.disabled]=\"disabled\"\n[ngClass]=\"column.customClass\">\n <div *ngIf=\"isFirstRow\"\n class=\"spacer\"\n (onResize)=\"onResize(i, $event)\"></div>\n\n <ng-container *ngTemplateOutlet=\"column.template ? column.template : simpleTemplate; context: { rowData: rowData, column: column }\">\n </ng-container>\n</div>\n\n<ng-template #simpleTemplate let-rowData=\"rowData\" let-column=\"column\">\n <span>{{ getValue(column.field) }}</span>\n</ng-template>", styles: [":host{grid-column:1 / -1;display:grid;grid-template-columns:subgrid}.cell{position:relative;align-content:center;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.cell.disabled{pointer-events:none;cursor:default;overflow:hidden;text-overflow:ellipsis;opacity:.6}.cell input{vertical-align:super}.spacer{position:absolute;inset:0;z-index:-1;height:0px}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: NgxResizeObserverModule }, { kind: "directive", type: i2.NgxResizeObserverDirective, selector: "[onResize]", inputs: ["resizeBoxModel"], outputs: ["onResize"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2116
+ }
2117
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: DataGridRowComponent, decorators: [{
2118
+ type: Component,
2119
+ args: [{ selector: 'lib-data-grid-row', host: {
2120
+ '[class.focused]': 'dataGridDataset.focusedRow === rowData',
2121
+ '[class.selected]': 'dataGridDataset.isKeySelected(rowKey)',
2122
+ }, changeDetection: ChangeDetectionStrategy.OnPush, imports: [
2123
+ FormsModule,
2124
+ NgClass,
2125
+ NgIf,
2126
+ NgFor,
2127
+ NgTemplateOutlet,
2128
+ NgxResizeObserverModule,
2129
+ ], template: "<div *ngIf=\"dataGridDataset.configs.multiSelect\"\nclass=\"cell\"\n(click)=\"onToggleSelection()\"\n(onResize)=\"onResizeSelection($event)\">\n <input class=\"form-input\"\n type=\"checkbox\"\n [attr.disabled]=\"disabled\"\n [(ngModel)]=\"selected\" />\n</div> \n\n<div #cell\n*ngFor=\"let column of columns; index as i\"\nclass=\"cell\"\n[class.disabled]=\"disabled\"\n[ngClass]=\"column.customClass\">\n <div *ngIf=\"isFirstRow\"\n class=\"spacer\"\n (onResize)=\"onResize(i, $event)\"></div>\n\n <ng-container *ngTemplateOutlet=\"column.template ? column.template : simpleTemplate; context: { rowData: rowData, column: column }\">\n </ng-container>\n</div>\n\n<ng-template #simpleTemplate let-rowData=\"rowData\" let-column=\"column\">\n <span>{{ getValue(column.field) }}</span>\n</ng-template>", styles: [":host{grid-column:1 / -1;display:grid;grid-template-columns:subgrid}.cell{position:relative;align-content:center;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.cell.disabled{pointer-events:none;cursor:default;overflow:hidden;text-overflow:ellipsis;opacity:.6}.cell input{vertical-align:super}.spacer{position:absolute;inset:0;z-index:-1;height:0px}\n"] }]
2130
+ }], ctorParameters: () => [], propDecorators: { disabled: [{
2131
+ type: Input
2132
+ }], isFirstRow: [{
2133
+ type: Input
2134
+ }], rowData: [{
2135
+ type: Input
2136
+ }], selectionResized: [{
2137
+ type: Output
2138
+ }], setFocus: [{
2139
+ type: HostListener,
2140
+ args: ['click']
2141
+ }] } });
2142
+
2143
+ class DataGridComponent extends BaseComponent {
2144
+ //#region ViewChilds, Inputs, Outputs
2145
+ bodyElement;
2146
+ viewport;
2147
+ disabled = false;
2148
+ lazyLoadRows = false;
2149
+ showButtons = false;
2150
+ //#endregion
2151
+ //#region Variables
2152
+ changeDetectorRef = inject(ChangeDetectorRef);
2153
+ dataGridDataset = inject(DataGridDataset);
2154
+ hasFailed = false;
2155
+ headerRightMargin = 0;
2156
+ loading = false;
2157
+ bodyResized$ = new Subject();
2158
+ bodyResizeObserver;
2159
+ gridRoute = '';
2160
+ isGridCurrentUrl = false;
2161
+ lastPosition = 0;
2162
+ ngZone = inject(NgZone);
2163
+ router = inject(Router);
2164
+ selectionColRealSize;
2165
+ //#endregion
2166
+ //#region Properties
2167
+ get bodyMinHeight() {
2168
+ let rowsToDisplay = this.configs.rowsToDisplay ?? 3;
2169
+ let rowsCount = this.dataGridDataset.loadedRows?.length ?? 0;
2170
+ if (rowsCount === 0) {
2171
+ rowsCount = 1;
2172
+ }
2173
+ if (rowsCount < rowsToDisplay) {
2174
+ rowsToDisplay = rowsCount;
2175
+ }
2176
+ return rowsToDisplay * this.configs.rowHeight;
2177
+ }
2178
+ get columns() {
2179
+ return this.dataGridDataset.columns;
2180
+ }
2181
+ get configs() {
2182
+ return this.dataGridDataset.configs;
2183
+ }
2184
+ get gridColumns() {
2185
+ return this.dataGridDataset.columnsSize;
2186
+ }
2187
+ get hasBeenLoaded() {
2188
+ return this.dataGridDataset.hasBeenLoaded;
2189
+ }
2190
+ get hasLoadedRows() {
2191
+ return (this.dataGridDataset.loadedRows?.length ?? 0) > 0;
2192
+ }
2193
+ get hasMessageToDisplayWhenEmpty() {
2194
+ return this.messageToDisplayWhenEmpty.length > 0;
2195
+ }
2196
+ get isHideColumns() {
2197
+ return this.dataGridDataset.configs.hideColumnHeaders ?? false;
2198
+ }
2199
+ get isShowMultiSelect() {
2200
+ return this.dataGridDataset.configs.multiSelect ?? false;
2201
+ }
2202
+ get messageToDisplayWhenEmpty() {
2203
+ return this.dataGridDataset?.configs?.messageOnEmpty ?? '';
2204
+ }
2205
+ get messageToDisplayWhenLazyLoad() {
2206
+ return this.dataGridDataset?.configs?.messageOnLazyLoad ?? '';
2207
+ }
2208
+ get messageToDisplayWhenFailed() {
2209
+ return this.dataGridDataset?.configs?.messageOnFailed ?? '';
2210
+ }
2211
+ get selectionColWidth() {
2212
+ return this.selectionColRealSize
2213
+ ? `${this.selectionColRealSize}px`
2214
+ : this.configs.multiSelectSize ?? 'minmax(1.5rem,min-content)';
2215
+ }
2216
+ get shouldDisplayMessageWhenEmpty() {
2217
+ return this.dataGridDataset?.configs?.showMessageOnEmpty ?? true;
2218
+ }
2219
+ //#endregion
2220
+ //#region Constructor and Angular life cycle methods
2221
+ constructor() {
2222
+ super();
2223
+ }
2224
+ ngOnInit() {
2225
+ this.gridRoute = this.router.url;
2226
+ this.isGridCurrentUrl = true;
2227
+ this.router.events
2228
+ .pipe(takeUntil(this.destroy$), filter((events) => events instanceof NavigationStart || events instanceof NavigationEnd))
2229
+ .subscribe((event) => {
2230
+ if (event instanceof NavigationStart && this.isGridCurrentUrl) {
2231
+ this.lastPosition = this.viewport?.measureScrollOffset() ?? 0;
2232
+ this.isGridCurrentUrl = false;
2233
+ }
2234
+ else if (event instanceof NavigationEnd && event.url === this.gridRoute) {
2235
+ this.viewport?.scrollTo({ top: this.lastPosition });
2236
+ this.viewport?.checkViewportSize();
2237
+ this.isGridCurrentUrl = true;
2238
+ }
2239
+ });
2240
+ this.dataGridDataset.loadStarted
2241
+ .pipe(takeUntil(this.destroy$))
2242
+ .subscribe(() => {
2243
+ this.loading = true;
2244
+ });
2245
+ this.dataGridDataset.loadFinished
2246
+ .pipe(takeUntil(this.destroy$))
2247
+ .subscribe((isSuccess) => {
2248
+ this.loading = false;
2249
+ this.hasFailed = !isSuccess;
2250
+ setTimeout(() => {
2251
+ const viewportWidth = this.viewport?.measureViewportSize('horizontal') ?? 0;
2252
+ if (viewportWidth > 0) {
2253
+ this.headerRightMargin = this.bodyElement.nativeElement.clientWidth - viewportWidth;
2254
+ }
2255
+ }, 100);
2256
+ this.changeDetectorRef.detectChanges();
2257
+ setTimeout(() => {
2258
+ this.viewport?.checkViewportSize();
2259
+ });
2260
+ });
2261
+ if (!this.lazyLoadRows) {
2262
+ if (!this.dataGridDataset.loadedRows) {
2263
+ this.loading = true;
2264
+ }
2265
+ this.dataGridDataset.loadRows();
2266
+ }
2267
+ else if (this.dataGridDataset.isLoading) {
2268
+ this.loading = true;
2269
+ }
2270
+ }
2271
+ ngAfterViewInit() {
2272
+ this.viewport?.renderedRangeStream
2273
+ .pipe(takeUntil(this.destroy$))
2274
+ .subscribe((listRange) => {
2275
+ const loadedRows = this.dataGridDataset.loadedRows?.length ?? 0;
2276
+ if (this.hasLoadedRows && !this.loading && listRange.end >= loadedRows && !this.dataGridDataset.loadedLastRow) {
2277
+ this.loading = true;
2278
+ this.dataGridDataset.loadRows();
2279
+ }
2280
+ });
2281
+ this.bodyResized$
2282
+ .pipe(debounceTime(150), takeUntil(this.destroy$))
2283
+ .subscribe(() => {
2284
+ const viewportWidth = this.viewport?.measureViewportSize('horizontal') ?? 0;
2285
+ if (viewportWidth > 0) {
2286
+ this.headerRightMargin = this.bodyElement.nativeElement.clientWidth - viewportWidth;
2287
+ }
2288
+ this.viewport?.checkViewportSize();
2289
+ this.changeDetectorRef.detectChanges();
2290
+ });
2291
+ this.ngZone.runOutsideAngular(() => {
2292
+ this.bodyResizeObserver = new ResizeObserver(() => {
2293
+ this.bodyResized$.next();
2294
+ });
2295
+ this.bodyResizeObserver.observe(this.bodyElement.nativeElement);
2296
+ });
2297
+ }
2298
+ ngOnDestroy() {
2299
+ this.bodyResizeObserver?.disconnect();
2300
+ super.ngOnDestroy();
2301
+ }
2302
+ //#endregion
2303
+ //#region Event handlers
2304
+ onSelectionResized(newSize) {
2305
+ if (this.selectionColRealSize != newSize) {
2306
+ this.selectionColRealSize = newSize;
2307
+ }
2308
+ }
2309
+ //#endregion
2310
+ //#region Public methods
2311
+ isFirstVisibleRow(rowIndex) {
2312
+ const renderedRange = this.viewport?.getRenderedRange();
2313
+ if (renderedRange) {
2314
+ return rowIndex === renderedRange.start;
2315
+ }
2316
+ return false;
2317
+ }
2318
+ //#endregion
2319
+ //#region Private methods
2320
+ trackByFn(index, item) {
2321
+ return item.key;
2322
+ }
2323
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: DataGridComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2324
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.6", type: DataGridComponent, isStandalone: true, selector: "lib-data-grid", inputs: { disabled: "disabled", lazyLoadRows: "lazyLoadRows", showButtons: "showButtons" }, host: { properties: { "class.no-borders": "showButtons" } }, viewQueries: [{ propertyName: "bodyElement", first: true, predicate: ["body"], descendants: true }, { propertyName: "viewport", first: true, predicate: CdkVirtualScrollViewport, descendants: true }], usesInheritance: true, ngImport: i0, template: "<div class=\"buttons\"\n*ngIf=\"showButtons\">\n <ng-content select=\"[buttons]\"></ng-content>\n</div>\n\n<div class=\"header\"\n[class.hide]=\"isHideColumns\"\n[style.padding-right.px]=\"headerRightMargin\">\n <div class=\"header-row\">\n <div *ngIf=\"isShowMultiSelect\"\n class=\"header-cell selection\"\n [ngStyle]=\"{\n 'width': selectionColWidth ? selectionColWidth : null\n }\">\n &nbsp;\n </div>\n \n <div *ngFor=\"let column of columns\"\n class=\"header-cell\"\n [ngStyle]=\"{\n 'width.px': column.realSize ?? 0 > 0 ? column.realSize : null\n }\">\n <span>{{ column.headerName | translate }}</span>\n </div>\n </div>\n</div>\n\n<div #body\nclass=\"body\"\n[style.min-height.px]=\"bodyMinHeight\">\n <cdk-virtual-scroll-viewport\n [itemSize]=\"configs.rowHeight\"\n [style.grid-template-columns]=\"gridColumns\">\n <lib-data-grid-row #row\n *cdkVirtualFor=\"let row of dataGridDataset.loadedRows; index as i; trackBy: trackByFn\"\n [disabled]=\"disabled\"\n [isFirstRow]=\"isFirstVisibleRow(i)\"\n [rowData]=\"row\"\n [style.height.px]=\"configs.rowHeight\"\n (selectionResized)=\"onSelectionResized($event)\">\n </lib-data-grid-row>\n\n <div *ngIf=\"loading\"\n class=\"loading\"\n [style.height.px]=\"configs.rowHeight\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\">\n <circle class=\"opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"4\"></circle>\n <path class=\"opacity-75\" fill=\"currentColor\" d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"></path>\n </svg>\n \n <span>\n {{ dataGridDataset.configs.loadingDisplayText ?? '' | translate }} \n </span>\n </div>\n \n <div *ngIf=\"!loading && !hasBeenLoaded && lazyLoadRows\"\n class=\"message\"\n [style.height.px]=\"configs.rowHeight\">\n <span>\n {{ messageToDisplayWhenLazyLoad | translate }}\n </span>\n </div>\n\n <ng-container *ngIf=\"!loading && hasBeenLoaded && (!hasLoadedRows || hasFailed)\">\n <div *ngIf=\"!hasFailed && !hasLoadedRows && hasMessageToDisplayWhenEmpty && shouldDisplayMessageWhenEmpty\"\n class=\"message\"\n [style.height.px]=\"configs.rowHeight\">\n <span>\n {{ messageToDisplayWhenEmpty | translate }}\n </span>\n </div>\n\n <div *ngIf=\"hasFailed\"\n class=\"message\"\n [style.height.px]=\"configs.rowHeight\">\n <span>\n <i class=\"fas fa-exclamation-triangle mr-2 text-yellow-600\"></i>{{ messageToDisplayWhenFailed | translate }}\n </span>\n </div>\n </ng-container>\n </cdk-virtual-scroll-viewport>\n</div>", styles: [":host-context{display:flex;-webkit-user-select:none;user-select:none;flex-direction:column;overflow:hidden}:host-context:not(.no-borders){border-radius:.5rem;border-width:1px;border-color:#d1d5dbe6;--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}:host-context.no-borders .header .header-row{padding-top:.375rem;padding-bottom:.375rem}:host-context.no-borders .body lib-data-grid-row{padding-top:.25rem;padding-bottom:.25rem}.header-row,lib-data-grid-row,.loading,.message{gap:1rem;padding-left:1rem;padding-right:1rem}.header{border-bottom-width:1px;border-color:#d1d5dbe6;background-color:#e5e7eb66;font-weight:700}.header.hide{display:none}.header .header-row{display:flex;flex-direction:row;padding-top:.75rem;padding-bottom:.75rem}.header .header-cell{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.header .header-cell:not(.selection){flex-grow:1}.body{position:relative;flex-grow:1}.body cdk-virtual-scroll-viewport{position:absolute;inset:0}.body cdk-virtual-scroll-viewport ::ng-deep .cdk-virtual-scroll-content-wrapper{display:grid;grid-template-columns:inherit}.body lib-data-grid-row{margin-top:-1px;cursor:pointer;border-top-width:1px;border-bottom-width:1px;border-color:#cbd5e180;padding-top:.5rem;padding-bottom:.5rem}.body lib-data-grid-row:nth-child(2n){background-color:#f8fafcb3}.body lib-data-grid-row:hover{background-color:#bfdbfe33}.body lib-data-grid-row.focused{z-index:1;border-color:#94a3b899}.body lib-data-grid-row.selected{position:relative;z-index:10;--tw-border-opacity: 1;border-color:rgb(147 197 253 / var(--tw-border-opacity, 1));background-color:#bfdbfe4d}.body lib-data-grid-row.selected:hover{background-color:#bfdbfe66}.body .loading,.body .message{display:flex;align-items:center;gap:.5rem;white-space:nowrap;height:2.5rem;border-bottom-width:1px;border-color:#cbd5e180;grid-column:1 / -1}.body .loading span,.body .message span{overflow:hidden;text-overflow:ellipsis}.body .loading svg{height:1.5rem;min-height:1.5rem;width:1.5rem;min-width:1.5rem}@keyframes spin{to{transform:rotate(360deg)}}.body .loading svg{animation:spin 1s linear infinite;transition-property:opacity;transition-duration:.2s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.buttons{display:flex;border-top-width:1px;border-bottom-width:1px;border-color:#d1d5dbe6;padding:.25rem}\n"], dependencies: [{ kind: "component", type: DataGridRowComponent, selector: "lib-data-grid-row", inputs: ["disabled", "isFirstRow", "rowData"], outputs: ["selectionResized"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: ScrollingModule }, { kind: "directive", type: i1$2.CdkFixedSizeVirtualScroll, selector: "cdk-virtual-scroll-viewport[itemSize]", inputs: ["itemSize", "minBufferPx", "maxBufferPx"] }, { kind: "directive", type: i1$2.CdkVirtualForOf, selector: "[cdkVirtualFor][cdkVirtualForOf]", inputs: ["cdkVirtualForOf", "cdkVirtualForTrackBy", "cdkVirtualForTemplate", "cdkVirtualForTemplateCacheSize"] }, { kind: "component", type: i1$2.CdkVirtualScrollViewport, selector: "cdk-virtual-scroll-viewport", inputs: ["orientation", "appendOnly"], outputs: ["scrolledIndexChange"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
2325
+ }
2326
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: DataGridComponent, decorators: [{
2327
+ type: Component,
2328
+ args: [{ selector: 'lib-data-grid', host: {
2329
+ '[class.no-borders]': 'showButtons',
2330
+ }, imports: [
2331
+ DataGridRowComponent,
2332
+ NgFor,
2333
+ NgIf,
2334
+ NgStyle,
2335
+ ScrollingModule,
2336
+ TranslatePipe,
2337
+ ], template: "<div class=\"buttons\"\n*ngIf=\"showButtons\">\n <ng-content select=\"[buttons]\"></ng-content>\n</div>\n\n<div class=\"header\"\n[class.hide]=\"isHideColumns\"\n[style.padding-right.px]=\"headerRightMargin\">\n <div class=\"header-row\">\n <div *ngIf=\"isShowMultiSelect\"\n class=\"header-cell selection\"\n [ngStyle]=\"{\n 'width': selectionColWidth ? selectionColWidth : null\n }\">\n &nbsp;\n </div>\n \n <div *ngFor=\"let column of columns\"\n class=\"header-cell\"\n [ngStyle]=\"{\n 'width.px': column.realSize ?? 0 > 0 ? column.realSize : null\n }\">\n <span>{{ column.headerName | translate }}</span>\n </div>\n </div>\n</div>\n\n<div #body\nclass=\"body\"\n[style.min-height.px]=\"bodyMinHeight\">\n <cdk-virtual-scroll-viewport\n [itemSize]=\"configs.rowHeight\"\n [style.grid-template-columns]=\"gridColumns\">\n <lib-data-grid-row #row\n *cdkVirtualFor=\"let row of dataGridDataset.loadedRows; index as i; trackBy: trackByFn\"\n [disabled]=\"disabled\"\n [isFirstRow]=\"isFirstVisibleRow(i)\"\n [rowData]=\"row\"\n [style.height.px]=\"configs.rowHeight\"\n (selectionResized)=\"onSelectionResized($event)\">\n </lib-data-grid-row>\n\n <div *ngIf=\"loading\"\n class=\"loading\"\n [style.height.px]=\"configs.rowHeight\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\">\n <circle class=\"opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"4\"></circle>\n <path class=\"opacity-75\" fill=\"currentColor\" d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"></path>\n </svg>\n \n <span>\n {{ dataGridDataset.configs.loadingDisplayText ?? '' | translate }} \n </span>\n </div>\n \n <div *ngIf=\"!loading && !hasBeenLoaded && lazyLoadRows\"\n class=\"message\"\n [style.height.px]=\"configs.rowHeight\">\n <span>\n {{ messageToDisplayWhenLazyLoad | translate }}\n </span>\n </div>\n\n <ng-container *ngIf=\"!loading && hasBeenLoaded && (!hasLoadedRows || hasFailed)\">\n <div *ngIf=\"!hasFailed && !hasLoadedRows && hasMessageToDisplayWhenEmpty && shouldDisplayMessageWhenEmpty\"\n class=\"message\"\n [style.height.px]=\"configs.rowHeight\">\n <span>\n {{ messageToDisplayWhenEmpty | translate }}\n </span>\n </div>\n\n <div *ngIf=\"hasFailed\"\n class=\"message\"\n [style.height.px]=\"configs.rowHeight\">\n <span>\n <i class=\"fas fa-exclamation-triangle mr-2 text-yellow-600\"></i>{{ messageToDisplayWhenFailed | translate }}\n </span>\n </div>\n </ng-container>\n </cdk-virtual-scroll-viewport>\n</div>", styles: [":host-context{display:flex;-webkit-user-select:none;user-select:none;flex-direction:column;overflow:hidden}:host-context:not(.no-borders){border-radius:.5rem;border-width:1px;border-color:#d1d5dbe6;--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}:host-context.no-borders .header .header-row{padding-top:.375rem;padding-bottom:.375rem}:host-context.no-borders .body lib-data-grid-row{padding-top:.25rem;padding-bottom:.25rem}.header-row,lib-data-grid-row,.loading,.message{gap:1rem;padding-left:1rem;padding-right:1rem}.header{border-bottom-width:1px;border-color:#d1d5dbe6;background-color:#e5e7eb66;font-weight:700}.header.hide{display:none}.header .header-row{display:flex;flex-direction:row;padding-top:.75rem;padding-bottom:.75rem}.header .header-cell{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.header .header-cell:not(.selection){flex-grow:1}.body{position:relative;flex-grow:1}.body cdk-virtual-scroll-viewport{position:absolute;inset:0}.body cdk-virtual-scroll-viewport ::ng-deep .cdk-virtual-scroll-content-wrapper{display:grid;grid-template-columns:inherit}.body lib-data-grid-row{margin-top:-1px;cursor:pointer;border-top-width:1px;border-bottom-width:1px;border-color:#cbd5e180;padding-top:.5rem;padding-bottom:.5rem}.body lib-data-grid-row:nth-child(2n){background-color:#f8fafcb3}.body lib-data-grid-row:hover{background-color:#bfdbfe33}.body lib-data-grid-row.focused{z-index:1;border-color:#94a3b899}.body lib-data-grid-row.selected{position:relative;z-index:10;--tw-border-opacity: 1;border-color:rgb(147 197 253 / var(--tw-border-opacity, 1));background-color:#bfdbfe4d}.body lib-data-grid-row.selected:hover{background-color:#bfdbfe66}.body .loading,.body .message{display:flex;align-items:center;gap:.5rem;white-space:nowrap;height:2.5rem;border-bottom-width:1px;border-color:#cbd5e180;grid-column:1 / -1}.body .loading span,.body .message span{overflow:hidden;text-overflow:ellipsis}.body .loading svg{height:1.5rem;min-height:1.5rem;width:1.5rem;min-width:1.5rem}@keyframes spin{to{transform:rotate(360deg)}}.body .loading svg{animation:spin 1s linear infinite;transition-property:opacity;transition-duration:.2s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.buttons{display:flex;border-top-width:1px;border-bottom-width:1px;border-color:#d1d5dbe6;padding:.25rem}\n"] }]
2338
+ }], ctorParameters: () => [], propDecorators: { bodyElement: [{
2339
+ type: ViewChild,
2340
+ args: ['body']
2341
+ }], viewport: [{
2342
+ type: ViewChild,
2343
+ args: [CdkVirtualScrollViewport]
2344
+ }], disabled: [{
2345
+ type: Input
2346
+ }], lazyLoadRows: [{
2347
+ type: Input
2348
+ }], showButtons: [{
2349
+ type: Input
2350
+ }] } });
2351
+
2352
+ class FormGroupComponent {
2353
+ //#region ViewChilds, Inputs, Outputs
2354
+ shouldExpand = false;
2355
+ label;
2356
+ //#endregion
2357
+ //#region Variables
2358
+ //#endregion
2359
+ //#region Properties
2360
+ get hasTitle() {
2361
+ return !!this.label;
2362
+ }
2363
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: FormGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2364
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.6", type: FormGroupComponent, isStandalone: true, selector: "lib-form-group", inputs: { shouldExpand: "shouldExpand", label: "label" }, host: { properties: { "class.expand": "shouldExpand" } }, ngImport: i0, template: "<h3 *ngIf=\"hasTitle\"\nclass=\"title\" >\n {{ label | translate }}\n</h3>\n\n<div class=\"content\"\n[class.has-title]=\"hasTitle\">\n <ng-content></ng-content>\n</div>", styles: [":host-context.expand{display:flex;max-height:100%;flex-direction:column;overflow-y:hidden}:host-context.expand .content{max-height:100%;flex-grow:1;overflow-y:hidden}.title{font-size:1.125rem;line-height:1.75rem;font-weight:600}.content{display:grid;grid-auto-rows:auto;grid-template-columns:min-content minmax(0,1fr);gap:.25rem}.content.has-title{border-top-width:1px;border-color:#d1d5dbe6;padding:.75rem .5rem .5rem}\n"], dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
2365
+ }
2366
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: FormGroupComponent, decorators: [{
2367
+ type: Component,
2368
+ args: [{ selector: 'lib-form-group', imports: [
2369
+ NgIf,
2370
+ TranslatePipe,
2371
+ ], host: {
2372
+ '[class.expand]': 'shouldExpand'
2373
+ }, template: "<h3 *ngIf=\"hasTitle\"\nclass=\"title\" >\n {{ label | translate }}\n</h3>\n\n<div class=\"content\"\n[class.has-title]=\"hasTitle\">\n <ng-content></ng-content>\n</div>", styles: [":host-context.expand{display:flex;max-height:100%;flex-direction:column;overflow-y:hidden}:host-context.expand .content{max-height:100%;flex-grow:1;overflow-y:hidden}.title{font-size:1.125rem;line-height:1.75rem;font-weight:600}.content{display:grid;grid-auto-rows:auto;grid-template-columns:min-content minmax(0,1fr);gap:.25rem}.content.has-title{border-top-width:1px;border-color:#d1d5dbe6;padding:.75rem .5rem .5rem}\n"] }]
2374
+ }], propDecorators: { shouldExpand: [{
2375
+ type: Input
2376
+ }], label: [{
2377
+ type: Input
2378
+ }] } });
2379
+
2380
+ class GroupAccordionComponent {
2381
+ //#region ViewChilds, Inputs, Outputs
2382
+ containerElementRef;
2383
+ label;
2384
+ //#endregion
2385
+ //#region Variables
2386
+ _maxHeight = -1;
2387
+ collapsed = false;
2388
+ //#endregion
2389
+ //#region Properties
2390
+ get maxHeight() {
2391
+ return this._maxHeight;
2392
+ }
2393
+ //#endregion
2394
+ //#region Constructor and Angular life cycle methods
2395
+ //#endregion
2396
+ //#region Event handlers
2397
+ onToggle() {
2398
+ // Before collapsing, we set the max height style for the closing effect.
2399
+ if (!this.collapsed) {
2400
+ this.setMaxHeight();
2401
+ }
2402
+ setTimeout(() => {
2403
+ this.collapsed = !this.collapsed;
2404
+ // After expanding, we wait the transition to end and remove the max height.
2405
+ if (!this.collapsed) {
2406
+ this.clearMaxHeight();
2407
+ }
2408
+ });
2409
+ }
2410
+ //#endregion
2411
+ //#region Public methods
2412
+ //#endregion
2413
+ //#region Private methods
2414
+ clearMaxHeight() {
2415
+ setTimeout(() => {
2416
+ this._maxHeight = -1;
2417
+ }, 150);
2418
+ }
2419
+ setMaxHeight() {
2420
+ this._maxHeight = this.containerElementRef?.nativeElement?.offsetHeight ?? 0;
2421
+ }
2422
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: GroupAccordionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2423
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.6", type: GroupAccordionComponent, isStandalone: true, selector: "lib-group-accordion", inputs: { label: "label" }, host: { properties: { "class.collapsed": "collapsed" } }, viewQueries: [{ propertyName: "containerElementRef", first: true, predicate: ["container"], descendants: true }], ngImport: i0, template: "<div class=\"title\">\n <h6 (click)=\"onToggle()\"><i class=\"fas fa-chevron-down fa-sm\"></i>{{ this.label }}</h6>\n</div>\n\n<div #container class=\"content animate\"\n[ngStyle]=\"maxHeight >= 0 && {\n 'height.px': maxHeight,\n 'max-height.px': maxHeight\n} || null\">\n <div #maxHeighContainer>\n <ng-content></ng-content>\n </div>\n</div>", styles: [":host-context{margin-bottom:1rem;display:flex;flex-direction:column}:host-context.collapsed i{--tw-rotate: -90deg;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))}:host-context.collapsed .content{max-height:0px!important;overflow:hidden!important}:host-context:not(.collapsed) .content{animation:show .15s}.title{-webkit-user-select:none;user-select:none}.title i{margin-right:.5rem;padding-top:.25rem;--tw-text-opacity: 1;color:rgb(100 116 139 / var(--tw-text-opacity, 1));transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.title h6{margin-bottom:0;display:inline;height:3rem;cursor:pointer;text-overflow:ellipsis;white-space:nowrap;padding-left:.25rem;padding-right:.5rem;font-size:1.125rem;text-transform:uppercase;line-height:3rem}.content{box-sizing:content-box;flex-grow:1;overflow-y:hidden;padding-left:.25rem;padding-right:.25rem}.content.animate{transition-property:max-height;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.content div{padding-bottom:.25rem}@media (min-width: 1024px){.content div{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.75rem}}@media (min-width: 1280px){.content div{grid-template-columns:repeat(3,minmax(0,1fr))}}.content div{height:100%}@keyframes show{0%{overflow:hidden}99%{overflow:hidden}to{overflow:auto}}\n"], dependencies: [{ kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }] });
2424
+ }
2425
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: GroupAccordionComponent, decorators: [{
2426
+ type: Component,
2427
+ args: [{ selector: 'lib-group-accordion', host: { '[class.collapsed]': 'collapsed' }, imports: [
2428
+ NgStyle,
2429
+ ], template: "<div class=\"title\">\n <h6 (click)=\"onToggle()\"><i class=\"fas fa-chevron-down fa-sm\"></i>{{ this.label }}</h6>\n</div>\n\n<div #container class=\"content animate\"\n[ngStyle]=\"maxHeight >= 0 && {\n 'height.px': maxHeight,\n 'max-height.px': maxHeight\n} || null\">\n <div #maxHeighContainer>\n <ng-content></ng-content>\n </div>\n</div>", styles: [":host-context{margin-bottom:1rem;display:flex;flex-direction:column}:host-context.collapsed i{--tw-rotate: -90deg;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))}:host-context.collapsed .content{max-height:0px!important;overflow:hidden!important}:host-context:not(.collapsed) .content{animation:show .15s}.title{-webkit-user-select:none;user-select:none}.title i{margin-right:.5rem;padding-top:.25rem;--tw-text-opacity: 1;color:rgb(100 116 139 / var(--tw-text-opacity, 1));transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.title h6{margin-bottom:0;display:inline;height:3rem;cursor:pointer;text-overflow:ellipsis;white-space:nowrap;padding-left:.25rem;padding-right:.5rem;font-size:1.125rem;text-transform:uppercase;line-height:3rem}.content{box-sizing:content-box;flex-grow:1;overflow-y:hidden;padding-left:.25rem;padding-right:.25rem}.content.animate{transition-property:max-height;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.content div{padding-bottom:.25rem}@media (min-width: 1024px){.content div{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.75rem}}@media (min-width: 1280px){.content div{grid-template-columns:repeat(3,minmax(0,1fr))}}.content div{height:100%}@keyframes show{0%{overflow:hidden}99%{overflow:hidden}to{overflow:auto}}\n"] }]
2430
+ }], propDecorators: { containerElementRef: [{
2431
+ type: ViewChild,
2432
+ args: ['container']
2433
+ }], label: [{
2434
+ type: Input,
2435
+ args: [{ required: true }]
2436
+ }] } });
2437
+
2438
+ class ScrollSpyDirective {
2439
+ //#region ViewChilds, Inputs, Outputs
2440
+ spiedTags = [];
2441
+ activeSectionChange = new EventEmitter();
2442
+ //#endregion
2443
+ //#region Variables
2444
+ _el = inject(ElementRef);
2445
+ activeSection = null;
2446
+ sections = new Array();
2447
+ //#endregion
2448
+ //#region Properties
2449
+ //#endregion
2450
+ //#region Constructor and Angular life cycle methods
2451
+ ngAfterViewInit() {
2452
+ this.updateSections(Array.from(this._el.nativeElement.children));
2453
+ }
2454
+ //#endregion
2455
+ //#region Event handlers
2456
+ onScroll(event) {
2457
+ const scrollTop = event.target.scrollTop;
2458
+ const scrollHeight = event.target.scrollHeight;
2459
+ const maxScroll = Math.round(event.target.scrollHeight - event.target.getBoundingClientRect().height);
2460
+ const currentScroll = Math.round(scrollTop * scrollHeight / maxScroll);
2461
+ if (currentScroll >= scrollHeight) {
2462
+ this.select(this.sections.length - 1);
2463
+ return;
2464
+ }
2465
+ const elements = this.sections.filter(el => (el.offsetTop) <= currentScroll);
2466
+ this.select(elements.length > 0 ? elements.length - 1 : 0);
2467
+ }
2468
+ //#endregion
2469
+ //#region Public methods
2470
+ scrollTo(section) {
2471
+ this._el.nativeElement.scrollTop = this.sections[section].offsetTop;
2472
+ }
2473
+ select(section) {
2474
+ if (section !== this.activeSection) {
2475
+ this.activeSection = section;
2476
+ this.activeSectionChange.emit(section);
2477
+ }
2478
+ }
2479
+ //#endregion
2480
+ //#region Private methods
2481
+ updateSections(children) {
2482
+ children.forEach(el => {
2483
+ if (this.spiedTags.some(spiedTag => spiedTag.toUpperCase() === el.tagName.toUpperCase())) {
2484
+ this.sections.push(el);
2485
+ }
2486
+ else {
2487
+ this.updateSections(Array.from(el.children));
2488
+ }
2489
+ });
2490
+ }
2491
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: ScrollSpyDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
2492
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.1.6", type: ScrollSpyDirective, isStandalone: true, selector: "[libScrollSpy]", inputs: { spiedTags: "spiedTags" }, outputs: { activeSectionChange: "activeSectionChange" }, host: { listeners: { "scroll": "onScroll($event)" } }, ngImport: i0 });
2493
+ }
2494
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: ScrollSpyDirective, decorators: [{
2495
+ type: Directive,
2496
+ args: [{
2497
+ selector: '[libScrollSpy]'
2498
+ }]
2499
+ }], propDecorators: { spiedTags: [{
2500
+ type: Input
2501
+ }], activeSectionChange: [{
2502
+ type: Output
2503
+ }], onScroll: [{
2504
+ type: HostListener,
2505
+ args: ['scroll', ['$event']]
2506
+ }] } });
2507
+
2508
+ class GroupContainerComponent {
2509
+ scrollSpy;
2510
+ sections;
2511
+ get titles() {
2512
+ return this.sections?.map(x => x.label) ?? [];
2513
+ }
2514
+ activeSection = 0;
2515
+ title;
2516
+ icon;
2517
+ activeSectionChanged(index) {
2518
+ this.activeSection = index;
2519
+ }
2520
+ scrollTo(section) {
2521
+ this.scrollSpy.scrollTo(section);
2522
+ }
2523
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: GroupContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2524
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.6", type: GroupContainerComponent, isStandalone: true, selector: "lib-group-container", inputs: { title: "title", icon: "icon" }, queries: [{ propertyName: "sections", predicate: GroupAccordionComponent, descendants: true }], viewQueries: [{ propertyName: "scrollSpy", first: true, predicate: ScrollSpyDirective, descendants: true }], ngImport: i0, template: "<div class=\"title\">\n <i *ngIf=\"!!title && !!icon\" class=\"fas fa-fw\" [ngClass]=\"icon\"></i>\n\n <h4 *ngIf=\"!!title\">{{ title | translate }}</h4>\n\n <svg *ngIf=\"!title\" [ngClass]=\"''\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\">\n <circle class=\"opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"4\"></circle>\n <path class=\"opacity-75\" fill=\"currentColor\" d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"></path>\n </svg>\n</div>\n\n<div class=\"content-container\">\n <ng-content></ng-content>\n</div>", styles: [":host-context{position:relative;display:flex;flex-grow:1;flex-direction:column;overflow:hidden;border-radius:.5rem;border-width:1px;border-color:#d1d5dbe6;background-color:#fffc}.title{display:flex;height:2rem;align-items:center;text-overflow:ellipsis;border-bottom-width:1px;border-color:#d1d5dbe6;padding:.25rem .5rem}.title i{padding-right:.5rem;--tw-text-opacity: 1;color:rgb(100 116 139 / var(--tw-text-opacity, 1))}.title h4{font-weight:600}.title svg{height:1.25rem;width:1.25rem}@keyframes spin{to{transform:rotate(360deg)}}.title svg{animation:spin 1s linear infinite}.content-container{position:relative;display:flex;flex-grow:1;overflow:hidden}.content-container ::ng-deep>*{flex-grow:1;overflow:hidden}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
2525
+ }
2526
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: GroupContainerComponent, decorators: [{
2527
+ type: Component,
2528
+ args: [{ selector: 'lib-group-container', imports: [
2529
+ NgClass,
2530
+ NgIf,
2531
+ TranslatePipe,
2532
+ ], template: "<div class=\"title\">\n <i *ngIf=\"!!title && !!icon\" class=\"fas fa-fw\" [ngClass]=\"icon\"></i>\n\n <h4 *ngIf=\"!!title\">{{ title | translate }}</h4>\n\n <svg *ngIf=\"!title\" [ngClass]=\"''\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\">\n <circle class=\"opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"4\"></circle>\n <path class=\"opacity-75\" fill=\"currentColor\" d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"></path>\n </svg>\n</div>\n\n<div class=\"content-container\">\n <ng-content></ng-content>\n</div>", styles: [":host-context{position:relative;display:flex;flex-grow:1;flex-direction:column;overflow:hidden;border-radius:.5rem;border-width:1px;border-color:#d1d5dbe6;background-color:#fffc}.title{display:flex;height:2rem;align-items:center;text-overflow:ellipsis;border-bottom-width:1px;border-color:#d1d5dbe6;padding:.25rem .5rem}.title i{padding-right:.5rem;--tw-text-opacity: 1;color:rgb(100 116 139 / var(--tw-text-opacity, 1))}.title h4{font-weight:600}.title svg{height:1.25rem;width:1.25rem}@keyframes spin{to{transform:rotate(360deg)}}.title svg{animation:spin 1s linear infinite}.content-container{position:relative;display:flex;flex-grow:1;overflow:hidden}.content-container ::ng-deep>*{flex-grow:1;overflow:hidden}\n"] }]
2533
+ }], propDecorators: { scrollSpy: [{
2534
+ type: ViewChild,
2535
+ args: [ScrollSpyDirective]
2536
+ }], sections: [{
2537
+ type: ContentChildren,
2538
+ args: [GroupAccordionComponent, { descendants: true }]
2539
+ }], title: [{
2540
+ type: Input
2541
+ }], icon: [{
2542
+ type: Input
2543
+ }] } });
2544
+
2545
+ class GroupScrollSpyComponent {
2546
+ scrollSpy;
2547
+ sections;
2548
+ get titles() {
2549
+ return this.sections?.map(x => x.label) ?? [];
2550
+ }
2551
+ activeSection = 0;
2552
+ activeSectionChanged(index) {
2553
+ this.activeSection = index;
2554
+ }
2555
+ scrollTo(section) {
2556
+ this.scrollSpy.scrollTo(section);
2557
+ }
2558
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: GroupScrollSpyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2559
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.6", type: GroupScrollSpyComponent, isStandalone: true, selector: "lib-group-scroll-spy", queries: [{ propertyName: "sections", predicate: GroupAccordionComponent, descendants: true }], viewQueries: [{ propertyName: "scrollSpy", first: true, predicate: ScrollSpyDirective, descendants: true }], ngImport: i0, template: "<div class=\"scroll-spy\">\n <a *ngFor=\"let title of titles; let i = index\"\n class=\"item\" [class.active]=\"i === activeSection\"\n (click)=\"scrollTo(i)\"\n >{{ title }}</a>\n</div>\n\n<div class=\"content\" libScrollSpy\n[class.no-scroll-spy]=\"titles.length === 0\"\n[spiedTags]=\"['lib-group-accordion']\"\n(activeSectionChange)=\"activeSectionChanged($event)\"\n>\n <ng-content></ng-content>\n</div>", styles: [":host-context{position:relative;display:flex;height:100%;max-height:100%;min-height:100%;flex-grow:1;overflow:hidden}.content{display:flex;flex-grow:1;flex-direction:column;overflow:auto;scroll-behavior:smooth;padding:.75rem .75rem .25rem}@media (min-width: 768px){.content{padding-right:10.5rem}.content.no-scroll-spy{padding-right:.75rem}}.scroll-spy{position:absolute;top:.75rem;right:.75rem;display:none;width:9rem;-webkit-user-select:none;user-select:none;flex-direction:column}@media (min-width: 768px){.scroll-spy{display:flex}}.scroll-spy a{cursor:pointer;border-left-width:2px;--tw-border-opacity: 1;border-left-color:rgb(209 213 219 / var(--tw-border-opacity, 1));padding:.5rem 1rem;font-size:.875rem;line-height:1.25rem;--tw-text-opacity: 1;color:rgb(75 85 99 / var(--tw-text-opacity, 1))}.scroll-spy a.active{--tw-border-opacity: 1;border-left-color:rgb(0 107 182 / var(--tw-border-opacity, 1));--tw-text-opacity: 1;color:rgb(0 107 182 / var(--tw-text-opacity, 1))}.scroll-spy a:active{background-color:#006bb61a}\n"], dependencies: [{ kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: ScrollSpyDirective, selector: "[libScrollSpy]", inputs: ["spiedTags"], outputs: ["activeSectionChange"] }] });
2560
+ }
2561
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: GroupScrollSpyComponent, decorators: [{
2562
+ type: Component,
2563
+ args: [{ selector: 'lib-group-scroll-spy', imports: [
2564
+ NgFor,
2565
+ ScrollSpyDirective,
2566
+ ], template: "<div class=\"scroll-spy\">\n <a *ngFor=\"let title of titles; let i = index\"\n class=\"item\" [class.active]=\"i === activeSection\"\n (click)=\"scrollTo(i)\"\n >{{ title }}</a>\n</div>\n\n<div class=\"content\" libScrollSpy\n[class.no-scroll-spy]=\"titles.length === 0\"\n[spiedTags]=\"['lib-group-accordion']\"\n(activeSectionChange)=\"activeSectionChanged($event)\"\n>\n <ng-content></ng-content>\n</div>", styles: [":host-context{position:relative;display:flex;height:100%;max-height:100%;min-height:100%;flex-grow:1;overflow:hidden}.content{display:flex;flex-grow:1;flex-direction:column;overflow:auto;scroll-behavior:smooth;padding:.75rem .75rem .25rem}@media (min-width: 768px){.content{padding-right:10.5rem}.content.no-scroll-spy{padding-right:.75rem}}.scroll-spy{position:absolute;top:.75rem;right:.75rem;display:none;width:9rem;-webkit-user-select:none;user-select:none;flex-direction:column}@media (min-width: 768px){.scroll-spy{display:flex}}.scroll-spy a{cursor:pointer;border-left-width:2px;--tw-border-opacity: 1;border-left-color:rgb(209 213 219 / var(--tw-border-opacity, 1));padding:.5rem 1rem;font-size:.875rem;line-height:1.25rem;--tw-text-opacity: 1;color:rgb(75 85 99 / var(--tw-text-opacity, 1))}.scroll-spy a.active{--tw-border-opacity: 1;border-left-color:rgb(0 107 182 / var(--tw-border-opacity, 1));--tw-text-opacity: 1;color:rgb(0 107 182 / var(--tw-text-opacity, 1))}.scroll-spy a:active{background-color:#006bb61a}\n"] }]
2567
+ }], propDecorators: { scrollSpy: [{
2568
+ type: ViewChild,
2569
+ args: [ScrollSpyDirective]
2570
+ }], sections: [{
2571
+ type: ContentChildren,
2572
+ args: [GroupAccordionComponent, { descendants: true }]
2573
+ }] } });
2574
+
2575
+ class ModalComponent extends BaseComponent {
2576
+ //#region ViewChilds, Inputs, Outputs
2577
+ closeButtonText = 'Close';
2578
+ dialog = true;
2579
+ modalProcessing = false;
2580
+ position = 'center';
2581
+ size = 'auto';
2582
+ title;
2583
+ closed = new EventEmitter();
2584
+ //#endregion
2585
+ //#region Variables
2586
+ _show = false;
2587
+ clickedOutside = false;
2588
+ elementRef;
2589
+ //#endregion
2590
+ //#region Properties
2591
+ get isShown() {
2592
+ return this._show;
2593
+ }
2594
+ //#endregion
2595
+ //#region Constructor and Angular life cycle methods
2596
+ constructor() {
2597
+ super();
2598
+ this.elementRef = inject(ElementRef);
2599
+ }
2600
+ //#endregion
2601
+ //#region Event handlers
2602
+ onCloseClick() {
2603
+ if (!this.modalProcessing && this.isShown) {
2604
+ this.closeModal();
2605
+ }
2606
+ }
2607
+ //#endregion
2608
+ //#region Public methods
2609
+ closeModal() {
2610
+ if (this.isShown) {
2611
+ this.toggleModal();
2612
+ }
2613
+ }
2614
+ toggleModal() {
2615
+ const wasShown = this._show;
2616
+ const body = document.querySelector('body');
2617
+ body?.classList.toggle('modal-active');
2618
+ this._show = body?.classList.contains('modal-active') ?? false;
2619
+ if (this.isShown) {
2620
+ this.clickedOutside = false;
2621
+ const autofocus = this.elementRef.nativeElement.querySelector('[autofocus]');
2622
+ if (autofocus && !!autofocus.focus) {
2623
+ autofocus.focus();
2624
+ }
2625
+ }
2626
+ else if (wasShown) {
2627
+ this.closed.emit();
2628
+ }
2629
+ }
2630
+ //#endregion
2631
+ //#region Private methods
2632
+ //#endregion
2633
+ //#region Host listeners
2634
+ bodyMouseDown(event) {
2635
+ if (this.dialog && this.isShown) {
2636
+ const target = event.target;
2637
+ this.clickedOutside = (!this.modalProcessing && event.button === 0 && target.closest('lib-modal') && !target.closest('.modal-content')) ?? false;
2638
+ }
2639
+ }
2640
+ bodyMouseUp(event) {
2641
+ if (this.dialog && this.isShown) {
2642
+ if (this.clickedOutside && !this.modalProcessing) {
2643
+ this.toggleModal();
2644
+ }
2645
+ }
2646
+ }
2647
+ documentKeyDown(event) {
2648
+ event = event || window.event;
2649
+ let isEscape = false;
2650
+ if ('key' in event) {
2651
+ isEscape = (event.key === 'Escape' || event.key === 'Esc');
2652
+ }
2653
+ else {
2654
+ isEscape = (event.keyCode === 27);
2655
+ }
2656
+ if (!this.modalProcessing && this.isShown && isEscape && document.body.classList.contains('modal-active')) {
2657
+ this.toggleModal();
2658
+ }
2659
+ }
2660
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: ModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2661
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.6", type: ModalComponent, isStandalone: true, selector: "lib-modal", inputs: { closeButtonText: "closeButtonText", dialog: "dialog", modalProcessing: "modalProcessing", position: "position", size: "size", title: "title" }, outputs: { closed: "closed" }, host: { attributes: { "title": "" }, listeners: { "body:mousedown": "bodyMouseDown($event)", "body:mouseup": "bodyMouseUp($event)", "document:keydown.escape": "documentKeyDown($event)" }, properties: { "class.show": "isShown" } }, usesInheritance: true, ngImport: i0, template: "<div class=\"modal-overlay\"></div>\n\n<div class=\"modal-container size-{{ size }} {{ position }}\">\n <form class=\"modal-content\">\n <div class=\"modal-header\" *ngIf=\"title\">\n <p class=\"text-2xl font-bold\">{{ title }}</p>\n\n <button type=\"button\" class=\"modal-close\" (click)=\"toggleModal()\">\n <svg class=\"fill-current text-black\" xmlns=\"http://www.w3.org/2000/svg\" width=\"18\" height=\"18\" viewBox=\"0 0 18 18\">\n <path d=\"M14.53 4.53l-1.06-1.06L9 7.94 4.53 3.47 3.47 4.53 7.94 9l-4.47 4.47 1.06 1.06L9 10.06l4.47 4.47 1.06-1.06L10.06 9z\"></path>\n </svg>\n </button>\n </div>\n\n <div class=\"modal-body\">\n <ng-content select=\"[body]\"></ng-content>\n </div>\n\n <div class=\"modal-footer\">\n <button type=\"button\" class=\"btn gray-300\" [disabled]=\"modalProcessing\" (click)=\"onCloseClick()\">{{ closeButtonText }}</button>\n\n <ng-content select=\"[footer]\"></ng-content>\n </div>\n </form>\n</div>", styles: ["body.modal-active{overflow-x:hidden;overflow-y:visible!important}:host{position:fixed;top:0;left:0;z-index:50;height:100%;width:100%}:host:not(.show){pointer-events:none;opacity:0;transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s;transition-delay:.15s}:host:not(.show) .modal-overlay{opacity:0;transition-delay:.15s}:host:not(.show) .modal-container.top,:host:not(.show) .modal-container.center{--tw-translate-y: -3.5rem;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))}:host:not(.show) .modal-container.bottom{--tw-translate-y: 3.5rem;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))}:host:not(.show) .modal-container.left{--tw-translate-x: -3.5rem;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))}:host:not(.show) .modal-container.right{--tw-translate-x: 3.5rem;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))}:host:not(.show) .modal-container .modal-content{animation:hidden 0s ease-in-out forwards}.modal-overlay{position:fixed;inset:0;height:100vh;width:100vw;--tw-bg-opacity: 1;background-color:rgb(0 0 0 / var(--tw-bg-opacity, 1));opacity:.5;transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.modal-container{margin:auto;display:grid;height:100%;max-height:100vh;min-height:100%;grid-template-columns:repeat(1,minmax(0,1fr));padding:1.75rem;transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.3s}.modal-container.top{align-items:flex-start}.modal-container.bottom{align-items:flex-end}.modal-container.left{margin-right:0;align-items:center}.modal-container.right{margin-left:0;align-items:center}.modal-container.center{align-items:center}.modal-container.size-sm{max-width:24rem}.modal-container.size-md{max-width:28rem}.modal-container.size-lg{max-width:32rem}.modal-container.size-xl{max-width:36rem}.modal-container.size-2xl{max-width:42rem}.modal-container.size-3xl{max-width:48rem}.modal-container.size-4xl{max-width:56rem}.modal-container.size-5xl{max-width:64rem}.modal-container.size-6xl{max-width:72rem}.modal-container.size-7xl{max-width:80rem}.modal-container.size-full{max-width:100%}.modal-container.size-auto{width:100%}@media (min-width: 640px){.modal-container.size-auto{max-width:640px}}@media (min-width: 768px){.modal-container.size-auto{max-width:768px}}@media (min-width: 1024px){.modal-container.size-auto{max-width:1024px}}@media (min-width: 1280px){.modal-container.size-auto{max-width:1280px}}@media (min-width: 1536px){.modal-container.size-auto{max-width:1536px}}.modal-container .modal-content{position:relative;width:100%;border-radius:.375rem;border-width:1px;border-color:#0003;--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1));background-clip:padding-box;outline-width:0px;display:grid;max-height:100%;grid-template-rows:min-content 1fr min-content;overflow:hidden;--tw-shadow: 0 0 4px 0 rgba(0,0,0,.2), 0 3px 20px 0 rgba(0,0,0,.19);--tw-shadow-colored: 0 0 4px 0 var(--tw-shadow-color), 0 3px 20px 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);animation:shown .3s ease-in-out forwards}.modal-container .modal-content .modal-header{border-bottom-width:1px;--tw-border-opacity: 1;border-color:rgb(209 213 219 / var(--tw-border-opacity, 1));padding:1rem}.modal-container .modal-content .modal-header .modal-close{position:absolute;top:0;right:0;margin:.5rem;cursor:pointer;padding:.5rem;opacity:.6}.modal-container .modal-content .modal-body{overflow:hidden;padding:1rem}.modal-container .modal-content .modal-footer{display:grid;grid-auto-columns:min-content;grid-auto-flow:column;justify-content:flex-end;gap:.25rem;white-space:nowrap;border-top-width:1px;--tw-border-opacity: 1;border-color:rgb(209 213 219 / var(--tw-border-opacity, 1));padding:.75rem}.modal-container .modal-content .modal-footer>*,.modal-container .modal-content .modal-footer ::ng-deep div[footer]>*{display:grid;grid-auto-columns:min-content;grid-auto-flow:column;gap:.25rem}@keyframes hidden{0%{display:flex}to{display:none}}@keyframes shown{0%{opacity:0}to{opacity:1}}\n"], dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
2662
+ }
2663
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: ModalComponent, decorators: [{
2664
+ type: Component,
2665
+ args: [{ selector: 'lib-modal', host: {
2666
+ 'title': '', // This is to ensure that the title input does not conflict with the native HTML title attribute.
2667
+ '[class.show]': 'isShown',
2668
+ }, imports: [
2669
+ NgIf,
2670
+ ], template: "<div class=\"modal-overlay\"></div>\n\n<div class=\"modal-container size-{{ size }} {{ position }}\">\n <form class=\"modal-content\">\n <div class=\"modal-header\" *ngIf=\"title\">\n <p class=\"text-2xl font-bold\">{{ title }}</p>\n\n <button type=\"button\" class=\"modal-close\" (click)=\"toggleModal()\">\n <svg class=\"fill-current text-black\" xmlns=\"http://www.w3.org/2000/svg\" width=\"18\" height=\"18\" viewBox=\"0 0 18 18\">\n <path d=\"M14.53 4.53l-1.06-1.06L9 7.94 4.53 3.47 3.47 4.53 7.94 9l-4.47 4.47 1.06 1.06L9 10.06l4.47 4.47 1.06-1.06L10.06 9z\"></path>\n </svg>\n </button>\n </div>\n\n <div class=\"modal-body\">\n <ng-content select=\"[body]\"></ng-content>\n </div>\n\n <div class=\"modal-footer\">\n <button type=\"button\" class=\"btn gray-300\" [disabled]=\"modalProcessing\" (click)=\"onCloseClick()\">{{ closeButtonText }}</button>\n\n <ng-content select=\"[footer]\"></ng-content>\n </div>\n </form>\n</div>", styles: ["body.modal-active{overflow-x:hidden;overflow-y:visible!important}:host{position:fixed;top:0;left:0;z-index:50;height:100%;width:100%}:host:not(.show){pointer-events:none;opacity:0;transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s;transition-delay:.15s}:host:not(.show) .modal-overlay{opacity:0;transition-delay:.15s}:host:not(.show) .modal-container.top,:host:not(.show) .modal-container.center{--tw-translate-y: -3.5rem;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))}:host:not(.show) .modal-container.bottom{--tw-translate-y: 3.5rem;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))}:host:not(.show) .modal-container.left{--tw-translate-x: -3.5rem;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))}:host:not(.show) .modal-container.right{--tw-translate-x: 3.5rem;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))}:host:not(.show) .modal-container .modal-content{animation:hidden 0s ease-in-out forwards}.modal-overlay{position:fixed;inset:0;height:100vh;width:100vw;--tw-bg-opacity: 1;background-color:rgb(0 0 0 / var(--tw-bg-opacity, 1));opacity:.5;transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.modal-container{margin:auto;display:grid;height:100%;max-height:100vh;min-height:100%;grid-template-columns:repeat(1,minmax(0,1fr));padding:1.75rem;transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.3s}.modal-container.top{align-items:flex-start}.modal-container.bottom{align-items:flex-end}.modal-container.left{margin-right:0;align-items:center}.modal-container.right{margin-left:0;align-items:center}.modal-container.center{align-items:center}.modal-container.size-sm{max-width:24rem}.modal-container.size-md{max-width:28rem}.modal-container.size-lg{max-width:32rem}.modal-container.size-xl{max-width:36rem}.modal-container.size-2xl{max-width:42rem}.modal-container.size-3xl{max-width:48rem}.modal-container.size-4xl{max-width:56rem}.modal-container.size-5xl{max-width:64rem}.modal-container.size-6xl{max-width:72rem}.modal-container.size-7xl{max-width:80rem}.modal-container.size-full{max-width:100%}.modal-container.size-auto{width:100%}@media (min-width: 640px){.modal-container.size-auto{max-width:640px}}@media (min-width: 768px){.modal-container.size-auto{max-width:768px}}@media (min-width: 1024px){.modal-container.size-auto{max-width:1024px}}@media (min-width: 1280px){.modal-container.size-auto{max-width:1280px}}@media (min-width: 1536px){.modal-container.size-auto{max-width:1536px}}.modal-container .modal-content{position:relative;width:100%;border-radius:.375rem;border-width:1px;border-color:#0003;--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1));background-clip:padding-box;outline-width:0px;display:grid;max-height:100%;grid-template-rows:min-content 1fr min-content;overflow:hidden;--tw-shadow: 0 0 4px 0 rgba(0,0,0,.2), 0 3px 20px 0 rgba(0,0,0,.19);--tw-shadow-colored: 0 0 4px 0 var(--tw-shadow-color), 0 3px 20px 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);animation:shown .3s ease-in-out forwards}.modal-container .modal-content .modal-header{border-bottom-width:1px;--tw-border-opacity: 1;border-color:rgb(209 213 219 / var(--tw-border-opacity, 1));padding:1rem}.modal-container .modal-content .modal-header .modal-close{position:absolute;top:0;right:0;margin:.5rem;cursor:pointer;padding:.5rem;opacity:.6}.modal-container .modal-content .modal-body{overflow:hidden;padding:1rem}.modal-container .modal-content .modal-footer{display:grid;grid-auto-columns:min-content;grid-auto-flow:column;justify-content:flex-end;gap:.25rem;white-space:nowrap;border-top-width:1px;--tw-border-opacity: 1;border-color:rgb(209 213 219 / var(--tw-border-opacity, 1));padding:.75rem}.modal-container .modal-content .modal-footer>*,.modal-container .modal-content .modal-footer ::ng-deep div[footer]>*{display:grid;grid-auto-columns:min-content;grid-auto-flow:column;gap:.25rem}@keyframes hidden{0%{display:flex}to{display:none}}@keyframes shown{0%{opacity:0}to{opacity:1}}\n"] }]
2671
+ }], ctorParameters: () => [], propDecorators: { closeButtonText: [{
2672
+ type: Input
2673
+ }], dialog: [{
2674
+ type: Input
2675
+ }], modalProcessing: [{
2676
+ type: Input
2677
+ }], position: [{
2678
+ type: Input
2679
+ }], size: [{
2680
+ type: Input
2681
+ }], title: [{
2682
+ type: Input
2683
+ }], closed: [{
2684
+ type: Output
2685
+ }], bodyMouseDown: [{
2686
+ type: HostListener,
2687
+ args: ['body:mousedown', ['$event']]
2688
+ }], bodyMouseUp: [{
2689
+ type: HostListener,
2690
+ args: ['body:mouseup', ['$event']]
2691
+ }], documentKeyDown: [{
2692
+ type: HostListener,
2693
+ args: ['document:keydown.escape', ['$event']]
2694
+ }] } });
2695
+
2696
+ class MultiEditorComponent extends ModalComponent {
2697
+ //#region ViewChilds, Inputs, Outputs
2698
+ modal;
2699
+ addButtonLabel = 'Add row';
2700
+ formGroup;
2701
+ removeButtonLabel = 'Remove row';
2702
+ showAddButton = true;
2703
+ showDeleteButton = true;
2704
+ //#endregion
2705
+ //#region Variables
2706
+ gridLoading = false;
2707
+ dataGridDataset = inject(DataGridDataset);
2708
+ formService = inject(FormService);
2709
+ multiEditorDataset = inject(MultiEditorDataset);
2710
+ selectedKey;
2711
+ selectedValue;
2712
+ //#endregion
2713
+ //#region Properties
2714
+ get formLoading() {
2715
+ return this.formService.loading;
2716
+ }
2717
+ get hasSelectedKey() {
2718
+ return !!this.selectedKey;
2719
+ }
2720
+ //#endregion
2721
+ //#region Constructor and Angular life cycle methods
2722
+ constructor() {
2723
+ super();
2724
+ }
2725
+ ngOnInit() {
2726
+ this.dataGridDataset.loadFinished
2727
+ .pipe(takeUntil(this.destroy$))
2728
+ .subscribe(() => {
2729
+ this.multiEditorDataset.clearChangedValues();
2730
+ this.gridLoading = false;
2731
+ this.selectedKey = undefined;
2732
+ this.selectedValue = undefined;
2733
+ });
2734
+ this.dataGridDataset.selectedRowsChanged
2735
+ .pipe(takeUntil(this.destroy$))
2736
+ .subscribe(() => {
2737
+ if (this.gridLoading || !this.dataGridDataset.hasLoadedRows || !this.dataGridDataset.hasSelectedRows) {
2738
+ return;
2739
+ }
2740
+ if (this.formGroup.disabled) {
2741
+ this.formGroup.enable();
2742
+ }
2743
+ const selectedRowKey = this.dataGridDataset.selectedRowKeys[0];
2744
+ if (!!this.selectedKey && this.formGroup.enabled && !this.formGroup.valid && this.selectedKey !== selectedRowKey) {
2745
+ // If the form is invalid, need to stay in the same selected row.
2746
+ this.dataGridDataset.selectRow(this.selectedKey);
2747
+ this.formGroup.markAllAsTouched();
2748
+ }
2749
+ else {
2750
+ this.selectedKey = selectedRowKey;
2751
+ this.selectedValue = this.dataGridDataset.getRowData(selectedRowKey);
2752
+ this.formService.model = this.selectedValue;
2753
+ this.formGroup.reset();
2754
+ this.formGroup.patchValue(this.selectedValue);
2755
+ }
2756
+ });
2757
+ this.formGroup.valueChanges
2758
+ .pipe(takeUntil(this.destroy$))
2759
+ .subscribe((_) => {
2760
+ if (this.selectedKey && this.formGroup.dirty) {
2761
+ this.multiEditorDataset.updateValues(this.selectedKey, this.formGroup.getRawValue());
2762
+ }
2763
+ });
2764
+ // Start with an empty form and, since there is no selected row, disable the form.
2765
+ this.formGroup.disable({ emitEvent: false });
2766
+ }
2767
+ //#endregion
2768
+ //#region Event handlers
2769
+ onDeleteClick() {
2770
+ if (!this.selectedKey) {
2771
+ return;
2772
+ }
2773
+ this.multiEditorDataset.removeValue(this.selectedKey);
2774
+ this.selectedKey = undefined;
2775
+ this.formGroup.reset({ emitEvent: false });
2776
+ this.clearAllFormArrays(this.formGroup);
2777
+ this.formGroup.disable({ emitEvent: false });
2778
+ }
2779
+ onNewClick() {
2780
+ const fakeNewID = (Math.floor(Math.random() * (999999 - 100000)) + 100000) * -1;
2781
+ const newModel = this.multiEditorDataset.newData(fakeNewID);
2782
+ if (!!newModel) {
2783
+ this.multiEditorDataset.storeFakeIDGenerated(fakeNewID);
2784
+ this.dataGridDataset.addNewRow(newModel);
2785
+ const newKey = this.dataGridDataset.loadedKeys[this.dataGridDataset.loadedKeys.length - 1];
2786
+ this.dataGridDataset.selectRow(newKey);
2787
+ }
2788
+ }
2789
+ onSaveClick() {
2790
+ if (this.selectedKey) {
2791
+ this.formGroup.markAllAsTouched();
2792
+ }
2793
+ if (!this.selectedKey || this.formGroup.valid) {
2794
+ this.modalProcessing = true;
2795
+ this.formGroup.disable();
2796
+ this.multiEditorDataset.saveChanges()
2797
+ .pipe(take(1))
2798
+ .subscribe({
2799
+ next: (_) => {
2800
+ this.modalProcessing = false;
2801
+ this.toggleModal();
2802
+ },
2803
+ error: (e) => {
2804
+ this.modalProcessing = false;
2805
+ this.formGroup.enable();
2806
+ if (e.status === 400 && e.error.errors) {
2807
+ this.dataGridDataset.selectRowID(e.error.entityKey);
2808
+ this.formService.setValidationErrorsFromHttpResponse(e);
2809
+ }
2810
+ }
2811
+ });
2812
+ }
2813
+ }
2814
+ //#endregion
2815
+ //#region Public methods
2816
+ closeModal() {
2817
+ this.modal.closeModal();
2818
+ }
2819
+ toggleModal() {
2820
+ this.modal.toggleModal();
2821
+ if (this.modal.isShown) {
2822
+ this.gridLoading = true;
2823
+ this.dataGridDataset.refresh();
2824
+ this.selectedKey = undefined;
2825
+ this.formGroup.reset({ emitEvent: false });
2826
+ this.clearAllFormArrays(this.formGroup);
2827
+ this.formGroup.disable({ emitEvent: false });
2828
+ }
2829
+ }
2830
+ //#endregion
2831
+ //#region Private methods
2832
+ clearAllFormArrays(form) {
2833
+ Object.keys(form.controls).forEach((key) => {
2834
+ const control = form.get(key);
2835
+ if (control instanceof FormArray) {
2836
+ control.clear({ emitEvent: false });
2837
+ }
2838
+ // Check for nested FormArrays.
2839
+ if (control instanceof FormGroup) {
2840
+ this.clearAllFormArrays(control);
2841
+ }
2842
+ });
2843
+ }
2844
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: MultiEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2845
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.6", type: MultiEditorComponent, isStandalone: true, selector: "lib-multi-editor", inputs: { addButtonLabel: "addButtonLabel", formGroup: "formGroup", removeButtonLabel: "removeButtonLabel", showAddButton: "showAddButton", showDeleteButton: "showDeleteButton" }, viewQueries: [{ propertyName: "modal", first: true, predicate: ModalComponent, descendants: true }], usesInheritance: true, ngImport: i0, template: "<lib-modal\n[closeButtonText]=\"closeButtonText | translate\"\n[dialog]=\"dialog\"\n[modalProcessing]=\"modalProcessing\"\n[position]=\"position\"\n[size]=\"size\"\n[title]=\"title\">\n <div body class=\"body-content\">\n <div class=\"data-grid\">\n <lib-data-grid [disabled]=\"modalProcessing\" [lazyLoadRows]=\"true\"></lib-data-grid>\n </div>\n\n <div class=\"data\">\n <div class=\"buttons\">\n <a *ngIf=\"showAddButton\"\n class=\"btn gray-500\"\n [class.disabled]=\"modalProcessing || gridLoading || formLoading || formGroup.invalid\"\n (click)=\"onNewClick()\">\n {{ addButtonLabel }}\n </a>\n\n <a *ngIf=\"showDeleteButton && hasSelectedKey\"\n class=\"btn red-500\"\n [class.disabled]=\"modalProcessing || gridLoading || formLoading\"\n (click)=\"onDeleteClick()\">\n {{ removeButtonLabel }}\n </a>\n </div>\n\n <lib-form-group>\n <ng-content></ng-content>\n </lib-form-group>\n </div>\n </div>\n\n <div footer>\n <button\n class=\"btn blue-500\"\n type=\"button\"\n [class.disabled]=\"gridLoading || formLoading\"\n [class.loading]=\"modalProcessing\"\n (click)=\"onSaveClick()\">\n {{ 'Button-Modal-MultiEditor-Confirm' | translate }}\n </button>\n </div>\n</lib-modal>", styles: [".body-content{display:grid;grid-auto-columns:minmax(0,1fr);grid-auto-flow:column;gap:1rem;overflow:clip;overflow-clip-margin:4px}.data-grid{display:grid;-webkit-user-select:none;user-select:none;overflow:hidden}.data{display:grid;-webkit-user-select:none;user-select:none;overflow:clip;overflow-clip-margin:4px;min-height:24rem;grid-template-rows:min-content minmax(0,1fr);gap:.5rem}.data .buttons{display:grid;grid-auto-columns:auto;grid-auto-flow:column;gap:.5rem;border-bottom-color:#d1d5dbe6}\n"], dependencies: [{ kind: "component", type: DataGridComponent, selector: "lib-data-grid", inputs: ["disabled", "lazyLoadRows", "showButtons"] }, { kind: "component", type: ModalComponent, selector: "lib-modal", inputs: ["closeButtonText", "dialog", "modalProcessing", "position", "size", "title"], outputs: ["closed"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: FormGroupComponent, selector: "lib-form-group", inputs: ["shouldExpand", "label"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
2846
+ }
2847
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: MultiEditorComponent, decorators: [{
2848
+ type: Component,
2849
+ args: [{ selector: 'lib-multi-editor', imports: [
2850
+ DataGridComponent,
2851
+ ModalComponent,
2852
+ NgIf,
2853
+ FormGroupComponent,
2854
+ TranslatePipe,
2855
+ ], template: "<lib-modal\n[closeButtonText]=\"closeButtonText | translate\"\n[dialog]=\"dialog\"\n[modalProcessing]=\"modalProcessing\"\n[position]=\"position\"\n[size]=\"size\"\n[title]=\"title\">\n <div body class=\"body-content\">\n <div class=\"data-grid\">\n <lib-data-grid [disabled]=\"modalProcessing\" [lazyLoadRows]=\"true\"></lib-data-grid>\n </div>\n\n <div class=\"data\">\n <div class=\"buttons\">\n <a *ngIf=\"showAddButton\"\n class=\"btn gray-500\"\n [class.disabled]=\"modalProcessing || gridLoading || formLoading || formGroup.invalid\"\n (click)=\"onNewClick()\">\n {{ addButtonLabel }}\n </a>\n\n <a *ngIf=\"showDeleteButton && hasSelectedKey\"\n class=\"btn red-500\"\n [class.disabled]=\"modalProcessing || gridLoading || formLoading\"\n (click)=\"onDeleteClick()\">\n {{ removeButtonLabel }}\n </a>\n </div>\n\n <lib-form-group>\n <ng-content></ng-content>\n </lib-form-group>\n </div>\n </div>\n\n <div footer>\n <button\n class=\"btn blue-500\"\n type=\"button\"\n [class.disabled]=\"gridLoading || formLoading\"\n [class.loading]=\"modalProcessing\"\n (click)=\"onSaveClick()\">\n {{ 'Button-Modal-MultiEditor-Confirm' | translate }}\n </button>\n </div>\n</lib-modal>", styles: [".body-content{display:grid;grid-auto-columns:minmax(0,1fr);grid-auto-flow:column;gap:1rem;overflow:clip;overflow-clip-margin:4px}.data-grid{display:grid;-webkit-user-select:none;user-select:none;overflow:hidden}.data{display:grid;-webkit-user-select:none;user-select:none;overflow:clip;overflow-clip-margin:4px;min-height:24rem;grid-template-rows:min-content minmax(0,1fr);gap:.5rem}.data .buttons{display:grid;grid-auto-columns:auto;grid-auto-flow:column;gap:.5rem;border-bottom-color:#d1d5dbe6}\n"] }]
2856
+ }], ctorParameters: () => [], propDecorators: { modal: [{
2857
+ type: ViewChild,
2858
+ args: [ModalComponent]
2859
+ }], addButtonLabel: [{
2860
+ type: Input
2861
+ }], formGroup: [{
2862
+ type: Input
2863
+ }], removeButtonLabel: [{
2864
+ type: Input
2865
+ }], showAddButton: [{
2866
+ type: Input
2867
+ }], showDeleteButton: [{
2868
+ type: Input
2869
+ }] } });
2870
+
2871
+ class MultiSelectResultGridComponent extends BaseComponent {
2872
+ //#region ViewChilds, Inputs, Outputs
2873
+ isDisabled = false;
2874
+ template;
2875
+ //#endregion
2876
+ //#region Variables
2877
+ dataGridDataset = inject(DataGridDataset);
2878
+ isLoading = true;
2879
+ resultDataset = inject(MultiSelectResultDataset);
2880
+ //#endregion
2881
+ //#region Properties
2882
+ //#endregion
2883
+ //#region Constructor and Angular life cycle methods
2884
+ constructor() {
2885
+ super();
2886
+ }
2887
+ ngOnInit() {
2888
+ this.resultDataset.loadStarted
2889
+ .pipe(takeUntil(this.destroy$))
2890
+ .subscribe(() => {
2891
+ this.isLoading = true;
2892
+ });
2893
+ this.resultDataset.loadFinished
2894
+ .pipe(takeUntil(this.destroy$))
2895
+ .subscribe(() => {
2896
+ this.isLoading = false;
2897
+ });
2898
+ }
2899
+ //#endregion
2900
+ //#region Event handlers
2901
+ onItemRemoved(rowIndex) {
2902
+ const id = this.resultDataset.displayedIDs[rowIndex];
2903
+ this.dataGridDataset.deselectRowID(id);
2904
+ }
2905
+ onScrolled(event) {
2906
+ const currentScroll = event.target.scrollTop + event.target.clientHeight;
2907
+ const scrollHeight = event.target.scrollHeight - event.target.clientHeight;
2908
+ if (currentScroll > scrollHeight && !this.isLoading && !this.resultDataset.loadedLastRow) {
2909
+ this.isLoading = true;
2910
+ this.resultDataset.loadRows();
2911
+ }
2912
+ }
2913
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: MultiSelectResultGridComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2914
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.6", type: MultiSelectResultGridComponent, isStandalone: true, selector: "lib-multi-select-result-grid", inputs: { isDisabled: "isDisabled", template: "template" }, host: { classAttribute: "flex-grow" }, usesInheritance: true, ngImport: i0, template: "<div\nclass=\"body\"\n(scroll)=\"onScrolled($event)\">\n <div\n class=\"row\"\n *ngFor=\"let row of resultDataset.displayedRows; let i = index\">\n <div class=\"cell\">\n <a\n class=\"remove\"\n [class.disabled]=\"isDisabled\"\n (click)=\"onItemRemoved(i)\">\n <i class=\"fas fa-times\"></i>\n </a>\n </div> \n\n <div class=\"cell\">\n <div>\n <ng-container *ngTemplateOutlet=\"template; context: { rowData: row }\">\n </ng-container>\n </div>\n </div>\n </div>\n\n <div\n class=\"loading\"\n *ngIf=\"isLoading\">\n <svg [ngClass]=\"''\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\">\n <circle class=\"opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"4\"></circle>\n <path class=\"opacity-75\" fill=\"currentColor\" d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"></path>\n </svg>\n\n <span>\n {{ resultDataset.configs.loadingDisplayText ?? '' | translate }} \n </span>\n </div>\n</div>", styles: [":host-context{display:grid;-webkit-user-select:none;user-select:none;grid-template-rows:auto 1fr;overflow:hidden}:host-context:not(.no-borders){border-radius:.5rem;border-width:1px;border-color:#d1d5dbe6;--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.body{overflow:overlay}.body .row{border-bottom-width:1px;border-color:#cbd5e180;margin-top:0;cursor:pointer;display:grid;grid-auto-flow:column;grid-template-columns:min-content minmax(0,1fr);gap:1rem;padding:.5rem 1rem}.body .row:nth-child(2n){background-color:#f8fafcb3}.body .row:hover{background-color:#bfdbfe33}.body .row.focused{z-index:1;border-color:#94a3b899}.body .row.selected{position:relative;z-index:10;--tw-border-opacity: 1;border-color:rgb(147 197 253 / var(--tw-border-opacity, 1));background-color:#bfdbfe4d}.body .row.selected:hover{background-color:#bfdbfe66}.body .row .cell{white-space:nowrap;display:flex;align-items:center}.body .row .cell a{padding-left:.5rem;padding-right:.5rem;font-size:1.125rem;line-height:1.75rem}.body .row .cell a:not(.disabled){cursor:pointer}.body .row .cell a.disabled{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity, 1))}.body .row .cell .remove{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity, 1))}.body .row .cell.disabled{pointer-events:none;cursor:default;opacity:.6}.body .row .cell>*{overflow:hidden;text-overflow:ellipsis}.body .row .cell div{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.body .loading{margin-bottom:.5rem;display:flex;height:2.5rem;align-items:center;border-bottom-width:1px;border-color:#cbd5e180;padding-left:1rem;padding-right:1rem}.body .loading svg{height:1.5rem;width:1.5rem}@keyframes spin{to{transform:rotate(360deg)}}.body .loading svg{animation:spin 1s linear infinite;transition-property:opacity;transition-duration:.2s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.body .loading span{padding-left:.5rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
2915
+ }
2916
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: MultiSelectResultGridComponent, decorators: [{
2917
+ type: Component,
2918
+ args: [{ selector: 'lib-multi-select-result-grid', host: {
2919
+ 'class': 'flex-grow'
2920
+ }, imports: [
2921
+ CommonModule,
2922
+ NgFor,
2923
+ NgIf,
2924
+ TranslatePipe,
2925
+ ], template: "<div\nclass=\"body\"\n(scroll)=\"onScrolled($event)\">\n <div\n class=\"row\"\n *ngFor=\"let row of resultDataset.displayedRows; let i = index\">\n <div class=\"cell\">\n <a\n class=\"remove\"\n [class.disabled]=\"isDisabled\"\n (click)=\"onItemRemoved(i)\">\n <i class=\"fas fa-times\"></i>\n </a>\n </div> \n\n <div class=\"cell\">\n <div>\n <ng-container *ngTemplateOutlet=\"template; context: { rowData: row }\">\n </ng-container>\n </div>\n </div>\n </div>\n\n <div\n class=\"loading\"\n *ngIf=\"isLoading\">\n <svg [ngClass]=\"''\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\">\n <circle class=\"opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"4\"></circle>\n <path class=\"opacity-75\" fill=\"currentColor\" d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"></path>\n </svg>\n\n <span>\n {{ resultDataset.configs.loadingDisplayText ?? '' | translate }} \n </span>\n </div>\n</div>", styles: [":host-context{display:grid;-webkit-user-select:none;user-select:none;grid-template-rows:auto 1fr;overflow:hidden}:host-context:not(.no-borders){border-radius:.5rem;border-width:1px;border-color:#d1d5dbe6;--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.body{overflow:overlay}.body .row{border-bottom-width:1px;border-color:#cbd5e180;margin-top:0;cursor:pointer;display:grid;grid-auto-flow:column;grid-template-columns:min-content minmax(0,1fr);gap:1rem;padding:.5rem 1rem}.body .row:nth-child(2n){background-color:#f8fafcb3}.body .row:hover{background-color:#bfdbfe33}.body .row.focused{z-index:1;border-color:#94a3b899}.body .row.selected{position:relative;z-index:10;--tw-border-opacity: 1;border-color:rgb(147 197 253 / var(--tw-border-opacity, 1));background-color:#bfdbfe4d}.body .row.selected:hover{background-color:#bfdbfe66}.body .row .cell{white-space:nowrap;display:flex;align-items:center}.body .row .cell a{padding-left:.5rem;padding-right:.5rem;font-size:1.125rem;line-height:1.75rem}.body .row .cell a:not(.disabled){cursor:pointer}.body .row .cell a.disabled{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity, 1))}.body .row .cell .remove{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity, 1))}.body .row .cell.disabled{pointer-events:none;cursor:default;opacity:.6}.body .row .cell>*{overflow:hidden;text-overflow:ellipsis}.body .row .cell div{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.body .loading{margin-bottom:.5rem;display:flex;height:2.5rem;align-items:center;border-bottom-width:1px;border-color:#cbd5e180;padding-left:1rem;padding-right:1rem}.body .loading svg{height:1.5rem;width:1.5rem}@keyframes spin{to{transform:rotate(360deg)}}.body .loading svg{animation:spin 1s linear infinite;transition-property:opacity;transition-duration:.2s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.body .loading span{padding-left:.5rem}\n"] }]
2926
+ }], ctorParameters: () => [], propDecorators: { isDisabled: [{
2927
+ type: Input
2928
+ }], template: [{
2929
+ type: Input
2930
+ }] } });
2931
+
2932
+ class MultiSelectComponent extends ModalComponent {
2933
+ //#region ViewChilds, Inputs, Outputs
2934
+ modal;
2935
+ itemTemplate;
2936
+ //#endregion
2937
+ //#region Variables
2938
+ isResultGridLoading = false;
2939
+ isSearchGridLoading = false;
2940
+ resultDataset = inject(MultiSelectResultDataset);
2941
+ searchCriteria = '';
2942
+ _searchColumn = '';
2943
+ dataGridDataset = inject(DataGridDataset);
2944
+ //#endregion
2945
+ //#region Properties
2946
+ get searchColumn() {
2947
+ if (this._searchColumn.length === 0) {
2948
+ let columnName = this.dataGridDataset.columns.filter((col) => col.field !== '')[0]?.field ?? 'Search';
2949
+ columnName = columnName.charAt(0).toUpperCase() + columnName.slice(1);
2950
+ this._searchColumn = columnName;
2951
+ }
2952
+ return this._searchColumn;
2953
+ }
2954
+ //#endregion
2955
+ //#region Constructor and Angular life cycle methods
2956
+ constructor() {
2957
+ super();
2958
+ this.dataGridDataset.configs.multiSelect = true;
2959
+ this.closeButtonText = 'Button-Modal-Cancel';
2960
+ }
2961
+ ngOnInit() {
2962
+ this.dataGridDataset.loadStarted
2963
+ .pipe(takeUntil(this.destroy$))
2964
+ .subscribe(() => {
2965
+ this.isSearchGridLoading = true;
2966
+ });
2967
+ this.dataGridDataset.loadFinished
2968
+ .pipe(takeUntil(this.destroy$))
2969
+ .subscribe(() => {
2970
+ this.isSearchGridLoading = false;
2971
+ this.updateSelectedItems();
2972
+ });
2973
+ this.resultDataset.loadFinished
2974
+ .pipe(takeUntil(this.destroy$))
2975
+ .subscribe(() => {
2976
+ this.isResultGridLoading = false;
2977
+ this.updateSelectedItems();
2978
+ });
2979
+ this.dataGridDataset.selectedRowsChanged
2980
+ .pipe(takeUntil(this.destroy$))
2981
+ .subscribe((selectionChanges) => {
2982
+ if (this.isSearchGridLoading || this.isResultGridLoading) {
2983
+ return;
2984
+ }
2985
+ Object.keys(selectionChanges)
2986
+ .forEach((key) => {
2987
+ const selection = selectionChanges[key];
2988
+ const id = this.dataGridDataset.getRowID(key);
2989
+ if (selection.selected) {
2990
+ this.resultDataset.setIDToAdd(id, selection.rowData);
2991
+ }
2992
+ else {
2993
+ this.resultDataset.setIDToRemove(id);
2994
+ }
2995
+ });
2996
+ });
2997
+ }
2998
+ //#endregion
2999
+ //#region Event handlers
3000
+ onSave() {
3001
+ this.modalProcessing = true;
3002
+ this.resultDataset.saveChanges()
3003
+ .pipe(take(1))
3004
+ .subscribe(() => {
3005
+ this.modalProcessing = false;
3006
+ this.toggleModal();
3007
+ });
3008
+ }
3009
+ onSearch() {
3010
+ const filters = this.dataGridDataset.filters ?? {};
3011
+ this.isSearchGridLoading = true;
3012
+ if (this.searchCriteria.length > 0) {
3013
+ filters[this.searchColumn] = this.searchCriteria;
3014
+ this.dataGridDataset.setFilters(filters);
3015
+ }
3016
+ else if (Object.keys(filters).includes(this.searchColumn)) {
3017
+ this.clearSearch();
3018
+ }
3019
+ }
3020
+ //#endregion
3021
+ //#region Public methods
3022
+ closeModal() {
3023
+ this.modal.closeModal();
3024
+ }
3025
+ toggleModal() {
3026
+ this.modal.toggleModal();
3027
+ if (this.modal.isShown) {
3028
+ this.searchCriteria = '';
3029
+ this.clearSearch();
3030
+ this.isResultGridLoading = true;
3031
+ this.resultDataset.refresh();
3032
+ }
3033
+ }
3034
+ //#endregion
3035
+ //#regiosn Private methods
3036
+ clearSearch() {
3037
+ const filters = this.dataGridDataset.filters ?? {};
3038
+ if (Object.keys(filters).includes(this.searchColumn)) {
3039
+ delete filters[this.searchColumn];
3040
+ }
3041
+ this.dataGridDataset.setFilters(filters);
3042
+ }
3043
+ updateSelectedItems() {
3044
+ if (this.isSearchGridLoading || this.isResultGridLoading) {
3045
+ return;
3046
+ }
3047
+ // Filter by the results that are not marked to be removed.
3048
+ const idsFromResult = (this.resultDataset.loadedKeys ?? [])
3049
+ .map((key) => this.resultDataset.getRowID(key))
3050
+ .filter((id) => this.resultDataset.idsToRemove.indexOf(id) < 0);
3051
+ idsFromResult.push(...this.resultDataset.idsToAdd);
3052
+ // Get each corresponding key from the data grid.
3053
+ const keysToSelect = (this.dataGridDataset.loadedKeys ?? [])
3054
+ .filter((key) => idsFromResult.indexOf(this.dataGridDataset.getRowID(key)) >= 0);
3055
+ this.dataGridDataset.selectRows(keysToSelect);
3056
+ }
3057
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: MultiSelectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3058
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.6", type: MultiSelectComponent, isStandalone: true, selector: "lib-multi-select", inputs: { itemTemplate: "itemTemplate" }, viewQueries: [{ propertyName: "modal", first: true, predicate: ModalComponent, descendants: true }], usesInheritance: true, ngImport: i0, template: "<lib-modal\n[closeButtonText]=\"closeButtonText | translate\"\n[dialog]=\"dialog\"\n[modalProcessing]=\"modalProcessing\"\n[position]=\"position\"\n[size]=\"size\"\n[title]=\"title\">\n <div body class=\"body-content\">\n <div class=\"search-grid\">\n <div class=\"search\">\n <input\n class=\"form-input\"\n placeholder=\"Search...\"\n type=\"text\"\n [(ngModel)]=\"searchCriteria\"\n [disabled]=\"modalProcessing || isSearchGridLoading\"\n (keydown.enter)=\"onSearch()\" />\n </div>\n \n <lib-data-grid\n [disabled]=\"modalProcessing || isSearchGridLoading || isResultGridLoading\"\n [lazyLoadRows]=\"true\">\n </lib-data-grid>\n </div>\n\n <lib-multi-select-result-grid\n [isDisabled]=\"modalProcessing || isSearchGridLoading || isResultGridLoading\"\n [template]=\"itemTemplate\">\n </lib-multi-select-result-grid>\n </div>\n\n <div footer>\n <button\n class=\"btn blue-500\"\n type=\"button\"\n [class.loading]=\"modalProcessing\"\n (click)=\"onSave()\">\n {{ 'Button-Modal-MultiSelect-Confirm' | translate }}\n </button>\n </div>\n</lib-modal>", styles: [".body-content{display:grid;height:100%;grid-auto-columns:minmax(0,1fr);grid-auto-flow:column;gap:1rem;grid-template-rows:min-content minmax(0,1fr);grid-auto-flow:row!important;overflow:clip;overflow-clip-margin:4px}.search-grid{display:grid;grid-template-rows:subgrid}.search-grid,lib-multi-select-result-grid{grid-row:1 / -1}lib-multi-select-result-grid{min-height:24rem}\n"], dependencies: [{ kind: "component", type: DataGridComponent, selector: "lib-data-grid", inputs: ["disabled", "lazyLoadRows", "showButtons"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: ModalComponent, selector: "lib-modal", inputs: ["closeButtonText", "dialog", "modalProcessing", "position", "size", "title"], outputs: ["closed"] }, { kind: "component", type: MultiSelectResultGridComponent, selector: "lib-multi-select-result-grid", inputs: ["isDisabled", "template"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
3059
+ }
3060
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: MultiSelectComponent, decorators: [{
3061
+ type: Component,
3062
+ args: [{ selector: 'lib-multi-select', imports: [
3063
+ DataGridComponent,
3064
+ FormsModule,
3065
+ ModalComponent,
3066
+ MultiSelectResultGridComponent,
3067
+ TranslatePipe,
3068
+ ], template: "<lib-modal\n[closeButtonText]=\"closeButtonText | translate\"\n[dialog]=\"dialog\"\n[modalProcessing]=\"modalProcessing\"\n[position]=\"position\"\n[size]=\"size\"\n[title]=\"title\">\n <div body class=\"body-content\">\n <div class=\"search-grid\">\n <div class=\"search\">\n <input\n class=\"form-input\"\n placeholder=\"Search...\"\n type=\"text\"\n [(ngModel)]=\"searchCriteria\"\n [disabled]=\"modalProcessing || isSearchGridLoading\"\n (keydown.enter)=\"onSearch()\" />\n </div>\n \n <lib-data-grid\n [disabled]=\"modalProcessing || isSearchGridLoading || isResultGridLoading\"\n [lazyLoadRows]=\"true\">\n </lib-data-grid>\n </div>\n\n <lib-multi-select-result-grid\n [isDisabled]=\"modalProcessing || isSearchGridLoading || isResultGridLoading\"\n [template]=\"itemTemplate\">\n </lib-multi-select-result-grid>\n </div>\n\n <div footer>\n <button\n class=\"btn blue-500\"\n type=\"button\"\n [class.loading]=\"modalProcessing\"\n (click)=\"onSave()\">\n {{ 'Button-Modal-MultiSelect-Confirm' | translate }}\n </button>\n </div>\n</lib-modal>", styles: [".body-content{display:grid;height:100%;grid-auto-columns:minmax(0,1fr);grid-auto-flow:column;gap:1rem;grid-template-rows:min-content minmax(0,1fr);grid-auto-flow:row!important;overflow:clip;overflow-clip-margin:4px}.search-grid{display:grid;grid-template-rows:subgrid}.search-grid,lib-multi-select-result-grid{grid-row:1 / -1}lib-multi-select-result-grid{min-height:24rem}\n"] }]
3069
+ }], ctorParameters: () => [], propDecorators: { modal: [{
3070
+ type: ViewChild,
3071
+ args: [ModalComponent]
3072
+ }], itemTemplate: [{
3073
+ type: Input
3074
+ }] } });
3075
+
3076
+ class SidebarItemComponent extends BaseComponent {
3077
+ //#region ViewChilds, Inputs, Outputs
3078
+ menuContainer;
3079
+ menu;
3080
+ level = 0;
3081
+ //#endregion
3082
+ //#region Variables
3083
+ hasFailed = false;
3084
+ isLoading = false;
3085
+ isSelected = false;
3086
+ _childHeight = 0;
3087
+ changeDetectorRef = inject(ChangeDetectorRef);
3088
+ sidebarService = inject(SidebarService);
3089
+ //#endregion
3090
+ //#region Properties
3091
+ get childHeight() {
3092
+ return this._childHeight;
3093
+ }
3094
+ get areChildrenLoaded() {
3095
+ return this.isParent && (this.menu.children?.length ?? 0) > 0;
3096
+ }
3097
+ get isActive() {
3098
+ return this.sidebarService.isActive;
3099
+ }
3100
+ get isCollapsed() {
3101
+ return this.sidebarService.isCollapsed;
3102
+ }
3103
+ get isParent() {
3104
+ return this.menu.childCount > 0;
3105
+ }
3106
+ get hasIcon() {
3107
+ return !!this.menu.icon && this.menu.icon.length > 0;
3108
+ }
3109
+ //#endregion
3110
+ //#region Constructor and Angular life cycle methods
3111
+ constructor() {
3112
+ super();
3113
+ }
3114
+ ngAfterViewInit() {
3115
+ if (!!this.menu && !!this.menu.parent) {
3116
+ this.menu.height = this.menuContainer?.nativeElement?.offsetHeight ?? 0;
3117
+ this.sidebarService.childrenInitialized.emit(this.menu.parent);
3118
+ }
3119
+ }
3120
+ ngOnInit() {
3121
+ if (this.isParent) {
3122
+ this.sidebarService.childrenFailed
3123
+ .pipe(takeUntil(this.destroy$), filter((menu) => menu.id === this.menu.id), delay(1000))
3124
+ .subscribe((_menu) => {
3125
+ this.hasFailed = true;
3126
+ this.isLoading = false;
3127
+ this.isSelected = false;
3128
+ this.menu.isSelected = false;
3129
+ this.changeDetectorRef.detectChanges();
3130
+ });
3131
+ this.sidebarService.childrenLoading
3132
+ .pipe(takeUntil(this.destroy$), filter((menu) => menu.id === this.menu.id))
3133
+ .subscribe((_menu) => {
3134
+ this.hasFailed = false;
3135
+ this.isLoading = true;
3136
+ });
3137
+ this.sidebarService.childrenInitialized
3138
+ .pipe(takeUntil(this.destroy$), filter((menu) => menu.id === this.menu.id && this.menu.children.every((child) => (child.height ?? 0) > 0)))
3139
+ .subscribe((_menu) => {
3140
+ this.isLoading = false;
3141
+ this.hasFailed = false;
3142
+ this.updateSubMenuHeight();
3143
+ if (!!this.menu.parent) {
3144
+ this.sidebarService.childrenInitialized.emit(this.menu.parent);
3145
+ }
3146
+ this.changeDetectorRef.detectChanges();
3147
+ });
3148
+ }
3149
+ this.sidebarService.selectionChanged
3150
+ .pipe(takeUntil(this.destroy$), filter((menu) => menu.id === this.menu.id))
3151
+ .subscribe((menu) => {
3152
+ this.isSelected = menu.isSelected;
3153
+ this.updateSubMenuHeight();
3154
+ });
3155
+ }
3156
+ //#endregion
3157
+ //#region Event handlers
3158
+ onSelectItem() {
3159
+ if (this.sidebarService.shouldActivate) {
3160
+ this.sidebarService.isActive = true;
3161
+ }
3162
+ this.sidebarService.select(this.menu);
3163
+ }
3164
+ //#endregion
3165
+ //#region Public methods
3166
+ //#endregion
3167
+ //#region Private methods
3168
+ trackByFn(_index, item) {
3169
+ return item.id;
3170
+ }
3171
+ getChildHeight(menu) {
3172
+ if (menu.isSelected) {
3173
+ return menu.children.reduce((height, childMenu) => height + (childMenu.height ?? 0) + this.getChildHeight(childMenu), 0);
3174
+ }
3175
+ return 0;
3176
+ }
3177
+ updateSubMenuHeight() {
3178
+ this._childHeight = this.getChildHeight(this.menu);
3179
+ }
3180
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: SidebarItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3181
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.6", type: SidebarItemComponent, isStandalone: true, selector: "lib-sidebar-item", inputs: { menu: "menu", level: "level" }, host: { properties: { "class.active": "isActive", "class.expanded": "!isCollapsed", "class.first-level": "level === 0", "class.selected": "isSelected" } }, viewQueries: [{ propertyName: "menuContainer", first: true, predicate: ["menuContainer"], descendants: true }], usesInheritance: true, ngImport: i0, template: "<li>\n <div #menuContainer\n [class.failed]=\"hasFailed\"\n [class.loading]=\"isLoading\"\n [class.parent]=\"isParent\"\n [class.selected]=\"isSelected\"\n (click)=\"onSelectItem()\">\n <a>\n <i *ngIf=\"hasIcon\" class=\"fa-solid fa-fw {{ menu.icon }}\"></i>\n <span>{{ menu.label | translate }}</span>\n </a>\n\n <div class=\"loading\" *ngIf=\"isParent\">\n <svg class=\"animate-spin text-white\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\">\n <circle class=\"opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"4\"></circle>\n <path class=\"opacity-75\" fill=\"currentColor\" d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"></path>\n </svg>\n </div>\n </div>\n\n <ul *ngIf=\"isParent\"\n [class.open]=\"isSelected\"\n [ngStyle]=\"{\n 'height.px': childHeight,\n 'max-height.px': childHeight\n }\">\n <lib-sidebar-item *ngFor=\"let child of menu.children; trackBy: trackByFn\"\n [menu]=\"child\"\n [level]=\"level+1\">\n </lib-sidebar-item>\n </ul>\n</li>", styles: ["@charset \"UTF-8\";:host{position:relative;display:block}@media (max-width: 767px){:host:not(.active) li>ul{height:0px!important}}@media (min-width: 768px){:host:not(.expanded):not(.active) li>ul{height:0px!important}:host.expanded li>div.parent:not(.loading):after{opacity:1}:host.expanded li>div.parent a{padding-right:2rem}:host.expanded li>div.parent.loading .loading{opacity:1!important}:host.expanded li>div a i{margin-left:0;font-size:1.125rem;line-height:1.75rem}:host.expanded li>div a span{opacity:1}}:host.active li>div.parent:not(.loading):after{opacity:1}:host.active li>div.parent a{padding-right:2rem}:host.active li>div.parent.loading .loading{opacity:1!important}:host.active li>div a i{margin-left:0;font-size:1.125rem;line-height:1.75rem}:host.active li>div a span{opacity:1}:host.expanded li .selected:not(.loading).parent:after,:host.active li .selected:not(.loading).parent:after{--tw-rotate: -90deg;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))}:host.first-level:before{position:absolute;top:0;left:0;bottom:0;z-index:10;width:0px;--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity, 1));--tw-content: \"\";content:var(--tw-content);transition-property:width;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:var(--sidebar-animation-duration);will-change:width}:host.first-level.selected:before{width:4px}li{position:relative;height:100%}li>div{position:relative;overflow:hidden;height:var(--sidebar-item-height);line-height:var(--sidebar-item-height)}li>div:hover{--tw-bg-opacity: 1;background-color:rgb(51 65 85 / var(--tw-bg-opacity, 1))}li>div.parent:after{position:absolute;top:0;right:.75rem;width:10px;font-weight:800;transition-property:opacity;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);opacity:0;font-family:\"Font Awesome 6 Free\";transition-duration:var(--sidebar-animation-duration);will-change:opacity}li>div.parent:not(.failed):after{--tw-content: \"\\f104\";content:var(--tw-content)}li>div.parent.failed:after{right:1rem;--tw-content: \"\\f071\";content:var(--tw-content)}li>div a{display:block;height:100%;cursor:pointer;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;padding-left:.75rem;padding-right:.75rem;text-align:left;width:var(--sidebar-expanded-width)}li>div a i{margin-right:.5rem;font-size:1.25rem;line-height:1.75rem;transition-property:font-size,line-height,margin-left;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);margin-left:var(--sidebar-icon-collapsed-margin);transition-duration:var(--sidebar-animation-duration);will-change:font-size,line-height,margin-left}li>div a span{text-overflow:ellipsis;opacity:0;transition-property:opacity;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:var(--sidebar-animation-duration);will-change:opacity}li>div .loading{position:absolute;top:0;right:.55rem;height:100%;width:1rem;opacity:0;transition-property:opacity;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:var(--sidebar-animation-duration);will-change:opacity}li>div .loading svg{height:100%;width:100%}li>ul{overflow:hidden;transition-property:height,max-height;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:var(--sidebar-animation-duration);will-change:height,max-height}li>ul a{padding-left:1.25rem}\n"], dependencies: [{ kind: "component", type: SidebarItemComponent, selector: "lib-sidebar-item", inputs: ["menu", "level"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
3182
+ }
3183
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: SidebarItemComponent, decorators: [{
3184
+ type: Component,
3185
+ args: [{ selector: 'lib-sidebar-item', imports: [
3186
+ NgFor,
3187
+ NgIf,
3188
+ NgStyle,
3189
+ TranslatePipe,
3190
+ ], host: {
3191
+ '[class.active]': 'isActive',
3192
+ '[class.expanded]': '!isCollapsed',
3193
+ '[class.first-level]': 'level === 0',
3194
+ '[class.selected]': 'isSelected',
3195
+ }, template: "<li>\n <div #menuContainer\n [class.failed]=\"hasFailed\"\n [class.loading]=\"isLoading\"\n [class.parent]=\"isParent\"\n [class.selected]=\"isSelected\"\n (click)=\"onSelectItem()\">\n <a>\n <i *ngIf=\"hasIcon\" class=\"fa-solid fa-fw {{ menu.icon }}\"></i>\n <span>{{ menu.label | translate }}</span>\n </a>\n\n <div class=\"loading\" *ngIf=\"isParent\">\n <svg class=\"animate-spin text-white\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\">\n <circle class=\"opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"4\"></circle>\n <path class=\"opacity-75\" fill=\"currentColor\" d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"></path>\n </svg>\n </div>\n </div>\n\n <ul *ngIf=\"isParent\"\n [class.open]=\"isSelected\"\n [ngStyle]=\"{\n 'height.px': childHeight,\n 'max-height.px': childHeight\n }\">\n <lib-sidebar-item *ngFor=\"let child of menu.children; trackBy: trackByFn\"\n [menu]=\"child\"\n [level]=\"level+1\">\n </lib-sidebar-item>\n </ul>\n</li>", styles: ["@charset \"UTF-8\";:host{position:relative;display:block}@media (max-width: 767px){:host:not(.active) li>ul{height:0px!important}}@media (min-width: 768px){:host:not(.expanded):not(.active) li>ul{height:0px!important}:host.expanded li>div.parent:not(.loading):after{opacity:1}:host.expanded li>div.parent a{padding-right:2rem}:host.expanded li>div.parent.loading .loading{opacity:1!important}:host.expanded li>div a i{margin-left:0;font-size:1.125rem;line-height:1.75rem}:host.expanded li>div a span{opacity:1}}:host.active li>div.parent:not(.loading):after{opacity:1}:host.active li>div.parent a{padding-right:2rem}:host.active li>div.parent.loading .loading{opacity:1!important}:host.active li>div a i{margin-left:0;font-size:1.125rem;line-height:1.75rem}:host.active li>div a span{opacity:1}:host.expanded li .selected:not(.loading).parent:after,:host.active li .selected:not(.loading).parent:after{--tw-rotate: -90deg;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))}:host.first-level:before{position:absolute;top:0;left:0;bottom:0;z-index:10;width:0px;--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity, 1));--tw-content: \"\";content:var(--tw-content);transition-property:width;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:var(--sidebar-animation-duration);will-change:width}:host.first-level.selected:before{width:4px}li{position:relative;height:100%}li>div{position:relative;overflow:hidden;height:var(--sidebar-item-height);line-height:var(--sidebar-item-height)}li>div:hover{--tw-bg-opacity: 1;background-color:rgb(51 65 85 / var(--tw-bg-opacity, 1))}li>div.parent:after{position:absolute;top:0;right:.75rem;width:10px;font-weight:800;transition-property:opacity;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);opacity:0;font-family:\"Font Awesome 6 Free\";transition-duration:var(--sidebar-animation-duration);will-change:opacity}li>div.parent:not(.failed):after{--tw-content: \"\\f104\";content:var(--tw-content)}li>div.parent.failed:after{right:1rem;--tw-content: \"\\f071\";content:var(--tw-content)}li>div a{display:block;height:100%;cursor:pointer;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;padding-left:.75rem;padding-right:.75rem;text-align:left;width:var(--sidebar-expanded-width)}li>div a i{margin-right:.5rem;font-size:1.25rem;line-height:1.75rem;transition-property:font-size,line-height,margin-left;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);margin-left:var(--sidebar-icon-collapsed-margin);transition-duration:var(--sidebar-animation-duration);will-change:font-size,line-height,margin-left}li>div a span{text-overflow:ellipsis;opacity:0;transition-property:opacity;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:var(--sidebar-animation-duration);will-change:opacity}li>div .loading{position:absolute;top:0;right:.55rem;height:100%;width:1rem;opacity:0;transition-property:opacity;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:var(--sidebar-animation-duration);will-change:opacity}li>div .loading svg{height:100%;width:100%}li>ul{overflow:hidden;transition-property:height,max-height;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:var(--sidebar-animation-duration);will-change:height,max-height}li>ul a{padding-left:1.25rem}\n"] }]
3196
+ }], ctorParameters: () => [], propDecorators: { menuContainer: [{
3197
+ type: ViewChild,
3198
+ args: ['menuContainer']
3199
+ }], menu: [{
3200
+ type: Input
3201
+ }], level: [{
3202
+ type: Input
3203
+ }] } });
3204
+
3205
+ class SidebarComponent extends BaseComponent {
3206
+ //#region ViewChilds, Inputs, Outputs
3207
+ //#endregion
3208
+ //#region Host listeners
3209
+ onBodyMouseDown(event) {
3210
+ if (this.isActive && event.target) {
3211
+ const target = event.target;
3212
+ this.wasClickedOutside = (event.button === 0 && !target.closest('lib-sidebar')) ?? false;
3213
+ }
3214
+ }
3215
+ onBodyMouseUp(_event) {
3216
+ if (this.wasClickedOutside) {
3217
+ this.wasClickedOutside = false;
3218
+ this.deactivate();
3219
+ }
3220
+ }
3221
+ onDocumentKeyDown(event) {
3222
+ event = event || window.event;
3223
+ let isEscapeKey = false;
3224
+ if ('key' in event) {
3225
+ isEscapeKey = (event.key === 'Escape' || event.key === 'Esc');
3226
+ }
3227
+ else {
3228
+ isEscapeKey = (event.keyCode === 27);
3229
+ }
3230
+ if (isEscapeKey) {
3231
+ this.deactivate();
3232
+ }
3233
+ }
3234
+ onResize(_event) {
3235
+ this.updateShouldActivate();
3236
+ }
3237
+ //#endregion
3238
+ //#region Variables
3239
+ sidebarConfigs = inject(SIDEBAR_CONFIGS);
3240
+ configErrorText = this.sidebarConfigs.errorText;
3241
+ configLoadingText = this.sidebarConfigs.loadingText;
3242
+ configLogoCollapsedPath = this.sidebarConfigs.logoCollapsedPath;
3243
+ configLogoExpandedPath = this.sidebarConfigs.logoExpandedPath;
3244
+ hasFailed = false;
3245
+ menus = [];
3246
+ profile;
3247
+ sidebarService = inject(SidebarService);
3248
+ wasClickedOutside = false;
3249
+ //#endregion
3250
+ //#region Properties
3251
+ get hasProfileImage() {
3252
+ return this.isProfileLoaded && (this.profile.image?.length ?? 0) > 0;
3253
+ }
3254
+ get isActive() {
3255
+ return this.sidebarService.isActive;
3256
+ }
3257
+ get isCollapsed() {
3258
+ return this.sidebarService.isCollapsed;
3259
+ }
3260
+ get isLoading() {
3261
+ return this.menus.length === 0;
3262
+ }
3263
+ get isProfileLoaded() {
3264
+ return !!this.profile;
3265
+ }
3266
+ //#endregion
3267
+ //#region Constructor and Angular life cycle methods
3268
+ constructor() {
3269
+ super();
3270
+ }
3271
+ ngAfterViewInit() {
3272
+ this.updateShouldActivate();
3273
+ }
3274
+ ngOnInit() {
3275
+ this.sidebarService.menuUrlSelected
3276
+ .pipe(takeUntil(this.destroy$))
3277
+ .subscribe((_menu) => this.deactivate());
3278
+ this.sidebarService.loadRoot()
3279
+ .pipe(take(1))
3280
+ .subscribe({
3281
+ next: (menus) => this.menus = menus,
3282
+ error: () => this.hasFailed = true
3283
+ });
3284
+ this.profile = this.sidebarService.getUserProfile();
3285
+ }
3286
+ //#endregion
3287
+ //#region Event handlers
3288
+ //#endregion
3289
+ //#region Public methods
3290
+ collapse() {
3291
+ this.sidebarService.isCollapsed = !this.sidebarService.isCollapsed;
3292
+ this.sidebarService.isActive = false;
3293
+ if (this.sidebarService.isCollapsed) {
3294
+ this.sidebarService.deselectAll();
3295
+ }
3296
+ this.updateShouldActivate();
3297
+ }
3298
+ //#endregion
3299
+ //#region Private methods
3300
+ trackByFn(_index, item) {
3301
+ return item.id;
3302
+ }
3303
+ deactivate() {
3304
+ if (this.sidebarService.isActive) {
3305
+ this.sidebarService.isActive = false;
3306
+ this.sidebarService.deselectAll();
3307
+ }
3308
+ }
3309
+ updateShouldActivate() {
3310
+ this.sidebarService.shouldActivate =
3311
+ (window.innerWidth <= 767 && !this.sidebarService.isCollapsed)
3312
+ || this.sidebarService.isCollapsed;
3313
+ }
3314
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: SidebarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3315
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.6", type: SidebarComponent, isStandalone: true, selector: "lib-sidebar", host: { listeners: { "body:mousedown": "onBodyMouseDown($event)", "body:mouseup": "onBodyMouseUp($event)", "document:keydown.escape": "onDocumentKeyDown($event)", "window:resize": "onResize($event)" }, properties: { "class.active": "isActive", "class.expanded": "!isCollapsed" } }, usesInheritance: true, ngImport: i0, template: "<div class=\"header\">\n <div class=\"logo-container\">\n <img class=\"collapsed\" [src]=\"configLogoCollapsedPath\" />\n <img class=\"expanded\" [src]=\"configLogoExpandedPath\" />\n </div>\n\n <div class=\"profile-container\" *ngIf=\"isProfileLoaded\">\n <div class=\"picture\" *ngIf=\"hasProfileImage\">\n <img [src]=\"profile!.image\" />\n </div>\n\n <div class=\"profile\">\n <h4>{{ profile!.name }}</h4>\n <h5>{{ profile!.title }}</h5>\n </div>\n </div>\n</div> \n\n<nav>\n <ul *ngIf=\"!isLoading; else loading\">\n <lib-sidebar-item *ngFor=\"let menu of menus; trackBy: trackByFn\"\n [menu]=\"menu\">\n </lib-sidebar-item>\n\n <div class=\"error\" *ngIf=\"hasFailed\">\n <div>\n <i class=\"fa-solid fa-fw fa-triangle-exclamation\"></i>\n <span>{{ configErrorText }}</span>\n </div>\n </div>\n </ul>\n</nav>\n\n<ng-template #loading>\n <div class=\"loading\">\n <div>\n <svg class=\"animate-spin text-white\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\">\n <circle class=\"opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"4\"></circle>\n <path class=\"opacity-75\" fill=\"currentColor\" d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"></path>\n </svg>\n <span>{{ configLoadingText }}</span>\n </div>\n </div>\n</ng-template>", styles: [":host{display:grid;height:100%;-webkit-user-select:none;user-select:none;overflow-x:hidden;--tw-bg-opacity: 1;background-color:rgb(0 67 114 / var(--tw-bg-opacity, 1));transition-property:margin-left,width;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);margin-left:calc(var(--sidebar-collapsed-width) * -1);transition-duration:var(--sidebar-animation-duration);width:var(--sidebar-collapsed-width);will-change:margin-left,width;grid-template-rows:auto minmax(0,1fr);grid-template-areas:\"header\" \"nav\"}@media (min-width: 768px){:host{margin-left:0}:host.expanded{width:var(--sidebar-expanded-width)}:host.expanded .header .logo-container img.collapsed{opacity:0}:host.expanded .header .logo-container img.expanded{opacity:1}:host.expanded .header .profile-container .profile{padding-left:.5rem;opacity:1;width:calc(var(--sidebar-expanded-width) - var(--sidebar-picture-size) - 24px)}:host.expanded .loading i,:host.expanded .error i{margin-left:0;font-size:1.125rem;line-height:1.75rem}:host.expanded .loading svg,:host.expanded .error svg{margin-top:-4px;margin-left:1px;height:1.25rem;width:1.25rem}:host.expanded .loading span,:host.expanded .error span{opacity:1}}:host.active{width:var(--sidebar-expanded-width)}:host.active .header .logo-container img.collapsed{opacity:0}:host.active .header .logo-container img.expanded{opacity:1}:host.active .header .profile-container .profile{padding-left:.5rem;opacity:1;width:calc(var(--sidebar-expanded-width) - var(--sidebar-picture-size) - 24px)}:host.active .loading i,:host.active .error i{margin-left:0;font-size:1.125rem;line-height:1.75rem}:host.active .loading svg,:host.active .error svg{margin-top:-4px;margin-left:1px;height:1.25rem;width:1.25rem}:host.active .loading span,:host.active .error span{opacity:1}:host.expanded,:host.active{margin-left:0}.header{padding-top:.5rem;padding-bottom:.75rem;grid-area:header}.header .logo-container{position:relative;width:100%;overflow:hidden;text-align:center;height:var(--sidebar-logo-height)}.header .logo-container img{margin:auto;max-height:100%;min-width:fit-content;transition-property:opacity;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:var(--sidebar-animation-duration);will-change:opacity}.header .logo-container img.expanded{position:absolute;top:0;left:50%;--tw-translate-x: -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));opacity:0}.header .profile-container{margin-left:.75rem;margin-right:.75rem;margin-top:.5rem;display:flex;flex-wrap:nowrap;height:var(--sidebar-picture-size)}.header .profile-container .picture{flex-shrink:0;overflow:hidden;border-radius:9999px;min-width:var(--sidebar-picture-size);width:var(--sidebar-picture-size)}.header .profile-container .picture img{min-width:var(--sidebar-picture-size)}.header .profile-container .profile{width:0px;overflow:hidden;white-space:nowrap;padding-left:0;opacity:0;transition-timing-function:cubic-bezier(.4,0,.2,1);transition:opacity var(--sidebar-animation-duration) ease-in-out,padding-left var(--sidebar-animation-duration) ease-in-out,width var(--sidebar-animation-duration) ease-in-out;will-change:width,opacity,padding-left}.header .profile-container .profile h4,.header .profile-container .profile h5{overflow:hidden;text-overflow:ellipsis;height:calc(var(--sidebar-picture-size) / 2);animation:text-clip .2s forwards}.header .profile-container .profile h4{font-weight:500;--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.header .profile-container .profile h5{font-size:.875rem;line-height:1.25rem;--tw-text-opacity: 1;color:rgb(209 213 219 / var(--tw-text-opacity, 1))}nav{overflow-y:auto;grid-area:nav;scrollbar-width:thin;scrollbar-color:rgba(0,0,0,.2) transparent}nav::-webkit-scrollbar{height:0px;width:7px}nav::-webkit-scrollbar-track{background-color:transparent}nav::-webkit-scrollbar-thumb{background-color:#0003;border-radius:10px;transition:background .2s ease}nav::-webkit-scrollbar-thumb:hover{background-color:#0000008c}nav:hover::-webkit-scrollbar-thumb{background-color:#00000073}@media screen and (max-width: 480px){nav{scrollbar-width:thin}nav::-webkit-scrollbar{width:5px}}ul,.loading{position:relative;background-color:#002f50cc}ul lib-sidebar-item,.loading,.error{font-weight:500;--tw-text-opacity: 1;color:rgb(229 231 235 / var(--tw-text-opacity, 1))}.loading,.error{overflow:hidden;height:var(--sidebar-item-height);line-height:var(--sidebar-item-height)}.loading>div,.error>div{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;padding-left:.75rem;padding-right:.75rem;text-align:left;width:var(--sidebar-expanded-width)}.loading i,.error i{margin-right:.5rem;font-size:1.25rem;line-height:1.75rem;transition-property:font-size,line-height,margin-left;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);margin-left:var(--sidebar-icon-collapsed-margin);transition-duration:var(--sidebar-animation-duration);will-change:font-size,line-height,margin-left}.loading svg,.error svg{margin-top:-4px;margin-left:.5rem;display:inline;height:1.5rem;width:1.5rem;transition-property:height,margin-left,width;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:var(--sidebar-animation-duration);will-change:height,margin-left,width}.loading svg+span,.error svg+span{margin-left:.6rem}.loading span,.error span{transition-property:opacity;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);opacity:0;transition-duration:var(--sidebar-animation-duration)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: SidebarItemComponent, selector: "lib-sidebar-item", inputs: ["menu", "level"] }] });
3316
+ }
3317
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: SidebarComponent, decorators: [{
3318
+ type: Component,
3319
+ args: [{ selector: 'lib-sidebar', imports: [
3320
+ CommonModule,
3321
+ NgFor,
3322
+ NgIf,
3323
+ SidebarItemComponent,
3324
+ ], host: {
3325
+ '[class.active]': 'isActive',
3326
+ '[class.expanded]': '!isCollapsed',
3327
+ }, template: "<div class=\"header\">\n <div class=\"logo-container\">\n <img class=\"collapsed\" [src]=\"configLogoCollapsedPath\" />\n <img class=\"expanded\" [src]=\"configLogoExpandedPath\" />\n </div>\n\n <div class=\"profile-container\" *ngIf=\"isProfileLoaded\">\n <div class=\"picture\" *ngIf=\"hasProfileImage\">\n <img [src]=\"profile!.image\" />\n </div>\n\n <div class=\"profile\">\n <h4>{{ profile!.name }}</h4>\n <h5>{{ profile!.title }}</h5>\n </div>\n </div>\n</div> \n\n<nav>\n <ul *ngIf=\"!isLoading; else loading\">\n <lib-sidebar-item *ngFor=\"let menu of menus; trackBy: trackByFn\"\n [menu]=\"menu\">\n </lib-sidebar-item>\n\n <div class=\"error\" *ngIf=\"hasFailed\">\n <div>\n <i class=\"fa-solid fa-fw fa-triangle-exclamation\"></i>\n <span>{{ configErrorText }}</span>\n </div>\n </div>\n </ul>\n</nav>\n\n<ng-template #loading>\n <div class=\"loading\">\n <div>\n <svg class=\"animate-spin text-white\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\">\n <circle class=\"opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"4\"></circle>\n <path class=\"opacity-75\" fill=\"currentColor\" d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"></path>\n </svg>\n <span>{{ configLoadingText }}</span>\n </div>\n </div>\n</ng-template>", styles: [":host{display:grid;height:100%;-webkit-user-select:none;user-select:none;overflow-x:hidden;--tw-bg-opacity: 1;background-color:rgb(0 67 114 / var(--tw-bg-opacity, 1));transition-property:margin-left,width;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);margin-left:calc(var(--sidebar-collapsed-width) * -1);transition-duration:var(--sidebar-animation-duration);width:var(--sidebar-collapsed-width);will-change:margin-left,width;grid-template-rows:auto minmax(0,1fr);grid-template-areas:\"header\" \"nav\"}@media (min-width: 768px){:host{margin-left:0}:host.expanded{width:var(--sidebar-expanded-width)}:host.expanded .header .logo-container img.collapsed{opacity:0}:host.expanded .header .logo-container img.expanded{opacity:1}:host.expanded .header .profile-container .profile{padding-left:.5rem;opacity:1;width:calc(var(--sidebar-expanded-width) - var(--sidebar-picture-size) - 24px)}:host.expanded .loading i,:host.expanded .error i{margin-left:0;font-size:1.125rem;line-height:1.75rem}:host.expanded .loading svg,:host.expanded .error svg{margin-top:-4px;margin-left:1px;height:1.25rem;width:1.25rem}:host.expanded .loading span,:host.expanded .error span{opacity:1}}:host.active{width:var(--sidebar-expanded-width)}:host.active .header .logo-container img.collapsed{opacity:0}:host.active .header .logo-container img.expanded{opacity:1}:host.active .header .profile-container .profile{padding-left:.5rem;opacity:1;width:calc(var(--sidebar-expanded-width) - var(--sidebar-picture-size) - 24px)}:host.active .loading i,:host.active .error i{margin-left:0;font-size:1.125rem;line-height:1.75rem}:host.active .loading svg,:host.active .error svg{margin-top:-4px;margin-left:1px;height:1.25rem;width:1.25rem}:host.active .loading span,:host.active .error span{opacity:1}:host.expanded,:host.active{margin-left:0}.header{padding-top:.5rem;padding-bottom:.75rem;grid-area:header}.header .logo-container{position:relative;width:100%;overflow:hidden;text-align:center;height:var(--sidebar-logo-height)}.header .logo-container img{margin:auto;max-height:100%;min-width:fit-content;transition-property:opacity;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:var(--sidebar-animation-duration);will-change:opacity}.header .logo-container img.expanded{position:absolute;top:0;left:50%;--tw-translate-x: -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));opacity:0}.header .profile-container{margin-left:.75rem;margin-right:.75rem;margin-top:.5rem;display:flex;flex-wrap:nowrap;height:var(--sidebar-picture-size)}.header .profile-container .picture{flex-shrink:0;overflow:hidden;border-radius:9999px;min-width:var(--sidebar-picture-size);width:var(--sidebar-picture-size)}.header .profile-container .picture img{min-width:var(--sidebar-picture-size)}.header .profile-container .profile{width:0px;overflow:hidden;white-space:nowrap;padding-left:0;opacity:0;transition-timing-function:cubic-bezier(.4,0,.2,1);transition:opacity var(--sidebar-animation-duration) ease-in-out,padding-left var(--sidebar-animation-duration) ease-in-out,width var(--sidebar-animation-duration) ease-in-out;will-change:width,opacity,padding-left}.header .profile-container .profile h4,.header .profile-container .profile h5{overflow:hidden;text-overflow:ellipsis;height:calc(var(--sidebar-picture-size) / 2);animation:text-clip .2s forwards}.header .profile-container .profile h4{font-weight:500;--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.header .profile-container .profile h5{font-size:.875rem;line-height:1.25rem;--tw-text-opacity: 1;color:rgb(209 213 219 / var(--tw-text-opacity, 1))}nav{overflow-y:auto;grid-area:nav;scrollbar-width:thin;scrollbar-color:rgba(0,0,0,.2) transparent}nav::-webkit-scrollbar{height:0px;width:7px}nav::-webkit-scrollbar-track{background-color:transparent}nav::-webkit-scrollbar-thumb{background-color:#0003;border-radius:10px;transition:background .2s ease}nav::-webkit-scrollbar-thumb:hover{background-color:#0000008c}nav:hover::-webkit-scrollbar-thumb{background-color:#00000073}@media screen and (max-width: 480px){nav{scrollbar-width:thin}nav::-webkit-scrollbar{width:5px}}ul,.loading{position:relative;background-color:#002f50cc}ul lib-sidebar-item,.loading,.error{font-weight:500;--tw-text-opacity: 1;color:rgb(229 231 235 / var(--tw-text-opacity, 1))}.loading,.error{overflow:hidden;height:var(--sidebar-item-height);line-height:var(--sidebar-item-height)}.loading>div,.error>div{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;padding-left:.75rem;padding-right:.75rem;text-align:left;width:var(--sidebar-expanded-width)}.loading i,.error i{margin-right:.5rem;font-size:1.25rem;line-height:1.75rem;transition-property:font-size,line-height,margin-left;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);margin-left:var(--sidebar-icon-collapsed-margin);transition-duration:var(--sidebar-animation-duration);will-change:font-size,line-height,margin-left}.loading svg,.error svg{margin-top:-4px;margin-left:.5rem;display:inline;height:1.5rem;width:1.5rem;transition-property:height,margin-left,width;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:var(--sidebar-animation-duration);will-change:height,margin-left,width}.loading svg+span,.error svg+span{margin-left:.6rem}.loading span,.error span{transition-property:opacity;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);opacity:0;transition-duration:var(--sidebar-animation-duration)}\n"] }]
3328
+ }], ctorParameters: () => [], propDecorators: { onBodyMouseDown: [{
3329
+ type: HostListener,
3330
+ args: ['body:mousedown', ['$event']]
3331
+ }], onBodyMouseUp: [{
3332
+ type: HostListener,
3333
+ args: ['body:mouseup', ['$event']]
3334
+ }], onDocumentKeyDown: [{
3335
+ type: HostListener,
3336
+ args: ['document:keydown.escape', ['$event']]
3337
+ }], onResize: [{
3338
+ type: HostListener,
3339
+ args: ['window:resize', ['$event']]
3340
+ }] } });
3341
+
3342
+ class RibbonButtonComponent {
3343
+ //#region ViewChilds, Inputs, Outputs
3344
+ color = 'text-primary-500';
3345
+ defaultOption = -1;
3346
+ disabled = false;
3347
+ icon;
3348
+ iconSize = 'large';
3349
+ label = '';
3350
+ loading = false;
3351
+ options = [];
3352
+ action = new EventEmitter();
3353
+ //#endregion
3354
+ //#region Host listeners
3355
+ bodyMouseDown(event) {
3356
+ if (this.showDropdown) {
3357
+ const target = event.target;
3358
+ const button = target.closest('.button-container')?.parentElement;
3359
+ this.clickedOutside =
3360
+ event.button === 0
3361
+ && !target.closest('.options-dropdown')
3362
+ && (!button || (!button?.classList.contains('dropdown') && !button?.classList.contains('open')));
3363
+ }
3364
+ }
3365
+ bodyMouseUp(event) {
3366
+ if (this.showDropdown && this.clickedOutside) {
3367
+ this.showDropdown = false;
3368
+ }
3369
+ }
3370
+ documentKeyDown(event) {
3371
+ event = event || window.event;
3372
+ let isEscape = false;
3373
+ if ('key' in event) {
3374
+ isEscape = (event.key === 'Escape' || event.key === 'Esc');
3375
+ }
3376
+ else {
3377
+ isEscape = (event.keyCode === 27);
3378
+ }
3379
+ if (this.showDropdown && isEscape) {
3380
+ this.showDropdown = false;
3381
+ }
3382
+ }
3383
+ //#endregion
3384
+ //#region Variables
3385
+ showDropdown = false;
3386
+ status = null;
3387
+ clickedOutside = false;
3388
+ //#endregion
3389
+ //#region Properties
3390
+ get buttonIcon() {
3391
+ switch (this.status) {
3392
+ case 'success':
3393
+ return 'fa-check';
3394
+ case 'failure':
3395
+ return 'fa-times';
3396
+ case 'warning':
3397
+ return 'fa-exclamation';
3398
+ default:
3399
+ return this.icon ?? '';
3400
+ }
3401
+ }
3402
+ get isButtonDisabled() {
3403
+ return this.disabled || this.loading || (this.options.length > 0 && !this.options.some((option) => this.isOptionVisible(option)));
3404
+ }
3405
+ //#endregion
3406
+ //#region Constructor and Angular life cycle methods
3407
+ //#endregion
3408
+ //#region Event handlers
3409
+ onButtonClicked() {
3410
+ if (this.options.length > 0) {
3411
+ if (this.defaultOption >= 0 && this.defaultOption < this.options.length) {
3412
+ this.showDropdown = false;
3413
+ this.action.emit(this.options[this.defaultOption].id);
3414
+ }
3415
+ else {
3416
+ this.onShowHideDropdown();
3417
+ }
3418
+ }
3419
+ else {
3420
+ this.action.emit();
3421
+ }
3422
+ }
3423
+ onOptionClicked(option) {
3424
+ this.showDropdown = false;
3425
+ this.action.emit(option.id);
3426
+ }
3427
+ onShowHideDropdown() {
3428
+ this.showDropdown = !this.showDropdown;
3429
+ }
3430
+ //#endregion
3431
+ //#region Public methods
3432
+ finishLoading(status) {
3433
+ this.loading = false;
3434
+ this.status = status;
3435
+ setTimeout(() => {
3436
+ this.status = null;
3437
+ }, 1000);
3438
+ }
3439
+ startLoading() {
3440
+ this.loading = true;
3441
+ }
3442
+ isOptionDisabled(option) {
3443
+ return option.isDisabled || ((option.allowedActions?.length ?? 0) > 0 && option.isAccessAllowed === undefined);
3444
+ }
3445
+ isOptionVisible(option) {
3446
+ return (option.isVisible ?? true) && (option.allowedActions === undefined || option.allowedActions.length === 0 || option.isAccessAllowed === true || (option.isAccessAllowed == undefined && option.allowedActions?.length > 0));
3447
+ }
3448
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: RibbonButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3449
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.6", type: RibbonButtonComponent, isStandalone: true, selector: "lib-ribbon-button", inputs: { color: "color", defaultOption: "defaultOption", disabled: "disabled", icon: "icon", iconSize: "iconSize", label: "label", loading: "loading", options: "options" }, outputs: { action: "action" }, host: { listeners: { "body:mousedown": "bodyMouseDown($event)", "body:mouseup": "bodyMouseUp($event)", "document:keydown.escape": "documentKeyDown($event)" }, properties: { "class.dropdown": "options.length > 0", "class.show": "showDropdown" } }, ngImport: i0, template: "<div class=\"button-container\">\n <a class=\"button\"\n [class.disabled]=\"isButtonDisabled\"\n [class.success]=\"status === 'success'\"\n [class.warning]=\"status === 'warning'\"\n [class.failure]=\"status === 'failure'\"\n [class.small]=\"iconSize === 'small'\"\n (click)=\"onButtonClicked()\"\n >\n <span class=\"icon\" [class.loading]=\"loading\">\n <i class=\"fas fa-fw\" [ngClass]=\"[ buttonIcon, color ]\"></i>\n \n <svg [ngClass]=\"[ color ]\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\">\n <circle class=\"opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"4\"></circle>\n <path class=\"opacity-75\" fill=\"currentColor\" d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"></path>\n </svg>\n </span>\n \n <span class=\"label\" [class.text-white]=\"status !== null\">\n {{ label | translate }}\n </span>\n </a>\n \n <a class=\"button-dropdown\" *ngIf=\"options.length > 0\"\n [class.disabled]=\"isButtonDisabled\"\n [class.success]=\"status === 'success'\"\n [class.warning]=\"status === 'warning'\"\n [class.failure]=\"status === 'failure'\"\n [class.small]=\"iconSize === 'small'\"\n (click)=\"onShowHideDropdown()\"\n >\n <i class=\"fas fa-chevron-down\" [ngClass]=\"[ color ]\"></i>\n </a>\n</div>\n\n<ul class=\"options-dropdown\" *ngIf=\"options.length > 0\">\n <li *ngFor=\"let option of options; let i = index\"\n [class.default-option]=\"i === defaultOption\"\n [class.disabled]=\"isOptionDisabled(option)\"\n [class.hidden]=\"!isOptionVisible(option)\"\n (click)=\"onOptionClicked(option)\"\n >\n <i class=\"fas fa-fw\" *ngIf=\"!!option.icon\" [ngClass]=\"[ option.icon, color ]\"></i> {{ option.label | translate }}\n </li>\n</ul>", styles: [":host-context.dropdown .button{border-top-right-radius:0;border-bottom-right-radius:0}.button-container{display:flex}.button{border-radius:.5rem;padding-left:.75rem;padding-right:.75rem}.button.success .icon i,.button.success .label,.button.warning .icon i,.button.warning .label,.button.failure .icon i,.button.failure .label{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.button.small{padding-left:.5rem;padding-right:.5rem}.button.small .icon i{height:1.75rem;font-size:1.5rem;line-height:2rem}@media (min-width: 768px){.button.small .icon i{font-size:1.25rem;line-height:1.75rem}}.button.small .icon svg{height:1.5rem}.button.small .label{font-size:.875rem;line-height:1.25rem}.button .icon{position:relative;display:flex;height:2rem;flex-direction:column;align-items:center;justify-content:center;text-align:center;font-size:2.25rem;line-height:2.5rem}@media (min-width: 768px){.button .icon{font-size:1.875rem;line-height:2.25rem}}.button .icon i{transition-property:all;transition-duration:.2s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.button .icon svg{position:absolute;height:2rem;width:2rem}@keyframes spin{to{transform:rotate(360deg)}}.button .icon svg{animation:spin 1s linear infinite;opacity:0;transition-property:opacity;transition-duration:.2s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.button .icon.loading i{opacity:0}.button .icon.loading svg{opacity:1}.button .label{margin-top:.5rem;text-align:center;--tw-text-opacity: 1;color:rgb(31 41 55 / var(--tw-text-opacity, 1));transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-duration:.2s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.button-dropdown{justify-content:center;border-top-right-radius:.5rem;border-bottom-right-radius:.5rem;padding-left:.25rem;padding-right:.25rem}.button-dropdown.success i,.button-dropdown.warning i,.button-dropdown.failure i{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.button-dropdown.small{font-size:.875rem;line-height:1.25rem}@media (min-width: 768px){.button-dropdown.small{font-size:1rem;line-height:1.5rem}}.button,.button-dropdown{display:flex;cursor:pointer;-webkit-user-select:none;user-select:none;flex-direction:column;padding-top:.5rem;padding-bottom:.5rem;transition-property:all;transition-duration:.2s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.button:hover,.button-dropdown:hover{background-color:#d1d5dbcc}.button:focus,.button-dropdown:focus{background-color:#d1d5dbcc}.button:active,.button-dropdown:active{background-color:#9ca3af99}.button:disabled,.button.disabled,.button-dropdown:disabled,.button-dropdown.disabled{pointer-events:none;opacity:.6}.button.success,.button-dropdown.success{background-color:#16a34abf}.button.warning,.button-dropdown.warning{background-color:#ca8a04bf}.button.failure,.button-dropdown.failure{background-color:#dc2626bf}.button.small,.button-dropdown.small{padding-top:.25rem;padding-bottom:.25rem}ul{z-index:1;min-width:fit-content;-webkit-user-select:none;user-select:none}ul li{cursor:pointer;white-space:nowrap;padding:.5rem 1rem}ul li.default-option{display:none}ul li.disabled{pointer-events:none;opacity:.6}ul li:hover{--tw-bg-opacity: 1;background-color:rgb(230 244 255 / var(--tw-bg-opacity, 1))}ul li i{margin-right:.25rem}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
3450
+ }
3451
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: RibbonButtonComponent, decorators: [{
3452
+ type: Component,
3453
+ args: [{ selector: 'lib-ribbon-button', host: {
3454
+ '[class.dropdown]': 'options.length > 0',
3455
+ '[class.show]': 'showDropdown',
3456
+ }, imports: [
3457
+ NgClass,
3458
+ NgFor,
3459
+ NgIf,
3460
+ TranslatePipe,
3461
+ ], template: "<div class=\"button-container\">\n <a class=\"button\"\n [class.disabled]=\"isButtonDisabled\"\n [class.success]=\"status === 'success'\"\n [class.warning]=\"status === 'warning'\"\n [class.failure]=\"status === 'failure'\"\n [class.small]=\"iconSize === 'small'\"\n (click)=\"onButtonClicked()\"\n >\n <span class=\"icon\" [class.loading]=\"loading\">\n <i class=\"fas fa-fw\" [ngClass]=\"[ buttonIcon, color ]\"></i>\n \n <svg [ngClass]=\"[ color ]\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\">\n <circle class=\"opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"4\"></circle>\n <path class=\"opacity-75\" fill=\"currentColor\" d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"></path>\n </svg>\n </span>\n \n <span class=\"label\" [class.text-white]=\"status !== null\">\n {{ label | translate }}\n </span>\n </a>\n \n <a class=\"button-dropdown\" *ngIf=\"options.length > 0\"\n [class.disabled]=\"isButtonDisabled\"\n [class.success]=\"status === 'success'\"\n [class.warning]=\"status === 'warning'\"\n [class.failure]=\"status === 'failure'\"\n [class.small]=\"iconSize === 'small'\"\n (click)=\"onShowHideDropdown()\"\n >\n <i class=\"fas fa-chevron-down\" [ngClass]=\"[ color ]\"></i>\n </a>\n</div>\n\n<ul class=\"options-dropdown\" *ngIf=\"options.length > 0\">\n <li *ngFor=\"let option of options; let i = index\"\n [class.default-option]=\"i === defaultOption\"\n [class.disabled]=\"isOptionDisabled(option)\"\n [class.hidden]=\"!isOptionVisible(option)\"\n (click)=\"onOptionClicked(option)\"\n >\n <i class=\"fas fa-fw\" *ngIf=\"!!option.icon\" [ngClass]=\"[ option.icon, color ]\"></i> {{ option.label | translate }}\n </li>\n</ul>", styles: [":host-context.dropdown .button{border-top-right-radius:0;border-bottom-right-radius:0}.button-container{display:flex}.button{border-radius:.5rem;padding-left:.75rem;padding-right:.75rem}.button.success .icon i,.button.success .label,.button.warning .icon i,.button.warning .label,.button.failure .icon i,.button.failure .label{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.button.small{padding-left:.5rem;padding-right:.5rem}.button.small .icon i{height:1.75rem;font-size:1.5rem;line-height:2rem}@media (min-width: 768px){.button.small .icon i{font-size:1.25rem;line-height:1.75rem}}.button.small .icon svg{height:1.5rem}.button.small .label{font-size:.875rem;line-height:1.25rem}.button .icon{position:relative;display:flex;height:2rem;flex-direction:column;align-items:center;justify-content:center;text-align:center;font-size:2.25rem;line-height:2.5rem}@media (min-width: 768px){.button .icon{font-size:1.875rem;line-height:2.25rem}}.button .icon i{transition-property:all;transition-duration:.2s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.button .icon svg{position:absolute;height:2rem;width:2rem}@keyframes spin{to{transform:rotate(360deg)}}.button .icon svg{animation:spin 1s linear infinite;opacity:0;transition-property:opacity;transition-duration:.2s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.button .icon.loading i{opacity:0}.button .icon.loading svg{opacity:1}.button .label{margin-top:.5rem;text-align:center;--tw-text-opacity: 1;color:rgb(31 41 55 / var(--tw-text-opacity, 1));transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-duration:.2s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.button-dropdown{justify-content:center;border-top-right-radius:.5rem;border-bottom-right-radius:.5rem;padding-left:.25rem;padding-right:.25rem}.button-dropdown.success i,.button-dropdown.warning i,.button-dropdown.failure i{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.button-dropdown.small{font-size:.875rem;line-height:1.25rem}@media (min-width: 768px){.button-dropdown.small{font-size:1rem;line-height:1.5rem}}.button,.button-dropdown{display:flex;cursor:pointer;-webkit-user-select:none;user-select:none;flex-direction:column;padding-top:.5rem;padding-bottom:.5rem;transition-property:all;transition-duration:.2s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.button:hover,.button-dropdown:hover{background-color:#d1d5dbcc}.button:focus,.button-dropdown:focus{background-color:#d1d5dbcc}.button:active,.button-dropdown:active{background-color:#9ca3af99}.button:disabled,.button.disabled,.button-dropdown:disabled,.button-dropdown.disabled{pointer-events:none;opacity:.6}.button.success,.button-dropdown.success{background-color:#16a34abf}.button.warning,.button-dropdown.warning{background-color:#ca8a04bf}.button.failure,.button-dropdown.failure{background-color:#dc2626bf}.button.small,.button-dropdown.small{padding-top:.25rem;padding-bottom:.25rem}ul{z-index:1;min-width:fit-content;-webkit-user-select:none;user-select:none}ul li{cursor:pointer;white-space:nowrap;padding:.5rem 1rem}ul li.default-option{display:none}ul li.disabled{pointer-events:none;opacity:.6}ul li:hover{--tw-bg-opacity: 1;background-color:rgb(230 244 255 / var(--tw-bg-opacity, 1))}ul li i{margin-right:.25rem}\n"] }]
3462
+ }], propDecorators: { color: [{
3463
+ type: Input
3464
+ }], defaultOption: [{
3465
+ type: Input
3466
+ }], disabled: [{
3467
+ type: Input
3468
+ }], icon: [{
3469
+ type: Input
3470
+ }], iconSize: [{
3471
+ type: Input
3472
+ }], label: [{
3473
+ type: Input
3474
+ }], loading: [{
3475
+ type: Input
3476
+ }], options: [{
3477
+ type: Input
3478
+ }], action: [{
3479
+ type: Output
3480
+ }], bodyMouseDown: [{
3481
+ type: HostListener,
3482
+ args: ['body:mousedown', ['$event']]
3483
+ }], bodyMouseUp: [{
3484
+ type: HostListener,
3485
+ args: ['body:mouseup', ['$event']]
3486
+ }], documentKeyDown: [{
3487
+ type: HostListener,
3488
+ args: ['document:keydown.escape', ['$event']]
3489
+ }] } });
3490
+
3491
+ class RibbonComponent {
3492
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: RibbonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3493
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.6", type: RibbonComponent, isStandalone: true, selector: "lib-ribbon", ngImport: i0, template: "<div>\n <ng-content></ng-content>\n</div>\n\n<ng-content select=\"[right]\"></ng-content>", styles: [":host-context{position:relative;display:flex;flex-direction:row;border-radius:.5rem;border-width:1px;padding:.25rem;border-color:#d1d5dbe6;background-color:#e5e7ebcc;justify-content:space-between}:host-context div{display:inline-flex;gap:.5rem}:host-context ::ng-deep *[right]{display:flex}:host-context ::ng-deep *[right] lib-ribbon-group{margin-right:.5rem;margin-left:0}:host-context ::ng-deep *[right] lib-ribbon-group:first-child{margin-right:0;padding-right:0;padding-left:.5rem}:host-context ::ng-deep *[right] lib-ribbon-group:after{left:-.25rem}:host-context ::ng-deep *[right] lib-ribbon-group lib-ribbon-button .options-dropdown{right:0}\n"] });
3494
+ }
3495
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: RibbonComponent, decorators: [{
3496
+ type: Component,
3497
+ args: [{ selector: 'lib-ribbon', template: "<div>\n <ng-content></ng-content>\n</div>\n\n<ng-content select=\"[right]\"></ng-content>", styles: [":host-context{position:relative;display:flex;flex-direction:row;border-radius:.5rem;border-width:1px;padding:.25rem;border-color:#d1d5dbe6;background-color:#e5e7ebcc;justify-content:space-between}:host-context div{display:inline-flex;gap:.5rem}:host-context ::ng-deep *[right]{display:flex}:host-context ::ng-deep *[right] lib-ribbon-group{margin-right:.5rem;margin-left:0}:host-context ::ng-deep *[right] lib-ribbon-group:first-child{margin-right:0;padding-right:0;padding-left:.5rem}:host-context ::ng-deep *[right] lib-ribbon-group:after{left:-.25rem}:host-context ::ng-deep *[right] lib-ribbon-group lib-ribbon-button .options-dropdown{right:0}\n"] }]
3498
+ }] });
3499
+
3500
+ class RibbonGroupComponent {
3501
+ //#region ViewChilds, Inputs, Outputs
3502
+ children;
3503
+ label = '';
3504
+ //#endregion
3505
+ //#region Host listeners
3506
+ //#endregion
3507
+ //#region Variables
3508
+ //#endregion
3509
+ //#region Properties
3510
+ get hasChildren() {
3511
+ return this.children?.some(x => x.visible) ?? false;
3512
+ }
3513
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: RibbonGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3514
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.6", type: RibbonGroupComponent, isStandalone: true, selector: "lib-ribbon-group", inputs: { label: "label" }, host: { properties: { "class.hidden": "!hasChildren" } }, queries: [{ propertyName: "children", predicate: RibbonGroupChild, descendants: true }], ngImport: i0, template: "<div class=\"content\">\n <ng-content></ng-content>\n</div>\n\n<div class=\"label\">\n <span>{{ label }}</span>\n</div>", styles: [":host-context{position:relative;padding-right:.5rem}:host-context:after{position:absolute;right:-.25rem;top:.25rem;bottom:.25rem;width:1px;--tw-content: \"\";content:var(--tw-content);background-color:#9ca3af99}:host-context .content{display:flex;flex-direction:row;flex-wrap:nowrap}:host-context .label{-webkit-user-select:none;user-select:none;padding-top:.05rem;text-align:center;font-size:.875rem;line-height:1.25rem;--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity, 1))}\n"] });
3515
+ }
3516
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: RibbonGroupComponent, decorators: [{
3517
+ type: Component,
3518
+ args: [{ selector: 'lib-ribbon-group', host: { '[class.hidden]': '!hasChildren' }, template: "<div class=\"content\">\n <ng-content></ng-content>\n</div>\n\n<div class=\"label\">\n <span>{{ label }}</span>\n</div>", styles: [":host-context{position:relative;padding-right:.5rem}:host-context:after{position:absolute;right:-.25rem;top:.25rem;bottom:.25rem;width:1px;--tw-content: \"\";content:var(--tw-content);background-color:#9ca3af99}:host-context .content{display:flex;flex-direction:row;flex-wrap:nowrap}:host-context .label{-webkit-user-select:none;user-select:none;padding-top:.05rem;text-align:center;font-size:.875rem;line-height:1.25rem;--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity, 1))}\n"] }]
3519
+ }], propDecorators: { children: [{
3520
+ type: ContentChildren,
3521
+ args: [RibbonGroupChild, { descendants: true }]
3522
+ }], label: [{
3523
+ type: Input
3524
+ }] } });
3525
+
3526
+ class EnumTranslatePipe {
3527
+ //#region ViewChilds, Inputs, Outputs
3528
+ //#endregion
3529
+ //#region Variables
3530
+ translate = inject(TranslateService);
3531
+ //#endregion
3532
+ //#region Properties
3533
+ //#endregion
3534
+ //#region Constructor and Angular life cycle methods
3535
+ //#endregion
3536
+ //#region Event handlers
3537
+ //#endregion
3538
+ //#region Public methods
3539
+ transform(enumType) {
3540
+ return Object.keys(enumType)
3541
+ .filter(key => isNaN(Number(key)))
3542
+ .map(key => ({
3543
+ value: enumType[key],
3544
+ display: this.translate.get(key),
3545
+ }));
3546
+ }
3547
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: EnumTranslatePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
3548
+ static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.1.6", ngImport: i0, type: EnumTranslatePipe, isStandalone: true, name: "enumTranslate" });
3549
+ }
3550
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: EnumTranslatePipe, decorators: [{
3551
+ type: Pipe,
3552
+ args: [{
3553
+ name: 'enumTranslate'
3554
+ }]
3555
+ }] });
3556
+
3557
+ class ReplaceManyPipe {
3558
+ //#region ViewChilds, Inputs, Outputs
3559
+ //#endregion
3560
+ //#region Variables
3561
+ //#endregion
3562
+ //#region Properties
3563
+ //#endregion
3564
+ //#region Constructor and Angular life cycle methods
3565
+ //#endregion
3566
+ //#region Event handlers
3567
+ //#endregion
3568
+ //#region Public methods
3569
+ transform(message, replaceArgs) {
3570
+ if (replaceArgs) {
3571
+ for (const [key, value] of Object.entries(replaceArgs)) {
3572
+ message = message.replace(key, value ?? '');
3573
+ }
3574
+ }
3575
+ return message;
3576
+ }
3577
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: ReplaceManyPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
3578
+ static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.1.6", ngImport: i0, type: ReplaceManyPipe, isStandalone: true, name: "replaceMany" });
3579
+ }
3580
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: ReplaceManyPipe, decorators: [{
3581
+ type: Pipe,
3582
+ args: [{
3583
+ name: 'replaceMany'
3584
+ }]
3585
+ }] });
3586
+
3587
+ class ReplacePipe {
3588
+ //#region ViewChilds, Inputs, Outputs
3589
+ //#endregion
3590
+ //#region Variables
3591
+ //#endregion
3592
+ //#region Properties
3593
+ //#endregion
3594
+ //#region Constructor and Angular life cycle methods
3595
+ //#endregion
3596
+ //#region Event handlers
3597
+ //#endregion
3598
+ //#region Public methods
3599
+ transform(message, searchValue, replaceValue) {
3600
+ return message.replace(searchValue, replaceValue ?? '');
3601
+ }
3602
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: ReplacePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
3603
+ static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.1.6", ngImport: i0, type: ReplacePipe, isStandalone: true, name: "replace" });
3604
+ }
3605
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: ReplacePipe, decorators: [{
3606
+ type: Pipe,
3607
+ args: [{
3608
+ name: 'replace'
3609
+ }]
3610
+ }] });
3611
+
3612
+ /**
3613
+ * @deprecated Use standalone component imports instead of LibraryModule.
3614
+ * Import individual components directly in your component's `imports` array.
3615
+ */
3616
+ class LibraryModule {
3617
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: LibraryModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
3618
+ static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.1.6", ngImport: i0, type: LibraryModule, imports: [CatalogSelectComponent, DataGridComponent, DataGridRowComponent, FormInputComponent, FormInputGroupComponent, FormGroupComponent, GroupAccordionComponent, GroupContainerComponent, GroupScrollSpyComponent, SidebarComponent, ModalComponent, MultiEditorComponent, MultiSelectComponent, MultiSelectResultGridComponent, RibbonComponent, RibbonButtonComponent, RibbonGroupComponent, ScrollSpyDirective, EnumTranslatePipe, ReplacePipe, ReplaceManyPipe], exports: [CatalogSelectComponent, DataGridComponent, DataGridRowComponent, FormInputComponent, FormInputGroupComponent, FormGroupComponent, GroupAccordionComponent, GroupContainerComponent, GroupScrollSpyComponent, SidebarComponent, ModalComponent, MultiEditorComponent, MultiSelectComponent, MultiSelectResultGridComponent, RibbonComponent, RibbonButtonComponent, RibbonGroupComponent, ScrollSpyDirective, EnumTranslatePipe, ReplacePipe, ReplaceManyPipe] });
3619
+ static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: LibraryModule, imports: [CatalogSelectComponent,
3620
+ DataGridComponent,
3621
+ DataGridRowComponent,
3622
+ FormInputComponent,
3623
+ FormInputGroupComponent,
3624
+ SidebarComponent,
3625
+ MultiEditorComponent,
3626
+ MultiSelectComponent,
3627
+ MultiSelectResultGridComponent] });
3628
+ }
3629
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: LibraryModule, decorators: [{
3630
+ type: NgModule,
3631
+ args: [{
3632
+ declarations: [],
3633
+ imports: [
3634
+ CatalogSelectComponent,
3635
+ DataGridComponent,
3636
+ DataGridRowComponent,
3637
+ FormInputComponent,
3638
+ FormInputGroupComponent,
3639
+ FormGroupComponent,
3640
+ GroupAccordionComponent,
3641
+ GroupContainerComponent,
3642
+ GroupScrollSpyComponent,
3643
+ SidebarComponent,
3644
+ ModalComponent,
3645
+ MultiEditorComponent,
3646
+ MultiSelectComponent,
3647
+ MultiSelectResultGridComponent,
3648
+ RibbonComponent,
3649
+ RibbonButtonComponent,
3650
+ RibbonGroupComponent,
3651
+ ScrollSpyDirective,
3652
+ EnumTranslatePipe,
3653
+ ReplacePipe,
3654
+ ReplaceManyPipe,
3655
+ ],
3656
+ exports: [
3657
+ CatalogSelectComponent,
3658
+ DataGridComponent,
3659
+ DataGridRowComponent,
3660
+ FormInputComponent,
3661
+ FormInputGroupComponent,
3662
+ FormGroupComponent,
3663
+ GroupAccordionComponent,
3664
+ GroupContainerComponent,
3665
+ GroupScrollSpyComponent,
3666
+ SidebarComponent,
3667
+ ModalComponent,
3668
+ MultiEditorComponent,
3669
+ MultiSelectComponent,
3670
+ MultiSelectResultGridComponent,
3671
+ RibbonComponent,
3672
+ RibbonButtonComponent,
3673
+ RibbonGroupComponent,
3674
+ ScrollSpyDirective,
3675
+ EnumTranslatePipe,
3676
+ ReplacePipe,
3677
+ ReplaceManyPipe,
3678
+ ]
3679
+ }]
3680
+ }] });
3681
+
3682
+ class DataGridConfigsProvider extends GridConfigsProvider {
3683
+ //#region ViewChilds, Inputs, Outputs
3684
+ //#endregion
3685
+ //#region Variables
3686
+ //#endregion
3687
+ //#region Properties
3688
+ //#endregion
3689
+ //#region Constructor and Angular life cycle methods
3690
+ constructor() {
3691
+ super();
3692
+ this._configs =
3693
+ {
3694
+ ...this._configs,
3695
+ multiSelect: false,
3696
+ multiSelectSize: 'minmax(1.5rem, min-content)',
3697
+ };
3698
+ }
3699
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: DataGridConfigsProvider, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
3700
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: DataGridConfigsProvider });
3701
+ }
3702
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: DataGridConfigsProvider, decorators: [{
3703
+ type: Injectable
3704
+ }], ctorParameters: () => [] });
3705
+
3706
+ class DateValidators {
3707
+ //#region Public methods
3708
+ static greaterThanControl(controlName) {
3709
+ return (control) => {
3710
+ const compareControl = control.parent?.get(controlName);
3711
+ if (!!control.value && !!compareControl?.value && new Date(control.value) <= new Date(compareControl?.value)) {
3712
+ return { 'greaterThanControl': true };
3713
+ }
3714
+ return null;
3715
+ };
3716
+ }
3717
+ static greaterThanOrEqualControl(controlName) {
3718
+ return (control) => {
3719
+ const compareControl = control.parent?.get(controlName);
3720
+ if (!!control.value && !!compareControl?.value && new Date(control.value) < new Date(compareControl?.value)) {
3721
+ return { 'greaterThanOrEqualControl': true };
3722
+ }
3723
+ return null;
3724
+ };
3725
+ }
3726
+ static greaterThanOrEqualToday(control) {
3727
+ let today = new Date();
3728
+ if (!!control.value && new Date(control.value) < today) {
3729
+ return { 'greaterThanOrEqualToday': true };
3730
+ }
3731
+ return null;
3732
+ }
3733
+ static greaterThanToday(control) {
3734
+ let today = new Date();
3735
+ if (!!control.value && new Date(control.value) <= today) {
3736
+ return { 'greaterThanToday': true };
3737
+ }
3738
+ return null;
3739
+ }
3740
+ static lessThanControl(controlName) {
3741
+ return (control) => {
3742
+ const compareControl = control.parent?.get(controlName);
3743
+ if (!!control.value && !!compareControl?.value && new Date(control.value) >= new Date(compareControl?.value)) {
3744
+ return { 'lessThanControl': true };
3745
+ }
3746
+ return null;
3747
+ };
3748
+ }
3749
+ static lessThanOrEqualControl(controlName) {
3750
+ return (control) => {
3751
+ const compareControl = control.parent?.get(controlName);
3752
+ if (!!control.value && !!compareControl?.value && new Date(control.value) > new Date(compareControl?.value)) {
3753
+ return { 'lessThanOrEqualControl': true };
3754
+ }
3755
+ return null;
3756
+ };
3757
+ }
3758
+ static lessThanOrEqualToday(control) {
3759
+ let today = new Date();
3760
+ if (!!control.value && new Date(control.value) > today) {
3761
+ return { 'lessThanOrEqualToday': true };
3762
+ }
3763
+ return null;
3764
+ }
3765
+ static lessThanToday(control) {
3766
+ let today = new Date();
3767
+ if (!!control.value && new Date(control.value) >= today) {
3768
+ return { 'lessThanToday': true };
3769
+ }
3770
+ return null;
3771
+ }
3772
+ static maxDate(maxDate) {
3773
+ return (control) => {
3774
+ if (!!control.value && new Date(control.value) > maxDate) {
3775
+ return { 'maxDate': true };
3776
+ }
3777
+ return null;
3778
+ };
3779
+ }
3780
+ static minDate(minDate) {
3781
+ return (control) => {
3782
+ if (!!control.value && new Date(control.value) < minDate) {
3783
+ return { 'minDate': true };
3784
+ }
3785
+ return null;
3786
+ };
3787
+ }
3788
+ }
3789
+
3790
+ /**
3791
+ * Generated bundle index. Do not edit.
3792
+ */
3793
+
3794
+ export { BaseComponent, BaseDataset, CatalogSelectComponent, CatalogService, DataGridComponent, DataGridConfigsProvider, DataGridDataset, DataGridRowComponent, DataProviderService, DateHelpers, DateValidators, EnumTranslatePipe, FormGroupComponent, FormGroupService, FormInputComponent, FormInputGroupComponent, FormService, GridConfigsProvider, GridDataset, GroupAccordionComponent, GroupContainerComponent, GroupScrollSpyComponent, GuidHelper, LibraryModule, ModalComponent, MultiEditorComponent, MultiEditorDataset, MultiSelectComponent, MultiSelectResultDataset, MultiSelectResultGridComponent, ReplaceManyPipe, ReplacePipe, RibbonButtonComponent, RibbonComponent, RibbonGroupChild, RibbonGroupComponent, RouterFormatter, SIDEBAR_CONFIGS, ScrollSpyDirective, SidebarComponent, SidebarConfigs, SidebarMenu, SidebarService };
3795
+ //# sourceMappingURL=zambon-dev-library.mjs.map