@scion/components 22.4.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 +179 -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 +61 -60
  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 +2 -2
  13. package/fesm2022/scion-components-splitter.mjs.map +1 -1
  14. package/fesm2022/scion-components-table.mjs +584 -182
  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 +125 -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 +772 -69
  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,23 +1,117 @@
1
1
  import * as i0 from '@angular/core';
2
- import { computed, signal, InjectionToken, makeEnvironmentProviders, untracked, inject, Injector, linkedSignal, isSignal, runInInjectionContext, NgZone, resource, effect, assertNotInReactiveContext, assertInInjectionContext, DestroyRef, Injectable, input, Component, output, viewChild, Directive, afterNextRender, ElementRef, Pipe, debounced, viewChildren, ViewEncapsulation } from '@angular/core';
3
- import { coerceSignal, 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';
5
+ import { formatDate, DATE_PIPE_DEFAULT_OPTIONS, NgTemplateOutlet } from '@angular/common';
6
+ import { text, SciTextPipe } from '@scion/components/text';
4
7
  import { toObservable, rxResource, takeUntilDestroyed } from '@angular/core/rxjs-interop';
5
- import { Objects, clamp, runSafe, Observables, Arrays } from '@scion/toolkit/util';
6
- import { fromEvent, switchMap, concat, of, timer, firstValueFrom, defaultIfEmpty, throwError, skip } from 'rxjs';
8
+ import { Objects, Arrays, clamp, runSafe, Observables } from '@scion/toolkit/util';
9
+ import { LatestTaskExecutor } from '@scion/toolkit/common';
10
+ import { fromEvent, switchMap, concat, of, timer, firstValueFrom, defaultIfEmpty, throwError, combineLatestWith, tap, skip } from 'rxjs';
7
11
  import { map, startWith, first } from 'rxjs/operators';
8
12
  import { subscribeIn } from '@scion/toolkit/operators';
9
13
  import { SciScrollbarComponent } from '@scion/components/viewport';
10
14
  import { dimension } from '@scion/components/dimension';
11
- import { NgTemplateOutlet } from '@angular/common';
12
15
  import { SciIconComponent } from '@scion/components/icon';
13
16
  import { SciToolbarComponent, contributeMenu, ɵSciMenuService as _SciMenuService } from '@scion/components/menu';
14
17
  import { UUID } from '@scion/toolkit/uuid';
15
18
  import { SciSplitterComponent } from '@scion/components/splitter';
16
- import { text, SciTextPipe } from '@scion/components/text';
17
19
  import { SciThrobberComponent } from '@scion/components/throbber';
18
- import * as i1 from '@angular/forms';
20
+ import * as i2 from '@angular/forms';
19
21
  import { FormsModule } from '@angular/forms';
20
22
 
23
+ /**
24
+ * Creates a {@link SciDateValue} from the given date-like input.
25
+ *
26
+ * Calling this function is expensive due to date parsing and formatting.
27
+ */
28
+ function coerceDateValue(dateLike, options) {
29
+ const { format, locale, timezone } = options;
30
+ switch (typeof dateLike) {
31
+ case 'string': {
32
+ const isoString = ensureIso8601DatePrefix(dateLike);
33
+ return {
34
+ isoString,
35
+ millis: new Date(isoString).getTime(),
36
+ formatted: formatDate(isoString, format, locale, timezone),
37
+ };
38
+ }
39
+ case 'number': {
40
+ const millis = dateLike;
41
+ const isoString = new Date(millis).toISOString();
42
+ return {
43
+ isoString,
44
+ millis,
45
+ formatted: formatDate(isoString, format, locale, timezone),
46
+ };
47
+ }
48
+ case 'object': {
49
+ const date = dateLike;
50
+ const isoString = date.toISOString();
51
+ return {
52
+ isoString,
53
+ millis: date.getTime(),
54
+ formatted: formatDate(isoString, format, locale, timezone),
55
+ };
56
+ }
57
+ case 'undefined': {
58
+ return undefined;
59
+ }
60
+ }
61
+ }
62
+ /**
63
+ * Prepends '1970-01-01' if the date prefix is missing, as required by {@link formatDate}.
64
+ *
65
+ * Uses fast ASCII character code checks (`-`, `T`, `t`) to avoid string allocation.
66
+ */
67
+ function ensureIso8601DatePrefix(isoDate) {
68
+ // Check if iso string already starts with a full year date (YYYY-MM-DD)
69
+ if (isoDate.charCodeAt(4) === 45) { // '-'
70
+ return isoDate;
71
+ }
72
+ // Prepend epoch, adding 'T' separator if missing.
73
+ switch (isoDate.charCodeAt(0)) {
74
+ case 84: // 'T'
75
+ case 116: // 't'
76
+ return '1970-01-01' + isoDate;
77
+ default:
78
+ return '1970-01-01T' + isoDate;
79
+ }
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
+
21
115
  /*
22
116
  * Copyright (c) 2018-2026 Swiss Federal Railways
23
117
  *
@@ -41,7 +135,7 @@ class ɵSciTableColumnFactory {
41
135
  this.columns.push({
42
136
  ...column,
43
137
  type: 'string',
44
- value: descriptor.value,
138
+ value: item => text(descriptor.value(item)),
45
139
  sortable: computed(() => this._table.sortable() && !!sortable),
46
140
  filterable: computed(() => this._table.filterable() && !!filterable),
47
141
  compare: typeof sortable === 'object' ? sortable.comparator : (a, b) => a.value.localeCompare(b.value),
@@ -52,28 +146,95 @@ class ɵSciTableColumnFactory {
52
146
  addNumberColumn(descriptorLike, value) {
53
147
  const descriptor = coerceColumnDescriptor(descriptorLike, value);
54
148
  const column = this.mapToColumn(descriptor);
149
+ const locale = coerceSignal(descriptor.locale ?? inject(SCI_LOCALE));
150
+ const currency = inject(DEFAULT_CURRENCY_CODE, { optional: true }) ?? 'USD';
55
151
  this.columns.push({
56
152
  ...column,
57
153
  type: 'number',
58
- value: descriptor.value,
154
+ value: item => coerceSignal(descriptor.value(item), { coerceUndefined: true }),
59
155
  sortable: computed(() => this._table.sortable() && (descriptor.sortable ?? true)),
60
156
  filterable: computed(() => this._table.filterable() && (descriptor.filterable ?? true)),
61
157
  compare: (a, b) => a.value - b.value,
62
158
  matches: (text, context) => context.value === text,
159
+ format: computed(() => new Intl.NumberFormat(locale(), { currency, ...descriptor.format })),
63
160
  });
64
161
  return this;
65
162
  }
66
163
  addBooleanColumn(descriptorLike, value) {
164
+ const descriptor = coerceColumnDescriptor(descriptorLike, value);
165
+ this.addEnumColumn({
166
+ ...descriptor,
167
+ value: item => coerceSignal(descriptor.value(item), { coerceUndefined: true }),
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',
175
+ });
176
+ return this;
177
+ }
178
+ addDateColumn(descriptorLike, value) {
67
179
  const descriptor = coerceColumnDescriptor(descriptorLike, value);
68
180
  const column = this.mapToColumn(descriptor);
181
+ const defaults = inject(DATE_PIPE_DEFAULT_OPTIONS, { optional: true });
182
+ const locale = coerceSignal(descriptor.locale ?? inject(SCI_LOCALE));
183
+ const format = descriptor.format ?? defaults?.dateFormat ?? 'mediumDate';
184
+ const timezone = descriptor.timezone ?? defaults?.timezone;
69
185
  this.columns.push({
70
186
  ...column,
71
- type: 'boolean',
72
- value: descriptor.value,
187
+ type: 'date',
188
+ value: item => {
189
+ const dateLike = coerceSignal(descriptor.value(item), { coerceUndefined: true });
190
+ return computed(() => coerceDateValue(dateLike(), { locale: locale(), format, timezone }));
191
+ },
73
192
  sortable: computed(() => this._table.sortable() && (descriptor.sortable ?? true)),
74
193
  filterable: computed(() => this._table.filterable() && (descriptor.filterable ?? true)),
75
- compare: (a, b) => a.value === b.value ? 0 : (a.value ? 1 : -1),
76
- matches: (text, context) => context.value === text,
194
+ compare: (a, b) => a.value.millis - b.value.millis,
195
+ matches: (text, context) => context.value.formatted.toLowerCase().includes(text.toLowerCase()),
196
+ });
197
+ return this;
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',
77
238
  });
78
239
  return this;
79
240
  }
@@ -94,7 +255,7 @@ class ɵSciTableColumnFactory {
94
255
  sortable: computed(() => this._table.sortable() && !!descriptor.sortable),
95
256
  filterable: computed(() => this._table.filterable() && !!descriptor.filterable),
96
257
  compare: typeof descriptor.sortable === 'object' ? descriptor.sortable.comparator : () => 0,
97
- matches: typeof descriptor.filterable === 'object' ? descriptor.filterable.matcher : () => true,
258
+ matches: typeof descriptor.filterable === 'object' ? descriptor.filterable.matcher : () => false,
98
259
  padding: descriptor.padding ?? true,
99
260
  });
100
261
  return this;
@@ -116,7 +277,7 @@ class ɵSciTableColumnFactory {
116
277
  sortable: computed(() => this._table.sortable() && !!descriptor.sortable),
117
278
  filterable: computed(() => this._table.filterable() && !!descriptor.filterable),
118
279
  compare: typeof descriptor.sortable === 'object' ? descriptor.sortable.comparator : () => 0,
119
- matches: typeof descriptor.filterable === 'object' ? descriptor.filterable.matcher : () => true,
280
+ matches: typeof descriptor.filterable === 'object' ? descriptor.filterable.matcher : () => false,
120
281
  padding: descriptor.padding ?? true,
121
282
  });
122
283
  return this;
@@ -140,7 +301,16 @@ class ɵSciTableColumnFactory {
140
301
  }
141
302
  return {
142
303
  name: name,
143
- 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
+ }),
144
314
  resizable: computed(() => this._table.resizable() && (descriptor.resizable ?? true)),
145
315
  width: computed(() => {
146
316
  const userSettings = this._table.userSettings().columns?.find(column => column.name === name);
@@ -154,14 +324,13 @@ class ɵSciTableColumnFactory {
154
324
  }
155
325
  }
156
326
  function coerceColumnDescriptor(argument1, argument2) {
157
- switch (typeof argument1) {
158
- case 'string':
159
- return { header: argument1, value: argument2 };
160
- case 'function':
161
- return { value: argument1 };
162
- default:
163
- return argument1;
327
+ if (argument2 === undefined) {
328
+ return typeof argument1 === 'function' ? { value: argument1 } : argument1;
164
329
  }
330
+ return {
331
+ header: argument1,
332
+ value: argument2,
333
+ };
165
334
  }
166
335
 
167
336
  /*
@@ -207,6 +376,71 @@ function provideTableStorage(storage) {
207
376
  ]);
208
377
  }
209
378
 
379
+ /** @inheritDoc */
380
+ class ɵSciTableCellValuePreloader {
381
+ _executor = new LatestTaskExecutor();
382
+ _queue = new Array(); // PERF: Do not change to signal to avoid array re-allocation on enqueue (expensive); queue size is tracked via a separate signal.
383
+ _queueSize = signal(0, /* @ts-ignore */
384
+ ...(ngDevMode ? [{ debugName: "_queueSize" }] : /* istanbul ignore next */ []));
385
+ _injector = inject(Injector);
386
+ /** @inheritDoc */
387
+ loading = computed(() => this._queueSize() > 0, /* @ts-ignore */
388
+ ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
389
+ constructor() {
390
+ inject(DestroyRef).onDestroy(() => {
391
+ this._executor.destroy();
392
+ this._queue.length = 0;
393
+ this._queueSize.set(0);
394
+ });
395
+ }
396
+ /** @inheritDoc */
397
+ queue(cellValueFn) {
398
+ this._queue.push(cellValueFn);
399
+ this._queueSize.update(size => size + 1);
400
+ this._executor.submit(() => this.whenIdle().then(() => this.preloadQueuedBatch(PRELOAD_BATCH_SIZE)));
401
+ }
402
+ /**
403
+ * Preloads the next batch of queued cell values, FIFO, then, if the queue is not empty, schedules the next batch.
404
+ */
405
+ async preloadQueuedBatch(batchSize) {
406
+ this._queue.splice(0, batchSize).forEach(fn => fn());
407
+ this._queueSize.set(this._queue.length);
408
+ // Preload next batch when idle again.
409
+ if (this._queue.length) {
410
+ this._executor.submit(() => this.whenIdle().then(() => this.preloadQueuedBatch(PRELOAD_BATCH_SIZE)));
411
+ }
412
+ }
413
+ /**
414
+ * Resolves when the browser becomes idle.
415
+ *
416
+ * @see onIdle
417
+ */
418
+ whenIdle() {
419
+ return runInInjectionContext(this._injector, onIdle);
420
+ }
421
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: ɵSciTableCellValuePreloader, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
422
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: ɵSciTableCellValuePreloader });
423
+ }
424
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: ɵSciTableCellValuePreloader, decorators: [{
425
+ type: Injectable
426
+ }], ctorParameters: () => [] });
427
+ const PRELOAD_BATCH_SIZE = 50_000;
428
+
429
+ /**
430
+ * Preloads cell values in the background when the browser is idle. This prevents lag with large datasets, in particular
431
+ * for expensive computations like formatting dates and generating ISO 8601 strings, providing faster sort and filter performance.
432
+ *
433
+ * If a cell's value is requested before background preloading finishes, it is computed synchronously.
434
+ */
435
+ class SciTableCellValuePreloader {
436
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: SciTableCellValuePreloader, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
437
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: SciTableCellValuePreloader, providedIn: 'root', useClass: ɵSciTableCellValuePreloader });
438
+ }
439
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: SciTableCellValuePreloader, decorators: [{
440
+ type: Injectable,
441
+ args: [{ providedIn: 'root', useClass: ɵSciTableCellValuePreloader }]
442
+ }] });
443
+
210
444
  /*
211
445
  * Copyright (c) 2018-2026 Swiss Federal Railways
212
446
  *
@@ -245,13 +479,23 @@ class Dataset {
245
479
  count = computed(() => this._dataview().length, /* @ts-ignore */
