@scion/components 22.5.0 → 22.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/_index.scss +32 -88
  2. package/fesm2022/scion-components-common.mjs +133 -5
  3. package/fesm2022/scion-components-common.mjs.map +1 -1
  4. package/fesm2022/scion-components-dimension.mjs +3 -3
  5. package/fesm2022/scion-components-dimension.mjs.map +1 -1
  6. package/fesm2022/scion-components-icon.mjs +134 -98
  7. package/fesm2022/scion-components-icon.mjs.map +1 -1
  8. package/fesm2022/scion-components-menu.mjs +58 -51
  9. package/fesm2022/scion-components-menu.mjs.map +1 -1
  10. package/fesm2022/scion-components-sashbox.mjs +1 -1
  11. package/fesm2022/scion-components-sashbox.mjs.map +1 -1
  12. package/fesm2022/scion-components-splitter.mjs +1 -1
  13. package/fesm2022/scion-components-splitter.mjs.map +1 -1
  14. package/fesm2022/scion-components-table.mjs +352 -159
  15. package/fesm2022/scion-components-table.mjs.map +1 -1
  16. package/fesm2022/scion-components-text.mjs +83 -40
  17. package/fesm2022/scion-components-text.mjs.map +1 -1
  18. package/fesm2022/scion-components-throbber.mjs +1 -1
  19. package/fesm2022/scion-components-throbber.mjs.map +1 -1
  20. package/fesm2022/scion-components-viewport.mjs +31 -18
  21. package/fesm2022/scion-components-viewport.mjs.map +1 -1
  22. package/package.json +2 -2
  23. package/types/scion-components-common.d.ts +91 -4
  24. package/types/scion-components-icon.d.ts +42 -15
  25. package/types/scion-components-menu.d.ts +7 -2
  26. package/types/scion-components-sashbox.d.ts +1 -1
  27. package/types/scion-components-splitter.d.ts +1 -1
  28. package/types/scion-components-table.d.ts +440 -59
  29. package/types/scion-components-text.d.ts +6 -5
  30. package/types/scion-components-throbber.d.ts +1 -1
  31. package/types/scion-components-viewport.d.ts +20 -11
  32. package/viewport/scrollbar/_scrollbar.scss +12 -0
  33. package/design/icons/_scion-icon-font.scss +0 -50
@@ -1,9 +1,11 @@
1
1
  import * as i0 from '@angular/core';
2
- import { computed, inject, DEFAULT_CURRENCY_CODE, signal, InjectionToken, makeEnvironmentProviders, Injector, DestroyRef, runInInjectionContext, onIdle, Injectable, untracked, linkedSignal, isSignal, NgZone, effect, resource, assertNotInReactiveContext, assertInInjectionContext, Pipe, input, Component, output, viewChild, Directive, afterNextRender, ElementRef, debounced, viewChildren, ViewEncapsulation } from '@angular/core';
3
- import { coerceSignal, SCI_LOCALE, createDestroyableInjector, toLazyObservable, SciComponentOutletDirective, SciAttributesDirective } from '@scion/components/common';
2
+ import { computed, inject, Injector, DEFAULT_CURRENCY_CODE, untracked, signal, InjectionToken, makeEnvironmentProviders, DestroyRef, runInInjectionContext, onIdle, Injectable, linkedSignal, isSignal, NgZone, effect, resource, assertNotInReactiveContext, assertInInjectionContext, Pipe, input, Component, output, viewChild, Directive, afterNextRender, ElementRef, debounced, viewChildren, ViewEncapsulation } from '@angular/core';
3
+ import * as i1 from '@scion/components/common';
4
+ import { coerceSignal, SCI_LOCALE, ɵSingleActiveInjectorFactory as _SingleActiveInjectorFactory, toLazyObservable, ɵcreateDestroyableInjector as _createDestroyableInjector, SciComponentOutletDirective, SciTooltipOnOverflowDirective, TOOLTIP_ON_OVERFLOW_DIRECTIVE_ADDITIONAL_TEXT, TOOLTIP_ON_OVERFLOW_DIRECTIVE_SOURCE_ELEMENT, SciAttributesDirective } from '@scion/components/common';
4
5
  import { formatDate, DATE_PIPE_DEFAULT_OPTIONS, NgTemplateOutlet } from '@angular/common';
6
+ import { text, SciTextPipe } from '@scion/components/text';
5
7
  import { toObservable, rxResource, takeUntilDestroyed } from '@angular/core/rxjs-interop';
6
- import { Objects, clamp, runSafe, Observables, Arrays } from '@scion/toolkit/util';
8
+ import { Objects, Arrays, clamp, runSafe, Observables } from '@scion/toolkit/util';
7
9
  import { LatestTaskExecutor } from '@scion/toolkit/common';
8
10
  import { fromEvent, switchMap, concat, of, timer, firstValueFrom, defaultIfEmpty, throwError, combineLatestWith, tap, skip } from 'rxjs';
9
11
  import { map, startWith, first } from 'rxjs/operators';
@@ -14,9 +16,8 @@ import { SciIconComponent } from '@scion/components/icon';
14
16
  import { SciToolbarComponent, contributeMenu, ɵSciMenuService as _SciMenuService } from '@scion/components/menu';
15
17
  import { UUID } from '@scion/toolkit/uuid';
16
18
  import { SciSplitterComponent } from '@scion/components/splitter';
17
- import { text, SciTextPipe } from '@scion/components/text';
18
19
  import { SciThrobberComponent } from '@scion/components/throbber';
19
- import * as i1 from '@angular/forms';
20
+ import * as i2 from '@angular/forms';
20
21
  import { FormsModule } from '@angular/forms';
21
22
 
22
23
  /**
@@ -78,6 +79,39 @@ function ensureIso8601DatePrefix(isoDate) {
78
79
  }
79
80
  }
80
81
 
82
+ /*
83
+ * Copyright (c) 2018-2026 Swiss Federal Railways
84
+ *
85
+ * This program and the accompanying materials are made
86
+ * available under the terms of the Eclipse Public License 2.0
87
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
88
+ *
89
+ * SPDX-License-Identifier: EPL-2.0
90
+ */
91
+ /**
92
+ * Creates a {@link SciEnumValue} from the given enum-like input.
93
+ */
94
+ function coerceEnumValue(enumLike, options) {
95
+ const value = coerceSignal(enumLike, { coerceUndefined: true });
96
+ const enumeration = coerceSignal(options.enumeration);
97
+ const enumEntry = computed(() => value() !== undefined ? enumeration().find(entry => entry.value === value()) : undefined, /* @ts-ignore */
98
+ ...(ngDevMode ? [{ debugName: "enumEntry" }] : /* istanbul ignore next */ []));
99
+ const label = text(computed(() => enumEntry()?.label), { injector: options.injector ?? inject(Injector) });
100
+ const tooltip = text(computed(() => enumEntry()?.tooltip), { injector: options.injector ?? inject(Injector) });
101
+ return computed(() => {
102
+ if (!enumEntry()) {
103
+ return undefined;
104
+ }
105
+ return {
106
+ value: enumEntry().value,
107
+ label: label(),
108
+ icon: enumEntry().icon,
109
+ // Use tooltip if provided, or fall back to label, but only if it is not configured for display.
110
+ tooltip: tooltip() ?? (tooltip() === null || options.format.label ? undefined : label()),
111
+ };
112
+ });
113
+ }
114
+
81
115
  /*
82
116
  * Copyright (c) 2018-2026 Swiss Federal Railways
83
117
  *
@@ -101,7 +135,7 @@ class ɵSciTableColumnFactory {
101
135
  this.columns.push({
102
136
  ...column,
103
137
  type: 'string',
104
- value: item => coerceSignal(descriptor.value(item), { coerceUndefined: true }),
138
+ value: item => text(descriptor.value(item)),
105
139
  sortable: computed(() => this._table.sortable() && !!sortable),
106
140
  filterable: computed(() => this._table.filterable() && !!filterable),
107
141
  compare: typeof sortable === 'object' ? sortable.comparator : (a, b) => a.value.localeCompare(b.value),
@@ -128,15 +162,16 @@ class ɵSciTableColumnFactory {
128
162
  }
129
163
  addBooleanColumn(descriptorLike, value) {
130
164
  const descriptor = coerceColumnDescriptor(descriptorLike, value);
131
- const column = this.mapToColumn(descriptor);
132
- this.columns.push({
133
- ...column,
134
- type: 'boolean',
165
+ this.addEnumColumn({
166
+ ...descriptor,
135
167
  value: item => coerceSignal(descriptor.value(item), { coerceUndefined: true }),
136
- sortable: computed(() => this._table.sortable() && (descriptor.sortable ?? true)),
137
- filterable: computed(() => this._table.filterable() && (descriptor.filterable ?? true)),
138
- compare: (a, b) => a.value === b.value ? 0 : (a.value ? 1 : -1),
139
- matches: (text, context) => context.value === text,
168
+ values: [
169
+ { value: true, icon: 'scion:true', label: '%scion:components:yes.value', tooltip: null },
170
+ { value: false, icon: 'scion:false', label: '%scion:components:no.value', tooltip: null },
171
+ ],
172
+ format: { icon: true },
173
+ filterable: descriptor.filterable === false ? false : 'single',
174
+ referenceType: 'boolean',
140
175
  });
141
176
  return this;
142
177
  }
@@ -161,6 +196,48 @@ class ɵSciTableColumnFactory {
161
196
  });
162
197
  return this;
163
198
  }
199
+ addEnumColumn(descriptor) {
200
+ const column = this.mapToColumn(descriptor);
201
+ const enumeration = coerceSignal(descriptor.values);
202
+ const positionByEnumValue = computed(() => enumeration().reduce((acc, entry, index) => acc.set(entry.value, index), new Map()), /* @ts-ignore */
203
+ ...(ngDevMode ? [{ debugName: "positionByEnumValue" }] : /* istanbul ignore next */ []));
204
+ const format = descriptor.format
205
+ ? { icon: descriptor.format.icon ?? false, label: descriptor.format.label ?? false, filter: { icon: descriptor.format.filter?.icon ?? false } } // eslint-disable-line @stylistic/operator-linebreak
206
+ : { icon: false, label: true, filter: { icon: true } }; // eslint-disable-line @stylistic/operator-linebreak
207
+ this.columns.push({
208
+ ...column,
209
+ type: 'enum',
210
+ referenceType: descriptor.referenceType,
211
+ value: item => coerceEnumValue(descriptor.value(item), { enumeration, format }),
212
+ sortable: computed(() => this._table.sortable() && (descriptor.sortable ?? true)),
213
+ filterable: computed(() => this._table.filterable() && (descriptor.filterable ?? 'multi')),
214
+ enumeration,
215
+ compare: (a, b) => untracked(() => (positionByEnumValue().get(a.value.value) ?? 0) - (positionByEnumValue().get(b.value.value) ?? 0)),
216
+ matches: (filter, context) => {
217
+ if (typeof filter === 'string') { // table filter
218
+ return context.value.label.toLowerCase().includes(filter.toLowerCase());
219
+ }
220
+ else {
221
+ return filter.includes(context.value.value); // column filter
222
+ }
223
+ },
224
+ format,
225
+ });
226
+ return this;
227
+ }
228
+ addIconColumn(descriptor) {
229
+ const resizable = typeof descriptor.header === 'string' || !!descriptor.header?.label?.length;
230
+ this.addEnumColumn({
231
+ ...descriptor,
232
+ value: item => coerceSignal(descriptor.value(item), { coerceUndefined: true }),
233
+ format: { icon: true, filter: { icon: true } },
234
+ resizable: descriptor.resizable ?? resizable,
235
+ width: descriptor.width ?? (resizable ? undefined : 'max-content'), // adapt width to intrinsic size
236
+ minWidth: descriptor.minWidth ?? (resizable ? undefined : 32),
237
+ referenceType: 'icon',
238
+ });
239
+ return this;
240
+ }
164
241
  addComponentColumn(descriptor) {
165
242
  // Require a filter matcher if filterable on an array datasource.
166
243
  if (this._table.isArrayDatasource() && descriptor.filterable === true) {
@@ -178,7 +255,7 @@ class ɵSciTableColumnFactory {
178
255
  sortable: computed(() => this._table.sortable() && !!descriptor.sortable),
179
256
  filterable: computed(() => this._table.filterable() && !!descriptor.filterable),
180
257
  compare: typeof descriptor.sortable === 'object' ? descriptor.sortable.comparator : () => 0,
181
- matches: typeof descriptor.filterable === 'object' ? descriptor.filterable.matcher : () => true,
258
+ matches: typeof descriptor.filterable === 'object' ? descriptor.filterable.matcher : () => false,
182
259
  padding: descriptor.padding ?? true,
183
260
  });
184
261
  return this;
@@ -200,7 +277,7 @@ class ɵSciTableColumnFactory {
200
277
  sortable: computed(() => this._table.sortable() && !!descriptor.sortable),
201
278
  filterable: computed(() => this._table.filterable() && !!descriptor.filterable),
202
279
  compare: typeof descriptor.sortable === 'object' ? descriptor.sortable.comparator : () => 0,
203
- matches: typeof descriptor.filterable === 'object' ? descriptor.filterable.matcher : () => true,
280
+ matches: typeof descriptor.filterable === 'object' ? descriptor.filterable.matcher : () => false,
204
281
  padding: descriptor.padding ?? true,
205
282
  });
206
283
  return this;
@@ -224,7 +301,16 @@ class ɵSciTableColumnFactory {
224
301
  }
