@weeehaoooz/talos-ui 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/fesm2022/weeehaoooz-talos-ui-button-button-group.mjs +25 -4
  2. package/fesm2022/weeehaoooz-talos-ui-button-button-group.mjs.map +1 -1
  3. package/fesm2022/weeehaoooz-talos-ui-button-button.mjs +2 -1
  4. package/fesm2022/weeehaoooz-talos-ui-button-button.mjs.map +1 -1
  5. package/fesm2022/weeehaoooz-talos-ui-data-display-data-grid.mjs +1627 -0
  6. package/fesm2022/weeehaoooz-talos-ui-data-display-data-grid.mjs.map +1 -0
  7. package/fesm2022/weeehaoooz-talos-ui-form-range-input.mjs +2 -2
  8. package/fesm2022/weeehaoooz-talos-ui-form-range-input.mjs.map +1 -1
  9. package/fesm2022/weeehaoooz-talos-ui-form-rating.mjs +2 -2
  10. package/fesm2022/weeehaoooz-talos-ui-form-rating.mjs.map +1 -1
  11. package/fesm2022/weeehaoooz-talos-ui-layout.mjs +4 -4
  12. package/fesm2022/weeehaoooz-talos-ui-layout.mjs.map +1 -1
  13. package/fesm2022/weeehaoooz-talos-ui-nav.mjs +118 -71
  14. package/fesm2022/weeehaoooz-talos-ui-nav.mjs.map +1 -1
  15. package/fesm2022/weeehaoooz-talos-ui.mjs +1765 -87
  16. package/fesm2022/weeehaoooz-talos-ui.mjs.map +1 -1
  17. package/package.json +5 -1
  18. package/styles/_button-group.scss +154 -6
  19. package/styles/_buttons.scss +105 -8
  20. package/styles/_variables.scss +4 -0
  21. package/types/weeehaoooz-talos-ui-button-button-group.d.ts +5 -2
  22. package/types/weeehaoooz-talos-ui-button-button-group.d.ts.map +1 -1
  23. package/types/weeehaoooz-talos-ui-button-button.d.ts +1 -1
  24. package/types/weeehaoooz-talos-ui-button-button.d.ts.map +1 -1
  25. package/types/weeehaoooz-talos-ui-data-display-data-grid.d.ts +272 -0
  26. package/types/weeehaoooz-talos-ui-data-display-data-grid.d.ts.map +1 -0
  27. package/types/weeehaoooz-talos-ui-nav.d.ts +36 -10
  28. package/types/weeehaoooz-talos-ui-nav.d.ts.map +1 -1
  29. package/types/weeehaoooz-talos-ui.d.ts +309 -16
  30. package/types/weeehaoooz-talos-ui.d.ts.map +1 -1