246
480
  ...(ngDevMode ? [{ debugName: "count" }] : /* istanbul ignore next */ []));
247
481
  constructor(data, columns) {
248
- const dataset = computed(() => data().map(item => ({
249
- item,
250
- cells: columns().reduce((cells, column) => cells.set(column.name, {
251
- column: column,
252
- value: 'value' in column ? coerceSignal(untracked(() => column.value(item)), { coerceUndefined: true })() : undefined,
253
- }), new Map()),
254
- })), /* @ts-ignore */
482
+ const tableCellValuePreloader = inject(SciTableCellValuePreloader);
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 */
255
499
  ...(ngDevMode ? [{ debugName: "dataset" }] : /* istanbul ignore next */ []));
256
500
  // Memoize filtered/sorted view; recomputes only when dataset or criteria change, not when scrolling through the view, as sorting is an expensive operation.
257
501
  this._dataview = computed(() => {
@@ -261,8 +505,9 @@ class Dataset {
261
505
  const sortCriteria = this.sortCriteria();
262
506
  // PERF: Do not track signals inside `Array.sort` to avoid Angular signal tracking overhead as the comparator runs repeatedly, degrading performance otherwise.
263
507
  return untracked(() => rows
264
- .filter(row => matchesRow(row, columnFilters) && matchesGlobalFilter(row, tableFilter ?? undefined))
265
- .sort((a, b) => compareRows(a, b, sortCriteria)));
508
+ .filter(row => matchesRow(row, columnFilters, { coerceEnumValueToArray: true }) && matchesGlobalFilter(row, tableFilter, { coerceEnumValueToArray: false }))
509
+ .sort((a, b) => compareRows(a, b, sortCriteria))
510
+ .map(row => row.item));
266
511
  }, /* @ts-ignore */
267
512
  ...(ngDevMode ? [{ debugName: "_dataview" }] : /* istanbul ignore next */ []));
268
513
  }