225
302
  return {
226
303
  name: name,
227
- header: coerceSignal(descriptor.header ?? ''),
304
+ header: untracked(() => {
305
+ if (typeof descriptor.header === 'string') {
306
+ return { label: text(descriptor.header), icon: undefined, tooltip: undefined };
307
+ }
308
+ return {
309
+ label: descriptor.header?.label !== undefined ? text(descriptor.header.label) : undefined,
310
+ icon: descriptor.header?.icon,
311
+ tooltip: descriptor.header?.tooltip !== undefined ? text(descriptor.header.tooltip) : undefined,
312
+ };
313
+ }),
228
314
  resizable: computed(() => this._table.resizable() && (descriptor.resizable ?? true)),
229
315
  width: computed(() => {
230
316
  const userSettings = this._table.userSettings().columns?.find(column => column.name === name);
@@ -238,14 +324,13 @@ class ɵSciTableColumnFactory {
238
324
  }
239
325
  }
240
326
  function coerceColumnDescriptor(argument1, argument2) {
241
- switch (typeof argument1) {
242
- case 'string':
243
- return { header: argument1, value: argument2 };
244
- case 'function':
245
- return { value: argument1 };
246
- default:
247
- return argument1;
327
+ if (argument2 === undefined) {
328
+ return typeof argument1 === 'function' ? { value: argument1 } : argument1;
248
329
  }
330
+ return {
331
+ header: argument1,
332
+ value: argument2,
333
+ };
249
334
  }
250
335
 
251
336
  /*
@@ -395,17 +480,22 @@ class Dataset {
395
480
  ...(ngDevMode ? [{ debugName: "count" }] : /* istanbul ignore next */ []));
396
481
  constructor(data, columns) {
397
482
  const tableCellValuePreloader = inject(SciTableCellValuePreloader);
398
- const dataset = computed(() => data().map(item => ({
399
- item,
400
- cells: columns().reduce((cells, column) => untracked(() => {
401
- // PERF: Cell values are computed lazily (on idle or first use) to defer expensive computations like date formatting.
402
- const tableCellValue = 'value' in column ? new eSciTableCellValue(tableCellValuePreloader, () => column.value(item)()) : undefined;
403
- return cells.set(column.name, {
404
- column: column,
405
- value: () => tableCellValue?.value(),
406
- });
407
- }), new Map()),
408
- })), /* @ts-ignore */
483
+ // Create a factory to instantiate a new injector per dataset run, releasing resources allocated in column value functions.
484
+ const injectorFactory = new _SingleActiveInjectorFactory();
485
+ const dataset = computed(() => {
486
+ const injector = injectorFactory.create();
487
+ return data().map(item => ({
488
+ item,
489
+ cells: columns().reduce((cells, column) => untracked(() => {
490
+ // PERF: Cell values are computed lazily (on idle or first use) to defer expensive computations like date formatting.
491
+ const tableCellValue = 'value' in column ? new eSciTableCellValue(tableCellValuePreloader, () => runInInjectionContext(injector, () => column.value(item)())) : undefined;
492
+ return cells.set(column.name, {
493
+ column: column,
494
+ value: () => tableCellValue?.value(),
495
+ });
496
+ }), new Map()),
497
+ }));
498
+ }, /* @ts-ignore */
409
499
  ...(ngDevMode ? [{ debugName: "dataset" }] : /* istanbul ignore next */ []));
410
500
  // Memoize filtered/sorted view; recomputes only when dataset or criteria change, not when scrolling through the view, as sorting is an expensive operation.
411
501
  this._dataview = computed(() => {
@@ -415,7 +505,7 @@ class Dataset {
415
505
  const sortCriteria = this.sortCriteria();
416
506
  // PERF: Do not track signals inside `Array.sort` to avoid Angular signal tracking overhead as the comparator runs repeatedly, degrading performance otherwise.
417
507
  return untracked(() => rows
418
- .filter(row => matchesRow(row, columnFilters) && matchesGlobalFilter(row, tableFilter))
508
+ .filter(row => matchesRow(row, columnFilters, { coerceEnumValueToArray: true }) && matchesGlobalFilter(row, tableFilter, { coerceEnumValueToArray: false }))
419
509
  .sort((a, b) => compareRows(a, b, sortCriteria))
420
510
  .map(row => row.item));
421
511
  }, /* @ts-ignore */
@@ -431,16 +521,16 @@ class Dataset {
431
521
  /**
432
522
  * Tests whether a row matches the given global filter.
433
523
  */
434
- function matchesGlobalFilter(row, filter) {
524
+ function matchesGlobalFilter(row, filter, options) {
435
525
  if (!filter?.trim()) {
436
526
  return true;
437
527
  }
438
528
  for (const cell of row.cells.values()) {
439
- const text = coerceFilterText(filter, { to: cell.column.type });
529
+ const text = cell.column.type === 'number' ? toNumber(filter) : filter;
440
530
  if (text === undefined) {
441
531
  continue;
442
532
  }
443
- if (matchesRow(row, [{ text, columnName: cell.column.name }])) {
533
+ if (matchesRow(row, [{ text, columnName: cell.column.name }], options)) {
444
534
  return true;
445
535
  }
446
536
  }
@@ -449,7 +539,7 @@ function matchesGlobalFilter(row, filter) {
449
539
  /**
450
540
  * Tests whether a row matches the given column filters.
451
541
  */
452
- function matchesRow(row, columnFilters) {
542
+ function matchesRow(row, columnFilters, options) {
453
543
  if (!columnFilters.length) {
454
544
  return true;
455
545
  }
@@ -458,7 +548,8 @@ function matchesRow(row, columnFilters) {
458
548
  if ('value' in cell.column && cell.value() === undefined) { // Component and template columns have no value.
459
549
  return false;
460
550
  }
461
- if (!cell.column.matches(columnFilter.text, { item: row.item, value: cell.value() })) {
551
+ const filter = cell.column.type === 'enum' && options.coerceEnumValueToArray ? Arrays.coerce(columnFilter.text) : columnFilter.text;
552
+ if (!cell.column.matches(filter, { item: row.item, value: cell.value() })) {
462
553
  return false;
463
554
  }
464
555
  }
@@ -483,6 +574,10 @@ function compareRows(row1, row2, sortCriteria) {
483
574
  if (value2 === undefined && value1 !== undefined) {
484
575
  return -1;
485
576
  }
577
+ // Prevent calling comparator for value columns where both values are undefined. Note that component and template columns have no value.
578
+ if (value1 === undefined && value2 === undefined && 'value' in cell1.column) {
579
+ return 0;
580
+ }
486
581
  const comparison = cell1.column.compare({ item: row1.item, value: value1 }, { item: row2.item, value: value2 });
487
582
  if (comparison !== 0) {
488
583
  return criterion.direction === 'asc' ? comparison : -comparison;
@@ -508,27 +603,11 @@ function markAsArrayDatasource(loader) {
508
603
  */
509
604
  const ARRAY_DATASOURCE_MARKER = Symbol('ARRAY_DATASOURCE_HINT');
510
605
  /**
511
- * Coerces the given filter text into the specified column data type.
606
+ * Converts the given string to a number, or `undefined` if it cannot be parsed.
512
607
  */
513
- function coerceFilterText(text, options) {
514
- switch (options.to) {
515
- case 'number': {
516
- const number = Number.parseFloat(text);
517
- return !Number.isNaN(number) ? number : undefined;
518
- }
519
- case 'boolean': {
520
- if (text === 'true' || text === '1') {
521
- return true;
522
- }
523
- if (text === 'false' || text === '0') {
524
- return false;
525
- }
526
- return undefined;
527
- }
528
- default: {
529
- return text;
530
- }
531
- }
608
+ function toNumber(numberLike) {
609
+ const number = Number.parseFloat(numberLike);
610
+ return !Number.isNaN(number) ? number : undefined;
532
611
  }
533
612
  /**
534
613
  * Provides lazy loading for the cell's value, preloading it when idle for better sort/filter performance.
@@ -625,32 +704,57 @@ class SciTableCache {
625
704
  *
626
705
  * SPDX-License-Identifier: EPL-2.0
627
706
  */
707
+ /**
708
+ * Provides row bindings to tables in the scope of the injector providing the bindings.
709
+ *
710
+ * Multiple providers can be registered. Providers at different injector tree levels form a hierarchy.
711
+ */
628
712
  function provideTableRowBinding(bindings) {
629
- return {
630
- provide: SCI_TABLE_ROW_BINDING,
631
- useValue: bindings,
632
- multi: true,
633
- };
713
+ return [
714
+ {
715
+ provide: SCI_TABLE_ROW_BINDING,
716
+ useValue: bindings,
717
+ multi: true,
718
+ },
719
+ {
720
+ provide: SCI_TABLE_ROW_BINDINGS,
721
+ useFactory: () => {
722
+ return [
723
+ // Collect bindings registered in parent injectors.
724
+ ...inject(SCI_TABLE_ROW_BINDINGS, { skipSelf: true, optional: true }) ?? [],
725
+ // Collect bindings registered in this injector.
726
+ ...inject(SCI_TABLE_ROW_BINDING),
727
+ ];
728
+ },
729
+ },
730
+ ];
634
731
  }
635
732
  class ɵSciTableRowBindingFactory {
636
- attributeBindings = new Array();
637
- classBindings = new Array();
638
- partBindings = new Array();
639
- addAttributeBinding(name, value) {
640
- if (value !== undefined) {
641
- this.attributeBindings.push([name, value]);
733
+ icons = new Array();
734
+ parts = new Array();
735
+ attributes = new Array();
736
+ classes = new Array();
737
+ addIconBinding(icon) {
738
+ if (icon) {
739
+ this.icons.push(icon);
642
740
  }
643
741
  return this;
644
742
  }
645
- addClassBinding(name) {
743
+ addPartBinding(name) {
646
744
  if (name) {
647
- this.classBindings.push(name);
745
+ this.parts.push(name);
648
746
  }
649
747
  return this;
650
748
  }
651
- addPartBinding(name) {
749
+ addAttributeBinding(name, value) {
750
+ if (value !== undefined) {
751
+ this.attributes.push([name, value]);
752
+ }
753
+ return this;
754
+ }
755
+ addClassBinding(name) {
652
756
  if (name) {
653
- this.partBindings.push(name);
757
+ this.classes.push(name);
654
758
  }
655
759
  return this;
656
760
  }
@@ -663,12 +767,20 @@ function coerceTableRowBindings(bindingsFn, item, index) {
663
767
  }, /* @ts-ignore */
664
768
  ...(ngDevMode ? [{ debugName: "factory" }] : /* istanbul ignore next */ []));
665
769
  return {
666
- attributes: computed(() => Object.fromEntries(factory().attributeBindings), { equal: Objects.isEqual }),
667
- cssClass: computed(() => factory().classBindings, { equal: Objects.isEqual }),
668
- part: computed(() => factory().partBindings, { equal: Objects.isEqual }),
770
+ icons: computed(() => factory().icons, { equal: Objects.isEqual }),
771
+ parts: computed(() => factory().parts, { equal: Objects.isEqual }),
772
+ attributes: computed(() => Object.fromEntries(factory().attributes), { equal: Objects.isEqual }),
773
+ classes: computed(() => factory().classes, { equal: Objects.isEqual }),
669
774
  };
670
775
  }
776
+ /**
777
+ * Multi-DI token for registering a {@link SciTableRowBindingFactory}.
778
+ */
671
779
  const SCI_TABLE_ROW_BINDING = new InjectionToken('SCI_TABLE_ROW_BINDING');
780
+ /**
781
+ * DI token to inject {@link SciTableRowBindingFactory} functions provided in the current and parent injectors.
782
+ */
783
+ const SCI_TABLE_ROW_BINDINGS = new InjectionToken('SCI_TABLE_ROW_BINDINGS');
672
784
 
673
785
  /*
674
786
  * Copyright (c) 2018-2026 Swiss Federal Railways
@@ -771,7 +883,7 @@ class ɵSciTable {
771
883
  this.rowActions = descriptor.rowActions;
772
884
  this._rowBindings = [
773
885
  ...descriptor.rowBindings ? [descriptor.rowBindings] : [],
774
- ...inject(SCI_TABLE_ROW_BINDING, { optional: true }) ?? [],
886
+ ...inject(SCI_TABLE_ROW_BINDINGS, { optional: true }) ?? [],
775
887
  ];
776
888
  this._trackBy = descriptor.trackBy;
777
889
  this._dataLoaderFn = isSignal(descriptor.ɵdatasource) ? arrayDatasource(descriptor.ɵdatasource, this.columns) : descriptor.ɵdatasource;
@@ -790,17 +902,13 @@ class ɵSciTable {
790
902
  this.tableViewRef.set(undefined);
791
903
  }
792
904
  computeColumns(descriptor) {
793
- // Create separate injection context per factory invocation to clean up allocated resources, like RxJS subscriptions.
794
- let injector;
795
- return computed(() => {
796
- injector?.destroy();
797
- injector = createDestroyableInjector({ parent: this._injector });
798
- return runInInjectionContext(injector, () => {
799
- const tableColumnFactory = new ɵSciTableColumnFactory(this);
800
- descriptor.columns(tableColumnFactory);
801
- return tableColumnFactory.columns;
802
- });
803
- });
905
+ // Create a factory to instantiate a new injector per column factory execution, releasing resources allocated in the previous execution.
906
+ const injectorFactory = new _SingleActiveInjectorFactory();
907
+ return computed(() => runInInjectionContext(injectorFactory.create(), () => {
908
+ const tableColumnFactory = new ɵSciTableColumnFactory(this);
909
+ descriptor.columns(tableColumnFactory);
910
+ return tableColumnFactory.columns;
911
+ }));
804
912
  }
805
913
  /**
806
914
  * Computes the rows currently visible in the viewport (+buffer).
@@ -920,7 +1028,7 @@ class ɵSciTable {
920
1028
  cancel: () => this._cache.deleteIfLoading(cacheKey),
921
1029
  };
922
1030
  }
923
- const cacheEntryInjector = createDestroyableInjector({ parent: this._injector });
1031
+ const cacheEntryInjector = _createDestroyableInjector({ parent: this._injector });
924
1032
  const cacheEntry = {
925
1033
  rows: runInInjectionContext(cacheEntryInjector, () => {
926
1034
  // Fetch data.
@@ -934,10 +1042,11 @@ class ɵSciTable {
934
1042
  columnFilters,
935
1043
  })), error => throwError(() => error));
936
1044
  const columns$ = toObservable(this.columns);
1045
+ const injectorFactory = new _SingleActiveInjectorFactory();
937
1046
  // Create a resource to track loading and error states.
938
1047
  return rxResource({
939
1048
  stream: () => tableResponse$.pipe(combineLatestWith(columns$), map(([response, columns]) => ({
940
- rows: this.mapItemsToRow(response.items, columns, pageStart),
1049
+ rows: runInInjectionContext(injectorFactory.create(), () => this.mapItemsToRow(response.items, columns, pageStart)),
941
1050
  totalCount: response.totalCount,
942
1051
  })), tap({
943
1052
  next: ({ totalCount }) => this.totalCount.set(totalCount),
@@ -996,12 +1105,9 @@ class ɵSciTable {
996
1105
  if (!column) {
997
1106
  throw Error(`[NullColumnError] Column '${options.columnName}' not found in table '${this.name()}'.`);
998
1107
  }
999
- this.filterCriteria.update(filterCriterion => {
1000
- const otherFilterCriteria = filterCriterion.filter(filterCriterion => filterCriterion.columnName !== options.columnName);
1001
- if (text === null) {
1002
- return otherFilterCriteria;
1003
- }
1004
- return otherFilterCriteria.concat({ columnName: column.name, text });
1108
+ this.filterCriteria.update(filterCriteria => {
1109
+ const newFilterCriteria = filterCriteria.filter(filterCriterion => filterCriterion.columnName !== options.columnName);
1110
+ return text === null ? newFilterCriteria : newFilterCriteria.concat({ columnName: column.name, text });
1005
1111
  });
1006
1112
  }
1007
1113
  updateSelectedItems(updateFn) {
@@ -1672,6 +1778,7 @@ class SciTableCellComponent {
1672
1778
  template = this.computeTemplate();
1673
1779
  templateContext = this.computeTemplateContext();
1674
1780
  partAttribute = this.computePartAttribute();
1781
+ value = this.computeValueAttribute();
1675
1782
  computeTemplate() {
1676
1783
  return computed(() => {
1677
1784
  const cell = this.cell();
@@ -1699,28 +1806,50 @@ class SciTableCellComponent {
1699
1806
  }
1700
1807
  computePartAttribute() {
1701
1808
  return computed(() => {
1809
+ const cell = this.cell();
1810
+ const referenceType = cell.type === 'enum' ? cell.column.referenceType : undefined;
1702
1811
  return Arrays.distinct([
1703
- // Enable column styling based on column type.
1704
- `column:${this.cell().column.type}`,
1812
+ // Enable column styling based on column type. For enum columns, resolves to the reference type, if any, i.e., boolean or icon.
1813
+ `column:${referenceType ?? cell.column.type}`,
1705
1814
  // Enable column styling based on column name.
1706
- this.cell().column.name,
1815
+ cell.column.name,
1707
1816
  // Enable row styling based on selection state.
1708
1817
  this.row().selected() ? 'row:selected' : 'row:not:selected',
1709
1818
  // Enable row styling based on active state.
1710
1819
  this.row().active() ? 'row:active' : 'row:not:active',
1711
1820
  // Enable row styling based on row bindings.
1712
- ...Arrays.coerce(this.row().bindings?.part()),
1821
+ ...Arrays.coerce(this.row().bindings?.parts()),
1713
1822
  ]).join(' ');
1714
1823
  });
1715
1824
  }
1825
+ computeValueAttribute() {
1826
+ return computed(() => {
1827
+ const cell = this.cell();
1828
+ const value = (() => {
1829
+ switch (cell.type) {
1830
+ case 'string':
1831
+ case 'number':
1832
+ return cell.value();
1833
+ case 'enum':
1834
+ return cell.value()?.value;
1835
+ case 'date':
1836
+ return cell.value()?.isoString;
1837
+ default:
1838
+ return undefined;
1839
+ }
1840
+ })();
1841
+ return value !== undefined ? `${value}` : undefined;
1842
+ });
1843
+ }
1716
1844
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: SciTableCellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1717
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: SciTableCellComponent, isStandalone: true, selector: "sci-table-cell", inputs: { cell: { classPropertyName: "cell", publicName: "cell", isSignal: true, isRequired: true, transformFunction: null }, row: { classPropertyName: "row", publicName: "row", isSignal: true, isRequired: true, transformFunction: null } }, host: { properties: { "attr.data-type": "cell().column.type", "attr.data-column": "cell().column.name", "attr.data-padding": "!cell().column.padding ? false : null", "attr.part": "partAttribute()" } }, ngImport: i0, template: "@let cell = this.cell();\n\n@switch (cell.type) {\n @case ('template') {\n <ng-template *ngTemplateOutlet=\"template(); context: templateContext()\"/>\n }\n @case ('component') {\n <ng-container *sciComponentOutlet=\"cell.component\"/>\n }\n @case ('boolean') {\n @if (cell.value() !== undefined) {\n <!-- TODO [dwi] Use another ligature for icon scion.clear -->\n <sci-icon class=\"checkbox\">{{cell.value() ? 'scion.checkmark' : 'scion.clear'}}</sci-icon>\n }\n }\n @case ('date') {\n {{cell.value()?.formatted}}\n }\n @case ('number') {\n {{cell.value() | sciNumber:cell.column.format()}}\n }\n @default {\n {{cell.value()}}\n }\n}\n", styles: ["@charset \"UTF-8\";:host{display:inline-grid;position:relative;border-inline:var(--\\275sci-table-cell-padding-inline) solid transparent;align-items:center;overflow:hidden}:host:is([data-type=string],[data-type=number],[data-type=date]){display:inline;white-space:nowrap;text-overflow:ellipsis;line-height:var(--\\275sci-table-row-height)}:host[data-type=number]{text-align:end}:host:is([data-type=number],[data-type=date]){font-variant-numeric:tabular-nums}:host[data-padding=false]{border-inline:none;align-items:stretch}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: SciIconComponent, selector: "sci-icon" }, { kind: "directive", type: SciComponentOutletDirective, selector: "ng-template[sciComponentOutlet]", inputs: ["sciComponentOutlet"] }, { kind: "pipe", type: SciNumberPipe, name: "sciNumber" }] });
1845
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: SciTableCellComponent, isStandalone: true, selector: "sci-table-cell", inputs: { cell: { classPropertyName: "cell", publicName: "cell", isSignal: true, isRequired: true, transformFunction: null }, row: { classPropertyName: "row", publicName: "row", isSignal: true, isRequired: true, transformFunction: null } }, host: { properties: { "attr.data-type": "cell().column.type", "attr.data-column": "cell().column.name", "attr.data-value": "value()", "attr.data-padding": "!cell().column.padding ? false : null", "attr.part": "partAttribute()" } }, hostDirectives: [{ directive: i1.SciTooltipOnOverflowDirective }], ngImport: i0, template: "@let cell = this.cell();\n\n@switch (cell.type) {\n @case ('template') {\n <ng-template *ngTemplateOutlet=\"template(); context: templateContext()\"/>\n }\n @case ('component') {\n <ng-container *sciComponentOutlet=\"cell.component\"/>\n }\n @case ('date') {\n {{cell.value()?.formatted}}\n }\n @case ('number') {\n {{cell.value() | sciNumber:cell.column.format()}}\n }\n @case ('enum') {\n @let value = cell.value();\n @if (value !== undefined) {\n @if (cell.column.format.icon && value.icon !== undefined) {\n <sci-icon [attr.title]=\"value.tooltip\">{{value.icon}}</sci-icon>\n }\n @if (cell.column.format.label) {\n <span>{{value.label}}</span>\n }\n }\n }\n @default {\n {{cell.value()}}\n }\n}\n", styles: ["@charset \"UTF-8\";:host{display:inline-grid;position:relative;border-inline:var(--\\275sci-table-cell-padding-inline) solid transparent;align-items:center;overflow:hidden}:host:is([data-type=string],[data-type=number],[data-type=date],[data-type=enum]){display:inline;white-space:nowrap;text-overflow:ellipsis;line-height:var(--\\275sci-table-row-height)}:host[data-type=number]{text-align:end}:host:is([data-type=number],[data-type=date]){font-variant-numeric:tabular-nums}:host[data-padding=false]{border-inline:none;align-items:stretch}:host>sci-icon:not(:last-child){margin-right:.75em}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: SciIconComponent, selector: "sci-icon" }, { kind: "directive", type: SciComponentOutletDirective, selector: "ng-template[sciComponentOutlet]", inputs: ["sciComponentOutlet"] }, { kind: "pipe", type: SciNumberPipe, name: "sciNumber" }] });
1718
1846
  }
1719
1847
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: SciTableCellComponent, decorators: [{
1720
1848
  type: Component,
1721
1849
  args: [{ selector: 'sci-table-cell', host: {
1722
1850
  '[attr.data-type]': 'cell().column.type',
1723
1851
  '[attr.data-column]': 'cell().column.name',
1852
+ '[attr.data-value]': 'value()',
1724
1853
  '[attr.data-padding]': '!cell().column.padding ? false : null',
1725
1854
  '[attr.part]': 'partAttribute()',
1726
1855
  }, imports: [
@@ -1728,7 +1857,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImpor
1728
1857
  SciIconComponent,
1729
1858
  SciComponentOutletDirective,
1730
1859
  SciNumberPipe,
1731
- ], template: "@let cell = this.cell();\n\n@switch (cell.type) {\n @case ('template') {\n <ng-template *ngTemplateOutlet=\"template(); context: templateContext()\"/>\n }\n @case ('component') {\n <ng-container *sciComponentOutlet=\"cell.component\"/>\n }\n @case ('boolean') {\n @if (cell.value() !== undefined) {\n <!-- TODO [dwi] Use another ligature for icon scion.clear -->\n <sci-icon class=\"checkbox\">{{cell.value() ? 'scion.checkmark' : 'scion.clear'}}</sci-icon>\n }\n }\n @case ('date') {\n {{cell.value()?.formatted}}\n }\n @case ('number') {\n {{cell.value() | sciNumber:cell.column.format()}}\n }\n @default {\n {{cell.value()}}\n }\n}\n", styles: ["@charset \"UTF-8\";:host{display:inline-grid;position:relative;border-inline:var(--\\275sci-table-cell-padding-inline) solid transparent;align-items:center;overflow:hidden}:host:is([data-type=string],[data-type=number],[data-type=date]){display:inline;white-space:nowrap;text-overflow:ellipsis;line-height:var(--\\275sci-table-row-height)}:host[data-type=number]{text-align:end}:host:is([data-type=number],[data-type=date]){font-variant-numeric:tabular-nums}:host[data-padding=false]{border-inline:none;align-items:stretch}\n"] }]
1860
+ ], hostDirectives: [
1861
+ SciTooltipOnOverflowDirective,
1862
+ ], template: "@let cell = this.cell();\n\n@switch (cell.type) {\n @case ('template') {\n <ng-template *ngTemplateOutlet=\"template(); context: templateContext()\"/>\n }\n @case ('component') {\n <ng-container *sciComponentOutlet=\"cell.component\"/>\n }\n @case ('date') {\n {{cell.value()?.formatted}}\n }\n @case ('number') {\n {{cell.value() | sciNumber:cell.column.format()}}\n }\n @case ('enum') {\n @let value = cell.value();\n @if (value !== undefined) {\n @if (cell.column.format.icon && value.icon !== undefined) {\n <sci-icon [attr.title]=\"value.tooltip\">{{value.icon}}</sci-icon>\n }\n @if (cell.column.format.label) {\n <span>{{value.label}}</span>\n }\n }\n }\n @default {\n {{cell.value()}}\n }\n}\n", styles: ["@charset \"UTF-8\";:host{display:inline-grid;position:relative;border-inline:var(--\\275sci-table-cell-padding-inline) solid transparent;align-items:center;overflow:hidden}:host:is([data-type=string],[data-type=number],[data-type=date],[data-type=enum]){display:inline;white-space:nowrap;text-overflow:ellipsis;line-height:var(--\\275sci-table-row-height)}:host[data-type=number]{text-align:end}:host:is([data-type=number],[data-type=date]){font-variant-numeric:tabular-nums}:host[data-padding=false]{border-inline:none;align-items:stretch}:host>sci-icon:not(:last-child){margin-right:.75em}\n"] }]
1732
1863
  }], propDecorators: { cell: [{ type: i0.Input, args: [{ isSignal: true, alias: "cell", required: true }] }], row: [{ type: i0.Input, args: [{ isSignal: true, alias: "row", required: true }] }] } });
1733
1864
 
1734
1865
  /*
@@ -1781,30 +1912,29 @@ class SciTableRowComponent {
1781
1912
  }
1782
1913
  contributeRowActions() {
1783
1914
  contributeMenu(this.rowActionsToolbarName, toolbar => {
1784
- const rowActionsFactoryFn = this.table().rowActions;
1785
- if (!this.row().loading && rowActionsFactoryFn) {
1786
- rowActionsFactoryFn(toolbar, this.row().item, this.row().index);
1915
+ if (!this.row().loading) {
1916
+ this.table().rowActions?.(toolbar, this.row().item, this.row().index);
1787
1917
  }
1788
1918
  });
1789
1919
  }
1790
1920
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: SciTableRowComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1791
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: SciTableRowComponent, isStandalone: true, selector: "sci-table-row", inputs: { row: { classPropertyName: "row", publicName: "row", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { primaryAction: "primaryAction" }, host: { listeners: { "click": "onRowClick($event)", "dblclick": "onRowDblClick($event)", "mouseenter": "onRowMouseEnter()", "mouseleave": "onRowMouseLeave($event)" }, properties: { "attr.data-active": "row().active() ? '' : null", "attr.data-selected": "row().selected() ? '' : null", "attr.data-hovered": "row().hovered() ? '' : null", "class": "row().bindings?.cssClass?.()" } }, viewQueries: [{ propertyName: "rowActionToolbar", first: true, predicate: SciToolbarComponent, descendants: true, isSignal: true }], ngImport: i0, template: "@let row = this.row();\n@for (cell of row.cells; track cell.column.name) {\n <sci-table-cell [cell]=\"cell\" [row]=\"row\"/>\n} @empty {\n @for (column of table().columns(); track column.name) {\n <div class=\"skeleton e2e-skeleton\" [attr.data-column]=\"column.name\"></div>\n }\n}\n\n@let hasRowActions = !!table().rowActions;\n@let isRowHovered = table().hoveredRow() === row;\n@let isRowActionMenuOpen = rowActionToolbar()?.menuOpen();\n\n@if (hasRowActions && (isRowHovered || isRowActionMenuOpen)) {\n <sci-toolbar [name]=\"rowActionsToolbarName\" (click)=\"onActionToolbarClick($event)\" class=\"row-actions\"/>\n}\n", styles: ["@charset \"UTF-8\";:host{display:inline-grid;grid-template-columns:subgrid;grid-auto-rows:var(--sci-table-row-height);align-items:stretch;-webkit-user-select:none;user-select:none;border-bottom:var(--\\275sci-table-border-size) solid var(--sci-table-gridline-color)}:host[data-hovered]{background-color:var(--sci-table-row-background-color-hover)}:host[data-selected]{background-color:var(--sci-table-row-background-color-selected);--\\275sci-table-row-selected: true}:host[data-hovered]:not([data-selected]){border-radius:var(--sci-table-row-border-radius)}:host:is(:first-child,:not([data-selected])+sci-table-row)[data-selected]{border-top-left-radius:var(--sci-table-row-border-radius);border-top-right-radius:var(--sci-table-row-border-radius)}:host[data-selected]:not(:has(+sci-table-row[data-selected])){border-bottom-left-radius:var(--sci-table-row-border-radius);border-bottom-right-radius:var(--sci-table-row-border-radius)}@container (style(--\\275sci-table-scrolling: true)) or (style(--\\275sci-table-resizing: true)){:host{pointer-events:none}}:host:has(>div.skeleton){pointer-events:none;align-items:center}:host:has(>div.skeleton)>div.skeleton{display:inline-block;position:relative;overflow:hidden;border-radius:var(--sci-corner-small);background-color:var(--sci-color-skeleton-subtle);opacity:0;animation-name:visiblity;animation-duration:.5s;animation-timing-function:ease-in;animation-fill-mode:forwards}:host:has(>div.skeleton)>div.skeleton:after{position:absolute;inset:0;transform:translate(-100%);background-image:linear-gradient(to right,transparent 0,color-mix(in srgb,light-dark(var(--sci-static-color-white),var(--sci-static-color-black)) 30%,transparent) 50%,transparent);animation:shimmer 2s infinite;content:\"\"}@keyframes shimmer{to{transform:translate(100%)}}@keyframes visiblity{to{opacity:1}}:host:has(>div.skeleton)>div.skeleton{height:1em;margin-inline:var(--\\275sci-table-cell-padding-inline)}:host>sci-toolbar.row-actions{position:sticky;right:0;transform:translate(calc(-1 * var(--\\275sci-table-cell-padding-inline)));justify-self:end;align-self:center;border-radius:var(--sci-corner);padding:1px;background-color:var(--\\275sci-table-row-action-background-color);z-index:2;--sci-toolbar-item-background-color-hover: color-mix(in srgb, var(--\\275sci-table-row-action-background-color) 85%, light-dark(var(--sci-static-color-black), var(--sci-static-color-white)));--sci-toolbar-item-background-color-active: color-mix(in srgb, var(--\\275sci-table-row-action-background-color) 75%, light-dark(var(--sci-static-color-black), var(--sci-static-color-white)))}@container not style(--\\275sci-table-row-selected: true){:host>sci-toolbar.row-actions{--\\275sci-table-row-action-background-color: var(--sci-table-row-action-background-color)}}@container style(--\\275sci-table-row-selected: true){:host>sci-toolbar.row-actions{--\\275sci-table-row-action-background-color: var(--sci-table-row-action-background-color-selected)}}:host>sci-toolbar.row-actions:empty{display:none}\n"], dependencies: [{ kind: "component", type: SciTableCellComponent, selector: "sci-table-cell", inputs: ["cell", "row"] }, { kind: "component", type: SciToolbarComponent, selector: "sci-toolbar", inputs: ["name", "orientation", "context", "acceleratorTarget", "popoverViewContainerRef"] }] });
1921
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: SciTableRowComponent, isStandalone: true, selector: "sci-table-row", inputs: { row: { classPropertyName: "row", publicName: "row", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { primaryAction: "primaryAction" }, host: { listeners: { "click": "onRowClick($event)", "dblclick": "onRowDblClick($event)", "mouseenter": "onRowMouseEnter()", "mouseleave": "onRowMouseLeave($event)" }, properties: { "attr.data-active": "row().active() ? '' : null", "attr.data-selected": "row().selected() ? '' : null", "attr.data-hovered": "row().hovered() ? '' : null" } }, viewQueries: [{ propertyName: "rowActionToolbar", first: true, predicate: SciToolbarComponent, descendants: true, isSignal: true }], ngImport: i0, template: "@let row = this.row();\n\n<!-- Row Icons -->\n@if (row.cells?.length) {\n @for (icon of row.bindings?.icons(); track $index) {\n <sci-icon>{{icon}}</sci-icon>\n }\n}\n\n<!-- Table Cells -->\n@for (cell of row.cells; track cell.column.name) {\n <sci-table-cell [cell]=\"cell\" [row]=\"row\"/>\n} @empty {\n @for (column of table().columns(); track column.name) {\n <div class=\"skeleton e2e-skeleton\" [attr.data-column]=\"column.name\"></div>\n }\n}\n\n<!--Row Actions -->\n@let hasRowActions = !!table().rowActions;\n@let isRowHovered = table().hoveredRow() === row;\n@let isRowActionMenuOpen = rowActionToolbar()?.menuOpen();\n\n@if (hasRowActions && (isRowHovered || isRowActionMenuOpen)) {\n <sci-toolbar [name]=\"rowActionsToolbarName\" (click)=\"onActionToolbarClick($event)\" class=\"row-actions\"/>\n}\n", styles: ["@charset \"UTF-8\";:host{display:inline-grid;grid-template-columns:subgrid;grid-auto-rows:var(--sci-table-row-height);align-items:stretch;-webkit-user-select:none;user-select:none;border-bottom:var(--\\275sci-table-border-size) solid var(--sci-table-gridline-color)}:host[data-hovered]{background-color:var(--sci-table-row-background-color-hover)}:host[data-selected]{background-color:var(--sci-table-row-background-color-selected);--\\275sci-table-row-selected: true}:host[data-hovered]:not([data-selected]){border-radius:var(--sci-table-row-border-radius)}:host:is(:first-child,:not([data-selected])+sci-table-row)[data-selected]{border-top-left-radius:var(--sci-table-row-border-radius);border-top-right-radius:var(--sci-table-row-border-radius)}:host[data-selected]:not(:has(+sci-table-row[data-selected])){border-bottom-left-radius:var(--sci-table-row-border-radius);border-bottom-right-radius:var(--sci-table-row-border-radius)}@container (style(--\\275sci-table-scrolling: true)) or (style(--\\275sci-table-resizing: true)){:host{pointer-events:none}}:host:has(>div.skeleton){pointer-events:none;align-items:center}:host:has(>div.skeleton)>div.skeleton{display:inline-block;position:relative;overflow:hidden;border-radius:var(--sci-corner-small);background-color:var(--sci-color-skeleton-subtle);opacity:0;animation-name:visiblity;animation-duration:.5s;animation-timing-function:ease-in;animation-fill-mode:forwards}:host:has(>div.skeleton)>div.skeleton:after{position:absolute;inset:0;transform:translate(-100%);background-image:linear-gradient(to right,transparent 0,color-mix(in srgb,light-dark(var(--sci-static-color-white),var(--sci-static-color-black)) 30%,transparent) 50%,transparent);animation:shimmer 2s infinite;content:\"\"}@keyframes shimmer{to{transform:translate(100%)}}@keyframes visiblity{to{opacity:1}}:host:has(>div.skeleton)>div.skeleton{height:1em;margin-inline:var(--\\275sci-table-cell-padding-inline)}:host>sci-icon:has(~sci-table-cell:first-of-type){grid-column:1;grid-row:1;align-self:center;--sci-icon-size: 1em;margin-left:calc(var(--\\275sci-table-cell-padding-inline) + (sibling-index() - 1) * 1.5em);width:auto;max-width:var(--sci-icon-size)}:host>sci-icon:has(~sci-table-cell:first-of-type)+sci-table-cell{grid-column:1;grid-row:1;border-left:calc(var(--\\275sci-table-cell-padding-inline) + (sibling-index() - 1) * 1.5em) solid transparent}:host>sci-toolbar.row-actions{position:sticky;right:0;transform:translate(calc(-1 * var(--\\275sci-table-cell-padding-inline)));justify-self:end;align-self:center;border-radius:var(--sci-corner);padding:1px;background-color:var(--\\275sci-table-row-action-background-color);z-index:2;--sci-toolbar-item-background-color-hover: color-mix(in srgb, var(--\\275sci-table-row-action-background-color) 85%, light-dark(var(--sci-static-color-black), var(--sci-static-color-white)));--sci-toolbar-item-background-color-active: color-mix(in srgb, var(--\\275sci-table-row-action-background-color) 75%, light-dark(var(--sci-static-color-black), var(--sci-static-color-white)))}@container not style(--\\275sci-table-row-selected: true){:host>sci-toolbar.row-actions{--\\275sci-table-row-action-background-color: var(--sci-table-row-action-background-color)}}@container style(--\\275sci-table-row-selected: true){:host>sci-toolbar.row-actions{--\\275sci-table-row-action-background-color: var(--sci-table-row-action-background-color-selected)}}:host>sci-toolbar.row-actions:empty{display:none}\n"], dependencies: [{ kind: "component", type: SciTableCellComponent, selector: "sci-table-cell", inputs: ["cell", "row"] }, { kind: "component", type: SciToolbarComponent, selector: "sci-toolbar", inputs: ["name", "orientation", "context", "acceleratorTarget", "popoverViewContainerRef"] }, { kind: "component", type: SciIconComponent, selector: "sci-icon" }] });
1792
1922
  }
1793
1923
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: SciTableRowComponent, decorators: [{
1794
1924
  type: Component,
1795
1925
  args: [{ selector: 'sci-table-row', imports: [
1796
1926
  SciTableCellComponent,
1797
1927
  SciToolbarComponent,
1928
+ SciIconComponent,
1798
1929
  ], host: {
1799
1930
  '[attr.data-active]': `row().active() ? '' : null`,
1800
1931
  '[attr.data-selected]': `row().selected() ? '' : null`,
1801
1932
  '[attr.data-hovered]': `row().hovered() ? '' : null`,
1802
- '[class]': 'row().bindings?.cssClass?.()',
1803
1933
  '(click)': 'onRowClick($event)',
1804
1934
  '(dblclick)': 'onRowDblClick($event)',
1805
1935
  '(mouseenter)': 'onRowMouseEnter()',
1806
1936
  '(mouseleave)': 'onRowMouseLeave($event)',
1807
- }, template: "@let row = this.row();\n@for (cell of row.cells; track cell.column.name) {\n <sci-table-cell [cell]=\"cell\" [row]=\"row\"/>\n} @empty {\n @for (column of table().columns(); track column.name) {\n <div class=\"skeleton e2e-skeleton\" [attr.data-column]=\"column.name\"></div>\n }\n}\n\n@let hasRowActions = !!table().rowActions;\n@let isRowHovered = table().hoveredRow() === row;\n@let isRowActionMenuOpen = rowActionToolbar()?.menuOpen();\n\n@if (hasRowActions && (isRowHovered || isRowActionMenuOpen)) {\n <sci-toolbar [name]=\"rowActionsToolbarName\" (click)=\"onActionToolbarClick($event)\" class=\"row-actions\"/>\n}\n", styles: ["@charset \"UTF-8\";:host{display:inline-grid;grid-template-columns:subgrid;grid-auto-rows:var(--sci-table-row-height);align-items:stretch;-webkit-user-select:none;user-select:none;border-bottom:var(--\\275sci-table-border-size) solid var(--sci-table-gridline-color)}:host[data-hovered]{background-color:var(--sci-table-row-background-color-hover)}:host[data-selected]{background-color:var(--sci-table-row-background-color-selected);--\\275sci-table-row-selected: true}:host[data-hovered]:not([data-selected]){border-radius:var(--sci-table-row-border-radius)}:host:is(:first-child,:not([data-selected])+sci-table-row)[data-selected]{border-top-left-radius:var(--sci-table-row-border-radius);border-top-right-radius:var(--sci-table-row-border-radius)}:host[data-selected]:not(:has(+sci-table-row[data-selected])){border-bottom-left-radius:var(--sci-table-row-border-radius);border-bottom-right-radius:var(--sci-table-row-border-radius)}@container (style(--\\275sci-table-scrolling: true)) or (style(--\\275sci-table-resizing: true)){:host{pointer-events:none}}:host:has(>div.skeleton){pointer-events:none;align-items:center}:host:has(>div.skeleton)>div.skeleton{display:inline-block;position:relative;overflow:hidden;border-radius:var(--sci-corner-small);background-color:var(--sci-color-skeleton-subtle);opacity:0;animation-name:visiblity;animation-duration:.5s;animation-timing-function:ease-in;animation-fill-mode:forwards}:host:has(>div.skeleton)>div.skeleton:after{position:absolute;inset:0;transform:translate(-100%);background-image:linear-gradient(to right,transparent 0,color-mix(in srgb,light-dark(var(--sci-static-color-white),var(--sci-static-color-black)) 30%,transparent) 50%,transparent);animation:shimmer 2s infinite;content:\"\"}@keyframes shimmer{to{transform:translate(100%)}}@keyframes visiblity{to{opacity:1}}:host:has(>div.skeleton)>div.skeleton{height:1em;margin-inline:var(--\\275sci-table-cell-padding-inline)}:host>sci-toolbar.row-actions{position:sticky;right:0;transform:translate(calc(-1 * var(--\\275sci-table-cell-padding-inline)));justify-self:end;align-self:center;border-radius:var(--sci-corner);padding:1px;background-color:var(--\\275sci-table-row-action-background-color);z-index:2;--sci-toolbar-item-background-color-hover: color-mix(in srgb, var(--\\275sci-table-row-action-background-color) 85%, light-dark(var(--sci-static-color-black), var(--sci-static-color-white)));--sci-toolbar-item-background-color-active: color-mix(in srgb, var(--\\275sci-table-row-action-background-color) 75%, light-dark(var(--sci-static-color-black), var(--sci-static-color-white)))}@container not style(--\\275sci-table-row-selected: true){:host>sci-toolbar.row-actions{--\\275sci-table-row-action-background-color: var(--sci-table-row-action-background-color)}}@container style(--\\275sci-table-row-selected: true){:host>sci-toolbar.row-actions{--\\275sci-table-row-action-background-color: var(--sci-table-row-action-background-color-selected)}}:host>sci-toolbar.row-actions:empty{display:none}\n"] }]
1937
+ }, template: "@let row = this.row();\n\n<!-- Row Icons -->\n@if (row.cells?.length) {\n @for (icon of row.bindings?.icons(); track $index) {\n <sci-icon>{{icon}}</sci-icon>\n }\n}\n\n<!-- Table Cells -->\n@for (cell of row.cells; track cell.column.name) {\n <sci-table-cell [cell]=\"cell\" [row]=\"row\"/>\n} @empty {\n @for (column of table().columns(); track column.name) {\n <div class=\"skeleton e2e-skeleton\" [attr.data-column]=\"column.name\"></div>\n }\n}\n\n<!--Row Actions -->\n@let hasRowActions = !!table().rowActions;\n@let isRowHovered = table().hoveredRow() === row;\n@let isRowActionMenuOpen = rowActionToolbar()?.menuOpen();\n\n@if (hasRowActions && (isRowHovered || isRowActionMenuOpen)) {\n <sci-toolbar [name]=\"rowActionsToolbarName\" (click)=\"onActionToolbarClick($event)\" class=\"row-actions\"/>\n}\n", styles: ["@charset \"UTF-8\";:host{display:inline-grid;grid-template-columns:subgrid;grid-auto-rows:var(--sci-table-row-height);align-items:stretch;-webkit-user-select:none;user-select:none;border-bottom:var(--\\275sci-table-border-size) solid var(--sci-table-gridline-color)}:host[data-hovered]{background-color:var(--sci-table-row-background-color-hover)}:host[data-selected]{background-color:var(--sci-table-row-background-color-selected);--\\275sci-table-row-selected: true}:host[data-hovered]:not([data-selected]){border-radius:var(--sci-table-row-border-radius)}:host:is(:first-child,:not([data-selected])+sci-table-row)[data-selected]{border-top-left-radius:var(--sci-table-row-border-radius);border-top-right-radius:var(--sci-table-row-border-radius)}:host[data-selected]:not(:has(+sci-table-row[data-selected])){border-bottom-left-radius:var(--sci-table-row-border-radius);border-bottom-right-radius:var(--sci-table-row-border-radius)}@container (style(--\\275sci-table-scrolling: true)) or (style(--\\275sci-table-resizing: true)){:host{pointer-events:none}}:host:has(>div.skeleton){pointer-events:none;align-items:center}:host:has(>div.skeleton)>div.skeleton{display:inline-block;position:relative;overflow:hidden;border-radius:var(--sci-corner-small);background-color:var(--sci-color-skeleton-subtle);opacity:0;animation-name:visiblity;animation-duration:.5s;animation-timing-function:ease-in;animation-fill-mode:forwards}:host:has(>div.skeleton)>div.skeleton:after{position:absolute;inset:0;transform:translate(-100%);background-image:linear-gradient(to right,transparent 0,color-mix(in srgb,light-dark(var(--sci-static-color-white),var(--sci-static-color-black)) 30%,transparent) 50%,transparent);animation:shimmer 2s infinite;content:\"\"}@keyframes shimmer{to{transform:translate(100%)}}@keyframes visiblity{to{opacity:1}}:host:has(>div.skeleton)>div.skeleton{height:1em;margin-inline:var(--\\275sci-table-cell-padding-inline)}:host>sci-icon:has(~sci-table-cell:first-of-type){grid-column:1;grid-row:1;align-self:center;--sci-icon-size: 1em;margin-left:calc(var(--\\275sci-table-cell-padding-inline) + (sibling-index() - 1) * 1.5em);width:auto;max-width:var(--sci-icon-size)}:host>sci-icon:has(~sci-table-cell:first-of-type)+sci-table-cell{grid-column:1;grid-row:1;border-left:calc(var(--\\275sci-table-cell-padding-inline) + (sibling-index() - 1) * 1.5em) solid transparent}:host>sci-toolbar.row-actions{position:sticky;right:0;transform:translate(calc(-1 * var(--\\275sci-table-cell-padding-inline)));justify-self:end;align-self:center;border-radius:var(--sci-corner);padding:1px;background-color:var(--\\275sci-table-row-action-background-color);z-index:2;--sci-toolbar-item-background-color-hover: color-mix(in srgb, var(--\\275sci-table-row-action-background-color) 85%, light-dark(var(--sci-static-color-black), var(--sci-static-color-white)));--sci-toolbar-item-background-color-active: color-mix(in srgb, var(--\\275sci-table-row-action-background-color) 75%, light-dark(var(--sci-static-color-black), var(--sci-static-color-white)))}@container not style(--\\275sci-table-row-selected: true){:host>sci-toolbar.row-actions{--\\275sci-table-row-action-background-color: var(--sci-table-row-action-background-color)}}@container style(--\\275sci-table-row-selected: true){:host>sci-toolbar.row-actions{--\\275sci-table-row-action-background-color: var(--sci-table-row-action-background-color-selected)}}:host>sci-toolbar.row-actions:empty{display:none}\n"] }]
1808
1938
  }], ctorParameters: () => [], propDecorators: { row: [{ type: i0.Input, args: [{ isSignal: true, alias: "row", required: true }] }], primaryAction: [{ type: i0.Output, args: ["primaryAction"] }], rowActionToolbar: [{ type: i0.ViewChild, args: [i0.forwardRef(() => SciToolbarComponent), { isSignal: true }] }] } });
1809
1939
 
1810
1940
  /*
@@ -2092,14 +2222,14 @@ class SciTableColumnsComponent {
2092
2222
  }
2093
2223
  }
2094
2224
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: SciTableColumnsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2095
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: SciTableColumnsComponent, isStandalone: true, selector: "sci-table-columns", inputs: { rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<!-- Render a resize splitter per resizable column. -->\n@for (column of table().columns(); track column.name; let i = $index) {\n @if (column.resizable()) {\n <sci-splitter orientation=\"vertical\"\n [class.resizing]=\"column.resizing()\"\n [attr.data-column]=\"column.name\"\n [style.grid-column]=\"i + 1\"\n (start)=\"onResizeStart(column)\"\n (move)=\"onResize(column, $event)\"\n (end)=\"onResizeEnd()\"\n (reset)=\"onPack(column)\"\n (mouseenter)=\"onMouseEnter()\"\n (mouseleave)=\"onMouseLeave()\"/>\n }\n}\n\n<!-- Render an invisible synthetic column per column to read current widths and set minimum widths. -->\n@for (column of table().columns(); track column.name; let i = $index) {\n <sci-table-column [column]=\"column\" [style.grid-column]=\"i + 1\"/>\n}\n", styles: ["@charset \"UTF-8\";:host{display:grid;grid-template-columns:subgrid;overflow-x:clip;pointer-events:none}@container (style(--sci-table-header-column-divider: visible)) and (style(--\\275sci-table-header-visible: true)){:host{--\\275sci-table-header-column-divider-visible: true}}:host:has(>sci-splitter:hover){--\\275sci-column-splitter-hover: true}:host>sci-splitter,:host>sci-table-column{grid-row:1}:host>sci-splitter{--sci-splitter-size: var(--\\275sci-table-border-size);--sci-splitter-size-hover: var(--\\275sci-table-border-size);--sci-splitter-cross-axis-size: 0;--sci-splitter-background-color: var(--\\275sci-table-border-color);justify-self:end;pointer-events:auto}@container style(--\\275sci-table-header-column-divider-visible: true){:host>sci-splitter{--sci-splitter-cross-axis-start: .5em;--sci-splitter-cross-axis-size: calc(var(--\\275sci-table-header-height) - 2 * var(--sci-splitter-cross-axis-start))}@container style(--\\275sci-table-viewport-client-width >= --\\275sci-table-viewport-width){:host>sci-splitter:last-of-type:not(.resizing):not(:hover){--sci-splitter-cross-axis-size: 0}}}:host>sci-splitter:is(:hover,.resizing){--sci-splitter-background-color: rgb(from var(--\\275sci-table-border-color) r g b / .5);--sci-splitter-cross-axis-size: 100%}@container style(--\\275sci-table-header-column-divider-visible: true){:host>sci-splitter:is(:hover,.resizing){--sci-splitter-cross-axis-size: auto;--sci-splitter-cross-axis-end: 0}}@container style(--\\275sci-table-resizing: true){:host>sci-splitter:not(.resizing){pointer-events:none}}@container (style(--\\275sci-table-scrolling: true)) and (not style(--\\275sci-table-resizing: true)) and (not style(--\\275sci-column-splitter-hover: true)){:host>sci-splitter{pointer-events:none}}:host>sci-table-column{height:0}\n"], dependencies: [{ kind: "component", type: SciSplitterComponent, selector: "sci-splitter", inputs: ["orientation"], outputs: ["start", "move", "end", "reset"] }, { kind: "component", type: SciTableColumnComponent, selector: "sci-table-column", inputs: ["column"] }] });
2225
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: SciTableColumnsComponent, isStandalone: true, selector: "sci-table-columns", inputs: { rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<!-- Render splitters, also for non-resizable columns, as non-interactive column header dividers. -->\n@for (column of table().columns(); track column.name; let i = $index) {\n <sci-splitter orientation=\"vertical\"\n [class.resizing]=\"column.resizing()\"\n [attr.data-column]=\"column.name\"\n [attr.data-non-interactive]=\"!column.resizable() ? '' : null\"\n [style.grid-column]=\"i + 1\"\n (start)=\"onResizeStart(column)\"\n (move)=\"onResize(column, $event)\"\n (end)=\"onResizeEnd()\"\n (reset)=\"onPack(column)\"\n (mouseenter)=\"onMouseEnter()\"\n (mouseleave)=\"onMouseLeave()\"/>\n}\n\n<!-- Render an invisible synthetic column per column to read current widths and set minimum widths. -->\n@for (column of table().columns(); track column.name; let i = $index) {\n <sci-table-column [column]=\"column\" [style.grid-column]=\"i + 1\"/>\n}\n", styles: ["@charset \"UTF-8\";:host{display:grid;grid-template-columns:subgrid;overflow-x:clip;pointer-events:none}@container (style(--sci-table-header-column-divider: visible)) and (style(--\\275sci-table-header-visible: true)){:host{--\\275sci-table-header-column-divider-visible: true}}:host:has(>sci-splitter:hover){--\\275sci-column-splitter-hover: true}:host>sci-splitter,:host>sci-table-column{grid-row:1}:host>sci-splitter{--sci-splitter-size: var(--\\275sci-table-border-size);--sci-splitter-size-hover: var(--\\275sci-table-border-size);--sci-splitter-cross-axis-size: 0;--sci-splitter-background-color: var(--\\275sci-table-border-color);justify-self:end}:host>sci-splitter:where(:not([data-non-interactive])){pointer-events:auto}@container style(--\\275sci-table-header-column-divider-visible: true){:host>sci-splitter{--sci-splitter-cross-axis-start: .5em;--sci-splitter-cross-axis-size: calc(var(--\\275sci-table-header-height) - 2 * var(--sci-splitter-cross-axis-start))}@container style(--\\275sci-table-viewport-client-width >= --\\275sci-table-viewport-width){:host>sci-splitter:last-of-type:not(.resizing):not(:hover){--sci-splitter-cross-axis-size: 0}}}:host>sci-splitter:is(:hover,.resizing){--sci-splitter-background-color: rgb(from var(--\\275sci-table-border-color) r g b / .5);--sci-splitter-cross-axis-start: .5em;--sci-splitter-cross-axis-size: auto;--sci-splitter-cross-axis-end: 0}@container not style(--\\275sci-table-header-column-divider-visible: true){:host>sci-splitter:is(:hover,.resizing){--sci-splitter-cross-axis-start: 0}}@container style(--\\275sci-table-resizing: true){:host>sci-splitter:not(.resizing){pointer-events:none}}@container (style(--\\275sci-table-scrolling: true)) and (not style(--\\275sci-table-resizing: true)) and (not style(--\\275sci-column-splitter-hover: true)){:host>sci-splitter{pointer-events:none}}:host>sci-table-column{height:0}\n"], dependencies: [{ kind: "component", type: SciSplitterComponent, selector: "sci-splitter", inputs: ["orientation"], outputs: ["start", "move", "end", "reset"] }, { kind: "component", type: SciTableColumnComponent, selector: "sci-table-column", inputs: ["column"] }] });
2096
2226
  }
2097
2227
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: SciTableColumnsComponent, decorators: [{
2098
2228
  type: Component,
2099
2229
  args: [{ selector: 'sci-table-columns', imports: [
2100
2230
  SciSplitterComponent,
2101
2231
  SciTableColumnComponent,
2102
- ], template: "<!-- Render a resize splitter per resizable column. -->\n@for (column of table().columns(); track column.name; let i = $index) {\n @if (column.resizable()) {\n <sci-splitter orientation=\"vertical\"\n [class.resizing]=\"column.resizing()\"\n [attr.data-column]=\"column.name\"\n [style.grid-column]=\"i + 1\"\n (start)=\"onResizeStart(column)\"\n (move)=\"onResize(column, $event)\"\n (end)=\"onResizeEnd()\"\n (reset)=\"onPack(column)\"\n (mouseenter)=\"onMouseEnter()\"\n (mouseleave)=\"onMouseLeave()\"/>\n }\n}\n\n<!-- Render an invisible synthetic column per column to read current widths and set minimum widths. -->\n@for (column of table().columns(); track column.name; let i = $index) {\n <sci-table-column [column]=\"column\" [style.grid-column]=\"i + 1\"/>\n}\n", styles: ["@charset \"UTF-8\";:host{display:grid;grid-template-columns:subgrid;overflow-x:clip;pointer-events:none}@container (style(--sci-table-header-column-divider: visible)) and (style(--\\275sci-table-header-visible: true)){:host{--\\275sci-table-header-column-divider-visible: true}}:host:has(>sci-splitter:hover){--\\275sci-column-splitter-hover: true}:host>sci-splitter,:host>sci-table-column{grid-row:1}:host>sci-splitter{--sci-splitter-size: var(--\\275sci-table-border-size);--sci-splitter-size-hover: var(--\\275sci-table-border-size);--sci-splitter-cross-axis-size: 0;--sci-splitter-background-color: var(--\\275sci-table-border-color);justify-self:end;pointer-events:auto}@container style(--\\275sci-table-header-column-divider-visible: true){:host>sci-splitter{--sci-splitter-cross-axis-start: .5em;--sci-splitter-cross-axis-size: calc(var(--\\275sci-table-header-height) - 2 * var(--sci-splitter-cross-axis-start))}@container style(--\\275sci-table-viewport-client-width >= --\\275sci-table-viewport-width){:host>sci-splitter:last-of-type:not(.resizing):not(:hover){--sci-splitter-cross-axis-size: 0}}}:host>sci-splitter:is(:hover,.resizing){--sci-splitter-background-color: rgb(from var(--\\275sci-table-border-color) r g b / .5);--sci-splitter-cross-axis-size: 100%}@container style(--\\275sci-table-header-column-divider-visible: true){:host>sci-splitter:is(:hover,.resizing){--sci-splitter-cross-axis-size: auto;--sci-splitter-cross-axis-end: 0}}@container style(--\\275sci-table-resizing: true){:host>sci-splitter:not(.resizing){pointer-events:none}}@container (style(--\\275sci-table-scrolling: true)) and (not style(--\\275sci-table-resizing: true)) and (not style(--\\275sci-column-splitter-hover: true)){:host>sci-splitter{pointer-events:none}}:host>sci-table-column{height:0}\n"] }]
2232
+ ], template: "<!-- Render splitters, also for non-resizable columns, as non-interactive column header dividers. -->\n@for (column of table().columns(); track column.name; let i = $index) {\n <sci-splitter orientation=\"vertical\"\n [class.resizing]=\"column.resizing()\"\n [attr.data-column]=\"column.name\"\n [attr.data-non-interactive]=\"!column.resizable() ? '' : null\"\n [style.grid-column]=\"i + 1\"\n (start)=\"onResizeStart(column)\"\n (move)=\"onResize(column, $event)\"\n (end)=\"onResizeEnd()\"\n (reset)=\"onPack(column)\"\n (mouseenter)=\"onMouseEnter()\"\n (mouseleave)=\"onMouseLeave()\"/>\n}\n\n<!-- Render an invisible synthetic column per column to read current widths and set minimum widths. -->\n@for (column of table().columns(); track column.name; let i = $index) {\n <sci-table-column [column]=\"column\" [style.grid-column]=\"i + 1\"/>\n}\n", styles: ["@charset \"UTF-8\";:host{display:grid;grid-template-columns:subgrid;overflow-x:clip;pointer-events:none}@container (style(--sci-table-header-column-divider: visible)) and (style(--\\275sci-table-header-visible: true)){:host{--\\275sci-table-header-column-divider-visible: true}}:host:has(>sci-splitter:hover){--\\275sci-column-splitter-hover: true}:host>sci-splitter,:host>sci-table-column{grid-row:1}:host>sci-splitter{--sci-splitter-size: var(--\\275sci-table-border-size);--sci-splitter-size-hover: var(--\\275sci-table-border-size);--sci-splitter-cross-axis-size: 0;--sci-splitter-background-color: var(--\\275sci-table-border-color);justify-self:end}:host>sci-splitter:where(:not([data-non-interactive])){pointer-events:auto}@container style(--\\275sci-table-header-column-divider-visible: true){:host>sci-splitter{--sci-splitter-cross-axis-start: .5em;--sci-splitter-cross-axis-size: calc(var(--\\275sci-table-header-height) - 2 * var(--sci-splitter-cross-axis-start))}@container style(--\\275sci-table-viewport-client-width >= --\\275sci-table-viewport-width){:host>sci-splitter:last-of-type:not(.resizing):not(:hover){--sci-splitter-cross-axis-size: 0}}}:host>sci-splitter:is(:hover,.resizing){--sci-splitter-background-color: rgb(from var(--\\275sci-table-border-color) r g b / .5);--sci-splitter-cross-axis-start: .5em;--sci-splitter-cross-axis-size: auto;--sci-splitter-cross-axis-end: 0}@container not style(--\\275sci-table-header-column-divider-visible: true){:host>sci-splitter:is(:hover,.resizing){--sci-splitter-cross-axis-start: 0}}@container style(--\\275sci-table-resizing: true){:host>sci-splitter:not(.resizing){pointer-events:none}}@container (style(--\\275sci-table-scrolling: true)) and (not style(--\\275sci-table-resizing: true)) and (not style(--\\275sci-column-splitter-hover: true)){:host>sci-splitter{pointer-events:none}}:host>sci-table-column{height:0}\n"] }]
2103
2233
  }], propDecorators: { rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: true }] }] } });
2104
2234
 
2105
2235
  /*
@@ -2215,15 +2345,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImpor
2215
2345
  class SciColumnHeaderComponent {
2216
2346
  column = input.required(/* @ts-ignore */
2217
2347
  ...(ngDevMode ? [{ debugName: "column" }] : /* istanbul ignore next */ []));
2348
+ labelElement = viewChild('label', /* @ts-ignore */
2349
+ ...(ngDevMode ? [{ debugName: "labelElement" }] : /* istanbul ignore next */ [])); // public visibility required to be accessible in `configureTooltip` function
2218
2350
  table = inject(ɵSCI_TABLE);
2219
- header = text(computed(() => this.column().header()));
2220
2351
  sortDirection = computed(() => this.table().sortCriteria().find(criteria => criteria.columnName === this.column().name)?.direction, /* @ts-ignore */
2221
2352
  ...(ngDevMode ? [{ debugName: "sortDirection" }] : /* istanbul ignore next */ []));
2222
2353
  onSort(event) {
2223
2354
  this.table().sort(this.column().name, event.ctrlKey || event.metaKey);
2224
2355
  }
2225
2356
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: SciColumnHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2226
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: SciColumnHeaderComponent, isStandalone: true, selector: "sci-column-header", inputs: { column: { classPropertyName: "column", publicName: "column", isSignal: true, isRequired: true, transformFunction: null } }, host: { properties: { "attr.data-column": "column().name", "attr.title": "header()" } }, ngImport: i0, template: "@if (column().sortable()) {\n <button class=\"sort e2e-sort\"\n [attr.data-sort-direction]=\"sortDirection()\"\n (click)=\"onSort($event)\">\n <!-- Header -->\n <ng-container *ngTemplateOutlet=\"header_template\"/>\n\n <!-- Sort Indicator -->\n @switch (sortDirection()) {\n @case ('asc') {\n <sci-icon>scion.sort_ascending</sci-icon>\n }\n @case ('desc') {\n <sci-icon>scion.sort_descending</sci-icon>\n }\n @default {\n <sci-icon class=\"no-sort\">scion.sort_indicator</sci-icon>\n }\n }\n </button>\n} @else {\n <ng-container *ngTemplateOutlet=\"header_template\"/>\n}\n\n<ng-template #header_template>\n <span class=\"header e2e-header\" [class.wrap]=\"table().wrapHeader()\">{{header()}}</span>\n</ng-template>\n", styles: ["@charset \"UTF-8\";:host{display:grid;align-items:center}:host:not(:has(button.sort)){padding-inline:var(--\\275sci-table-cell-padding-inline)}:host:is(:focus-visible,:hover){--\\275sci-table-column-header-hover: true}:host>button.sort{all:unset;display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:.5em;padding-inline:var(--\\275sci-table-cell-padding-inline);cursor:var(--sci-table-header-cursor);align-self:stretch}@container not style(--\\275sci-table-column-header-hover: true){:host>button.sort>sci-icon.no-sort{visibility:hidden}}@container style(--\\275sci-table-column-header-hover: true){:host>button.sort{background-color:var(--sci-table-header-background-color-hover)}}:host span.header{display:inline;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--sci-table-header-text-color);font-family:var(--sci-table-header-font-family),sans-serif;font-weight:var(--sci-table-header-font-weight);font-size:var(--sci-table-header-font-size);padding-block:.25em}:host span.header.wrap{white-space:wrap}\n"], dependencies: [{ kind: "component", type: SciIconComponent, selector: "sci-icon" }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] });
2357
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: SciColumnHeaderComponent, isStandalone: true, selector: "sci-column-header", inputs: { column: { classPropertyName: "column", publicName: "column", isSignal: true, isRequired: true, transformFunction: null } }, host: { properties: { "attr.data-column": "column().name" } }, providers: [
2358
+ configureTooltip$1(),
2359
+ ], viewQueries: [{ propertyName: "labelElement", first: true, predicate: ["label"], descendants: true, isSignal: true }], hostDirectives: [{ directive: i1.SciTooltipOnOverflowDirective }], ngImport: i0, template: "@if (column().sortable()) {\n <button class=\"sort e2e-sort\"\n [attr.data-sort-direction]=\"sortDirection()\"\n (click)=\"onSort($event)\">\n <!-- Header -->\n <ng-container *ngTemplateOutlet=\"header_template\"/>\n\n <!-- Sort Indicator -->\n @switch (sortDirection()) {\n @case ('asc') {\n <sci-icon>scion:sort_ascending</sci-icon>\n }\n @case ('desc') {\n <sci-icon>scion:sort_descending</sci-icon>\n }\n @default {\n <sci-icon class=\"no-sort\">scion:sort_indicator</sci-icon>\n }\n }\n </button>\n} @else {\n <ng-container *ngTemplateOutlet=\"header_template\"/>\n}\n\n<ng-template #header_template>\n @if (column().header.icon) {\n <sci-icon class=\"icon e2e-icon\">{{column().header.icon}}</sci-icon>\n }\n\n @if (column().header.label) {\n <span [class.wrap]=\"table().wrapHeader()\" class=\"label\" #label>\n {{column().header.label!()}}\n </span>\n }\n</ng-template>\n", styles: ["@charset \"UTF-8\";:host{display:grid;align-items:center;gap:.25em}:host:not(:has(button.sort)){padding-inline:var(--\\275sci-table-cell-padding-inline)}:host:is(:focus-visible,:hover){--\\275sci-table-column-header-hover: true}:host:has(>sci-icon.icon):has(>span.label){grid-template-columns:auto 1fr}:host>button.sort{all:unset;display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:.25em;padding-inline:var(--\\275sci-table-cell-padding-inline);cursor:var(--sci-table-header-cursor);align-self:stretch}:host>button.sort:has(>sci-icon.icon):has(>span.label){grid-template-columns:auto minmax(0,1fr) auto}@container not style(--\\275sci-table-column-header-hover: true){:host>button.sort>sci-icon.no-sort{visibility:hidden}}@container style(--\\275sci-table-column-header-hover: true){:host>button.sort{background-color:var(--sci-table-header-background-color-hover)}}:host span.label{display:inline;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--sci-table-header-text-color);font-family:var(--sci-table-header-font-family),sans-serif;font-weight:var(--sci-table-header-font-weight);font-size:var(--sci-table-header-font-size);padding-block:.25em}:host span.label.wrap{white-space:wrap}\n"], dependencies: [{ kind: "component", type: SciIconComponent, selector: "sci-icon" }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] });
2227
2360
  }
2228
2361
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: SciColumnHeaderComponent, decorators: [{
2229
2362
  type: Component,
@@ -2232,9 +2365,27 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImpor
2232
2365
  NgTemplateOutlet,
2233
2366
  ], host: {
2234
2367
  '[attr.data-column]': 'column().name',
2235
- '[attr.title]': 'header()',
2236
- }, template: "@if (column().sortable()) {\n <button class=\"sort e2e-sort\"\n [attr.data-sort-direction]=\"sortDirection()\"\n (click)=\"onSort($event)\">\n <!-- Header -->\n <ng-container *ngTemplateOutlet=\"header_template\"/>\n\n <!-- Sort Indicator -->\n @switch (sortDirection()) {\n @case ('asc') {\n <sci-icon>scion.sort_ascending</sci-icon>\n }\n @case ('desc') {\n <sci-icon>scion.sort_descending</sci-icon>\n }\n @default {\n <sci-icon class=\"no-sort\">scion.sort_indicator</sci-icon>\n }\n }\n </button>\n} @else {\n <ng-container *ngTemplateOutlet=\"header_template\"/>\n}\n\n<ng-template #header_template>\n <span class=\"header e2e-header\" [class.wrap]=\"table().wrapHeader()\">{{header()}}</span>\n</ng-template>\n", styles: ["@charset \"UTF-8\";:host{display:grid;align-items:center}:host:not(:has(button.sort)){padding-inline:var(--\\275sci-table-cell-padding-inline)}:host:is(:focus-visible,:hover){--\\275sci-table-column-header-hover: true}:host>button.sort{all:unset;display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:.5em;padding-inline:var(--\\275sci-table-cell-padding-inline);cursor:var(--sci-table-header-cursor);align-self:stretch}@container not style(--\\275sci-table-column-header-hover: true){:host>button.sort>sci-icon.no-sort{visibility:hidden}}@container style(--\\275sci-table-column-header-hover: true){:host>button.sort{background-color:var(--sci-table-header-background-color-hover)}}:host span.header{display:inline;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--sci-table-header-text-color);font-family:var(--sci-table-header-font-family),sans-serif;font-weight:var(--sci-table-header-font-weight);font-size:var(--sci-table-header-font-size);padding-block:.25em}:host span.header.wrap{white-space:wrap}\n"] }]
2237
- }], propDecorators: { column: [{ type: i0.Input, args: [{ isSignal: true, alias: "column", required: true }] }] } });
2368
+ }, hostDirectives: [
2369
+ SciTooltipOnOverflowDirective,
2370
+ ], providers: [
2371
+ configureTooltip$1(),
2372
+ ], template: "@if (column().sortable()) {\n <button class=\"sort e2e-sort\"\n [attr.data-sort-direction]=\"sortDirection()\"\n (click)=\"onSort($event)\">\n <!-- Header -->\n <ng-container *ngTemplateOutlet=\"header_template\"/>\n\n <!-- Sort Indicator -->\n @switch (sortDirection()) {\n @case ('asc') {\n <sci-icon>scion:sort_ascending</sci-icon>\n }\n @case ('desc') {\n <sci-icon>scion:sort_descending</sci-icon>\n }\n @default {\n <sci-icon class=\"no-sort\">scion:sort_indicator</sci-icon>\n }\n }\n </button>\n} @else {\n <ng-container *ngTemplateOutlet=\"header_template\"/>\n}\n\n<ng-template #header_template>\n @if (column().header.icon) {\n <sci-icon class=\"icon e2e-icon\">{{column().header.icon}}</sci-icon>\n }\n\n @if (column().header.label) {\n <span [class.wrap]=\"table().wrapHeader()\" class=\"label\" #label>\n {{column().header.label!()}}\n </span>\n }\n</ng-template>\n", styles: ["@charset \"UTF-8\";:host{display:grid;align-items:center;gap:.25em}:host:not(:has(button.sort)){padding-inline:var(--\\275sci-table-cell-padding-inline)}:host:is(:focus-visible,:hover){--\\275sci-table-column-header-hover: true}:host:has(>sci-icon.icon):has(>span.label){grid-template-columns:auto 1fr}:host>button.sort{all:unset;display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:.25em;padding-inline:var(--\\275sci-table-cell-padding-inline);cursor:var(--sci-table-header-cursor);align-self:stretch}:host>button.sort:has(>sci-icon.icon):has(>span.label){grid-template-columns:auto minmax(0,1fr) auto}@container not style(--\\275sci-table-column-header-hover: true){:host>button.sort>sci-icon.no-sort{visibility:hidden}}@container style(--\\275sci-table-column-header-hover: true){:host>button.sort{background-color:var(--sci-table-header-background-color-hover)}}:host span.label{display:inline;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--sci-table-header-text-color);font-family:var(--sci-table-header-font-family),sans-serif;font-weight:var(--sci-table-header-font-weight);font-size:var(--sci-table-header-font-size);padding-block:.25em}:host span.label.wrap{white-space:wrap}\n"] }]
2373
+ }], propDecorators: { column: [{ type: i0.Input, args: [{ isSignal: true, alias: "column", required: true }] }], labelElement: [{ type: i0.ViewChild, args: ['label', { isSignal: true }] }] } });
2374
+ function configureTooltip$1() {
2375
+ return [
2376
+ {
2377
+ provide: TOOLTIP_ON_OVERFLOW_DIRECTIVE_ADDITIONAL_TEXT,
2378
+ useFactory: () => {
2379
+ const host = inject(SciColumnHeaderComponent);
2380
+ return computed(() => host.column().header.tooltip?.());
2381
+ },
2382
+ },
2383
+ {
2384
+ provide: TOOLTIP_ON_OVERFLOW_DIRECTIVE_SOURCE_ELEMENT,
2385
+ useFactory: () => inject(SciColumnHeaderComponent).labelElement,
2386
+ },
2387
+ ];
2388
+ }
2238
2389
 
2239
2390
  /*
2240
2391
  * Copyright (c) 2018-2026 Swiss Federal Railways
@@ -2245,22 +2396,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImpor
2245
2396
  *
2246
2397
  * SPDX-License-Identifier: EPL-2.0
2247
2398
  */
2248
- class SciColumnFilterBooleanLabelPipe {
2249
- transform(value) {
2250
- if (value === true) {
2251
- return '%scion.components.yes.value';
2252
- }
2253
- if (value === false) {
2254
- return '%scion.components.no.value';
2255
- }
2256
- return null;
2257
- }
2258
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: SciColumnFilterBooleanLabelPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
2259
- static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "22.1.7", ngImport: i0, type: SciColumnFilterBooleanLabelPipe, isStandalone: true, name: "sciColumnFilterBooleanLabel" });
2399
+ /**
2400
+ * Creates a comma-separated string with localized labels of the given enum values.
2401
+ */
2402
+ class SciEnumLabelPipe {
2403
+ _injector = inject(Injector);
2404
+ transform(values, enumeration) {
2405
+ return computed(() => Arrays.coerce(values)
2406
+ .map(value => enumeration.find(entry => entry.value === value)?.label)
2407
+ .map(label => untracked(() => text(label, { injector: this._injector }))())
2408
+ .filter(Boolean)
2409
+ .join(', ') || undefined);
2410
+ }
2411
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: SciEnumLabelPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
2412
+ static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "22.1.7", ngImport: i0, type: SciEnumLabelPipe, isStandalone: true, name: "sciEnumLabel" });
2260
2413
  }