@@ -0,0 +1,1627 @@
1
+ import * as i0 from '@angular/core';
2
+ import { input, computed, Component, output, signal, Pipe, effect, NgModule } from '@angular/core';
3
+ import { TalosStatusTagComponent } from '@weeehaoooz/talos-ui/data-display/status-tag';
4
+ import { NgComponentOutlet, NgTemplateOutlet } from '@angular/common';
5
+ import { TalosButtonDirective } from '@weeehaoooz/talos-ui/button/button';
6
+ import { TalosTooltipDirective } from '@weeehaoooz/talos-ui/feedback/tooltip';
7
+ import * as i1 from '@angular/forms';
8
+ import { FormsModule } from '@angular/forms';
9
+ import { LucideSearch, LucideCheck, LucideChevronDown, LucideX, LucideArrowUp, LucideArrowDown, LucideArrowUpDown, LucideChevronLeft, LucideChevronRight, LucideChevronsLeft, LucideChevronsRight, LucideInbox, LucideLoader2, LucideFilter } from '@lucide/angular';
10
+ import { DatePickerComponent } from '@weeehaoooz/talos-ui/form/date-picker';
11
+ import * as i1$1 from '@angular/cdk/overlay';
12
+ import { OverlayModule, CdkConnectedOverlay, CdkOverlayOrigin } from '@angular/cdk/overlay';
13
+
14
+ class TalosGridStatusCellComponent {
15
+ row = input.required(/* @ts-ignore */
16
+ ...(ngDevMode ? [{ debugName: "row" }] : /* istanbul ignore next */ []));
17
+ column = input.required(/* @ts-ignore */
18
+ ...(ngDevMode ? [{ debugName: "column" }] : /* istanbul ignore next */ []));
19
+ rawValue = input(/* @ts-ignore */
20
+ ...(ngDevMode ? [undefined, { debugName: "rawValue" }] : /* istanbul ignore next */ []));
21
+ value = computed(() => {
22
+ const directVal = this.rawValue();
23
+ if (directVal !== undefined) {
24
+ return directVal;
25
+ }
26
+ const r = this.row();
27
+ const col = this.column();
28
+ if (!r || !col?.field)
29
+ return '';
30
+ return r[col.field];
31
+ }, /* @ts-ignore */
32
+ ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
33
+ status = computed(() => {
34
+ const val = this.value();
35
+ const r = this.row();
36
+ const config = this.column().statusConfig;
37
+ if (config?.statusMap) {
38
+ return config.statusMap(val, r);
39
+ }
40
+ return (val ?? 'PENDING');
41
+ }, /* @ts-ignore */
42
+ ...(ngDevMode ? [{ debugName: "status" }] : /* istanbul ignore next */ []));
43
+ label = computed(() => {
44
+ const val = this.value();
45
+ const r = this.row();
46
+ const col = this.column();
47
+ if (col.formatter) {
48
+ return col.formatter(val, r);
49
+ }
50
+ return String(val ?? '');
51
+ }, /* @ts-ignore */
52
+ ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
53
+ variant = computed(() => {
54
+ return this.column().statusConfig?.variant ?? 'subtle';
55
+ }, /* @ts-ignore */
56
+ ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
57
+ size = computed(() => {
58
+ return this.column().statusConfig?.size ?? 'sm';
59
+ }, /* @ts-ignore */
60
+ ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
61
+ shape = computed(() => {
62
+ return this.column().statusConfig?.shape ?? 'rounded';
63
+ }, /* @ts-ignore */
64
+ ...(ngDevMode ? [{ debugName: "shape" }] : /* istanbul ignore next */ []));
65
+ showIcon = computed(() => {
66
+ return this.column().statusConfig?.showIcon ?? true;
67
+ }, /* @ts-ignore */
68
+ ...(ngDevMode ? [{ debugName: "showIcon" }] : /* istanbul ignore next */ []));
69
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: TalosGridStatusCellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
70
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.1", type: TalosGridStatusCellComponent, isStandalone: true, selector: "talos-grid-status-cell", inputs: { row: { classPropertyName: "row", publicName: "row", isSignal: true, isRequired: true, transformFunction: null }, column: { classPropertyName: "column", publicName: "column", isSignal: true, isRequired: true, transformFunction: null }, rawValue: { classPropertyName: "rawValue", publicName: "rawValue", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "talos-grid-status-cell" }, ngImport: i0, template: `
71
+ <talos-status-tag
72
+ [status]="status()"
73
+ [variant]="variant()"
74
+ [size]="size()"
75
+ [shape]="shape()"
76
+ [showIcon]="showIcon()"
77
+ >
78
+ {{ label() }}
79
+ </talos-status-tag>
80
+ `, isInline: true, dependencies: [{ kind: "component", type: TalosStatusTagComponent, selector: "talos-status-tag, [talosStatusTag]", inputs: ["status", "label", "variant", "size", "shape", "showIcon", "icon", "iconOnly", "pulse", "ariaLabel"] }] });
81
+ }
82
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: TalosGridStatusCellComponent, decorators: [{
83
+ type: Component,
84
+ args: [{
85
+ selector: 'talos-grid-status-cell',
86
+ imports: [TalosStatusTagComponent],
87
+ template: `
88
+ <talos-status-tag
89
+ [status]="status()"
90
+ [variant]="variant()"
91
+ [size]="size()"
92
+ [shape]="shape()"
93
+ [showIcon]="showIcon()"
94
+ >
95
+ {{ label() }}
96
+ </talos-status-tag>
97
+ `,
98
+ host: {
99
+ class: 'talos-grid-status-cell',
100
+ },
101
+ }]
102
+ }], propDecorators: { row: [{ type: i0.Input, args: [{ isSignal: true, alias: "row", required: true }] }], column: [{ type: i0.Input, args: [{ isSignal: true, alias: "column", required: true }] }], rawValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "rawValue", required: false }] }] } });
103
+
104
+ class TalosGridActionsCellComponent {
105
+ row = input.required(/* @ts-ignore */
106
+ ...(ngDevMode ? [{ debugName: "row" }] : /* istanbul ignore next */ []));
107
+ rowIndex = input.required(/* @ts-ignore */
108
+ ...(ngDevMode ? [{ debugName: "rowIndex" }] : /* istanbul ignore next */ []));
109
+ actions = input([], /* @ts-ignore */
110
+ ...(ngDevMode ? [{ debugName: "actions" }] : /* istanbul ignore next */ []));
111
+ actionClick = output();
112
+ visibleActions() {
113
+ const r = this.row();
114
+ return (this.actions() || []).filter((a) => {
115
+ if (typeof a.hidden === 'function') {
116
+ return !a.hidden(r);
117
+ }
118
+ return !a.hidden;
119
+ });
120
+ }
121
+ isActionDisabled(action) {
122
+ if (typeof action.disabled === 'function') {
123
+ return action.disabled(this.row());
124
+ }
125
+ return !!action.disabled;
126
+ }
127
+ getActionTooltip(action) {
128
+ if (typeof action.tooltip === 'function') {
129
+ return action.tooltip(this.row());
130
+ }
131
+ return action.tooltip || '';
132
+ }
133
+ onActionClick(action, event) {
134
+ event.stopPropagation();
135
+ if (this.isActionDisabled(action))
136
+ return;
137
+ if (action.onClick) {
138
+ action.onClick(this.row(), this.rowIndex(), event);
139
+ }
140
+ this.actionClick.emit({
141
+ actionId: action.id,
142
+ row: this.row(),
143
+ index: this.rowIndex(),
144
+ });
145
+ }
146
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: TalosGridActionsCellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
147
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: TalosGridActionsCellComponent, isStandalone: true, selector: "talos-grid-actions-cell", inputs: { row: { classPropertyName: "row", publicName: "row", isSignal: true, isRequired: true, transformFunction: null }, rowIndex: { classPropertyName: "rowIndex", publicName: "rowIndex", isSignal: true, isRequired: true, transformFunction: null }, actions: { classPropertyName: "actions", publicName: "actions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { actionClick: "actionClick" }, host: { classAttribute: "talos-grid-actions-cell" }, ngImport: i0, template: `
148
+ <div class="talos-grid-actions-wrapper">
149
+ @for (action of visibleActions(); track action.id) {
150
+ <button
151
+ talosButton
152
+ type="button"
153
+ [variant]="action.variant ?? 'ghost'"
154
+ [size]="action.size ?? 'xs'"
155
+ [iconOnly]="action.iconOnly ?? !action.label"
156
+ [disabled]="isActionDisabled(action)"
157
+ [talosTooltip]="getActionTooltip(action)"
158
+ [tooltipDisabled]="!getActionTooltip(action)"
159
+ (click)="onActionClick(action, $event)"
160
+ >
161
+ @if (action.icon) {
162
+ <ng-container *ngComponentOutlet="action.icon" />
163
+ }
164
+ @if (action.label && !action.iconOnly) {
165
+ <span class="action-label">{{ action.label }}</span>
166
+ }
167
+ </button>
168
+ }
169
+ </div>
170
+ `, isInline: true, styles: [".talos-grid-actions-wrapper{display:inline-flex;align-items:center;gap:4px;flex-wrap:nowrap}.action-label{margin-left:2px;font-size:11.5px;font-weight:500}\n"], dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "directive", type: TalosButtonDirective, selector: "button[talosButton], a[talosButton], button[talos-btn], a[talos-btn]", inputs: ["variant", "size", "iconOnly", "fullWidth", "pill", "loading", "disabled", "animation", "spinning", "popping", "feedback"] }, { kind: "directive", type: TalosTooltipDirective, selector: "[talosTooltip], [tooltip]", inputs: ["talosTooltip", "tooltip", "tooltipPosition", "tooltipDisabled"] }] });
171
+ }
172
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: TalosGridActionsCellComponent, decorators: [{
173
+ type: Component,
174
+ args: [{ selector: 'talos-grid-actions-cell', imports: [NgComponentOutlet, TalosButtonDirective, TalosTooltipDirective], template: `
175
+ <div class="talos-grid-actions-wrapper">
176
+ @for (action of visibleActions(); track action.id) {
177
+ <button
178
+ talosButton
179
+ type="button"
180
+ [variant]="action.variant ?? 'ghost'"
181
+ [size]="action.size ?? 'xs'"
182
+ [iconOnly]="action.iconOnly ?? !action.label"
183
+ [disabled]="isActionDisabled(action)"
184
+ [talosTooltip]="getActionTooltip(action)"
185
+ [tooltipDisabled]="!getActionTooltip(action)"
186
+ (click)="onActionClick(action, $event)"
187
+ >
188
+ @if (action.icon) {
189
+ <ng-container *ngComponentOutlet="action.icon" />
190
+ }
191
+ @if (action.label && !action.iconOnly) {
192
+ <span class="action-label">{{ action.label }}</span>
193
+ }
194
+ </button>
195
+ }
196
+ </div>
197
+ `, host: {
198
+ class: 'talos-grid-actions-cell',
199
+ }, styles: [".talos-grid-actions-wrapper{display:inline-flex;align-items:center;gap:4px;flex-wrap:nowrap}.action-label{margin-left:2px;font-size:11.5px;font-weight:500}\n"] }]
200
+ }], propDecorators: { row: [{ type: i0.Input, args: [{ isSignal: true, alias: "row", required: true }] }], rowIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowIndex", required: true }] }], actions: [{ type: i0.Input, args: [{ isSignal: true, alias: "actions", required: false }] }], actionClick: [{ type: i0.Output, args: ["actionClick"] }] } });
201
+
202
+ class TalosGridFilterDialogComponent {
203
+ column = input.required(/* @ts-ignore */
204
+ ...(ngDevMode ? [{ debugName: "column" }] : /* istanbul ignore next */ []));
205
+ distinctValues = input([], /* @ts-ignore */
206
+ ...(ngDevMode ? [{ debugName: "distinctValues" }] : /* istanbul ignore next */ []));
207
+ currentFilter = input(undefined, /* @ts-ignore */
208
+ ...(ngDevMode ? [{ debugName: "currentFilter" }] : /* istanbul ignore next */ []));
209
+ align = input('left', /* @ts-ignore */
210
+ ...(ngDevMode ? [{ debugName: "align" }] : /* istanbul ignore next */ []));
211
+ filterApplied = output();
212
+ filterCleared = output();
213
+ closed = output();
214
+ // Template icons
215
+ searchIcon = LucideSearch;
216
+ checkIcon = LucideCheck;
217
+ chevronDownIcon = LucideChevronDown;
218
+ xIcon = LucideX;
219
+ // Filter mode derived from column:
220
+ // Mode A: text, select, status
221
+ // Mode B: number, date
222
+ filterMode = computed(() => {
223
+ const col = this.column();
224
+ if (col.filterMode)
225
+ return col.filterMode;
226
+ if (col.type === 'number' ||
227
+ col.sortType === 'number' ||
228
+ col.type === 'date' ||
229
+ col.sortType === 'date') {
230
+ return 'condition';
231
+ }
232
+ return 'set';
233
+ }, /* @ts-ignore */
234
+ ...(ngDevMode ? [{ debugName: "filterMode" }] : /* istanbul ignore next */ []));
235
+ // Mode A local state
236
+ searchTerm = signal('', /* @ts-ignore */
237
+ ...(ngDevMode ? [{ debugName: "searchTerm" }] : /* istanbul ignore next */ []));
238
+ selectedSet = signal(new Set(), /* @ts-ignore */
239
+ ...(ngDevMode ? [{ debugName: "selectedSet" }] : /* istanbul ignore next */ []));
240
+ // Mode B local state
241
+ selectedOperator = signal('eq', /* @ts-ignore */
242
+ ...(ngDevMode ? [{ debugName: "selectedOperator" }] : /* istanbul ignore next */ []));
243
+ filterVal = signal('', /* @ts-ignore */
244
+ ...(ngDevMode ? [{ debugName: "filterVal" }] : /* istanbul ignore next */ []));
245
+ filterVal2 = signal('', /* @ts-ignore */
246
+ ...(ngDevMode ? [{ debugName: "filterVal2" }] : /* istanbul ignore next */ []));
247
+ operatorOptions = computed(() => {
248
+ if (this.isDateField()) {
249
+ return [
250
+ { value: 'eq', label: 'Is on' },
251
+ { value: 'neq', label: 'Is not on' },
252
+ { value: 'gt', label: 'After' },
253
+ { value: 'gte', label: 'On or after' },
254
+ { value: 'lt', label: 'Before' },
255
+ { value: 'lte', label: 'On or before' },
256
+ { value: 'between', label: 'Between' },
257
+ { value: 'blank', label: 'Blank' },
258
+ { value: 'notBlank', label: 'Not blank' },
259
+ ];
260
+ }
261
+ if (this.isNumberField()) {
262
+ return [
263
+ { value: 'eq', label: 'Equals' },
264
+ { value: 'neq', label: 'Does not equal' },
265
+ { value: 'gt', label: 'Greater than' },
266
+ { value: 'gte', label: 'Greater than or equal to' },
267
+ { value: 'lt', label: 'Less than' },
268
+ { value: 'lte', label: 'Less than or equal to' },
269
+ { value: 'between', label: 'Between' },
270
+ { value: 'blank', label: 'Blank' },
271
+ { value: 'notBlank', label: 'Not blank' },
272
+ ];
273
+ }
274
+ return [
275
+ { value: 'contains', label: 'Contains' },
276
+ { value: 'eq', label: 'Equals' },
277
+ { value: 'neq', label: 'Does not equal' },
278
+ { value: 'startsWith', label: 'Starts with' },
279
+ { value: 'endsWith', label: 'Ends with' },
280
+ { value: 'blank', label: 'Blank' },
281
+ { value: 'notBlank', label: 'Not blank' },
282
+ ];
283
+ }, /* @ts-ignore */
284
+ ...(ngDevMode ? [{ debugName: "operatorOptions" }] : /* istanbul ignore next */ []));
285
+ ngOnInit() {
286
+ this.initFromCurrentFilter();
287
+ }
288
+ initFromCurrentFilter() {
289
+ const cf = this.currentFilter();
290
+ const all = this.distinctValues();
291
+ if (this.filterMode() === 'set') {
292
+ if (cf && cf.mode === 'set' && cf.selectedValues) {
293
+ this.selectedSet.set(new Set(cf.selectedValues));
294
+ }
295
+ else {
296
+ // Default: all selected
297
+ this.selectedSet.set(new Set(all));
298
+ }
299
+ }
300
+ else {
301
+ if (cf && cf.mode === 'condition') {
302
+ this.selectedOperator.set(cf.operator || 'eq');
303
+ this.filterVal.set(cf.value !== undefined && cf.value !== null ? String(cf.value) : '');
304
+ this.filterVal2.set(cf.secondValue !== undefined && cf.secondValue !== null ? String(cf.secondValue) : '');
305
+ }
306
+ else {
307
+ const defaultOp = this.isDateField() || this.isNumberField() ? 'eq' : 'contains';
308
+ this.selectedOperator.set(defaultOp);
309
+ this.filterVal.set('');
310
+ this.filterVal2.set('');
311
+ }
312
+ }
313
+ }
314
+ isDateField() {
315
+ const col = this.column();
316
+ return col.type === 'date' || col.sortType === 'date';
317
+ }
318
+ isNumberField() {
319
+ const col = this.column();
320
+ return col.type === 'number' || col.sortType === 'number';
321
+ }
322
+ onDateFilterValChange(val) {
323
+ if (!val) {
324
+ this.filterVal.set('');
325
+ }
326
+ else if (val instanceof Date) {
327
+ const y = val.getFullYear();
328
+ const m = String(val.getMonth() + 1).padStart(2, '0');
329
+ const d = String(val.getDate()).padStart(2, '0');
330
+ this.filterVal.set(`${y}-${m}-${d}`);
331
+ }
332
+ else {
333
+ this.filterVal.set(String(val));
334
+ }
335
+ }
336
+ onDateFilterVal2Change(val) {
337
+ if (!val) {
338
+ this.filterVal2.set('');
339
+ }
340
+ else if (val instanceof Date) {
341
+ const y = val.getFullYear();
342
+ const m = String(val.getMonth() + 1).padStart(2, '0');
343
+ const d = String(val.getDate()).padStart(2, '0');
344
+ this.filterVal2.set(`${y}-${m}-${d}`);
345
+ }
346
+ else {
347
+ this.filterVal2.set(String(val));
348
+ }
349
+ }
350
+ // Mode A Helpers
351
+ visibleDistinctValues = computed(() => {
352
+ const term = this.searchTerm().toLowerCase().trim();
353
+ const all = this.distinctValues();
354
+ if (!term)
355
+ return all;
356
+ return all.filter((v) => (v || '').toLowerCase().includes(term));
357
+ }, /* @ts-ignore */
358
+ ...(ngDevMode ? [{ debugName: "visibleDistinctValues" }] : /* istanbul ignore next */ []));
359
+ isAllSelected() {
360
+ const visible = this.visibleDistinctValues();
361
+ if (visible.length === 0)
362
+ return false;
363
+ const sel = this.selectedSet();
364
+ return visible.every((v) => sel.has(v));
365
+ }
366
+ isIndeterminate() {
367
+ const visible = this.visibleDistinctValues();
368
+ if (visible.length === 0)
369
+ return false;
370
+ const sel = this.selectedSet();
371
+ const count = visible.filter((v) => sel.has(v)).length;
372
+ return count > 0 && count < visible.length;
373
+ }
374
+ toggleSelectAll(event) {
375
+ const checked = event.target.checked;
376
+ const sel = new Set(this.selectedSet());
377
+ const visible = this.visibleDistinctValues();
378
+ if (checked) {
379
+ visible.forEach((v) => sel.add(v));
380
+ }
381
+ else {
382
+ visible.forEach((v) => sel.delete(v));
383
+ }
384
+ this.selectedSet.set(sel);
385
+ }
386
+ isValSelected(val) {
387
+ return this.selectedSet().has(val);
388
+ }
389
+ toggleVal(val, event) {
390
+ const checked = event.target.checked;
391
+ const sel = new Set(this.selectedSet());
392
+ if (checked) {
393
+ sel.add(val);
394
+ }
395
+ else {
396
+ sel.delete(val);
397
+ }
398
+ this.selectedSet.set(sel);
399
+ }
400
+ onSearchEnter() {
401
+ const term = this.searchTerm().trim();
402
+ if (term) {
403
+ const visible = this.visibleDistinctValues();
404
+ this.selectedSet.set(new Set(visible));
405
+ }
406
+ this.applySetFilter();
407
+ }
408
+ // Apply Actions
409
+ applyFilter() {
410
+ if (this.filterMode() === 'set') {
411
+ this.applySetFilter();
412
+ }
413
+ else {
414
+ this.applyConditionFilter();
415
+ }
416
+ }
417
+ applySetFilter() {
418
+ const field = this.column().field;
419
+ const all = this.distinctValues();
420
+ let sel = Array.from(this.selectedSet());
421
+ // If search term is present and user hadn't changed individual checkboxes (still all selected),
422
+ // narrow selection to the visible search matches
423
+ const term = this.searchTerm().trim();
424
+ if (term && sel.length === all.length) {
425
+ sel = this.visibleDistinctValues();
426
+ this.selectedSet.set(new Set(sel));
427
+ }
428
+ // If all distinct values are selected and no search term, it's effectively no filter
429
+ if (sel.length === all.length && all.every((v) => this.selectedSet().has(v))) {
430
+ this.clearFilter();
431
+ return;
432
+ }
433
+ this.filterApplied.emit({
434
+ field,
435
+ mode: 'set',
436
+ selectedValues: sel,
437
+ });
438
+ this.closed.emit();
439
+ }
440
+ applyConditionFilter() {
441
+ const field = this.column().field;
442
+ const op = this.selectedOperator();
443
+ const val = this.filterVal().trim();
444
+ const val2 = this.filterVal2().trim();
445
+ if (op !== 'blank' && op !== 'notBlank' && val === '') {
446
+ this.clearFilter();
447
+ return;
448
+ }
449
+ this.filterApplied.emit({
450
+ field,
451
+ mode: 'condition',
452
+ operator: op,
453
+ value: val,
454
+ secondValue: op === 'between' ? val2 : undefined,
455
+ });
456
+ this.closed.emit();
457
+ }
458
+ clearFilter() {
459
+ this.filterCleared.emit();
460
+ this.closed.emit();
461
+ }
462
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: TalosGridFilterDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
463
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: TalosGridFilterDialogComponent, isStandalone: true, selector: "talos-grid-filter-dialog", inputs: { column: { classPropertyName: "column", publicName: "column", isSignal: true, isRequired: true, transformFunction: null }, distinctValues: { classPropertyName: "distinctValues", publicName: "distinctValues", isSignal: true, isRequired: false, transformFunction: null }, currentFilter: { classPropertyName: "currentFilter", publicName: "currentFilter", isSignal: true, isRequired: false, transformFunction: null }, align: { classPropertyName: "align", publicName: "align", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { filterApplied: "filterApplied", filterCleared: "filterCleared", closed: "closed" }, host: { listeners: { "keydown.escape": "closed.emit()" }, properties: { "class.align-right": "align() === 'right'" } }, ngImport: i0, template: `
464
+ <div class="filter-dialog-container" (click)="$event.stopPropagation()">
465
+ @if (filterMode() === 'set') {
466
+ <!-- MODE A: Searchable Checkbox List (for text / select / status) -->
467
+ <div class="mode-a-wrapper">
468
+ <!-- Search input -->
469
+ <div class="search-input-wrapper">
470
+ <span class="search-icon">
471
+ <ng-container *ngComponentOutlet="searchIcon" />
472
+ </span>
473
+ <input
474
+ type="text"
475
+ class="filter-search-input"
476
+ placeholder="Search..."
477
+ [ngModel]="searchTerm()"
478
+ (ngModelChange)="searchTerm.set($event)"
479
+ (keydown.enter)="onSearchEnter()"
480
+ autofocus
481
+ />
482
+ @if (searchTerm()) {
483
+ <button type="button" class="clear-search-btn" (click)="searchTerm.set('')">
484
+ <ng-container *ngComponentOutlet="xIcon" />
485
+ </button>
486
+ }
487
+ </div>
488
+
489
+ <!-- Checkbox List -->
490
+ <div class="checkbox-list-wrapper">
491
+ <!-- (Select All) item -->
492
+ <label class="checkbox-item select-all-item">
493
+ <input
494
+ type="checkbox"
495
+ class="filter-checkbox"
496
+ [checked]="isAllSelected()"
497
+ [indeterminate]="isIndeterminate()"
498
+ (change)="toggleSelectAll($event)"
499
+ />
500
+ <span
501
+ class="checkbox-custom"
502
+ [class.is-checked]="isAllSelected()"
503
+ [class.is-indeterminate]="isIndeterminate()"
504
+ >
505
+ @if (isAllSelected()) {
506
+ <ng-container *ngComponentOutlet="checkIcon" />
507
+ } @else if (isIndeterminate()) {
508
+ <span class="indeterminate-bar"></span>
509
+ }
510
+ </span>
511
+ <span class="checkbox-label">
512
+ {{ searchTerm() ? '(Select All Matches)' : '(Select All)' }}
513
+ </span>
514
+ </label>
515
+
516
+ <!-- Distinct item checkboxes -->
517
+ @for (val of visibleDistinctValues(); track val) {
518
+ <label class="checkbox-item">
519
+ <input
520
+ type="checkbox"
521
+ class="filter-checkbox"
522
+ [checked]="isValSelected(val)"
523
+ (change)="toggleVal(val, $event)"
524
+ />
525
+ <span class="checkbox-custom" [class.is-checked]="isValSelected(val)">
526
+ @if (isValSelected(val)) {
527
+ <ng-container *ngComponentOutlet="checkIcon" />
528
+ }
529
+ </span>
530
+ <span class="checkbox-label">{{ val || '(Blank)' }}</span>
531
+ </label>
532
+ }
533
+
534
+ @if (visibleDistinctValues().length === 0) {
535
+ <div class="no-matches-msg">No matching values</div>
536
+ }
537
+ </div>
538
+ </div>
539
+ } @else {
540
+ <!-- MODE B: Operator Dropdown & Filter Input (for numbers / dates) -->
541
+ <div class="mode-b-wrapper">
542
+ <!-- Operator Dropdown -->
543
+ <div class="operator-dropdown-wrapper">
544
+ <select
545
+ class="operator-select"
546
+ [ngModel]="selectedOperator()"
547
+ (ngModelChange)="selectedOperator.set($event)"
548
+ >
549
+ @for (op of operatorOptions(); track op.value) {
550
+ <option [value]="op.value">{{ op.label }}</option>
551
+ }
552
+ </select>
553
+ <span class="dropdown-arrow">
554
+ <ng-container *ngComponentOutlet="chevronDownIcon" />
555
+ </span>
556
+ </div>
557
+
558
+ <!-- Filter Value Input -->
559
+ @if (selectedOperator() !== 'blank' && selectedOperator() !== 'notBlank') {
560
+ @if (isDateField()) {
561
+ <div class="date-picker-wrapper" (keydown.enter)="applyConditionFilter()">
562
+ <talos-date-picker
563
+ [ngModel]="filterVal()"
564
+ (ngModelChange)="onDateFilterValChange($event)"
565
+ size="sm"
566
+ placeholder="Select date"
567
+ />
568
+ </div>
569
+ } @else {
570
+ <div class="search-input-wrapper">
571
+ <span class="search-icon">
572
+ <ng-container *ngComponentOutlet="searchIcon" />
573
+ </span>
574
+ <input
575
+ type="text"
576
+ class="filter-search-input"
577
+ placeholder="Filter..."
578
+ [ngModel]="filterVal()"
579
+ (ngModelChange)="filterVal.set($event)"
580
+ (keydown.enter)="applyConditionFilter()"
581
+ autofocus
582
+ />
583
+ </div>
584
+ }
585
+
586
+ <!-- Second input for Between -->
587
+ @if (selectedOperator() === 'between') {
588
+ @if (isDateField()) {
589
+ <div class="date-picker-wrapper" style="margin-top: 6px;" (keydown.enter)="applyConditionFilter()">
590
+ <talos-date-picker
591
+ [ngModel]="filterVal2()"
592
+ (ngModelChange)="onDateFilterVal2Change($event)"
593
+ size="sm"
594
+ placeholder="And max date"
595
+ />
596
+ </div>
597
+ } @else {
598
+ <div class="search-input-wrapper" style="margin-top: 6px;">
599
+ <span class="search-icon">
600
+ <ng-container *ngComponentOutlet="searchIcon" />
601
+ </span>
602
+ <input
603
+ type="text"
604
+ class="filter-search-input"
605
+ placeholder="And max value..."
606
+ [ngModel]="filterVal2()"
607
+ (ngModelChange)="filterVal2.set($event)"
608
+ (keydown.enter)="applyConditionFilter()"
609
+ />
610
+ </div>
611
+ }
612
+ }
613
+ }
614
+ </div>
615
+ }
616
+
617
+ <!-- Dialog Footer -->
618
+ <div class="dialog-footer">
619
+ <button talosButton variant="ghost" size="sm" type="button" (click)="clearFilter()">
620
+ Clear
621
+ </button>
622
+ <button talosButton variant="primary" size="sm" type="button" (click)="applyFilter()">
623
+ Apply
624
+ </button>
625
+ </div>
626
+ </div>
627
+ `, isInline: true, styles: [":host{display:block;font-family:var(--talos-font-sans, system-ui, sans-serif);font-size:13px}.filter-dialog-container{background:var(--talos-dropdown-bg, var(--talos-bg-surface, #ffffff));color:var(--talos-text-primary, #0f172a);border:1px solid var(--talos-border-default, #cbd5e1);border-radius:var(--talos-radius-md, 8px);box-shadow:0 12px 28px -4px #00000040,0 4px 10px #0000001a;width:240px;padding:10px;box-sizing:border-box;-webkit-user-select:none;user-select:none}.date-picker-wrapper{width:100%}.date-picker-wrapper talos-date-picker{display:block;width:100%}.search-input-wrapper{position:relative;display:flex;align-items:center;width:100%}.search-icon{position:absolute;left:9px;width:14px;height:14px;opacity:.6;display:flex;align-items:center;justify-content:center;pointer-events:none;color:var(--talos-text-muted, #94a3b8)}.search-icon ::ng-deep svg,.search-icon svg{width:13px;height:13px;display:block}.filter-search-input{width:100%;height:32px;padding:0 24px 0 28px;background:var(--talos-bg-subtle, #f8fafc);border:1px solid var(--talos-border-default, #cbd5e1);border-radius:var(--talos-radius-sm, 6px);color:var(--talos-text-primary, #0f172a);font-size:13px;outline:none;box-sizing:border-box;transition:border-color .15s,box-shadow .15s}.filter-search-input::placeholder{color:var(--talos-text-muted, #94a3b8)}.filter-search-input:focus{border-color:var(--talos-primary, #2563eb);box-shadow:0 0 0 2px #2563eb40}.clear-search-btn{position:absolute;right:6px;background:transparent;border:none;color:var(--talos-text-muted, #94a3b8);cursor:pointer;padding:0;width:16px;height:16px;display:flex;align-items:center;justify-content:center}.clear-search-btn ::ng-deep svg,.clear-search-btn svg{width:12px;height:12px;display:block}.clear-search-btn:hover{color:var(--talos-text-primary, #0f172a)}.checkbox-list-wrapper{margin-top:8px;max-height:175px;overflow-y:auto;overflow-x:hidden;padding-right:4px;scrollbar-width:thin;scrollbar-color:var(--talos-border-default, #cbd5e1) transparent}.checkbox-list-wrapper::-webkit-scrollbar{width:5px}.checkbox-list-wrapper::-webkit-scrollbar-thumb{background:var(--talos-border-default, #cbd5e1);border-radius:4px}.checkbox-item{display:flex;align-items:center;gap:8px;padding:5px 6px;border-radius:4px;cursor:pointer;color:var(--talos-text-primary, #0f172a);transition:background-color .12s}.checkbox-item:hover{background:var(--talos-neutral-subtle-bg-hover, rgba(0, 0, 0, .05))}.checkbox-item.select-all-item{font-weight:500;border-bottom:1px solid var(--talos-border-subtle, rgba(0, 0, 0, .08));margin-bottom:3px;padding-bottom:6px}.filter-checkbox{position:absolute;opacity:0;width:0;height:0;pointer-events:none}.checkbox-custom{width:16px;height:16px;border-radius:4px;border:1.5px solid var(--talos-border-hover, #94a3b8);background:transparent;display:flex;align-items:center;justify-content:center;flex-shrink:0;transition:all .12s}.checkbox-custom ::ng-deep svg,.checkbox-custom svg{width:12px;height:12px;display:block}.checkbox-custom.is-checked,.checkbox-custom.is-indeterminate{background:var(--talos-primary, #2563eb);border-color:var(--talos-primary, #2563eb);color:#fff}.checkbox-custom .indeterminate-bar{width:8px;height:2px;background:#fff;border-radius:1px}.checkbox-label{font-size:12.5px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;flex:1}.no-matches-msg{padding:12px;text-align:center;color:var(--talos-text-muted, #94a3b8);font-size:12px}.operator-dropdown-wrapper{position:relative;margin-bottom:8px}.operator-select{width:100%;height:32px;padding:0 24px 0 10px;background:var(--talos-bg-subtle, #f8fafc);border:1px solid var(--talos-border-default, #cbd5e1);border-radius:var(--talos-radius-sm, 6px);color:var(--talos-text-primary, #0f172a);font-size:13px;outline:none;cursor:pointer;appearance:none;box-sizing:border-box}.operator-select:focus{border-color:var(--talos-primary, #2563eb);box-shadow:0 0 0 2px #2563eb40}.dropdown-arrow{position:absolute;right:8px;top:50%;transform:translateY(-50%);pointer-events:none;opacity:.6;width:14px;height:14px;display:flex;align-items:center;justify-content:center;color:var(--talos-text-muted, #94a3b8)}.dropdown-arrow ::ng-deep svg,.dropdown-arrow svg{width:13px;height:13px;display:block}.dialog-footer{display:flex;align-items:center;justify-content:flex-end;gap:6px;margin-top:10px;padding-top:8px;border-top:1px solid var(--talos-border-subtle, rgba(0, 0, 0, .08))}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.SelectControlValueAccessor, selector: "select:not([multiple]):not([ngNoCva])[formControlName],select:not([multiple]):not([ngNoCva])[formControl],select:not([multiple]):not([ngNoCva])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "directive", type: TalosButtonDirective, selector: "button[talosButton], a[talosButton], button[talos-btn], a[talos-btn]", inputs: ["variant", "size", "iconOnly", "fullWidth", "pill", "loading", "disabled", "animation", "spinning", "popping", "feedback"] }, { kind: "component", type: DatePickerComponent, selector: "talos-date-picker", inputs: ["placeholder", "label", "floatingLabel", "floating", "required", "disabled", "clearable", "size", "displayFormat", "valueFormat", "showTime", "use24Hour", "showSeconds", "minuteStep", "minDate", "maxDate", "firstDayOfWeek", "filterDate"], outputs: ["dateChange", "opened", "closed"] }] });
628
+ }
629
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: TalosGridFilterDialogComponent, decorators: [{
630
+ type: Component,
631
+ args: [{ selector: 'talos-grid-filter-dialog', imports: [FormsModule, NgComponentOutlet, TalosButtonDirective, DatePickerComponent], host: {
632
+ '[class.align-right]': "align() === 'right'",
633
+ '(keydown.escape)': 'closed.emit()',
634
+ }, template: `
635
+ <div class="filter-dialog-container" (click)="$event.stopPropagation()">
636
+ @if (filterMode() === 'set') {
637
+ <!-- MODE A: Searchable Checkbox List (for text / select / status) -->
638
+ <div class="mode-a-wrapper">
639
+ <!-- Search input -->
640
+ <div class="search-input-wrapper">
641
+ <span class="search-icon">
642
+ <ng-container *ngComponentOutlet="searchIcon" />
643
+ </span>
644
+ <input
645
+ type="text"
646
+ class="filter-search-input"
647
+ placeholder="Search..."
648
+ [ngModel]="searchTerm()"
649
+ (ngModelChange)="searchTerm.set($event)"
650
+ (keydown.enter)="onSearchEnter()"
651
+ autofocus
652
+ />
653
+ @if (searchTerm()) {
654
+ <button type="button" class="clear-search-btn" (click)="searchTerm.set('')">
655
+ <ng-container *ngComponentOutlet="xIcon" />
656
+ </button>
657
+ }
658
+ </div>
659
+
660
+ <!-- Checkbox List -->
661
+ <div class="checkbox-list-wrapper">
662
+ <!-- (Select All) item -->
663
+ <label class="checkbox-item select-all-item">
664
+ <input
665
+ type="checkbox"
666
+ class="filter-checkbox"
667
+ [checked]="isAllSelected()"
668
+ [indeterminate]="isIndeterminate()"
669
+ (change)="toggleSelectAll($event)"
670
+ />
671
+ <span
672
+ class="checkbox-custom"
673
+ [class.is-checked]="isAllSelected()"
674
+ [class.is-indeterminate]="isIndeterminate()"
675
+ >
676
+ @if (isAllSelected()) {
677
+ <ng-container *ngComponentOutlet="checkIcon" />
678
+ } @else if (isIndeterminate()) {
679
+ <span class="indeterminate-bar"></span>
680
+ }
681
+ </span>
682
+ <span class="checkbox-label">
683
+ {{ searchTerm() ? '(Select All Matches)' : '(Select All)' }}
684
+ </span>
685
+ </label>
686
+
687
+ <!-- Distinct item checkboxes -->
688
+ @for (val of visibleDistinctValues(); track val) {
689
+ <label class="checkbox-item">
690
+ <input
691
+ type="checkbox"
692
+ class="filter-checkbox"
693
+ [checked]="isValSelected(val)"
694
+ (change)="toggleVal(val, $event)"
695
+ />
696
+ <span class="checkbox-custom" [class.is-checked]="isValSelected(val)">
697
+ @if (isValSelected(val)) {
698
+ <ng-container *ngComponentOutlet="checkIcon" />
699
+ }
700
+ </span>
701
+ <span class="checkbox-label">{{ val || '(Blank)' }}</span>
702
+ </label>
703
+ }
704
+
705
+ @if (visibleDistinctValues().length === 0) {
706
+ <div class="no-matches-msg">No matching values</div>
707
+ }
708
+ </div>
709
+ </div>
710
+ } @else {
711
+ <!-- MODE B: Operator Dropdown & Filter Input (for numbers / dates) -->
712
+ <div class="mode-b-wrapper">
713
+ <!-- Operator Dropdown -->
714
+ <div class="operator-dropdown-wrapper">
715
+ <select
716
+ class="operator-select"
717
+ [ngModel]="selectedOperator()"
718
+ (ngModelChange)="selectedOperator.set($event)"
719
+ >
720
+ @for (op of operatorOptions(); track op.value) {
721
+ <option [value]="op.value">{{ op.label }}</option>
722
+ }
723
+ </select>
724
+ <span class="dropdown-arrow">
725
+ <ng-container *ngComponentOutlet="chevronDownIcon" />
726
+ </span>
727
+ </div>
728
+
729
+ <!-- Filter Value Input -->
730
+ @if (selectedOperator() !== 'blank' && selectedOperator() !== 'notBlank') {
731
+ @if (isDateField()) {
732
+ <div class="date-picker-wrapper" (keydown.enter)="applyConditionFilter()">
733
+ <talos-date-picker
734
+ [ngModel]="filterVal()"
735
+ (ngModelChange)="onDateFilterValChange($event)"
736
+ size="sm"
737
+ placeholder="Select date"
738
+ />
739
+ </div>
740
+ } @else {
741
+ <div class="search-input-wrapper">
742
+ <span class="search-icon">
743
+ <ng-container *ngComponentOutlet="searchIcon" />
744
+ </span>
745
+ <input
746
+ type="text"
747
+ class="filter-search-input"
748
+ placeholder="Filter..."
749
+ [ngModel]="filterVal()"
750
+ (ngModelChange)="filterVal.set($event)"
751
+ (keydown.enter)="applyConditionFilter()"
752
+ autofocus
753
+ />
754
+ </div>
755
+ }
756
+
757
+ <!-- Second input for Between -->
758
+ @if (selectedOperator() === 'between') {
759
+ @if (isDateField()) {
760
+ <div class="date-picker-wrapper" style="margin-top: 6px;" (keydown.enter)="applyConditionFilter()">
761
+ <talos-date-picker
762
+ [ngModel]="filterVal2()"
763
+ (ngModelChange)="onDateFilterVal2Change($event)"
764
+ size="sm"
765
+ placeholder="And max date"
766
+ />
767
+ </div>
768
+ } @else {
769
+ <div class="search-input-wrapper" style="margin-top: 6px;">
770
+ <span class="search-icon">
771
+ <ng-container *ngComponentOutlet="searchIcon" />
772
+ </span>
773
+ <input
774
+ type="text"
775
+ class="filter-search-input"
776
+ placeholder="And max value..."
777
+ [ngModel]="filterVal2()"
778
+ (ngModelChange)="filterVal2.set($event)"
779
+ (keydown.enter)="applyConditionFilter()"
780
+ />
781
+ </div>
782
+ }
783
+ }
784
+ }
785
+ </div>
786
+ }
787
+
788
+ <!-- Dialog Footer -->
789
+ <div class="dialog-footer">
790
+ <button talosButton variant="ghost" size="sm" type="button" (click)="clearFilter()">
791
+ Clear
792
+ </button>
793
+ <button talosButton variant="primary" size="sm" type="button" (click)="applyFilter()">
794
+ Apply
795
+ </button>
796
+ </div>
797
+ </div>
798
+ `, styles: [":host{display:block;font-family:var(--talos-font-sans, system-ui, sans-serif);font-size:13px}.filter-dialog-container{background:var(--talos-dropdown-bg, var(--talos-bg-surface, #ffffff));color:var(--talos-text-primary, #0f172a);border:1px solid var(--talos-border-default, #cbd5e1);border-radius:var(--talos-radius-md, 8px);box-shadow:0 12px 28px -4px #00000040,0 4px 10px #0000001a;width:240px;padding:10px;box-sizing:border-box;-webkit-user-select:none;user-select:none}.date-picker-wrapper{width:100%}.date-picker-wrapper talos-date-picker{display:block;width:100%}.search-input-wrapper{position:relative;display:flex;align-items:center;width:100%}.search-icon{position:absolute;left:9px;width:14px;height:14px;opacity:.6;display:flex;align-items:center;justify-content:center;pointer-events:none;color:var(--talos-text-muted, #94a3b8)}.search-icon ::ng-deep svg,.search-icon svg{width:13px;height:13px;display:block}.filter-search-input{width:100%;height:32px;padding:0 24px 0 28px;background:var(--talos-bg-subtle, #f8fafc);border:1px solid var(--talos-border-default, #cbd5e1);border-radius:var(--talos-radius-sm, 6px);color:var(--talos-text-primary, #0f172a);font-size:13px;outline:none;box-sizing:border-box;transition:border-color .15s,box-shadow .15s}.filter-search-input::placeholder{color:var(--talos-text-muted, #94a3b8)}.filter-search-input:focus{border-color:var(--talos-primary, #2563eb);box-shadow:0 0 0 2px #2563eb40}.clear-search-btn{position:absolute;right:6px;background:transparent;border:none;color:var(--talos-text-muted, #94a3b8);cursor:pointer;padding:0;width:16px;height:16px;display:flex;align-items:center;justify-content:center}.clear-search-btn ::ng-deep svg,.clear-search-btn svg{width:12px;height:12px;display:block}.clear-search-btn:hover{color:var(--talos-text-primary, #0f172a)}.checkbox-list-wrapper{margin-top:8px;max-height:175px;overflow-y:auto;overflow-x:hidden;padding-right:4px;scrollbar-width:thin;scrollbar-color:var(--talos-border-default, #cbd5e1) transparent}.checkbox-list-wrapper::-webkit-scrollbar{width:5px}.checkbox-list-wrapper::-webkit-scrollbar-thumb{background:var(--talos-border-default, #cbd5e1);border-radius:4px}.checkbox-item{display:flex;align-items:center;gap:8px;padding:5px 6px;border-radius:4px;cursor:pointer;color:var(--talos-text-primary, #0f172a);transition:background-color .12s}.checkbox-item:hover{background:var(--talos-neutral-subtle-bg-hover, rgba(0, 0, 0, .05))}.checkbox-item.select-all-item{font-weight:500;border-bottom:1px solid var(--talos-border-subtle, rgba(0, 0, 0, .08));margin-bottom:3px;padding-bottom:6px}.filter-checkbox{position:absolute;opacity:0;width:0;height:0;pointer-events:none}.checkbox-custom{width:16px;height:16px;border-radius:4px;border:1.5px solid var(--talos-border-hover, #94a3b8);background:transparent;display:flex;align-items:center;justify-content:center;flex-shrink:0;transition:all .12s}.checkbox-custom ::ng-deep svg,.checkbox-custom svg{width:12px;height:12px;display:block}.checkbox-custom.is-checked,.checkbox-custom.is-indeterminate{background:var(--talos-primary, #2563eb);border-color:var(--talos-primary, #2563eb);color:#fff}.checkbox-custom .indeterminate-bar{width:8px;height:2px;background:#fff;border-radius:1px}.checkbox-label{font-size:12.5px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;flex:1}.no-matches-msg{padding:12px;text-align:center;color:var(--talos-text-muted, #94a3b8);font-size:12px}.operator-dropdown-wrapper{position:relative;margin-bottom:8px}.operator-select{width:100%;height:32px;padding:0 24px 0 10px;background:var(--talos-bg-subtle, #f8fafc);border:1px solid var(--talos-border-default, #cbd5e1);border-radius:var(--talos-radius-sm, 6px);color:var(--talos-text-primary, #0f172a);font-size:13px;outline:none;cursor:pointer;appearance:none;box-sizing:border-box}.operator-select:focus{border-color:var(--talos-primary, #2563eb);box-shadow:0 0 0 2px #2563eb40}.dropdown-arrow{position:absolute;right:8px;top:50%;transform:translateY(-50%);pointer-events:none;opacity:.6;width:14px;height:14px;display:flex;align-items:center;justify-content:center;color:var(--talos-text-muted, #94a3b8)}.dropdown-arrow ::ng-deep svg,.dropdown-arrow svg{width:13px;height:13px;display:block}.dialog-footer{display:flex;align-items:center;justify-content:flex-end;gap:6px;margin-top:10px;padding-top:8px;border-top:1px solid var(--talos-border-subtle, rgba(0, 0, 0, .08))}\n"] }]
799
+ }], propDecorators: { column: [{ type: i0.Input, args: [{ isSignal: true, alias: "column", required: true }] }], distinctValues: [{ type: i0.Input, args: [{ isSignal: true, alias: "distinctValues", required: false }] }], currentFilter: [{ type: i0.Input, args: [{ isSignal: true, alias: "currentFilter", required: false }] }], align: [{ type: i0.Input, args: [{ isSignal: true, alias: "align", required: false }] }], filterApplied: [{ type: i0.Output, args: ["filterApplied"] }], filterCleared: [{ type: i0.Output, args: ["filterCleared"] }], closed: [{ type: i0.Output, args: ["closed"] }] } });
800
+
801
+ class TalosGridCellFormatPipe {
802
+ fieldPartsCache = new Map();
803
+ transform(row, col) {
804
+ if (!row || !col)
805
+ return '';
806
+ const val = this.extractFieldValue(row, col.field);
807
+ if (col.formatter) {
808
+ return col.formatter(val, row);
809
+ }
810
+ if (val === null || val === undefined) {
811
+ return '';
812
+ }
813
+ if (col.type === 'date' && val instanceof Date) {
814
+ return val.toLocaleDateString();
815
+ }
816
+ return String(val);
817
+ }
818
+ extractFieldValue(item, field) {
819
+ if (!item || !field)
820
+ return undefined;
821
+ if (!field.includes('.')) {
822
+ return item[field];
823
+ }
824
+ let parts = this.fieldPartsCache.get(field);
825
+ if (!parts) {
826
+ parts = field.split('.');
827
+ this.fieldPartsCache.set(field, parts);
828
+ }
829
+ let cur = item;
830
+ for (let i = 0; i < parts.length; i++) {
831
+ if (cur == null)
832
+ return undefined;
833
+ cur = cur[parts[i]];
834
+ }
835
+ return cur;
836
+ }
837
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: TalosGridCellFormatPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
838
+ static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "22.1.1", ngImport: i0, type: TalosGridCellFormatPipe, isStandalone: true, name: "talosGridCellFormat" });
839
+ }
840
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: TalosGridCellFormatPipe, decorators: [{
841
+ type: Pipe,
842
+ args: [{
843
+ name: 'talosGridCellFormat',
844
+ pure: true,
845
+ }]
846
+ }] });
847
+
848
+ class TalosDataGridComponent {
849
+ // Inputs
850
+ data = input.required(/* @ts-ignore */
851
+ ...(ngDevMode ? [{ debugName: "data" }] : /* istanbul ignore next */ []));
852
+ columns = input.required(/* @ts-ignore */
853
+ ...(ngDevMode ? [{ debugName: "columns" }] : /* istanbul ignore next */ []));
854
+ density = input('comfortable', /* @ts-ignore */
855
+ ...(ngDevMode ? [{ debugName: "density" }] : /* istanbul ignore next */ []));
856
+ filterable = input(false, /* @ts-ignore */
857
+ ...(ngDevMode ? [{ debugName: "filterable" }] : /* istanbul ignore next */ []));
858
+ pagination = input(false, /* @ts-ignore */
859
+ ...(ngDevMode ? [{ debugName: "pagination" }] : /* istanbul ignore next */ []));
860
+ pageSize = input(10, /* @ts-ignore */
861
+ ...(ngDevMode ? [{ debugName: "pageSize" }] : /* istanbul ignore next */ []));
862
+ pageSizeOptions = input([5, 10, 20, 50], /* @ts-ignore */
863
+ ...(ngDevMode ? [{ debugName: "pageSizeOptions" }] : /* istanbul ignore next */ []));
864
+ pageIndex = input(0, /* @ts-ignore */
865
+ ...(ngDevMode ? [{ debugName: "pageIndex" }] : /* istanbul ignore next */ []));
866
+ totalItems = input(null, /* @ts-ignore */
867
+ ...(ngDevMode ? [{ debugName: "totalItems" }] : /* istanbul ignore next */ []));
868
+ trackBy = input(null, /* @ts-ignore */
869
+ ...(ngDevMode ? [{ debugName: "trackBy" }] : /* istanbul ignore next */ []));
870
+ striped = input(false, /* @ts-ignore */
871
+ ...(ngDevMode ? [{ debugName: "striped" }] : /* istanbul ignore next */ []));
872
+ bordered = input(true, /* @ts-ignore */
873
+ ...(ngDevMode ? [{ debugName: "bordered" }] : /* istanbul ignore next */ []));
874
+ hoverable = input(true, /* @ts-ignore */
875
+ ...(ngDevMode ? [{ debugName: "hoverable" }] : /* istanbul ignore next */ []));
876
+ emptyMessage = input('No records to display', /* @ts-ignore */
877
+ ...(ngDevMode ? [{ debugName: "emptyMessage" }] : /* istanbul ignore next */ []));
878
+ loading = input(false, /* @ts-ignore */
879
+ ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
880
+ clientSort = input(true, /* @ts-ignore */
881
+ ...(ngDevMode ? [{ debugName: "clientSort" }] : /* istanbul ignore next */ []));
882
+ clientPagination = input(true, /* @ts-ignore */
883
+ ...(ngDevMode ? [{ debugName: "clientPagination" }] : /* istanbul ignore next */ []));
884
+ clientFilter = input(true, /* @ts-ignore */
885
+ ...(ngDevMode ? [{ debugName: "clientFilter" }] : /* istanbul ignore next */ []));
886
+ resizable = input(false, /* @ts-ignore */
887
+ ...(ngDevMode ? [{ debugName: "resizable" }] : /* istanbul ignore next */ []));
888
+ rowKey = input(null, /* @ts-ignore */
889
+ ...(ngDevMode ? [{ debugName: "rowKey" }] : /* istanbul ignore next */ []));
890
+ // Outputs
891
+ sortChange = output();
892
+ filterChange = output();
893
+ pageChange = output();
894
+ rowClick = output();
895
+ actionClick = output();
896
+ columnResize = output();
897
+ // Icons for template
898
+ arrowUpIcon = LucideArrowUp;
899
+ arrowDownIcon = LucideArrowDown;
900
+ arrowUpDownIcon = LucideArrowUpDown;
901
+ chevronLeftIcon = LucideChevronLeft;
902
+ chevronRightIcon = LucideChevronRight;
903
+ chevronsLeftIcon = LucideChevronsLeft;
904
+ chevronsRightIcon = LucideChevronsRight;
905
+ xIcon = LucideX;
906
+ inboxIcon = LucideInbox;
907
+ loaderIcon = LucideLoader2;
908
+ filterIcon = LucideFilter;
909
+ // Internal reactive states
910
+ internalSortState = signal({ field: '', direction: null }, /* @ts-ignore */
911
+ ...(ngDevMode ? [{ debugName: "internalSortState" }] : /* istanbul ignore next */ []));
912
+ internalFilterStates = signal(new Map(), /* @ts-ignore */
913
+ ...(ngDevMode ? [{ debugName: "internalFilterStates" }] : /* istanbul ignore next */ []));
914
+ internalPageIndex = signal(0, /* @ts-ignore */
915
+ ...(ngDevMode ? [{ debugName: "internalPageIndex" }] : /* istanbul ignore next */ []));
916
+ internalPageSize = signal(10, /* @ts-ignore */
917
+ ...(ngDevMode ? [{ debugName: "internalPageSize" }] : /* istanbul ignore next */ []));
918
+ activeFilterCol = signal(null, /* @ts-ignore */
919
+ ...(ngDevMode ? [{ debugName: "activeFilterCol" }] : /* istanbul ignore next */ []));
920
+ columnWidths = signal({}, /* @ts-ignore */
921
+ ...(ngDevMode ? [{ debugName: "columnWidths" }] : /* istanbul ignore next */ []));
922
+ resizingColumn = signal(null, /* @ts-ignore */
923
+ ...(ngDevMode ? [{ debugName: "resizingColumn" }] : /* istanbul ignore next */ []));
924
+ isResizing = computed(() => this.resizingColumn() !== null, /* @ts-ignore */
925
+ ...(ngDevMode ? [{ debugName: "isResizing" }] : /* istanbul ignore next */ []));
926
+ hasResizableColumns = computed(() => {
927
+ return this.resizable() || this.columns().some((c) => c.resizable === true);
928
+ }, /* @ts-ignore */
929
+ ...(ngDevMode ? [{ debugName: "hasResizableColumns" }] : /* istanbul ignore next */ []));
930
+ // CDK Connected Overlay Positions
931
+ overlayPositions = [
932
+ {
933
+ originX: 'start',
934
+ originY: 'bottom',
935
+ overlayX: 'start',
936
+ overlayY: 'top',
937
+ offsetY: 6,
938
+ },
939
+ {
940
+ originX: 'end',
941
+ originY: 'bottom',
942
+ overlayX: 'end',
943
+ overlayY: 'top',
944
+ offsetY: 6,
945
+ },
946
+ {
947
+ originX: 'start',
948
+ originY: 'top',
949
+ overlayX: 'start',
950
+ overlayY: 'bottom',
951
+ offsetY: -6,
952
+ },
953
+ {
954
+ originX: 'end',
955
+ originY: 'top',
956
+ overlayX: 'end',
957
+ overlayY: 'bottom',
958
+ offsetY: -6,
959
+ },
960
+ ];
961
+ constructor() {
962
+ effect(() => {
963
+ this.internalPageIndex.set(this.pageIndex());
964
+ });
965
+ effect(() => {
966
+ this.internalPageSize.set(this.pageSize());
967
+ });
968
+ }
969
+ // Active filters list
970
+ activeFiltersList = computed(() => {
971
+ return Array.from(this.internalFilterStates().values());
972
+ }, /* @ts-ignore */
973
+ ...(ngDevMode ? [{ debugName: "activeFiltersList" }] : /* istanbul ignore next */ []));
974
+ hasActiveFilters = computed(() => {
975
+ return this.internalFilterStates().size > 0;
976
+ }, /* @ts-ignore */
977
+ ...(ngDevMode ? [{ debugName: "hasActiveFilters" }] : /* istanbul ignore next */ []));
978
+ // Cached distinct values for the currently active filter column
979
+ activeColumnDistinctValues = computed(() => {
980
+ const field = this.activeFilterCol();
981
+ if (!field)
982
+ return [];
983
+ const col = this.columns().find((c) => c.field === field);
984
+ if (!col)
985
+ return [];
986
+ return this.getDistinctValues(col);
987
+ }, /* @ts-ignore */
988
+ ...(ngDevMode ? [{ debugName: "activeColumnDistinctValues" }] : /* istanbul ignore next */ []));
989
+ // Multi-Stage Processing Pipeline:
990
+ // Stage 1: Filtered Data
991
+ filteredData = computed(() => {
992
+ const raw = this.data() || [];
993
+ if (!this.clientFilter() || this.internalFilterStates().size === 0) {
994
+ return raw;
995
+ }
996
+ const filters = this.internalFilterStates();
997
+ const colsMap = new Map(this.columns().map((c) => [c.field, c]));
998
+ return raw.filter((item) => {
999
+ for (const [field, filter] of filters.entries()) {
1000
+ const rawVal = this.extractFieldValue(item, field);
1001
+ const col = colsMap.get(field);
1002
+ if (!this.matchesFilter(rawVal, filter, col)) {
1003
+ return false;
1004
+ }
1005
+ }
1006
+ return true;
1007
+ });
1008
+ }, /* @ts-ignore */
1009
+ ...(ngDevMode ? [{ debugName: "filteredData" }] : /* istanbul ignore next */ []));
1010
+ // Stage 2: Sorted Data
1011
+ sortedData = computed(() => {
1012
+ const base = this.filteredData();
1013
+ const sort = this.internalSortState();
1014
+ if (!this.clientSort() || !sort.direction || !sort.field) {
1015
+ return base;
1016
+ }
1017
+ const col = this.columns().find((c) => c.field === sort.field);
1018
+ const sortType = sort.sortType || col?.sortType || this.inferSortType(col);
1019
+ const dir = sort.direction === 'asc' ? 1 : -1;
1020
+ // Schwartzian transform: Pre-extract and parse sort keys in an O(N) pass
1021
+ // to eliminate reflection, string conversions, and Date parsing from the O(N log N) comparator
1022
+ const mapped = base.map((item, index) => {
1023
+ const raw = this.extractFieldValue(item, sort.field);
1024
+ let sortKey = null;
1025
+ if (raw !== null && raw !== undefined && raw !== '') {
1026
+ switch (sortType) {
1027
+ case 'number': {
1028
+ const num = Number(raw);
1029
+ sortKey = isNaN(num) ? null : num;
1030
+ break;
1031
+ }
1032
+ case 'date': {
1033
+ const time = raw instanceof Date ? raw.getTime() : new Date(raw).getTime();
1034
+ sortKey = isNaN(time) ? null : time;
1035
+ break;
1036
+ }
1037
+ case 'string':
1038
+ default:
1039
+ sortKey = String(raw);
1040
+ break;
1041
+ }
1042
+ }
1043
+ return { item, index, sortKey };
1044
+ });
1045
+ mapped.sort((a, b) => {
1046
+ if (a.sortKey === b.sortKey)
1047
+ return a.index - b.index;
1048
+ if (a.sortKey === null)
1049
+ return 1;
1050
+ if (b.sortKey === null)
1051
+ return -1;
1052
+ if (sortType === 'number' || sortType === 'date') {
1053
+ const diff = (a.sortKey - b.sortKey) * dir;
1054
+ return diff !== 0 ? diff : a.index - b.index;
1055
+ }
1056
+ // string
1057
+ const cmp = a.sortKey.localeCompare(b.sortKey, undefined, {
1058
+ numeric: true,
1059
+ sensitivity: 'base',
1060
+ }) * dir;
1061
+ return cmp !== 0 ? cmp : a.index - b.index;
1062
+ });
1063
+ return mapped.map((m) => m.item);
1064
+ }, /* @ts-ignore */
1065
+ ...(ngDevMode ? [{ debugName: "sortedData" }] : /* istanbul ignore next */ []));
1066
+ // Stage 3: Paged Data
1067
+ pagedData = computed(() => {
1068
+ const sorted = this.sortedData();
1069
+ if (!this.pagination() || !this.clientPagination()) {
1070
+ return sorted;
1071
+ }
1072
+ const pageSize = this.internalPageSize();
1073
+ const pageIndex = this.internalPageIndex();
1074
+ const start = pageIndex * pageSize;
1075
+ return sorted.slice(start, start + pageSize);
1076
+ }, /* @ts-ignore */
1077
+ ...(ngDevMode ? [{ debugName: "pagedData" }] : /* istanbul ignore next */ []));
1078
+ // Stage 4: Pagination Metrics
1079
+ totalCount = computed(() => {
1080
+ const externalTotal = this.totalItems();
1081
+ if (externalTotal !== null && externalTotal !== undefined) {
1082
+ return externalTotal;
1083
+ }
1084
+ return this.filteredData().length;
1085
+ }, /* @ts-ignore */
1086
+ ...(ngDevMode ? [{ debugName: "totalCount" }] : /* istanbul ignore next */ []));
1087
+ totalPages = computed(() => {
1088
+ const count = this.totalCount();
1089
+ const size = this.internalPageSize();
1090
+ if (count <= 0 || size <= 0)
1091
+ return 1;
1092
+ return Math.ceil(count / size);
1093
+ }, /* @ts-ignore */
1094
+ ...(ngDevMode ? [{ debugName: "totalPages" }] : /* istanbul ignore next */ []));
1095
+ startRecord = computed(() => {
1096
+ const total = this.totalCount();
1097
+ if (total === 0)
1098
+ return 0;
1099
+ return this.internalPageIndex() * this.internalPageSize() + 1;
1100
+ }, /* @ts-ignore */
1101
+ ...(ngDevMode ? [{ debugName: "startRecord" }] : /* istanbul ignore next */ []));
1102
+ endRecord = computed(() => {
1103
+ const total = this.totalCount();
1104
+ if (total === 0)
1105
+ return 0;
1106
+ return Math.min(total, (this.internalPageIndex() + 1) * this.internalPageSize());
1107
+ }, /* @ts-ignore */
1108
+ ...(ngDevMode ? [{ debugName: "endRecord" }] : /* istanbul ignore next */ []));
1109
+ get hasRowClickListeners() {
1110
+ return true;
1111
+ }
1112
+ // Row Identification for TrackBy
1113
+ trackByRow(index, row) {
1114
+ const customTrack = this.trackBy();
1115
+ if (customTrack) {
1116
+ return customTrack(index, row);
1117
+ }
1118
+ const customKey = this.rowKey();
1119
+ if (customKey && row && typeof row === 'object') {
1120
+ return row[customKey];
1121
+ }
1122
+ if (row && typeof row === 'object') {
1123
+ const anyRow = row;
1124
+ return anyRow.id ?? anyRow._id ?? anyRow.uuid ?? anyRow.key ?? index;
1125
+ }
1126
+ return index;
1127
+ }
1128
+ // Header & Sorting helpers
1129
+ isSortable(col) {
1130
+ if (col.type === 'actions')
1131
+ return false;
1132
+ if (col.sortable !== undefined)
1133
+ return col.sortable;
1134
+ return !!col.sortType;
1135
+ }
1136
+ isSorted(col) {
1137
+ return (this.internalSortState().field === col.field && this.internalSortState().direction !== null);
1138
+ }
1139
+ getSortDirection(col) {
1140
+ if (this.internalSortState().field !== col.field) {
1141
+ return null;
1142
+ }
1143
+ return this.internalSortState().direction;
1144
+ }
1145
+ getAriaSort(col) {
1146
+ if (!this.isSortable(col))
1147
+ return null;
1148
+ const dir = this.getSortDirection(col);
1149
+ if (dir === 'asc')
1150
+ return 'ascending';
1151
+ if (dir === 'desc')
1152
+ return 'descending';
1153
+ return 'none';
1154
+ }
1155
+ onHeaderClick(col) {
1156
+ if (!this.isSortable(col))
1157
+ return;
1158
+ const current = this.internalSortState();
1159
+ let nextDirection;
1160
+ if (current.field !== col.field) {
1161
+ nextDirection = 'asc';
1162
+ }
1163
+ else if (current.direction === 'asc') {
1164
+ nextDirection = 'desc';
1165
+ }
1166
+ else if (current.direction === 'desc') {
1167
+ nextDirection = null;
1168
+ }
1169
+ else {
1170
+ nextDirection = 'asc';
1171
+ }
1172
+ const sortType = col.sortType || this.inferSortType(col);
1173
+ const newState = {
1174
+ field: nextDirection ? col.field : '',
1175
+ direction: nextDirection,
1176
+ sortType,
1177
+ };
1178
+ this.internalSortState.set(newState);
1179
+ this.sortChange.emit(newState);
1180
+ }
1181
+ // Column Resizing Helpers & Handlers
1182
+ isResizable(col) {
1183
+ if (col.resizable !== undefined) {
1184
+ return col.resizable;
1185
+ }
1186
+ if (col.type === 'actions') {
1187
+ return false;
1188
+ }
1189
+ return this.resizable();
1190
+ }
1191
+ isColumnResizing(field) {
1192
+ return this.resizingColumn() === field;
1193
+ }
1194
+ getColumnWidth(col) {
1195
+ const dynamic = this.columnWidths()[col.field];
1196
+ if (dynamic !== undefined) {
1197
+ return `${dynamic}px`;
1198
+ }
1199
+ return col.width || col.minWidth;
1200
+ }
1201
+ setColumnWidth(field, width) {
1202
+ this.columnWidths.update((prev) => ({
1203
+ ...prev,
1204
+ [field]: width,
1205
+ }));
1206
+ }
1207
+ resetColumnWidths() {
1208
+ this.columnWidths.set({});
1209
+ }
1210
+ onResizeStart(col, event, thEl) {
1211
+ if (!this.isResizable(col))
1212
+ return;
1213
+ event.preventDefault();
1214
+ event.stopPropagation();
1215
+ const startX = 'touches' in event ? event.touches[0].clientX : event.clientX;
1216
+ const startWidth = thEl.getBoundingClientRect().width;
1217
+ const minW = col.minWidth ? parseInt(col.minWidth, 10) : 50;
1218
+ const maxW = col.maxWidth ? parseInt(col.maxWidth, 10) : undefined;
1219
+ this.resizingColumn.set(col.field);
1220
+ if (typeof document !== 'undefined') {
1221
+ document.body.style.cursor = 'col-resize';
1222
+ document.body.style.userSelect = 'none';
1223
+ }
1224
+ let lastWidth = startWidth;
1225
+ const onMove = (moveEvent) => {
1226
+ const currentX = 'touches' in moveEvent ? moveEvent.touches[0].clientX : moveEvent.clientX;
1227
+ const deltaX = currentX - startX;
1228
+ let newWidth = Math.round(startWidth + deltaX);
1229
+ if (minW !== undefined && newWidth < minW)
1230
+ newWidth = minW;
1231
+ if (maxW !== undefined && newWidth > maxW)
1232
+ newWidth = maxW;
1233
+ lastWidth = newWidth;
1234
+ this.columnWidths.update((prev) => ({
1235
+ ...prev,
1236
+ [col.field]: newWidth,
1237
+ }));
1238
+ };
1239
+ const onEnd = () => {
1240
+ if (typeof document !== 'undefined') {
1241
+ document.removeEventListener('mousemove', onMove);
1242
+ document.removeEventListener('mouseup', onEnd);
1243
+ document.removeEventListener('touchmove', onMove);
1244
+ document.removeEventListener('touchend', onEnd);
1245
+ document.body.style.cursor = '';
1246
+ document.body.style.userSelect = '';
1247
+ }
1248
+ this.resizingColumn.set(null);
1249
+ this.columnResize.emit({ column: col, width: lastWidth });
1250
+ };
1251
+ if (typeof document !== 'undefined') {
1252
+ document.addEventListener('mousemove', onMove);
1253
+ document.addEventListener('mouseup', onEnd);
1254
+ document.addEventListener('touchmove', onMove, { passive: true });
1255
+ document.addEventListener('touchend', onEnd);
1256
+ }
1257
+ }
1258
+ onResizeDoubleClick(col, event) {
1259
+ event.stopPropagation();
1260
+ event.preventDefault();
1261
+ this.columnWidths.update((prev) => {
1262
+ const next = { ...prev };
1263
+ delete next[col.field];
1264
+ return next;
1265
+ });
1266
+ if (col.width) {
1267
+ const parsed = parseInt(col.width, 10);
1268
+ if (!isNaN(parsed)) {
1269
+ this.columnResize.emit({ column: col, width: parsed });
1270
+ }
1271
+ }
1272
+ }
1273
+ onResizeKeydown(col, event, thEl) {
1274
+ if (event.key === 'ArrowLeft' || event.key === 'ArrowRight') {
1275
+ event.preventDefault();
1276
+ event.stopPropagation();
1277
+ const currentWidth = this.columnWidths()[col.field] ?? thEl.getBoundingClientRect().width;
1278
+ const step = event.key === 'ArrowRight' ? 10 : -10;
1279
+ const minW = col.minWidth ? parseInt(col.minWidth, 10) : 50;
1280
+ const maxW = col.maxWidth ? parseInt(col.maxWidth, 10) : undefined;
1281
+ let newWidth = Math.round(currentWidth + step);
1282
+ if (minW !== undefined && newWidth < minW)
1283
+ newWidth = minW;
1284
+ if (maxW !== undefined && newWidth > maxW)
1285
+ newWidth = maxW;
1286
+ this.columnWidths.update((prev) => ({
1287
+ ...prev,
1288
+ [col.field]: newWidth,
1289
+ }));
1290
+ this.columnResize.emit({ column: col, width: newWidth });
1291
+ }
1292
+ }
1293
+ // Filter Helpers & Dialog Management
1294
+ isFilterable(col) {
1295
+ if (col.type === 'actions')
1296
+ return false;
1297
+ if (col.filterable !== undefined)
1298
+ return col.filterable;
1299
+ return this.filterable();
1300
+ }
1301
+ isFilterDialogOpen(field) {
1302
+ return this.activeFilterCol() === field;
1303
+ }
1304
+ toggleFilterDialog(field, event) {
1305
+ event.stopPropagation();
1306
+ if (this.activeFilterCol() === field) {
1307
+ this.activeFilterCol.set(null);
1308
+ }
1309
+ else {
1310
+ this.activeFilterCol.set(field);
1311
+ }
1312
+ }
1313
+ closeFilterDialog() {
1314
+ this.activeFilterCol.set(null);
1315
+ }
1316
+ onOverlayOutsideClick(event, field) {
1317
+ const target = event.target;
1318
+ if (target &&
1319
+ (target.closest(`.filter-anchor-${field}`) ||
1320
+ target.closest('.datepicker-dropdown-panel') ||
1321
+ target.closest('.talos-datepicker-host'))) {
1322
+ return;
1323
+ }
1324
+ this.closeFilterDialog();
1325
+ }
1326
+ getDistinctValues(col) {
1327
+ const raw = this.data() || [];
1328
+ const valuesSet = new Set();
1329
+ for (const item of raw) {
1330
+ const val = this.extractFieldValue(item, col.field);
1331
+ if (val === null || val === undefined) {
1332
+ valuesSet.add('');
1333
+ }
1334
+ else {
1335
+ valuesSet.add(String(val));
1336
+ }
1337
+ }
1338
+ return Array.from(valuesSet).sort((a, b) => {
1339
+ if (a === '')
1340
+ return 1;
1341
+ if (b === '')
1342
+ return -1;
1343
+ return a.localeCompare(b, undefined, { numeric: true, sensitivity: 'base' });
1344
+ });
1345
+ }
1346
+ getFilter(field) {
1347
+ return this.internalFilterStates().get(field);
1348
+ }
1349
+ hasFilter(field) {
1350
+ return this.internalFilterStates().has(field);
1351
+ }
1352
+ onFilterApplied(filter) {
1353
+ const map = new Map(this.internalFilterStates());
1354
+ map.set(filter.field, filter);
1355
+ this.internalFilterStates.set(map);
1356
+ this.internalPageIndex.set(0);
1357
+ this.filterChange.emit(this.activeFiltersList());
1358
+ }
1359
+ onFilterCleared(field) {
1360
+ const map = new Map(this.internalFilterStates());
1361
+ map.delete(field);
1362
+ this.internalFilterStates.set(map);
1363
+ this.internalPageIndex.set(0);
1364
+ this.filterChange.emit(this.activeFiltersList());
1365
+ }
1366
+ clearAllFilters() {
1367
+ this.internalFilterStates.set(new Map());
1368
+ this.internalPageIndex.set(0);
1369
+ this.filterChange.emit([]);
1370
+ }
1371
+ // Filter Matching Evaluator
1372
+ matchesFilter(rawValue, filter, col) {
1373
+ // Mode A: Set Filter (text / select / status)
1374
+ if (filter.mode === 'set') {
1375
+ if (!filter.selectedValues)
1376
+ return true;
1377
+ const strVal = rawValue === null || rawValue === undefined ? '' : String(rawValue);
1378
+ return filter.selectedValues.includes(strVal);
1379
+ }
1380
+ // Mode B: Condition Filter (numbers / dates / text)
1381
+ const op = filter.operator || 'eq';
1382
+ if (op === 'blank') {
1383
+ return rawValue === null || rawValue === undefined || rawValue === '';
1384
+ }
1385
+ if (op === 'notBlank') {
1386
+ return rawValue !== null && rawValue !== undefined && rawValue !== '';
1387
+ }
1388
+ if (rawValue === null || rawValue === undefined || rawValue === '') {
1389
+ return false;
1390
+ }
1391
+ const filterVal = filter.value;
1392
+ const secondVal = filter.secondValue;
1393
+ // Check if comparing dates
1394
+ const isDate = col?.type === 'date' ||
1395
+ col?.sortType === 'date' ||
1396
+ filterVal instanceof Date ||
1397
+ (typeof filterVal === 'string' && /^\d{4}-\d{2}-\d{2}/.test(filterVal)) ||
1398
+ rawValue instanceof Date;
1399
+ if (isDate) {
1400
+ const rowDate = this.normalizeDateToYMD(rawValue);
1401
+ const targetDate = this.normalizeDateToYMD(filterVal);
1402
+ const targetDate2 = secondVal ? this.normalizeDateToYMD(secondVal) : '';
1403
+ switch (op) {
1404
+ case 'eq':
1405
+ return rowDate === targetDate;
1406
+ case 'neq':
1407
+ return rowDate !== targetDate;
1408
+ case 'gt':
1409
+ return rowDate > targetDate;
1410
+ case 'gte':
1411
+ return rowDate >= targetDate;
1412
+ case 'lt':
1413
+ return rowDate < targetDate;
1414
+ case 'lte':
1415
+ return rowDate <= targetDate;
1416
+ case 'between': {
1417
+ if (!targetDate2)
1418
+ return rowDate >= targetDate;
1419
+ const minD = targetDate <= targetDate2 ? targetDate : targetDate2;
1420
+ const maxD = targetDate <= targetDate2 ? targetDate2 : targetDate;
1421
+ return rowDate >= minD && rowDate <= maxD;
1422
+ }
1423
+ default:
1424
+ return true;
1425
+ }
1426
+ }
1427
+ // Check if comparing numbers
1428
+ const isNumber = col?.type === 'number' || col?.sortType === 'number';
1429
+ const hasFilterNum = filterVal !== '' &&
1430
+ filterVal !== null &&
1431
+ filterVal !== undefined &&
1432
+ !isNaN(Number(filterVal));
1433
+ const numRaw = Number(rawValue);
1434
+ if ((isNumber || hasFilterNum) && !isNaN(numRaw)) {
1435
+ const numFilter = Number(filterVal);
1436
+ const numFilter2 = secondVal !== '' && secondVal !== null && secondVal !== undefined ? Number(secondVal) : NaN;
1437
+ switch (op) {
1438
+ case 'eq':
1439
+ return numRaw === numFilter;
1440
+ case 'neq':
1441
+ return numRaw !== numFilter;
1442
+ case 'gt':
1443
+ return numRaw > numFilter;
1444
+ case 'gte':
1445
+ return numRaw >= numFilter;
1446
+ case 'lt':
1447
+ return numRaw < numFilter;
1448
+ case 'lte':
1449
+ return numRaw <= numFilter;
1450
+ case 'between': {
1451
+ if (isNaN(numFilter2))
1452
+ return numRaw >= numFilter;
1453
+ const minN = Math.min(numFilter, numFilter2);
1454
+ const maxN = Math.max(numFilter, numFilter2);
1455
+ return numRaw >= minN && numRaw <= maxN;
1456
+ }
1457
+ default:
1458
+ return true;
1459
+ }
1460
+ }
1461
+ // String / Text comparison
1462
+ const strRaw = String(rawValue).toLowerCase().trim();
1463
+ const strFilter = String(filterVal ?? '')
1464
+ .toLowerCase()
1465
+ .trim();
1466
+ switch (op) {
1467
+ case 'eq':
1468
+ return strRaw === strFilter;
1469
+ case 'neq':
1470
+ return strRaw !== strFilter;
1471
+ case 'startsWith':
1472
+ return strRaw.startsWith(strFilter);
1473
+ case 'endsWith':
1474
+ return strRaw.endsWith(strFilter);
1475
+ case 'contains':
1476
+ default:
1477
+ return strRaw.includes(strFilter);
1478
+ }
1479
+ }
1480
+ normalizeDateToYMD(val) {
1481
+ if (!val)
1482
+ return '';
1483
+ if (typeof val === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(val)) {
1484
+ return val;
1485
+ }
1486
+ const d = new Date(val);
1487
+ if (isNaN(d.getTime()))
1488
+ return '';
1489
+ const y = d.getFullYear();
1490
+ const m = String(d.getMonth() + 1).padStart(2, '0');
1491
+ const day = String(d.getDate()).padStart(2, '0');
1492
+ return `${y}-${m}-${day}`;
1493
+ }
1494
+ // Pagination Handlers
1495
+ setPage(pageIndex) {
1496
+ const validPage = Math.max(0, Math.min(pageIndex, this.totalPages() - 1));
1497
+ if (validPage !== this.internalPageIndex()) {
1498
+ this.internalPageIndex.set(validPage);
1499
+ this.pageChange.emit({
1500
+ pageIndex: validPage,
1501
+ pageSize: this.internalPageSize(),
1502
+ totalItems: this.totalCount(),
1503
+ });
1504
+ }
1505
+ }
1506
+ onPageSizeChange(event) {
1507
+ const target = event.target;
1508
+ const newSize = Number(target.value);
1509
+ this.internalPageSize.set(newSize);
1510
+ this.internalPageIndex.set(0);
1511
+ this.pageChange.emit({
1512
+ pageIndex: 0,
1513
+ pageSize: newSize,
1514
+ totalItems: this.totalCount(),
1515
+ });
1516
+ }
1517
+ // Cell Content & Row Click Helpers
1518
+ getCellValue(row, col) {
1519
+ return this.extractFieldValue(row, col.field);
1520
+ }
1521
+ formatCellValue(row, col) {
1522
+ const val = this.getCellValue(row, col);
1523
+ if (col.formatter) {
1524
+ return col.formatter(val, row);
1525
+ }
1526
+ if (val === null || val === undefined) {
1527
+ return '';
1528
+ }
1529
+ if (col.type === 'date' && val instanceof Date) {
1530
+ return val.toLocaleDateString();
1531
+ }
1532
+ return String(val);
1533
+ }
1534
+ onRowClick(row, index) {
1535
+ this.rowClick.emit({ row, index });
1536
+ }
1537
+ fieldPartsCache = new Map();
1538
+ extractFieldValue(item, field) {
1539
+ if (!item || !field)
1540
+ return undefined;
1541
+ if (!field.includes('.')) {
1542
+ return item[field];
1543
+ }
1544
+ let parts = this.fieldPartsCache.get(field);
1545
+ if (!parts) {
1546
+ parts = field.split('.');
1547
+ this.fieldPartsCache.set(field, parts);
1548
+ }
1549
+ let cur = item;
1550
+ for (let i = 0; i < parts.length; i++) {
1551
+ if (cur == null)
1552
+ return undefined;
1553
+ cur = cur[parts[i]];
1554
+ }
1555
+ return cur;
1556
+ }
1557
+ inferSortType(col) {
1558
+ if (!col)
1559
+ return 'string';
1560
+ if (col.type === 'number')
1561
+ return 'number';
1562
+ if (col.type === 'date')
1563
+ return 'date';
1564
+ return 'string';
1565
+ }
1566
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: TalosDataGridComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1567
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: TalosDataGridComponent, isStandalone: true, selector: "talos-data-grid", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: true, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: true, transformFunction: null }, density: { classPropertyName: "density", publicName: "density", isSignal: true, isRequired: false, transformFunction: null }, filterable: { classPropertyName: "filterable", publicName: "filterable", isSignal: true, isRequired: false, transformFunction: null }, pagination: { classPropertyName: "pagination", publicName: "pagination", isSignal: true, isRequired: false, transformFunction: null }, pageSize: { classPropertyName: "pageSize", publicName: "pageSize", isSignal: true, isRequired: false, transformFunction: null }, pageSizeOptions: { classPropertyName: "pageSizeOptions", publicName: "pageSizeOptions", isSignal: true, isRequired: false, transformFunction: null }, pageIndex: { classPropertyName: "pageIndex", publicName: "pageIndex", isSignal: true, isRequired: false, transformFunction: null }, totalItems: { classPropertyName: "totalItems", publicName: "totalItems", isSignal: true, isRequired: false, transformFunction: null }, trackBy: { classPropertyName: "trackBy", publicName: "trackBy", isSignal: true, isRequired: false, transformFunction: null }, striped: { classPropertyName: "striped", publicName: "striped", isSignal: true, isRequired: false, transformFunction: null }, bordered: { classPropertyName: "bordered", publicName: "bordered", isSignal: true, isRequired: false, transformFunction: null }, hoverable: { classPropertyName: "hoverable", publicName: "hoverable", isSignal: true, isRequired: false, transformFunction: null }, emptyMessage: { classPropertyName: "emptyMessage", publicName: "emptyMessage", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, clientSort: { classPropertyName: "clientSort", publicName: "clientSort", isSignal: true, isRequired: false, transformFunction: null }, clientPagination: { classPropertyName: "clientPagination", publicName: "clientPagination", isSignal: true, isRequired: false, transformFunction: null }, clientFilter: { classPropertyName: "clientFilter", publicName: "clientFilter", isSignal: true, isRequired: false, transformFunction: null }, resizable: { classPropertyName: "resizable", publicName: "resizable", isSignal: true, isRequired: false, transformFunction: null }, rowKey: { classPropertyName: "rowKey", publicName: "rowKey", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sortChange: "sortChange", filterChange: "filterChange", pageChange: "pageChange", rowClick: "rowClick", actionClick: "actionClick", columnResize: "columnResize" }, host: { classAttribute: "talos-data-grid-host" }, ngImport: i0, template: "<div\n class=\"talos-data-grid-container\"\n [class.density-compact]=\"density() === 'compact'\"\n [class.density-comfortable]=\"density() === 'comfortable'\"\n [class.density-spacious]=\"density() === 'spacious'\"\n [class.is-bordered]=\"bordered()\"\n [class.is-striped]=\"striped()\"\n [class.is-resizing]=\"isResizing()\"\n>\n <!-- Loading overlay -->\n @if (loading()) {\n <div class=\"talos-data-grid-loading-overlay\">\n <ng-container *ngComponentOutlet=\"loaderIcon\" />\n </div>\n }\n\n <!-- Main Scrollable Table Wrapper -->\n <div class=\"talos-data-grid-table-wrapper\">\n <table class=\"talos-data-grid-table\" [class.has-resizable-columns]=\"hasResizableColumns()\" role=\"grid\">\n <thead>\n <!-- Header Column Row -->\n <tr>\n @for (col of columns(); track col.field) {\n <th\n #thEl\n [style.width]=\"getColumnWidth(col)\"\n [style.min-width]=\"col.minWidth\"\n [style.max-width]=\"col.maxWidth\"\n [class.align-left]=\"!col.align || col.align === 'left'\"\n [class.align-center]=\"col.align === 'center'\"\n [class.align-right]=\"col.align === 'right'\"\n [class.is-sortable]=\"isSortable(col)\"\n [class.is-sorted]=\"isSorted(col)\"\n [class.is-resizable]=\"isResizable(col)\"\n [class.is-resizing]=\"isColumnResizing(col.field)\"\n (click)=\"onHeaderClick(col)\"\n role=\"columnheader\"\n [attr.aria-sort]=\"getAriaSort(col)\"\n >\n <div class=\"header-content-wrapper\">\n <span class=\"header-title\" [title]=\"col.header\">{{ col.header }}</span>\n @if (isSortable(col) || isFilterable(col)) {\n <div class=\"header-tools\">\n @if (isSortable(col)) {\n <button\n talosButton\n variant=\"ghost\"\n size=\"xs\"\n [iconOnly]=\"true\"\n type=\"button\"\n class=\"header-sort-btn\"\n [class.is-sorted]=\"isSorted(col)\"\n (click)=\"$event.stopPropagation(); onHeaderClick(col)\"\n [attr.aria-label]=\"'Sort by ' + col.header\"\n [title]=\"'Sort by ' + col.header\"\n >\n @if (getSortDirection(col) === 'asc') {\n <ng-container *ngComponentOutlet=\"arrowUpIcon\" />\n } @else if (getSortDirection(col) === 'desc') {\n <ng-container *ngComponentOutlet=\"arrowDownIcon\" />\n } @else {\n <ng-container *ngComponentOutlet=\"arrowUpDownIcon\" />\n }\n </button>\n }\n @if (isFilterable(col)) {\n <div\n class=\"talos-grid-filter-anchor\"\n [class]=\"'filter-anchor-' + col.field\"\n cdkOverlayOrigin\n #filterOrigin=\"cdkOverlayOrigin\"\n (click)=\"$event.stopPropagation()\"\n >\n <button\n talosButton\n variant=\"ghost\"\n size=\"xs\"\n [iconOnly]=\"true\"\n type=\"button\"\n class=\"header-filter-btn\"\n [class.is-active]=\"hasFilter(col.field)\"\n [class.is-open]=\"isFilterDialogOpen(col.field)\"\n (click)=\"toggleFilterDialog(col.field, $event)\"\n [attr.aria-label]=\"'Filter ' + col.header\"\n title=\"Filter\"\n >\n <ng-container *ngComponentOutlet=\"filterIcon\" />\n </button>\n\n <ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"filterOrigin\"\n [cdkConnectedOverlayOpen]=\"isFilterDialogOpen(col.field)\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayPush]=\"true\"\n (overlayOutsideClick)=\"onOverlayOutsideClick($event, col.field)\"\n >\n <talos-grid-filter-dialog\n [column]=\"col\"\n [distinctValues]=\"activeColumnDistinctValues()\"\n [currentFilter]=\"getFilter(col.field)\"\n [align]=\"col.align === 'right' ? 'right' : 'left'\"\n (filterApplied)=\"onFilterApplied($event)\"\n (filterCleared)=\"onFilterCleared(col.field)\"\n (closed)=\"closeFilterDialog()\"\n />\n </ng-template>\n </div>\n }\n </div>\n }\n </div>\n @if (isResizable(col)) {\n <div\n class=\"talos-grid-resize-handle\"\n [class.is-resizing]=\"isColumnResizing(col.field)\"\n (mousedown)=\"onResizeStart(col, $event, thEl)\"\n (touchstart)=\"onResizeStart(col, $event, thEl)\"\n (dblclick)=\"onResizeDoubleClick(col, $event)\"\n (click)=\"$event.stopPropagation()\"\n role=\"separator\"\n aria-orientation=\"vertical\"\n tabindex=\"0\"\n [attr.aria-label]=\"'Resize ' + col.header + ' column'\"\n (keydown)=\"onResizeKeydown(col, $event, thEl)\"\n ></div>\n }\n </th>\n }\n </tr>\n </thead>\n\n <!-- Table Body with TrackBy for Optimal Refresh Performance -->\n <tbody>\n @for (row of pagedData(); track trackByRow($index, row)) {\n <tr\n [class.is-hoverable]=\"hoverable()\"\n [class.is-clickable]=\"hasRowClickListeners\"\n (click)=\"onRowClick(row, $index)\"\n role=\"row\"\n >\n @for (col of columns(); track col.field) {\n <td\n [class.align-left]=\"!col.align || col.align === 'left'\"\n [class.align-center]=\"col.align === 'center'\"\n [class.align-right]=\"col.align === 'right'\"\n role=\"gridcell\"\n >\n @if (col.cellTemplate) {\n <ng-container\n *ngTemplateOutlet=\"\n col.cellTemplate;\n context: {\n $implicit: getCellValue(row, col),\n row: row,\n index: $index,\n column: col,\n }\n \"\n />\n } @else if (col.type === 'status') {\n <talos-grid-status-cell\n [row]=\"row\"\n [column]=\"col\"\n [rawValue]=\"getCellValue(row, col)\"\n />\n } @else if (col.type === 'actions' && col.actionsConfig) {\n <talos-grid-actions-cell\n [row]=\"row\"\n [rowIndex]=\"$index\"\n [actions]=\"col.actionsConfig\"\n (actionClick)=\"actionClick.emit($event)\"\n />\n } @else {\n {{ row | talosGridCellFormat: col }}\n }\n </td>\n }\n </tr>\n }\n </tbody>\n </table>\n\n <!-- Empty State -->\n @if (pagedData().length === 0) {\n <div class=\"talos-data-grid-empty\">\n <div class=\"empty-icon\">\n <ng-container *ngComponentOutlet=\"inboxIcon\" />\n </div>\n <div class=\"empty-text\">\n @if (hasActiveFilters()) {\n <span>No results matching the active filters.</span>\n } @else {\n <span>{{ emptyMessage() }}</span>\n }\n </div>\n @if (hasActiveFilters()) {\n <button talosButton variant=\"outline\" size=\"sm\" (click)=\"clearAllFilters()\">\n Clear Filters\n </button>\n }\n </div>\n }\n </div>\n\n <!-- Pagination Bar -->\n @if (pagination()) {\n <div class=\"talos-data-grid-pagination\">\n <div class=\"pagination-left\">\n <span class=\"pagination-info\">\n Showing {{ startRecord() }} to {{ endRecord() }} of {{ totalCount() }} entries\n </span>\n\n @if (pageSizeOptions().length > 1) {\n <div class=\"pagination-size-selector\">\n <span>Rows per page:</span>\n <select\n [value]=\"internalPageSize()\"\n (change)=\"onPageSizeChange($event)\"\n aria-label=\"Select page size\"\n >\n @for (size of pageSizeOptions(); track size) {\n <option [value]=\"size\">{{ size }}</option>\n }\n </select>\n </div>\n }\n </div>\n\n <div class=\"pagination-right\">\n <button\n talosButton\n variant=\"ghost\"\n size=\"sm\"\n [iconOnly]=\"true\"\n [disabled]=\"internalPageIndex() <= 0\"\n (click)=\"setPage(0)\"\n title=\"First page\"\n aria-label=\"First page\"\n >\n <ng-container *ngComponentOutlet=\"chevronsLeftIcon\" />\n </button>\n\n <button\n talosButton\n variant=\"ghost\"\n size=\"sm\"\n [iconOnly]=\"true\"\n [disabled]=\"internalPageIndex() <= 0\"\n (click)=\"setPage(internalPageIndex() - 1)\"\n title=\"Previous page\"\n aria-label=\"Previous page\"\n >\n <ng-container *ngComponentOutlet=\"chevronLeftIcon\" />\n </button>\n\n <span class=\"page-indicator\">\n Page {{ internalPageIndex() + 1 }} of {{ totalPages() }}\n </span>\n\n <button\n talosButton\n variant=\"ghost\"\n size=\"sm\"\n [iconOnly]=\"true\"\n [disabled]=\"internalPageIndex() >= totalPages() - 1\"\n (click)=\"setPage(internalPageIndex() + 1)\"\n title=\"Next page\"\n aria-label=\"Next page\"\n >\n <ng-container *ngComponentOutlet=\"chevronRightIcon\" />\n </button>\n\n <button\n talosButton\n variant=\"ghost\"\n size=\"sm\"\n [iconOnly]=\"true\"\n [disabled]=\"internalPageIndex() >= totalPages() - 1\"\n (click)=\"setPage(totalPages() - 1)\"\n title=\"Last page\"\n aria-label=\"Last page\"\n >\n <ng-container *ngComponentOutlet=\"chevronsRightIcon\" />\n </button>\n </div>\n </div>\n }\n</div>\n", styles: [":host{display:block;width:100%;font-family:var(--talos-font-sans, system-ui, -apple-system, sans-serif);color:var(--talos-text-primary, #0f172a)}.talos-data-grid-container{display:flex;flex-direction:column;background-color:var(--talos-bg-surface, #ffffff);border-radius:var(--talos-radius-lg, 8px);position:relative;width:100%;overflow:hidden}.talos-data-grid-container.is-bordered{border:1px solid var(--talos-border-default, #cbd5e1)}.talos-data-grid-container.is-resizing{cursor:col-resize!important;-webkit-user-select:none!important;user-select:none!important}.talos-data-grid-container.is-resizing *{-webkit-user-select:none!important;user-select:none!important;cursor:col-resize!important}.talos-data-grid-table-wrapper{width:100%;overflow-x:auto;overflow-y:visible;position:relative;scrollbar-width:thin;scrollbar-color:var(--talos-border-default, #cbd5e1) transparent}.talos-data-grid-table-wrapper::-webkit-scrollbar{height:6px;width:6px}.talos-data-grid-table-wrapper::-webkit-scrollbar-thumb{background:var(--talos-border-default, #cbd5e1);border-radius:4px}.talos-data-grid-table{width:100%;min-width:100%;border-collapse:collapse;text-align:left;font-size:var(--talos-font-size-sm, 13px);line-height:var(--talos-line-height-normal, 1.5)}.talos-data-grid-table.has-resizable-columns{table-layout:fixed;width:max-content;min-width:100%}.talos-data-grid-container.density-compact{--grid-header-padding-y: 6px;--grid-header-padding-x: 10px;--grid-cell-padding-y: 5px;--grid-cell-padding-x: 10px;--grid-font-size: 12px;--grid-header-font-size: 11px;--grid-filter-height: 24px}.talos-data-grid-container.density-comfortable{--grid-header-padding-y: 10px;--grid-header-padding-x: 14px;--grid-cell-padding-y: 9px;--grid-cell-padding-x: 14px;--grid-font-size: 13.5px;--grid-header-font-size: 12px;--grid-filter-height: 28px}.talos-data-grid-container.density-spacious{--grid-header-padding-y: 14px;--grid-header-padding-x: 18px;--grid-cell-padding-y: 14px;--grid-cell-padding-x: 18px;--grid-font-size: 14.5px;--grid-header-font-size: 13px;--grid-filter-height: 32px}.talos-data-grid-table thead th{position:relative;background-color:var(--talos-bg-subtle, #f8fafc);color:var(--talos-text-secondary, #475569);font-size:var(--grid-header-font-size, 12px);font-weight:var(--talos-font-weight-semibold, 600);text-transform:uppercase;letter-spacing:.04em;padding:var(--grid-header-padding-y) 8px var(--grid-header-padding-y) var(--grid-header-padding-x);border-bottom:1px solid var(--talos-border-default, #cbd5e1);white-space:nowrap;-webkit-user-select:none;user-select:none;vertical-align:middle;overflow:hidden}.talos-data-grid-table thead th.is-sortable{cursor:pointer;transition:background-color var(--talos-transition, .15s ease)}.talos-data-grid-table thead th.is-sortable:hover{background-color:var(--talos-neutral-subtle-bg-hover, #f1f5f9);color:var(--talos-text-primary, #0f172a)}.talos-data-grid-table thead th.is-sorted{color:var(--talos-primary, #2563eb);background-color:var(--talos-primary-subtle-bg, #eff6ff)}.talos-data-grid-table thead th.is-resizable{padding-right:10px}.talos-data-grid-table thead th.is-resizing{background-color:var(--talos-neutral-subtle-bg-hover, #f1f5f9)}.header-content-wrapper{display:flex;align-items:center;gap:4px;width:100%;min-width:0;overflow:hidden}.header-title{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.header-tools{display:inline-flex;align-items:center;gap:2px;flex-shrink:0}.sort-icon-wrapper{display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;opacity:.45;transition:opacity var(--talos-transition, .15s ease),transform .15s ease}.sort-icon-wrapper ::ng-deep svg,.sort-icon-wrapper ::ng-deep .lucide,.sort-icon-wrapper svg,.sort-icon-wrapper .lucide{width:13px!important;height:13px!important;display:block}.is-sortable:hover .sort-icon-wrapper{opacity:.85}.is-sorted .sort-icon-wrapper{opacity:1;color:var(--talos-primary, #2563eb)}.header-sort-btn{width:22px!important;height:22px!important;min-width:22px!important;padding:0!important;border-radius:var(--talos-radius-sm, 4px)!important;border:1px solid transparent!important;background:transparent!important;color:var(--talos-text-muted, #94a3b8)!important;cursor:pointer;opacity:.45;transition:all var(--talos-transition, .15s ease);box-shadow:none!important}.header-sort-btn ::ng-deep svg,.header-sort-btn ::ng-deep .lucide,.header-sort-btn svg,.header-sort-btn .lucide{width:13px!important;height:13px!important;display:block;flex-shrink:0}th:hover .header-sort-btn{opacity:.85}.header-sort-btn:hover{opacity:1;color:var(--talos-text-primary, #0f172a)!important;background:var(--talos-neutral-subtle-bg-hover, rgba(0, 0, 0, .06))!important}.header-sort-btn.is-sorted{opacity:1;color:var(--talos-primary, #2563eb)!important;background:var(--talos-primary-subtle-bg, #eff6ff)!important}.align-left{text-align:left}.align-left .header-content-wrapper{justify-content:flex-start}.align-center{text-align:center}.align-center .header-content-wrapper{justify-content:center}.align-right{text-align:right}.align-right .header-content-wrapper{justify-content:flex-end}.talos-grid-resize-handle{position:absolute;top:0;right:0;bottom:0;width:8px;cursor:col-resize;-webkit-user-select:none;user-select:none;touch-action:none;z-index:2;display:flex;align-items:center;justify-content:center}.talos-grid-resize-handle:after{content:\"\";display:block;width:2px;height:50%;border-radius:1px;background-color:transparent;transition:background-color var(--talos-transition, .15s ease),height var(--talos-transition, .15s ease)}.talos-grid-resize-handle:hover:after,.talos-grid-resize-handle:focus-visible:after,.talos-grid-resize-handle.is-resizing:after{background-color:var(--talos-primary, #2563eb);height:100%}.talos-grid-resize-handle:focus-visible{outline:none}.talos-grid-filter-anchor{position:relative;display:inline-flex;align-items:center}.header-filter-btn{display:inline-flex;align-items:center;justify-content:center;width:22px!important;height:22px!important;min-width:22px!important;padding:0!important;border-radius:var(--talos-radius-sm, 4px)!important;border:1px solid transparent!important;background:transparent!important;color:var(--talos-text-muted, #94a3b8)!important;cursor:pointer;opacity:.5;transition:all var(--talos-transition, .15s ease);box-shadow:none!important}.header-filter-btn ::ng-deep svg,.header-filter-btn ::ng-deep .lucide,.header-filter-btn svg,.header-filter-btn .lucide{width:13px!important;height:13px!important;display:block;flex-shrink:0}th:hover .header-filter-btn{opacity:.85}.header-filter-btn:hover,.header-filter-btn.is-open{opacity:1;color:var(--talos-text-primary, #0f172a)!important;background:var(--talos-neutral-subtle-bg-hover, rgba(0, 0, 0, .06))!important}.header-filter-btn.is-active{opacity:1;color:#fff!important;background:var(--talos-primary, #2563eb)!important;border-color:var(--talos-primary, #2563eb)!important}.talos-data-grid-table tbody tr{border-bottom:1px solid var(--talos-border-subtle, #f1f5f9);transition:background-color var(--talos-transition, .12s ease)}.talos-data-grid-table tbody tr.is-hoverable:hover{background-color:var(--talos-neutral-subtle-bg-hover, #f8fafc)}.talos-data-grid-table tbody tr.is-clickable{cursor:pointer}.talos-data-grid-container.is-striped .talos-data-grid-table tbody tr:nth-child(2n){background-color:var(--talos-bg-subtle, #fcfdfe)}.talos-data-grid-container.is-striped .talos-data-grid-table tbody tr:nth-child(2n).is-hoverable:hover{background-color:var(--talos-neutral-subtle-bg-hover, #f8fafc)}.talos-data-grid-table tbody td{padding:var(--grid-cell-padding-y) var(--grid-cell-padding-x);font-size:var(--grid-font-size, 13.5px);color:var(--talos-text-primary, #0f172a);vertical-align:middle;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.talos-data-grid-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:40px 20px;color:var(--talos-text-muted, #64748b);text-align:center;gap:12px}.talos-data-grid-empty .empty-icon{opacity:.5;width:36px;height:36px;display:flex;align-items:center;justify-content:center}.talos-data-grid-empty .empty-icon ::ng-deep svg,.talos-data-grid-empty .empty-icon ::ng-deep .lucide,.talos-data-grid-empty .empty-icon svg,.talos-data-grid-empty .empty-icon .lucide{width:36px;height:36px}.talos-data-grid-empty .empty-text{font-size:var(--talos-font-size-base, 14px);font-weight:var(--talos-font-weight-medium, 500)}.talos-data-grid-pagination{display:flex;align-items:center;justify-content:space-between;padding:10px 16px;border-top:1px solid var(--talos-border-default, #cbd5e1);background-color:var(--talos-bg-surface, #ffffff);gap:12px;flex-wrap:wrap}.pagination-left{display:flex;align-items:center;gap:12px}.pagination-info{font-size:var(--talos-font-size-sm, 13px);color:var(--talos-text-muted, #64748b)}.pagination-size-selector{display:flex;align-items:center;gap:6px;font-size:var(--talos-font-size-sm, 13px);color:var(--talos-text-secondary, #334155)}.pagination-size-selector select{height:28px;padding:0 8px;font-size:var(--talos-font-size-sm, 13px);border:1px solid var(--talos-border-default, #cbd5e1);border-radius:var(--talos-radius-sm, 4px);background-color:var(--talos-bg-surface, #ffffff);color:var(--talos-text-primary, #0f172a);cursor:pointer;outline:none}.pagination-size-selector select:focus{border-color:var(--talos-border-focus, #2563eb)}.pagination-right{display:flex;align-items:center;gap:4px}.pagination-right ::ng-deep button.talos-btn svg,.pagination-right ::ng-deep button.talos-btn .lucide{width:14px;height:14px}.page-indicator{font-size:var(--talos-font-size-sm, 13px);color:var(--talos-text-secondary, #334155);font-weight:var(--talos-font-weight-medium, 500);padding:0 8px}.talos-data-grid-loading-overlay{position:absolute;inset:0;background:#ffffffb3;display:flex;align-items:center;justify-content:center;z-index:10;-webkit-backdrop-filter:blur(1.5px);backdrop-filter:blur(1.5px)}.talos-data-grid-loading-overlay ::ng-deep svg,.talos-data-grid-loading-overlay ::ng-deep .lucide,.talos-data-grid-loading-overlay svg,.talos-data-grid-loading-overlay .lucide{width:24px;height:24px;animation:talos-grid-spin .8s linear infinite}@keyframes talos-grid-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"], dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: OverlayModule }, { kind: "directive", type: i1$1.CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation", "cdkConnectedOverlayUsePopover", "cdkConnectedOverlayMatchWidth", "cdkConnectedOverlay"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "directive", type: i1$1.CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "directive", type: TalosButtonDirective, selector: "button[talosButton], a[talosButton], button[talos-btn], a[talos-btn]", inputs: ["variant", "size", "iconOnly", "fullWidth", "pill", "loading", "disabled", "animation", "spinning", "popping", "feedback"] }, { kind: "component", type: TalosGridStatusCellComponent, selector: "talos-grid-status-cell", inputs: ["row", "column", "rawValue"] }, { kind: "component", type: TalosGridActionsCellComponent, selector: "talos-grid-actions-cell", inputs: ["row", "rowIndex", "actions"], outputs: ["actionClick"] }, { kind: "component", type: TalosGridFilterDialogComponent, selector: "talos-grid-filter-dialog", inputs: ["column", "distinctValues", "currentFilter", "align"], outputs: ["filterApplied", "filterCleared", "closed"] }, { kind: "pipe", type: TalosGridCellFormatPipe, name: "talosGridCellFormat" }] });
1568
+ }
1569
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: TalosDataGridComponent, decorators: [{
1570
+ type: Component,
1571
+ args: [{ selector: 'talos-data-grid', imports: [
1572
+ NgComponentOutlet,
1573
+ NgTemplateOutlet,
1574
+ OverlayModule,
1575
+ CdkConnectedOverlay,
1576
+ CdkOverlayOrigin,
1577
+ TalosButtonDirective,
1578
+ TalosGridStatusCellComponent,
1579
+ TalosGridActionsCellComponent,
1580
+ TalosGridFilterDialogComponent,
1581
+ TalosGridCellFormatPipe,
1582
+ ], host: {
1583
+ class: 'talos-data-grid-host',
1584
+ }, template: "<div\n class=\"talos-data-grid-container\"\n [class.density-compact]=\"density() === 'compact'\"\n [class.density-comfortable]=\"density() === 'comfortable'\"\n [class.density-spacious]=\"density() === 'spacious'\"\n [class.is-bordered]=\"bordered()\"\n [class.is-striped]=\"striped()\"\n [class.is-resizing]=\"isResizing()\"\n>\n <!-- Loading overlay -->\n @if (loading()) {\n <div class=\"talos-data-grid-loading-overlay\">\n <ng-container *ngComponentOutlet=\"loaderIcon\" />\n </div>\n }\n\n <!-- Main Scrollable Table Wrapper -->\n <div class=\"talos-data-grid-table-wrapper\">\n <table class=\"talos-data-grid-table\" [class.has-resizable-columns]=\"hasResizableColumns()\" role=\"grid\">\n <thead>\n <!-- Header Column Row -->\n <tr>\n @for (col of columns(); track col.field) {\n <th\n #thEl\n [style.width]=\"getColumnWidth(col)\"\n [style.min-width]=\"col.minWidth\"\n [style.max-width]=\"col.maxWidth\"\n [class.align-left]=\"!col.align || col.align === 'left'\"\n [class.align-center]=\"col.align === 'center'\"\n [class.align-right]=\"col.align === 'right'\"\n [class.is-sortable]=\"isSortable(col)\"\n [class.is-sorted]=\"isSorted(col)\"\n [class.is-resizable]=\"isResizable(col)\"\n [class.is-resizing]=\"isColumnResizing(col.field)\"\n (click)=\"onHeaderClick(col)\"\n role=\"columnheader\"\n [attr.aria-sort]=\"getAriaSort(col)\"\n >\n <div class=\"header-content-wrapper\">\n <span class=\"header-title\" [title]=\"col.header\">{{ col.header }}</span>\n @if (isSortable(col) || isFilterable(col)) {\n <div class=\"header-tools\">\n @if (isSortable(col)) {\n <button\n talosButton\n variant=\"ghost\"\n size=\"xs\"\n [iconOnly]=\"true\"\n type=\"button\"\n class=\"header-sort-btn\"\n [class.is-sorted]=\"isSorted(col)\"\n (click)=\"$event.stopPropagation(); onHeaderClick(col)\"\n [attr.aria-label]=\"'Sort by ' + col.header\"\n [title]=\"'Sort by ' + col.header\"\n >\n @if (getSortDirection(col) === 'asc') {\n <ng-container *ngComponentOutlet=\"arrowUpIcon\" />\n } @else if (getSortDirection(col) === 'desc') {\n <ng-container *ngComponentOutlet=\"arrowDownIcon\" />\n } @else {\n <ng-container *ngComponentOutlet=\"arrowUpDownIcon\" />\n }\n </button>\n }\n @if (isFilterable(col)) {\n <div\n class=\"talos-grid-filter-anchor\"\n [class]=\"'filter-anchor-' + col.field\"\n cdkOverlayOrigin\n #filterOrigin=\"cdkOverlayOrigin\"\n (click)=\"$event.stopPropagation()\"\n >\n <button\n talosButton\n variant=\"ghost\"\n size=\"xs\"\n [iconOnly]=\"true\"\n type=\"button\"\n class=\"header-filter-btn\"\n [class.is-active]=\"hasFilter(col.field)\"\n [class.is-open]=\"isFilterDialogOpen(col.field)\"\n (click)=\"toggleFilterDialog(col.field, $event)\"\n [attr.aria-label]=\"'Filter ' + col.header\"\n title=\"Filter\"\n >\n <ng-container *ngComponentOutlet=\"filterIcon\" />\n </button>\n\n <ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"filterOrigin\"\n [cdkConnectedOverlayOpen]=\"isFilterDialogOpen(col.field)\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayPush]=\"true\"\n (overlayOutsideClick)=\"onOverlayOutsideClick($event, col.field)\"\n >\n <talos-grid-filter-dialog\n [column]=\"col\"\n [distinctValues]=\"activeColumnDistinctValues()\"\n [currentFilter]=\"getFilter(col.field)\"\n [align]=\"col.align === 'right' ? 'right' : 'left'\"\n (filterApplied)=\"onFilterApplied($event)\"\n (filterCleared)=\"onFilterCleared(col.field)\"\n (closed)=\"closeFilterDialog()\"\n />\n </ng-template>\n </div>\n }\n </div>\n }\n </div>\n @if (isResizable(col)) {\n <div\n class=\"talos-grid-resize-handle\"\n [class.is-resizing]=\"isColumnResizing(col.field)\"\n (mousedown)=\"onResizeStart(col, $event, thEl)\"\n (touchstart)=\"onResizeStart(col, $event, thEl)\"\n (dblclick)=\"onResizeDoubleClick(col, $event)\"\n (click)=\"$event.stopPropagation()\"\n role=\"separator\"\n aria-orientation=\"vertical\"\n tabindex=\"0\"\n [attr.aria-label]=\"'Resize ' + col.header + ' column'\"\n (keydown)=\"onResizeKeydown(col, $event, thEl)\"\n ></div>\n }\n </th>\n }\n </tr>\n </thead>\n\n <!-- Table Body with TrackBy for Optimal Refresh Performance -->\n <tbody>\n @for (row of pagedData(); track trackByRow($index, row)) {\n <tr\n [class.is-hoverable]=\"hoverable()\"\n [class.is-clickable]=\"hasRowClickListeners\"\n (click)=\"onRowClick(row, $index)\"\n role=\"row\"\n >\n @for (col of columns(); track col.field) {\n <td\n [class.align-left]=\"!col.align || col.align === 'left'\"\n [class.align-center]=\"col.align === 'center'\"\n [class.align-right]=\"col.align === 'right'\"\n role=\"gridcell\"\n >\n @if (col.cellTemplate) {\n <ng-container\n *ngTemplateOutlet=\"\n col.cellTemplate;\n context: {\n $implicit: getCellValue(row, col),\n row: row,\n index: $index,\n column: col,\n }\n \"\n />\n } @else if (col.type === 'status') {\n <talos-grid-status-cell\n [row]=\"row\"\n [column]=\"col\"\n [rawValue]=\"getCellValue(row, col)\"\n />\n } @else if (col.type === 'actions' && col.actionsConfig) {\n <talos-grid-actions-cell\n [row]=\"row\"\n [rowIndex]=\"$index\"\n [actions]=\"col.actionsConfig\"\n (actionClick)=\"actionClick.emit($event)\"\n />\n } @else {\n {{ row | talosGridCellFormat: col }}\n }\n </td>\n }\n </tr>\n }\n </tbody>\n </table>\n\n <!-- Empty State -->\n @if (pagedData().length === 0) {\n <div class=\"talos-data-grid-empty\">\n <div class=\"empty-icon\">\n <ng-container *ngComponentOutlet=\"inboxIcon\" />\n </div>\n <div class=\"empty-text\">\n @if (hasActiveFilters()) {\n <span>No results matching the active filters.</span>\n } @else {\n <span>{{ emptyMessage() }}</span>\n }\n </div>\n @if (hasActiveFilters()) {\n <button talosButton variant=\"outline\" size=\"sm\" (click)=\"clearAllFilters()\">\n Clear Filters\n </button>\n }\n </div>\n }\n </div>\n\n <!-- Pagination Bar -->\n @if (pagination()) {\n <div class=\"talos-data-grid-pagination\">\n <div class=\"pagination-left\">\n <span class=\"pagination-info\">\n Showing {{ startRecord() }} to {{ endRecord() }} of {{ totalCount() }} entries\n </span>\n\n @if (pageSizeOptions().length > 1) {\n <div class=\"pagination-size-selector\">\n <span>Rows per page:</span>\n <select\n [value]=\"internalPageSize()\"\n (change)=\"onPageSizeChange($event)\"\n aria-label=\"Select page size\"\n >\n @for (size of pageSizeOptions(); track size) {\n <option [value]=\"size\">{{ size }}</option>\n }\n </select>\n </div>\n }\n </div>\n\n <div class=\"pagination-right\">\n <button\n talosButton\n variant=\"ghost\"\n size=\"sm\"\n [iconOnly]=\"true\"\n [disabled]=\"internalPageIndex() <= 0\"\n (click)=\"setPage(0)\"\n title=\"First page\"\n aria-label=\"First page\"\n >\n <ng-container *ngComponentOutlet=\"chevronsLeftIcon\" />\n </button>\n\n <button\n talosButton\n variant=\"ghost\"\n size=\"sm\"\n [iconOnly]=\"true\"\n [disabled]=\"internalPageIndex() <= 0\"\n (click)=\"setPage(internalPageIndex() - 1)\"\n title=\"Previous page\"\n aria-label=\"Previous page\"\n >\n <ng-container *ngComponentOutlet=\"chevronLeftIcon\" />\n </button>\n\n <span class=\"page-indicator\">\n Page {{ internalPageIndex() + 1 }} of {{ totalPages() }}\n </span>\n\n <button\n talosButton\n variant=\"ghost\"\n size=\"sm\"\n [iconOnly]=\"true\"\n [disabled]=\"internalPageIndex() >= totalPages() - 1\"\n (click)=\"setPage(internalPageIndex() + 1)\"\n title=\"Next page\"\n aria-label=\"Next page\"\n >\n <ng-container *ngComponentOutlet=\"chevronRightIcon\" />\n </button>\n\n <button\n talosButton\n variant=\"ghost\"\n size=\"sm\"\n [iconOnly]=\"true\"\n [disabled]=\"internalPageIndex() >= totalPages() - 1\"\n (click)=\"setPage(totalPages() - 1)\"\n title=\"Last page\"\n aria-label=\"Last page\"\n >\n <ng-container *ngComponentOutlet=\"chevronsRightIcon\" />\n </button>\n </div>\n </div>\n }\n</div>\n", styles: [":host{display:block;width:100%;font-family:var(--talos-font-sans, system-ui, -apple-system, sans-serif);color:var(--talos-text-primary, #0f172a)}.talos-data-grid-container{display:flex;flex-direction:column;background-color:var(--talos-bg-surface, #ffffff);border-radius:var(--talos-radius-lg, 8px);position:relative;width:100%;overflow:hidden}.talos-data-grid-container.is-bordered{border:1px solid var(--talos-border-default, #cbd5e1)}.talos-data-grid-container.is-resizing{cursor:col-resize!important;-webkit-user-select:none!important;user-select:none!important}.talos-data-grid-container.is-resizing *{-webkit-user-select:none!important;user-select:none!important;cursor:col-resize!important}.talos-data-grid-table-wrapper{width:100%;overflow-x:auto;overflow-y:visible;position:relative;scrollbar-width:thin;scrollbar-color:var(--talos-border-default, #cbd5e1) transparent}.talos-data-grid-table-wrapper::-webkit-scrollbar{height:6px;width:6px}.talos-data-grid-table-wrapper::-webkit-scrollbar-thumb{background:var(--talos-border-default, #cbd5e1);border-radius:4px}.talos-data-grid-table{width:100%;min-width:100%;border-collapse:collapse;text-align:left;font-size:var(--talos-font-size-sm, 13px);line-height:var(--talos-line-height-normal, 1.5)}.talos-data-grid-table.has-resizable-columns{table-layout:fixed;width:max-content;min-width:100%}.talos-data-grid-container.density-compact{--grid-header-padding-y: 6px;--grid-header-padding-x: 10px;--grid-cell-padding-y: 5px;--grid-cell-padding-x: 10px;--grid-font-size: 12px;--grid-header-font-size: 11px;--grid-filter-height: 24px}.talos-data-grid-container.density-comfortable{--grid-header-padding-y: 10px;--grid-header-padding-x: 14px;--grid-cell-padding-y: 9px;--grid-cell-padding-x: 14px;--grid-font-size: 13.5px;--grid-header-font-size: 12px;--grid-filter-height: 28px}.talos-data-grid-container.density-spacious{--grid-header-padding-y: 14px;--grid-header-padding-x: 18px;--grid-cell-padding-y: 14px;--grid-cell-padding-x: 18px;--grid-font-size: 14.5px;--grid-header-font-size: 13px;--grid-filter-height: 32px}.talos-data-grid-table thead th{position:relative;background-color:var(--talos-bg-subtle, #f8fafc);color:var(--talos-text-secondary, #475569);font-size:var(--grid-header-font-size, 12px);font-weight:var(--talos-font-weight-semibold, 600);text-transform:uppercase;letter-spacing:.04em;padding:var(--grid-header-padding-y) 8px var(--grid-header-padding-y) var(--grid-header-padding-x);border-bottom:1px solid var(--talos-border-default, #cbd5e1);white-space:nowrap;-webkit-user-select:none;user-select:none;vertical-align:middle;overflow:hidden}.talos-data-grid-table thead th.is-sortable{cursor:pointer;transition:background-color var(--talos-transition, .15s ease)}.talos-data-grid-table thead th.is-sortable:hover{background-color:var(--talos-neutral-subtle-bg-hover, #f1f5f9);color:var(--talos-text-primary, #0f172a)}.talos-data-grid-table thead th.is-sorted{color:var(--talos-primary, #2563eb);background-color:var(--talos-primary-subtle-bg, #eff6ff)}.talos-data-grid-table thead th.is-resizable{padding-right:10px}.talos-data-grid-table thead th.is-resizing{background-color:var(--talos-neutral-subtle-bg-hover, #f1f5f9)}.header-content-wrapper{display:flex;align-items:center;gap:4px;width:100%;min-width:0;overflow:hidden}.header-title{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.header-tools{display:inline-flex;align-items:center;gap:2px;flex-shrink:0}.sort-icon-wrapper{display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;opacity:.45;transition:opacity var(--talos-transition, .15s ease),transform .15s ease}.sort-icon-wrapper ::ng-deep svg,.sort-icon-wrapper ::ng-deep .lucide,.sort-icon-wrapper svg,.sort-icon-wrapper .lucide{width:13px!important;height:13px!important;display:block}.is-sortable:hover .sort-icon-wrapper{opacity:.85}.is-sorted .sort-icon-wrapper{opacity:1;color:var(--talos-primary, #2563eb)}.header-sort-btn{width:22px!important;height:22px!important;min-width:22px!important;padding:0!important;border-radius:var(--talos-radius-sm, 4px)!important;border:1px solid transparent!important;background:transparent!important;color:var(--talos-text-muted, #94a3b8)!important;cursor:pointer;opacity:.45;transition:all var(--talos-transition, .15s ease);box-shadow:none!important}.header-sort-btn ::ng-deep svg,.header-sort-btn ::ng-deep .lucide,.header-sort-btn svg,.header-sort-btn .lucide{width:13px!important;height:13px!important;display:block;flex-shrink:0}th:hover .header-sort-btn{opacity:.85}.header-sort-btn:hover{opacity:1;color:var(--talos-text-primary, #0f172a)!important;background:var(--talos-neutral-subtle-bg-hover, rgba(0, 0, 0, .06))!important}.header-sort-btn.is-sorted{opacity:1;color:var(--talos-primary, #2563eb)!important;background:var(--talos-primary-subtle-bg, #eff6ff)!important}.align-left{text-align:left}.align-left .header-content-wrapper{justify-content:flex-start}.align-center{text-align:center}.align-center .header-content-wrapper{justify-content:center}.align-right{text-align:right}.align-right .header-content-wrapper{justify-content:flex-end}.talos-grid-resize-handle{position:absolute;top:0;right:0;bottom:0;width:8px;cursor:col-resize;-webkit-user-select:none;user-select:none;touch-action:none;z-index:2;display:flex;align-items:center;justify-content:center}.talos-grid-resize-handle:after{content:\"\";display:block;width:2px;height:50%;border-radius:1px;background-color:transparent;transition:background-color var(--talos-transition, .15s ease),height var(--talos-transition, .15s ease)}.talos-grid-resize-handle:hover:after,.talos-grid-resize-handle:focus-visible:after,.talos-grid-resize-handle.is-resizing:after{background-color:var(--talos-primary, #2563eb);height:100%}.talos-grid-resize-handle:focus-visible{outline:none}.talos-grid-filter-anchor{position:relative;display:inline-flex;align-items:center}.header-filter-btn{display:inline-flex;align-items:center;justify-content:center;width:22px!important;height:22px!important;min-width:22px!important;padding:0!important;border-radius:var(--talos-radius-sm, 4px)!important;border:1px solid transparent!important;background:transparent!important;color:var(--talos-text-muted, #94a3b8)!important;cursor:pointer;opacity:.5;transition:all var(--talos-transition, .15s ease);box-shadow:none!important}.header-filter-btn ::ng-deep svg,.header-filter-btn ::ng-deep .lucide,.header-filter-btn svg,.header-filter-btn .lucide{width:13px!important;height:13px!important;display:block;flex-shrink:0}th:hover .header-filter-btn{opacity:.85}.header-filter-btn:hover,.header-filter-btn.is-open{opacity:1;color:var(--talos-text-primary, #0f172a)!important;background:var(--talos-neutral-subtle-bg-hover, rgba(0, 0, 0, .06))!important}.header-filter-btn.is-active{opacity:1;color:#fff!important;background:var(--talos-primary, #2563eb)!important;border-color:var(--talos-primary, #2563eb)!important}.talos-data-grid-table tbody tr{border-bottom:1px solid var(--talos-border-subtle, #f1f5f9);transition:background-color var(--talos-transition, .12s ease)}.talos-data-grid-table tbody tr.is-hoverable:hover{background-color:var(--talos-neutral-subtle-bg-hover, #f8fafc)}.talos-data-grid-table tbody tr.is-clickable{cursor:pointer}.talos-data-grid-container.is-striped .talos-data-grid-table tbody tr:nth-child(2n){background-color:var(--talos-bg-subtle, #fcfdfe)}.talos-data-grid-container.is-striped .talos-data-grid-table tbody tr:nth-child(2n).is-hoverable:hover{background-color:var(--talos-neutral-subtle-bg-hover, #f8fafc)}.talos-data-grid-table tbody td{padding:var(--grid-cell-padding-y) var(--grid-cell-padding-x);font-size:var(--grid-font-size, 13.5px);color:var(--talos-text-primary, #0f172a);vertical-align:middle;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.talos-data-grid-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:40px 20px;color:var(--talos-text-muted, #64748b);text-align:center;gap:12px}.talos-data-grid-empty .empty-icon{opacity:.5;width:36px;height:36px;display:flex;align-items:center;justify-content:center}.talos-data-grid-empty .empty-icon ::ng-deep svg,.talos-data-grid-empty .empty-icon ::ng-deep .lucide,.talos-data-grid-empty .empty-icon svg,.talos-data-grid-empty .empty-icon .lucide{width:36px;height:36px}.talos-data-grid-empty .empty-text{font-size:var(--talos-font-size-base, 14px);font-weight:var(--talos-font-weight-medium, 500)}.talos-data-grid-pagination{display:flex;align-items:center;justify-content:space-between;padding:10px 16px;border-top:1px solid var(--talos-border-default, #cbd5e1);background-color:var(--talos-bg-surface, #ffffff);gap:12px;flex-wrap:wrap}.pagination-left{display:flex;align-items:center;gap:12px}.pagination-info{font-size:var(--talos-font-size-sm, 13px);color:var(--talos-text-muted, #64748b)}.pagination-size-selector{display:flex;align-items:center;gap:6px;font-size:var(--talos-font-size-sm, 13px);color:var(--talos-text-secondary, #334155)}.pagination-size-selector select{height:28px;padding:0 8px;font-size:var(--talos-font-size-sm, 13px);border:1px solid var(--talos-border-default, #cbd5e1);border-radius:var(--talos-radius-sm, 4px);background-color:var(--talos-bg-surface, #ffffff);color:var(--talos-text-primary, #0f172a);cursor:pointer;outline:none}.pagination-size-selector select:focus{border-color:var(--talos-border-focus, #2563eb)}.pagination-right{display:flex;align-items:center;gap:4px}.pagination-right ::ng-deep button.talos-btn svg,.pagination-right ::ng-deep button.talos-btn .lucide{width:14px;height:14px}.page-indicator{font-size:var(--talos-font-size-sm, 13px);color:var(--talos-text-secondary, #334155);font-weight:var(--talos-font-weight-medium, 500);padding:0 8px}.talos-data-grid-loading-overlay{position:absolute;inset:0;background:#ffffffb3;display:flex;align-items:center;justify-content:center;z-index:10;-webkit-backdrop-filter:blur(1.5px);backdrop-filter:blur(1.5px)}.talos-data-grid-loading-overlay ::ng-deep svg,.talos-data-grid-loading-overlay ::ng-deep .lucide,.talos-data-grid-loading-overlay svg,.talos-data-grid-loading-overlay .lucide{width:24px;height:24px;animation:talos-grid-spin .8s linear infinite}@keyframes talos-grid-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"] }]
1585
+ }], ctorParameters: () => [], propDecorators: { data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: true }] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: true }] }], density: [{ type: i0.Input, args: [{ isSignal: true, alias: "density", required: false }] }], filterable: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterable", required: false }] }], pagination: [{ type: i0.Input, args: [{ isSignal: true, alias: "pagination", required: false }] }], pageSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSize", required: false }] }], pageSizeOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSizeOptions", required: false }] }], pageIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageIndex", required: false }] }], totalItems: [{ type: i0.Input, args: [{ isSignal: true, alias: "totalItems", required: false }] }], trackBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "trackBy", required: false }] }], striped: [{ type: i0.Input, args: [{ isSignal: true, alias: "striped", required: false }] }], bordered: [{ type: i0.Input, args: [{ isSignal: true, alias: "bordered", required: false }] }], hoverable: [{ type: i0.Input, args: [{ isSignal: true, alias: "hoverable", required: false }] }], emptyMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyMessage", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], clientSort: [{ type: i0.Input, args: [{ isSignal: true, alias: "clientSort", required: false }] }], clientPagination: [{ type: i0.Input, args: [{ isSignal: true, alias: "clientPagination", required: false }] }], clientFilter: [{ type: i0.Input, args: [{ isSignal: true, alias: "clientFilter", required: false }] }], resizable: [{ type: i0.Input, args: [{ isSignal: true, alias: "resizable", required: false }] }], rowKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowKey", required: false }] }], sortChange: [{ type: i0.Output, args: ["sortChange"] }], filterChange: [{ type: i0.Output, args: ["filterChange"] }], pageChange: [{ type: i0.Output, args: ["pageChange"] }], rowClick: [{ type: i0.Output, args: ["rowClick"] }], actionClick: [{ type: i0.Output, args: ["actionClick"] }], columnResize: [{ type: i0.Output, args: ["columnResize"] }] } });
1586
+
1587
+ class TalosDataGridModule {
1588
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: TalosDataGridModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
1589
+ static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "22.1.1", ngImport: i0, type: TalosDataGridModule, imports: [TalosDataGridComponent,
1590
+ TalosGridStatusCellComponent,
1591
+ TalosGridActionsCellComponent,
1592
+ TalosGridFilterDialogComponent,
1593
+ TalosGridCellFormatPipe], exports: [TalosDataGridComponent,
1594
+ TalosGridStatusCellComponent,
1595
+ TalosGridActionsCellComponent,
1596
+ TalosGridFilterDialogComponent,
1597
+ TalosGridCellFormatPipe] });
1598
+ static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: TalosDataGridModule, imports: [TalosDataGridComponent,
1599
+ TalosGridStatusCellComponent,
1600
+ TalosGridFilterDialogComponent] });
1601
+ }
1602
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: TalosDataGridModule, decorators: [{
1603
+ type: NgModule,
1604
+ args: [{
1605
+ imports: [
1606
+ TalosDataGridComponent,
1607
+ TalosGridStatusCellComponent,
1608
+ TalosGridActionsCellComponent,
1609
+ TalosGridFilterDialogComponent,
1610
+ TalosGridCellFormatPipe,
1611
+ ],
1612
+ exports: [
1613
+ TalosDataGridComponent,
1614
+ TalosGridStatusCellComponent,
1615
+ TalosGridActionsCellComponent,
1616
+ TalosGridFilterDialogComponent,
1617
+ TalosGridCellFormatPipe,
1618
+ ],
1619
+ }]
1620
+ }] });
1621
+
1622
+ /**
1623
+ * Generated bundle index. Do not edit.
1624
+ */
1625
+
1626
+ export { TalosDataGridComponent, TalosDataGridModule, TalosGridActionsCellComponent, TalosGridCellFormatPipe, TalosGridFilterDialogComponent, TalosGridStatusCellComponent };
1627
+ //# sourceMappingURL=weeehaoooz-talos-ui-data-display-data-grid.mjs.map