@@ -270,22 +515,22 @@ class Dataset {
270
515
  * Returns a slice of the filtered and sorted data within the given range (start inclusive, end exclusive).
271
516
  */
272
517
  slice(start, end) {
273
- return computed(() => this._dataview().slice(start, end).map(row => row.item));
518
+ return computed(() => this._dataview().slice(start, end));
274
519
  }
275
520
  }
276
521
  /**
277
522
  * Tests whether a row matches the given global filter.
278
523
  */
279
- function matchesGlobalFilter(row, filter) {
524
+ function matchesGlobalFilter(row, filter, options) {
280
525
  if (!filter?.trim()) {
281
526
  return true;
282
527
  }
283
528
  for (const cell of row.cells.values()) {
284
- const text = coerceFilterText(filter, { to: cell.column.type });
529
+ const text = cell.column.type === 'number' ? toNumber(filter) : filter;
285
530
  if (text === undefined) {
286
531
  continue;
287
532
  }
288
- if (matchesRow(row, [{ text, columnName: cell.column.name }])) {
533
+ if (matchesRow(row, [{ text, columnName: cell.column.name }], options)) {
289
534
  return true;
290
535
  }
291
536
  }
@@ -294,13 +539,17 @@ function matchesGlobalFilter(row, filter) {
294
539
  /**
295
540
  * Tests whether a row matches the given column filters.
296
541
  */
297
- function matchesRow(row, columnFilters) {
542
+ function matchesRow(row, columnFilters, options) {
298
543
  if (!columnFilters.length) {
299
544
  return true;
300
545
  }
301
546
  for (const columnFilter of columnFilters) {
302
547
  const cell = row.cells.get(columnFilter.columnName);
303
- if (!cell.column.matches(columnFilter.text, { item: row.item, value: cell.value })) { // component and template columns have no value
548
+ if ('value' in cell.column && cell.value() === undefined) { // Component and template columns have no value.
549
+ return false;
550
+ }
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() })) {
304
553
  return false;
305
554
  }
306
555
  }
@@ -316,10 +565,22 @@ function compareRows(row1, row2, sortCriteria) {
316
565
  for (const criterion of sortCriteria) {
317
566
  const cell1 = row1.cells.get(criterion.columnName);
318
567
  const cell2 = row2.cells.get(criterion.columnName);
319
- const comparison = cell1.column.compare({ item: row1.item, value: cell1.value }, { item: row2.item, value: cell2.value });
568
+ const value1 = cell1.value();
569
+ const value2 = cell2.value();
570
+ // Always place `undefined` values at the end, independent of the sort direction. Note that component and template columns have no value.
571
+ if (value1 === undefined && value2 !== undefined) {
572
+ return 1;
573
+ }
574
+ if (value2 === undefined && value1 !== undefined) {
575
+ return -1;
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
+ }
581
+ const comparison = cell1.column.compare({ item: row1.item, value: value1 }, { item: row2.item, value: value2 });
320
582
  if (comparison !== 0) {
321
- const signum = criterion.direction === 'asc' ? 1 : -1;
322
- return signum * comparison;
583
+ return criterion.direction === 'asc' ? comparison : -comparison;
323
584
  }
324
585
  }
325
586
  return 0;
@@ -342,26 +603,28 @@ function markAsArrayDatasource(loader) {
342
603
  */
343
604
  const ARRAY_DATASOURCE_MARKER = Symbol('ARRAY_DATASOURCE_HINT');
344
605
  /**
345
- * 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.
346
607
  */
347
- function coerceFilterText(text, options) {
348
- switch (options.to) {
349
- case 'number': {
350
- const number = Number.parseFloat(text);
351
- return !Number.isNaN(number) ? number : undefined;
352
- }
353
- case 'boolean': {
354
- if (text === 'true' || text === '1') {
355
- return true;
356
- }
357
- if (text === 'false' || text === '0') {
358
- return false;
359
- }
360
- return undefined;
361
- }
362
- default: {
363
- return text;
364
- }
608
+ function toNumber(numberLike) {
609
+ const number = Number.parseFloat(numberLike);
610
+ return !Number.isNaN(number) ? number : undefined;
611
+ }
612
+ /**
613
+ * Provides lazy loading for the cell's value, preloading it when idle for better sort/filter performance.
614
+ *
615
+ * The value is computed at most once, whether it gets preloaded during idle time or requested synchronously via {@link value()}.
616
+ */
617
+ class eSciTableCellValue {
618
+ _valueFn;
619
+ _value;
620
+ constructor(preloader, _valueFn) {
621
+ this._valueFn = _valueFn;
622
+ preloader.queue(() => {
623
+ this._value ??= this._valueFn();
624
+ });
625
+ }
626
+ value() {
627
+ return this._value ??= untracked(() => this._valueFn());
365
628
  }
366
629
  }
367
630
 
@@ -441,32 +704,57 @@ class SciTableCache {
441
704
  *
442
705
  * SPDX-License-Identifier: EPL-2.0
443
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
+ */
444
712
  function provideTableRowBinding(bindings) {
445
- return {
446
- provide: SCI_TABLE_ROW_BINDING,
447
- useValue: bindings,
448
- multi: true,
449
- };
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
+ ];
450
731
  }
451
732
  class ɵSciTableRowBindingFactory {
452
- attributeBindings = new Array();
453
- classBindings = new Array();
454
- partBindings = new Array();
455
- addAttributeBinding(name, value) {
456
- if (value !== undefined) {
457
- 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);
458
740
  }
459
741
  return this;
460
742
  }
461
- addClassBinding(name) {
743
+ addPartBinding(name) {
462
744
  if (name) {
463
- this.classBindings.push(name);
745
+ this.parts.push(name);
464
746
  }
465
747
  return this;
466
748
  }
467
- addPartBinding(name) {
749
+ addAttributeBinding(name, value) {
750
+ if (value !== undefined) {
751
+ this.attributes.push([name, value]);
752
+ }
753
+ return this;
754
+ }
755
+ addClassBinding(name) {
468
756
  if (name) {
469
- this.partBindings.push(name);
757
+ this.classes.push(name);
470
758
  }
471
759
  return this;
472
760
  }
@@ -479,12 +767,20 @@ function coerceTableRowBindings(bindingsFn, item, index) {
479
767
  }, /* @ts-ignore */
480
768
  ...(ngDevMode ? [{ debugName: "factory" }] : /* istanbul ignore next */ []));
481
769
  return {
482
- attributes: computed(() => Object.fromEntries(factory().attributeBindings), { equal: Objects.isEqual }),
483
- cssClass: computed(() => factory().classBindings, { equal: Objects.isEqual }),
484
- 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 }),
485
774
  };
486
775
  }
776
+ /**
777
+ * Multi-DI token for registering a {@link SciTableRowBindingFactory}.
778
+ */
487
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');
488
784
 
489
785
  /*
490
786
  * Copyright (c) 2018-2026 Swiss Federal Railways
@@ -530,6 +826,7 @@ class ɵSciTable {
530
826
  computation: (columns, previous) => previous?.value.filter(criteria => columns.some(column => column.name === criteria.columnName)) ?? [],
531
827
  equal: Objects.isEqual });
532
828
  _cache = new SciTableCache();
829
+ _tableCellValuePreloader = inject(SciTableCellValuePreloader);
533
830
  _tableFilter = signal(null, /* @ts-ignore */
534
831
  ...(ngDevMode ? [{ debugName: "_tableFilter" }] : /* istanbul ignore next */ []));
535
832
  _selectedItems = signal(new Map(), /* @ts-ignore */
@@ -546,7 +843,8 @@ class ɵSciTable {
546
843
  computation: () => -1 });
547
844
  criteria = computed(() => ({ sort: this.sortCriteria(), filter: this.filterCriteria(), tableFilter: this._tableFilter() }), /* @ts-ignore */
548
845
  ...(ngDevMode ? [{ debugName: "criteria" }] : /* istanbul ignore next */ []));
549
- loading = this._cache.loading;
846
+ loading = computed(() => this._cache.loading() || this._tableCellValuePreloader.loading(), /* @ts-ignore */
847
+ ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
550
848
  error = this._cache.error;
551
849
  activeRow;
552
850
  hoveredRow = computed(() => this.rowsByIndex().get(this.hoveredIndex()), /* @ts-ignore */
@@ -585,7 +883,7 @@ class ɵSciTable {
585
883
  this.rowActions = descriptor.rowActions;
586
884
  this._rowBindings = [
587
885
  ...descriptor.rowBindings ? [descriptor.rowBindings] : [],
588
- ...inject(SCI_TABLE_ROW_BINDING, { optional: true }) ?? [],
886
+ ...inject(SCI_TABLE_ROW_BINDINGS, { optional: true }) ?? [],
589
887
  ];
590
888
  this._trackBy = descriptor.trackBy;
591
889
  this._dataLoaderFn = isSignal(descriptor.ɵdatasource) ? arrayDatasource(descriptor.ɵdatasource, this.columns) : descriptor.ɵdatasource;
@@ -604,17 +902,13 @@ class ɵSciTable {
604
902
  this.tableViewRef.set(undefined);
605
903
  }
606
904
  computeColumns(descriptor) {
607
- // Create separate injection context per factory invocation to clean up allocated resources, like RxJS subscriptions.
608
- let injector;
609
- return computed(() => {
610
- injector?.destroy();
611
- injector = createDestroyableInjector({ parent: this._injector });
612
- return runInInjectionContext(injector, () => {
613
- const tableColumnFactory = new ɵSciTableColumnFactory(this);
614
- descriptor.columns(tableColumnFactory);
615
- return tableColumnFactory.columns;
616
- });
617
- });
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
+ }));
618
912
  }
619
913
  /**
620
914
  * Computes the rows currently visible in the viewport (+buffer).
@@ -734,7 +1028,7 @@ class ɵSciTable {
734
1028
  cancel: () => this._cache.deleteIfLoading(cacheKey),
735
1029
  };
736
1030
  }
737
- const cacheEntryInjector = createDestroyableInjector({ parent: this._injector });
1031
+ const cacheEntryInjector = _createDestroyableInjector({ parent: this._injector });
738
1032
  const cacheEntry = {
739
1033
  rows: runInInjectionContext(cacheEntryInjector, () => {
740
1034
  // Fetch data.
@@ -747,32 +1041,23 @@ class ɵSciTable {
747
1041
  tableFilter,
748
1042
  columnFilters,
749
1043
  })), error => throwError(() => error));
1044
+ const columns$ = toObservable(this.columns);
1045
+ const injectorFactory = new _SingleActiveInjectorFactory();
750
1046
  // Create a resource to track loading and error states.
751
- const tableResponse = rxResource({ stream: () => tableResponse$ });
752
- // Create a derived resource mapping the response to rows. Must be done in a separate resource to not fetch data anew on column change.
753
- const rows = resource({ ...(ngDevMode ? { debugName: "rows" } : /* istanbul ignore next */ {}), params: ({ chain }) => {
754
- const items = chain(tableResponse)?.items;
755
- return items && { items, columns: this.columns() };
756
- },
757
- loader: async ({ params }) => this.mapItemsToRow(params.items, params.columns, pageStart),
758
- defaultValue: [] });
759
- // Synchronize total row count based on resource state.
760
- effect(() => {
761
- switch (rows.status()) {
762
- case 'resolved': {
763
- // Update count only after rows resolve to prevent premature scroll invalidation.
764
- this.totalCount.set(tableResponse.value()?.totalCount);
765
- break;
766
- }
767
- case 'error': {
768
- console.error(rows.error().cause);
1047
+ return rxResource({
1048
+ stream: () => tableResponse$.pipe(combineLatestWith(columns$), map(([response, columns]) => ({
1049
+ rows: runInInjectionContext(injectorFactory.create(), () => this.mapItemsToRow(response.items, columns, pageStart)),
1050
+ totalCount: response.totalCount,
1051
+ })), tap({
1052
+ next: ({ totalCount }) => this.totalCount.set(totalCount),
1053
+ error: error => {
1054
+ console.error(error);
769
1055
  // Reset count to scroll to the top so the user sees the error.
770
1056
  this.totalCount.set(0);
771
- break;
772
- }
773
- }
1057
+ },
1058
+ }), map(({ rows }) => rows)),
1059
+ defaultValue: [],
774
1060
  });
775
- return rows;
776
1061
  }),
777
1062
  dispose: () => cacheEntryInjector.destroy(),
778
1063
  start: pageStart,
@@ -820,12 +1105,9 @@ class ɵSciTable {
820
1105
  if (!column) {
821
1106
  throw Error(`[NullColumnError] Column '${options.columnName}' not found in table '${this.name()}'.`);
822
1107
  }
823
- this.filterCriteria.update(filterCriterion => {
824
- const otherFilterCriteria = filterCriterion.filter(filterCriterion => filterCriterion.columnName !== options.columnName);
825
- if (text === null) {
826
- return otherFilterCriteria;
827
- }
828
- 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 });
829
1111
  });
830
1112
  }