2261
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: SciColumnFilterBooleanLabelPipe, decorators: [{
2414
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: SciEnumLabelPipe, decorators: [{
2262
2415
  type: Pipe,
2263
- args: [{ name: 'sciColumnFilterBooleanLabel' }]
2416
+ args: [{ name: 'sciEnumLabel' }]
2264
2417
  }] });
2265
2418
 
2266
2419
  /*
@@ -2275,33 +2428,36 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImpor
2275
2428
  class SciColumnFilterComponent {
2276
2429
  column = input.required(/* @ts-ignore */
2277
2430
  ...(ngDevMode ? [{ debugName: "column" }] : /* istanbul ignore next */ []));
2431
+ inputElement = viewChild('input', /* @ts-ignore */
2432
+ ...(ngDevMode ? [{ debugName: "inputElement" }] : /* istanbul ignore next */ [])); // public visibility required to be accessible in `configureTooltip` function
2278
2433
  filter = signal(null, /* @ts-ignore */
2279
2434
  ...(ngDevMode ? [{ debugName: "filter" }] : /* istanbul ignore next */ []));
2280
- input = viewChild('input', /* @ts-ignore */
2281
- ...(ngDevMode ? [{ debugName: "input" }] : /* istanbul ignore next */ []));
2435
+ dimension = dimension(inject(ElementRef));
2282
2436
  _table = inject(ɵSCI_TABLE);