831
1113
  updateSelectedItems(updateFn) {
@@ -899,7 +1181,7 @@ class ɵSciTable {
899
1181
  hovered: computed(() => this.hoveredRow()?.index === index),
900
1182
  bindings: coerceTableRowBindings(this._rowBindings, item, pageStart + i),
901
1183
  cells: columns.map(column => ({
902
- value: column.type !== 'component' && column.type !== 'template' ? coerceSignal(column.value(item)) : undefined,
1184
+ value: 'value' in column ? column.value(item) : undefined,
903
1185
  component: column.type === 'component' ? column.component(item) : undefined,
904
1186
  template: column.type === 'template' ? column.template(item) : undefined,
905
1187
  type: column.type,
@@ -1455,6 +1737,30 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImpor
1455
1737
  type: Injectable
1456
1738
  }] });
1457
1739
 
1740
+ /*
1741
+ * Copyright (c) 2018-2026 Swiss Federal Railways
1742
+ *
1743
+ * This program and the accompanying materials are made
1744
+ * available under the terms of the Eclipse Public License 2.0
1745
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
1746
+ *
1747
+ * SPDX-License-Identifier: EPL-2.0
1748
+ */
1749
+ /**
1750
+ * Formats a given number based on the provided {@link Intl.NumberFormat}.
1751
+ */
1752
+ class SciNumberPipe {
1753
+ transform(number, format) {
1754
+ return number !== undefined ? format.format(number) : undefined;
1755
+ }
1756
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: SciNumberPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
1757
+ static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "22.1.7", ngImport: i0, type: SciNumberPipe, isStandalone: true, name: "sciNumber" });
1758
+ }
1759
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: SciNumberPipe, decorators: [{
1760
+ type: Pipe,
1761
+ args: [{ name: 'sciNumber' }]
1762
+ }] });
1763
+
1458
1764
  /*
1459
1765
  * Copyright (c) 2018-2026 Swiss Federal Railways
1460
1766
  *
@@ -1472,6 +1778,7 @@ class SciTableCellComponent {
1472
1778
  template = this.computeTemplate();
1473
1779
  templateContext = this.computeTemplateContext();
1474
1780
  partAttribute = this.computePartAttribute();
1781
+ value = this.computeValueAttribute();
1475
1782
  computeTemplate() {
1476
1783
  return computed(() => {
1477
1784
  const cell = this.cell();
@@ -1499,27 +1806,60 @@ class SciTableCellComponent {
1499
1806
  }
1500
1807
  computePartAttribute() {
1501
1808
  return computed(() => {
1502
- return [
1503
- ...Arrays.coerce(this.row().bindings?.part()),
1504
- this.cell().column.name,
1505
- ].join(' ');
1809
+ const cell = this.cell();
1810
+ const referenceType = cell.type === 'enum' ? cell.column.referenceType : undefined;
1811
+ return Arrays.distinct([
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}`,
1814
+ // Enable column styling based on column name.
1815
+ cell.column.name,
1816
+ // Enable row styling based on selection state.
1817
+ this.row().selected() ? 'row:selected' : 'row:not:selected',
1818
+ // Enable row styling based on active state.
1819
+ this.row().active() ? 'row:active' : 'row:not:active',
1820
+ // Enable row styling based on row bindings.
1821
+ ...Arrays.coerce(this.row().bindings?.parts()),
1822
+ ]).join(' ');
1823
+ });
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;
1506
1842
  });
1507
1843
  }
1508
1844
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: SciTableCellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1509
- 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": "row().selected() ? null : 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 <!-- TODO [dwi] Use another ligature for icon scion.clear -->\n <sci-icon class=\"checkbox\">{{cell.value() ? 'scion.checkmark' : 'scion.clear'}}</sci-icon>\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]){display:inline;white-space:nowrap;text-overflow:ellipsis;line-height:var(--\\275sci-table-row-height)}:host[data-type=number]{text-align:end}: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"] }] });
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" }] });
1510
1846
  }
1511
1847
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: SciTableCellComponent, decorators: [{
1512
1848
  type: Component,
1513
1849
  args: [{ selector: 'sci-table-cell', host: {
1514
1850
  '[attr.data-type]': 'cell().column.type',
1515
1851
  '[attr.data-column]': 'cell().column.name',
1852
+ '[attr.data-value]': 'value()',
1516
1853
  '[attr.data-padding]': '!cell().column.padding ? false : null',
1517
- '[attr.part]': 'row().selected() ? null : partAttribute()', // prevent styling selected rows
1854
+ '[attr.part]': 'partAttribute()',
1518
1855
  }, imports: [
1519
1856
  NgTemplateOutlet,
1520
1857
  SciIconComponent,
1521
1858
  SciComponentOutletDirective,
1522
- ], 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 <!-- TODO [dwi] Use another ligature for icon scion.clear -->\n <sci-icon class=\"checkbox\">{{cell.value() ? 'scion.checkmark' : 'scion.clear'}}</sci-icon>\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]){display:inline;white-space:nowrap;text-overflow:ellipsis;line-height:var(--\\275sci-table-row-height)}:host[data-type=number]{text-align:end}:host[data-padding=false]{border-inline:none;align-items:stretch}\n"] }]
1859
+ SciNumberPipe,
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"] }]
1523
1863
  }], propDecorators: { cell: [{ type: i0.Input, args: [{ isSignal: true, alias: "cell", required: true }] }], row: [{ type: i0.Input, args: [{ isSignal: true, alias: "row", required: true }] }] } });
1524
1864
 
1525
1865
  /*
@@ -1572,30 +1912,29 @@ class SciTableRowComponent {
1572
1912
  }
1573
1913
  contributeRowActions() {
1574
1914
  contributeMenu(this.rowActionsToolbarName, toolbar => {
1575
- const rowActionsFactoryFn = this.table().rowActions;
1576
- if (!this.row().loading && rowActionsFactoryFn) {
1577
- rowActionsFactoryFn(toolbar, this.row().item, this.row().index);
1915
+ if (!this.row().loading) {
1916
+ this.table().rowActions?.(toolbar, this.row().item, this.row().index);
1578
1917
  }
1579
1918
  });
1580
1919
  }
1581
1920
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: SciTableRowComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1582
- 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 <!--eslint-disable-next-line @angular-eslint/template/click-events-have-key-events,@angular-eslint/template/interactive-supports-focus -->\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:0s;animation-delay:50ms;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" }] });
1583
1922
  }
1584
1923
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: SciTableRowComponent, decorators: [{
1585
1924
  type: Component,
1586
1925
  args: [{ selector: 'sci-table-row', imports: [
1587
1926
  SciTableCellComponent,
1588
1927
  SciToolbarComponent,
1928
+ SciIconComponent,
1589
1929
  ], host: {
1590
1930
  '[attr.data-active]': `row().active() ? '' : null`,
1591
1931
  '[attr.data-selected]': `row().selected() ? '' : null`,
1592
1932
  '[attr.data-hovered]': `row().hovered() ? '' : null`,
1593
- '[class]': 'row().bindings?.cssClass?.()',
1594
1933
  '(click)': 'onRowClick($event)',
1595
1934
  '(dblclick)': 'onRowDblClick($event)',
1596
1935
  '(mouseenter)': 'onRowMouseEnter()',
1597
1936
  '(mouseleave)': 'onRowMouseLeave($event)',
1598
- }, 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 <!--eslint-disable-next-line @angular-eslint/template/click-events-have-key-events,@angular-eslint/template/interactive-supports-focus -->\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:0s;animation-delay:50ms;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"] }]
1599
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 }] }] } });
1600
1939
 
1601
1940
  /*
@@ -1883,14 +2222,14 @@ class SciTableColumnsComponent {
1883
2222
  }
1884
2223
  }
1885
2224
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: SciTableColumnsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1886
- 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"] }] });
1887
2226
  }
1888
2227
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: SciTableColumnsComponent, decorators: [{
1889
2228
  type: Component,
1890
2229
  args: [{ selector: 'sci-table-columns', imports: [
1891
2230
  SciSplitterComponent,
1892
2231
  SciTableColumnComponent,
1893
- ], 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"] }]
1894
2233
  }], propDecorators: { rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: true }] }] } });
1895
2234
 
1896
2235
  /*
@@ -2006,15 +2345,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImpor
2006
2345
  class SciColumnHeaderComponent {
2007
2346
  column = input.required(/* @ts-ignore */
2008
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
2009
2350
  table = inject(ɵSCI_TABLE);
2010
- header = text(computed(() => this.column().header()));
2011
2351
  sortDirection = computed(() => this.table().sortCriteria().find(criteria => criteria.columnName === this.column().name)?.direction, /* @ts-ignore */
2012
2352
  ...(ngDevMode ? [{ debugName: "sortDirection" }] : /* istanbul ignore next */ []));
2013
2353
  onSort(event) {
2014
2354
  this.table().sort(this.column().name, event.ctrlKey || event.metaKey);
2015
2355
  }
2016
2356
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: SciColumnHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2017
- 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"] }] });
2018
2360
  }
2019
2361
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: SciColumnHeaderComponent, decorators: [{
2020
2362
  type: Component,
@@ -2023,9 +2365,27 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImpor
2023
2365
  NgTemplateOutlet,
2024
2366
  ], host: {
2025
2367
  '[attr.data-column]': 'column().name',
2026
- '[attr.title]': 'header()',
2027
- }, 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"] }]
2028
- }], 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
+ }
2029
2389
 
2030
2390
  /*
2031
2391
  * Copyright (c) 2018-2026 Swiss Federal Railways
@@ -2036,22 +2396,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImpor
2036
2396
  *
2037
2397
  * SPDX-License-Identifier: EPL-2.0
2038
2398
  */