2283
2437
  _menuService = inject(_SciMenuService);
2284
2438
  _injector = inject(Injector);
2285
2439
  _host = inject(ElementRef).nativeElement;
2286
- _menu;
2440
+ menu;
2287
2441
  constructor() {
2288
2442
  this.bindToModel();
2289
2443
  }
2290
2444
  onFilterClick() {
2291
- if (this.column().type === 'boolean') {
2292
- this.toggleBooleanMenu();
2445
+ if (this.column().type === 'enum') {
2446
+ this.toggleEnumChooser();
2447
+ }
2448
+ else {
2449
+ this.inputElement().nativeElement.focus();
2293
2450
  }
2294
- this.input().nativeElement.focus();
2295
2451
  }
2296
2452
  onReset(event) {
2297
2453
  this.filter.set(null);
2298
- this.input().nativeElement.focus();
2454
+ this.inputElement().nativeElement.focus();
2299
2455
  event.stopPropagation(); // prevent opening menu
2300
2456
  }
2301
2457
  bindToModel() {
2302
2458
  const filterDebounced = debounced(computed(() => trim(this.filter())), async (value) => {
2303
- // Debounce only if not empty.
2304
- if (value !== null && typeof value !== 'boolean') {
2459
+ // Debounce only if not empty and not an enum.
2460
+ if (value !== null && this.column().type !== 'enum') {
2305
2461
  await firstValueFrom(timer(200));
2306
2462
  }
2307
2463
  });
@@ -2314,56 +2470,93 @@ class SciColumnFilterComponent {
2314
2470
  });
2315
2471
  });