2039
- class SciColumnFilterBooleanLabelPipe {
2040
- transform(value) {
2041
- if (value === true) {
2042
- return '%scion.components.yes.value';
2043
- }
2044
- if (value === false) {
2045
- return '%scion.components.no.value';
2046
- }
2047
- return null;
2048
- }
2049
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: SciColumnFilterBooleanLabelPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
2050
- 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" });
2051
2413
  }
2052
- 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: [{
2053
2415
  type: Pipe,
2054
- args: [{ name: 'sciColumnFilterBooleanLabel' }]
2416
+ args: [{ name: 'sciEnumLabel' }]
2055
2417
  }] });
2056
2418
 
2057
2419
  /*
@@ -2066,33 +2428,36 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImpor
2066
2428
  class SciColumnFilterComponent {
2067
2429
  column = input.required(/* @ts-ignore */
2068
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
2069
2433
  filter = signal(null, /* @ts-ignore */
2070
2434
  ...(ngDevMode ? [{ debugName: "filter" }] : /* istanbul ignore next */ []));
2071
- input = viewChild('input', /* @ts-ignore */
2072
- ...(ngDevMode ? [{ debugName: "input" }] : /* istanbul ignore next */ []));
2435
+ dimension = dimension(inject(ElementRef));
2073
2436
  _table = inject(ɵSCI_TABLE);