2316
2472
  }
2317
- toggleBooleanMenu() {
2473
+ toggleEnumChooser() {
2318
2474
  // Close menu if already opened.
2319
- if (this._menu) {
2320
- this._menu.close();
2475
+ if (this.menu) {
2476
+ this.menu.close();
2321
2477
  return;
2322
2478
  }
2323
- const injector = createDestroyableInjector({ parent: this._injector });
2479
+ const column = this.column();
2480
+ const injector = _createDestroyableInjector({ parent: this._injector });
2324
2481
  runInInjectionContext(injector, () => {
2482
+ const menuId = `menu:column-filter-${UUID.randomUUID()}`;
2325
2483
  // Contribute menu items.
2326
- contributeMenu('menu:column-filter', menu => menu
2327
- .addMenuItem({ label: '%scion.components.yes.value', attributes: { 'data-option': 'true' }, onSelect: () => this.filter.set(true) })
2328
- .addMenuItem({ label: '%scion.components.no.value', attributes: { 'data-option': 'false' }, onSelect: () => this.filter.set(false) }));
2484
+ contributeMenu(menuId, menu => {
2485
+ const multi = column.filterable() === 'multi';
2486
+ column.enumeration().forEach(entry => menu.addMenuItem({
2487
+ label: entry.label,
2488
+ checked: multi ? computed(() => this.filter()?.includes(entry.value) ?? false) : undefined,
2489
+ icon: column.format.filter.icon ? entry.icon : undefined,
2490
+ attributes: { 'data-option': `${entry.value}` },
2491
+ onSelect: () => this.filter.update(filter => {
2492
+ if (multi) {
2493
+ const values = new Set(filter ?? []);
2494
+ values.has(entry.value) ? values.delete(entry.value) : values.add(entry.value);
2495
+ return values.size ? Array.from(values) : null;
2496
+ }
2497
+ else {
2498
+ return entry.value;
2499
+ }
2500
+ }),
2501
+ }));
2502
+ });
2329
2503
  // Open menu.
2330
- this._menu = this._menuService.open('menu:column-filter', {
2504
+ this.menu = this._menuService.open(menuId, {
2331
2505
  anchor: this._host,
2332
2506
  minWidth: `${this._host.offsetWidth}px`,
2507
+ maxHeight: '300px',
2508
+ cssClass: 'e2e-filter',
2509
+ filter: column.enumeration().length >= 8 ? { focus: true } : false,
2510
+ maxWidth: `max(300px, ${this._host.offsetWidth}px)`, // overflow long enumeration labels
2333
2511
  offset: { y: -1 },
2334
2512
  });
2335
2513
  // Close menu when start resizing a column. By default, closes the menu only on mouse up.
2336
2514
  toObservable(this._table().resizing)
2337
2515
  .pipe(skip(1))
2338
- .subscribe(() => this._menu?.close());
2339
- this._menu.onClose(() => {
2516
+ .subscribe(() => this.menu?.close());
2517
+ this.menu.onClose(() => {
2340
2518
  injector.destroy();
2341
2519
  // Restore focus manually as it is not restored automatically since opening the popover inside Shadow DOM.
2342
2520
  // TODO [menu] Consider restore focus manually in Menu API due to restriction in shadow DOM.
2343
- this.input().nativeElement.focus();
2344
- this._menu = undefined;
2521
+ this.inputElement().nativeElement.focus();
2522
+ this.menu = undefined;
2345
2523
  });
2346
2524
  });
2347
2525
  }
2348
2526
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: SciColumnFilterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2349
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: SciColumnFilterComponent, isStandalone: true, selector: "sci-column-filter", inputs: { column: { classPropertyName: "column", publicName: "column", isSignal: true, isRequired: true, transformFunction: null } }, host: { listeners: { "click": "column().filterable() && onFilterClick()" }, properties: { "attr.data-column": "column().name", "attr.data-disabled": "!column().filterable() ? '' : null" } }, viewQueries: [{ propertyName: "input", first: true, predicate: ["input"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (column().filterable()) {\n @let active = filter() !== null && filter() !== '';\n\n <sci-icon class=\"filter e2e-filter\" [class.active]=\"active\">scion.filter</sci-icon>\n\n @switch (column().type) {\n @case ('number') {\n <input [(ngModel)]=\"filter\" type=\"number\" #input/>\n }\n @case ('boolean') {\n <input [ngModel]=\"(filter() | sciColumnFilterBooleanLabel | sciText)()\" type=\"button\" #input>\n <sci-icon class=\"chevron\">scion.chevron_down</sci-icon>\n }\n @default {\n <input [(ngModel)]=\"filter\" type=\"text\" #input/>\n }\n }\n\n @if (active) {\n <button class=\"clear e2e-clear\" (click)=\"onReset($event)\">\n <sci-icon>scion.clear</sci-icon>\n </button>\n }\n}\n", styles: ["@charset \"UTF-8\";:host{display:flex;gap:.5em;align-items:center;padding-inline:var(--\\275sci-table-cell-padding-inline);border-bottom:var(--\\275sci-table-border-size) solid transparent;margin-bottom:-1px;transition:border-bottom-color ease-in-out .15s,color ease-in-out .15s}:host:focus-within:not(:disabled):not(:has(input:disabled)){border-bottom-color:var(--sci-color-accent)}:host>sci-icon.filter.active{color:var(--sci-color-accent)}:host>sci-icon.chevron{--sci-icon-size: var(--sci-button-icon-size)}:host:has(input[type=button]){cursor:var(--sci-button-cursor)}:host:has(input[type=button]):has(button.clear)>sci-icon.chevron{display:none}:host>input{all:unset;flex:auto;align-self:stretch;width:0;min-width:0;color:var(--sci-color-text);appearance:auto;box-sizing:border-box}:host>input[type=number]::-webkit-outer-spin-button,:host>input[type=number]::-webkit-inner-spin-button{appearance:none;margin:0}:host>button.clear{all:unset;display:inline-grid;place-content:center;place-items:center;padding:.25em;border-radius:var(--sci-button-border-radius);-webkit-user-select:none;user-select:none;overflow:hidden;cursor:var(--sci-button-cursor);--sci-icon-size: var(--sci-button-icon-size)}:host>button.clear:hover:where(:not(:disabled)){background-color:var(--sci-button-background-color-hover)}:host>button.clear:active:where(:not(:disabled)){background-color:var(--sci-button-background-color-active)}:host>button.clear:focus:not(:focus-visible){outline:none}:host>button.clear:focus-visible{outline:var(--sci-button-outline-width) solid var(--sci-color-accent)}:host>button.clear:disabled{color:var(--sci-button-text-color-disabled)}:host>button.clear{padding:1px}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { 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.NumberValueAccessor, selector: "input[type=number]:not([ngNoCva])[formControlName],input[type=number]:not([ngNoCva])[formControl],input[type=number]:not([ngNoCva])[ngModel]" }, { 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: "component", type: SciIconComponent, selector: "sci-icon" }, { kind: "pipe", type: SciTextPipe, name: "sciText" }, { kind: "pipe", type: SciColumnFilterBooleanLabelPipe, name: "sciColumnFilterBooleanLabel" }] });
2527
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: SciColumnFilterComponent, isStandalone: true, selector: "sci-column-filter", inputs: { column: { classPropertyName: "column", publicName: "column", isSignal: true, isRequired: true, transformFunction: null } }, host: { listeners: { "click": "column().filterable() && onFilterClick()" }, properties: { "attr.data-column": "column().name", "attr.data-disabled": "!column().filterable() ? '' : null", "class.collapsed": "dimension().clientWidth < 50" } }, providers: [
2528
+ configureTooltip(),
2529
+ ], viewQueries: [{ propertyName: "inputElement", first: true, predicate: ["input"], descendants: true, isSignal: true }], hostDirectives: [{ directive: i1.SciTooltipOnOverflowDirective }], ngImport: i0, template: "@let column = this.column();\n@if (column.filterable()) {\n @let active = filter() !== null && filter() !== '';\n\n <sci-icon class=\"filter e2e-filter\" [class.active]=\"active\">scion:filter</sci-icon>\n\n @switch (column.type) {\n @case ('number') {\n <input [(ngModel)]=\"filter\" type=\"number\" #input/>\n }\n @case ('enum') {\n <input [ngModel]=\"(filter() | sciEnumLabel:column.enumeration())()\" type=\"button\" #input>\n <sci-icon class=\"chevron\">{{menu ? 'scion:chevron_up' : 'scion:chevron_down'}}</sci-icon>\n }\n @default {\n <input [(ngModel)]=\"filter\" type=\"text\" #input/>\n }\n }\n\n @if (active) {\n <button class=\"clear e2e-clear\" (click)=\"onReset($event)\" [attr.title]=\"('%scion:components:clear.tooltip' | sciText)()\">\n <sci-icon>scion:clear</sci-icon>\n </button>\n }\n}\n", styles: ["@charset \"UTF-8\";:host{display:flex;gap:.25em;align-items:center;padding-inline:var(--\\275sci-table-cell-padding-inline);border-bottom:var(--\\275sci-table-border-size) solid transparent;margin-bottom:-1px;transition:border-bottom-color ease-in-out .15s,color ease-in-out .15s}:host:focus-within:not(:disabled):not(:has(input:disabled)){border-bottom-color:var(--sci-color-accent)}:host>sci-icon.filter.active{color:var(--sci-color-accent)}:host:has(input[type=button]){cursor:var(--sci-button-cursor)}:host:has(input[type=button]):has(button.clear)>sci-icon.chevron{display:none}:host.collapsed{gap:0}:host>input{all:unset;flex:auto;align-self:stretch;width:0;min-width:0;color:var(--sci-color-text);appearance:auto;box-sizing:border-box}:host>input[type=number]::-webkit-outer-spin-button,:host>input[type=number]::-webkit-inner-spin-button{appearance:none;margin:0}:host>input[type=button]{white-space:nowrap;text-overflow:ellipsis}:host>button.clear{all:unset;display:inline-grid;place-content:center;place-items:center;padding:.25em;border-radius:var(--sci-button-border-radius);-webkit-user-select:none;user-select:none;overflow:hidden;cursor:var(--sci-button-cursor);--sci-icon-size: var(--sci-button-icon-size)}:host>button.clear:hover:where(:not(:disabled)){background-color:var(--sci-button-background-color-hover)}:host>button.clear:active:where(:not(:disabled)){background-color:var(--sci-button-background-color-active)}:host>button.clear:focus:not(:focus-visible){outline:none}:host>button.clear:focus-visible{outline:var(--sci-button-outline-width) solid var(--sci-color-accent)}:host>button.clear:disabled{color:var(--sci-button-text-color-disabled)}:host>button.clear{padding:1px}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.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: i2.NumberValueAccessor, selector: "input[type=number]:not([ngNoCva])[formControlName],input[type=number]:not([ngNoCva])[formControl],input[type=number]:not([ngNoCva])[ngModel]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: SciIconComponent, selector: "sci-icon" }, { kind: "pipe", type: SciEnumLabelPipe, name: "sciEnumLabel" }, { kind: "pipe", type: SciTextPipe, name: "sciText" }] });
2350
2530
  }
2351
2531
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: SciColumnFilterComponent, decorators: [{
2352
2532
  type: Component,
2353
2533
  args: [{ selector: 'sci-column-filter', imports: [
2354
2534
  FormsModule,
2355
2535
  SciIconComponent,
2536
+ SciEnumLabelPipe,
2356
2537
  SciTextPipe,
2357
- SciColumnFilterBooleanLabelPipe,
2538
+ ], hostDirectives: [
2539
+ SciTooltipOnOverflowDirective,
2358
2540
  ], host: {
2359
2541
  '[attr.data-column]': 'column().name',
2360
2542
  '[attr.data-disabled]': `!column().filterable() ? '' : null`,
2543
+ '[class.collapsed]': 'dimension().clientWidth < 50',
2361
2544
  '(click)': 'column().filterable() && onFilterClick()',
2362
- }, template: "@if (column().filterable()) {\n @let active = filter() !== null && filter() !== '';\n\n <sci-icon class=\"filter e2e-filter\" [class.active]=\"active\">scion.filter</sci-icon>\n\n @switch (column().type) {\n @case ('number') {\n <input [(ngModel)]=\"filter\" type=\"number\" #input/>\n }\n @case ('boolean') {\n <input [ngModel]=\"(filter() | sciColumnFilterBooleanLabel | sciText)()\" type=\"button\" #input>\n <sci-icon class=\"chevron\">scion.chevron_down</sci-icon>\n }\n @default {\n <input [(ngModel)]=\"filter\" type=\"text\" #input/>\n }\n }\n\n @if (active) {\n <button class=\"clear e2e-clear\" (click)=\"onReset($event)\">\n <sci-icon>scion.clear</sci-icon>\n </button>\n }\n}\n", styles: ["@charset \"UTF-8\";:host{display:flex;gap:.5em;align-items:center;padding-inline:var(--\\275sci-table-cell-padding-inline);border-bottom:var(--\\275sci-table-border-size) solid transparent;margin-bottom:-1px;transition:border-bottom-color ease-in-out .15s,color ease-in-out .15s}:host:focus-within:not(:disabled):not(:has(input:disabled)){border-bottom-color:var(--sci-color-accent)}:host>sci-icon.filter.active{color:var(--sci-color-accent)}:host>sci-icon.chevron{--sci-icon-size: var(--sci-button-icon-size)}:host:has(input[type=button]){cursor:var(--sci-button-cursor)}:host:has(input[type=button]):has(button.clear)>sci-icon.chevron{display:none}:host>input{all:unset;flex:auto;align-self:stretch;width:0;min-width:0;color:var(--sci-color-text);appearance:auto;box-sizing:border-box}:host>input[type=number]::-webkit-outer-spin-button,:host>input[type=number]::-webkit-inner-spin-button{appearance:none;margin:0}:host>button.clear{all:unset;display:inline-grid;place-content:center;place-items:center;padding:.25em;border-radius:var(--sci-button-border-radius);-webkit-user-select:none;user-select:none;overflow:hidden;cursor:var(--sci-button-cursor);--sci-icon-size: var(--sci-button-icon-size)}:host>button.clear:hover:where(:not(:disabled)){background-color:var(--sci-button-background-color-hover)}:host>button.clear:active:where(:not(:disabled)){background-color:var(--sci-button-background-color-active)}:host>button.clear:focus:not(:focus-visible){outline:none}:host>button.clear:focus-visible{outline:var(--sci-button-outline-width) solid var(--sci-color-accent)}:host>button.clear:disabled{color:var(--sci-button-text-color-disabled)}:host>button.clear{padding:1px}\n"] }]
2363
- }], ctorParameters: () => [], propDecorators: { column: [{ type: i0.Input, args: [{ isSignal: true, alias: "column", required: true }] }], input: [{ type: i0.ViewChild, args: ['input', { isSignal: true }] }] } });
2545
+ }, providers: [
2546
+ configureTooltip(),
2547
+ ], template: "@let column = this.column();\n@if (column.filterable()) {\n @let active = filter() !== null && filter() !== '';\n\n <sci-icon class=\"filter e2e-filter\" [class.active]=\"active\">scion:filter</sci-icon>\n\n @switch (column.type) {\n @case ('number') {\n <input [(ngModel)]=\"filter\" type=\"number\" #input/>\n }\n @case ('enum') {\n <input [ngModel]=\"(filter() | sciEnumLabel:column.enumeration())()\" type=\"button\" #input>\n <sci-icon class=\"chevron\">{{menu ? 'scion:chevron_up' : 'scion:chevron_down'}}</sci-icon>\n }\n @default {\n <input [(ngModel)]=\"filter\" type=\"text\" #input/>\n }\n }\n\n @if (active) {\n <button class=\"clear e2e-clear\" (click)=\"onReset($event)\" [attr.title]=\"('%scion:components:clear.tooltip' | sciText)()\">\n <sci-icon>scion:clear</sci-icon>\n </button>\n }\n}\n", styles: ["@charset \"UTF-8\";:host{display:flex;gap:.25em;align-items:center;padding-inline:var(--\\275sci-table-cell-padding-inline);border-bottom:var(--\\275sci-table-border-size) solid transparent;margin-bottom:-1px;transition:border-bottom-color ease-in-out .15s,color ease-in-out .15s}:host:focus-within:not(:disabled):not(:has(input:disabled)){border-bottom-color:var(--sci-color-accent)}:host>sci-icon.filter.active{color:var(--sci-color-accent)}:host:has(input[type=button]){cursor:var(--sci-button-cursor)}:host:has(input[type=button]):has(button.clear)>sci-icon.chevron{display:none}:host.collapsed{gap:0}:host>input{all:unset;flex:auto;align-self:stretch;width:0;min-width:0;color:var(--sci-color-text);appearance:auto;box-sizing:border-box}:host>input[type=number]::-webkit-outer-spin-button,:host>input[type=number]::-webkit-inner-spin-button{appearance:none;margin:0}:host>input[type=button]{white-space:nowrap;text-overflow:ellipsis}:host>button.clear{all:unset;display:inline-grid;place-content:center;place-items:center;padding:.25em;border-radius:var(--sci-button-border-radius);-webkit-user-select:none;user-select:none;overflow:hidden;cursor:var(--sci-button-cursor);--sci-icon-size: var(--sci-button-icon-size)}:host>button.clear:hover:where(:not(:disabled)){background-color:var(--sci-button-background-color-hover)}:host>button.clear:active:where(:not(:disabled)){background-color:var(--sci-button-background-color-active)}:host>button.clear:focus:not(:focus-visible){outline:none}:host>button.clear:focus-visible{outline:var(--sci-button-outline-width) solid var(--sci-color-accent)}:host>button.clear:disabled{color:var(--sci-button-text-color-disabled)}:host>button.clear{padding:1px}\n"] }]
2548
+ }], ctorParameters: () => [], propDecorators: { column: [{ type: i0.Input, args: [{ isSignal: true, alias: "column", required: true }] }], inputElement: [{ type: i0.ViewChild, args: ['input', { isSignal: true }] }] } });
2364
2549
  function trim(value) {
2365
2550
  return typeof value === 'string' ? value.trim() || null : value;
2366
2551
  }