2074
2437
  _menuService = inject(_SciMenuService);
2075
2438
  _injector = inject(Injector);
2076
2439
  _host = inject(ElementRef).nativeElement;
2077
- _menu;
2440
+ menu;
2078
2441
  constructor() {
2079
2442
  this.bindToModel();
2080
2443
  }
2081
2444
  onFilterClick() {
2082
- if (this.column().type === 'boolean') {
2083
- this.toggleBooleanMenu();
2445
+ if (this.column().type === 'enum') {
2446
+ this.toggleEnumChooser();
2447
+ }
2448
+ else {
2449
+ this.inputElement().nativeElement.focus();
2084
2450
  }
2085
- this.input().nativeElement.focus();
2086
2451
  }
2087
2452
  onReset(event) {
2088
2453
  this.filter.set(null);
2089
- this.input().nativeElement.focus();
2454
+ this.inputElement().nativeElement.focus();
2090
2455
  event.stopPropagation(); // prevent opening menu
2091
2456
  }
2092
2457
  bindToModel() {
2093
2458
  const filterDebounced = debounced(computed(() => trim(this.filter())), async (value) => {
2094
- // Debounce only if not empty.
2095
- if (value !== null && typeof value !== 'boolean') {
2459
+ // Debounce only if not empty and not an enum.
2460
+ if (value !== null && this.column().type !== 'enum') {
2096
2461
  await firstValueFrom(timer(200));
2097
2462
  }
2098
2463
  });
@@ -2105,56 +2470,93 @@ class SciColumnFilterComponent {
2105
2470
  });
2106
2471
  });
2107
2472
  }
2108
- toggleBooleanMenu() {
2473
+ toggleEnumChooser() {
2109
2474
  // Close menu if already opened.
2110
- if (this._menu) {
2111
- this._menu.close();
2475
+ if (this.menu) {
2476
+ this.menu.close();
2112
2477
  return;
2113
2478
  }
2114
- const injector = createDestroyableInjector({ parent: this._injector });
2479
+ const column = this.column();
2480
+ const injector = _createDestroyableInjector({ parent: this._injector });
2115
2481
  runInInjectionContext(injector, () => {
2482
+ const menuId = `menu:column-filter-${UUID.randomUUID()}`;
2116
2483
  // Contribute menu items.
2117
- contributeMenu('menu:column-filter', menu => menu
2118
- .addMenuItem({ label: '%scion.components.yes.value', attributes: { 'data-option': 'true' }, onSelect: () => this.filter.set(true) })
2119
- .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
+ });
2120
2503
  // Open menu.
2121
- this._menu = this._menuService.open('menu:column-filter', {
2504
+ this.menu = this._menuService.open(menuId, {
2122
2505
  anchor: this._host,
2123
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
2124
2511
  offset: { y: -1 },
2125
2512
  });
2126
2513
  // Close menu when start resizing a column. By default, closes the menu only on mouse up.
2127
2514
  toObservable(this._table().resizing)
2128
2515
  .pipe(skip(1))
2129
- .subscribe(() => this._menu?.close());
2130
- this._menu.onClose(() => {
2516
+ .subscribe(() => this.menu?.close());
2517
+ this.menu.onClose(() => {
2131
2518
  injector.destroy();
2132
2519
  // Restore focus manually as it is not restored automatically since opening the popover inside Shadow DOM.
2133
2520
  // TODO [menu] Consider restore focus manually in Menu API due to restriction in shadow DOM.
2134
- this.input().nativeElement.focus();
2135
- this._menu = undefined;
2521
+ this.inputElement().nativeElement.focus();
2522
+ this.menu = undefined;
2136
2523
  });
2137
2524
  });
2138
2525
  }
2139
2526
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: SciColumnFilterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2140
- 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" }] });
2141
2530
  }
2142
2531
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: SciColumnFilterComponent, decorators: [{
2143
2532
  type: Component,
2144
2533
  args: [{ selector: 'sci-column-filter', imports: [
2145
2534
  FormsModule,
2146
2535
  SciIconComponent,
2536
+ SciEnumLabelPipe,
2147
2537
  SciTextPipe,
2148
- SciColumnFilterBooleanLabelPipe,
2538
+ ], hostDirectives: [
2539
+ SciTooltipOnOverflowDirective,
2149
2540
  ], host: {
2150
2541
  '[attr.data-column]': 'column().name',
2151
2542
  '[attr.data-disabled]': `!column().filterable() ? '' : null`,
2543
+ '[class.collapsed]': 'dimension().clientWidth < 50',
2152
2544
  '(click)': 'column().filterable() && onFilterClick()',
2153
- }, 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"] }]
2154
- }], 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 }] }] } });
2155
2549
  function trim(value) {
2156
2550
  return typeof value === 'string' ? value.trim() || null : value;
2157
2551
  }
2552
+ function configureTooltip() {
2553
+ return [
2554
+ {
2555
+ provide: TOOLTIP_ON_OVERFLOW_DIRECTIVE_SOURCE_ELEMENT,
2556
+ useFactory: () => inject(SciColumnFilterComponent).inputElement,
2557
+ },
2558
+ ];
2559
+ }
2158
2560
 
2159
2561
  /*
2160
2562
  * Copyright (c) 2018-2026 Swiss Federal Railways
@@ -2286,7 +2688,7 @@ class SciTableComponent {
2286
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: [
2287
2689
  provideSciTable(),
2288
2690
  SciTableSelectionService,
2289
- ], 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\"/>\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{--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 });
2290
2692
  }
2291
2693
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: SciTableComponent, decorators: [{
2292
2694
  type: Component,
@@ -2310,7 +2712,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImpor
2310
2712
  ], providers: [
2311
2713
  provideSciTable(),
2312
2714
  SciTableSelectionService,
2313
- ], 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\"/>\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{--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"] }]
2314
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 }] }] } });
2315
2717
  function provideSciTable() {
2316
2718
  return {