2552
+ function configureTooltip() {
2553
+ return [
2554
+ {
2555
+ provide: TOOLTIP_ON_OVERFLOW_DIRECTIVE_SOURCE_ELEMENT,
2556
+ useFactory: () => inject(SciColumnFilterComponent).inputElement,
2557
+ },
2558
+ ];
2559
+ }
2367
2560
 
2368
2561
  /*
2369
2562
  * Copyright (c) 2018-2026 Swiss Federal Railways
@@ -2495,7 +2688,7 @@ class SciTableComponent {
2495
2688
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: SciTableComponent, isStandalone: true, selector: "sci-table", inputs: { name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: true, transformFunction: null }, table: { classPropertyName: "table", publicName: "table", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { primaryAction: "primaryAction" }, host: { properties: { "attr.name": "name()", "style.--\u0275sci-table-scrolling": "table().scrolling() ? `true` : null", "style.--\u0275sci-table-resizing": "table().resizing() ? `true` : null" } }, providers: [
2496
2689
  provideSciTable(),
2497
2690
  SciTableSelectionService,
2498
- ], viewQueries: [{ propertyName: "_viewport", first: true, predicate: SciTableViewportComponent, descendants: true, read: ElementRef, isSignal: true }, { propertyName: "_viewportClient", first: true, predicate: SciTableGridComponent, descendants: true, read: ElementRef, isSignal: true }, { propertyName: "_tableHeader", first: true, predicate: SciTableHeaderComponent, descendants: true, read: ElementRef, isSignal: true }, { propertyName: "_tableBody", first: true, predicate: SciTableBodyComponent, descendants: true, read: ElementRef, isSignal: true }, { propertyName: "_itemSizeSyntheticElement", first: true, predicate: ["item_size_synthetic_element"], descendants: true, isSignal: true }, { propertyName: "_cellPaddingSyntheticElement", first: true, predicate: ["cell_padding_synthetic_element"], descendants: true, isSignal: true }, { propertyName: "rows", predicate: SciTableRowComponent, descendants: true, isSignal: true }], ngImport: i0, template: "@let table = this.table();\n\n<sci-table-viewport #viewport>\n <sci-table-grid>\n <!-- Table header -->\n @if (table.showHeader() || table.filterable()) {\n <sci-table-header/>\n }\n\n <!-- Table body -->\n <sci-table-body sciTableKeyboardNavigator>\n @if (!table.error()) {\n <!-- Track index to reuse the table row elements -->\n @for (row of table.rows(); track $index) {\n <sci-table-row [row]=\"row\" (primaryAction)=\"onRowPrimaryAction($event)\" [sciAttributes]=\"row.bindings?.attributes()\"/>\n } @empty {\n <span class=\"message e2e-no-data\">{{('%scion.components.no_items.message' | sciText)()}}</span>\n }\n } @else {\n <span class=\"message e2e-datasource\">\n {{('%scion.components.datasource_error.message' | sciText)()}}\n\n <button (click)=\"onRetry()\" class=\"action e2e-datasource-retry\">\n <!-- TODO [dwi] Replace with SCION icon -->\n <sci-icon>refresh</sci-icon>\n <span>{{('%scion.components.retry.label' | sciText)()}}</span>\n </button>\n </span>\n }\n\n <!-- Render active row outline separately to preserve row border-radius within block selection. -->\n @if (table.activeRow()) {\n <div class=\"active-row-outline e2e-active-row-outline\"></div>\n }\n </sci-table-body>\n\n <!-- Render column splitters. -->\n <sci-table-columns [rows]=\"rows()\"/>\n </sci-table-grid>\n</sci-table-viewport>\n\n<!-- Scrollbars -->\n<sci-scrollbar direction=\"hscroll\" [viewport]=\"viewport.host\"/>\n<sci-scrollbar direction=\"vscroll\" [viewport]=\"viewport.host\"/>\n\n@if (table.loading()) {\n <sci-throbber type=\"spinner\" class=\"loading-indicator e2e-loading-indicator\"/>\n}\n\n<!-- Synthetic element to compute the absolute pixel value of the `--sci-table-row-height`, relative to the table's font size if em-based. -->\n<div class=\"synthetic item-size\" #item_size_synthetic_element></div>\n\n<!-- Synthetic element to compute the absolute pixel value of the `--sci-table-cell-padding-inline`, relative to the table's font size if em-based. -->\n<div class=\"synthetic cell-padding\" #cell_padding_synthetic_element></div>\n", styles: ["@charset \"UTF-8\";:host{display:block;overflow:hidden;--\\275sci-table-border-size: 1px;--\\275sci-table-border-color: var(--sci-color-border)}:host:is(:not(:hover):not(:active),:host(:not(:hover):not(:active))) sci-scrollbar:not(.scrolling){opacity:0}:host>sci-table-viewport{display:block;position:relative;height:100%;overflow:auto;scrollbar-width:none;isolation:isolate;box-sizing:border-box;anchor-name:--sci-table-viewport}:host>sci-table-viewport:has(sci-table-header){--\\275sci-table-header-visible: true}:host>sci-table-viewport:has(sci-table-header>sci-column-header){--\\275sci-column-header-visible: true}:host>sci-table-viewport:has(~sci-scrollbar.scrolling){--\\275sci-table-scrolling: true}:host>sci-table-viewport>sci-table-grid{display:grid;grid-template-columns:var(--\\275sci-table-columns, repeat(auto-fit, 100px)) 0;grid-auto-rows:min-content;grid-template-rows:min-content 1fr;width:min-content;min-width:100%;min-height:100%}@container not style(--\\275sci-table-header-visible: true){:host>sci-table-viewport>sci-table-grid{grid-template-rows:1fr}}:host>sci-table-viewport>sci-table-grid>sci-table-header{z-index:1;grid-row:1;grid-column:1/-1;position:sticky;top:0}:host>sci-table-viewport>sci-table-grid>sci-table-body{anchor-name:--sci-table-body;grid-row:2}@container not style(--\\275sci-table-header-visible: true){:host>sci-table-viewport>sci-table-grid>sci-table-body{grid-row:1}}:host>sci-table-viewport>sci-table-grid>sci-table-body{display:grid;grid-template-columns:subgrid;grid-column:1/-1;grid-auto-rows:var(--sci-table-row-height);outline:none;padding-top:var(--\\275sci-table-virtual-scroll-offset-top);padding-bottom:var(--\\275sci-table-virtual-scroll-offset-bottom)}:host>sci-table-viewport>sci-table-grid>sci-table-body>sci-table-row{grid-column:1/-1}:host>sci-table-viewport>sci-table-grid>sci-table-body>sci-table-row[data-active]{anchor-name:--sci-table-active-row}:host>sci-table-viewport>sci-table-grid>sci-table-body>div.active-row-outline{position:absolute;top:anchor(--sci-table-active-row top);right:anchor(--sci-table-active-row right);bottom:anchor(--sci-table-active-row bottom);left:anchor(--sci-table-active-row left);border:var(--sci-table-row-outline-width) var(--sci-table-row-outline-style) var(--sci-table-row-outline-color);border-radius:var(--sci-table-row-border-radius);pointer-events:none}:host>sci-table-viewport>sci-table-grid>sci-table-body:not(:has(sci-table-row[data-active]))>div.active-row-outline{display:none}:host>sci-table-viewport>sci-table-grid>sci-table-body>span.message{grid-column:1/-1;grid-row:auto/span 2;justify-self:center;align-self:center;display:flex;flex-direction:column;align-items:center;gap:1em;-webkit-user-select:none;user-select:none;white-space:nowrap;color:var(--sci-color-text-subtle)}:host>sci-table-viewport>sci-table-grid>sci-table-body>span.message:has(button.action){grid-row:auto/span 4}:host>sci-table-viewport>sci-table-grid>sci-table-body>span.message>button.action{all:unset;display:inline-grid;place-content:center;place-items:center;padding:.25em;border-radius:var(--sci-button-border-radius);-webkit-user-select:none;user-select:none;overflow:hidden;cursor:var(--sci-button-cursor);--sci-icon-size: var(--sci-button-icon-size)}:host>sci-table-viewport>sci-table-grid>sci-table-body>span.message>button.action:hover:where(:not(:disabled)){background-color:var(--sci-button-background-color-hover)}:host>sci-table-viewport>sci-table-grid>sci-table-body>span.message>button.action:active:where(:not(:disabled)){background-color:var(--sci-button-background-color-active)}:host>sci-table-viewport>sci-table-grid>sci-table-body>span.message>button.action:focus:not(:focus-visible){outline:none}:host>sci-table-viewport>sci-table-grid>sci-table-body>span.message>button.action:focus-visible{outline:var(--sci-button-outline-width) solid var(--sci-color-accent)}:host>sci-table-viewport>sci-table-grid>sci-table-body>span.message>button.action:disabled{color:var(--sci-button-text-color-disabled)}:host>sci-table-viewport>sci-table-grid>sci-table-body>span.message>button.action{border:1px solid var(--sci-color-border);padding:.25em 1em}@container style(--\\275sci-table-viewport-client-width > --\\275sci-table-viewport-width){:host>sci-table-viewport>sci-table-grid>sci-table-body>span.message{position:sticky;left:50%;right:50%;width:0}}:host>sci-table-viewport>sci-table-grid>sci-table-body:has(>span.message){z-index:2}:host>sci-table-viewport>sci-table-grid>sci-table-columns{grid-area:1/1/-1/-1;z-index:1;position:sticky;top:0;max-height:var(--\\275sci-table-viewport-height)}:host>sci-scrollbar{position:absolute}:host>sci-scrollbar[direction=vscroll]{top:calc(anchor(--sci-table-body top) + 2px);right:calc(anchor(--sci-table-viewport right) + 2px);bottom:calc(anchor(--sci-table-viewport bottom) + 2px)}:host>sci-scrollbar[direction=hscroll]{left:calc(anchor(--sci-table-viewport left) + 2px);bottom:calc(anchor(--sci-table-viewport bottom) + 1px);right:calc(anchor(--sci-table-viewport right) + 2px)}@container style(--\\275sci-table-resizing: true){:host>sci-scrollbar{pointer-events:none}}:host>sci-throbber.loading-indicator{--sci-throbber-size: 16px;position:absolute;top:calc(anchor(--sci-table-viewport top) + .5em);right:calc(anchor(--sci-table-viewport right) + .5em)}:host>div.synthetic{position:absolute;pointer-events:none;visibility:hidden}:host>div.synthetic.item-size{height:var(--sci-table-row-height)}:host>div.synthetic.cell-padding{width:var(--sci-table-cell-padding-inline)}\n"], dependencies: [{ kind: "component", type: SciTableViewportComponent, selector: "sci-table-viewport" }, { kind: "component", type: SciTableHeaderComponent, selector: "sci-table-header" }, { kind: "component", type: SciTableGridComponent, selector: "sci-table-grid" }, { kind: "component", type: SciTableBodyComponent, selector: "sci-table-body" }, { kind: "component", type: SciTableRowComponent, selector: "sci-table-row", inputs: ["row"], outputs: ["primaryAction"] }, { kind: "component", type: SciTableColumnsComponent, selector: "sci-table-columns", inputs: ["rows"] }, { kind: "directive", type: SciTableKeyboardNavigatorDirective, selector: "[sciTableKeyboardNavigator]" }, { kind: "directive", type: SciAttributesDirective, selector: "[sciAttributes]", inputs: ["sciAttributes"] }, { kind: "component", type: SciScrollbarComponent, selector: "sci-scrollbar", inputs: ["direction", "viewport"] }, { kind: "component", type: SciThrobberComponent, selector: "sci-throbber", inputs: ["type"] }, { kind: "component", type: SciIconComponent, selector: "sci-icon" }, { kind: "pipe", type: SciTextPipe, name: "sciText" }], encapsulation: i0.ViewEncapsulation.ShadowDom });
2691
+ ], viewQueries: [{ propertyName: "_viewport", first: true, predicate: SciTableViewportComponent, descendants: true, read: ElementRef, isSignal: true }, { propertyName: "_viewportClient", first: true, predicate: SciTableGridComponent, descendants: true, read: ElementRef, isSignal: true }, { propertyName: "_tableHeader", first: true, predicate: SciTableHeaderComponent, descendants: true, read: ElementRef, isSignal: true }, { propertyName: "_tableBody", first: true, predicate: SciTableBodyComponent, descendants: true, read: ElementRef, isSignal: true }, { propertyName: "_itemSizeSyntheticElement", first: true, predicate: ["item_size_synthetic_element"], descendants: true, isSignal: true }, { propertyName: "_cellPaddingSyntheticElement", first: true, predicate: ["cell_padding_synthetic_element"], descendants: true, isSignal: true }, { propertyName: "rows", predicate: SciTableRowComponent, descendants: true, isSignal: true }], ngImport: i0, template: "@let table = this.table();\n\n<!-- TODO [#384] Replace with sci-viewport -->\n<sci-table-viewport #viewport>\n <sci-table-grid>\n <!-- Table header -->\n @if (table.showHeader() || table.filterable()) {\n <sci-table-header/>\n }\n\n <!-- Table body -->\n <sci-table-body sciTableKeyboardNavigator>\n @if (!table.error()) {\n <!-- Track index to reuse the table row elements -->\n @for (row of table.rows(); track $index) {\n <sci-table-row [row]=\"row\" [sciAttributes]=\"row.bindings?.attributes()\" [class]=\"row.bindings?.classes?.()\" (primaryAction)=\"onRowPrimaryAction($event)\"/>\n } @empty {\n <span class=\"message e2e-no-data\">{{('%scion:components:no_items.message' | sciText)()}}</span>\n }\n } @else {\n <span class=\"message e2e-datasource\">\n {{('%scion:components:datasource_error.message' | sciText)()}}\n\n <button (click)=\"onRetry()\" class=\"action e2e-datasource-retry\">\n <sci-icon>scion:reload</sci-icon>\n <span>{{('%scion:components:retry.action' | sciText)()}}</span>\n </button>\n </span>\n }\n\n <!-- Render active row outline separately to preserve row border-radius within block selection. -->\n @if (table.activeRow()) {\n <div class=\"active-row-outline e2e-active-row-outline\"></div>\n }\n </sci-table-body>\n\n <!-- Render column splitters. -->\n <sci-table-columns [rows]=\"rows()\"/>\n </sci-table-grid>\n</sci-table-viewport>\n\n<!-- Scrollbars -->\n<sci-scrollbar direction=\"hscroll\" [viewport]=\"viewport.host\"/>\n<sci-scrollbar direction=\"vscroll\" [viewport]=\"viewport.host\"/>\n\n@if (table.loading()) {\n <sci-throbber type=\"spinner\" class=\"loading-indicator e2e-loading-indicator\"/>\n}\n\n<!-- Synthetic element to compute the absolute pixel value of the `--sci-table-row-height`, relative to the table's font size if em-based. -->\n<div class=\"synthetic item-size\" #item_size_synthetic_element></div>\n\n<!-- Synthetic element to compute the absolute pixel value of the `--sci-table-cell-padding-inline`, relative to the table's font size if em-based. -->\n<div class=\"synthetic cell-padding\" #cell_padding_synthetic_element></div>\n", styles: ["@charset \"UTF-8\";:host{display:block;overflow:hidden;--\\275sci-table-border-size: 1px;--\\275sci-table-border-color: var(--sci-color-border)}:host>sci-table-viewport{display:block;position:relative;height:100%;overflow:auto;scrollbar-width:none;isolation:isolate;box-sizing:border-box;anchor-name:--sci-table-viewport}:host>sci-table-viewport:is(:not(:hover),:has(sci-table-header:hover))~sci-scrollbar{opacity:0}:host>sci-table-viewport:has(sci-table-header){--\\275sci-table-header-visible: true}:host>sci-table-viewport:has(sci-table-header>sci-column-header){--\\275sci-column-header-visible: true}:host>sci-table-viewport:has(~sci-scrollbar.scrolling){--\\275sci-table-scrolling: true}:host>sci-table-viewport>sci-table-grid{display:grid;grid-template-columns:var(--\\275sci-table-columns, repeat(auto-fit, 100px)) 0;grid-auto-rows:min-content;grid-template-rows:min-content 1fr;width:min-content;min-width:100%;min-height:100%}@container not style(--\\275sci-table-header-visible: true){:host>sci-table-viewport>sci-table-grid{grid-template-rows:1fr}}:host>sci-table-viewport>sci-table-grid>sci-table-header{z-index:1;grid-row:1;grid-column:1/-1;position:sticky;top:0}:host>sci-table-viewport>sci-table-grid>sci-table-body{anchor-name:--sci-table-body;grid-row:2}@container not style(--\\275sci-table-header-visible: true){:host>sci-table-viewport>sci-table-grid>sci-table-body{grid-row:1}}:host>sci-table-viewport>sci-table-grid>sci-table-body{display:grid;grid-template-columns:subgrid;grid-column:1/-1;grid-auto-rows:var(--sci-table-row-height);outline:none;padding-top:var(--\\275sci-table-virtual-scroll-offset-top);padding-bottom:var(--\\275sci-table-virtual-scroll-offset-bottom)}:host>sci-table-viewport>sci-table-grid>sci-table-body>sci-table-row{grid-column:1/-1}:host>sci-table-viewport>sci-table-grid>sci-table-body>sci-table-row[data-active]{anchor-name:--sci-table-active-row}:host>sci-table-viewport>sci-table-grid>sci-table-body>div.active-row-outline{position:absolute;top:anchor(--sci-table-active-row top);right:anchor(--sci-table-active-row right);bottom:anchor(--sci-table-active-row bottom);left:anchor(--sci-table-active-row left);border:var(--sci-table-row-outline-width) var(--sci-table-row-outline-style) var(--sci-table-row-outline-color);border-radius:var(--sci-table-row-border-radius);pointer-events:none}:host>sci-table-viewport>sci-table-grid>sci-table-body:not(:has(sci-table-row[data-active]))>div.active-row-outline{display:none}:host>sci-table-viewport>sci-table-grid>sci-table-body>span.message{grid-column:1/-1;grid-row:auto/span 2;justify-self:center;align-self:center;display:flex;flex-direction:column;align-items:center;gap:1em;-webkit-user-select:none;user-select:none;white-space:nowrap;color:var(--sci-color-text-subtle)}:host>sci-table-viewport>sci-table-grid>sci-table-body>span.message:has(button.action){grid-row:auto/span 4}:host>sci-table-viewport>sci-table-grid>sci-table-body>span.message>button.action{all:unset;display:inline-grid;place-content:center;place-items:center;padding:.25em;border-radius:var(--sci-button-border-radius);-webkit-user-select:none;user-select:none;overflow:hidden;cursor:var(--sci-button-cursor);--sci-icon-size: var(--sci-button-icon-size)}:host>sci-table-viewport>sci-table-grid>sci-table-body>span.message>button.action:hover:where(:not(:disabled)){background-color:var(--sci-button-background-color-hover)}:host>sci-table-viewport>sci-table-grid>sci-table-body>span.message>button.action:active:where(:not(:disabled)){background-color:var(--sci-button-background-color-active)}:host>sci-table-viewport>sci-table-grid>sci-table-body>span.message>button.action:focus:not(:focus-visible){outline:none}:host>sci-table-viewport>sci-table-grid>sci-table-body>span.message>button.action:focus-visible{outline:var(--sci-button-outline-width) solid var(--sci-color-accent)}:host>sci-table-viewport>sci-table-grid>sci-table-body>span.message>button.action:disabled{color:var(--sci-button-text-color-disabled)}:host>sci-table-viewport>sci-table-grid>sci-table-body>span.message>button.action{display:flex;gap:.5em;border:1px solid var(--sci-color-border);padding:.35em 1em}@container style(--\\275sci-table-viewport-client-width > --\\275sci-table-viewport-width){:host>sci-table-viewport>sci-table-grid>sci-table-body>span.message{position:sticky;left:50%;right:50%;width:0}}:host>sci-table-viewport>sci-table-grid>sci-table-body:has(>span.message){z-index:2}:host>sci-table-viewport>sci-table-grid>sci-table-columns{grid-area:1/1/-1/-1;z-index:1;position:sticky;top:0;max-height:var(--\\275sci-table-viewport-height)}:host>sci-scrollbar{position:absolute}:host>sci-scrollbar[direction=vscroll]{top:calc(anchor(--sci-table-body top) + 2px);right:calc(anchor(--sci-table-viewport right) + 2px);bottom:calc(anchor(--sci-table-viewport bottom) + 2px)}:host>sci-scrollbar[direction=hscroll]{left:calc(anchor(--sci-table-viewport left) + 2px);bottom:calc(anchor(--sci-table-viewport bottom) + 1px);right:calc(anchor(--sci-table-viewport right) + 2px)}@container style(--\\275sci-table-resizing: true){:host>sci-scrollbar{pointer-events:none}}:host>sci-throbber.loading-indicator{--sci-throbber-size: 16px;position:absolute;top:calc(anchor(--sci-table-viewport top) + .5em);right:calc(anchor(--sci-table-viewport right) + .5em)}:host>div.synthetic{position:absolute;pointer-events:none;visibility:hidden}:host>div.synthetic.item-size{height:var(--sci-table-row-height)}:host>div.synthetic.cell-padding{width:var(--sci-table-cell-padding-inline)}\n"], dependencies: [{ kind: "component", type: SciTableViewportComponent, selector: "sci-table-viewport" }, { kind: "component", type: SciTableHeaderComponent, selector: "sci-table-header" }, { kind: "component", type: SciTableGridComponent, selector: "sci-table-grid" }, { kind: "component", type: SciTableBodyComponent, selector: "sci-table-body" }, { kind: "component", type: SciTableRowComponent, selector: "sci-table-row", inputs: ["row"], outputs: ["primaryAction"] }, { kind: "component", type: SciTableColumnsComponent, selector: "sci-table-columns", inputs: ["rows"] }, { kind: "directive", type: SciTableKeyboardNavigatorDirective, selector: "[sciTableKeyboardNavigator]" }, { kind: "directive", type: SciAttributesDirective, selector: "[sciAttributes]", inputs: ["sciAttributes"] }, { kind: "component", type: SciScrollbarComponent, selector: "sci-scrollbar", inputs: ["direction", "viewport"] }, { kind: "component", type: SciThrobberComponent, selector: "sci-throbber", inputs: ["type"] }, { kind: "component", type: SciIconComponent, selector: "sci-icon" }, { kind: "pipe", type: SciTextPipe, name: "sciText" }], encapsulation: i0.ViewEncapsulation.ShadowDom });
2499
2692
  }
2500
2693
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: SciTableComponent, decorators: [{
2501
2694
  type: Component,
@@ -2519,7 +2712,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImpor
2519
2712
  ], providers: [
2520
2713
  provideSciTable(),
2521
2714
  SciTableSelectionService,
2522
- ], template: "@let table = this.table();\n\n<sci-table-viewport #viewport>\n <sci-table-grid>\n <!-- Table header -->\n @if (table.showHeader() || table.filterable()) {\n <sci-table-header/>\n }\n\n <!-- Table body -->\n <sci-table-body sciTableKeyboardNavigator>\n @if (!table.error()) {\n <!-- Track index to reuse the table row elements -->\n @for (row of table.rows(); track $index) {\n <sci-table-row [row]=\"row\" (primaryAction)=\"onRowPrimaryAction($event)\" [sciAttributes]=\"row.bindings?.attributes()\"/>\n } @empty {\n <span class=\"message e2e-no-data\">{{('%scion.components.no_items.message' | sciText)()}}</span>\n }\n } @else {\n <span class=\"message e2e-datasource\">\n {{('%scion.components.datasource_error.message' | sciText)()}}\n\n <button (click)=\"onRetry()\" class=\"action e2e-datasource-retry\">\n <!-- TODO [dwi] Replace with SCION icon -->\n <sci-icon>refresh</sci-icon>\n <span>{{('%scion.components.retry.label' | sciText)()}}</span>\n </button>\n </span>\n }\n\n <!-- Render active row outline separately to preserve row border-radius within block selection. -->\n @if (table.activeRow()) {\n <div class=\"active-row-outline e2e-active-row-outline\"></div>\n }\n </sci-table-body>\n\n <!-- Render column splitters. -->\n <sci-table-columns [rows]=\"rows()\"/>\n </sci-table-grid>\n</sci-table-viewport>\n\n<!-- Scrollbars -->\n<sci-scrollbar direction=\"hscroll\" [viewport]=\"viewport.host\"/>\n<sci-scrollbar direction=\"vscroll\" [viewport]=\"viewport.host\"/>\n\n@if (table.loading()) {\n <sci-throbber type=\"spinner\" class=\"loading-indicator e2e-loading-indicator\"/>\n}\n\n<!-- Synthetic element to compute the absolute pixel value of the `--sci-table-row-height`, relative to the table's font size if em-based. -->\n<div class=\"synthetic item-size\" #item_size_synthetic_element></div>\n\n<!-- Synthetic element to compute the absolute pixel value of the `--sci-table-cell-padding-inline`, relative to the table's font size if em-based. -->\n<div class=\"synthetic cell-padding\" #cell_padding_synthetic_element></div>\n", styles: ["@charset \"UTF-8\";:host{display:block;overflow:hidden;--\\275sci-table-border-size: 1px;--\\275sci-table-border-color: var(--sci-color-border)}:host:is(:not(:hover):not(:active),:host(:not(:hover):not(:active))) sci-scrollbar:not(.scrolling){opacity:0}:host>sci-table-viewport{display:block;position:relative;height:100%;overflow:auto;scrollbar-width:none;isolation:isolate;box-sizing:border-box;anchor-name:--sci-table-viewport}:host>sci-table-viewport:has(sci-table-header){--\\275sci-table-header-visible: true}:host>sci-table-viewport:has(sci-table-header>sci-column-header){--\\275sci-column-header-visible: true}:host>sci-table-viewport:has(~sci-scrollbar.scrolling){--\\275sci-table-scrolling: true}:host>sci-table-viewport>sci-table-grid{display:grid;grid-template-columns:var(--\\275sci-table-columns, repeat(auto-fit, 100px)) 0;grid-auto-rows:min-content;grid-template-rows:min-content 1fr;width:min-content;min-width:100%;min-height:100%}@container not style(--\\275sci-table-header-visible: true){:host>sci-table-viewport>sci-table-grid{grid-template-rows:1fr}}:host>sci-table-viewport>sci-table-grid>sci-table-header{z-index:1;grid-row:1;grid-column:1/-1;position:sticky;top:0}:host>sci-table-viewport>sci-table-grid>sci-table-body{anchor-name:--sci-table-body;grid-row:2}@container not style(--\\275sci-table-header-visible: true){:host>sci-table-viewport>sci-table-grid>sci-table-body{grid-row:1}}:host>sci-table-viewport>sci-table-grid>sci-table-body{display:grid;grid-template-columns:subgrid;grid-column:1/-1;grid-auto-rows:var(--sci-table-row-height);outline:none;padding-top:var(--\\275sci-table-virtual-scroll-offset-top);padding-bottom:var(--\\275sci-table-virtual-scroll-offset-bottom)}:host>sci-table-viewport>sci-table-grid>sci-table-body>sci-table-row{grid-column:1/-1}:host>sci-table-viewport>sci-table-grid>sci-table-body>sci-table-row[data-active]{anchor-name:--sci-table-active-row}:host>sci-table-viewport>sci-table-grid>sci-table-body>div.active-row-outline{position:absolute;top:anchor(--sci-table-active-row top);right:anchor(--sci-table-active-row right);bottom:anchor(--sci-table-active-row bottom);left:anchor(--sci-table-active-row left);border:var(--sci-table-row-outline-width) var(--sci-table-row-outline-style) var(--sci-table-row-outline-color);border-radius:var(--sci-table-row-border-radius);pointer-events:none}:host>sci-table-viewport>sci-table-grid>sci-table-body:not(:has(sci-table-row[data-active]))>div.active-row-outline{display:none}:host>sci-table-viewport>sci-table-grid>sci-table-body>span.message{grid-column:1/-1;grid-row:auto/span 2;justify-self:center;align-self:center;display:flex;flex-direction:column;align-items:center;gap:1em;-webkit-user-select:none;user-select:none;white-space:nowrap;color:var(--sci-color-text-subtle)}:host>sci-table-viewport>sci-table-grid>sci-table-body>span.message:has(button.action){grid-row:auto/span 4}:host>sci-table-viewport>sci-table-grid>sci-table-body>span.message>button.action{all:unset;display:inline-grid;place-content:center;place-items:center;padding:.25em;border-radius:var(--sci-button-border-radius);-webkit-user-select:none;user-select:none;overflow:hidden;cursor:var(--sci-button-cursor);--sci-icon-size: var(--sci-button-icon-size)}:host>sci-table-viewport>sci-table-grid>sci-table-body>span.message>button.action:hover:where(:not(:disabled)){background-color:var(--sci-button-background-color-hover)}:host>sci-table-viewport>sci-table-grid>sci-table-body>span.message>button.action:active:where(:not(:disabled)){background-color:var(--sci-button-background-color-active)}:host>sci-table-viewport>sci-table-grid>sci-table-body>span.message>button.action:focus:not(:focus-visible){outline:none}:host>sci-table-viewport>sci-table-grid>sci-table-body>span.message>button.action:focus-visible{outline:var(--sci-button-outline-width) solid var(--sci-color-accent)}:host>sci-table-viewport>sci-table-grid>sci-table-body>span.message>button.action:disabled{color:var(--sci-button-text-color-disabled)}:host>sci-table-viewport>sci-table-grid>sci-table-body>span.message>button.action{border:1px solid var(--sci-color-border);padding:.25em 1em}@container style(--\\275sci-table-viewport-client-width > --\\275sci-table-viewport-width){:host>sci-table-viewport>sci-table-grid>sci-table-body>span.message{position:sticky;left:50%;right:50%;width:0}}:host>sci-table-viewport>sci-table-grid>sci-table-body:has(>span.message){z-index:2}:host>sci-table-viewport>sci-table-grid>sci-table-columns{grid-area:1/1/-1/-1;z-index:1;position:sticky;top:0;max-height:var(--\\275sci-table-viewport-height)}:host>sci-scrollbar{position:absolute}:host>sci-scrollbar[direction=vscroll]{top:calc(anchor(--sci-table-body top) + 2px);right:calc(anchor(--sci-table-viewport right) + 2px);bottom:calc(anchor(--sci-table-viewport bottom) + 2px)}:host>sci-scrollbar[direction=hscroll]{left:calc(anchor(--sci-table-viewport left) + 2px);bottom:calc(anchor(--sci-table-viewport bottom) + 1px);right:calc(anchor(--sci-table-viewport right) + 2px)}@container style(--\\275sci-table-resizing: true){:host>sci-scrollbar{pointer-events:none}}:host>sci-throbber.loading-indicator{--sci-throbber-size: 16px;position:absolute;top:calc(anchor(--sci-table-viewport top) + .5em);right:calc(anchor(--sci-table-viewport right) + .5em)}:host>div.synthetic{position:absolute;pointer-events:none;visibility:hidden}:host>div.synthetic.item-size{height:var(--sci-table-row-height)}:host>div.synthetic.cell-padding{width:var(--sci-table-cell-padding-inline)}\n"] }]
2715
+ ], template: "@let table = this.table();\n\n<!-- TODO [#384] Replace with sci-viewport -->\n<sci-table-viewport #viewport>\n <sci-table-grid>\n <!-- Table header -->\n @if (table.showHeader() || table.filterable()) {\n <sci-table-header/>\n }\n\n <!-- Table body -->\n <sci-table-body sciTableKeyboardNavigator>\n @if (!table.error()) {\n <!-- Track index to reuse the table row elements -->\n @for (row of table.rows(); track $index) {\n <sci-table-row [row]=\"row\" [sciAttributes]=\"row.bindings?.attributes()\" [class]=\"row.bindings?.classes?.()\" (primaryAction)=\"onRowPrimaryAction($event)\"/>\n } @empty {\n <span class=\"message e2e-no-data\">{{('%scion:components:no_items.message' | sciText)()}}</span>\n }\n } @else {\n <span class=\"message e2e-datasource\">\n {{('%scion:components:datasource_error.message' | sciText)()}}\n\n <button (click)=\"onRetry()\" class=\"action e2e-datasource-retry\">\n <sci-icon>scion:reload</sci-icon>\n <span>{{('%scion:components:retry.action' | sciText)()}}</span>\n </button>\n </span>\n }\n\n <!-- Render active row outline separately to preserve row border-radius within block selection. -->\n @if (table.activeRow()) {\n <div class=\"active-row-outline e2e-active-row-outline\"></div>\n }\n </sci-table-body>\n\n <!-- Render column splitters. -->\n <sci-table-columns [rows]=\"rows()\"/>\n </sci-table-grid>\n</sci-table-viewport>\n\n<!-- Scrollbars -->\n<sci-scrollbar direction=\"hscroll\" [viewport]=\"viewport.host\"/>\n<sci-scrollbar direction=\"vscroll\" [viewport]=\"viewport.host\"/>\n\n@if (table.loading()) {\n <sci-throbber type=\"spinner\" class=\"loading-indicator e2e-loading-indicator\"/>\n}\n\n<!-- Synthetic element to compute the absolute pixel value of the `--sci-table-row-height`, relative to the table's font size if em-based. -->\n<div class=\"synthetic item-size\" #item_size_synthetic_element></div>\n\n<!-- Synthetic element to compute the absolute pixel value of the `--sci-table-cell-padding-inline`, relative to the table's font size if em-based. -->\n<div class=\"synthetic cell-padding\" #cell_padding_synthetic_element></div>\n", styles: ["@charset \"UTF-8\";:host{display:block;overflow:hidden;--\\275sci-table-border-size: 1px;--\\275sci-table-border-color: var(--sci-color-border)}:host>sci-table-viewport{display:block;position:relative;height:100%;overflow:auto;scrollbar-width:none;isolation:isolate;box-sizing:border-box;anchor-name:--sci-table-viewport}:host>sci-table-viewport:is(:not(:hover),:has(sci-table-header:hover))~sci-scrollbar{opacity:0}:host>sci-table-viewport:has(sci-table-header){--\\275sci-table-header-visible: true}:host>sci-table-viewport:has(sci-table-header>sci-column-header){--\\275sci-column-header-visible: true}:host>sci-table-viewport:has(~sci-scrollbar.scrolling){--\\275sci-table-scrolling: true}:host>sci-table-viewport>sci-table-grid{display:grid;grid-template-columns:var(--\\275sci-table-columns, repeat(auto-fit, 100px)) 0;grid-auto-rows:min-content;grid-template-rows:min-content 1fr;width:min-content;min-width:100%;min-height:100%}@container not style(--\\275sci-table-header-visible: true){:host>sci-table-viewport>sci-table-grid{grid-template-rows:1fr}}:host>sci-table-viewport>sci-table-grid>sci-table-header{z-index:1;grid-row:1;grid-column:1/-1;position:sticky;top:0}:host>sci-table-viewport>sci-table-grid>sci-table-body{anchor-name:--sci-table-body;grid-row:2}@container not style(--\\275sci-table-header-visible: true){:host>sci-table-viewport>sci-table-grid>sci-table-body{grid-row:1}}:host>sci-table-viewport>sci-table-grid>sci-table-body{display:grid;grid-template-columns:subgrid;grid-column:1/-1;grid-auto-rows:var(--sci-table-row-height);outline:none;padding-top:var(--\\275sci-table-virtual-scroll-offset-top);padding-bottom:var(--\\275sci-table-virtual-scroll-offset-bottom)}:host>sci-table-viewport>sci-table-grid>sci-table-body>sci-table-row{grid-column:1/-1}:host>sci-table-viewport>sci-table-grid>sci-table-body>sci-table-row[data-active]{anchor-name:--sci-table-active-row}:host>sci-table-viewport>sci-table-grid>sci-table-body>div.active-row-outline{position:absolute;top:anchor(--sci-table-active-row top);right:anchor(--sci-table-active-row right);bottom:anchor(--sci-table-active-row bottom);left:anchor(--sci-table-active-row left);border:var(--sci-table-row-outline-width) var(--sci-table-row-outline-style) var(--sci-table-row-outline-color);border-radius:var(--sci-table-row-border-radius);pointer-events:none}:host>sci-table-viewport>sci-table-grid>sci-table-body:not(:has(sci-table-row[data-active]))>div.active-row-outline{display:none}:host>sci-table-viewport>sci-table-grid>sci-table-body>span.message{grid-column:1/-1;grid-row:auto/span 2;justify-self:center;align-self:center;display:flex;flex-direction:column;align-items:center;gap:1em;-webkit-user-select:none;user-select:none;white-space:nowrap;color:var(--sci-color-text-subtle)}:host>sci-table-viewport>sci-table-grid>sci-table-body>span.message:has(button.action){grid-row:auto/span 4}:host>sci-table-viewport>sci-table-grid>sci-table-body>span.message>button.action{all:unset;display:inline-grid;place-content:center;place-items:center;padding:.25em;border-radius:var(--sci-button-border-radius);-webkit-user-select:none;user-select:none;overflow:hidden;cursor:var(--sci-button-cursor);--sci-icon-size: var(--sci-button-icon-size)}:host>sci-table-viewport>sci-table-grid>sci-table-body>span.message>button.action:hover:where(:not(:disabled)){background-color:var(--sci-button-background-color-hover)}:host>sci-table-viewport>sci-table-grid>sci-table-body>span.message>button.action:active:where(:not(:disabled)){background-color:var(--sci-button-background-color-active)}:host>sci-table-viewport>sci-table-grid>sci-table-body>span.message>button.action:focus:not(:focus-visible){outline:none}:host>sci-table-viewport>sci-table-grid>sci-table-body>span.message>button.action:focus-visible{outline:var(--sci-button-outline-width) solid var(--sci-color-accent)}:host>sci-table-viewport>sci-table-grid>sci-table-body>span.message>button.action:disabled{color:var(--sci-button-text-color-disabled)}:host>sci-table-viewport>sci-table-grid>sci-table-body>span.message>button.action{display:flex;gap:.5em;border:1px solid var(--sci-color-border);padding:.35em 1em}@container style(--\\275sci-table-viewport-client-width > --\\275sci-table-viewport-width){:host>sci-table-viewport>sci-table-grid>sci-table-body>span.message{position:sticky;left:50%;right:50%;width:0}}:host>sci-table-viewport>sci-table-grid>sci-table-body:has(>span.message){z-index:2}:host>sci-table-viewport>sci-table-grid>sci-table-columns{grid-area:1/1/-1/-1;z-index:1;position:sticky;top:0;max-height:var(--\\275sci-table-viewport-height)}:host>sci-scrollbar{position:absolute}:host>sci-scrollbar[direction=vscroll]{top:calc(anchor(--sci-table-body top) + 2px);right:calc(anchor(--sci-table-viewport right) + 2px);bottom:calc(anchor(--sci-table-viewport bottom) + 2px)}:host>sci-scrollbar[direction=hscroll]{left:calc(anchor(--sci-table-viewport left) + 2px);bottom:calc(anchor(--sci-table-viewport bottom) + 1px);right:calc(anchor(--sci-table-viewport right) + 2px)}@container style(--\\275sci-table-resizing: true){:host>sci-scrollbar{pointer-events:none}}:host>sci-throbber.loading-indicator{--sci-throbber-size: 16px;position:absolute;top:calc(anchor(--sci-table-viewport top) + .5em);right:calc(anchor(--sci-table-viewport right) + .5em)}:host>div.synthetic{position:absolute;pointer-events:none;visibility:hidden}:host>div.synthetic.item-size{height:var(--sci-table-row-height)}:host>div.synthetic.cell-padding{width:var(--sci-table-cell-padding-inline)}\n"] }]
2523
2716
  }], ctorParameters: () => [], propDecorators: { name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: true }] }], table: [{ type: i0.Input, args: [{ isSignal: true, alias: "table", required: true }] }], primaryAction: [{ type: i0.Output, args: ["primaryAction"] }], _viewport: [{ type: i0.ViewChild, args: [i0.forwardRef(() => SciTableViewportComponent), { ...{ read: ElementRef }, isSignal: true }] }], _viewportClient: [{ type: i0.ViewChild, args: [i0.forwardRef(() => SciTableGridComponent), { ...{ read: ElementRef }, isSignal: true }] }], _tableHeader: [{ type: i0.ViewChild, args: [i0.forwardRef(() => SciTableHeaderComponent), { ...{ read: ElementRef }, isSignal: true }] }], _tableBody: [{ type: i0.ViewChild, args: [i0.forwardRef(() => SciTableBodyComponent), { ...{ read: ElementRef }, isSignal: true }] }], _itemSizeSyntheticElement: [{ type: i0.ViewChild, args: ['item_size_synthetic_element', { isSignal: true }] }], _cellPaddingSyntheticElement: [{ type: i0.ViewChild, args: ['cell_padding_synthetic_element', { isSignal: true }] }], rows: [{ type: i0.ViewChildren, args: [i0.forwardRef(() => SciTableRowComponent), { isSignal: true }] }] } });
2524
2717
  function provideSciTable() {
2525
2718
  return {