@akcelik/strct 5.29.0 → 5.31.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.
@@ -1098,9 +1098,20 @@ function strctDevWarn(key, message) {
1098
1098
  warned.add(key);
1099
1099
  console.warn(message);
1100
1100
  }
1101
+ /**
1102
+ * A diagnostic that keeps its own state (one that collects names and reports
1103
+ * them together, say) registers how to forget it, so the test hook below
1104
+ * stays the one way to reset dev-mode diagnostics.
1105
+ */
1106
+ const resetters = new Set();
1107
+ function strctOnDevWarnReset(forget) {
1108
+ resetters.add(forget);
1109
+ }
1101
1110
  /** Test hook: forget which warnings have fired. Not part of the public API. */
1102
1111
  function resetStrctDevWarnings() {
1103
1112
  warned.clear();
1113
+ for (const forget of resetters)
1114
+ forget();
1104
1115
  }
1105
1116
 
1106
1117
  /**
@@ -15232,6 +15243,37 @@ const DG_LABELS = {
15232
15243
  pageOf: (page, pages) => `Page ${page} of ${pages}`,
15233
15244
  rowsMatch: (shown, total) => `${shown} of ${total} rows match`,
15234
15245
  };
15246
+
15247
+ /*
15248
+ * ── The grid's features, as objects (5.30.0) ──────────────────────────────
15249
+ *
15250
+ * The component grew to 54 inputs, which is more than a template can read.
15251
+ * Each cluster of them becomes one object; the flat inputs keep working
15252
+ * through 5.x and dev mode says which object each has moved into, so a
15253
+ * consumer upgrades a screen at a time. They are removed in 6.0.
15254
+ *
15255
+ * What stays flat on purpose: the two-way state (`selectedIds`, `filters`,
15256
+ * `columnState`, `collapsedGroups`, `quickFilter`, `activeRowId`,
15257
+ * `selectedId`), because `[(…)]` cannot bind through a property of an
15258
+ * object; and the grid's core (`columns`, `rows`, `rowId`, `labels`) with the
15259
+ * handful of look flags.
15260
+ */
15261
+ /** Flat inputs already named in a deprecation warning, per app run. */
15262
+ const warnedFlatInputs = new Set();
15263
+ const pendingFlatInputWarnings = new Set();
15264
+ let flatInputWarningQueued = false;
15265
+ strctOnDevWarnReset(() => {
15266
+ warnedFlatInputs.clear();
15267
+ pendingFlatInputWarnings.clear();
15268
+ });
15269
+ /**
15270
+ * `virtual` and `pane` take the flag they always took — including the bare
15271
+ * attribute, which arrives as `''` — or the object that replaced their
15272
+ * satellite inputs.
15273
+ */
15274
+ function objectOrBoolean(value) {
15275
+ return typeof value === 'object' && value !== null ? value : booleanAttribute(value);
15276
+ }
15235
15277
  /**
15236
15278
  * A fully custom group header row:
15237
15279
  *
@@ -15328,28 +15370,25 @@ class StrctDatagrid {
15328
15370
  rows = input.required(/* @ts-ignore */
15329
15371
  ...(ngDevMode ? [{ debugName: "rows" }] : /* istanbul ignore next */ []));
15330
15372
  /** Rows per page (0 disables paging). */
15331
- pageSize = input(0, /* @ts-ignore */
15332
- ...(ngDevMode ? [{ debugName: "pageSize" }] : /* istanbul ignore next */ []));
15373
+ pageSizeFlat = input(0, { ...(ngDevMode ? { debugName: "pageSizeFlat" } : /* istanbul ignore next */ {}), alias: 'pageSize' });
15333
15374
  /** Enable row selection. */
15334
- selectable = input(false, { ...(ngDevMode ? { debugName: "selectable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
15375
+ selectableFlat = input(false, { ...(ngDevMode ? { debugName: "selectableFlat" } : /* istanbul ignore next */ {}), alias: 'selectable', transform: booleanAttribute });
15335
15376
  /** Expand a row in place to reveal its detail template below it. */
15336
15377
  expandable = input(false, { ...(ngDevMode ? { debugName: "expandable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
15337
15378
  /** Collapse the grid to a single column and open a side detail pane for the
15338
15379
  * clicked row (distinct from {@link expandable}). */
15339
- detailPane = input(false, { ...(ngDevMode ? { debugName: "detailPane" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
15380
+ detailPaneFlat = input(false, { ...(ngDevMode ? { debugName: "detailPaneFlat" } : /* istanbul ignore next */ {}), alias: 'detailPane', transform: booleanAttribute });
15340
15381
  /**
15341
15382
  * How wide the list column is while the pane is open — px or any CSS width.
15342
15383
  * 260px cuts a test name like "Validate Switch Enabled Teaming
15343
15384
  * Configurations" in half and hides its status.
15344
15385
  */
15345
- paneListWidth = input('260px', /* @ts-ignore */
15346
- ...(ngDevMode ? [{ debugName: "paneListWidth" }] : /* istanbul ignore next */ []));
15386
+ paneListWidthFlat = input('260px', { ...(ngDevMode ? { debugName: "paneListWidthFlat" } : /* istanbul ignore next */ {}), alias: 'paneListWidth' });
15347
15387
  /**
15348
15388
  * Below this container width (px) the pane takes the list's place, with a
15349
15389
  * way back in its header, rather than squeezing both into a phone.
15350
15390
  */
15351
- paneTakeoverWidth = input(900, /* @ts-ignore */
15352
- ...(ngDevMode ? [{ debugName: "paneTakeoverWidth" }] : /* istanbul ignore next */ []));
15391
+ paneTakeoverWidthFlat = input(900, { ...(ngDevMode ? { debugName: "paneTakeoverWidthFlat" } : /* istanbul ignore next */ {}), alias: 'paneTakeoverWidth' });
15353
15392
  /**
15354
15393
  * The row whose pane is open, two-way: a "what to do next" chip opens the
15355
15394
  * finding it settles, a URL fragment deep-links to a test, `null` closes it.
@@ -15373,7 +15412,7 @@ class StrctDatagrid {
15373
15412
  /** Show a column-chooser dropdown in the footer. */
15374
15413
  columnChooser = input(false, { ...(ngDevMode ? { debugName: "columnChooser" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
15375
15414
  /** Show a refresh button in the footer. */
15376
- sync = input(false, { ...(ngDevMode ? { debugName: "sync" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
15415
+ syncFlat = input(false, { ...(ngDevMode ? { debugName: "syncFlat" } : /* istanbul ignore next */ {}), alias: 'sync', transform: booleanAttribute });
15377
15416
  /** Disable all footer action buttons (sync, column chooser, etc.). */
15378
15417
  footerActionsDisabled = input(false, { ...(ngDevMode ? { debugName: "footerActionsDisabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
15379
15418
  /**
@@ -15389,8 +15428,7 @@ class StrctDatagrid {
15389
15428
  * exactly one row is grid behaviour rather than a column of radios the
15390
15429
  * consumer builds and wires by hand.
15391
15430
  */
15392
- selectionMode = input('none', /* @ts-ignore */
15393
- ...(ngDevMode ? [{ debugName: "selectionMode" }] : /* istanbul ignore next */ []));
15431
+ selectionModeFlat = input('none', { ...(ngDevMode ? { debugName: "selectionModeFlat" } : /* istanbul ignore next */ {}), alias: 'selectionMode' });
15394
15432
  /** Two-way single selection: the row id (per `rowId`), or null. */
15395
15433
  selectedId = model(null, /* @ts-ignore */
15396
15434
  ...(ngDevMode ? [{ debugName: "selectedId" }] : /* istanbul ignore next */ []));
@@ -15399,30 +15437,22 @@ class StrctDatagrid {
15399
15437
  * it AND say why — shown as the row's tooltip and given to assistive tech as
15400
15438
  * the control's description, following the `hint` convention.
15401
15439
  */
15402
- rowSelectable = input(null, /* @ts-ignore */
15403
- ...(ngDevMode ? [{ debugName: "rowSelectable" }] : /* istanbul ignore next */ []));
15440
+ rowSelectableFlat = input(null, { ...(ngDevMode ? { debugName: "rowSelectableFlat" } : /* istanbul ignore next */ {}), alias: 'rowSelectable' });
15404
15441
  /** No outer border, radius or shadow — for a grid inside a bordered panel. */
15405
15442
  flush = input(false, { ...(ngDevMode ? { debugName: "flush" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
15406
15443
  /** The grid's title, rendered above the header and used as its accessible name. */
15407
15444
  caption = input('', /* @ts-ignore */
15408
15445
  ...(ngDevMode ? [{ debugName: "caption" }] : /* istanbul ignore next */ []));
15409
- /**
15410
- * `'more'` swaps the pager for a count and a Load more button — the shape a
15411
- * cursor API can answer. The consumer appends rows; the grid does not page.
15412
- */
15413
- paging = input('pages', /* @ts-ignore */
15414
- ...(ngDevMode ? [{ debugName: "paging" }] : /* istanbul ignore next */ []));
15415
15446
  /** Whether there is another slice to ask for. */
15416
- hasMore = input(false, { ...(ngDevMode ? { debugName: "hasMore" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
15447
+ hasMoreFlat = input(false, { ...(ngDevMode ? { debugName: "hasMoreFlat" } : /* istanbul ignore next */ {}), alias: 'hasMore', transform: booleanAttribute });
15417
15448
  /** A slice is on its way: the button waits with a spinner. */
15418
- loadingMore = input(false, { ...(ngDevMode ? { debugName: "loadingMore" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
15449
+ loadingMoreFlat = input(false, { ...(ngDevMode ? { debugName: "loadingMoreFlat" } : /* istanbul ignore next */ {}), alias: 'loadingMore', transform: booleanAttribute });
15419
15450
  /** The grand total, when the API knows it — "Showing the latest 50 of 812". */
15420
- moreTotal = input(null, /* @ts-ignore */
15421
- ...(ngDevMode ? [{ debugName: "moreTotal" }] : /* istanbul ignore next */ []));
15451
+ moreTotalFlat = input(null, { ...(ngDevMode ? { debugName: "moreTotalFlat" } : /* istanbul ignore next */ {}), alias: 'moreTotal' });
15422
15452
  /** The user asked for the next slice. */
15423
15453
  loadMore = output();
15424
15454
  /** With `groupBy` and multiple selection: a tri-state checkbox per group. */
15425
- groupSelect = input(false, { ...(ngDevMode ? { debugName: "groupSelect" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
15455
+ groupSelectFlat = input(false, { ...(ngDevMode ? { debugName: "groupSelectFlat" } : /* istanbul ignore next */ {}), alias: 'groupSelect', transform: booleanAttribute });
15426
15456
  /**
15427
15457
  * Per-row action menu resolver. When set, each row gets a trailing actions
15428
15458
  * column with a vertical-dots (kebab) button that opens this row's menu.
@@ -15436,8 +15466,7 @@ class StrctDatagrid {
15436
15466
  * `selectable`. Assigning a new array (e.g. when opening a dialog) re-seeds the
15437
15467
  * selection; the user's subsequent toggles are preserved until the next change.
15438
15468
  */
15439
- initialSelection = input(null, /* @ts-ignore */
15440
- ...(ngDevMode ? [{ debugName: "initialSelection" }] : /* istanbul ignore next */ []));
15469
+ initialSelectionFlat = input(null, { ...(ngDevMode ? { debugName: "initialSelectionFlat" } : /* istanbul ignore next */ {}), alias: 'initialSelection' });
15441
15470
  /**
15442
15471
  * The selection itself, two-way, for `selectionMode="multiple"` — what
15443
15472
  * `selectedId` is for single mode. Write it and the grid checks those rows
@@ -15453,48 +15482,36 @@ class StrctDatagrid {
15453
15482
  /** Effective labels (defaults + overrides). */
15454
15483
  L = computed(() => ({ ...DG_LABELS, ...this.labels() }), /* @ts-ignore */
15455
15484
  ...(ngDevMode ? [{ debugName: "L" }] : /* istanbul ignore next */ []));
15456
- /**
15457
- * Virtual scrolling for large data sets: only the rows inside the viewport
15458
- * (plus a small overscan) are in the DOM — 20k+ rows stay smooth. The scroll
15459
- * area gets `viewportHeight` and a sticky header. Assumes uniform
15460
- * `rowHeight`; combine with `expandable` is unsupported.
15461
- */
15462
- virtual = input(false, { ...(ngDevMode ? { debugName: "virtual" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
15463
15485
  /** Scroll viewport height in px (virtual mode). */
15464
- viewportHeight = input(360, /* @ts-ignore */
15465
- ...(ngDevMode ? [{ debugName: "viewportHeight" }] : /* istanbul ignore next */ []));
15486
+ viewportHeightFlat = input(360, { ...(ngDevMode ? { debugName: "viewportHeightFlat" } : /* istanbul ignore next */ {}), alias: 'viewportHeight' });
15466
15487
  /**
15467
15488
  * Bound the grid's own scroll box (px), with a sticky header — what
15468
15489
  * `viewportHeight` does for `virtual`, for every other grid: a grouped
15469
15490
  * picker inside a dialog scrolls instead of growing the page.
15470
15491
  */
15471
- maxHeight = input(null, /* @ts-ignore */
15472
- ...(ngDevMode ? [{ debugName: "maxHeight" }] : /* istanbul ignore next */ []));
15492
+ maxHeightFlat = input(null, { ...(ngDevMode ? { debugName: "maxHeightFlat" } : /* istanbul ignore next */ {}), alias: 'maxHeight' });
15473
15493
  /**
15474
15494
  * Show a pencil on an editable cell when it is hovered or focused. A mouse
15475
15495
  * user finds an editable cell by double-clicking one; nothing said so.
15476
15496
  */
15477
15497
  editHint = input(false, { ...(ngDevMode ? { debugName: "editHint" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
15478
15498
  /** Uniform row height in px (virtual mode; tune when `compact`). */
15479
- rowHeight = input(38, /* @ts-ignore */
15480
- ...(ngDevMode ? [{ debugName: "rowHeight" }] : /* istanbul ignore next */ []));
15499
+ rowHeightFlat = input(38, { ...(ngDevMode ? { debugName: "rowHeightFlat" } : /* istanbul ignore next */ {}), alias: 'rowHeight' });
15481
15500
  /**
15482
15501
  * Server-side mode: the grid never sorts or slices `rows` itself — it shows
15483
15502
  * them as given and emits `(lazyLoad)` with `{ page, pageSize, sortKey,
15484
15503
  * sortDir }` whenever the user pages or sorts (and once on init). Provide
15485
15504
  * `total` so the pager and count reflect the full server-side set.
15486
15505
  */
15487
- lazy = input(false, { ...(ngDevMode ? { debugName: "lazy" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
15506
+ lazyFlat = input(false, { ...(ngDevMode ? { debugName: "lazyFlat" } : /* istanbul ignore next */ {}), alias: 'lazy', transform: booleanAttribute });
15488
15507
  /** Total row count on the server (lazy mode). */
15489
- total = input(null, /* @ts-ignore */
15490
- ...(ngDevMode ? [{ debugName: "total" }] : /* istanbul ignore next */ []));
15508
+ totalFlat = input(null, { ...(ngDevMode ? { debugName: "totalFlat" } : /* istanbul ignore next */ {}), alias: 'total' });
15491
15509
  /**
15492
15510
  * Persist the user's column widths + hidden columns under this key in
15493
15511
  * localStorage (`strct-dg:<key>`), restoring them on init. For full control
15494
15512
  * use the two-way `columnState` instead (both may be combined).
15495
15513
  */
15496
- stateKey = input(null, /* @ts-ignore */
15497
- ...(ngDevMode ? [{ debugName: "stateKey" }] : /* istanbul ignore next */ []));
15514
+ stateKeyFlat = input(null, { ...(ngDevMode ? { debugName: "stateKeyFlat" } : /* istanbul ignore next */ {}), alias: 'stateKey' });
15498
15515
  /** User column preferences (two-way): widths from resize, hidden from the chooser. */
15499
15516
  columnState = model(null, /* @ts-ignore */
15500
15517
  ...(ngDevMode ? [{ debugName: "columnState" }] : /* istanbul ignore next */ []));
@@ -15505,17 +15522,16 @@ class StrctDatagrid {
15505
15522
  * per distinct value (with a count), respecting the current sort within
15506
15523
  * groups. Paging is bypassed while grouped; not combinable with `virtual`.
15507
15524
  */
15508
- groupBy = input(null, /* @ts-ignore */
15509
- ...(ngDevMode ? [{ debugName: "groupBy" }] : /* istanbul ignore next */ []));
15525
+ groupByFlat = input(null, { ...(ngDevMode ? { debugName: "groupByFlat" } : /* istanbul ignore next */ {}), alias: 'groupBy' });
15510
15526
  /**
15511
15527
  * The groups themselves, beside `groupBy`: their order, their labels, a
15512
15528
  * tone, a note, whether they start folded and a count of the consumer's
15513
15529
  * own. Values no group mentions follow, in the order the rows arrived.
15514
15530
  */
15515
- groups = input(null, /* @ts-ignore */
15516
- ...(ngDevMode ? [{ debugName: "groups" }] : /* istanbul ignore next */ []));
15531
+ groupsFlat = input(null, { ...(ngDevMode ? { debugName: "groupsFlat" } : /* istanbul ignore next */ {}), alias: 'groups' });
15517
15532
  /** Keep a declared group that holds no rows, rather than dropping it. */
15518
- showEmptyGroups = input(false, { ...(ngDevMode ? { debugName: "showEmptyGroups" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
15533
+ showEmptyGroupsFlat = input(false, { ...(ngDevMode ? { debugName: "showEmptyGroupsFlat" } : /* istanbul ignore next */ {}), alias: 'showEmptyGroups',
15534
+ transform: booleanAttribute });
15519
15535
  /**
15520
15536
  * Which groups are folded, two-way — so a summary counter elsewhere on the
15521
15537
  * page can open the group it counts.
@@ -15538,18 +15554,17 @@ class StrctDatagrid {
15538
15554
  quickFilter = model('', /* @ts-ignore */
15539
15555
  ...(ngDevMode ? [{ debugName: "quickFilter" }] : /* istanbul ignore next */ []));
15540
15556
  /** Columns the quick term scans. Defaults to every column's `key`. */
15541
- quickFilterFields = input(null, /* @ts-ignore */
15542
- ...(ngDevMode ? [{ debugName: "quickFilterFields" }] : /* istanbul ignore next */ []));
15557
+ quickFilterFieldsFlat = input(null, { ...(ngDevMode ? { debugName: "quickFilterFieldsFlat" } : /* istanbul ignore next */ {}), alias: 'quickFilterFields' });
15543
15558
  /** Render the built-in quick-filter searchbox in the toolbar. */
15544
- quickFilterable = input(false, { ...(ngDevMode ? { debugName: "quickFilterable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
15559
+ quickFilterableFlat = input(false, { ...(ngDevMode ? { debugName: "quickFilterableFlat" } : /* istanbul ignore next */ {}), alias: 'quickFilterable',
15560
+ transform: booleanAttribute });
15545
15561
  /**
15546
15562
  * Where the built-in box sits in the toolbar. `'end'` (default) follows the
15547
15563
  * console convention: action verbs lead on the left, view controls sit at
15548
15564
  * the far right — and the box keeps a stable position across grids whose
15549
15565
  * button sets differ.
15550
15566
  */
15551
- quickFilterAlign = input('end', /* @ts-ignore */
15552
- ...(ngDevMode ? [{ debugName: "quickFilterAlign" }] : /* istanbul ignore next */ []));
15567
+ quickFilterAlignFlat = input('end', { ...(ngDevMode ? { debugName: "quickFilterAlignFlat" } : /* istanbul ignore next */ {}), alias: 'quickFilterAlign' });
15553
15568
  /**
15554
15569
  * Tree-grid: the row property holding a row's children array. Rows render
15555
15570
  * hierarchically with indent + carets; sorting applies per sibling level;
@@ -15562,8 +15577,7 @@ class StrctDatagrid {
15562
15577
  * ArrowLeft collapses an expanded one. There is no cell-level navigation —
15563
15578
  * Tab walks the interactive elements inside each row as usual.
15564
15579
  */
15565
- childrenKey = input(null, /* @ts-ignore */
15566
- ...(ngDevMode ? [{ debugName: "childrenKey" }] : /* istanbul ignore next */ []));
15580
+ childrenKeyFlat = input(null, { ...(ngDevMode ? { debugName: "childrenKeyFlat" } : /* istanbul ignore next */ {}), alias: 'childrenKey' });
15567
15581
  /**
15568
15582
  * `'table'` (the default) is what the grid has always been: a table whose
15569
15583
  * controls each take a tab stop. `'grid'` is the APG grid: the table claims
@@ -15582,8 +15596,7 @@ class StrctDatagrid {
15582
15596
  * file list selects by clicking the row, not a control in it. The row is
15583
15597
  * then the control, so pair it with `interactionMode="grid"`.
15584
15598
  */
15585
- selectionColumn = input('auto', /* @ts-ignore */
15586
- ...(ngDevMode ? [{ debugName: "selectionColumn" }] : /* istanbul ignore next */ []));
15599
+ selectionColumnFlat = input('auto', { ...(ngDevMode ? { debugName: "selectionColumnFlat" } : /* istanbul ignore next */ {}), alias: 'selectionColumn' });
15587
15600
  /**
15588
15601
  * The row was opened: a double-click, or Enter on the focused row in grid
15589
15602
  * mode. Selection is "which rows do I mean"; activation is "go".
@@ -15601,6 +15614,193 @@ class StrctDatagrid {
15601
15614
  * editor). The grid does not mutate rows — apply the value to your data and
15602
15615
  * pass the updated array back in. */
15603
15616
  cellEdit = output();
15617
+ // ── The feature objects, and the flat inputs they replace ───────────────
15618
+ // Each resolved signal below keeps the name the rest of the component (and
15619
+ // the template) already uses, so the object is read where the flat input
15620
+ // was. The object wins when it carries the property; otherwise the flat
15621
+ // input answers, as it did before 5.30.0.
15622
+ /** How rows are picked — replaces six flat inputs. */
15623
+ selectionCfg = input({}, { ...(ngDevMode ? { debugName: "selectionCfg" } : /* istanbul ignore next */ {}), alias: 'selection' });
15624
+ /** The detail pane — replaces three. `[pane]` on its own turns it on. */
15625
+ paneFlat = input(false, { ...(ngDevMode ? { debugName: "paneFlat" } : /* istanbul ignore next */ {}), alias: 'pane',
15626
+ transform: objectOrBoolean });
15627
+ paneCfg = computed(() => {
15628
+ const v = this.paneFlat();
15629
+ return typeof v === 'boolean' ? (v ? { enabled: true } : {}) : { enabled: true, ...v };
15630
+ }, /* @ts-ignore */
15631
+ ...(ngDevMode ? [{ debugName: "paneCfg" }] : /* istanbul ignore next */ []));
15632
+ /** How rows are paged. Takes the mode on its own, as before, or the object. */
15633
+ pagingFlat = input('pages', { ...(ngDevMode ? { debugName: "pagingFlat" } : /* istanbul ignore next */ {}), alias: 'paging' });
15634
+ pagingCfg = computed(() => {
15635
+ const v = this.pagingFlat();
15636
+ return typeof v === 'string' ? { mode: v } : v;
15637
+ }, /* @ts-ignore */
15638
+ ...(ngDevMode ? [{ debugName: "pagingCfg" }] : /* istanbul ignore next */ []));
15639
+ /** Numbered pages or a "load more" footer. */
15640
+ paging = computed(() => this.pagingCfg().mode ?? 'pages', /* @ts-ignore */
15641
+ ...(ngDevMode ? [{ debugName: "paging" }] : /* istanbul ignore next */ []));
15642
+ /** Virtual scrolling. Takes the flag on its own, as before, or the object. */
15643
+ virtualFlat = input(false, { ...(ngDevMode ? { debugName: "virtualFlat" } : /* istanbul ignore next */ {}), alias: 'virtual', transform: objectOrBoolean });
15644
+ virtualCfg = computed(() => {
15645
+ const v = this.virtualFlat();
15646
+ return typeof v === 'boolean' ? (v ? { enabled: true } : {}) : { enabled: true, ...v };
15647
+ }, /* @ts-ignore */
15648
+ ...(ngDevMode ? [{ debugName: "virtualCfg" }] : /* istanbul ignore next */ []));
15649
+ /** Whether the grid renders only the rows in view. */
15650
+ virtual = computed(() => this.virtualCfg().enabled ?? false, /* @ts-ignore */
15651
+ ...(ngDevMode ? [{ debugName: "virtual" }] : /* istanbul ignore next */ []));
15652
+ /** How rows are grouped — replaces four flat inputs. */
15653
+ groupingCfg = input({}, { ...(ngDevMode ? { debugName: "groupingCfg" } : /* istanbul ignore next */ {}), alias: 'grouping' });
15654
+ /** The quick filter — replaces three flat inputs. */
15655
+ filteringCfg = input({}, { ...(ngDevMode ? { debugName: "filteringCfg" } : /* istanbul ignore next */ {}), alias: 'filtering' });
15656
+ /** Remembered state — replaces `stateKey` and `sync`. */
15657
+ stateCfg = input({}, { ...(ngDevMode ? { debugName: "stateCfg" } : /* istanbul ignore next */ {}), alias: 'state' });
15658
+ /**
15659
+ * The flat inputs this grid is actually given, with the object each has
15660
+ * moved into. "Given" is read as "not its default", which is what a
15661
+ * template can be checked against from inside the component.
15662
+ */
15663
+ flatInputsInUse = [
15664
+ ['pageSize', () => this.pageSizeFlat() !== 0, '[paging].pageSize'],
15665
+ ['selectable', () => this.selectableFlat(), '[selection].enabled'],
15666
+ ['detailPane', () => this.detailPaneFlat(), '[pane].enabled'],
15667
+ ['paneListWidth', () => this.paneListWidthFlat() !== '260px', '[pane].listWidth'],
15668
+ ['paneTakeoverWidth', () => this.paneTakeoverWidthFlat() !== 900, '[pane].takeoverWidth'],
15669
+ ['sync', () => this.syncFlat(), '[state].sync'],
15670
+ ['selectionMode', () => this.selectionModeFlat() !== 'none', '[selection].mode'],
15671
+ ['rowSelectable', () => this.rowSelectableFlat() !== null, '[selection].rowSelectable'],
15672
+ ['hasMore', () => this.hasMoreFlat(), '[paging].hasMore'],
15673
+ ['loadingMore', () => this.loadingMoreFlat(), '[paging].loadingMore'],
15674
+ ['moreTotal', () => this.moreTotalFlat() !== null, '[paging].moreTotal'],
15675
+ ['groupSelect', () => this.groupSelectFlat(), '[selection].groupSelect'],
15676
+ ['initialSelection', () => this.initialSelectionFlat() !== null, '[selection].initial'],
15677
+ ['viewportHeight', () => this.viewportHeightFlat() !== 360, '[virtual].viewportHeight'],
15678
+ ['maxHeight', () => this.maxHeightFlat() !== null, '[virtual].maxHeight'],
15679
+ ['rowHeight', () => this.rowHeightFlat() !== 38, '[virtual].rowHeight'],
15680
+ ['lazy', () => this.lazyFlat(), '[paging].lazy'],
15681
+ ['total', () => this.totalFlat() !== null, '[paging].total'],
15682
+ ['stateKey', () => this.stateKeyFlat() !== null, '[state].key'],
15683
+ ['groupBy', () => this.groupByFlat() !== null, '[grouping].by'],
15684
+ ['groups', () => this.groupsFlat() !== null, '[grouping].groups'],
15685
+ ['showEmptyGroups', () => this.showEmptyGroupsFlat(), '[grouping].showEmpty'],
15686
+ ['quickFilterFields', () => this.quickFilterFieldsFlat() !== null, '[filtering].fields'],
15687
+ ['quickFilterable', () => this.quickFilterableFlat(), '[filtering].quick'],
15688
+ ['quickFilterAlign', () => this.quickFilterAlignFlat() !== 'end', '[filtering].align'],
15689
+ ['childrenKey', () => this.childrenKeyFlat() !== null, '[grouping].childrenKey'],
15690
+ ['selectionColumn', () => this.selectionColumnFlat() !== 'auto', '[selection].column'],
15691
+ ];
15692
+ /**
15693
+ * Dev mode says, once per app run, which flat inputs have moved — a
15694
+ * console line per name would be a wall in a screen with four grids, so
15695
+ * the names are collected and reported together.
15696
+ */
15697
+ reportFlatInputs() {
15698
+ const fresh = this.flatInputsInUse
15699
+ .filter(([name, given]) => given() && !warnedFlatInputs.has(name))
15700
+ .map(([name, , to]) => {
15701
+ warnedFlatInputs.add(name);
15702
+ return ` ${name} → ${to}`;
15703
+ });
15704
+ if (!fresh.length)
15705
+ return;
15706
+ for (const line of fresh)
15707
+ pendingFlatInputWarnings.add(line);
15708
+ if (flatInputWarningQueued)
15709
+ return;
15710
+ flatInputWarningQueued = true;
15711
+ setTimeout(() => {
15712
+ flatInputWarningQueued = false;
15713
+ const lines = [...pendingFlatInputWarnings].sort();
15714
+ pendingFlatInputWarnings.clear();
15715
+ console.warn(`[strct-datagrid] ${lines.length} input(s) have moved into a feature ` +
15716
+ `object and are removed in 6.0. They keep working through 5.x:\n` +
15717
+ lines.join('\n') +
15718
+ `\nThe objects are [selection], [paging], [pane], [virtual], ` +
15719
+ `[grouping], [filtering] and [state]; two-way state ` +
15720
+ `(selectedIds, filters, columnState, …) stays where it is.`);
15721
+ });
15722
+ }
15723
+ /** `[paging].pageSize` when it is set, else the flat `pageSize` (deprecated). */
15724
+ pageSize = computed(() => this.pagingCfg().pageSize ?? this.pageSizeFlat(), /* @ts-ignore */
15725
+ ...(ngDevMode ? [{ debugName: "pageSize" }] : /* istanbul ignore next */ []));
15726
+ /** `[selection].enabled` when it is set, else the flat `selectable` (deprecated). */
15727
+ selectable = computed(() => this.selectionCfg().enabled ?? this.selectableFlat(), /* @ts-ignore */
15728
+ ...(ngDevMode ? [{ debugName: "selectable" }] : /* istanbul ignore next */ []));
15729
+ /** `[pane].enabled` when it is set, else the flat `detailPane` (deprecated). */
15730
+ detailPane = computed(() => this.paneCfg().enabled ?? this.detailPaneFlat(), /* @ts-ignore */
15731
+ ...(ngDevMode ? [{ debugName: "detailPane" }] : /* istanbul ignore next */ []));
15732
+ /** `[pane].listWidth` when it is set, else the flat `paneListWidth` (deprecated). */
15733
+ paneListWidth = computed(() => this.paneCfg().listWidth ?? this.paneListWidthFlat(), /* @ts-ignore */
15734
+ ...(ngDevMode ? [{ debugName: "paneListWidth" }] : /* istanbul ignore next */ []));
15735
+ /** `[pane].takeoverWidth` when it is set, else the flat `paneTakeoverWidth` (deprecated). */
15736
+ paneTakeoverWidth = computed(() => this.paneCfg().takeoverWidth ?? this.paneTakeoverWidthFlat(), /* @ts-ignore */
15737
+ ...(ngDevMode ? [{ debugName: "paneTakeoverWidth" }] : /* istanbul ignore next */ []));
15738
+ /** `[state].sync` when it is set, else the flat `sync` (deprecated). */
15739
+ sync = computed(() => this.stateCfg().sync ?? this.syncFlat(), /* @ts-ignore */
15740
+ ...(ngDevMode ? [{ debugName: "sync" }] : /* istanbul ignore next */ []));
15741
+ /** `[selection].mode` when it is set, else the flat `selectionMode` (deprecated). */
15742
+ selectionMode = computed(() => this.selectionCfg().mode ?? this.selectionModeFlat(), /* @ts-ignore */
15743
+ ...(ngDevMode ? [{ debugName: "selectionMode" }] : /* istanbul ignore next */ []));
15744
+ /** `[selection].rowSelectable` when it is set, else the flat `rowSelectable` (deprecated). */
15745
+ rowSelectable = computed(() => this.selectionCfg().rowSelectable ?? this.rowSelectableFlat(), /* @ts-ignore */
15746
+ ...(ngDevMode ? [{ debugName: "rowSelectable" }] : /* istanbul ignore next */ []));
15747
+ /** `[paging].hasMore` when it is set, else the flat `hasMore` (deprecated). */
15748
+ hasMore = computed(() => this.pagingCfg().hasMore ?? this.hasMoreFlat(), /* @ts-ignore */
15749
+ ...(ngDevMode ? [{ debugName: "hasMore" }] : /* istanbul ignore next */ []));
15750
+ /** `[paging].loadingMore` when it is set, else the flat `loadingMore` (deprecated). */
15751
+ loadingMore = computed(() => this.pagingCfg().loadingMore ?? this.loadingMoreFlat(), /* @ts-ignore */
15752
+ ...(ngDevMode ? [{ debugName: "loadingMore" }] : /* istanbul ignore next */ []));
15753
+ /** `[paging].moreTotal` when it is set, else the flat `moreTotal` (deprecated). */
15754
+ moreTotal = computed(() => this.pagingCfg().moreTotal ?? this.moreTotalFlat(), /* @ts-ignore */
15755
+ ...(ngDevMode ? [{ debugName: "moreTotal" }] : /* istanbul ignore next */ []));
15756
+ /** `[selection].groupSelect` when it is set, else the flat `groupSelect` (deprecated). */
15757
+ groupSelect = computed(() => this.selectionCfg().groupSelect ?? this.groupSelectFlat(), /* @ts-ignore */
15758
+ ...(ngDevMode ? [{ debugName: "groupSelect" }] : /* istanbul ignore next */ []));
15759
+ /** `[selection].initial` when it is set, else the flat `initialSelection` (deprecated). */
15760
+ initialSelection = computed(() => this.selectionCfg().initial ?? this.initialSelectionFlat(), /* @ts-ignore */
15761
+ ...(ngDevMode ? [{ debugName: "initialSelection" }] : /* istanbul ignore next */ []));
15762
+ /** `[virtual].viewportHeight` when it is set, else the flat `viewportHeight` (deprecated). */
15763
+ viewportHeight = computed(() => this.virtualCfg().viewportHeight ?? this.viewportHeightFlat(), /* @ts-ignore */
15764
+ ...(ngDevMode ? [{ debugName: "viewportHeight" }] : /* istanbul ignore next */ []));
15765
+ /** `[virtual].maxHeight` when it is set, else the flat `maxHeight` (deprecated). */
15766
+ maxHeight = computed(() => this.virtualCfg().maxHeight ?? this.maxHeightFlat(), /* @ts-ignore */
15767
+ ...(ngDevMode ? [{ debugName: "maxHeight" }] : /* istanbul ignore next */ []));
15768
+ /** `[virtual].rowHeight` when it is set, else the flat `rowHeight` (deprecated). */
15769
+ rowHeight = computed(() => this.virtualCfg().rowHeight ?? this.rowHeightFlat(), /* @ts-ignore */
15770
+ ...(ngDevMode ? [{ debugName: "rowHeight" }] : /* istanbul ignore next */ []));
15771
+ /** `[paging].lazy` when it is set, else the flat `lazy` (deprecated). */
15772
+ lazy = computed(() => this.pagingCfg().lazy ?? this.lazyFlat(), /* @ts-ignore */
15773
+ ...(ngDevMode ? [{ debugName: "lazy" }] : /* istanbul ignore next */ []));
15774
+ /** `[paging].total` when it is set, else the flat `total` (deprecated). */
15775
+ total = computed(() => this.pagingCfg().total ?? this.totalFlat(), /* @ts-ignore */
15776
+ ...(ngDevMode ? [{ debugName: "total" }] : /* istanbul ignore next */ []));
15777
+ /** `[state].key` when it is set, else the flat `stateKey` (deprecated). */
15778
+ stateKey = computed(() => this.stateCfg().key ?? this.stateKeyFlat(), /* @ts-ignore */
15779
+ ...(ngDevMode ? [{ debugName: "stateKey" }] : /* istanbul ignore next */ []));
15780
+ /** `[grouping].by` when it is set, else the flat `groupBy` (deprecated). */
15781
+ groupBy = computed(() => this.groupingCfg().by ?? this.groupByFlat(), /* @ts-ignore */
15782
+ ...(ngDevMode ? [{ debugName: "groupBy" }] : /* istanbul ignore next */ []));
15783
+ /** `[grouping].groups` when it is set, else the flat `groups` (deprecated). */
15784
+ groups = computed(() => this.groupingCfg().groups ?? this.groupsFlat(), /* @ts-ignore */
15785
+ ...(ngDevMode ? [{ debugName: "groups" }] : /* istanbul ignore next */ []));
15786
+ /** `[grouping].showEmpty` when it is set, else the flat `showEmptyGroups` (deprecated). */
15787
+ showEmptyGroups = computed(() => this.groupingCfg().showEmpty ?? this.showEmptyGroupsFlat(), /* @ts-ignore */
15788
+ ...(ngDevMode ? [{ debugName: "showEmptyGroups" }] : /* istanbul ignore next */ []));
15789
+ /** `[filtering].fields` when it is set, else the flat `quickFilterFields` (deprecated). */
15790
+ quickFilterFields = computed(() => this.filteringCfg().fields ?? this.quickFilterFieldsFlat(), /* @ts-ignore */
15791
+ ...(ngDevMode ? [{ debugName: "quickFilterFields" }] : /* istanbul ignore next */ []));
15792
+ /** `[filtering].quick` when it is set, else the flat `quickFilterable` (deprecated). */
15793
+ quickFilterable = computed(() => this.filteringCfg().quick ?? this.quickFilterableFlat(), /* @ts-ignore */
15794
+ ...(ngDevMode ? [{ debugName: "quickFilterable" }] : /* istanbul ignore next */ []));
15795
+ /** `[filtering].align` when it is set, else the flat `quickFilterAlign` (deprecated). */
15796
+ quickFilterAlign = computed(() => this.filteringCfg().align ?? this.quickFilterAlignFlat(), /* @ts-ignore */
15797
+ ...(ngDevMode ? [{ debugName: "quickFilterAlign" }] : /* istanbul ignore next */ []));
15798
+ /** `[grouping].childrenKey` when it is set, else the flat `childrenKey` (deprecated). */
15799
+ childrenKey = computed(() => this.groupingCfg().childrenKey ?? this.childrenKeyFlat(), /* @ts-ignore */
15800
+ ...(ngDevMode ? [{ debugName: "childrenKey" }] : /* istanbul ignore next */ []));
15801
+ /** `[selection].column` when it is set, else the flat `selectionColumn` (deprecated). */
15802
+ selectionColumn = computed(() => this.selectionCfg().column ?? this.selectionColumnFlat(), /* @ts-ignore */
15803
+ ...(ngDevMode ? [{ debugName: "selectionColumn" }] : /* istanbul ignore next */ []));
15604
15804
  menu = inject(StrctMenuService);
15605
15805
  detailDef = contentChild(StrctRowDetailDef, /* @ts-ignore */
15606
15806
  ...(ngDevMode ? [{ debugName: "detailDef" }] : /* istanbul ignore next */ []));
@@ -17108,6 +17308,12 @@ class StrctDatagrid {
17108
17308
  this.lazyLoad.emit(state);
17109
17309
  });
17110
17310
  });
17311
+ // The inputs are bound by the first render, so this is where they can be
17312
+ // read and reported.
17313
+ afterNextRender(() => {
17314
+ if (typeof ngDevMode !== 'undefined' && ngDevMode)
17315
+ this.reportFlatInputs();
17316
+ });
17111
17317
  // Below `paneTakeoverWidth` the pane replaces the list. The grid's own
17112
17318
  // width decides, not the viewport: the same grid is wide in a page and
17113
17319
  // narrow in a drawer.
@@ -17612,796 +17818,7 @@ class StrctDatagrid {
17612
17818
  return String(a ?? '').localeCompare(String(b ?? ''), undefined, { numeric: true });
17613
17819
  }
17614
17820
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: StrctDatagrid, deps: [], target: i0.ɵɵFactoryTarget.Component });
17615
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.4", type: StrctDatagrid, isStandalone: true, selector: "strct-datagrid", inputs: { columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: true, transformFunction: null }, rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: true, transformFunction: null }, pageSize: { classPropertyName: "pageSize", publicName: "pageSize", isSignal: true, isRequired: false, transformFunction: null }, selectable: { classPropertyName: "selectable", publicName: "selectable", isSignal: true, isRequired: false, transformFunction: null }, expandable: { classPropertyName: "expandable", publicName: "expandable", isSignal: true, isRequired: false, transformFunction: null }, detailPane: { classPropertyName: "detailPane", publicName: "detailPane", isSignal: true, isRequired: false, transformFunction: null }, paneListWidth: { classPropertyName: "paneListWidth", publicName: "paneListWidth", isSignal: true, isRequired: false, transformFunction: null }, paneTakeoverWidth: { classPropertyName: "paneTakeoverWidth", publicName: "paneTakeoverWidth", isSignal: true, isRequired: false, transformFunction: null }, activeRowId: { classPropertyName: "activeRowId", publicName: "activeRowId", isSignal: true, isRequired: false, transformFunction: null }, compact: { classPropertyName: "compact", publicName: "compact", isSignal: true, isRequired: false, transformFunction: null }, singleLine: { classPropertyName: "singleLine", publicName: "singleLine", isSignal: true, isRequired: false, transformFunction: null }, emptyText: { classPropertyName: "emptyText", publicName: "emptyText", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, resizable: { classPropertyName: "resizable", publicName: "resizable", isSignal: true, isRequired: false, transformFunction: null }, columnChooser: { classPropertyName: "columnChooser", publicName: "columnChooser", isSignal: true, isRequired: false, transformFunction: null }, sync: { classPropertyName: "sync", publicName: "sync", isSignal: true, isRequired: false, transformFunction: null }, footerActionsDisabled: { classPropertyName: "footerActionsDisabled", publicName: "footerActionsDisabled", isSignal: true, isRequired: false, transformFunction: null }, rowId: { classPropertyName: "rowId", publicName: "rowId", isSignal: true, isRequired: false, transformFunction: null }, selectionMode: { classPropertyName: "selectionMode", publicName: "selectionMode", isSignal: true, isRequired: false, transformFunction: null }, selectedId: { classPropertyName: "selectedId", publicName: "selectedId", isSignal: true, isRequired: false, transformFunction: null }, rowSelectable: { classPropertyName: "rowSelectable", publicName: "rowSelectable", isSignal: true, isRequired: false, transformFunction: null }, flush: { classPropertyName: "flush", publicName: "flush", isSignal: true, isRequired: false, transformFunction: null }, caption: { classPropertyName: "caption", publicName: "caption", isSignal: true, isRequired: false, transformFunction: null }, paging: { classPropertyName: "paging", publicName: "paging", isSignal: true, isRequired: false, transformFunction: null }, hasMore: { classPropertyName: "hasMore", publicName: "hasMore", isSignal: true, isRequired: false, transformFunction: null }, loadingMore: { classPropertyName: "loadingMore", publicName: "loadingMore", isSignal: true, isRequired: false, transformFunction: null }, moreTotal: { classPropertyName: "moreTotal", publicName: "moreTotal", isSignal: true, isRequired: false, transformFunction: null }, groupSelect: { classPropertyName: "groupSelect", publicName: "groupSelect", isSignal: true, isRequired: false, transformFunction: null }, rowActions: { classPropertyName: "rowActions", publicName: "rowActions", isSignal: true, isRequired: false, transformFunction: null }, initialSelection: { classPropertyName: "initialSelection", publicName: "initialSelection", isSignal: true, isRequired: false, transformFunction: null }, selectedIds: { classPropertyName: "selectedIds", publicName: "selectedIds", isSignal: true, isRequired: false, transformFunction: null }, labels: { classPropertyName: "labels", publicName: "labels", isSignal: true, isRequired: false, transformFunction: null }, virtual: { classPropertyName: "virtual", publicName: "virtual", isSignal: true, isRequired: false, transformFunction: null }, viewportHeight: { classPropertyName: "viewportHeight", publicName: "viewportHeight", isSignal: true, isRequired: false, transformFunction: null }, maxHeight: { classPropertyName: "maxHeight", publicName: "maxHeight", isSignal: true, isRequired: false, transformFunction: null }, editHint: { classPropertyName: "editHint", publicName: "editHint", isSignal: true, isRequired: false, transformFunction: null }, rowHeight: { classPropertyName: "rowHeight", publicName: "rowHeight", isSignal: true, isRequired: false, transformFunction: null }, lazy: { classPropertyName: "lazy", publicName: "lazy", isSignal: true, isRequired: false, transformFunction: null }, total: { classPropertyName: "total", publicName: "total", isSignal: true, isRequired: false, transformFunction: null }, stateKey: { classPropertyName: "stateKey", publicName: "stateKey", isSignal: true, isRequired: false, transformFunction: null }, columnState: { classPropertyName: "columnState", publicName: "columnState", isSignal: true, isRequired: false, transformFunction: null }, reorderable: { classPropertyName: "reorderable", publicName: "reorderable", isSignal: true, isRequired: false, transformFunction: null }, groupBy: { classPropertyName: "groupBy", publicName: "groupBy", isSignal: true, isRequired: false, transformFunction: null }, groups: { classPropertyName: "groups", publicName: "groups", isSignal: true, isRequired: false, transformFunction: null }, showEmptyGroups: { classPropertyName: "showEmptyGroups", publicName: "showEmptyGroups", isSignal: true, isRequired: false, transformFunction: null }, collapsedGroups: { classPropertyName: "collapsedGroups", publicName: "collapsedGroups", isSignal: true, isRequired: false, transformFunction: null }, filters: { classPropertyName: "filters", publicName: "filters", isSignal: true, isRequired: false, transformFunction: null }, quickFilter: { classPropertyName: "quickFilter", publicName: "quickFilter", isSignal: true, isRequired: false, transformFunction: null }, quickFilterFields: { classPropertyName: "quickFilterFields", publicName: "quickFilterFields", isSignal: true, isRequired: false, transformFunction: null }, quickFilterable: { classPropertyName: "quickFilterable", publicName: "quickFilterable", isSignal: true, isRequired: false, transformFunction: null }, quickFilterAlign: { classPropertyName: "quickFilterAlign", publicName: "quickFilterAlign", isSignal: true, isRequired: false, transformFunction: null }, childrenKey: { classPropertyName: "childrenKey", publicName: "childrenKey", isSignal: true, isRequired: false, transformFunction: null }, interactionMode: { classPropertyName: "interactionMode", publicName: "interactionMode", isSignal: true, isRequired: false, transformFunction: null }, selectionColumn: { classPropertyName: "selectionColumn", publicName: "selectionColumn", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { activeRowId: "activeRowIdChange", selectedId: "selectedIdChange", loadMore: "loadMore", selectedIds: "selectedIdsChange", columnState: "columnStateChange", collapsedGroups: "collapsedGroupsChange", filters: "filtersChange", quickFilter: "quickFilterChange", rowActivate: "rowActivate", selectionChange: "selectionChange", syncChange: "syncChange", rowAction: "rowAction", lazyLoad: "lazyLoad", cellEdit: "cellEdit" }, host: { listeners: { "document:click": "onDocClickOverlays($event)", "document:keydown.escape": "onEscapeOverlays($event)" }, properties: { "attr.aria-busy": "loading() || loadingMore() ? \"true\" : null", "class.strct-dg-host--compact": "compact()", "class.strct-dg-host--singleline": "singleLine()", "class.strct-dg-host--virtual": "virtual()", "class.strct-dg-host--bounded": "!virtual() && maxHeight() !== null", "class.strct-dg-host--sticky": "stickyActive()", "class.strct-dg-host--flush": "flush()" }, classAttribute: "strct-dg-host" }, queries: [{ propertyName: "detailDef", first: true, predicate: StrctRowDetailDef, descendants: true, isSignal: true }, { propertyName: "groupHeaderDef", first: true, predicate: StrctGroupHeaderDef, descendants: true, isSignal: true }, { propertyName: "paneTitleDef", first: true, predicate: StrctDetailPaneTitle, descendants: true, isSignal: true }, { propertyName: "actionBarDef", first: true, predicate: StrctDatagridActionBar, descendants: true, isSignal: true }, { propertyName: "actionBarCaptionDef", first: true, predicate: StrctDatagridActionBarCaption, descendants: true, isSignal: true }, { propertyName: "cellDefs", predicate: StrctCellDef, isSignal: true }], ngImport: i0, template: `
17616
- <!-- One polite region for everything the grid does to itself: sorting,
17617
- paging and filtering all change the rows without saying so. -->
17618
- <span class="strct-dg__announcer" role="status" aria-live="polite">{{ announcement() }}</span>
17619
- @if (actionBarDef() || quickFilterable() || actionBarCaptionDef()) {
17620
- <div class="strct-dg__toolbar">
17621
- @if (quickFilterable()) {
17622
- <strct-searchbox
17623
- class="strct-dg__quickfilter"
17624
- [class.strct-dg__quickfilter--end]="quickFilterAlign() === 'end'"
17625
- [placeholder]="L().quickFilter"
17626
- [ariaLabel]="L().quickFilter"
17627
- [value]="quickFilter()"
17628
- (valueChange)="quickFilter.set($event)"
17629
- />
17630
- @if (filterNote(); as note) {
17631
- <span
17632
- class="strct-dg__filternote"
17633
- [class.strct-dg__filternote--end]="quickFilterAlign() === 'end'"
17634
- >{{ note }}</span
17635
- >
17636
- }
17637
- }
17638
- <span class="strct-dg__actioncaption">
17639
- <ng-content select="[strctDatagridActionBarCaption]" />
17640
- </span>
17641
- <ng-content select="[strctDatagridActionBar]" />
17642
- </div>
17643
- }
17644
-
17645
- @if (hasEditableColumn()) {
17646
- <!-- What the editable cells point at with aria-describedby: a keyboard
17647
- user is told the cell can be edited, and how. -->
17648
- <!-- In flow, not absolutely positioned: an out-of-flow box whose
17649
- containing block is the page extends the document's scrollable area
17650
- from wherever the grid happens to sit. 1x1 and clipped is enough. -->
17651
- <span
17652
- class="strct-sr-only"
17653
- style="display: inline-block; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap"
17654
- [id]="editHintId"
17655
- >{{ L().editCellInstructions }}</span
17656
- >
17657
- }
17658
-
17659
- @if (bulkHint(); as hint) {
17660
- <!-- Selecting the page is not selecting the set. Say which one happened,
17661
- and offer the other. -->
17662
- <div class="strct-dg__bulk" role="status">
17663
- <span>{{ hint.text }}</span>
17664
- <button strct-button variant="flat" size="sm" (click)="hint.run()">
17665
- {{ hint.action }}
17666
- </button>
17667
- </div>
17668
- }
17669
- <div
17670
- class="strct-dg__layout"
17671
- [class.strct-dg__layout--paned]="paneOpen()"
17672
- [class.strct-dg__layout--panetakeover]="paneOpen() && narrowPane()"
17673
- [style.--strct-dg-pane-list-w]="paneListWidth()"
17674
- >
17675
- <div
17676
- class="strct-dg__scroll"
17677
- tabindex="0"
17678
- role="region"
17679
- [attr.aria-label]="L().rows"
17680
- [style.max-height.px]="virtual() ? viewportHeight() : maxHeight()"
17681
- (scroll)="virtual() && onScroll($event)"
17682
- >
17683
- <table
17684
- class="strct-dg"
17685
- [attr.role]="childrenKey() ? 'treegrid' : gridMode() ? 'grid' : null"
17686
- [attr.aria-rowcount]="ariaRowCount()"
17687
- [attr.aria-labelledby]="caption() ? captionId : null"
17688
- >
17689
- @if (caption()) {
17690
- <caption class="strct-dg__caption" [id]="captionId">
17691
- {{
17692
- caption()
17693
- }}
17694
- </caption>
17695
- }
17696
- <thead>
17697
- <tr>
17698
- <!-- A utility column is still a column: axe reports every empty
17699
- header, and a screen-reader user hears nothing where the
17700
- detail, expand and select controls are. The name is hidden,
17701
- not absent. -->
17702
- @if (canDetail()) {
17703
- <th
17704
- class="strct-dg__expandcol"
17705
- scope="col"
17706
- [class.strct-dg__cell--sticky]="stickyActive()"
17707
- [style.insetInlineStart.px]="utilLeft('detail')"
17708
- >
17709
- <span class="strct-sr-only">{{ L().detailColumn }}</span>
17710
- </th>
17711
- }
17712
- @if (canExpand()) {
17713
- <th
17714
- class="strct-dg__expandcol"
17715
- scope="col"
17716
- [class.strct-dg__cell--sticky]="stickyActive()"
17717
- [style.insetInlineStart.px]="utilLeft('expand')"
17718
- >
17719
- <span class="strct-sr-only">{{ L().expandColumn }}</span>
17720
- </th>
17721
- }
17722
- @if (hasSelectionColumn()) {
17723
- <th
17724
- class="strct-dg__sel"
17725
- scope="col"
17726
- [class.strct-dg__cell--sticky]="stickyActive()"
17727
- [style.insetInlineStart.px]="utilLeft('sel')"
17728
- >
17729
- @if (mode() === 'multiple') {
17730
- <strct-checkbox
17731
- [ariaLabel]="L().selectAll"
17732
- [checked]="allPageSelected()"
17733
- [indeterminate]="somePageSelected()"
17734
- (checkedChange)="toggleAll()"
17735
- />
17736
- } @else {
17737
- <span class="strct-sr-only">{{ L().selectColumn }}</span>
17738
- }
17739
- </th>
17740
- }
17741
- @for (col of visibleColumns(); track col.key) {
17742
- <th
17743
- scope="col"
17744
- [style.text-align]="col.align ?? (col.numeric ? 'end' : 'start')"
17745
- [style.width]="colWidth(col.key) ?? col.width ?? null"
17746
- [class.strct-dg__th--sortable]="col.sortable"
17747
- [class.strct-dg__th--drop]="col.key === dropKey()"
17748
- [class.strct-dg__cell--sticky]="isSticky(col)"
17749
- [class.strct-dg__cell--sticky-last]="col.key === lastStickyKey()"
17750
- [style.insetInlineStart.px]="stickyLeft(col.key)"
17751
- [attr.data-col]="col.key"
17752
- [attr.aria-sort]="col.sortable ? ariaSort(col.key) : null"
17753
- [attr.draggable]="reorderable() ? true : null"
17754
- (dragstart)="onColDragStart(col.key, $event)"
17755
- (dragover)="onColDragOver(col.key, $event)"
17756
- (drop)="onColDrop(col.key)"
17757
- (dragend)="onColDragEnd()"
17758
- >
17759
- <span class="strct-dg__hd">
17760
- <!-- A header that sorts is a button: a th with tabindex is
17761
- read as "column header, focusable" and never says it
17762
- can be activated. aria-sort stays on the th, which is
17763
- what carries the column's sorted state. -->
17764
- @if (col.sortable) {
17765
- <!-- The button covers most of the header, so it carries
17766
- the drag source too: a reorderable column has to be
17767
- grabbable where the pointer actually lands. The
17768
- event bubbles to the th's own handlers. -->
17769
- <button
17770
- type="button"
17771
- class="strct-dg__sortbtn"
17772
- [attr.draggable]="reorderable() ? true : null"
17773
- (click)="sortBy(col.key)"
17774
- >
17775
- {{ col.label }}
17776
- <strct-icon
17777
- class="strct-dg__sorticon"
17778
- [name]="sortIcon(col.key)"
17779
- [size]="13"
17780
- />
17781
- </button>
17782
- } @else {
17783
- {{ col.label }}
17784
- }
17785
- @if (col.filterable || col.filterOptions) {
17786
- <button
17787
- #fbtn
17788
- type="button"
17789
- class="strct-dg__filterbtn"
17790
- [class.strct-dg__filterbtn--active]="hasFilter(col.key)"
17791
- [attr.aria-expanded]="filterOpenKey() === col.key"
17792
- [attr.aria-label]="L().filterColumn + ': ' + col.label"
17793
- (click)="toggleFilterPanel(col.key, $event)"
17794
- (keydown.enter)="$event.stopPropagation()"
17795
- (keydown.space)="$event.stopPropagation()"
17796
- >
17797
- <strct-icon name="filter" [size]="12" />
17798
- </button>
17799
- @if (filterOpenKey() === col.key) {
17800
- <div
17801
- class="strct-dg__filterpanel"
17802
- role="dialog"
17803
- [attr.aria-label]="L().filterColumn + ': ' + col.label"
17804
- [strctOverlay]="fbtn"
17805
- strctOverlayPlacement="bottom-start"
17806
- (click)="$event.stopPropagation()"
17807
- (keydown.enter)="$event.stopPropagation()"
17808
- (keydown.space)="$event.stopPropagation()"
17809
- >
17810
- @if (col.filterOptions; as opts) {
17811
- @for (opt of opts; track $index) {
17812
- <label class="strct-dg__filteropt">
17813
- <input
17814
- type="checkbox"
17815
- [checked]="optionChecked(col.key, opt)"
17816
- (change)="toggleFilterOption(col.key, opt)"
17817
- />
17818
- <span>{{ opt }}</span>
17819
- </label>
17820
- }
17821
- } @else {
17822
- <input
17823
- class="strct-dg__filterinput"
17824
- type="text"
17825
- [attr.placeholder]="L().filterPlaceholder"
17826
- [attr.aria-label]="L().filterColumn + ': ' + col.label"
17827
- [value]="textFilter(col.key)"
17828
- (input)="setTextFilter(col.key, $any($event.target).value)"
17829
- />
17830
- }
17831
- <button
17832
- type="button"
17833
- class="strct-dg__filterclear"
17834
- [disabled]="!hasFilter(col.key)"
17835
- (click)="clearFilter(col.key)"
17836
- >
17837
- {{ L().clearFilter }}
17838
- </button>
17839
- </div>
17840
- }
17841
- }
17842
- </span>
17843
- @if (resizable() && col.resizable !== false) {
17844
- <!-- A separator, not a bare div: a column could only be
17845
- resized with a mouse, which is the one input a
17846
- console cannot assume. -->
17847
- <div
17848
- class="strct-dg__resize"
17849
- role="separator"
17850
- aria-orientation="vertical"
17851
- tabindex="0"
17852
- [attr.aria-label]="L().resizeColumn(col.label)"
17853
- [attr.aria-valuenow]="separatorValue(col.key)"
17854
- [attr.aria-valuemin]="40"
17855
- (mousedown)="onResizeStart($event, col.key)"
17856
- (keydown)="onResizeKey($event, col.key)"
17857
- (dblclick)="resetWidth(col.key)"
17858
- ></div>
17859
- }
17860
- </th>
17861
- }
17862
- @if (canActions()) {
17863
- <th class="strct-dg__actioncol" scope="col" [attr.aria-label]="L().actions"></th>
17864
- }
17865
- </tr>
17866
- </thead>
17867
- <!--
17868
- focusin is the real keyboard path (focus does not bubble from cells);
17869
- the plain focus binding exists to satisfy mouse-events-have-key-events.
17870
- -->
17871
- <tbody
17872
- (mouseover)="revealIfClipped($event)"
17873
- (focusin)="revealIfClipped($event)"
17874
- (focus)="revealIfClipped($event)"
17875
- >
17876
- @if (loading()) {
17877
- @for (_ of [1, 2, 3, 4, 5]; track $index) {
17878
- <tr class="strct-dg__skeleton-row">
17879
- @if (canDetail()) {
17880
- <td class="strct-dg__expandcell"></td>
17881
- }
17882
- @if (canExpand()) {
17883
- <td class="strct-dg__expandcell"></td>
17884
- }
17885
- @if (hasSelectionColumn()) {
17886
- <td class="strct-dg__sel"></td>
17887
- }
17888
- @for (col of visibleColumns(); track col.key) {
17889
- <td [style.text-align]="col.align ?? 'start'">
17890
- <div class="strct-dg__skeleton-block"></div>
17891
- </td>
17892
- }
17893
- @if (canActions()) {
17894
- <td class="strct-dg__actioncell"></td>
17895
- }
17896
- </tr>
17897
- }
17898
- } @else {
17899
- @if (virtual() && topPad() > 0) {
17900
- <tr class="strct-dg__vspacer" aria-hidden="true">
17901
- <td [attr.colspan]="colspan()" [style.height.px]="topPad()"></td>
17902
- </tr>
17903
- }
17904
- @for (it of displayItems(); track itemKey(it)) {
17905
- @if (it.group; as grp) {
17906
- <!-- Group header: distinct value + count, collapsible. -->
17907
- <tr class="strct-dg__grouprow">
17908
- @if (groupSelect() && mode() === 'multiple') {
17909
- <td class="strct-dg__sel">
17910
- <strct-checkbox
17911
- [ariaLabel]="L().selectAllInGroup(grp.label)"
17912
- [checked]="groupAllSelected(grp.key)"
17913
- [indeterminate]="groupSomeSelected(grp.key)"
17914
- (checkedChange)="toggleGroupSelection(grp.key)"
17915
- />
17916
- </td>
17917
- }
17918
- <td
17919
- [attr.colspan]="
17920
- groupSelect() && mode() === 'multiple' ? colspan() - 1 : colspan()
17921
- "
17922
- >
17923
- <button
17924
- type="button"
17925
- class="strct-dg__groupbtn"
17926
- [attr.aria-expanded]="!grp.collapsed"
17927
- (click)="toggleGroup(grp.key)"
17928
- >
17929
- <span
17930
- class="strct-dg__groupchev"
17931
- [class.strct-dg__groupchev--open]="!grp.collapsed"
17932
- >
17933
- <strct-icon name="chevronRight" [size]="12" [strokeWidth]="1.7" />
17934
- </span>
17935
- @if (groupHeaderDef(); as tpl) {
17936
- <!-- The consumer draws the row; the button, its
17937
- aria-expanded and the keyboard stay ours. -->
17938
- <ng-container
17939
- [ngTemplateOutlet]="tpl.template"
17940
- [ngTemplateOutletContext]="{
17941
- $implicit: grp,
17942
- group: grp,
17943
- count: grp.count,
17944
- }"
17945
- />
17946
- } @else {
17947
- <span
17948
- class="strct-dg__grouplabel strct-dg__grouplabel--{{
17949
- grp.status ?? 'none'
17950
- }}"
17951
- >{{ grp.label }}</span
17952
- >
17953
- <!-- The three spans run together in the button's
17954
- name ("Needs your decision1Blocks the join…");
17955
- the separators are read, not seen. -->
17956
- <span class="strct-dg__announcer">, </span>
17957
- <span
17958
- class="strct-dg__groupcount strct-dg__groupcount--{{
17959
- grp.status ?? 'none'
17960
- }}"
17961
- >{{ grp.count }}</span
17962
- >
17963
- <!-- Seen: the chip's number. Heard: the number with
17964
- the grid's noun, because "Inconclusive, 3" does
17965
- not say three of what. -->
17966
- <span class="strct-dg__announcer"> {{ countNoun(grp.count) }}</span>
17967
- @if (grp.note) {
17968
- <span class="strct-dg__announcer">. </span>
17969
- <span class="strct-dg__groupnote">{{ grp.note }}</span>
17970
- }
17971
- }
17972
- </button>
17973
- </td>
17974
- </tr>
17975
- } @else {
17976
- @let row = it.row!;
17977
- <tr
17978
- [attr.aria-rowindex]="ariaRowIndex(row)"
17979
- [class.strct-dg__row--selected]="isSelected(row)"
17980
- [class.strct-dg__row--locked]="lockedReason(row) !== null"
17981
- [attr.aria-selected]="mode() === 'none' ? null : isSelected(row)"
17982
- [attr.aria-disabled]="lockedReason(row) !== null ? 'true' : null"
17983
- [attr.title]="lockedReason(row) || null"
17984
- (click)="onRowPick(row)"
17985
- [class.strct-dg__row--active]="paneOpen() && row === activeRow()"
17986
- [attr.aria-level]="childrenKey() ? treeDepth(row) + 1 : null"
17987
- [attr.aria-expanded]="
17988
- childrenKey() && treeHasChildren(row) ? treeExpanded(row) : null
17989
- "
17990
- [attr.tabindex]="
17991
- childrenKey()
17992
- ? treeRowTabindex(row)
17993
- : gridMode()
17994
- ? gridRowTabindex(row)
17995
- : canDetail()
17996
- ? paneRowTabindex(row)
17997
- : null
17998
- "
17999
- (keydown)="
18000
- childrenKey()
18001
- ? onTreeRowKeydown(row, $event)
18002
- : gridMode()
18003
- ? onGridRowKeydown(row, $event)
18004
- : onPaneRowKeydown(row, $event)
18005
- "
18006
- (dblclick)="onRowActivate(row)"
18007
- >
18008
- @if (canDetail()) {
18009
- <td
18010
- class="strct-dg__expandcell"
18011
- [class.strct-dg__cell--sticky]="stickyActive()"
18012
- [style.insetInlineStart.px]="utilLeft('detail')"
18013
- >
18014
- <button
18015
- type="button"
18016
- [attr.tabindex]="ctlTabindex()"
18017
- class="strct-dg__detailbtn"
18018
- [class.strct-dg__detailbtn--active]="row === activeRow()"
18019
- [attr.aria-expanded]="row === activeRow()"
18020
- [attr.aria-label]="L().openDetail"
18021
- (click)="openDetail(row)"
18022
- >
18023
- <strct-icon name="chevronDoubleRight" [size]="13" [strokeWidth]="1.6" />
18024
- </button>
18025
- </td>
18026
- }
18027
- @if (canExpand()) {
18028
- <td
18029
- class="strct-dg__expandcell"
18030
- [class.strct-dg__cell--sticky]="stickyActive()"
18031
- [style.insetInlineStart.px]="utilLeft('expand')"
18032
- >
18033
- <button
18034
- type="button"
18035
- [attr.tabindex]="ctlTabindex()"
18036
- class="strct-dg__expandbtn"
18037
- [class.strct-dg__expandbtn--open]="isExpanded(row)"
18038
- [attr.aria-expanded]="isExpanded(row)"
18039
- [attr.aria-label]="L().toggleDetail"
18040
- (click)="toggleExpand(row)"
18041
- >
18042
- <strct-icon name="chevronRight" [size]="12" [strokeWidth]="1.7" />
18043
- </button>
18044
- </td>
18045
- }
18046
- @if (hasSelectionColumn()) {
18047
- <td
18048
- class="strct-dg__sel"
18049
- [class.strct-dg__cell--sticky]="stickyActive()"
18050
- [style.insetInlineStart.px]="utilLeft('sel')"
18051
- >
18052
- @if (mode() === 'single') {
18053
- <!-- A native radio group: one name per grid, so the
18054
- arrow keys move between rows and Space picks,
18055
- without the grid inventing its own keyboarding. -->
18056
- <input
18057
- type="radio"
18058
- class="strct-dg__radio"
18059
- [name]="radioName"
18060
- [attr.aria-label]="selectRowLabel(row)"
18061
- [attr.aria-description]="lockedReason(row) || null"
18062
- [checked]="isSelected(row)"
18063
- [disabled]="lockedReason(row) !== null"
18064
- [attr.tabindex]="ctlTabindex()"
18065
- (change)="selectSingle(row)"
18066
- (click)="$event.stopPropagation()"
18067
- />
18068
- } @else {
18069
- <!-- click fires (and bubbles) before the input's change,
18070
- so the modifier is recorded by the time toggleRow runs. -->
18071
- <strct-checkbox
18072
- [ariaLabel]="selectRowLabel(row)"
18073
- [attr.aria-description]="lockedReason(row) || null"
18074
- [checked]="isSelected(row)"
18075
- [disabled]="lockedReason(row) !== null"
18076
- [tabIndex]="ctlTabindex()"
18077
- (click)="noteRangeIntent($event); $event.stopPropagation()"
18078
- (checkedChange)="toggleRow(row)"
18079
- />
18080
- }
18081
- </td>
18082
- }
18083
- @for (col of visibleColumns(); track col.key; let colIdx = $index) {
18084
- <td
18085
- [style.text-align]="col.align ?? (col.numeric ? 'end' : 'start')"
18086
- [class.strct-dg__cell--mono]="col.mono"
18087
- [class.strct-dg__cell--muted]="col.muted"
18088
- [class.strct-dg__cell--numeric]="col.numeric"
18089
- [class.strct-dg__cell--sticky]="isSticky(col)"
18090
- [class.strct-dg__cell--sticky-last]="col.key === lastStickyKey()"
18091
- [class.strct-dg__cell--editable]="col.editable"
18092
- [style.insetInlineStart.px]="stickyLeft(col.key)"
18093
- [attr.data-edit]="col.editable ? '' : null"
18094
- [attr.tabindex]="col.editable ? editCellTabindex(row, col) : null"
18095
- [attr.aria-describedby]="col.editable ? editHintId : null"
18096
- (dblclick)="col.editable && startEdit(row, col)"
18097
- (focus)="col.editable && onEditCellFocus(row, col)"
18098
- (keydown)="col.editable && onEditCellKeydown(row, col, $event)"
18099
- >
18100
- @if (childrenKey() && colIdx === 0) {
18101
- <span
18102
- class="strct-dg__treepad"
18103
- [style.width.px]="treeDepth(row) * 18"
18104
- aria-hidden="true"
18105
- ></span>
18106
- @if (treeHasChildren(row)) {
18107
- <button
18108
- type="button"
18109
- class="strct-dg__treebtn"
18110
- [class.strct-dg__treebtn--open]="treeExpanded(row)"
18111
- [attr.aria-expanded]="treeExpanded(row)"
18112
- [attr.aria-label]="L().toggleChildren"
18113
- (click)="toggleTreeRow(row)"
18114
- >
18115
- <strct-icon name="chevronRight" [size]="12" [strokeWidth]="1.7" />
18116
- </button>
18117
- } @else {
18118
- <span
18119
- class="strct-dg__treebtn strct-dg__treebtn--leaf"
18120
- aria-hidden="true"
18121
- ></span>
18122
- }
18123
- }
18124
- @if (isEditingCell(row, col)) {
18125
- @switch (col.editor ?? 'text') {
18126
- @case ('select') {
18127
- <!-- Choosing is the commit: there is nothing to
18128
- confirm with Enter once an option is picked. -->
18129
- <strct-select
18130
- class="strct-dg__editselect"
18131
- [options]="editorOptionsFor(row, col)"
18132
- [listLabel]="L().editCell + ': ' + col.label"
18133
- [ngModel]="row[col.key]"
18134
- (ngModelChange)="commitValue(row, col, $event)"
18135
- (keydown.escape)="cancelEdit($event)"
18136
- (click)="$event.stopPropagation()"
18137
- />
18138
- }
18139
- @case ('number') {
18140
- <strct-number
18141
- class="strct-dg__editnumber"
18142
- [ariaLabel]="L().editCell + ': ' + col.label"
18143
- [min]="col.editorMin ?? null"
18144
- [max]="col.editorMax ?? null"
18145
- [step]="col.editorStep ?? 1"
18146
- [ngModel]="row[col.key]"
18147
- (ngModelChange)="editDraft.set($event)"
18148
- (keydown.enter)="commitDraft(row, col, $event)"
18149
- (keydown.escape)="cancelEdit($event)"
18150
- (focusout)="commitOnLeave(row, col, $event)"
18151
- (click)="$event.stopPropagation()"
18152
- />
18153
- }
18154
- @default {
18155
- <input
18156
- class="strct-dg__editinput"
18157
- type="text"
18158
- [value]="row[col.key] ?? ''"
18159
- [attr.aria-label]="L().editCell + ': ' + col.label"
18160
- (keydown.enter)="commitEdit(row, col, $event)"
18161
- (keydown.escape)="cancelEdit($event)"
18162
- (blur)="commitEdit(row, col, $event)"
18163
- (click)="$event.stopPropagation()"
18164
- />
18165
- }
18166
- }
18167
- } @else if (cellTemplate(col.key); as tpl) {
18168
- <ng-container
18169
- [ngTemplateOutlet]="tpl"
18170
- [ngTemplateOutletContext]="{
18171
- $implicit: row,
18172
- row,
18173
- value: row[col.key],
18174
- column: col,
18175
- }"
18176
- />
18177
- } @else if (isBlank(row, col)) {
18178
- <span
18179
- class="strct-dg__empty"
18180
- [attr.aria-label]="col.emptyLabel || null"
18181
- >{{ col.emptyText }}</span
18182
- >
18183
- } @else {
18184
- {{ displayValue(row, col) }}
18185
- @if (col.descriptionKey && row[col.descriptionKey]) {
18186
- <span class="strct-dg__celldesc">{{ row[col.descriptionKey] }}</span>
18187
- }
18188
- }
18189
- <!-- The affordance a mouse never needed: a cell that
18190
- can be edited says so on hover and on focus. -->
18191
- @if (col.editable && editHint() && !isEditingCell(row, col)) {
18192
- <strct-icon
18193
- class="strct-dg__editpencil"
18194
- name="pencil"
18195
- [size]="12"
18196
- [strokeWidth]="1.6"
18197
- aria-hidden="true"
18198
- />
18199
- }
18200
- </td>
18201
- }
18202
- @if (canActions()) {
18203
- <td class="strct-dg__actioncell">
18204
- <button
18205
- type="button"
18206
- [attr.tabindex]="ctlTabindex()"
18207
- class="strct-dg__kebab"
18208
- [attr.aria-label]="L().rowActions"
18209
- (click)="openRowMenu(row, $event)"
18210
- >
18211
- <strct-icon name="dots" [size]="16" />
18212
- </button>
18213
- </td>
18214
- }
18215
- </tr>
18216
- @if (canExpand() && isExpanded(row)) {
18217
- <tr class="strct-dg__detailrow">
18218
- <td [attr.colspan]="colspan()">
18219
- <div class="strct-dg__detail">
18220
- <ng-container
18221
- [ngTemplateOutlet]="detailDef()!.template"
18222
- [ngTemplateOutletContext]="{ $implicit: row, row }"
18223
- />
18224
- </div>
18225
- </td>
18226
- </tr>
18227
- }
18228
- }
18229
- } @empty {
18230
- <!-- "Nothing here" and "nothing matches what you typed" are
18231
- different states: the second one can be undone, and
18232
- saying so is the whole point. -->
18233
- <tr>
18234
- <td class="strct-dg__emptyrow" [attr.colspan]="colspan()">
18235
- @if (filteredAway()) {
18236
- <strct-empty-state
18237
- size="sm"
18238
- [titleLevel]="null"
18239
- [title]="L().noMatches"
18240
- [description]="noMatchesDescription()"
18241
- >
18242
- <button strct-button variant="flat" size="sm" (click)="clearAllFilters()">
18243
- {{ L().clearFilters }}
18244
- </button>
18245
- </strct-empty-state>
18246
- } @else {
18247
- <!-- Not a heading: a grid is a region of a page, and an
18248
- empty one used to put an <h3> in its outline. -->
18249
- <strct-empty-state size="sm" [titleLevel]="null" [title]="emptyText()" />
18250
- }
18251
- </td>
18252
- </tr>
18253
- }
18254
- @if (virtual() && bottomPad() > 0) {
18255
- <tr class="strct-dg__vspacer" aria-hidden="true">
18256
- <td [attr.colspan]="colspan()" [style.height.px]="bottomPad()"></td>
18257
- </tr>
18258
- }
18259
- }
18260
- </tbody>
18261
- </table>
18262
- </div>
18263
-
18264
- @if (paneOpen()) {
18265
- <aside class="strct-dg__pane">
18266
- <div class="strct-dg__pane-head">
18267
- @if (narrowPane()) {
18268
- <!-- The pane has taken the list's place, so there is a way back. -->
18269
- <button
18270
- type="button"
18271
- class="strct-dg__pane-back"
18272
- [attr.aria-label]="L().backToList"
18273
- (click)="closePane()"
18274
- >
18275
- <strct-icon name="chevronLeft" [size]="13" />
18276
- </button>
18277
- }
18278
- <span class="strct-dg__pane-title">
18279
- <ng-content select="[strctDetailPaneTitle]" />
18280
- @if (!paneTitleDef()) {
18281
- {{ activeRow()![columns()[0].key] }}
18282
- }
18283
- </span>
18284
- <span class="strct-dg__pane-meta">
18285
- <ng-content select="[strctDetailPaneMeta]" />
18286
- </span>
18287
- <span class="strct-dg__pane-actions">
18288
- <ng-content select="[strctDetailPaneActions]" />
18289
- </span>
18290
- <button
18291
- type="button"
18292
- class="strct-dg__pane-close"
18293
- [attr.aria-label]="L().closeDetail"
18294
- (click)="closePane()"
18295
- >
18296
- <strct-icon name="close" [size]="13" />
18297
- </button>
18298
- </div>
18299
- <div class="strct-dg__pane-body">
18300
- <ng-container
18301
- [ngTemplateOutlet]="detailDef()!.template"
18302
- [ngTemplateOutletContext]="{ $implicit: activeRow(), row: activeRow() }"
18303
- />
18304
- </div>
18305
- </aside>
18306
- }
18307
- </div>
18308
-
18309
- @if (showFooter()) {
18310
- <div class="strct-dg__foot">
18311
- <div class="strct-dg__foot-left">
18312
- @if (columnChooser() || sync()) {
18313
- <div class="strct-dg__actions">
18314
- <strct-button-group>
18315
- @if (columnChooser()) {
18316
- <button
18317
- strct-button
18318
- size="sm"
18319
- [class.is-open]="chooserOpen()"
18320
- [disabled]="footerActionsDisabled()"
18321
- [attr.aria-label]="L().chooseColumns"
18322
- (click)="toggleChooser($event)"
18323
- >
18324
- <strct-icon name="settings" [size]="16" />
18325
- </button>
18326
- }
18327
- @if (sync()) {
18328
- <button
18329
- strct-button
18330
- size="sm"
18331
- [disabled]="footerActionsDisabled()"
18332
- [attr.aria-label]="L().refresh"
18333
- (click)="syncChange.emit()"
18334
- >
18335
- <strct-icon name="refresh" [size]="16" />
18336
- </button>
18337
- }
18338
- </strct-button-group>
18339
- @if (columnChooser() && chooserOpen()) {
18340
- <div class="strct-dg__chooser-menu">
18341
- @for (col of columns(); track col.key) {
18342
- <!-- The whole row toggles; the checkbox's own click must not
18343
- reach the row or the column would flip twice. -->
18344
- <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->
18345
- <div
18346
- class="strct-dg__chooser-item"
18347
- (click)="toggleColumn(col.key, hiddenColumns().has(col.key))"
18348
- >
18349
- <strct-checkbox
18350
- [checked]="!hiddenColumns().has(col.key)"
18351
- (checkedChange)="toggleColumn(col.key, $event)"
18352
- (click)="$event.stopPropagation()"
18353
- />
18354
- <span>{{ col.label }}</span>
18355
- </div>
18356
- }
18357
- </div>
18358
- }
18359
- </div>
18360
- }
18361
- <span class="strct-dg__count">
18362
- @if (paging() === 'more') {
18363
- {{ L().showing(rows().length, moreTotal() ?? undefined) }}
18364
- } @else {
18365
- {{ totalCount() }} {{ totalCount() === 1 ? L().row : L().rows }}
18366
- }
18367
- @if (selectedCount() && mode() !== 'single') {
18368
- <span class="strct-dg__count-sep">|</span>
18369
- <span class="strct-dg__count-sel">{{ selectedCount() }} {{ L().selected }}</span>
18370
- }
18371
- </span>
18372
- </div>
18373
- <div class="strct-dg__foot-right">
18374
- <!-- A feed that pages by cursor loads more at the end: page numbers
18375
- are something a cursor API cannot answer. It takes the pager's
18376
- place — the column chooser, the sync button and the count are
18377
- the footer's, not the pager's. -->
18378
- @if (paging() === 'more') {
18379
- @if (hasMore()) {
18380
- <button
18381
- strct-button
18382
- size="sm"
18383
- class="strct-dg__more"
18384
- [disabled]="loadingMore()"
18385
- (click)="loadMore.emit()"
18386
- >
18387
- @if (loadingMore()) {
18388
- <strct-spinner size="sm" />
18389
- }
18390
- {{ L().loadMore }}
18391
- </button>
18392
- }
18393
- } @else if (pageSize() > 0 && !groupBy()) {
18394
- <strct-pagination
18395
- [total]="totalCount()"
18396
- [pageSize]="pageSize()"
18397
- [page]="page()"
18398
- (pageChange)="goToPage($event)"
18399
- />
18400
- }
18401
- </div>
18402
- </div>
18403
- }
18404
- `, isInline: true, styles: [".strct-dg__radio{width:15px;height:15px;accent-color:var(--acc);cursor:pointer}.strct-dg__radio:disabled{cursor:not-allowed}.strct-dg__row--locked .strct-dg__sel{opacity:.55}.strct-dg__actioncaption:empty{display:none}.strct-dg__actioncaption{display:inline-flex;align-items:center;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:var(--text-sm);color:var(--t2)}.strct-dg__bulk{display:flex;align-items:center;justify-content:center;flex-wrap:wrap;gap:4px 10px;padding:7px 12px;border-bottom:1px solid var(--b2);background:var(--acc-s);color:var(--t2);font-size:var(--text-md)}.strct-dg__announcer{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}.strct-dg-host{display:block;border:1px solid var(--b2);border-radius:var(--radius-lg);background:var(--bg-2);box-shadow:var(--shadow-rest)}.strct-dg-host--flush{border:0;border-radius:0;background:transparent;box-shadow:none}.strct-dg__more strct-spinner{margin-inline-end:4px}.strct-dg__caption{caption-side:top;text-align:start;padding:var(--space-2) var(--space-3);font-size:var(--text-sm);font-weight:600;color:var(--t2)}.strct-dg td.strct-dg__cell--mono{font-family:var(--mono);font-size:var(--text-sm)}.strct-dg td.strct-dg__cell--muted{color:var(--t2)}.strct-dg td.strct-dg__cell--numeric{font-variant-numeric:tabular-nums}.strct-dg__empty{color:var(--t2)}.strct-dg__celldesc{display:block;font-size:var(--text-sm);color:var(--t2);line-height:1.3}.strct-dg__scroll{flex:1 1 auto;min-width:0;overflow-x:auto;-webkit-overflow-scrolling:touch}.strct-dg-host--virtual .strct-dg__scroll,.strct-dg-host--bounded .strct-dg__scroll{overflow-y:auto}.strct-dg-host--virtual .strct-dg thead th,.strct-dg-host--bounded .strct-dg thead th{position:sticky;top:0;z-index:var(--z-raised)}.strct-dg__vspacer td{padding:0;border:0;background:transparent}.strct-dg th[draggable]{cursor:grab}.strct-dg__th--drop{box-shadow:inset 3px 0 0 var(--acc)}.strct-dg__grouprow td{background:var(--bg-2)!important;padding:0}.strct-dg__groupbtn{display:flex;align-items:center;gap:8px;width:100%;padding:7px 13px;border:0;background:transparent;color:var(--t1);font-family:var(--font);font-size:var(--text-sm);font-weight:600;cursor:pointer;text-align:start}.strct-dg__groupbtn:hover{background:var(--bg-3)}.strct-dg__groupbtn:focus-visible{outline:2px solid var(--acc50);outline-offset:-2px}.strct-dg__groupchev{display:inline-flex;color:var(--t3);transition:transform .15s ease}.strct-dg__groupchev--open{transform:rotate(90deg)}@media(prefers-reduced-motion:reduce){.strct-dg__groupchev{transition:none}}.strct-dg__grouplabel--critical{color:var(--critical)}.strct-dg__grouplabel--warning{color:var(--warning)}.strct-dg__grouplabel--success{color:var(--success)}.strct-dg__grouplabel--accent{color:var(--acc)}.strct-dg__grouplabel--neutral,.strct-dg__grouplabel--none{color:var(--t1)}.strct-dg__groupnote{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:var(--text-sm);font-weight:400;color:var(--t2)}.strct-dg__groupcount{min-width:18px;height:18px;padding:0 5px;display:inline-flex;align-items:center;justify-content:center;font-size:12px;border-radius:4px;background:var(--acc-m);color:var(--acc);font-variant-numeric:tabular-nums}.strct-dg__groupcount--critical{background:var(--critical-bg);color:var(--critical)}.strct-dg__groupcount--warning{background:var(--warning-bg);color:var(--warning)}.strct-dg__groupcount--success{background:var(--success-bg);color:var(--success)}.strct-dg__groupcount--neutral,.strct-dg__groupcount--none{background:var(--bg-3);color:var(--t2)}.strct-dg-host--singleline .strct-dg{border-collapse:separate;border-spacing:0}.strct-dg-host--sticky .strct-dg,.strct-dg-host--virtual .strct-dg,.strct-dg-host--bounded .strct-dg{border-collapse:separate;border-spacing:0;overflow:visible;border-radius:0}.strct-dg-host--sticky .strct-dg{width:max-content;min-width:100%}.strct-dg .strct-dg__cell--sticky{position:sticky;z-index:calc(var(--z-base) + 2)}.strct-dg thead th.strct-dg__cell--sticky{z-index:calc(var(--z-raised) + 1)}.strct-dg .strct-dg__cell--sticky-last:after{content:\"\";position:absolute;top:0;bottom:0;inset-inline-end:-1px;width:7px;pointer-events:none;background:linear-gradient(to right,rgba(0,0,0,.14),transparent)}[dir=rtl] .strct-dg .strct-dg__cell--sticky-last:after{background:linear-gradient(to left,rgba(0,0,0,.14),transparent)}.strct-dg-host--sticky .strct-dg__expandcol,.strct-dg-host--sticky .strct-dg__expandcell{width:36px;min-width:36px;max-width:36px;box-sizing:border-box}.strct-dg-host--sticky .strct-dg__sel{width:40px;min-width:40px;max-width:40px;box-sizing:border-box}.strct-dg-host--sticky tbody .strct-dg__cell--sticky{background:var(--bg-1)}[data-theme=dark] .strct-dg-host--sticky tbody .strct-dg__cell--sticky{background:var(--bg-3)}.strct-dg-host--sticky tbody tr:hover .strct-dg__cell--sticky{background:linear-gradient(var(--acc-s),var(--acc-s)) var(--bg-1)}[data-theme=dark] .strct-dg-host--sticky tbody tr:hover .strct-dg__cell--sticky{background:linear-gradient(var(--acc-s),var(--acc-s)) var(--bg-3)}.strct-dg-host--sticky tbody .strct-dg__row--selected .strct-dg__cell--sticky{background:linear-gradient(var(--acc-m),var(--acc-m)) var(--bg-1)}[data-theme=dark] .strct-dg-host--sticky tbody .strct-dg__row--selected .strct-dg__cell--sticky{background:linear-gradient(var(--acc-m),var(--acc-m)) var(--bg-3)}.strct-dg{width:100%;border-collapse:collapse;font-size:13px;overflow:hidden;background:var(--bg-2)}.strct-dg-host:not(:has(.strct-dg__toolbar)) .strct-dg{border-start-start-radius:4px;border-start-end-radius:4px}.strct-dg-host:not(:has(.strct-dg__foot)) .strct-dg{border-end-start-radius:4px;border-end-end-radius:4px}.strct-dg th,.strct-dg td{padding:9px 13px;text-align:start;border-bottom:1px solid var(--b1)}.strct-dg tbody td{background:var(--bg-1)}[data-theme=dark] .strct-dg tbody td{background:var(--bg-3)}.strct-dg-host--compact .strct-dg th,.strct-dg-host--compact .strct-dg td{padding:5px 11px}.strct-dg-host--singleline .strct-dg tbody tr:not(.strct-dg__detailrow)>td{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:360px}.strct-dg th{position:relative;font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:.4px;color:var(--t2);background:var(--bg-2);white-space:nowrap;-webkit-user-select:none;user-select:none}.strct-dg__th--sortable{cursor:pointer}.strct-dg__th--sortable:hover{color:var(--t1)}.strct-dg__sortbtn{display:inline-flex;align-items:center;gap:inherit;padding:0;border:0;background:none;color:inherit;font:inherit;text-transform:inherit;letter-spacing:inherit;text-align:inherit;cursor:pointer}.strct-dg__sortbtn:focus-visible{outline:2px solid var(--acc50);outline-offset:2px;border-radius:var(--radius-sm)}.strct-dg__th--sortable:focus-visible{outline:2px solid var(--acc50);outline-offset:-2px}.strct-dg__hd{display:inline-flex;align-items:center;gap:5px}.strct-dg__sorticon{color:var(--t3)}.strct-dg__th--sortable:hover .strct-dg__sorticon{color:var(--acc)}.strct-dg__resize{position:absolute;inset-inline-end:0;top:0;bottom:0;width:4px;cursor:col-resize;background:transparent;z-index:calc(var(--z-base) + 1)}.strct-dg__resize:hover{background:var(--acc)}.strct-dg td{color:var(--t1)}.strct-dg tbody tr:last-child td{border-bottom:0}.strct-dg tbody tr:not(.strct-dg__detailrow):hover td{background:linear-gradient(var(--acc-s),var(--acc-s)) var(--bg-1)}[data-theme=dark] .strct-dg tbody tr:not(.strct-dg__detailrow):hover td{background:linear-gradient(var(--acc-s),var(--acc-s)) var(--bg-3)}.strct-dg tbody .strct-dg__row--selected td{background:linear-gradient(var(--acc-m),var(--acc-m)) var(--bg-1)}[data-theme=dark] .strct-dg tbody .strct-dg__row--selected td{background:linear-gradient(var(--acc-m),var(--acc-m)) var(--bg-3)}.strct-dg tbody tr.strct-dg__row--selected:hover td{background:linear-gradient(var(--acc-s),var(--acc-s)),linear-gradient(var(--acc-m),var(--acc-m)) var(--bg-1)}[data-theme=dark] .strct-dg tbody tr.strct-dg__row--selected:hover td{background:linear-gradient(var(--acc-s),var(--acc-s)),linear-gradient(var(--acc-m),var(--acc-m)) var(--bg-3)}.strct-dg__sel{width:1%;white-space:nowrap}.strct-dg__sel .strct-cb,.strct-dg__sel .strct-dg__radio,.strct-dg__expandbtn{position:relative}.strct-dg__sel .strct-cb:after,.strct-dg__sel .strct-dg__radio:after,.strct-dg__expandbtn:after{content:\"\";position:absolute;inset:-11px -13px;min-width:24px;min-height:24px}.strct-dg-host--compact .strct-dg__sel .strct-cb:after,.strct-dg-host--compact .strct-dg__sel .strct-dg__radio:after,.strct-dg-host--compact .strct-dg__expandbtn:after{inset:-7px -11px}.strct-dg__sel input{accent-color:var(--acc);width:15px;height:15px;cursor:pointer}.strct-dg__expandcol,.strct-dg__expandcell{width:1%;white-space:nowrap}.strct-dg__actioncol,.strct-dg__actioncell{width:1%;white-space:nowrap;text-align:end}.strct-dg__kebab{display:inline-flex;padding:4px;border:0;border-radius:2px;background:transparent;color:var(--t3);cursor:pointer;transition:color .14s ease,background .14s ease}.strct-dg__kebab:hover{color:var(--t1);background:var(--bg-3)}.strct-dg__expandbtn{position:relative;display:inline-flex;align-items:center;justify-content:center;padding:3px;border:0;border-radius:2px;background:transparent;color:var(--t3);cursor:pointer;transition:transform .15s ease,color .15s ease}.strct-dg__expandbtn:hover{color:var(--t1);background:var(--bg-3)}.strct-dg__expandbtn--open{transform:rotate(90deg);color:var(--acc)}.strct-dg__detailbtn{display:inline-flex;padding:3px;border:0;border-radius:2px;background:transparent;color:var(--t3);cursor:pointer;transition:color .14s ease,background .14s ease}.strct-dg__detailbtn:hover{color:var(--acc);background:var(--bg-3)}.strct-dg__detailbtn--active{color:var(--acc);background:var(--acc-m)}.strct-dg__detailrow td{background:var(--bg-2);padding:0}.strct-dg__detail{padding:14px 16px;font-size:13px;color:var(--t2)}.strct-dg__layout{display:flex;align-items:flex-start;min-width:0}.strct-dg__layout--paned{gap:0}.strct-dg__layout--paned>.strct-dg__scroll{flex:0 0 auto;width:var(--strct-dg-pane-list-w, 260px);min-width:var(--strct-dg-pane-list-w, 260px);max-width:var(--strct-dg-pane-list-w, 260px)}.strct-dg__layout--paned>.strct-dg__scroll>.strct-dg{width:100%;min-width:0;max-width:none;flex-shrink:0;border-start-end-radius:0;border-end-end-radius:0}.strct-dg__layout--panetakeover>.strct-dg__scroll{display:none}.strct-dg__layout--panetakeover>.strct-dg__pane{flex:1;min-width:0}.strct-dg__pane-meta,.strct-dg__pane-actions{display:inline-flex;align-items:center;gap:var(--space-2)}.strct-dg__pane-meta:empty,.strct-dg__pane-actions:empty{display:none}.strct-dg__pane-actions{margin-inline-start:auto}.strct-dg__pane-back{display:inline-flex;align-items:center;border:0;background:none;color:var(--t2);cursor:pointer;padding:2px;border-radius:var(--radius-sm)}.strct-dg__row--clickable{cursor:pointer}.strct-dg tbody .strct-dg__row--active td{background:linear-gradient(var(--acc-m),var(--acc-m)) var(--bg-1)}[data-theme=dark] .strct-dg tbody .strct-dg__row--active td{background:linear-gradient(var(--acc-m),var(--acc-m)) var(--bg-3)}.strct-dg__layout--paned .strct-dg__row--active td:last-child{position:relative;padding-inline-end:26px}.strct-dg__layout--paned .strct-dg__row--active td:last-child:after{content:\"\";position:absolute;inset-inline-end:11px;top:50%;width:6px;height:6px;border-top:1.6px solid var(--acc);border-inline-end:1.6px solid var(--acc);transform:translateY(-50%) rotate(45deg)}.strct-dg__pane{flex:1;min-width:0;align-self:stretch;background:var(--bg-1);border:1px solid var(--b2);border-inline-start:2px solid var(--acc);border-radius:4px;border-start-start-radius:0;border-end-start-radius:0;overflow:hidden;animation:strct-dg-pane-in .14s ease}.strct-dg__pane-head{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:11px 14px;border-bottom:1px solid var(--b1);font-size:13px;font-weight:600;color:var(--t1)}.strct-dg__pane-close{display:inline-flex;padding:3px;border:0;border-radius:2px;background:transparent;color:var(--t3);cursor:pointer}.strct-dg__pane-close:hover{color:var(--t1);background:var(--bg-3)}.strct-dg__pane-body{padding:14px 16px;font-size:13px;color:var(--t2)}@keyframes strct-dg-pane-in{0%{opacity:0;transform:translate(8px)}}@keyframes strct-skeleton-pulse{0%,to{opacity:.4}50%{opacity:.7}}.strct-dg__skeleton-block{height:12px;border-radius:var(--radius-sm);background:linear-gradient(90deg,var(--bg-3) 25%,var(--skeleton-hi, var(--acc18)) 50%,var(--bg-3) 75%);background-size:200% 100%;animation:strct-dg-skeleton-sweep 1.1s linear infinite}@keyframes strct-dg-skeleton-sweep{0%{background-position:100% 0}to{background-position:-100% 0}}@media(prefers-reduced-motion:reduce){.strct-dg__skeleton-block{animation:none;background:var(--skeleton-hi, var(--acc18))}}.strct-dg__skeleton-row td{border-bottom:1px solid var(--b1)}.strct-dg__empty{text-align:center;color:var(--t2);padding:22px}.strct-dg__quickfilter{min-width:180px;max-width:260px}.strct-dg__quickfilter--end{order:2;margin-inline-start:auto}.strct-dg__filternote--end{order:3}.strct-dg__filternote{font-size:var(--text-sm);color:var(--t2);font-variant-numeric:tabular-nums;white-space:nowrap}.strct-dg__toolbar{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:9px 14px;border-bottom:1px solid var(--b2)}.strct-dg__foot{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:9px 14px;border-top:1px solid var(--b2);flex-wrap:wrap}.strct-dg__foot-left,.strct-dg__foot-right{display:flex;align-items:center;gap:12px}.strct-dg__actions{position:relative}.strct-dg__chooser-menu{position:absolute;bottom:calc(100% + 6px);inset-inline-start:0;z-index:var(--z-sticky);min-width:180px;background:var(--bg-1);border:1px solid var(--b2);border-radius:4px;box-shadow:var(--shadow-pop);padding:6px;display:flex;flex-direction:column;gap:2px}.strct-dg__chooser-menu--right{inset-inline-start:auto;inset-inline-end:0}.strct-dg__chooser-item{display:flex;align-items:center;gap:8px;padding:5px 8px;border-radius:3px;cursor:pointer;font-size:12px;color:var(--t1);transition:background .1s ease;-webkit-user-select:none;user-select:none}.strct-dg__chooser-item:hover{background:var(--bg-3)}.strct-dg__count{font-size:12px;color:var(--t2)}.strct-dg__count-sep{margin:0 8px;color:var(--b2)}.strct-dg__count-sel{color:var(--acc)}.strct-dg__filterbtn{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;margin-inline-start:2px;padding:0;border:0;border-radius:2px;background:transparent;color:var(--t3);cursor:pointer;vertical-align:middle}.strct-dg__filterbtn:hover{color:var(--t1);background:var(--bg-3)}.strct-dg__filterbtn--active{color:var(--acc)}.strct-dg__filterbtn:focus-visible{outline:2px solid var(--acc50);outline-offset:1px}.strct-dg__filterpanel{position:absolute;z-index:calc(var(--z-dropdown) + 10);display:flex;flex-direction:column;gap:6px;min-width:180px;padding:10px;background:var(--bg-1);border:1px solid var(--b2);border-radius:3px;box-shadow:var(--shh);font-weight:400;text-transform:none;letter-spacing:normal;white-space:nowrap}.strct-dg__filterinput{padding:5px 8px;border:1px solid var(--b2);border-radius:var(--radius-sm);background:var(--bg-2);color:var(--t1);font-family:var(--font);font-size:12px}.strct-dg__filterinput:focus-visible{outline:2px solid var(--acc50);outline-offset:1px}.strct-dg__filteropt{display:flex;align-items:center;gap:7px;font-size:12.5px;color:var(--t1);cursor:pointer}.strct-dg__filterclear{align-self:flex-start;padding:3px 8px;border:1px solid var(--b2);border-radius:var(--radius-sm);background:transparent;color:var(--t2);font-family:var(--font);font-size:var(--text-sm);cursor:pointer}.strct-dg__filterclear:hover:not(:disabled){color:var(--t1);background:var(--bg-3)}.strct-dg__filterclear:disabled{color:var(--t4);cursor:default}.strct-dg tbody tr[tabindex]:focus-visible{outline:2px solid var(--acc50);outline-offset:-2px}.strct-dg__treepad{display:inline-block;height:1px;vertical-align:middle}.strct-dg__treebtn{display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;margin-inline-end:4px;padding:0;border:0;border-radius:2px;background:transparent;color:var(--t3);cursor:pointer;vertical-align:middle}.strct-dg__treebtn:hover{color:var(--t1);background:var(--bg-3)}.strct-dg__treebtn:focus-visible{outline:2px solid var(--acc50);outline-offset:1px}.strct-dg__treebtn strct-icon{transition:transform .15s ease}.strct-dg__treebtn--open strct-icon{transform:rotate(90deg)}.strct-dg__treebtn--leaf{cursor:default;pointer-events:none}@media(prefers-reduced-motion:reduce){.strct-dg__treebtn strct-icon{transition:none}}.strct-dg__cell--editable{cursor:text;position:relative}.strct-dg__cell--editable:hover{box-shadow:inset 0 0 0 1px var(--b2)}.strct-dg__cell--editable:focus-visible{outline:none;box-shadow:inset 0 0 0 2px var(--acc50)}.strct-dg__editpencil{position:absolute;top:50%;inset-inline-end:6px;transform:translateY(-50%);color:var(--t3);opacity:0;pointer-events:none}td:hover>.strct-dg__editpencil,td:focus-within>.strct-dg__editpencil{opacity:1}.strct-dg__editinput{width:100%;box-sizing:border-box;padding:3px 6px;border:1px solid var(--acc);border-radius:var(--radius-sm);background:var(--bg-1);color:var(--t1);font:inherit}.strct-dg__editselect,.strct-dg__editnumber{display:block;width:100%}.strct-dg__editinput:focus-visible{outline:2px solid var(--acc50);outline-offset:1px}\n"], dependencies: [{ kind: "component", type: StrctIcon, selector: "strct-icon", inputs: ["name", "strictName", "size", "strokeWidth", "badge", "mode", "modeLabel", "ariaLabel", "count", "countMax", "countStatus", "countLabel"] }, { kind: "component", type: StrctPagination, selector: "strct-pagination", inputs: ["prevLabel", "nextLabel", "regionLabel", "pageLabel", "total", "pageSize", "page"], outputs: ["pageChange"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: StrctCheckbox, selector: "strct-checkbox", inputs: ["checked", "disabled", "indeterminate", "ariaLabel", "description", "tabIndex"], outputs: ["checkedChange"] }, { kind: "component", type: StrctButton, selector: "button[strct-button], a[strct-button]", inputs: ["variant", "size", "solid", "block", "iconOnly", "disabled", "disabledReason", "disabledReasonPosition"] }, { kind: "component", type: StrctButtonGroup, selector: "strct-button-group" }, { kind: "directive", type: StrctOverlay, selector: "[strctOverlay]", inputs: ["strctOverlay", "strctOverlayPlacement", "strctOverlayMatchWidth", "strctOverlayGap"] }, { kind: "component", type: StrctSearchbox, selector: "strct-searchbox", inputs: ["placeholder", "hint", "trigger", "clearable", "ariaLabel", "clearLabel", "value"], outputs: ["valueChange", "search", "activated"] }, { kind: "component", type: StrctSelect, selector: "strct-select", inputs: ["options", "placeholder", "ariaLabel", "listLabel", "emptyText", "disabled", "compareWith"] }, { kind: "component", type: StrctNumber, selector: "strct-number", inputs: ["min", "max", "step", "placeholder", "ariaLabel", "incrementLabel", "decrementLabel", "disabled"] }, { kind: "component", type: StrctSpinner, selector: "strct-spinner", inputs: ["size", "label", "caption"] }, { kind: "component", type: StrctEmptyState, selector: "strct-empty-state", inputs: ["variant", "icon", "tone", "title", "titleLevel", "size", "description"] }, { kind: "ngmodule", type: FormsModule }, { 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"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
17821
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.4", type: StrctDatagrid, isStandalone: true, selector: "strct-datagrid", inputs: { columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: true, transformFunction: null }, rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: true, transformFunction: null }, pageSizeFlat: { classPropertyName: "pageSizeFlat", publicName: "pageSize", isSignal: true, isRequired: false, transformFunction: null }, selectableFlat: { classPropertyName: "selectableFlat", publicName: "selectable", isSignal: true, isRequired: false, transformFunction: null }, expandable: { classPropertyName: "expandable", publicName: "expandable", isSignal: true, isRequired: false, transformFunction: null }, detailPaneFlat: { classPropertyName: "detailPaneFlat", publicName: "detailPane", isSignal: true, isRequired: false, transformFunction: null }, paneListWidthFlat: { classPropertyName: "paneListWidthFlat", publicName: "paneListWidth", isSignal: true, isRequired: false, transformFunction: null }, paneTakeoverWidthFlat: { classPropertyName: "paneTakeoverWidthFlat", publicName: "paneTakeoverWidth", isSignal: true, isRequired: false, transformFunction: null }, activeRowId: { classPropertyName: "activeRowId", publicName: "activeRowId", isSignal: true, isRequired: false, transformFunction: null }, compact: { classPropertyName: "compact", publicName: "compact", isSignal: true, isRequired: false, transformFunction: null }, singleLine: { classPropertyName: "singleLine", publicName: "singleLine", isSignal: true, isRequired: false, transformFunction: null }, emptyText: { classPropertyName: "emptyText", publicName: "emptyText", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, resizable: { classPropertyName: "resizable", publicName: "resizable", isSignal: true, isRequired: false, transformFunction: null }, columnChooser: { classPropertyName: "columnChooser", publicName: "columnChooser", isSignal: true, isRequired: false, transformFunction: null }, syncFlat: { classPropertyName: "syncFlat", publicName: "sync", isSignal: true, isRequired: false, transformFunction: null }, footerActionsDisabled: { classPropertyName: "footerActionsDisabled", publicName: "footerActionsDisabled", isSignal: true, isRequired: false, transformFunction: null }, rowId: { classPropertyName: "rowId", publicName: "rowId", isSignal: true, isRequired: false, transformFunction: null }, selectionModeFlat: { classPropertyName: "selectionModeFlat", publicName: "selectionMode", isSignal: true, isRequired: false, transformFunction: null }, selectedId: { classPropertyName: "selectedId", publicName: "selectedId", isSignal: true, isRequired: false, transformFunction: null }, rowSelectableFlat: { classPropertyName: "rowSelectableFlat", publicName: "rowSelectable", isSignal: true, isRequired: false, transformFunction: null }, flush: { classPropertyName: "flush", publicName: "flush", isSignal: true, isRequired: false, transformFunction: null }, caption: { classPropertyName: "caption", publicName: "caption", isSignal: true, isRequired: false, transformFunction: null }, hasMoreFlat: { classPropertyName: "hasMoreFlat", publicName: "hasMore", isSignal: true, isRequired: false, transformFunction: null }, loadingMoreFlat: { classPropertyName: "loadingMoreFlat", publicName: "loadingMore", isSignal: true, isRequired: false, transformFunction: null }, moreTotalFlat: { classPropertyName: "moreTotalFlat", publicName: "moreTotal", isSignal: true, isRequired: false, transformFunction: null }, groupSelectFlat: { classPropertyName: "groupSelectFlat", publicName: "groupSelect", isSignal: true, isRequired: false, transformFunction: null }, rowActions: { classPropertyName: "rowActions", publicName: "rowActions", isSignal: true, isRequired: false, transformFunction: null }, initialSelectionFlat: { classPropertyName: "initialSelectionFlat", publicName: "initialSelection", isSignal: true, isRequired: false, transformFunction: null }, selectedIds: { classPropertyName: "selectedIds", publicName: "selectedIds", isSignal: true, isRequired: false, transformFunction: null }, labels: { classPropertyName: "labels", publicName: "labels", isSignal: true, isRequired: false, transformFunction: null }, viewportHeightFlat: { classPropertyName: "viewportHeightFlat", publicName: "viewportHeight", isSignal: true, isRequired: false, transformFunction: null }, maxHeightFlat: { classPropertyName: "maxHeightFlat", publicName: "maxHeight", isSignal: true, isRequired: false, transformFunction: null }, editHint: { classPropertyName: "editHint", publicName: "editHint", isSignal: true, isRequired: false, transformFunction: null }, rowHeightFlat: { classPropertyName: "rowHeightFlat", publicName: "rowHeight", isSignal: true, isRequired: false, transformFunction: null }, lazyFlat: { classPropertyName: "lazyFlat", publicName: "lazy", isSignal: true, isRequired: false, transformFunction: null }, totalFlat: { classPropertyName: "totalFlat", publicName: "total", isSignal: true, isRequired: false, transformFunction: null }, stateKeyFlat: { classPropertyName: "stateKeyFlat", publicName: "stateKey", isSignal: true, isRequired: false, transformFunction: null }, columnState: { classPropertyName: "columnState", publicName: "columnState", isSignal: true, isRequired: false, transformFunction: null }, reorderable: { classPropertyName: "reorderable", publicName: "reorderable", isSignal: true, isRequired: false, transformFunction: null }, groupByFlat: { classPropertyName: "groupByFlat", publicName: "groupBy", isSignal: true, isRequired: false, transformFunction: null }, groupsFlat: { classPropertyName: "groupsFlat", publicName: "groups", isSignal: true, isRequired: false, transformFunction: null }, showEmptyGroupsFlat: { classPropertyName: "showEmptyGroupsFlat", publicName: "showEmptyGroups", isSignal: true, isRequired: false, transformFunction: null }, collapsedGroups: { classPropertyName: "collapsedGroups", publicName: "collapsedGroups", isSignal: true, isRequired: false, transformFunction: null }, filters: { classPropertyName: "filters", publicName: "filters", isSignal: true, isRequired: false, transformFunction: null }, quickFilter: { classPropertyName: "quickFilter", publicName: "quickFilter", isSignal: true, isRequired: false, transformFunction: null }, quickFilterFieldsFlat: { classPropertyName: "quickFilterFieldsFlat", publicName: "quickFilterFields", isSignal: true, isRequired: false, transformFunction: null }, quickFilterableFlat: { classPropertyName: "quickFilterableFlat", publicName: "quickFilterable", isSignal: true, isRequired: false, transformFunction: null }, quickFilterAlignFlat: { classPropertyName: "quickFilterAlignFlat", publicName: "quickFilterAlign", isSignal: true, isRequired: false, transformFunction: null }, childrenKeyFlat: { classPropertyName: "childrenKeyFlat", publicName: "childrenKey", isSignal: true, isRequired: false, transformFunction: null }, interactionMode: { classPropertyName: "interactionMode", publicName: "interactionMode", isSignal: true, isRequired: false, transformFunction: null }, selectionColumnFlat: { classPropertyName: "selectionColumnFlat", publicName: "selectionColumn", isSignal: true, isRequired: false, transformFunction: null }, selectionCfg: { classPropertyName: "selectionCfg", publicName: "selection", isSignal: true, isRequired: false, transformFunction: null }, paneFlat: { classPropertyName: "paneFlat", publicName: "pane", isSignal: true, isRequired: false, transformFunction: null }, pagingFlat: { classPropertyName: "pagingFlat", publicName: "paging", isSignal: true, isRequired: false, transformFunction: null }, virtualFlat: { classPropertyName: "virtualFlat", publicName: "virtual", isSignal: true, isRequired: false, transformFunction: null }, groupingCfg: { classPropertyName: "groupingCfg", publicName: "grouping", isSignal: true, isRequired: false, transformFunction: null }, filteringCfg: { classPropertyName: "filteringCfg", publicName: "filtering", isSignal: true, isRequired: false, transformFunction: null }, stateCfg: { classPropertyName: "stateCfg", publicName: "state", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { activeRowId: "activeRowIdChange", selectedId: "selectedIdChange", loadMore: "loadMore", selectedIds: "selectedIdsChange", columnState: "columnStateChange", collapsedGroups: "collapsedGroupsChange", filters: "filtersChange", quickFilter: "quickFilterChange", rowActivate: "rowActivate", selectionChange: "selectionChange", syncChange: "syncChange", rowAction: "rowAction", lazyLoad: "lazyLoad", cellEdit: "cellEdit" }, host: { listeners: { "document:click": "onDocClickOverlays($event)", "document:keydown.escape": "onEscapeOverlays($event)" }, properties: { "attr.aria-busy": "loading() || loadingMore() ? \"true\" : null", "class.strct-dg-host--compact": "compact()", "class.strct-dg-host--singleline": "singleLine()", "class.strct-dg-host--virtual": "virtual()", "class.strct-dg-host--bounded": "!virtual() && maxHeight() !== null", "class.strct-dg-host--sticky": "stickyActive()", "class.strct-dg-host--flush": "flush()" }, classAttribute: "strct-dg-host" }, queries: [{ propertyName: "detailDef", first: true, predicate: StrctRowDetailDef, descendants: true, isSignal: true }, { propertyName: "groupHeaderDef", first: true, predicate: StrctGroupHeaderDef, descendants: true, isSignal: true }, { propertyName: "paneTitleDef", first: true, predicate: StrctDetailPaneTitle, descendants: true, isSignal: true }, { propertyName: "actionBarDef", first: true, predicate: StrctDatagridActionBar, descendants: true, isSignal: true }, { propertyName: "actionBarCaptionDef", first: true, predicate: StrctDatagridActionBarCaption, descendants: true, isSignal: true }, { propertyName: "cellDefs", predicate: StrctCellDef, isSignal: true }], ngImport: i0, template: "<!-- One polite region for everything the grid does to itself: sorting,\n paging and filtering all change the rows without saying so. -->\n<span class=\"strct-dg__announcer\" role=\"status\" aria-live=\"polite\">{{ announcement() }}</span>\n@if (actionBarDef() || quickFilterable() || actionBarCaptionDef()) {\n <div class=\"strct-dg__toolbar\">\n @if (quickFilterable()) {\n <strct-searchbox\n class=\"strct-dg__quickfilter\"\n [class.strct-dg__quickfilter--end]=\"quickFilterAlign() === 'end'\"\n [placeholder]=\"L().quickFilter\"\n [ariaLabel]=\"L().quickFilter\"\n [value]=\"quickFilter()\"\n (valueChange)=\"quickFilter.set($event)\"\n />\n @if (filterNote(); as note) {\n <span\n class=\"strct-dg__filternote\"\n [class.strct-dg__filternote--end]=\"quickFilterAlign() === 'end'\"\n >{{ note }}</span\n >\n }\n }\n <span class=\"strct-dg__actioncaption\">\n <ng-content select=\"[strctDatagridActionBarCaption]\" />\n </span>\n <ng-content select=\"[strctDatagridActionBar]\" />\n </div>\n}\n\n@if (hasEditableColumn()) {\n <!-- What the editable cells point at with aria-describedby: a keyboard\n user is told the cell can be edited, and how. -->\n <!-- In flow, not absolutely positioned: an out-of-flow box whose\n containing block is the page extends the document's scrollable area\n from wherever the grid happens to sit. 1x1 and clipped is enough. -->\n <span\n class=\"strct-sr-only\"\n style=\"\n display: inline-block;\n width: 1px;\n height: 1px;\n overflow: hidden;\n clip-path: inset(50%);\n white-space: nowrap;\n \"\n [id]=\"editHintId\"\n >{{ L().editCellInstructions }}</span\n >\n}\n\n@if (bulkHint(); as hint) {\n <!-- Selecting the page is not selecting the set. Say which one happened,\n and offer the other. -->\n <div class=\"strct-dg__bulk\" role=\"status\">\n <span>{{ hint.text }}</span>\n <button strct-button variant=\"flat\" size=\"sm\" (click)=\"hint.run()\">\n {{ hint.action }}\n </button>\n </div>\n}\n<div\n class=\"strct-dg__layout\"\n [class.strct-dg__layout--paned]=\"paneOpen()\"\n [class.strct-dg__layout--panetakeover]=\"paneOpen() && narrowPane()\"\n [style.--strct-dg-pane-list-w]=\"paneListWidth()\"\n>\n <div\n class=\"strct-dg__scroll\"\n tabindex=\"0\"\n role=\"region\"\n [attr.aria-label]=\"L().rows\"\n [style.max-height.px]=\"virtual() ? viewportHeight() : maxHeight()\"\n (scroll)=\"virtual() && onScroll($event)\"\n >\n <table\n class=\"strct-dg\"\n [attr.role]=\"childrenKey() ? 'treegrid' : gridMode() ? 'grid' : null\"\n [attr.aria-rowcount]=\"ariaRowCount()\"\n [attr.aria-labelledby]=\"caption() ? captionId : null\"\n >\n @if (caption()) {\n <caption class=\"strct-dg__caption\" [id]=\"captionId\">\n {{\n caption()\n }}\n </caption>\n }\n <thead>\n <tr>\n <!-- A utility column is still a column: axe reports every empty\n header, and a screen-reader user hears nothing where the\n detail, expand and select controls are. The name is hidden,\n not absent. -->\n @if (canDetail()) {\n <th\n class=\"strct-dg__expandcol\"\n scope=\"col\"\n [class.strct-dg__cell--sticky]=\"stickyActive()\"\n [style.insetInlineStart.px]=\"utilLeft('detail')\"\n >\n <span class=\"strct-sr-only\">{{ L().detailColumn }}</span>\n </th>\n }\n @if (canExpand()) {\n <th\n class=\"strct-dg__expandcol\"\n scope=\"col\"\n [class.strct-dg__cell--sticky]=\"stickyActive()\"\n [style.insetInlineStart.px]=\"utilLeft('expand')\"\n >\n <span class=\"strct-sr-only\">{{ L().expandColumn }}</span>\n </th>\n }\n @if (hasSelectionColumn()) {\n <th\n class=\"strct-dg__sel\"\n scope=\"col\"\n [class.strct-dg__cell--sticky]=\"stickyActive()\"\n [style.insetInlineStart.px]=\"utilLeft('sel')\"\n >\n @if (mode() === 'multiple') {\n <strct-checkbox\n [ariaLabel]=\"L().selectAll\"\n [checked]=\"allPageSelected()\"\n [indeterminate]=\"somePageSelected()\"\n (checkedChange)=\"toggleAll()\"\n />\n } @else {\n <span class=\"strct-sr-only\">{{ L().selectColumn }}</span>\n }\n </th>\n }\n @for (col of visibleColumns(); track col.key) {\n <th\n scope=\"col\"\n [style.text-align]=\"col.align ?? (col.numeric ? 'end' : 'start')\"\n [style.width]=\"colWidth(col.key) ?? col.width ?? null\"\n [class.strct-dg__th--sortable]=\"col.sortable\"\n [class.strct-dg__th--drop]=\"col.key === dropKey()\"\n [class.strct-dg__cell--sticky]=\"isSticky(col)\"\n [class.strct-dg__cell--sticky-last]=\"col.key === lastStickyKey()\"\n [style.insetInlineStart.px]=\"stickyLeft(col.key)\"\n [attr.data-col]=\"col.key\"\n [attr.aria-sort]=\"col.sortable ? ariaSort(col.key) : null\"\n [attr.draggable]=\"reorderable() ? true : null\"\n (dragstart)=\"onColDragStart(col.key, $event)\"\n (dragover)=\"onColDragOver(col.key, $event)\"\n (drop)=\"onColDrop(col.key)\"\n (dragend)=\"onColDragEnd()\"\n >\n <span class=\"strct-dg__hd\">\n <!-- A header that sorts is a button: a th with tabindex is\n read as \"column header, focusable\" and never says it\n can be activated. aria-sort stays on the th, which is\n what carries the column's sorted state. -->\n @if (col.sortable) {\n <!-- The button covers most of the header, so it carries\n the drag source too: a reorderable column has to be\n grabbable where the pointer actually lands. The\n event bubbles to the th's own handlers. -->\n <button\n type=\"button\"\n class=\"strct-dg__sortbtn\"\n [attr.draggable]=\"reorderable() ? true : null\"\n (click)=\"sortBy(col.key)\"\n >\n {{ col.label }}\n <strct-icon class=\"strct-dg__sorticon\" [name]=\"sortIcon(col.key)\" [size]=\"13\" />\n </button>\n } @else {\n {{ col.label }}\n }\n @if (col.filterable || col.filterOptions) {\n <button\n #fbtn\n type=\"button\"\n class=\"strct-dg__filterbtn\"\n [class.strct-dg__filterbtn--active]=\"hasFilter(col.key)\"\n [attr.aria-expanded]=\"filterOpenKey() === col.key\"\n [attr.aria-label]=\"L().filterColumn + ': ' + col.label\"\n (click)=\"toggleFilterPanel(col.key, $event)\"\n (keydown.enter)=\"$event.stopPropagation()\"\n (keydown.space)=\"$event.stopPropagation()\"\n >\n <strct-icon name=\"filter\" [size]=\"12\" />\n </button>\n @if (filterOpenKey() === col.key) {\n <div\n class=\"strct-dg__filterpanel\"\n role=\"dialog\"\n [attr.aria-label]=\"L().filterColumn + ': ' + col.label\"\n [strctOverlay]=\"fbtn\"\n strctOverlayPlacement=\"bottom-start\"\n (click)=\"$event.stopPropagation()\"\n (keydown.enter)=\"$event.stopPropagation()\"\n (keydown.space)=\"$event.stopPropagation()\"\n >\n @if (col.filterOptions; as opts) {\n @for (opt of opts; track $index) {\n <label class=\"strct-dg__filteropt\">\n <input\n type=\"checkbox\"\n [checked]=\"optionChecked(col.key, opt)\"\n (change)=\"toggleFilterOption(col.key, opt)\"\n />\n <span>{{ opt }}</span>\n </label>\n }\n } @else {\n <input\n class=\"strct-dg__filterinput\"\n type=\"text\"\n [attr.placeholder]=\"L().filterPlaceholder\"\n [attr.aria-label]=\"L().filterColumn + ': ' + col.label\"\n [value]=\"textFilter(col.key)\"\n (input)=\"setTextFilter(col.key, $any($event.target).value)\"\n />\n }\n <button\n type=\"button\"\n class=\"strct-dg__filterclear\"\n [disabled]=\"!hasFilter(col.key)\"\n (click)=\"clearFilter(col.key)\"\n >\n {{ L().clearFilter }}\n </button>\n </div>\n }\n }\n </span>\n @if (resizable() && col.resizable !== false) {\n <!-- A separator, not a bare div: a column could only be\n resized with a mouse, which is the one input a\n console cannot assume. -->\n <div\n class=\"strct-dg__resize\"\n role=\"separator\"\n aria-orientation=\"vertical\"\n tabindex=\"0\"\n [attr.aria-label]=\"L().resizeColumn(col.label)\"\n [attr.aria-valuenow]=\"separatorValue(col.key)\"\n [attr.aria-valuemin]=\"40\"\n (mousedown)=\"onResizeStart($event, col.key)\"\n (keydown)=\"onResizeKey($event, col.key)\"\n (dblclick)=\"resetWidth(col.key)\"\n ></div>\n }\n </th>\n }\n @if (canActions()) {\n <th class=\"strct-dg__actioncol\" scope=\"col\" [attr.aria-label]=\"L().actions\"></th>\n }\n </tr>\n </thead>\n <!--\n focusin is the real keyboard path (focus does not bubble from cells);\n the plain focus binding exists to satisfy mouse-events-have-key-events.\n -->\n <tbody\n (mouseover)=\"revealIfClipped($event)\"\n (focusin)=\"revealIfClipped($event)\"\n (focus)=\"revealIfClipped($event)\"\n >\n @if (loading()) {\n @for (_ of [1, 2, 3, 4, 5]; track $index) {\n <tr class=\"strct-dg__skeleton-row\">\n @if (canDetail()) {\n <td class=\"strct-dg__expandcell\"></td>\n }\n @if (canExpand()) {\n <td class=\"strct-dg__expandcell\"></td>\n }\n @if (hasSelectionColumn()) {\n <td class=\"strct-dg__sel\"></td>\n }\n @for (col of visibleColumns(); track col.key) {\n <td [style.text-align]=\"col.align ?? 'start'\">\n <div class=\"strct-dg__skeleton-block\"></div>\n </td>\n }\n @if (canActions()) {\n <td class=\"strct-dg__actioncell\"></td>\n }\n </tr>\n }\n } @else {\n @if (virtual() && topPad() > 0) {\n <tr class=\"strct-dg__vspacer\" aria-hidden=\"true\">\n <td [attr.colspan]=\"colspan()\" [style.height.px]=\"topPad()\"></td>\n </tr>\n }\n @for (it of displayItems(); track itemKey(it)) {\n @if (it.group; as grp) {\n <!-- Group header: distinct value + count, collapsible. -->\n <tr class=\"strct-dg__grouprow\">\n @if (groupSelect() && mode() === 'multiple') {\n <td class=\"strct-dg__sel\">\n <strct-checkbox\n [ariaLabel]=\"L().selectAllInGroup(grp.label)\"\n [checked]=\"groupAllSelected(grp.key)\"\n [indeterminate]=\"groupSomeSelected(grp.key)\"\n (checkedChange)=\"toggleGroupSelection(grp.key)\"\n />\n </td>\n }\n <td\n [attr.colspan]=\"\n groupSelect() && mode() === 'multiple' ? colspan() - 1 : colspan()\n \"\n >\n <button\n type=\"button\"\n class=\"strct-dg__groupbtn\"\n [attr.aria-expanded]=\"!grp.collapsed\"\n (click)=\"toggleGroup(grp.key)\"\n >\n <span\n class=\"strct-dg__groupchev\"\n [class.strct-dg__groupchev--open]=\"!grp.collapsed\"\n >\n <strct-icon name=\"chevronRight\" [size]=\"12\" [strokeWidth]=\"1.7\" />\n </span>\n @if (groupHeaderDef(); as tpl) {\n <!-- The consumer draws the row; the button, its\n aria-expanded and the keyboard stay ours. -->\n <ng-container\n [ngTemplateOutlet]=\"tpl.template\"\n [ngTemplateOutletContext]=\"{\n $implicit: grp,\n group: grp,\n count: grp.count,\n }\"\n />\n } @else {\n <span\n class=\"strct-dg__grouplabel strct-dg__grouplabel--{{\n grp.status ?? 'none'\n }}\"\n >{{ grp.label }}</span\n >\n <!-- The three spans run together in the button's\n name (\"Needs your decision1Blocks the join\u2026\");\n the separators are read, not seen. -->\n <span class=\"strct-dg__announcer\">, </span>\n <span\n class=\"strct-dg__groupcount strct-dg__groupcount--{{\n grp.status ?? 'none'\n }}\"\n >{{ grp.count }}</span\n >\n <!-- Seen: the chip's number. Heard: the number with\n the grid's noun, because \"Inconclusive, 3\" does\n not say three of what. -->\n <span class=\"strct-dg__announcer\"> {{ countNoun(grp.count) }}</span>\n @if (grp.note) {\n <span class=\"strct-dg__announcer\">. </span>\n <span class=\"strct-dg__groupnote\">{{ grp.note }}</span>\n }\n }\n </button>\n </td>\n </tr>\n } @else {\n @let row = it.row!;\n <tr\n [attr.aria-rowindex]=\"ariaRowIndex(row)\"\n [class.strct-dg__row--selected]=\"isSelected(row)\"\n [class.strct-dg__row--locked]=\"lockedReason(row) !== null\"\n [attr.aria-selected]=\"mode() === 'none' ? null : isSelected(row)\"\n [attr.aria-disabled]=\"lockedReason(row) !== null ? 'true' : null\"\n [attr.title]=\"lockedReason(row) || null\"\n (click)=\"onRowPick(row)\"\n [class.strct-dg__row--active]=\"paneOpen() && row === activeRow()\"\n [attr.aria-level]=\"childrenKey() ? treeDepth(row) + 1 : null\"\n [attr.aria-expanded]=\"\n childrenKey() && treeHasChildren(row) ? treeExpanded(row) : null\n \"\n [attr.tabindex]=\"\n childrenKey()\n ? treeRowTabindex(row)\n : gridMode()\n ? gridRowTabindex(row)\n : canDetail()\n ? paneRowTabindex(row)\n : null\n \"\n (keydown)=\"\n childrenKey()\n ? onTreeRowKeydown(row, $event)\n : gridMode()\n ? onGridRowKeydown(row, $event)\n : onPaneRowKeydown(row, $event)\n \"\n (dblclick)=\"onRowActivate(row)\"\n >\n @if (canDetail()) {\n <td\n class=\"strct-dg__expandcell\"\n [class.strct-dg__cell--sticky]=\"stickyActive()\"\n [style.insetInlineStart.px]=\"utilLeft('detail')\"\n >\n <button\n type=\"button\"\n [attr.tabindex]=\"ctlTabindex()\"\n class=\"strct-dg__detailbtn\"\n [class.strct-dg__detailbtn--active]=\"row === activeRow()\"\n [attr.aria-expanded]=\"row === activeRow()\"\n [attr.aria-label]=\"L().openDetail\"\n (click)=\"openDetail(row)\"\n >\n <strct-icon name=\"chevronDoubleRight\" [size]=\"13\" [strokeWidth]=\"1.6\" />\n </button>\n </td>\n }\n @if (canExpand()) {\n <td\n class=\"strct-dg__expandcell\"\n [class.strct-dg__cell--sticky]=\"stickyActive()\"\n [style.insetInlineStart.px]=\"utilLeft('expand')\"\n >\n <button\n type=\"button\"\n [attr.tabindex]=\"ctlTabindex()\"\n class=\"strct-dg__expandbtn\"\n [class.strct-dg__expandbtn--open]=\"isExpanded(row)\"\n [attr.aria-expanded]=\"isExpanded(row)\"\n [attr.aria-label]=\"L().toggleDetail\"\n (click)=\"toggleExpand(row)\"\n >\n <strct-icon name=\"chevronRight\" [size]=\"12\" [strokeWidth]=\"1.7\" />\n </button>\n </td>\n }\n @if (hasSelectionColumn()) {\n <td\n class=\"strct-dg__sel\"\n [class.strct-dg__cell--sticky]=\"stickyActive()\"\n [style.insetInlineStart.px]=\"utilLeft('sel')\"\n >\n @if (mode() === 'single') {\n <!-- A native radio group: one name per grid, so the\n arrow keys move between rows and Space picks,\n without the grid inventing its own keyboarding. -->\n <input\n type=\"radio\"\n class=\"strct-dg__radio\"\n [name]=\"radioName\"\n [attr.aria-label]=\"selectRowLabel(row)\"\n [attr.aria-description]=\"lockedReason(row) || null\"\n [checked]=\"isSelected(row)\"\n [disabled]=\"lockedReason(row) !== null\"\n [attr.tabindex]=\"ctlTabindex()\"\n (change)=\"selectSingle(row)\"\n (click)=\"$event.stopPropagation()\"\n />\n } @else {\n <!-- click fires (and bubbles) before the input's change,\n so the modifier is recorded by the time toggleRow runs. -->\n <strct-checkbox\n [ariaLabel]=\"selectRowLabel(row)\"\n [attr.aria-description]=\"lockedReason(row) || null\"\n [checked]=\"isSelected(row)\"\n [disabled]=\"lockedReason(row) !== null\"\n [tabIndex]=\"ctlTabindex()\"\n (click)=\"noteRangeIntent($event); $event.stopPropagation()\"\n (checkedChange)=\"toggleRow(row)\"\n />\n }\n </td>\n }\n @for (col of visibleColumns(); track col.key; let colIdx = $index) {\n <td\n [style.text-align]=\"col.align ?? (col.numeric ? 'end' : 'start')\"\n [class.strct-dg__cell--mono]=\"col.mono\"\n [class.strct-dg__cell--muted]=\"col.muted\"\n [class.strct-dg__cell--numeric]=\"col.numeric\"\n [class.strct-dg__cell--sticky]=\"isSticky(col)\"\n [class.strct-dg__cell--sticky-last]=\"col.key === lastStickyKey()\"\n [class.strct-dg__cell--editable]=\"col.editable\"\n [style.insetInlineStart.px]=\"stickyLeft(col.key)\"\n [attr.data-edit]=\"col.editable ? '' : null\"\n [attr.tabindex]=\"col.editable ? editCellTabindex(row, col) : null\"\n [attr.aria-describedby]=\"col.editable ? editHintId : null\"\n (dblclick)=\"col.editable && startEdit(row, col)\"\n (focus)=\"col.editable && onEditCellFocus(row, col)\"\n (keydown)=\"col.editable && onEditCellKeydown(row, col, $event)\"\n >\n @if (childrenKey() && colIdx === 0) {\n <span\n class=\"strct-dg__treepad\"\n [style.width.px]=\"treeDepth(row) * 18\"\n aria-hidden=\"true\"\n ></span>\n @if (treeHasChildren(row)) {\n <button\n type=\"button\"\n class=\"strct-dg__treebtn\"\n [class.strct-dg__treebtn--open]=\"treeExpanded(row)\"\n [attr.aria-expanded]=\"treeExpanded(row)\"\n [attr.aria-label]=\"L().toggleChildren\"\n (click)=\"toggleTreeRow(row)\"\n >\n <strct-icon name=\"chevronRight\" [size]=\"12\" [strokeWidth]=\"1.7\" />\n </button>\n } @else {\n <span\n class=\"strct-dg__treebtn strct-dg__treebtn--leaf\"\n aria-hidden=\"true\"\n ></span>\n }\n }\n @if (isEditingCell(row, col)) {\n @switch (col.editor ?? 'text') {\n @case ('select') {\n <!-- Choosing is the commit: there is nothing to\n confirm with Enter once an option is picked. -->\n <strct-select\n class=\"strct-dg__editselect\"\n [options]=\"editorOptionsFor(row, col)\"\n [listLabel]=\"L().editCell + ': ' + col.label\"\n [ngModel]=\"row[col.key]\"\n (ngModelChange)=\"commitValue(row, col, $event)\"\n (keydown.escape)=\"cancelEdit($event)\"\n (click)=\"$event.stopPropagation()\"\n />\n }\n @case ('number') {\n <strct-number\n class=\"strct-dg__editnumber\"\n [ariaLabel]=\"L().editCell + ': ' + col.label\"\n [min]=\"col.editorMin ?? null\"\n [max]=\"col.editorMax ?? null\"\n [step]=\"col.editorStep ?? 1\"\n [ngModel]=\"row[col.key]\"\n (ngModelChange)=\"editDraft.set($event)\"\n (keydown.enter)=\"commitDraft(row, col, $event)\"\n (keydown.escape)=\"cancelEdit($event)\"\n (focusout)=\"commitOnLeave(row, col, $event)\"\n (click)=\"$event.stopPropagation()\"\n />\n }\n @default {\n <input\n class=\"strct-dg__editinput\"\n type=\"text\"\n [value]=\"row[col.key] ?? ''\"\n [attr.aria-label]=\"L().editCell + ': ' + col.label\"\n (keydown.enter)=\"commitEdit(row, col, $event)\"\n (keydown.escape)=\"cancelEdit($event)\"\n (blur)=\"commitEdit(row, col, $event)\"\n (click)=\"$event.stopPropagation()\"\n />\n }\n }\n } @else if (cellTemplate(col.key); as tpl) {\n <ng-container\n [ngTemplateOutlet]=\"tpl\"\n [ngTemplateOutletContext]=\"{\n $implicit: row,\n row,\n value: row[col.key],\n column: col,\n }\"\n />\n } @else if (isBlank(row, col)) {\n <span class=\"strct-dg__empty\" [attr.aria-label]=\"col.emptyLabel || null\">{{\n col.emptyText\n }}</span>\n } @else {\n {{ displayValue(row, col) }}\n @if (col.descriptionKey && row[col.descriptionKey]) {\n <span class=\"strct-dg__celldesc\">{{ row[col.descriptionKey] }}</span>\n }\n }\n <!-- The affordance a mouse never needed: a cell that\n can be edited says so on hover and on focus. -->\n @if (col.editable && editHint() && !isEditingCell(row, col)) {\n <strct-icon\n class=\"strct-dg__editpencil\"\n name=\"pencil\"\n [size]=\"12\"\n [strokeWidth]=\"1.6\"\n aria-hidden=\"true\"\n />\n }\n </td>\n }\n @if (canActions()) {\n <td class=\"strct-dg__actioncell\">\n <button\n type=\"button\"\n [attr.tabindex]=\"ctlTabindex()\"\n class=\"strct-dg__kebab\"\n [attr.aria-label]=\"L().rowActions\"\n (click)=\"openRowMenu(row, $event)\"\n >\n <strct-icon name=\"dots\" [size]=\"16\" />\n </button>\n </td>\n }\n </tr>\n @if (canExpand() && isExpanded(row)) {\n <tr class=\"strct-dg__detailrow\">\n <td [attr.colspan]=\"colspan()\">\n <div class=\"strct-dg__detail\">\n <ng-container\n [ngTemplateOutlet]=\"detailDef()!.template\"\n [ngTemplateOutletContext]=\"{ $implicit: row, row }\"\n />\n </div>\n </td>\n </tr>\n }\n }\n } @empty {\n <!-- \"Nothing here\" and \"nothing matches what you typed\" are\n different states: the second one can be undone, and\n saying so is the whole point. -->\n <tr>\n <td class=\"strct-dg__emptyrow\" [attr.colspan]=\"colspan()\">\n @if (filteredAway()) {\n <strct-empty-state\n size=\"sm\"\n [titleLevel]=\"null\"\n [title]=\"L().noMatches\"\n [description]=\"noMatchesDescription()\"\n >\n <button strct-button variant=\"flat\" size=\"sm\" (click)=\"clearAllFilters()\">\n {{ L().clearFilters }}\n </button>\n </strct-empty-state>\n } @else {\n <!-- Not a heading: a grid is a region of a page, and an\n empty one used to put an <h3> in its outline. -->\n <strct-empty-state size=\"sm\" [titleLevel]=\"null\" [title]=\"emptyText()\" />\n }\n </td>\n </tr>\n }\n @if (virtual() && bottomPad() > 0) {\n <tr class=\"strct-dg__vspacer\" aria-hidden=\"true\">\n <td [attr.colspan]=\"colspan()\" [style.height.px]=\"bottomPad()\"></td>\n </tr>\n }\n }\n </tbody>\n </table>\n </div>\n\n @if (paneOpen()) {\n <aside class=\"strct-dg__pane\">\n <div class=\"strct-dg__pane-head\">\n @if (narrowPane()) {\n <!-- The pane has taken the list's place, so there is a way back. -->\n <button\n type=\"button\"\n class=\"strct-dg__pane-back\"\n [attr.aria-label]=\"L().backToList\"\n (click)=\"closePane()\"\n >\n <strct-icon name=\"chevronLeft\" [size]=\"13\" />\n </button>\n }\n <span class=\"strct-dg__pane-title\">\n <ng-content select=\"[strctDetailPaneTitle]\" />\n @if (!paneTitleDef()) {\n {{ activeRow()![columns()[0].key] }}\n }\n </span>\n <span class=\"strct-dg__pane-meta\">\n <ng-content select=\"[strctDetailPaneMeta]\" />\n </span>\n <span class=\"strct-dg__pane-actions\">\n <ng-content select=\"[strctDetailPaneActions]\" />\n </span>\n <button\n type=\"button\"\n class=\"strct-dg__pane-close\"\n [attr.aria-label]=\"L().closeDetail\"\n (click)=\"closePane()\"\n >\n <strct-icon name=\"close\" [size]=\"13\" />\n </button>\n </div>\n <div class=\"strct-dg__pane-body\">\n <ng-container\n [ngTemplateOutlet]=\"detailDef()!.template\"\n [ngTemplateOutletContext]=\"{ $implicit: activeRow(), row: activeRow() }\"\n />\n </div>\n </aside>\n }\n</div>\n\n@if (showFooter()) {\n <div class=\"strct-dg__foot\">\n <div class=\"strct-dg__foot-left\">\n @if (columnChooser() || sync()) {\n <div class=\"strct-dg__actions\">\n <strct-button-group>\n @if (columnChooser()) {\n <button\n strct-button\n size=\"sm\"\n [class.is-open]=\"chooserOpen()\"\n [disabled]=\"footerActionsDisabled()\"\n [attr.aria-label]=\"L().chooseColumns\"\n (click)=\"toggleChooser($event)\"\n >\n <strct-icon name=\"settings\" [size]=\"16\" />\n </button>\n }\n @if (sync()) {\n <button\n strct-button\n size=\"sm\"\n [disabled]=\"footerActionsDisabled()\"\n [attr.aria-label]=\"L().refresh\"\n (click)=\"syncChange.emit()\"\n >\n <strct-icon name=\"refresh\" [size]=\"16\" />\n </button>\n }\n </strct-button-group>\n @if (columnChooser() && chooserOpen()) {\n <div class=\"strct-dg__chooser-menu\">\n @for (col of columns(); track col.key) {\n <!-- The whole row toggles; the checkbox's own click must not\n reach the row or the column would flip twice. -->\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <div\n class=\"strct-dg__chooser-item\"\n (click)=\"toggleColumn(col.key, hiddenColumns().has(col.key))\"\n >\n <strct-checkbox\n [checked]=\"!hiddenColumns().has(col.key)\"\n (checkedChange)=\"toggleColumn(col.key, $event)\"\n (click)=\"$event.stopPropagation()\"\n />\n <span>{{ col.label }}</span>\n </div>\n }\n </div>\n }\n </div>\n }\n <span class=\"strct-dg__count\">\n @if (paging() === 'more') {\n {{ L().showing(rows().length, moreTotal() ?? undefined) }}\n } @else {\n {{ totalCount() }} {{ totalCount() === 1 ? L().row : L().rows }}\n }\n @if (selectedCount() && mode() !== 'single') {\n <span class=\"strct-dg__count-sep\">|</span>\n <span class=\"strct-dg__count-sel\">{{ selectedCount() }} {{ L().selected }}</span>\n }\n </span>\n </div>\n <div class=\"strct-dg__foot-right\">\n <!-- A feed that pages by cursor loads more at the end: page numbers\n are something a cursor API cannot answer. It takes the pager's\n place \u2014 the column chooser, the sync button and the count are\n the footer's, not the pager's. -->\n @if (paging() === 'more') {\n @if (hasMore()) {\n <button\n strct-button\n size=\"sm\"\n class=\"strct-dg__more\"\n [disabled]=\"loadingMore()\"\n (click)=\"loadMore.emit()\"\n >\n @if (loadingMore()) {\n <strct-spinner size=\"sm\" />\n }\n {{ L().loadMore }}\n </button>\n }\n } @else if (pageSize() > 0 && !groupBy()) {\n <strct-pagination\n [total]=\"totalCount()\"\n [pageSize]=\"pageSize()\"\n [page]=\"page()\"\n (pageChange)=\"goToPage($event)\"\n />\n }\n </div>\n </div>\n}\n", styles: [".strct-dg__radio{width:15px;height:15px;accent-color:var(--acc);cursor:pointer}.strct-dg__radio:disabled{cursor:not-allowed}.strct-dg__row--locked .strct-dg__sel{opacity:.55}.strct-dg__actioncaption:empty{display:none}.strct-dg__actioncaption{display:inline-flex;align-items:center;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:var(--text-sm);color:var(--t2)}.strct-dg__bulk{display:flex;align-items:center;justify-content:center;flex-wrap:wrap;gap:4px 10px;padding:7px 12px;border-bottom:1px solid var(--b2);background:var(--acc-s);color:var(--t2);font-size:var(--text-md)}.strct-dg__announcer{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}.strct-dg-host{display:block;border:1px solid var(--b2);border-radius:var(--radius-lg);background:var(--bg-2);box-shadow:var(--shadow-rest)}.strct-dg-host--flush{border:0;border-radius:0;background:transparent;box-shadow:none}.strct-dg__more strct-spinner{margin-inline-end:4px}.strct-dg__caption{caption-side:top;text-align:start;padding:var(--space-2) var(--space-3);font-size:var(--text-sm);font-weight:600;color:var(--t2)}.strct-dg td.strct-dg__cell--mono{font-family:var(--mono);font-size:var(--text-sm)}.strct-dg td.strct-dg__cell--muted{color:var(--t2)}.strct-dg td.strct-dg__cell--numeric{font-variant-numeric:tabular-nums}.strct-dg__empty{color:var(--t2)}.strct-dg__celldesc{display:block;font-size:var(--text-sm);color:var(--t2);line-height:1.3}.strct-dg__scroll{flex:1 1 auto;min-width:0;overflow-x:auto;-webkit-overflow-scrolling:touch}.strct-dg-host--virtual .strct-dg__scroll,.strct-dg-host--bounded .strct-dg__scroll{overflow-y:auto}.strct-dg-host--virtual .strct-dg thead th,.strct-dg-host--bounded .strct-dg thead th{position:sticky;top:0;z-index:var(--z-raised)}.strct-dg__vspacer td{padding:0;border:0;background:transparent}.strct-dg th[draggable]{cursor:grab}.strct-dg__th--drop{box-shadow:inset 3px 0 0 var(--acc)}.strct-dg__grouprow td{background:var(--bg-2)!important;padding:0}.strct-dg__groupbtn{display:flex;align-items:center;gap:8px;width:100%;padding:7px 13px;border:0;background:transparent;color:var(--t1);font-family:var(--font);font-size:var(--text-sm);font-weight:600;cursor:pointer;text-align:start}.strct-dg__groupbtn:hover{background:var(--bg-3)}.strct-dg__groupbtn:focus-visible{outline:2px solid var(--acc50);outline-offset:-2px}.strct-dg__groupchev{display:inline-flex;color:var(--t3);transition:transform .15s ease}.strct-dg__groupchev--open{transform:rotate(90deg)}@media(prefers-reduced-motion:reduce){.strct-dg__groupchev{transition:none}}.strct-dg__grouplabel--critical{color:var(--critical)}.strct-dg__grouplabel--warning{color:var(--warning)}.strct-dg__grouplabel--success{color:var(--success)}.strct-dg__grouplabel--accent{color:var(--acc)}.strct-dg__grouplabel--neutral,.strct-dg__grouplabel--none{color:var(--t1)}.strct-dg__groupnote{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:var(--text-sm);font-weight:400;color:var(--t2)}.strct-dg__groupcount{min-width:18px;height:18px;padding:0 5px;display:inline-flex;align-items:center;justify-content:center;font-size:12px;border-radius:4px;background:var(--acc-m);color:var(--acc);font-variant-numeric:tabular-nums}.strct-dg__groupcount--critical{background:var(--critical-bg);color:var(--critical)}.strct-dg__groupcount--warning{background:var(--warning-bg);color:var(--warning)}.strct-dg__groupcount--success{background:var(--success-bg);color:var(--success)}.strct-dg__groupcount--neutral,.strct-dg__groupcount--none{background:var(--bg-3);color:var(--t2)}.strct-dg-host--singleline .strct-dg{border-collapse:separate;border-spacing:0}.strct-dg-host--sticky .strct-dg,.strct-dg-host--virtual .strct-dg,.strct-dg-host--bounded .strct-dg{border-collapse:separate;border-spacing:0;overflow:visible;border-radius:0}.strct-dg-host--sticky .strct-dg{width:max-content;min-width:100%}.strct-dg .strct-dg__cell--sticky{position:sticky;z-index:calc(var(--z-base) + 2)}.strct-dg thead th.strct-dg__cell--sticky{z-index:calc(var(--z-raised) + 1)}.strct-dg .strct-dg__cell--sticky-last:after{content:\"\";position:absolute;top:0;bottom:0;inset-inline-end:-1px;width:7px;pointer-events:none;background:linear-gradient(to right,rgba(0,0,0,.14),transparent)}[dir=rtl] .strct-dg .strct-dg__cell--sticky-last:after{background:linear-gradient(to left,rgba(0,0,0,.14),transparent)}.strct-dg-host--sticky .strct-dg__expandcol,.strct-dg-host--sticky .strct-dg__expandcell{width:36px;min-width:36px;max-width:36px;box-sizing:border-box}.strct-dg-host--sticky .strct-dg__sel{width:40px;min-width:40px;max-width:40px;box-sizing:border-box}.strct-dg-host--sticky tbody .strct-dg__cell--sticky{background:var(--bg-1)}[data-theme=dark] .strct-dg-host--sticky tbody .strct-dg__cell--sticky{background:var(--bg-3)}.strct-dg-host--sticky tbody tr:hover .strct-dg__cell--sticky{background:linear-gradient(var(--acc-s),var(--acc-s)) var(--bg-1)}[data-theme=dark] .strct-dg-host--sticky tbody tr:hover .strct-dg__cell--sticky{background:linear-gradient(var(--acc-s),var(--acc-s)) var(--bg-3)}.strct-dg-host--sticky tbody .strct-dg__row--selected .strct-dg__cell--sticky{background:linear-gradient(var(--acc-m),var(--acc-m)) var(--bg-1)}[data-theme=dark] .strct-dg-host--sticky tbody .strct-dg__row--selected .strct-dg__cell--sticky{background:linear-gradient(var(--acc-m),var(--acc-m)) var(--bg-3)}.strct-dg{width:100%;border-collapse:collapse;font-size:13px;overflow:hidden;background:var(--bg-2)}.strct-dg-host:not(:has(.strct-dg__toolbar)) .strct-dg{border-start-start-radius:4px;border-start-end-radius:4px}.strct-dg-host:not(:has(.strct-dg__foot)) .strct-dg{border-end-start-radius:4px;border-end-end-radius:4px}.strct-dg th,.strct-dg td{padding:9px 13px;text-align:start;border-bottom:1px solid var(--b1)}.strct-dg tbody td{background:var(--bg-1)}[data-theme=dark] .strct-dg tbody td{background:var(--bg-3)}.strct-dg-host--compact .strct-dg th,.strct-dg-host--compact .strct-dg td{padding:5px 11px}.strct-dg-host--singleline .strct-dg tbody tr:not(.strct-dg__detailrow)>td{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:360px}.strct-dg th{position:relative;font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:.4px;color:var(--t2);background:var(--bg-2);white-space:nowrap;-webkit-user-select:none;user-select:none}.strct-dg__th--sortable{cursor:pointer}.strct-dg__th--sortable:hover{color:var(--t1)}.strct-dg__sortbtn{display:inline-flex;align-items:center;gap:inherit;padding:0;border:0;background:none;color:inherit;font:inherit;text-transform:inherit;letter-spacing:inherit;text-align:inherit;cursor:pointer}.strct-dg__sortbtn:focus-visible{outline:2px solid var(--acc50);outline-offset:2px;border-radius:var(--radius-sm)}.strct-dg__th--sortable:focus-visible{outline:2px solid var(--acc50);outline-offset:-2px}.strct-dg__hd{display:inline-flex;align-items:center;gap:5px}.strct-dg__sorticon{color:var(--t3)}.strct-dg__th--sortable:hover .strct-dg__sorticon{color:var(--acc)}.strct-dg__resize{position:absolute;inset-inline-end:0;top:0;bottom:0;width:4px;cursor:col-resize;background:transparent;z-index:calc(var(--z-base) + 1)}.strct-dg__resize:hover{background:var(--acc)}.strct-dg td{color:var(--t1)}.strct-dg tbody tr:last-child td{border-bottom:0}.strct-dg tbody tr:not(.strct-dg__detailrow):hover td{background:linear-gradient(var(--acc-s),var(--acc-s)) var(--bg-1)}[data-theme=dark] .strct-dg tbody tr:not(.strct-dg__detailrow):hover td{background:linear-gradient(var(--acc-s),var(--acc-s)) var(--bg-3)}.strct-dg tbody .strct-dg__row--selected td{background:linear-gradient(var(--acc-m),var(--acc-m)) var(--bg-1)}[data-theme=dark] .strct-dg tbody .strct-dg__row--selected td{background:linear-gradient(var(--acc-m),var(--acc-m)) var(--bg-3)}.strct-dg tbody tr.strct-dg__row--selected:hover td{background:linear-gradient(var(--acc-s),var(--acc-s)),linear-gradient(var(--acc-m),var(--acc-m)) var(--bg-1)}[data-theme=dark] .strct-dg tbody tr.strct-dg__row--selected:hover td{background:linear-gradient(var(--acc-s),var(--acc-s)),linear-gradient(var(--acc-m),var(--acc-m)) var(--bg-3)}.strct-dg__sel{width:1%;white-space:nowrap}.strct-dg__sel .strct-cb,.strct-dg__sel .strct-dg__radio,.strct-dg__expandbtn{position:relative}.strct-dg__sel .strct-cb:after,.strct-dg__sel .strct-dg__radio:after,.strct-dg__expandbtn:after{content:\"\";position:absolute;inset:-11px -13px;min-width:24px;min-height:24px}.strct-dg-host--compact .strct-dg__sel .strct-cb:after,.strct-dg-host--compact .strct-dg__sel .strct-dg__radio:after,.strct-dg-host--compact .strct-dg__expandbtn:after{inset:-7px -11px}.strct-dg__sel input{accent-color:var(--acc);width:15px;height:15px;cursor:pointer}.strct-dg__expandcol,.strct-dg__expandcell{width:1%;white-space:nowrap}.strct-dg__actioncol,.strct-dg__actioncell{width:1%;white-space:nowrap;text-align:end}.strct-dg__kebab{display:inline-flex;padding:4px;border:0;border-radius:2px;background:transparent;color:var(--t3);cursor:pointer;transition:color .14s ease,background .14s ease}.strct-dg__kebab:hover{color:var(--t1);background:var(--bg-3)}.strct-dg__expandbtn{position:relative;display:inline-flex;align-items:center;justify-content:center;padding:3px;border:0;border-radius:2px;background:transparent;color:var(--t3);cursor:pointer;transition:transform .15s ease,color .15s ease}.strct-dg__expandbtn:hover{color:var(--t1);background:var(--bg-3)}.strct-dg__expandbtn--open{transform:rotate(90deg);color:var(--acc)}.strct-dg__detailbtn{display:inline-flex;padding:3px;border:0;border-radius:2px;background:transparent;color:var(--t3);cursor:pointer;transition:color .14s ease,background .14s ease}.strct-dg__detailbtn:hover{color:var(--acc);background:var(--bg-3)}.strct-dg__detailbtn--active{color:var(--acc);background:var(--acc-m)}.strct-dg__detailrow td{background:var(--bg-2);padding:0}.strct-dg__detail{padding:14px 16px;font-size:13px;color:var(--t2)}.strct-dg__layout{display:flex;align-items:flex-start;min-width:0}.strct-dg__layout--paned{gap:0}.strct-dg__layout--paned>.strct-dg__scroll{flex:0 0 auto;width:var(--strct-dg-pane-list-w, 260px);min-width:var(--strct-dg-pane-list-w, 260px);max-width:var(--strct-dg-pane-list-w, 260px)}.strct-dg__layout--paned>.strct-dg__scroll>.strct-dg{width:100%;min-width:0;max-width:none;flex-shrink:0;border-start-end-radius:0;border-end-end-radius:0}.strct-dg__layout--panetakeover>.strct-dg__scroll{display:none}.strct-dg__layout--panetakeover>.strct-dg__pane{flex:1;min-width:0}.strct-dg__pane-meta,.strct-dg__pane-actions{display:inline-flex;align-items:center;gap:var(--space-2)}.strct-dg__pane-meta:empty,.strct-dg__pane-actions:empty{display:none}.strct-dg__pane-actions{margin-inline-start:auto}.strct-dg__pane-back{display:inline-flex;align-items:center;border:0;background:none;color:var(--t2);cursor:pointer;padding:2px;border-radius:var(--radius-sm)}.strct-dg__row--clickable{cursor:pointer}.strct-dg tbody .strct-dg__row--active td{background:linear-gradient(var(--acc-m),var(--acc-m)) var(--bg-1)}[data-theme=dark] .strct-dg tbody .strct-dg__row--active td{background:linear-gradient(var(--acc-m),var(--acc-m)) var(--bg-3)}.strct-dg__layout--paned .strct-dg__row--active td:last-child{position:relative;padding-inline-end:26px}.strct-dg__layout--paned .strct-dg__row--active td:last-child:after{content:\"\";position:absolute;inset-inline-end:11px;top:50%;width:6px;height:6px;border-top:1.6px solid var(--acc);border-inline-end:1.6px solid var(--acc);transform:translateY(-50%) rotate(45deg)}.strct-dg__pane{flex:1;min-width:0;align-self:stretch;background:var(--bg-1);border:1px solid var(--b2);border-inline-start:2px solid var(--acc);border-radius:4px;border-start-start-radius:0;border-end-start-radius:0;overflow:hidden;animation:strct-dg-pane-in .14s ease}.strct-dg__pane-head{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:11px 14px;border-bottom:1px solid var(--b1);font-size:13px;font-weight:600;color:var(--t1)}.strct-dg__pane-close{display:inline-flex;padding:3px;border:0;border-radius:2px;background:transparent;color:var(--t3);cursor:pointer}.strct-dg__pane-close:hover{color:var(--t1);background:var(--bg-3)}.strct-dg__pane-body{padding:14px 16px;font-size:13px;color:var(--t2)}@keyframes strct-dg-pane-in{0%{opacity:0;transform:translate(8px)}}@keyframes strct-skeleton-pulse{0%,to{opacity:.4}50%{opacity:.7}}.strct-dg__skeleton-block{height:12px;border-radius:var(--radius-sm);background:linear-gradient(90deg,var(--bg-3) 25%,var(--skeleton-hi, var(--acc18)) 50%,var(--bg-3) 75%);background-size:200% 100%;animation:strct-dg-skeleton-sweep 1.1s linear infinite}@keyframes strct-dg-skeleton-sweep{0%{background-position:100% 0}to{background-position:-100% 0}}@media(prefers-reduced-motion:reduce){.strct-dg__skeleton-block{animation:none;background:var(--skeleton-hi, var(--acc18))}}.strct-dg__skeleton-row td{border-bottom:1px solid var(--b1)}.strct-dg__empty{text-align:center;color:var(--t2);padding:22px}.strct-dg__quickfilter{min-width:180px;max-width:260px}.strct-dg__quickfilter--end{order:2;margin-inline-start:auto}.strct-dg__filternote--end{order:3}.strct-dg__filternote{font-size:var(--text-sm);color:var(--t2);font-variant-numeric:tabular-nums;white-space:nowrap}.strct-dg__toolbar{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:9px 14px;border-bottom:1px solid var(--b2)}.strct-dg__foot{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:9px 14px;border-top:1px solid var(--b2);flex-wrap:wrap}.strct-dg__foot-left,.strct-dg__foot-right{display:flex;align-items:center;gap:12px}.strct-dg__actions{position:relative}.strct-dg__chooser-menu{position:absolute;bottom:calc(100% + 6px);inset-inline-start:0;z-index:var(--z-sticky);min-width:180px;background:var(--bg-1);border:1px solid var(--b2);border-radius:4px;box-shadow:var(--shadow-pop);padding:6px;display:flex;flex-direction:column;gap:2px}.strct-dg__chooser-menu--right{inset-inline-start:auto;inset-inline-end:0}.strct-dg__chooser-item{display:flex;align-items:center;gap:8px;padding:5px 8px;border-radius:3px;cursor:pointer;font-size:12px;color:var(--t1);transition:background .1s ease;-webkit-user-select:none;user-select:none}.strct-dg__chooser-item:hover{background:var(--bg-3)}.strct-dg__count{font-size:12px;color:var(--t2)}.strct-dg__count-sep{margin:0 8px;color:var(--b2)}.strct-dg__count-sel{color:var(--acc)}.strct-dg__filterbtn{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;margin-inline-start:2px;padding:0;border:0;border-radius:2px;background:transparent;color:var(--t3);cursor:pointer;vertical-align:middle}.strct-dg__filterbtn:hover{color:var(--t1);background:var(--bg-3)}.strct-dg__filterbtn--active{color:var(--acc)}.strct-dg__filterbtn:focus-visible{outline:2px solid var(--acc50);outline-offset:1px}.strct-dg__filterpanel{position:absolute;z-index:calc(var(--z-dropdown) + 10);display:flex;flex-direction:column;gap:6px;min-width:180px;padding:10px;background:var(--bg-1);border:1px solid var(--b2);border-radius:3px;box-shadow:var(--shh);font-weight:400;text-transform:none;letter-spacing:normal;white-space:nowrap}.strct-dg__filterinput{padding:5px 8px;border:1px solid var(--b2);border-radius:var(--radius-sm);background:var(--bg-2);color:var(--t1);font-family:var(--font);font-size:12px}.strct-dg__filterinput:focus-visible{outline:2px solid var(--acc50);outline-offset:1px}.strct-dg__filteropt{display:flex;align-items:center;gap:7px;font-size:12.5px;color:var(--t1);cursor:pointer}.strct-dg__filterclear{align-self:flex-start;padding:3px 8px;border:1px solid var(--b2);border-radius:var(--radius-sm);background:transparent;color:var(--t2);font-family:var(--font);font-size:var(--text-sm);cursor:pointer}.strct-dg__filterclear:hover:not(:disabled){color:var(--t1);background:var(--bg-3)}.strct-dg__filterclear:disabled{color:var(--t4);cursor:default}.strct-dg tbody tr[tabindex]:focus-visible{outline:2px solid var(--acc50);outline-offset:-2px}.strct-dg__treepad{display:inline-block;height:1px;vertical-align:middle}.strct-dg__treebtn{display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;margin-inline-end:4px;padding:0;border:0;border-radius:2px;background:transparent;color:var(--t3);cursor:pointer;vertical-align:middle}.strct-dg__treebtn:hover{color:var(--t1);background:var(--bg-3)}.strct-dg__treebtn:focus-visible{outline:2px solid var(--acc50);outline-offset:1px}.strct-dg__treebtn strct-icon{transition:transform .15s ease}.strct-dg__treebtn--open strct-icon{transform:rotate(90deg)}.strct-dg__treebtn--leaf{cursor:default;pointer-events:none}@media(prefers-reduced-motion:reduce){.strct-dg__treebtn strct-icon{transition:none}}.strct-dg__cell--editable{cursor:text;position:relative}.strct-dg__cell--editable:hover{box-shadow:inset 0 0 0 1px var(--b2)}.strct-dg__cell--editable:focus-visible{outline:none;box-shadow:inset 0 0 0 2px var(--acc50)}.strct-dg__editpencil{position:absolute;top:50%;inset-inline-end:6px;transform:translateY(-50%);color:var(--t3);opacity:0;pointer-events:none}td:hover>.strct-dg__editpencil,td:focus-within>.strct-dg__editpencil{opacity:1}.strct-dg__editinput{width:100%;box-sizing:border-box;padding:3px 6px;border:1px solid var(--acc);border-radius:var(--radius-sm);background:var(--bg-1);color:var(--t1);font:inherit}.strct-dg__editselect,.strct-dg__editnumber{display:block;width:100%}.strct-dg__editinput:focus-visible{outline:2px solid var(--acc50);outline-offset:1px}\n"], dependencies: [{ kind: "component", type: StrctIcon, selector: "strct-icon", inputs: ["name", "strictName", "size", "strokeWidth", "badge", "mode", "modeLabel", "ariaLabel", "count", "countMax", "countStatus", "countLabel"] }, { kind: "component", type: StrctPagination, selector: "strct-pagination", inputs: ["prevLabel", "nextLabel", "regionLabel", "pageLabel", "total", "pageSize", "page"], outputs: ["pageChange"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: StrctCheckbox, selector: "strct-checkbox", inputs: ["checked", "disabled", "indeterminate", "ariaLabel", "description", "tabIndex"], outputs: ["checkedChange"] }, { kind: "component", type: StrctButton, selector: "button[strct-button], a[strct-button]", inputs: ["variant", "size", "solid", "block", "iconOnly", "disabled", "disabledReason", "disabledReasonPosition"] }, { kind: "component", type: StrctButtonGroup, selector: "strct-button-group" }, { kind: "directive", type: StrctOverlay, selector: "[strctOverlay]", inputs: ["strctOverlay", "strctOverlayPlacement", "strctOverlayMatchWidth", "strctOverlayGap"] }, { kind: "component", type: StrctSearchbox, selector: "strct-searchbox", inputs: ["placeholder", "hint", "trigger", "clearable", "ariaLabel", "clearLabel", "value"], outputs: ["valueChange", "search", "activated"] }, { kind: "component", type: StrctSelect, selector: "strct-select", inputs: ["options", "placeholder", "ariaLabel", "listLabel", "emptyText", "disabled", "compareWith"] }, { kind: "component", type: StrctNumber, selector: "strct-number", inputs: ["min", "max", "step", "placeholder", "ariaLabel", "incrementLabel", "decrementLabel", "disabled"] }, { kind: "component", type: StrctSpinner, selector: "strct-spinner", inputs: ["size", "label", "caption"] }, { kind: "component", type: StrctEmptyState, selector: "strct-empty-state", inputs: ["variant", "icon", "tone", "title", "titleLevel", "size", "description"] }, { kind: "ngmodule", type: FormsModule }, { 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"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
18405
17822
  }
18406
17823
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: StrctDatagrid, decorators: [{
18407
17824
  type: Component,
@@ -18419,796 +17836,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImpor
18419
17836
  StrctSpinner,
18420
17837
  StrctEmptyState,
18421
17838
  FormsModule,
18422
- ], template: `
18423
- <!-- One polite region for everything the grid does to itself: sorting,
18424
- paging and filtering all change the rows without saying so. -->
18425
- <span class="strct-dg__announcer" role="status" aria-live="polite">{{ announcement() }}</span>
18426
- @if (actionBarDef() || quickFilterable() || actionBarCaptionDef()) {
18427
- <div class="strct-dg__toolbar">
18428
- @if (quickFilterable()) {
18429
- <strct-searchbox
18430
- class="strct-dg__quickfilter"
18431
- [class.strct-dg__quickfilter--end]="quickFilterAlign() === 'end'"
18432
- [placeholder]="L().quickFilter"
18433
- [ariaLabel]="L().quickFilter"
18434
- [value]="quickFilter()"
18435
- (valueChange)="quickFilter.set($event)"
18436
- />
18437
- @if (filterNote(); as note) {
18438
- <span
18439
- class="strct-dg__filternote"
18440
- [class.strct-dg__filternote--end]="quickFilterAlign() === 'end'"
18441
- >{{ note }}</span
18442
- >
18443
- }
18444
- }
18445
- <span class="strct-dg__actioncaption">
18446
- <ng-content select="[strctDatagridActionBarCaption]" />
18447
- </span>
18448
- <ng-content select="[strctDatagridActionBar]" />
18449
- </div>
18450
- }
18451
-
18452
- @if (hasEditableColumn()) {
18453
- <!-- What the editable cells point at with aria-describedby: a keyboard
18454
- user is told the cell can be edited, and how. -->
18455
- <!-- In flow, not absolutely positioned: an out-of-flow box whose
18456
- containing block is the page extends the document's scrollable area
18457
- from wherever the grid happens to sit. 1x1 and clipped is enough. -->
18458
- <span
18459
- class="strct-sr-only"
18460
- style="display: inline-block; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap"
18461
- [id]="editHintId"
18462
- >{{ L().editCellInstructions }}</span
18463
- >
18464
- }
18465
-
18466
- @if (bulkHint(); as hint) {
18467
- <!-- Selecting the page is not selecting the set. Say which one happened,
18468
- and offer the other. -->
18469
- <div class="strct-dg__bulk" role="status">
18470
- <span>{{ hint.text }}</span>
18471
- <button strct-button variant="flat" size="sm" (click)="hint.run()">
18472
- {{ hint.action }}
18473
- </button>
18474
- </div>
18475
- }
18476
- <div
18477
- class="strct-dg__layout"
18478
- [class.strct-dg__layout--paned]="paneOpen()"
18479
- [class.strct-dg__layout--panetakeover]="paneOpen() && narrowPane()"
18480
- [style.--strct-dg-pane-list-w]="paneListWidth()"
18481
- >
18482
- <div
18483
- class="strct-dg__scroll"
18484
- tabindex="0"
18485
- role="region"
18486
- [attr.aria-label]="L().rows"
18487
- [style.max-height.px]="virtual() ? viewportHeight() : maxHeight()"
18488
- (scroll)="virtual() && onScroll($event)"
18489
- >
18490
- <table
18491
- class="strct-dg"
18492
- [attr.role]="childrenKey() ? 'treegrid' : gridMode() ? 'grid' : null"
18493
- [attr.aria-rowcount]="ariaRowCount()"
18494
- [attr.aria-labelledby]="caption() ? captionId : null"
18495
- >
18496
- @if (caption()) {
18497
- <caption class="strct-dg__caption" [id]="captionId">
18498
- {{
18499
- caption()
18500
- }}
18501
- </caption>
18502
- }
18503
- <thead>
18504
- <tr>
18505
- <!-- A utility column is still a column: axe reports every empty
18506
- header, and a screen-reader user hears nothing where the
18507
- detail, expand and select controls are. The name is hidden,
18508
- not absent. -->
18509
- @if (canDetail()) {
18510
- <th
18511
- class="strct-dg__expandcol"
18512
- scope="col"
18513
- [class.strct-dg__cell--sticky]="stickyActive()"
18514
- [style.insetInlineStart.px]="utilLeft('detail')"
18515
- >
18516
- <span class="strct-sr-only">{{ L().detailColumn }}</span>
18517
- </th>
18518
- }
18519
- @if (canExpand()) {
18520
- <th
18521
- class="strct-dg__expandcol"
18522
- scope="col"
18523
- [class.strct-dg__cell--sticky]="stickyActive()"
18524
- [style.insetInlineStart.px]="utilLeft('expand')"
18525
- >
18526
- <span class="strct-sr-only">{{ L().expandColumn }}</span>
18527
- </th>
18528
- }
18529
- @if (hasSelectionColumn()) {
18530
- <th
18531
- class="strct-dg__sel"
18532
- scope="col"
18533
- [class.strct-dg__cell--sticky]="stickyActive()"
18534
- [style.insetInlineStart.px]="utilLeft('sel')"
18535
- >
18536
- @if (mode() === 'multiple') {
18537
- <strct-checkbox
18538
- [ariaLabel]="L().selectAll"
18539
- [checked]="allPageSelected()"
18540
- [indeterminate]="somePageSelected()"
18541
- (checkedChange)="toggleAll()"
18542
- />
18543
- } @else {
18544
- <span class="strct-sr-only">{{ L().selectColumn }}</span>
18545
- }
18546
- </th>
18547
- }
18548
- @for (col of visibleColumns(); track col.key) {
18549
- <th
18550
- scope="col"
18551
- [style.text-align]="col.align ?? (col.numeric ? 'end' : 'start')"
18552
- [style.width]="colWidth(col.key) ?? col.width ?? null"
18553
- [class.strct-dg__th--sortable]="col.sortable"
18554
- [class.strct-dg__th--drop]="col.key === dropKey()"
18555
- [class.strct-dg__cell--sticky]="isSticky(col)"
18556
- [class.strct-dg__cell--sticky-last]="col.key === lastStickyKey()"
18557
- [style.insetInlineStart.px]="stickyLeft(col.key)"
18558
- [attr.data-col]="col.key"
18559
- [attr.aria-sort]="col.sortable ? ariaSort(col.key) : null"
18560
- [attr.draggable]="reorderable() ? true : null"
18561
- (dragstart)="onColDragStart(col.key, $event)"
18562
- (dragover)="onColDragOver(col.key, $event)"
18563
- (drop)="onColDrop(col.key)"
18564
- (dragend)="onColDragEnd()"
18565
- >
18566
- <span class="strct-dg__hd">
18567
- <!-- A header that sorts is a button: a th with tabindex is
18568
- read as "column header, focusable" and never says it
18569
- can be activated. aria-sort stays on the th, which is
18570
- what carries the column's sorted state. -->
18571
- @if (col.sortable) {
18572
- <!-- The button covers most of the header, so it carries
18573
- the drag source too: a reorderable column has to be
18574
- grabbable where the pointer actually lands. The
18575
- event bubbles to the th's own handlers. -->
18576
- <button
18577
- type="button"
18578
- class="strct-dg__sortbtn"
18579
- [attr.draggable]="reorderable() ? true : null"
18580
- (click)="sortBy(col.key)"
18581
- >
18582
- {{ col.label }}
18583
- <strct-icon
18584
- class="strct-dg__sorticon"
18585
- [name]="sortIcon(col.key)"
18586
- [size]="13"
18587
- />
18588
- </button>
18589
- } @else {
18590
- {{ col.label }}
18591
- }
18592
- @if (col.filterable || col.filterOptions) {
18593
- <button
18594
- #fbtn
18595
- type="button"
18596
- class="strct-dg__filterbtn"
18597
- [class.strct-dg__filterbtn--active]="hasFilter(col.key)"
18598
- [attr.aria-expanded]="filterOpenKey() === col.key"
18599
- [attr.aria-label]="L().filterColumn + ': ' + col.label"
18600
- (click)="toggleFilterPanel(col.key, $event)"
18601
- (keydown.enter)="$event.stopPropagation()"
18602
- (keydown.space)="$event.stopPropagation()"
18603
- >
18604
- <strct-icon name="filter" [size]="12" />
18605
- </button>
18606
- @if (filterOpenKey() === col.key) {
18607
- <div
18608
- class="strct-dg__filterpanel"
18609
- role="dialog"
18610
- [attr.aria-label]="L().filterColumn + ': ' + col.label"
18611
- [strctOverlay]="fbtn"
18612
- strctOverlayPlacement="bottom-start"
18613
- (click)="$event.stopPropagation()"
18614
- (keydown.enter)="$event.stopPropagation()"
18615
- (keydown.space)="$event.stopPropagation()"
18616
- >
18617
- @if (col.filterOptions; as opts) {
18618
- @for (opt of opts; track $index) {
18619
- <label class="strct-dg__filteropt">
18620
- <input
18621
- type="checkbox"
18622
- [checked]="optionChecked(col.key, opt)"
18623
- (change)="toggleFilterOption(col.key, opt)"
18624
- />
18625
- <span>{{ opt }}</span>
18626
- </label>
18627
- }
18628
- } @else {
18629
- <input
18630
- class="strct-dg__filterinput"
18631
- type="text"
18632
- [attr.placeholder]="L().filterPlaceholder"
18633
- [attr.aria-label]="L().filterColumn + ': ' + col.label"
18634
- [value]="textFilter(col.key)"
18635
- (input)="setTextFilter(col.key, $any($event.target).value)"
18636
- />
18637
- }
18638
- <button
18639
- type="button"
18640
- class="strct-dg__filterclear"
18641
- [disabled]="!hasFilter(col.key)"
18642
- (click)="clearFilter(col.key)"
18643
- >
18644
- {{ L().clearFilter }}
18645
- </button>
18646
- </div>
18647
- }
18648
- }
18649
- </span>
18650
- @if (resizable() && col.resizable !== false) {
18651
- <!-- A separator, not a bare div: a column could only be
18652
- resized with a mouse, which is the one input a
18653
- console cannot assume. -->
18654
- <div
18655
- class="strct-dg__resize"
18656
- role="separator"
18657
- aria-orientation="vertical"
18658
- tabindex="0"
18659
- [attr.aria-label]="L().resizeColumn(col.label)"
18660
- [attr.aria-valuenow]="separatorValue(col.key)"
18661
- [attr.aria-valuemin]="40"
18662
- (mousedown)="onResizeStart($event, col.key)"
18663
- (keydown)="onResizeKey($event, col.key)"
18664
- (dblclick)="resetWidth(col.key)"
18665
- ></div>
18666
- }
18667
- </th>
18668
- }
18669
- @if (canActions()) {
18670
- <th class="strct-dg__actioncol" scope="col" [attr.aria-label]="L().actions"></th>
18671
- }
18672
- </tr>
18673
- </thead>
18674
- <!--
18675
- focusin is the real keyboard path (focus does not bubble from cells);
18676
- the plain focus binding exists to satisfy mouse-events-have-key-events.
18677
- -->
18678
- <tbody
18679
- (mouseover)="revealIfClipped($event)"
18680
- (focusin)="revealIfClipped($event)"
18681
- (focus)="revealIfClipped($event)"
18682
- >
18683
- @if (loading()) {
18684
- @for (_ of [1, 2, 3, 4, 5]; track $index) {
18685
- <tr class="strct-dg__skeleton-row">
18686
- @if (canDetail()) {
18687
- <td class="strct-dg__expandcell"></td>
18688
- }
18689
- @if (canExpand()) {
18690
- <td class="strct-dg__expandcell"></td>
18691
- }
18692
- @if (hasSelectionColumn()) {
18693
- <td class="strct-dg__sel"></td>
18694
- }
18695
- @for (col of visibleColumns(); track col.key) {
18696
- <td [style.text-align]="col.align ?? 'start'">
18697
- <div class="strct-dg__skeleton-block"></div>
18698
- </td>
18699
- }
18700
- @if (canActions()) {
18701
- <td class="strct-dg__actioncell"></td>
18702
- }
18703
- </tr>
18704
- }
18705
- } @else {
18706
- @if (virtual() && topPad() > 0) {
18707
- <tr class="strct-dg__vspacer" aria-hidden="true">
18708
- <td [attr.colspan]="colspan()" [style.height.px]="topPad()"></td>
18709
- </tr>
18710
- }
18711
- @for (it of displayItems(); track itemKey(it)) {
18712
- @if (it.group; as grp) {
18713
- <!-- Group header: distinct value + count, collapsible. -->
18714
- <tr class="strct-dg__grouprow">
18715
- @if (groupSelect() && mode() === 'multiple') {
18716
- <td class="strct-dg__sel">
18717
- <strct-checkbox
18718
- [ariaLabel]="L().selectAllInGroup(grp.label)"
18719
- [checked]="groupAllSelected(grp.key)"
18720
- [indeterminate]="groupSomeSelected(grp.key)"
18721
- (checkedChange)="toggleGroupSelection(grp.key)"
18722
- />
18723
- </td>
18724
- }
18725
- <td
18726
- [attr.colspan]="
18727
- groupSelect() && mode() === 'multiple' ? colspan() - 1 : colspan()
18728
- "
18729
- >
18730
- <button
18731
- type="button"
18732
- class="strct-dg__groupbtn"
18733
- [attr.aria-expanded]="!grp.collapsed"
18734
- (click)="toggleGroup(grp.key)"
18735
- >
18736
- <span
18737
- class="strct-dg__groupchev"
18738
- [class.strct-dg__groupchev--open]="!grp.collapsed"
18739
- >
18740
- <strct-icon name="chevronRight" [size]="12" [strokeWidth]="1.7" />
18741
- </span>
18742
- @if (groupHeaderDef(); as tpl) {
18743
- <!-- The consumer draws the row; the button, its
18744
- aria-expanded and the keyboard stay ours. -->
18745
- <ng-container
18746
- [ngTemplateOutlet]="tpl.template"
18747
- [ngTemplateOutletContext]="{
18748
- $implicit: grp,
18749
- group: grp,
18750
- count: grp.count,
18751
- }"
18752
- />
18753
- } @else {
18754
- <span
18755
- class="strct-dg__grouplabel strct-dg__grouplabel--{{
18756
- grp.status ?? 'none'
18757
- }}"
18758
- >{{ grp.label }}</span
18759
- >
18760
- <!-- The three spans run together in the button's
18761
- name ("Needs your decision1Blocks the join…");
18762
- the separators are read, not seen. -->
18763
- <span class="strct-dg__announcer">, </span>
18764
- <span
18765
- class="strct-dg__groupcount strct-dg__groupcount--{{
18766
- grp.status ?? 'none'
18767
- }}"
18768
- >{{ grp.count }}</span
18769
- >
18770
- <!-- Seen: the chip's number. Heard: the number with
18771
- the grid's noun, because "Inconclusive, 3" does
18772
- not say three of what. -->
18773
- <span class="strct-dg__announcer"> {{ countNoun(grp.count) }}</span>
18774
- @if (grp.note) {
18775
- <span class="strct-dg__announcer">. </span>
18776
- <span class="strct-dg__groupnote">{{ grp.note }}</span>
18777
- }
18778
- }
18779
- </button>
18780
- </td>
18781
- </tr>
18782
- } @else {
18783
- @let row = it.row!;
18784
- <tr
18785
- [attr.aria-rowindex]="ariaRowIndex(row)"
18786
- [class.strct-dg__row--selected]="isSelected(row)"
18787
- [class.strct-dg__row--locked]="lockedReason(row) !== null"
18788
- [attr.aria-selected]="mode() === 'none' ? null : isSelected(row)"
18789
- [attr.aria-disabled]="lockedReason(row) !== null ? 'true' : null"
18790
- [attr.title]="lockedReason(row) || null"
18791
- (click)="onRowPick(row)"
18792
- [class.strct-dg__row--active]="paneOpen() && row === activeRow()"
18793
- [attr.aria-level]="childrenKey() ? treeDepth(row) + 1 : null"
18794
- [attr.aria-expanded]="
18795
- childrenKey() && treeHasChildren(row) ? treeExpanded(row) : null
18796
- "
18797
- [attr.tabindex]="
18798
- childrenKey()
18799
- ? treeRowTabindex(row)
18800
- : gridMode()
18801
- ? gridRowTabindex(row)
18802
- : canDetail()
18803
- ? paneRowTabindex(row)
18804
- : null
18805
- "
18806
- (keydown)="
18807
- childrenKey()
18808
- ? onTreeRowKeydown(row, $event)
18809
- : gridMode()
18810
- ? onGridRowKeydown(row, $event)
18811
- : onPaneRowKeydown(row, $event)
18812
- "
18813
- (dblclick)="onRowActivate(row)"
18814
- >
18815
- @if (canDetail()) {
18816
- <td
18817
- class="strct-dg__expandcell"
18818
- [class.strct-dg__cell--sticky]="stickyActive()"
18819
- [style.insetInlineStart.px]="utilLeft('detail')"
18820
- >
18821
- <button
18822
- type="button"
18823
- [attr.tabindex]="ctlTabindex()"
18824
- class="strct-dg__detailbtn"
18825
- [class.strct-dg__detailbtn--active]="row === activeRow()"
18826
- [attr.aria-expanded]="row === activeRow()"
18827
- [attr.aria-label]="L().openDetail"
18828
- (click)="openDetail(row)"
18829
- >
18830
- <strct-icon name="chevronDoubleRight" [size]="13" [strokeWidth]="1.6" />
18831
- </button>
18832
- </td>
18833
- }
18834
- @if (canExpand()) {
18835
- <td
18836
- class="strct-dg__expandcell"
18837
- [class.strct-dg__cell--sticky]="stickyActive()"
18838
- [style.insetInlineStart.px]="utilLeft('expand')"
18839
- >
18840
- <button
18841
- type="button"
18842
- [attr.tabindex]="ctlTabindex()"
18843
- class="strct-dg__expandbtn"
18844
- [class.strct-dg__expandbtn--open]="isExpanded(row)"
18845
- [attr.aria-expanded]="isExpanded(row)"
18846
- [attr.aria-label]="L().toggleDetail"
18847
- (click)="toggleExpand(row)"
18848
- >
18849
- <strct-icon name="chevronRight" [size]="12" [strokeWidth]="1.7" />
18850
- </button>
18851
- </td>
18852
- }
18853
- @if (hasSelectionColumn()) {
18854
- <td
18855
- class="strct-dg__sel"
18856
- [class.strct-dg__cell--sticky]="stickyActive()"
18857
- [style.insetInlineStart.px]="utilLeft('sel')"
18858
- >
18859
- @if (mode() === 'single') {
18860
- <!-- A native radio group: one name per grid, so the
18861
- arrow keys move between rows and Space picks,
18862
- without the grid inventing its own keyboarding. -->
18863
- <input
18864
- type="radio"
18865
- class="strct-dg__radio"
18866
- [name]="radioName"
18867
- [attr.aria-label]="selectRowLabel(row)"
18868
- [attr.aria-description]="lockedReason(row) || null"
18869
- [checked]="isSelected(row)"
18870
- [disabled]="lockedReason(row) !== null"
18871
- [attr.tabindex]="ctlTabindex()"
18872
- (change)="selectSingle(row)"
18873
- (click)="$event.stopPropagation()"
18874
- />
18875
- } @else {
18876
- <!-- click fires (and bubbles) before the input's change,
18877
- so the modifier is recorded by the time toggleRow runs. -->
18878
- <strct-checkbox
18879
- [ariaLabel]="selectRowLabel(row)"
18880
- [attr.aria-description]="lockedReason(row) || null"
18881
- [checked]="isSelected(row)"
18882
- [disabled]="lockedReason(row) !== null"
18883
- [tabIndex]="ctlTabindex()"
18884
- (click)="noteRangeIntent($event); $event.stopPropagation()"
18885
- (checkedChange)="toggleRow(row)"
18886
- />
18887
- }
18888
- </td>
18889
- }
18890
- @for (col of visibleColumns(); track col.key; let colIdx = $index) {
18891
- <td
18892
- [style.text-align]="col.align ?? (col.numeric ? 'end' : 'start')"
18893
- [class.strct-dg__cell--mono]="col.mono"
18894
- [class.strct-dg__cell--muted]="col.muted"
18895
- [class.strct-dg__cell--numeric]="col.numeric"
18896
- [class.strct-dg__cell--sticky]="isSticky(col)"
18897
- [class.strct-dg__cell--sticky-last]="col.key === lastStickyKey()"
18898
- [class.strct-dg__cell--editable]="col.editable"
18899
- [style.insetInlineStart.px]="stickyLeft(col.key)"
18900
- [attr.data-edit]="col.editable ? '' : null"
18901
- [attr.tabindex]="col.editable ? editCellTabindex(row, col) : null"
18902
- [attr.aria-describedby]="col.editable ? editHintId : null"
18903
- (dblclick)="col.editable && startEdit(row, col)"
18904
- (focus)="col.editable && onEditCellFocus(row, col)"
18905
- (keydown)="col.editable && onEditCellKeydown(row, col, $event)"
18906
- >
18907
- @if (childrenKey() && colIdx === 0) {
18908
- <span
18909
- class="strct-dg__treepad"
18910
- [style.width.px]="treeDepth(row) * 18"
18911
- aria-hidden="true"
18912
- ></span>
18913
- @if (treeHasChildren(row)) {
18914
- <button
18915
- type="button"
18916
- class="strct-dg__treebtn"
18917
- [class.strct-dg__treebtn--open]="treeExpanded(row)"
18918
- [attr.aria-expanded]="treeExpanded(row)"
18919
- [attr.aria-label]="L().toggleChildren"
18920
- (click)="toggleTreeRow(row)"
18921
- >
18922
- <strct-icon name="chevronRight" [size]="12" [strokeWidth]="1.7" />
18923
- </button>
18924
- } @else {
18925
- <span
18926
- class="strct-dg__treebtn strct-dg__treebtn--leaf"
18927
- aria-hidden="true"
18928
- ></span>
18929
- }
18930
- }
18931
- @if (isEditingCell(row, col)) {
18932
- @switch (col.editor ?? 'text') {
18933
- @case ('select') {
18934
- <!-- Choosing is the commit: there is nothing to
18935
- confirm with Enter once an option is picked. -->
18936
- <strct-select
18937
- class="strct-dg__editselect"
18938
- [options]="editorOptionsFor(row, col)"
18939
- [listLabel]="L().editCell + ': ' + col.label"
18940
- [ngModel]="row[col.key]"
18941
- (ngModelChange)="commitValue(row, col, $event)"
18942
- (keydown.escape)="cancelEdit($event)"
18943
- (click)="$event.stopPropagation()"
18944
- />
18945
- }
18946
- @case ('number') {
18947
- <strct-number
18948
- class="strct-dg__editnumber"
18949
- [ariaLabel]="L().editCell + ': ' + col.label"
18950
- [min]="col.editorMin ?? null"
18951
- [max]="col.editorMax ?? null"
18952
- [step]="col.editorStep ?? 1"
18953
- [ngModel]="row[col.key]"
18954
- (ngModelChange)="editDraft.set($event)"
18955
- (keydown.enter)="commitDraft(row, col, $event)"
18956
- (keydown.escape)="cancelEdit($event)"
18957
- (focusout)="commitOnLeave(row, col, $event)"
18958
- (click)="$event.stopPropagation()"
18959
- />
18960
- }
18961
- @default {
18962
- <input
18963
- class="strct-dg__editinput"
18964
- type="text"
18965
- [value]="row[col.key] ?? ''"
18966
- [attr.aria-label]="L().editCell + ': ' + col.label"
18967
- (keydown.enter)="commitEdit(row, col, $event)"
18968
- (keydown.escape)="cancelEdit($event)"
18969
- (blur)="commitEdit(row, col, $event)"
18970
- (click)="$event.stopPropagation()"
18971
- />
18972
- }
18973
- }
18974
- } @else if (cellTemplate(col.key); as tpl) {
18975
- <ng-container
18976
- [ngTemplateOutlet]="tpl"
18977
- [ngTemplateOutletContext]="{
18978
- $implicit: row,
18979
- row,
18980
- value: row[col.key],
18981
- column: col,
18982
- }"
18983
- />
18984
- } @else if (isBlank(row, col)) {
18985
- <span
18986
- class="strct-dg__empty"
18987
- [attr.aria-label]="col.emptyLabel || null"
18988
- >{{ col.emptyText }}</span
18989
- >
18990
- } @else {
18991
- {{ displayValue(row, col) }}
18992
- @if (col.descriptionKey && row[col.descriptionKey]) {
18993
- <span class="strct-dg__celldesc">{{ row[col.descriptionKey] }}</span>
18994
- }
18995
- }
18996
- <!-- The affordance a mouse never needed: a cell that
18997
- can be edited says so on hover and on focus. -->
18998
- @if (col.editable && editHint() && !isEditingCell(row, col)) {
18999
- <strct-icon
19000
- class="strct-dg__editpencil"
19001
- name="pencil"
19002
- [size]="12"
19003
- [strokeWidth]="1.6"
19004
- aria-hidden="true"
19005
- />
19006
- }
19007
- </td>
19008
- }
19009
- @if (canActions()) {
19010
- <td class="strct-dg__actioncell">
19011
- <button
19012
- type="button"
19013
- [attr.tabindex]="ctlTabindex()"
19014
- class="strct-dg__kebab"
19015
- [attr.aria-label]="L().rowActions"
19016
- (click)="openRowMenu(row, $event)"
19017
- >
19018
- <strct-icon name="dots" [size]="16" />
19019
- </button>
19020
- </td>
19021
- }
19022
- </tr>
19023
- @if (canExpand() && isExpanded(row)) {
19024
- <tr class="strct-dg__detailrow">
19025
- <td [attr.colspan]="colspan()">
19026
- <div class="strct-dg__detail">
19027
- <ng-container
19028
- [ngTemplateOutlet]="detailDef()!.template"
19029
- [ngTemplateOutletContext]="{ $implicit: row, row }"
19030
- />
19031
- </div>
19032
- </td>
19033
- </tr>
19034
- }
19035
- }
19036
- } @empty {
19037
- <!-- "Nothing here" and "nothing matches what you typed" are
19038
- different states: the second one can be undone, and
19039
- saying so is the whole point. -->
19040
- <tr>
19041
- <td class="strct-dg__emptyrow" [attr.colspan]="colspan()">
19042
- @if (filteredAway()) {
19043
- <strct-empty-state
19044
- size="sm"
19045
- [titleLevel]="null"
19046
- [title]="L().noMatches"
19047
- [description]="noMatchesDescription()"
19048
- >
19049
- <button strct-button variant="flat" size="sm" (click)="clearAllFilters()">
19050
- {{ L().clearFilters }}
19051
- </button>
19052
- </strct-empty-state>
19053
- } @else {
19054
- <!-- Not a heading: a grid is a region of a page, and an
19055
- empty one used to put an <h3> in its outline. -->
19056
- <strct-empty-state size="sm" [titleLevel]="null" [title]="emptyText()" />
19057
- }
19058
- </td>
19059
- </tr>
19060
- }
19061
- @if (virtual() && bottomPad() > 0) {
19062
- <tr class="strct-dg__vspacer" aria-hidden="true">
19063
- <td [attr.colspan]="colspan()" [style.height.px]="bottomPad()"></td>
19064
- </tr>
19065
- }
19066
- }
19067
- </tbody>
19068
- </table>
19069
- </div>
19070
-
19071
- @if (paneOpen()) {
19072
- <aside class="strct-dg__pane">
19073
- <div class="strct-dg__pane-head">
19074
- @if (narrowPane()) {
19075
- <!-- The pane has taken the list's place, so there is a way back. -->
19076
- <button
19077
- type="button"
19078
- class="strct-dg__pane-back"
19079
- [attr.aria-label]="L().backToList"
19080
- (click)="closePane()"
19081
- >
19082
- <strct-icon name="chevronLeft" [size]="13" />
19083
- </button>
19084
- }
19085
- <span class="strct-dg__pane-title">
19086
- <ng-content select="[strctDetailPaneTitle]" />
19087
- @if (!paneTitleDef()) {
19088
- {{ activeRow()![columns()[0].key] }}
19089
- }
19090
- </span>
19091
- <span class="strct-dg__pane-meta">
19092
- <ng-content select="[strctDetailPaneMeta]" />
19093
- </span>
19094
- <span class="strct-dg__pane-actions">
19095
- <ng-content select="[strctDetailPaneActions]" />
19096
- </span>
19097
- <button
19098
- type="button"
19099
- class="strct-dg__pane-close"
19100
- [attr.aria-label]="L().closeDetail"
19101
- (click)="closePane()"
19102
- >
19103
- <strct-icon name="close" [size]="13" />
19104
- </button>
19105
- </div>
19106
- <div class="strct-dg__pane-body">
19107
- <ng-container
19108
- [ngTemplateOutlet]="detailDef()!.template"
19109
- [ngTemplateOutletContext]="{ $implicit: activeRow(), row: activeRow() }"
19110
- />
19111
- </div>
19112
- </aside>
19113
- }
19114
- </div>
19115
-
19116
- @if (showFooter()) {
19117
- <div class="strct-dg__foot">
19118
- <div class="strct-dg__foot-left">
19119
- @if (columnChooser() || sync()) {
19120
- <div class="strct-dg__actions">
19121
- <strct-button-group>
19122
- @if (columnChooser()) {
19123
- <button
19124
- strct-button
19125
- size="sm"
19126
- [class.is-open]="chooserOpen()"
19127
- [disabled]="footerActionsDisabled()"
19128
- [attr.aria-label]="L().chooseColumns"
19129
- (click)="toggleChooser($event)"
19130
- >
19131
- <strct-icon name="settings" [size]="16" />
19132
- </button>
19133
- }
19134
- @if (sync()) {
19135
- <button
19136
- strct-button
19137
- size="sm"
19138
- [disabled]="footerActionsDisabled()"
19139
- [attr.aria-label]="L().refresh"
19140
- (click)="syncChange.emit()"
19141
- >
19142
- <strct-icon name="refresh" [size]="16" />
19143
- </button>
19144
- }
19145
- </strct-button-group>
19146
- @if (columnChooser() && chooserOpen()) {
19147
- <div class="strct-dg__chooser-menu">
19148
- @for (col of columns(); track col.key) {
19149
- <!-- The whole row toggles; the checkbox's own click must not
19150
- reach the row or the column would flip twice. -->
19151
- <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->
19152
- <div
19153
- class="strct-dg__chooser-item"
19154
- (click)="toggleColumn(col.key, hiddenColumns().has(col.key))"
19155
- >
19156
- <strct-checkbox
19157
- [checked]="!hiddenColumns().has(col.key)"
19158
- (checkedChange)="toggleColumn(col.key, $event)"
19159
- (click)="$event.stopPropagation()"
19160
- />
19161
- <span>{{ col.label }}</span>
19162
- </div>
19163
- }
19164
- </div>
19165
- }
19166
- </div>
19167
- }
19168
- <span class="strct-dg__count">
19169
- @if (paging() === 'more') {
19170
- {{ L().showing(rows().length, moreTotal() ?? undefined) }}
19171
- } @else {
19172
- {{ totalCount() }} {{ totalCount() === 1 ? L().row : L().rows }}
19173
- }
19174
- @if (selectedCount() && mode() !== 'single') {
19175
- <span class="strct-dg__count-sep">|</span>
19176
- <span class="strct-dg__count-sel">{{ selectedCount() }} {{ L().selected }}</span>
19177
- }
19178
- </span>
19179
- </div>
19180
- <div class="strct-dg__foot-right">
19181
- <!-- A feed that pages by cursor loads more at the end: page numbers
19182
- are something a cursor API cannot answer. It takes the pager's
19183
- place — the column chooser, the sync button and the count are
19184
- the footer's, not the pager's. -->
19185
- @if (paging() === 'more') {
19186
- @if (hasMore()) {
19187
- <button
19188
- strct-button
19189
- size="sm"
19190
- class="strct-dg__more"
19191
- [disabled]="loadingMore()"
19192
- (click)="loadMore.emit()"
19193
- >
19194
- @if (loadingMore()) {
19195
- <strct-spinner size="sm" />
19196
- }
19197
- {{ L().loadMore }}
19198
- </button>
19199
- }
19200
- } @else if (pageSize() > 0 && !groupBy()) {
19201
- <strct-pagination
19202
- [total]="totalCount()"
19203
- [pageSize]="pageSize()"
19204
- [page]="page()"
19205
- (pageChange)="goToPage($event)"
19206
- />
19207
- }
19208
- </div>
19209
- </div>
19210
- }
19211
- `, host: {
17839
+ ], host: {
19212
17840
  class: 'strct-dg-host',
19213
17841
  // The skeleton rows say "updating" to the eye; this says it to everyone else.
19214
17842
  '[attr.aria-busy]': 'loading() || loadingMore() ? "true" : null',
@@ -19218,8 +17846,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImpor
19218
17846
  '[class.strct-dg-host--bounded]': '!virtual() && maxHeight() !== null',
19219
17847
  '[class.strct-dg-host--sticky]': 'stickyActive()',
19220
17848
  '[class.strct-dg-host--flush]': 'flush()',
19221
- }, styles: [".strct-dg__radio{width:15px;height:15px;accent-color:var(--acc);cursor:pointer}.strct-dg__radio:disabled{cursor:not-allowed}.strct-dg__row--locked .strct-dg__sel{opacity:.55}.strct-dg__actioncaption:empty{display:none}.strct-dg__actioncaption{display:inline-flex;align-items:center;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:var(--text-sm);color:var(--t2)}.strct-dg__bulk{display:flex;align-items:center;justify-content:center;flex-wrap:wrap;gap:4px 10px;padding:7px 12px;border-bottom:1px solid var(--b2);background:var(--acc-s);color:var(--t2);font-size:var(--text-md)}.strct-dg__announcer{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}.strct-dg-host{display:block;border:1px solid var(--b2);border-radius:var(--radius-lg);background:var(--bg-2);box-shadow:var(--shadow-rest)}.strct-dg-host--flush{border:0;border-radius:0;background:transparent;box-shadow:none}.strct-dg__more strct-spinner{margin-inline-end:4px}.strct-dg__caption{caption-side:top;text-align:start;padding:var(--space-2) var(--space-3);font-size:var(--text-sm);font-weight:600;color:var(--t2)}.strct-dg td.strct-dg__cell--mono{font-family:var(--mono);font-size:var(--text-sm)}.strct-dg td.strct-dg__cell--muted{color:var(--t2)}.strct-dg td.strct-dg__cell--numeric{font-variant-numeric:tabular-nums}.strct-dg__empty{color:var(--t2)}.strct-dg__celldesc{display:block;font-size:var(--text-sm);color:var(--t2);line-height:1.3}.strct-dg__scroll{flex:1 1 auto;min-width:0;overflow-x:auto;-webkit-overflow-scrolling:touch}.strct-dg-host--virtual .strct-dg__scroll,.strct-dg-host--bounded .strct-dg__scroll{overflow-y:auto}.strct-dg-host--virtual .strct-dg thead th,.strct-dg-host--bounded .strct-dg thead th{position:sticky;top:0;z-index:var(--z-raised)}.strct-dg__vspacer td{padding:0;border:0;background:transparent}.strct-dg th[draggable]{cursor:grab}.strct-dg__th--drop{box-shadow:inset 3px 0 0 var(--acc)}.strct-dg__grouprow td{background:var(--bg-2)!important;padding:0}.strct-dg__groupbtn{display:flex;align-items:center;gap:8px;width:100%;padding:7px 13px;border:0;background:transparent;color:var(--t1);font-family:var(--font);font-size:var(--text-sm);font-weight:600;cursor:pointer;text-align:start}.strct-dg__groupbtn:hover{background:var(--bg-3)}.strct-dg__groupbtn:focus-visible{outline:2px solid var(--acc50);outline-offset:-2px}.strct-dg__groupchev{display:inline-flex;color:var(--t3);transition:transform .15s ease}.strct-dg__groupchev--open{transform:rotate(90deg)}@media(prefers-reduced-motion:reduce){.strct-dg__groupchev{transition:none}}.strct-dg__grouplabel--critical{color:var(--critical)}.strct-dg__grouplabel--warning{color:var(--warning)}.strct-dg__grouplabel--success{color:var(--success)}.strct-dg__grouplabel--accent{color:var(--acc)}.strct-dg__grouplabel--neutral,.strct-dg__grouplabel--none{color:var(--t1)}.strct-dg__groupnote{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:var(--text-sm);font-weight:400;color:var(--t2)}.strct-dg__groupcount{min-width:18px;height:18px;padding:0 5px;display:inline-flex;align-items:center;justify-content:center;font-size:12px;border-radius:4px;background:var(--acc-m);color:var(--acc);font-variant-numeric:tabular-nums}.strct-dg__groupcount--critical{background:var(--critical-bg);color:var(--critical)}.strct-dg__groupcount--warning{background:var(--warning-bg);color:var(--warning)}.strct-dg__groupcount--success{background:var(--success-bg);color:var(--success)}.strct-dg__groupcount--neutral,.strct-dg__groupcount--none{background:var(--bg-3);color:var(--t2)}.strct-dg-host--singleline .strct-dg{border-collapse:separate;border-spacing:0}.strct-dg-host--sticky .strct-dg,.strct-dg-host--virtual .strct-dg,.strct-dg-host--bounded .strct-dg{border-collapse:separate;border-spacing:0;overflow:visible;border-radius:0}.strct-dg-host--sticky .strct-dg{width:max-content;min-width:100%}.strct-dg .strct-dg__cell--sticky{position:sticky;z-index:calc(var(--z-base) + 2)}.strct-dg thead th.strct-dg__cell--sticky{z-index:calc(var(--z-raised) + 1)}.strct-dg .strct-dg__cell--sticky-last:after{content:\"\";position:absolute;top:0;bottom:0;inset-inline-end:-1px;width:7px;pointer-events:none;background:linear-gradient(to right,rgba(0,0,0,.14),transparent)}[dir=rtl] .strct-dg .strct-dg__cell--sticky-last:after{background:linear-gradient(to left,rgba(0,0,0,.14),transparent)}.strct-dg-host--sticky .strct-dg__expandcol,.strct-dg-host--sticky .strct-dg__expandcell{width:36px;min-width:36px;max-width:36px;box-sizing:border-box}.strct-dg-host--sticky .strct-dg__sel{width:40px;min-width:40px;max-width:40px;box-sizing:border-box}.strct-dg-host--sticky tbody .strct-dg__cell--sticky{background:var(--bg-1)}[data-theme=dark] .strct-dg-host--sticky tbody .strct-dg__cell--sticky{background:var(--bg-3)}.strct-dg-host--sticky tbody tr:hover .strct-dg__cell--sticky{background:linear-gradient(var(--acc-s),var(--acc-s)) var(--bg-1)}[data-theme=dark] .strct-dg-host--sticky tbody tr:hover .strct-dg__cell--sticky{background:linear-gradient(var(--acc-s),var(--acc-s)) var(--bg-3)}.strct-dg-host--sticky tbody .strct-dg__row--selected .strct-dg__cell--sticky{background:linear-gradient(var(--acc-m),var(--acc-m)) var(--bg-1)}[data-theme=dark] .strct-dg-host--sticky tbody .strct-dg__row--selected .strct-dg__cell--sticky{background:linear-gradient(var(--acc-m),var(--acc-m)) var(--bg-3)}.strct-dg{width:100%;border-collapse:collapse;font-size:13px;overflow:hidden;background:var(--bg-2)}.strct-dg-host:not(:has(.strct-dg__toolbar)) .strct-dg{border-start-start-radius:4px;border-start-end-radius:4px}.strct-dg-host:not(:has(.strct-dg__foot)) .strct-dg{border-end-start-radius:4px;border-end-end-radius:4px}.strct-dg th,.strct-dg td{padding:9px 13px;text-align:start;border-bottom:1px solid var(--b1)}.strct-dg tbody td{background:var(--bg-1)}[data-theme=dark] .strct-dg tbody td{background:var(--bg-3)}.strct-dg-host--compact .strct-dg th,.strct-dg-host--compact .strct-dg td{padding:5px 11px}.strct-dg-host--singleline .strct-dg tbody tr:not(.strct-dg__detailrow)>td{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:360px}.strct-dg th{position:relative;font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:.4px;color:var(--t2);background:var(--bg-2);white-space:nowrap;-webkit-user-select:none;user-select:none}.strct-dg__th--sortable{cursor:pointer}.strct-dg__th--sortable:hover{color:var(--t1)}.strct-dg__sortbtn{display:inline-flex;align-items:center;gap:inherit;padding:0;border:0;background:none;color:inherit;font:inherit;text-transform:inherit;letter-spacing:inherit;text-align:inherit;cursor:pointer}.strct-dg__sortbtn:focus-visible{outline:2px solid var(--acc50);outline-offset:2px;border-radius:var(--radius-sm)}.strct-dg__th--sortable:focus-visible{outline:2px solid var(--acc50);outline-offset:-2px}.strct-dg__hd{display:inline-flex;align-items:center;gap:5px}.strct-dg__sorticon{color:var(--t3)}.strct-dg__th--sortable:hover .strct-dg__sorticon{color:var(--acc)}.strct-dg__resize{position:absolute;inset-inline-end:0;top:0;bottom:0;width:4px;cursor:col-resize;background:transparent;z-index:calc(var(--z-base) + 1)}.strct-dg__resize:hover{background:var(--acc)}.strct-dg td{color:var(--t1)}.strct-dg tbody tr:last-child td{border-bottom:0}.strct-dg tbody tr:not(.strct-dg__detailrow):hover td{background:linear-gradient(var(--acc-s),var(--acc-s)) var(--bg-1)}[data-theme=dark] .strct-dg tbody tr:not(.strct-dg__detailrow):hover td{background:linear-gradient(var(--acc-s),var(--acc-s)) var(--bg-3)}.strct-dg tbody .strct-dg__row--selected td{background:linear-gradient(var(--acc-m),var(--acc-m)) var(--bg-1)}[data-theme=dark] .strct-dg tbody .strct-dg__row--selected td{background:linear-gradient(var(--acc-m),var(--acc-m)) var(--bg-3)}.strct-dg tbody tr.strct-dg__row--selected:hover td{background:linear-gradient(var(--acc-s),var(--acc-s)),linear-gradient(var(--acc-m),var(--acc-m)) var(--bg-1)}[data-theme=dark] .strct-dg tbody tr.strct-dg__row--selected:hover td{background:linear-gradient(var(--acc-s),var(--acc-s)),linear-gradient(var(--acc-m),var(--acc-m)) var(--bg-3)}.strct-dg__sel{width:1%;white-space:nowrap}.strct-dg__sel .strct-cb,.strct-dg__sel .strct-dg__radio,.strct-dg__expandbtn{position:relative}.strct-dg__sel .strct-cb:after,.strct-dg__sel .strct-dg__radio:after,.strct-dg__expandbtn:after{content:\"\";position:absolute;inset:-11px -13px;min-width:24px;min-height:24px}.strct-dg-host--compact .strct-dg__sel .strct-cb:after,.strct-dg-host--compact .strct-dg__sel .strct-dg__radio:after,.strct-dg-host--compact .strct-dg__expandbtn:after{inset:-7px -11px}.strct-dg__sel input{accent-color:var(--acc);width:15px;height:15px;cursor:pointer}.strct-dg__expandcol,.strct-dg__expandcell{width:1%;white-space:nowrap}.strct-dg__actioncol,.strct-dg__actioncell{width:1%;white-space:nowrap;text-align:end}.strct-dg__kebab{display:inline-flex;padding:4px;border:0;border-radius:2px;background:transparent;color:var(--t3);cursor:pointer;transition:color .14s ease,background .14s ease}.strct-dg__kebab:hover{color:var(--t1);background:var(--bg-3)}.strct-dg__expandbtn{position:relative;display:inline-flex;align-items:center;justify-content:center;padding:3px;border:0;border-radius:2px;background:transparent;color:var(--t3);cursor:pointer;transition:transform .15s ease,color .15s ease}.strct-dg__expandbtn:hover{color:var(--t1);background:var(--bg-3)}.strct-dg__expandbtn--open{transform:rotate(90deg);color:var(--acc)}.strct-dg__detailbtn{display:inline-flex;padding:3px;border:0;border-radius:2px;background:transparent;color:var(--t3);cursor:pointer;transition:color .14s ease,background .14s ease}.strct-dg__detailbtn:hover{color:var(--acc);background:var(--bg-3)}.strct-dg__detailbtn--active{color:var(--acc);background:var(--acc-m)}.strct-dg__detailrow td{background:var(--bg-2);padding:0}.strct-dg__detail{padding:14px 16px;font-size:13px;color:var(--t2)}.strct-dg__layout{display:flex;align-items:flex-start;min-width:0}.strct-dg__layout--paned{gap:0}.strct-dg__layout--paned>.strct-dg__scroll{flex:0 0 auto;width:var(--strct-dg-pane-list-w, 260px);min-width:var(--strct-dg-pane-list-w, 260px);max-width:var(--strct-dg-pane-list-w, 260px)}.strct-dg__layout--paned>.strct-dg__scroll>.strct-dg{width:100%;min-width:0;max-width:none;flex-shrink:0;border-start-end-radius:0;border-end-end-radius:0}.strct-dg__layout--panetakeover>.strct-dg__scroll{display:none}.strct-dg__layout--panetakeover>.strct-dg__pane{flex:1;min-width:0}.strct-dg__pane-meta,.strct-dg__pane-actions{display:inline-flex;align-items:center;gap:var(--space-2)}.strct-dg__pane-meta:empty,.strct-dg__pane-actions:empty{display:none}.strct-dg__pane-actions{margin-inline-start:auto}.strct-dg__pane-back{display:inline-flex;align-items:center;border:0;background:none;color:var(--t2);cursor:pointer;padding:2px;border-radius:var(--radius-sm)}.strct-dg__row--clickable{cursor:pointer}.strct-dg tbody .strct-dg__row--active td{background:linear-gradient(var(--acc-m),var(--acc-m)) var(--bg-1)}[data-theme=dark] .strct-dg tbody .strct-dg__row--active td{background:linear-gradient(var(--acc-m),var(--acc-m)) var(--bg-3)}.strct-dg__layout--paned .strct-dg__row--active td:last-child{position:relative;padding-inline-end:26px}.strct-dg__layout--paned .strct-dg__row--active td:last-child:after{content:\"\";position:absolute;inset-inline-end:11px;top:50%;width:6px;height:6px;border-top:1.6px solid var(--acc);border-inline-end:1.6px solid var(--acc);transform:translateY(-50%) rotate(45deg)}.strct-dg__pane{flex:1;min-width:0;align-self:stretch;background:var(--bg-1);border:1px solid var(--b2);border-inline-start:2px solid var(--acc);border-radius:4px;border-start-start-radius:0;border-end-start-radius:0;overflow:hidden;animation:strct-dg-pane-in .14s ease}.strct-dg__pane-head{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:11px 14px;border-bottom:1px solid var(--b1);font-size:13px;font-weight:600;color:var(--t1)}.strct-dg__pane-close{display:inline-flex;padding:3px;border:0;border-radius:2px;background:transparent;color:var(--t3);cursor:pointer}.strct-dg__pane-close:hover{color:var(--t1);background:var(--bg-3)}.strct-dg__pane-body{padding:14px 16px;font-size:13px;color:var(--t2)}@keyframes strct-dg-pane-in{0%{opacity:0;transform:translate(8px)}}@keyframes strct-skeleton-pulse{0%,to{opacity:.4}50%{opacity:.7}}.strct-dg__skeleton-block{height:12px;border-radius:var(--radius-sm);background:linear-gradient(90deg,var(--bg-3) 25%,var(--skeleton-hi, var(--acc18)) 50%,var(--bg-3) 75%);background-size:200% 100%;animation:strct-dg-skeleton-sweep 1.1s linear infinite}@keyframes strct-dg-skeleton-sweep{0%{background-position:100% 0}to{background-position:-100% 0}}@media(prefers-reduced-motion:reduce){.strct-dg__skeleton-block{animation:none;background:var(--skeleton-hi, var(--acc18))}}.strct-dg__skeleton-row td{border-bottom:1px solid var(--b1)}.strct-dg__empty{text-align:center;color:var(--t2);padding:22px}.strct-dg__quickfilter{min-width:180px;max-width:260px}.strct-dg__quickfilter--end{order:2;margin-inline-start:auto}.strct-dg__filternote--end{order:3}.strct-dg__filternote{font-size:var(--text-sm);color:var(--t2);font-variant-numeric:tabular-nums;white-space:nowrap}.strct-dg__toolbar{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:9px 14px;border-bottom:1px solid var(--b2)}.strct-dg__foot{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:9px 14px;border-top:1px solid var(--b2);flex-wrap:wrap}.strct-dg__foot-left,.strct-dg__foot-right{display:flex;align-items:center;gap:12px}.strct-dg__actions{position:relative}.strct-dg__chooser-menu{position:absolute;bottom:calc(100% + 6px);inset-inline-start:0;z-index:var(--z-sticky);min-width:180px;background:var(--bg-1);border:1px solid var(--b2);border-radius:4px;box-shadow:var(--shadow-pop);padding:6px;display:flex;flex-direction:column;gap:2px}.strct-dg__chooser-menu--right{inset-inline-start:auto;inset-inline-end:0}.strct-dg__chooser-item{display:flex;align-items:center;gap:8px;padding:5px 8px;border-radius:3px;cursor:pointer;font-size:12px;color:var(--t1);transition:background .1s ease;-webkit-user-select:none;user-select:none}.strct-dg__chooser-item:hover{background:var(--bg-3)}.strct-dg__count{font-size:12px;color:var(--t2)}.strct-dg__count-sep{margin:0 8px;color:var(--b2)}.strct-dg__count-sel{color:var(--acc)}.strct-dg__filterbtn{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;margin-inline-start:2px;padding:0;border:0;border-radius:2px;background:transparent;color:var(--t3);cursor:pointer;vertical-align:middle}.strct-dg__filterbtn:hover{color:var(--t1);background:var(--bg-3)}.strct-dg__filterbtn--active{color:var(--acc)}.strct-dg__filterbtn:focus-visible{outline:2px solid var(--acc50);outline-offset:1px}.strct-dg__filterpanel{position:absolute;z-index:calc(var(--z-dropdown) + 10);display:flex;flex-direction:column;gap:6px;min-width:180px;padding:10px;background:var(--bg-1);border:1px solid var(--b2);border-radius:3px;box-shadow:var(--shh);font-weight:400;text-transform:none;letter-spacing:normal;white-space:nowrap}.strct-dg__filterinput{padding:5px 8px;border:1px solid var(--b2);border-radius:var(--radius-sm);background:var(--bg-2);color:var(--t1);font-family:var(--font);font-size:12px}.strct-dg__filterinput:focus-visible{outline:2px solid var(--acc50);outline-offset:1px}.strct-dg__filteropt{display:flex;align-items:center;gap:7px;font-size:12.5px;color:var(--t1);cursor:pointer}.strct-dg__filterclear{align-self:flex-start;padding:3px 8px;border:1px solid var(--b2);border-radius:var(--radius-sm);background:transparent;color:var(--t2);font-family:var(--font);font-size:var(--text-sm);cursor:pointer}.strct-dg__filterclear:hover:not(:disabled){color:var(--t1);background:var(--bg-3)}.strct-dg__filterclear:disabled{color:var(--t4);cursor:default}.strct-dg tbody tr[tabindex]:focus-visible{outline:2px solid var(--acc50);outline-offset:-2px}.strct-dg__treepad{display:inline-block;height:1px;vertical-align:middle}.strct-dg__treebtn{display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;margin-inline-end:4px;padding:0;border:0;border-radius:2px;background:transparent;color:var(--t3);cursor:pointer;vertical-align:middle}.strct-dg__treebtn:hover{color:var(--t1);background:var(--bg-3)}.strct-dg__treebtn:focus-visible{outline:2px solid var(--acc50);outline-offset:1px}.strct-dg__treebtn strct-icon{transition:transform .15s ease}.strct-dg__treebtn--open strct-icon{transform:rotate(90deg)}.strct-dg__treebtn--leaf{cursor:default;pointer-events:none}@media(prefers-reduced-motion:reduce){.strct-dg__treebtn strct-icon{transition:none}}.strct-dg__cell--editable{cursor:text;position:relative}.strct-dg__cell--editable:hover{box-shadow:inset 0 0 0 1px var(--b2)}.strct-dg__cell--editable:focus-visible{outline:none;box-shadow:inset 0 0 0 2px var(--acc50)}.strct-dg__editpencil{position:absolute;top:50%;inset-inline-end:6px;transform:translateY(-50%);color:var(--t3);opacity:0;pointer-events:none}td:hover>.strct-dg__editpencil,td:focus-within>.strct-dg__editpencil{opacity:1}.strct-dg__editinput{width:100%;box-sizing:border-box;padding:3px 6px;border:1px solid var(--acc);border-radius:var(--radius-sm);background:var(--bg-1);color:var(--t1);font:inherit}.strct-dg__editselect,.strct-dg__editnumber{display:block;width:100%}.strct-dg__editinput:focus-visible{outline:2px solid var(--acc50);outline-offset:1px}\n"] }]
19222
- }], ctorParameters: () => [], propDecorators: { columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: true }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: true }] }], pageSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSize", required: false }] }], selectable: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectable", required: false }] }], expandable: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandable", required: false }] }], detailPane: [{ type: i0.Input, args: [{ isSignal: true, alias: "detailPane", required: false }] }], paneListWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "paneListWidth", required: false }] }], paneTakeoverWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "paneTakeoverWidth", required: false }] }], activeRowId: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeRowId", required: false }] }, { type: i0.Output, args: ["activeRowIdChange"] }], compact: [{ type: i0.Input, args: [{ isSignal: true, alias: "compact", required: false }] }], singleLine: [{ type: i0.Input, args: [{ isSignal: true, alias: "singleLine", required: false }] }], emptyText: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyText", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], resizable: [{ type: i0.Input, args: [{ isSignal: true, alias: "resizable", required: false }] }], columnChooser: [{ type: i0.Input, args: [{ isSignal: true, alias: "columnChooser", required: false }] }], sync: [{ type: i0.Input, args: [{ isSignal: true, alias: "sync", required: false }] }], footerActionsDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "footerActionsDisabled", required: false }] }], rowId: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowId", required: false }] }], selectionMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectionMode", required: false }] }], selectedId: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedId", required: false }] }, { type: i0.Output, args: ["selectedIdChange"] }], rowSelectable: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowSelectable", required: false }] }], flush: [{ type: i0.Input, args: [{ isSignal: true, alias: "flush", required: false }] }], caption: [{ type: i0.Input, args: [{ isSignal: true, alias: "caption", required: false }] }], paging: [{ type: i0.Input, args: [{ isSignal: true, alias: "paging", required: false }] }], hasMore: [{ type: i0.Input, args: [{ isSignal: true, alias: "hasMore", required: false }] }], loadingMore: [{ type: i0.Input, args: [{ isSignal: true, alias: "loadingMore", required: false }] }], moreTotal: [{ type: i0.Input, args: [{ isSignal: true, alias: "moreTotal", required: false }] }], loadMore: [{ type: i0.Output, args: ["loadMore"] }], groupSelect: [{ type: i0.Input, args: [{ isSignal: true, alias: "groupSelect", required: false }] }], rowActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowActions", required: false }] }], initialSelection: [{ type: i0.Input, args: [{ isSignal: true, alias: "initialSelection", required: false }] }], selectedIds: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedIds", required: false }] }, { type: i0.Output, args: ["selectedIdsChange"] }], labels: [{ type: i0.Input, args: [{ isSignal: true, alias: "labels", required: false }] }], virtual: [{ type: i0.Input, args: [{ isSignal: true, alias: "virtual", required: false }] }], viewportHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "viewportHeight", required: false }] }], maxHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxHeight", required: false }] }], editHint: [{ type: i0.Input, args: [{ isSignal: true, alias: "editHint", required: false }] }], rowHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowHeight", required: false }] }], lazy: [{ type: i0.Input, args: [{ isSignal: true, alias: "lazy", required: false }] }], total: [{ type: i0.Input, args: [{ isSignal: true, alias: "total", required: false }] }], stateKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "stateKey", required: false }] }], columnState: [{ type: i0.Input, args: [{ isSignal: true, alias: "columnState", required: false }] }, { type: i0.Output, args: ["columnStateChange"] }], reorderable: [{ type: i0.Input, args: [{ isSignal: true, alias: "reorderable", required: false }] }], groupBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "groupBy", required: false }] }], groups: [{ type: i0.Input, args: [{ isSignal: true, alias: "groups", required: false }] }], showEmptyGroups: [{ type: i0.Input, args: [{ isSignal: true, alias: "showEmptyGroups", required: false }] }], collapsedGroups: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsedGroups", required: false }] }, { type: i0.Output, args: ["collapsedGroupsChange"] }], filters: [{ type: i0.Input, args: [{ isSignal: true, alias: "filters", required: false }] }, { type: i0.Output, args: ["filtersChange"] }], quickFilter: [{ type: i0.Input, args: [{ isSignal: true, alias: "quickFilter", required: false }] }, { type: i0.Output, args: ["quickFilterChange"] }], quickFilterFields: [{ type: i0.Input, args: [{ isSignal: true, alias: "quickFilterFields", required: false }] }], quickFilterable: [{ type: i0.Input, args: [{ isSignal: true, alias: "quickFilterable", required: false }] }], quickFilterAlign: [{ type: i0.Input, args: [{ isSignal: true, alias: "quickFilterAlign", required: false }] }], childrenKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "childrenKey", required: false }] }], interactionMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "interactionMode", required: false }] }], selectionColumn: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectionColumn", required: false }] }], rowActivate: [{ type: i0.Output, args: ["rowActivate"] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }], syncChange: [{ type: i0.Output, args: ["syncChange"] }], rowAction: [{ type: i0.Output, args: ["rowAction"] }], lazyLoad: [{ type: i0.Output, args: ["lazyLoad"] }], cellEdit: [{ type: i0.Output, args: ["cellEdit"] }], detailDef: [{ type: i0.ContentChild, args: [i0.forwardRef(() => StrctRowDetailDef), { isSignal: true }] }], groupHeaderDef: [{ type: i0.ContentChild, args: [i0.forwardRef(() => StrctGroupHeaderDef), { isSignal: true }] }], paneTitleDef: [{ type: i0.ContentChild, args: [i0.forwardRef(() => StrctDetailPaneTitle), { isSignal: true }] }], actionBarDef: [{ type: i0.ContentChild, args: [i0.forwardRef(() => StrctDatagridActionBar), { isSignal: true }] }], actionBarCaptionDef: [{ type: i0.ContentChild, args: [i0.forwardRef(() => StrctDatagridActionBarCaption), { isSignal: true }] }], cellDefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => StrctCellDef), { isSignal: true }] }], onDocClickOverlays: [{
17849
+ }, template: "<!-- One polite region for everything the grid does to itself: sorting,\n paging and filtering all change the rows without saying so. -->\n<span class=\"strct-dg__announcer\" role=\"status\" aria-live=\"polite\">{{ announcement() }}</span>\n@if (actionBarDef() || quickFilterable() || actionBarCaptionDef()) {\n <div class=\"strct-dg__toolbar\">\n @if (quickFilterable()) {\n <strct-searchbox\n class=\"strct-dg__quickfilter\"\n [class.strct-dg__quickfilter--end]=\"quickFilterAlign() === 'end'\"\n [placeholder]=\"L().quickFilter\"\n [ariaLabel]=\"L().quickFilter\"\n [value]=\"quickFilter()\"\n (valueChange)=\"quickFilter.set($event)\"\n />\n @if (filterNote(); as note) {\n <span\n class=\"strct-dg__filternote\"\n [class.strct-dg__filternote--end]=\"quickFilterAlign() === 'end'\"\n >{{ note }}</span\n >\n }\n }\n <span class=\"strct-dg__actioncaption\">\n <ng-content select=\"[strctDatagridActionBarCaption]\" />\n </span>\n <ng-content select=\"[strctDatagridActionBar]\" />\n </div>\n}\n\n@if (hasEditableColumn()) {\n <!-- What the editable cells point at with aria-describedby: a keyboard\n user is told the cell can be edited, and how. -->\n <!-- In flow, not absolutely positioned: an out-of-flow box whose\n containing block is the page extends the document's scrollable area\n from wherever the grid happens to sit. 1x1 and clipped is enough. -->\n <span\n class=\"strct-sr-only\"\n style=\"\n display: inline-block;\n width: 1px;\n height: 1px;\n overflow: hidden;\n clip-path: inset(50%);\n white-space: nowrap;\n \"\n [id]=\"editHintId\"\n >{{ L().editCellInstructions }}</span\n >\n}\n\n@if (bulkHint(); as hint) {\n <!-- Selecting the page is not selecting the set. Say which one happened,\n and offer the other. -->\n <div class=\"strct-dg__bulk\" role=\"status\">\n <span>{{ hint.text }}</span>\n <button strct-button variant=\"flat\" size=\"sm\" (click)=\"hint.run()\">\n {{ hint.action }}\n </button>\n </div>\n}\n<div\n class=\"strct-dg__layout\"\n [class.strct-dg__layout--paned]=\"paneOpen()\"\n [class.strct-dg__layout--panetakeover]=\"paneOpen() && narrowPane()\"\n [style.--strct-dg-pane-list-w]=\"paneListWidth()\"\n>\n <div\n class=\"strct-dg__scroll\"\n tabindex=\"0\"\n role=\"region\"\n [attr.aria-label]=\"L().rows\"\n [style.max-height.px]=\"virtual() ? viewportHeight() : maxHeight()\"\n (scroll)=\"virtual() && onScroll($event)\"\n >\n <table\n class=\"strct-dg\"\n [attr.role]=\"childrenKey() ? 'treegrid' : gridMode() ? 'grid' : null\"\n [attr.aria-rowcount]=\"ariaRowCount()\"\n [attr.aria-labelledby]=\"caption() ? captionId : null\"\n >\n @if (caption()) {\n <caption class=\"strct-dg__caption\" [id]=\"captionId\">\n {{\n caption()\n }}\n </caption>\n }\n <thead>\n <tr>\n <!-- A utility column is still a column: axe reports every empty\n header, and a screen-reader user hears nothing where the\n detail, expand and select controls are. The name is hidden,\n not absent. -->\n @if (canDetail()) {\n <th\n class=\"strct-dg__expandcol\"\n scope=\"col\"\n [class.strct-dg__cell--sticky]=\"stickyActive()\"\n [style.insetInlineStart.px]=\"utilLeft('detail')\"\n >\n <span class=\"strct-sr-only\">{{ L().detailColumn }}</span>\n </th>\n }\n @if (canExpand()) {\n <th\n class=\"strct-dg__expandcol\"\n scope=\"col\"\n [class.strct-dg__cell--sticky]=\"stickyActive()\"\n [style.insetInlineStart.px]=\"utilLeft('expand')\"\n >\n <span class=\"strct-sr-only\">{{ L().expandColumn }}</span>\n </th>\n }\n @if (hasSelectionColumn()) {\n <th\n class=\"strct-dg__sel\"\n scope=\"col\"\n [class.strct-dg__cell--sticky]=\"stickyActive()\"\n [style.insetInlineStart.px]=\"utilLeft('sel')\"\n >\n @if (mode() === 'multiple') {\n <strct-checkbox\n [ariaLabel]=\"L().selectAll\"\n [checked]=\"allPageSelected()\"\n [indeterminate]=\"somePageSelected()\"\n (checkedChange)=\"toggleAll()\"\n />\n } @else {\n <span class=\"strct-sr-only\">{{ L().selectColumn }}</span>\n }\n </th>\n }\n @for (col of visibleColumns(); track col.key) {\n <th\n scope=\"col\"\n [style.text-align]=\"col.align ?? (col.numeric ? 'end' : 'start')\"\n [style.width]=\"colWidth(col.key) ?? col.width ?? null\"\n [class.strct-dg__th--sortable]=\"col.sortable\"\n [class.strct-dg__th--drop]=\"col.key === dropKey()\"\n [class.strct-dg__cell--sticky]=\"isSticky(col)\"\n [class.strct-dg__cell--sticky-last]=\"col.key === lastStickyKey()\"\n [style.insetInlineStart.px]=\"stickyLeft(col.key)\"\n [attr.data-col]=\"col.key\"\n [attr.aria-sort]=\"col.sortable ? ariaSort(col.key) : null\"\n [attr.draggable]=\"reorderable() ? true : null\"\n (dragstart)=\"onColDragStart(col.key, $event)\"\n (dragover)=\"onColDragOver(col.key, $event)\"\n (drop)=\"onColDrop(col.key)\"\n (dragend)=\"onColDragEnd()\"\n >\n <span class=\"strct-dg__hd\">\n <!-- A header that sorts is a button: a th with tabindex is\n read as \"column header, focusable\" and never says it\n can be activated. aria-sort stays on the th, which is\n what carries the column's sorted state. -->\n @if (col.sortable) {\n <!-- The button covers most of the header, so it carries\n the drag source too: a reorderable column has to be\n grabbable where the pointer actually lands. The\n event bubbles to the th's own handlers. -->\n <button\n type=\"button\"\n class=\"strct-dg__sortbtn\"\n [attr.draggable]=\"reorderable() ? true : null\"\n (click)=\"sortBy(col.key)\"\n >\n {{ col.label }}\n <strct-icon class=\"strct-dg__sorticon\" [name]=\"sortIcon(col.key)\" [size]=\"13\" />\n </button>\n } @else {\n {{ col.label }}\n }\n @if (col.filterable || col.filterOptions) {\n <button\n #fbtn\n type=\"button\"\n class=\"strct-dg__filterbtn\"\n [class.strct-dg__filterbtn--active]=\"hasFilter(col.key)\"\n [attr.aria-expanded]=\"filterOpenKey() === col.key\"\n [attr.aria-label]=\"L().filterColumn + ': ' + col.label\"\n (click)=\"toggleFilterPanel(col.key, $event)\"\n (keydown.enter)=\"$event.stopPropagation()\"\n (keydown.space)=\"$event.stopPropagation()\"\n >\n <strct-icon name=\"filter\" [size]=\"12\" />\n </button>\n @if (filterOpenKey() === col.key) {\n <div\n class=\"strct-dg__filterpanel\"\n role=\"dialog\"\n [attr.aria-label]=\"L().filterColumn + ': ' + col.label\"\n [strctOverlay]=\"fbtn\"\n strctOverlayPlacement=\"bottom-start\"\n (click)=\"$event.stopPropagation()\"\n (keydown.enter)=\"$event.stopPropagation()\"\n (keydown.space)=\"$event.stopPropagation()\"\n >\n @if (col.filterOptions; as opts) {\n @for (opt of opts; track $index) {\n <label class=\"strct-dg__filteropt\">\n <input\n type=\"checkbox\"\n [checked]=\"optionChecked(col.key, opt)\"\n (change)=\"toggleFilterOption(col.key, opt)\"\n />\n <span>{{ opt }}</span>\n </label>\n }\n } @else {\n <input\n class=\"strct-dg__filterinput\"\n type=\"text\"\n [attr.placeholder]=\"L().filterPlaceholder\"\n [attr.aria-label]=\"L().filterColumn + ': ' + col.label\"\n [value]=\"textFilter(col.key)\"\n (input)=\"setTextFilter(col.key, $any($event.target).value)\"\n />\n }\n <button\n type=\"button\"\n class=\"strct-dg__filterclear\"\n [disabled]=\"!hasFilter(col.key)\"\n (click)=\"clearFilter(col.key)\"\n >\n {{ L().clearFilter }}\n </button>\n </div>\n }\n }\n </span>\n @if (resizable() && col.resizable !== false) {\n <!-- A separator, not a bare div: a column could only be\n resized with a mouse, which is the one input a\n console cannot assume. -->\n <div\n class=\"strct-dg__resize\"\n role=\"separator\"\n aria-orientation=\"vertical\"\n tabindex=\"0\"\n [attr.aria-label]=\"L().resizeColumn(col.label)\"\n [attr.aria-valuenow]=\"separatorValue(col.key)\"\n [attr.aria-valuemin]=\"40\"\n (mousedown)=\"onResizeStart($event, col.key)\"\n (keydown)=\"onResizeKey($event, col.key)\"\n (dblclick)=\"resetWidth(col.key)\"\n ></div>\n }\n </th>\n }\n @if (canActions()) {\n <th class=\"strct-dg__actioncol\" scope=\"col\" [attr.aria-label]=\"L().actions\"></th>\n }\n </tr>\n </thead>\n <!--\n focusin is the real keyboard path (focus does not bubble from cells);\n the plain focus binding exists to satisfy mouse-events-have-key-events.\n -->\n <tbody\n (mouseover)=\"revealIfClipped($event)\"\n (focusin)=\"revealIfClipped($event)\"\n (focus)=\"revealIfClipped($event)\"\n >\n @if (loading()) {\n @for (_ of [1, 2, 3, 4, 5]; track $index) {\n <tr class=\"strct-dg__skeleton-row\">\n @if (canDetail()) {\n <td class=\"strct-dg__expandcell\"></td>\n }\n @if (canExpand()) {\n <td class=\"strct-dg__expandcell\"></td>\n }\n @if (hasSelectionColumn()) {\n <td class=\"strct-dg__sel\"></td>\n }\n @for (col of visibleColumns(); track col.key) {\n <td [style.text-align]=\"col.align ?? 'start'\">\n <div class=\"strct-dg__skeleton-block\"></div>\n </td>\n }\n @if (canActions()) {\n <td class=\"strct-dg__actioncell\"></td>\n }\n </tr>\n }\n } @else {\n @if (virtual() && topPad() > 0) {\n <tr class=\"strct-dg__vspacer\" aria-hidden=\"true\">\n <td [attr.colspan]=\"colspan()\" [style.height.px]=\"topPad()\"></td>\n </tr>\n }\n @for (it of displayItems(); track itemKey(it)) {\n @if (it.group; as grp) {\n <!-- Group header: distinct value + count, collapsible. -->\n <tr class=\"strct-dg__grouprow\">\n @if (groupSelect() && mode() === 'multiple') {\n <td class=\"strct-dg__sel\">\n <strct-checkbox\n [ariaLabel]=\"L().selectAllInGroup(grp.label)\"\n [checked]=\"groupAllSelected(grp.key)\"\n [indeterminate]=\"groupSomeSelected(grp.key)\"\n (checkedChange)=\"toggleGroupSelection(grp.key)\"\n />\n </td>\n }\n <td\n [attr.colspan]=\"\n groupSelect() && mode() === 'multiple' ? colspan() - 1 : colspan()\n \"\n >\n <button\n type=\"button\"\n class=\"strct-dg__groupbtn\"\n [attr.aria-expanded]=\"!grp.collapsed\"\n (click)=\"toggleGroup(grp.key)\"\n >\n <span\n class=\"strct-dg__groupchev\"\n [class.strct-dg__groupchev--open]=\"!grp.collapsed\"\n >\n <strct-icon name=\"chevronRight\" [size]=\"12\" [strokeWidth]=\"1.7\" />\n </span>\n @if (groupHeaderDef(); as tpl) {\n <!-- The consumer draws the row; the button, its\n aria-expanded and the keyboard stay ours. -->\n <ng-container\n [ngTemplateOutlet]=\"tpl.template\"\n [ngTemplateOutletContext]=\"{\n $implicit: grp,\n group: grp,\n count: grp.count,\n }\"\n />\n } @else {\n <span\n class=\"strct-dg__grouplabel strct-dg__grouplabel--{{\n grp.status ?? 'none'\n }}\"\n >{{ grp.label }}</span\n >\n <!-- The three spans run together in the button's\n name (\"Needs your decision1Blocks the join\u2026\");\n the separators are read, not seen. -->\n <span class=\"strct-dg__announcer\">, </span>\n <span\n class=\"strct-dg__groupcount strct-dg__groupcount--{{\n grp.status ?? 'none'\n }}\"\n >{{ grp.count }}</span\n >\n <!-- Seen: the chip's number. Heard: the number with\n the grid's noun, because \"Inconclusive, 3\" does\n not say three of what. -->\n <span class=\"strct-dg__announcer\"> {{ countNoun(grp.count) }}</span>\n @if (grp.note) {\n <span class=\"strct-dg__announcer\">. </span>\n <span class=\"strct-dg__groupnote\">{{ grp.note }}</span>\n }\n }\n </button>\n </td>\n </tr>\n } @else {\n @let row = it.row!;\n <tr\n [attr.aria-rowindex]=\"ariaRowIndex(row)\"\n [class.strct-dg__row--selected]=\"isSelected(row)\"\n [class.strct-dg__row--locked]=\"lockedReason(row) !== null\"\n [attr.aria-selected]=\"mode() === 'none' ? null : isSelected(row)\"\n [attr.aria-disabled]=\"lockedReason(row) !== null ? 'true' : null\"\n [attr.title]=\"lockedReason(row) || null\"\n (click)=\"onRowPick(row)\"\n [class.strct-dg__row--active]=\"paneOpen() && row === activeRow()\"\n [attr.aria-level]=\"childrenKey() ? treeDepth(row) + 1 : null\"\n [attr.aria-expanded]=\"\n childrenKey() && treeHasChildren(row) ? treeExpanded(row) : null\n \"\n [attr.tabindex]=\"\n childrenKey()\n ? treeRowTabindex(row)\n : gridMode()\n ? gridRowTabindex(row)\n : canDetail()\n ? paneRowTabindex(row)\n : null\n \"\n (keydown)=\"\n childrenKey()\n ? onTreeRowKeydown(row, $event)\n : gridMode()\n ? onGridRowKeydown(row, $event)\n : onPaneRowKeydown(row, $event)\n \"\n (dblclick)=\"onRowActivate(row)\"\n >\n @if (canDetail()) {\n <td\n class=\"strct-dg__expandcell\"\n [class.strct-dg__cell--sticky]=\"stickyActive()\"\n [style.insetInlineStart.px]=\"utilLeft('detail')\"\n >\n <button\n type=\"button\"\n [attr.tabindex]=\"ctlTabindex()\"\n class=\"strct-dg__detailbtn\"\n [class.strct-dg__detailbtn--active]=\"row === activeRow()\"\n [attr.aria-expanded]=\"row === activeRow()\"\n [attr.aria-label]=\"L().openDetail\"\n (click)=\"openDetail(row)\"\n >\n <strct-icon name=\"chevronDoubleRight\" [size]=\"13\" [strokeWidth]=\"1.6\" />\n </button>\n </td>\n }\n @if (canExpand()) {\n <td\n class=\"strct-dg__expandcell\"\n [class.strct-dg__cell--sticky]=\"stickyActive()\"\n [style.insetInlineStart.px]=\"utilLeft('expand')\"\n >\n <button\n type=\"button\"\n [attr.tabindex]=\"ctlTabindex()\"\n class=\"strct-dg__expandbtn\"\n [class.strct-dg__expandbtn--open]=\"isExpanded(row)\"\n [attr.aria-expanded]=\"isExpanded(row)\"\n [attr.aria-label]=\"L().toggleDetail\"\n (click)=\"toggleExpand(row)\"\n >\n <strct-icon name=\"chevronRight\" [size]=\"12\" [strokeWidth]=\"1.7\" />\n </button>\n </td>\n }\n @if (hasSelectionColumn()) {\n <td\n class=\"strct-dg__sel\"\n [class.strct-dg__cell--sticky]=\"stickyActive()\"\n [style.insetInlineStart.px]=\"utilLeft('sel')\"\n >\n @if (mode() === 'single') {\n <!-- A native radio group: one name per grid, so the\n arrow keys move between rows and Space picks,\n without the grid inventing its own keyboarding. -->\n <input\n type=\"radio\"\n class=\"strct-dg__radio\"\n [name]=\"radioName\"\n [attr.aria-label]=\"selectRowLabel(row)\"\n [attr.aria-description]=\"lockedReason(row) || null\"\n [checked]=\"isSelected(row)\"\n [disabled]=\"lockedReason(row) !== null\"\n [attr.tabindex]=\"ctlTabindex()\"\n (change)=\"selectSingle(row)\"\n (click)=\"$event.stopPropagation()\"\n />\n } @else {\n <!-- click fires (and bubbles) before the input's change,\n so the modifier is recorded by the time toggleRow runs. -->\n <strct-checkbox\n [ariaLabel]=\"selectRowLabel(row)\"\n [attr.aria-description]=\"lockedReason(row) || null\"\n [checked]=\"isSelected(row)\"\n [disabled]=\"lockedReason(row) !== null\"\n [tabIndex]=\"ctlTabindex()\"\n (click)=\"noteRangeIntent($event); $event.stopPropagation()\"\n (checkedChange)=\"toggleRow(row)\"\n />\n }\n </td>\n }\n @for (col of visibleColumns(); track col.key; let colIdx = $index) {\n <td\n [style.text-align]=\"col.align ?? (col.numeric ? 'end' : 'start')\"\n [class.strct-dg__cell--mono]=\"col.mono\"\n [class.strct-dg__cell--muted]=\"col.muted\"\n [class.strct-dg__cell--numeric]=\"col.numeric\"\n [class.strct-dg__cell--sticky]=\"isSticky(col)\"\n [class.strct-dg__cell--sticky-last]=\"col.key === lastStickyKey()\"\n [class.strct-dg__cell--editable]=\"col.editable\"\n [style.insetInlineStart.px]=\"stickyLeft(col.key)\"\n [attr.data-edit]=\"col.editable ? '' : null\"\n [attr.tabindex]=\"col.editable ? editCellTabindex(row, col) : null\"\n [attr.aria-describedby]=\"col.editable ? editHintId : null\"\n (dblclick)=\"col.editable && startEdit(row, col)\"\n (focus)=\"col.editable && onEditCellFocus(row, col)\"\n (keydown)=\"col.editable && onEditCellKeydown(row, col, $event)\"\n >\n @if (childrenKey() && colIdx === 0) {\n <span\n class=\"strct-dg__treepad\"\n [style.width.px]=\"treeDepth(row) * 18\"\n aria-hidden=\"true\"\n ></span>\n @if (treeHasChildren(row)) {\n <button\n type=\"button\"\n class=\"strct-dg__treebtn\"\n [class.strct-dg__treebtn--open]=\"treeExpanded(row)\"\n [attr.aria-expanded]=\"treeExpanded(row)\"\n [attr.aria-label]=\"L().toggleChildren\"\n (click)=\"toggleTreeRow(row)\"\n >\n <strct-icon name=\"chevronRight\" [size]=\"12\" [strokeWidth]=\"1.7\" />\n </button>\n } @else {\n <span\n class=\"strct-dg__treebtn strct-dg__treebtn--leaf\"\n aria-hidden=\"true\"\n ></span>\n }\n }\n @if (isEditingCell(row, col)) {\n @switch (col.editor ?? 'text') {\n @case ('select') {\n <!-- Choosing is the commit: there is nothing to\n confirm with Enter once an option is picked. -->\n <strct-select\n class=\"strct-dg__editselect\"\n [options]=\"editorOptionsFor(row, col)\"\n [listLabel]=\"L().editCell + ': ' + col.label\"\n [ngModel]=\"row[col.key]\"\n (ngModelChange)=\"commitValue(row, col, $event)\"\n (keydown.escape)=\"cancelEdit($event)\"\n (click)=\"$event.stopPropagation()\"\n />\n }\n @case ('number') {\n <strct-number\n class=\"strct-dg__editnumber\"\n [ariaLabel]=\"L().editCell + ': ' + col.label\"\n [min]=\"col.editorMin ?? null\"\n [max]=\"col.editorMax ?? null\"\n [step]=\"col.editorStep ?? 1\"\n [ngModel]=\"row[col.key]\"\n (ngModelChange)=\"editDraft.set($event)\"\n (keydown.enter)=\"commitDraft(row, col, $event)\"\n (keydown.escape)=\"cancelEdit($event)\"\n (focusout)=\"commitOnLeave(row, col, $event)\"\n (click)=\"$event.stopPropagation()\"\n />\n }\n @default {\n <input\n class=\"strct-dg__editinput\"\n type=\"text\"\n [value]=\"row[col.key] ?? ''\"\n [attr.aria-label]=\"L().editCell + ': ' + col.label\"\n (keydown.enter)=\"commitEdit(row, col, $event)\"\n (keydown.escape)=\"cancelEdit($event)\"\n (blur)=\"commitEdit(row, col, $event)\"\n (click)=\"$event.stopPropagation()\"\n />\n }\n }\n } @else if (cellTemplate(col.key); as tpl) {\n <ng-container\n [ngTemplateOutlet]=\"tpl\"\n [ngTemplateOutletContext]=\"{\n $implicit: row,\n row,\n value: row[col.key],\n column: col,\n }\"\n />\n } @else if (isBlank(row, col)) {\n <span class=\"strct-dg__empty\" [attr.aria-label]=\"col.emptyLabel || null\">{{\n col.emptyText\n }}</span>\n } @else {\n {{ displayValue(row, col) }}\n @if (col.descriptionKey && row[col.descriptionKey]) {\n <span class=\"strct-dg__celldesc\">{{ row[col.descriptionKey] }}</span>\n }\n }\n <!-- The affordance a mouse never needed: a cell that\n can be edited says so on hover and on focus. -->\n @if (col.editable && editHint() && !isEditingCell(row, col)) {\n <strct-icon\n class=\"strct-dg__editpencil\"\n name=\"pencil\"\n [size]=\"12\"\n [strokeWidth]=\"1.6\"\n aria-hidden=\"true\"\n />\n }\n </td>\n }\n @if (canActions()) {\n <td class=\"strct-dg__actioncell\">\n <button\n type=\"button\"\n [attr.tabindex]=\"ctlTabindex()\"\n class=\"strct-dg__kebab\"\n [attr.aria-label]=\"L().rowActions\"\n (click)=\"openRowMenu(row, $event)\"\n >\n <strct-icon name=\"dots\" [size]=\"16\" />\n </button>\n </td>\n }\n </tr>\n @if (canExpand() && isExpanded(row)) {\n <tr class=\"strct-dg__detailrow\">\n <td [attr.colspan]=\"colspan()\">\n <div class=\"strct-dg__detail\">\n <ng-container\n [ngTemplateOutlet]=\"detailDef()!.template\"\n [ngTemplateOutletContext]=\"{ $implicit: row, row }\"\n />\n </div>\n </td>\n </tr>\n }\n }\n } @empty {\n <!-- \"Nothing here\" and \"nothing matches what you typed\" are\n different states: the second one can be undone, and\n saying so is the whole point. -->\n <tr>\n <td class=\"strct-dg__emptyrow\" [attr.colspan]=\"colspan()\">\n @if (filteredAway()) {\n <strct-empty-state\n size=\"sm\"\n [titleLevel]=\"null\"\n [title]=\"L().noMatches\"\n [description]=\"noMatchesDescription()\"\n >\n <button strct-button variant=\"flat\" size=\"sm\" (click)=\"clearAllFilters()\">\n {{ L().clearFilters }}\n </button>\n </strct-empty-state>\n } @else {\n <!-- Not a heading: a grid is a region of a page, and an\n empty one used to put an <h3> in its outline. -->\n <strct-empty-state size=\"sm\" [titleLevel]=\"null\" [title]=\"emptyText()\" />\n }\n </td>\n </tr>\n }\n @if (virtual() && bottomPad() > 0) {\n <tr class=\"strct-dg__vspacer\" aria-hidden=\"true\">\n <td [attr.colspan]=\"colspan()\" [style.height.px]=\"bottomPad()\"></td>\n </tr>\n }\n }\n </tbody>\n </table>\n </div>\n\n @if (paneOpen()) {\n <aside class=\"strct-dg__pane\">\n <div class=\"strct-dg__pane-head\">\n @if (narrowPane()) {\n <!-- The pane has taken the list's place, so there is a way back. -->\n <button\n type=\"button\"\n class=\"strct-dg__pane-back\"\n [attr.aria-label]=\"L().backToList\"\n (click)=\"closePane()\"\n >\n <strct-icon name=\"chevronLeft\" [size]=\"13\" />\n </button>\n }\n <span class=\"strct-dg__pane-title\">\n <ng-content select=\"[strctDetailPaneTitle]\" />\n @if (!paneTitleDef()) {\n {{ activeRow()![columns()[0].key] }}\n }\n </span>\n <span class=\"strct-dg__pane-meta\">\n <ng-content select=\"[strctDetailPaneMeta]\" />\n </span>\n <span class=\"strct-dg__pane-actions\">\n <ng-content select=\"[strctDetailPaneActions]\" />\n </span>\n <button\n type=\"button\"\n class=\"strct-dg__pane-close\"\n [attr.aria-label]=\"L().closeDetail\"\n (click)=\"closePane()\"\n >\n <strct-icon name=\"close\" [size]=\"13\" />\n </button>\n </div>\n <div class=\"strct-dg__pane-body\">\n <ng-container\n [ngTemplateOutlet]=\"detailDef()!.template\"\n [ngTemplateOutletContext]=\"{ $implicit: activeRow(), row: activeRow() }\"\n />\n </div>\n </aside>\n }\n</div>\n\n@if (showFooter()) {\n <div class=\"strct-dg__foot\">\n <div class=\"strct-dg__foot-left\">\n @if (columnChooser() || sync()) {\n <div class=\"strct-dg__actions\">\n <strct-button-group>\n @if (columnChooser()) {\n <button\n strct-button\n size=\"sm\"\n [class.is-open]=\"chooserOpen()\"\n [disabled]=\"footerActionsDisabled()\"\n [attr.aria-label]=\"L().chooseColumns\"\n (click)=\"toggleChooser($event)\"\n >\n <strct-icon name=\"settings\" [size]=\"16\" />\n </button>\n }\n @if (sync()) {\n <button\n strct-button\n size=\"sm\"\n [disabled]=\"footerActionsDisabled()\"\n [attr.aria-label]=\"L().refresh\"\n (click)=\"syncChange.emit()\"\n >\n <strct-icon name=\"refresh\" [size]=\"16\" />\n </button>\n }\n </strct-button-group>\n @if (columnChooser() && chooserOpen()) {\n <div class=\"strct-dg__chooser-menu\">\n @for (col of columns(); track col.key) {\n <!-- The whole row toggles; the checkbox's own click must not\n reach the row or the column would flip twice. -->\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <div\n class=\"strct-dg__chooser-item\"\n (click)=\"toggleColumn(col.key, hiddenColumns().has(col.key))\"\n >\n <strct-checkbox\n [checked]=\"!hiddenColumns().has(col.key)\"\n (checkedChange)=\"toggleColumn(col.key, $event)\"\n (click)=\"$event.stopPropagation()\"\n />\n <span>{{ col.label }}</span>\n </div>\n }\n </div>\n }\n </div>\n }\n <span class=\"strct-dg__count\">\n @if (paging() === 'more') {\n {{ L().showing(rows().length, moreTotal() ?? undefined) }}\n } @else {\n {{ totalCount() }} {{ totalCount() === 1 ? L().row : L().rows }}\n }\n @if (selectedCount() && mode() !== 'single') {\n <span class=\"strct-dg__count-sep\">|</span>\n <span class=\"strct-dg__count-sel\">{{ selectedCount() }} {{ L().selected }}</span>\n }\n </span>\n </div>\n <div class=\"strct-dg__foot-right\">\n <!-- A feed that pages by cursor loads more at the end: page numbers\n are something a cursor API cannot answer. It takes the pager's\n place \u2014 the column chooser, the sync button and the count are\n the footer's, not the pager's. -->\n @if (paging() === 'more') {\n @if (hasMore()) {\n <button\n strct-button\n size=\"sm\"\n class=\"strct-dg__more\"\n [disabled]=\"loadingMore()\"\n (click)=\"loadMore.emit()\"\n >\n @if (loadingMore()) {\n <strct-spinner size=\"sm\" />\n }\n {{ L().loadMore }}\n </button>\n }\n } @else if (pageSize() > 0 && !groupBy()) {\n <strct-pagination\n [total]=\"totalCount()\"\n [pageSize]=\"pageSize()\"\n [page]=\"page()\"\n (pageChange)=\"goToPage($event)\"\n />\n }\n </div>\n </div>\n}\n", styles: [".strct-dg__radio{width:15px;height:15px;accent-color:var(--acc);cursor:pointer}.strct-dg__radio:disabled{cursor:not-allowed}.strct-dg__row--locked .strct-dg__sel{opacity:.55}.strct-dg__actioncaption:empty{display:none}.strct-dg__actioncaption{display:inline-flex;align-items:center;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:var(--text-sm);color:var(--t2)}.strct-dg__bulk{display:flex;align-items:center;justify-content:center;flex-wrap:wrap;gap:4px 10px;padding:7px 12px;border-bottom:1px solid var(--b2);background:var(--acc-s);color:var(--t2);font-size:var(--text-md)}.strct-dg__announcer{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}.strct-dg-host{display:block;border:1px solid var(--b2);border-radius:var(--radius-lg);background:var(--bg-2);box-shadow:var(--shadow-rest)}.strct-dg-host--flush{border:0;border-radius:0;background:transparent;box-shadow:none}.strct-dg__more strct-spinner{margin-inline-end:4px}.strct-dg__caption{caption-side:top;text-align:start;padding:var(--space-2) var(--space-3);font-size:var(--text-sm);font-weight:600;color:var(--t2)}.strct-dg td.strct-dg__cell--mono{font-family:var(--mono);font-size:var(--text-sm)}.strct-dg td.strct-dg__cell--muted{color:var(--t2)}.strct-dg td.strct-dg__cell--numeric{font-variant-numeric:tabular-nums}.strct-dg__empty{color:var(--t2)}.strct-dg__celldesc{display:block;font-size:var(--text-sm);color:var(--t2);line-height:1.3}.strct-dg__scroll{flex:1 1 auto;min-width:0;overflow-x:auto;-webkit-overflow-scrolling:touch}.strct-dg-host--virtual .strct-dg__scroll,.strct-dg-host--bounded .strct-dg__scroll{overflow-y:auto}.strct-dg-host--virtual .strct-dg thead th,.strct-dg-host--bounded .strct-dg thead th{position:sticky;top:0;z-index:var(--z-raised)}.strct-dg__vspacer td{padding:0;border:0;background:transparent}.strct-dg th[draggable]{cursor:grab}.strct-dg__th--drop{box-shadow:inset 3px 0 0 var(--acc)}.strct-dg__grouprow td{background:var(--bg-2)!important;padding:0}.strct-dg__groupbtn{display:flex;align-items:center;gap:8px;width:100%;padding:7px 13px;border:0;background:transparent;color:var(--t1);font-family:var(--font);font-size:var(--text-sm);font-weight:600;cursor:pointer;text-align:start}.strct-dg__groupbtn:hover{background:var(--bg-3)}.strct-dg__groupbtn:focus-visible{outline:2px solid var(--acc50);outline-offset:-2px}.strct-dg__groupchev{display:inline-flex;color:var(--t3);transition:transform .15s ease}.strct-dg__groupchev--open{transform:rotate(90deg)}@media(prefers-reduced-motion:reduce){.strct-dg__groupchev{transition:none}}.strct-dg__grouplabel--critical{color:var(--critical)}.strct-dg__grouplabel--warning{color:var(--warning)}.strct-dg__grouplabel--success{color:var(--success)}.strct-dg__grouplabel--accent{color:var(--acc)}.strct-dg__grouplabel--neutral,.strct-dg__grouplabel--none{color:var(--t1)}.strct-dg__groupnote{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:var(--text-sm);font-weight:400;color:var(--t2)}.strct-dg__groupcount{min-width:18px;height:18px;padding:0 5px;display:inline-flex;align-items:center;justify-content:center;font-size:12px;border-radius:4px;background:var(--acc-m);color:var(--acc);font-variant-numeric:tabular-nums}.strct-dg__groupcount--critical{background:var(--critical-bg);color:var(--critical)}.strct-dg__groupcount--warning{background:var(--warning-bg);color:var(--warning)}.strct-dg__groupcount--success{background:var(--success-bg);color:var(--success)}.strct-dg__groupcount--neutral,.strct-dg__groupcount--none{background:var(--bg-3);color:var(--t2)}.strct-dg-host--singleline .strct-dg{border-collapse:separate;border-spacing:0}.strct-dg-host--sticky .strct-dg,.strct-dg-host--virtual .strct-dg,.strct-dg-host--bounded .strct-dg{border-collapse:separate;border-spacing:0;overflow:visible;border-radius:0}.strct-dg-host--sticky .strct-dg{width:max-content;min-width:100%}.strct-dg .strct-dg__cell--sticky{position:sticky;z-index:calc(var(--z-base) + 2)}.strct-dg thead th.strct-dg__cell--sticky{z-index:calc(var(--z-raised) + 1)}.strct-dg .strct-dg__cell--sticky-last:after{content:\"\";position:absolute;top:0;bottom:0;inset-inline-end:-1px;width:7px;pointer-events:none;background:linear-gradient(to right,rgba(0,0,0,.14),transparent)}[dir=rtl] .strct-dg .strct-dg__cell--sticky-last:after{background:linear-gradient(to left,rgba(0,0,0,.14),transparent)}.strct-dg-host--sticky .strct-dg__expandcol,.strct-dg-host--sticky .strct-dg__expandcell{width:36px;min-width:36px;max-width:36px;box-sizing:border-box}.strct-dg-host--sticky .strct-dg__sel{width:40px;min-width:40px;max-width:40px;box-sizing:border-box}.strct-dg-host--sticky tbody .strct-dg__cell--sticky{background:var(--bg-1)}[data-theme=dark] .strct-dg-host--sticky tbody .strct-dg__cell--sticky{background:var(--bg-3)}.strct-dg-host--sticky tbody tr:hover .strct-dg__cell--sticky{background:linear-gradient(var(--acc-s),var(--acc-s)) var(--bg-1)}[data-theme=dark] .strct-dg-host--sticky tbody tr:hover .strct-dg__cell--sticky{background:linear-gradient(var(--acc-s),var(--acc-s)) var(--bg-3)}.strct-dg-host--sticky tbody .strct-dg__row--selected .strct-dg__cell--sticky{background:linear-gradient(var(--acc-m),var(--acc-m)) var(--bg-1)}[data-theme=dark] .strct-dg-host--sticky tbody .strct-dg__row--selected .strct-dg__cell--sticky{background:linear-gradient(var(--acc-m),var(--acc-m)) var(--bg-3)}.strct-dg{width:100%;border-collapse:collapse;font-size:13px;overflow:hidden;background:var(--bg-2)}.strct-dg-host:not(:has(.strct-dg__toolbar)) .strct-dg{border-start-start-radius:4px;border-start-end-radius:4px}.strct-dg-host:not(:has(.strct-dg__foot)) .strct-dg{border-end-start-radius:4px;border-end-end-radius:4px}.strct-dg th,.strct-dg td{padding:9px 13px;text-align:start;border-bottom:1px solid var(--b1)}.strct-dg tbody td{background:var(--bg-1)}[data-theme=dark] .strct-dg tbody td{background:var(--bg-3)}.strct-dg-host--compact .strct-dg th,.strct-dg-host--compact .strct-dg td{padding:5px 11px}.strct-dg-host--singleline .strct-dg tbody tr:not(.strct-dg__detailrow)>td{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:360px}.strct-dg th{position:relative;font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:.4px;color:var(--t2);background:var(--bg-2);white-space:nowrap;-webkit-user-select:none;user-select:none}.strct-dg__th--sortable{cursor:pointer}.strct-dg__th--sortable:hover{color:var(--t1)}.strct-dg__sortbtn{display:inline-flex;align-items:center;gap:inherit;padding:0;border:0;background:none;color:inherit;font:inherit;text-transform:inherit;letter-spacing:inherit;text-align:inherit;cursor:pointer}.strct-dg__sortbtn:focus-visible{outline:2px solid var(--acc50);outline-offset:2px;border-radius:var(--radius-sm)}.strct-dg__th--sortable:focus-visible{outline:2px solid var(--acc50);outline-offset:-2px}.strct-dg__hd{display:inline-flex;align-items:center;gap:5px}.strct-dg__sorticon{color:var(--t3)}.strct-dg__th--sortable:hover .strct-dg__sorticon{color:var(--acc)}.strct-dg__resize{position:absolute;inset-inline-end:0;top:0;bottom:0;width:4px;cursor:col-resize;background:transparent;z-index:calc(var(--z-base) + 1)}.strct-dg__resize:hover{background:var(--acc)}.strct-dg td{color:var(--t1)}.strct-dg tbody tr:last-child td{border-bottom:0}.strct-dg tbody tr:not(.strct-dg__detailrow):hover td{background:linear-gradient(var(--acc-s),var(--acc-s)) var(--bg-1)}[data-theme=dark] .strct-dg tbody tr:not(.strct-dg__detailrow):hover td{background:linear-gradient(var(--acc-s),var(--acc-s)) var(--bg-3)}.strct-dg tbody .strct-dg__row--selected td{background:linear-gradient(var(--acc-m),var(--acc-m)) var(--bg-1)}[data-theme=dark] .strct-dg tbody .strct-dg__row--selected td{background:linear-gradient(var(--acc-m),var(--acc-m)) var(--bg-3)}.strct-dg tbody tr.strct-dg__row--selected:hover td{background:linear-gradient(var(--acc-s),var(--acc-s)),linear-gradient(var(--acc-m),var(--acc-m)) var(--bg-1)}[data-theme=dark] .strct-dg tbody tr.strct-dg__row--selected:hover td{background:linear-gradient(var(--acc-s),var(--acc-s)),linear-gradient(var(--acc-m),var(--acc-m)) var(--bg-3)}.strct-dg__sel{width:1%;white-space:nowrap}.strct-dg__sel .strct-cb,.strct-dg__sel .strct-dg__radio,.strct-dg__expandbtn{position:relative}.strct-dg__sel .strct-cb:after,.strct-dg__sel .strct-dg__radio:after,.strct-dg__expandbtn:after{content:\"\";position:absolute;inset:-11px -13px;min-width:24px;min-height:24px}.strct-dg-host--compact .strct-dg__sel .strct-cb:after,.strct-dg-host--compact .strct-dg__sel .strct-dg__radio:after,.strct-dg-host--compact .strct-dg__expandbtn:after{inset:-7px -11px}.strct-dg__sel input{accent-color:var(--acc);width:15px;height:15px;cursor:pointer}.strct-dg__expandcol,.strct-dg__expandcell{width:1%;white-space:nowrap}.strct-dg__actioncol,.strct-dg__actioncell{width:1%;white-space:nowrap;text-align:end}.strct-dg__kebab{display:inline-flex;padding:4px;border:0;border-radius:2px;background:transparent;color:var(--t3);cursor:pointer;transition:color .14s ease,background .14s ease}.strct-dg__kebab:hover{color:var(--t1);background:var(--bg-3)}.strct-dg__expandbtn{position:relative;display:inline-flex;align-items:center;justify-content:center;padding:3px;border:0;border-radius:2px;background:transparent;color:var(--t3);cursor:pointer;transition:transform .15s ease,color .15s ease}.strct-dg__expandbtn:hover{color:var(--t1);background:var(--bg-3)}.strct-dg__expandbtn--open{transform:rotate(90deg);color:var(--acc)}.strct-dg__detailbtn{display:inline-flex;padding:3px;border:0;border-radius:2px;background:transparent;color:var(--t3);cursor:pointer;transition:color .14s ease,background .14s ease}.strct-dg__detailbtn:hover{color:var(--acc);background:var(--bg-3)}.strct-dg__detailbtn--active{color:var(--acc);background:var(--acc-m)}.strct-dg__detailrow td{background:var(--bg-2);padding:0}.strct-dg__detail{padding:14px 16px;font-size:13px;color:var(--t2)}.strct-dg__layout{display:flex;align-items:flex-start;min-width:0}.strct-dg__layout--paned{gap:0}.strct-dg__layout--paned>.strct-dg__scroll{flex:0 0 auto;width:var(--strct-dg-pane-list-w, 260px);min-width:var(--strct-dg-pane-list-w, 260px);max-width:var(--strct-dg-pane-list-w, 260px)}.strct-dg__layout--paned>.strct-dg__scroll>.strct-dg{width:100%;min-width:0;max-width:none;flex-shrink:0;border-start-end-radius:0;border-end-end-radius:0}.strct-dg__layout--panetakeover>.strct-dg__scroll{display:none}.strct-dg__layout--panetakeover>.strct-dg__pane{flex:1;min-width:0}.strct-dg__pane-meta,.strct-dg__pane-actions{display:inline-flex;align-items:center;gap:var(--space-2)}.strct-dg__pane-meta:empty,.strct-dg__pane-actions:empty{display:none}.strct-dg__pane-actions{margin-inline-start:auto}.strct-dg__pane-back{display:inline-flex;align-items:center;border:0;background:none;color:var(--t2);cursor:pointer;padding:2px;border-radius:var(--radius-sm)}.strct-dg__row--clickable{cursor:pointer}.strct-dg tbody .strct-dg__row--active td{background:linear-gradient(var(--acc-m),var(--acc-m)) var(--bg-1)}[data-theme=dark] .strct-dg tbody .strct-dg__row--active td{background:linear-gradient(var(--acc-m),var(--acc-m)) var(--bg-3)}.strct-dg__layout--paned .strct-dg__row--active td:last-child{position:relative;padding-inline-end:26px}.strct-dg__layout--paned .strct-dg__row--active td:last-child:after{content:\"\";position:absolute;inset-inline-end:11px;top:50%;width:6px;height:6px;border-top:1.6px solid var(--acc);border-inline-end:1.6px solid var(--acc);transform:translateY(-50%) rotate(45deg)}.strct-dg__pane{flex:1;min-width:0;align-self:stretch;background:var(--bg-1);border:1px solid var(--b2);border-inline-start:2px solid var(--acc);border-radius:4px;border-start-start-radius:0;border-end-start-radius:0;overflow:hidden;animation:strct-dg-pane-in .14s ease}.strct-dg__pane-head{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:11px 14px;border-bottom:1px solid var(--b1);font-size:13px;font-weight:600;color:var(--t1)}.strct-dg__pane-close{display:inline-flex;padding:3px;border:0;border-radius:2px;background:transparent;color:var(--t3);cursor:pointer}.strct-dg__pane-close:hover{color:var(--t1);background:var(--bg-3)}.strct-dg__pane-body{padding:14px 16px;font-size:13px;color:var(--t2)}@keyframes strct-dg-pane-in{0%{opacity:0;transform:translate(8px)}}@keyframes strct-skeleton-pulse{0%,to{opacity:.4}50%{opacity:.7}}.strct-dg__skeleton-block{height:12px;border-radius:var(--radius-sm);background:linear-gradient(90deg,var(--bg-3) 25%,var(--skeleton-hi, var(--acc18)) 50%,var(--bg-3) 75%);background-size:200% 100%;animation:strct-dg-skeleton-sweep 1.1s linear infinite}@keyframes strct-dg-skeleton-sweep{0%{background-position:100% 0}to{background-position:-100% 0}}@media(prefers-reduced-motion:reduce){.strct-dg__skeleton-block{animation:none;background:var(--skeleton-hi, var(--acc18))}}.strct-dg__skeleton-row td{border-bottom:1px solid var(--b1)}.strct-dg__empty{text-align:center;color:var(--t2);padding:22px}.strct-dg__quickfilter{min-width:180px;max-width:260px}.strct-dg__quickfilter--end{order:2;margin-inline-start:auto}.strct-dg__filternote--end{order:3}.strct-dg__filternote{font-size:var(--text-sm);color:var(--t2);font-variant-numeric:tabular-nums;white-space:nowrap}.strct-dg__toolbar{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:9px 14px;border-bottom:1px solid var(--b2)}.strct-dg__foot{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:9px 14px;border-top:1px solid var(--b2);flex-wrap:wrap}.strct-dg__foot-left,.strct-dg__foot-right{display:flex;align-items:center;gap:12px}.strct-dg__actions{position:relative}.strct-dg__chooser-menu{position:absolute;bottom:calc(100% + 6px);inset-inline-start:0;z-index:var(--z-sticky);min-width:180px;background:var(--bg-1);border:1px solid var(--b2);border-radius:4px;box-shadow:var(--shadow-pop);padding:6px;display:flex;flex-direction:column;gap:2px}.strct-dg__chooser-menu--right{inset-inline-start:auto;inset-inline-end:0}.strct-dg__chooser-item{display:flex;align-items:center;gap:8px;padding:5px 8px;border-radius:3px;cursor:pointer;font-size:12px;color:var(--t1);transition:background .1s ease;-webkit-user-select:none;user-select:none}.strct-dg__chooser-item:hover{background:var(--bg-3)}.strct-dg__count{font-size:12px;color:var(--t2)}.strct-dg__count-sep{margin:0 8px;color:var(--b2)}.strct-dg__count-sel{color:var(--acc)}.strct-dg__filterbtn{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;margin-inline-start:2px;padding:0;border:0;border-radius:2px;background:transparent;color:var(--t3);cursor:pointer;vertical-align:middle}.strct-dg__filterbtn:hover{color:var(--t1);background:var(--bg-3)}.strct-dg__filterbtn--active{color:var(--acc)}.strct-dg__filterbtn:focus-visible{outline:2px solid var(--acc50);outline-offset:1px}.strct-dg__filterpanel{position:absolute;z-index:calc(var(--z-dropdown) + 10);display:flex;flex-direction:column;gap:6px;min-width:180px;padding:10px;background:var(--bg-1);border:1px solid var(--b2);border-radius:3px;box-shadow:var(--shh);font-weight:400;text-transform:none;letter-spacing:normal;white-space:nowrap}.strct-dg__filterinput{padding:5px 8px;border:1px solid var(--b2);border-radius:var(--radius-sm);background:var(--bg-2);color:var(--t1);font-family:var(--font);font-size:12px}.strct-dg__filterinput:focus-visible{outline:2px solid var(--acc50);outline-offset:1px}.strct-dg__filteropt{display:flex;align-items:center;gap:7px;font-size:12.5px;color:var(--t1);cursor:pointer}.strct-dg__filterclear{align-self:flex-start;padding:3px 8px;border:1px solid var(--b2);border-radius:var(--radius-sm);background:transparent;color:var(--t2);font-family:var(--font);font-size:var(--text-sm);cursor:pointer}.strct-dg__filterclear:hover:not(:disabled){color:var(--t1);background:var(--bg-3)}.strct-dg__filterclear:disabled{color:var(--t4);cursor:default}.strct-dg tbody tr[tabindex]:focus-visible{outline:2px solid var(--acc50);outline-offset:-2px}.strct-dg__treepad{display:inline-block;height:1px;vertical-align:middle}.strct-dg__treebtn{display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;margin-inline-end:4px;padding:0;border:0;border-radius:2px;background:transparent;color:var(--t3);cursor:pointer;vertical-align:middle}.strct-dg__treebtn:hover{color:var(--t1);background:var(--bg-3)}.strct-dg__treebtn:focus-visible{outline:2px solid var(--acc50);outline-offset:1px}.strct-dg__treebtn strct-icon{transition:transform .15s ease}.strct-dg__treebtn--open strct-icon{transform:rotate(90deg)}.strct-dg__treebtn--leaf{cursor:default;pointer-events:none}@media(prefers-reduced-motion:reduce){.strct-dg__treebtn strct-icon{transition:none}}.strct-dg__cell--editable{cursor:text;position:relative}.strct-dg__cell--editable:hover{box-shadow:inset 0 0 0 1px var(--b2)}.strct-dg__cell--editable:focus-visible{outline:none;box-shadow:inset 0 0 0 2px var(--acc50)}.strct-dg__editpencil{position:absolute;top:50%;inset-inline-end:6px;transform:translateY(-50%);color:var(--t3);opacity:0;pointer-events:none}td:hover>.strct-dg__editpencil,td:focus-within>.strct-dg__editpencil{opacity:1}.strct-dg__editinput{width:100%;box-sizing:border-box;padding:3px 6px;border:1px solid var(--acc);border-radius:var(--radius-sm);background:var(--bg-1);color:var(--t1);font:inherit}.strct-dg__editselect,.strct-dg__editnumber{display:block;width:100%}.strct-dg__editinput:focus-visible{outline:2px solid var(--acc50);outline-offset:1px}\n"] }]
17850
+ }], ctorParameters: () => [], propDecorators: { columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: true }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: true }] }], pageSizeFlat: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSize", required: false }] }], selectableFlat: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectable", required: false }] }], expandable: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandable", required: false }] }], detailPaneFlat: [{ type: i0.Input, args: [{ isSignal: true, alias: "detailPane", required: false }] }], paneListWidthFlat: [{ type: i0.Input, args: [{ isSignal: true, alias: "paneListWidth", required: false }] }], paneTakeoverWidthFlat: [{ type: i0.Input, args: [{ isSignal: true, alias: "paneTakeoverWidth", required: false }] }], activeRowId: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeRowId", required: false }] }, { type: i0.Output, args: ["activeRowIdChange"] }], compact: [{ type: i0.Input, args: [{ isSignal: true, alias: "compact", required: false }] }], singleLine: [{ type: i0.Input, args: [{ isSignal: true, alias: "singleLine", required: false }] }], emptyText: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyText", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], resizable: [{ type: i0.Input, args: [{ isSignal: true, alias: "resizable", required: false }] }], columnChooser: [{ type: i0.Input, args: [{ isSignal: true, alias: "columnChooser", required: false }] }], syncFlat: [{ type: i0.Input, args: [{ isSignal: true, alias: "sync", required: false }] }], footerActionsDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "footerActionsDisabled", required: false }] }], rowId: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowId", required: false }] }], selectionModeFlat: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectionMode", required: false }] }], selectedId: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedId", required: false }] }, { type: i0.Output, args: ["selectedIdChange"] }], rowSelectableFlat: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowSelectable", required: false }] }], flush: [{ type: i0.Input, args: [{ isSignal: true, alias: "flush", required: false }] }], caption: [{ type: i0.Input, args: [{ isSignal: true, alias: "caption", required: false }] }], hasMoreFlat: [{ type: i0.Input, args: [{ isSignal: true, alias: "hasMore", required: false }] }], loadingMoreFlat: [{ type: i0.Input, args: [{ isSignal: true, alias: "loadingMore", required: false }] }], moreTotalFlat: [{ type: i0.Input, args: [{ isSignal: true, alias: "moreTotal", required: false }] }], loadMore: [{ type: i0.Output, args: ["loadMore"] }], groupSelectFlat: [{ type: i0.Input, args: [{ isSignal: true, alias: "groupSelect", required: false }] }], rowActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowActions", required: false }] }], initialSelectionFlat: [{ type: i0.Input, args: [{ isSignal: true, alias: "initialSelection", required: false }] }], selectedIds: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedIds", required: false }] }, { type: i0.Output, args: ["selectedIdsChange"] }], labels: [{ type: i0.Input, args: [{ isSignal: true, alias: "labels", required: false }] }], viewportHeightFlat: [{ type: i0.Input, args: [{ isSignal: true, alias: "viewportHeight", required: false }] }], maxHeightFlat: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxHeight", required: false }] }], editHint: [{ type: i0.Input, args: [{ isSignal: true, alias: "editHint", required: false }] }], rowHeightFlat: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowHeight", required: false }] }], lazyFlat: [{ type: i0.Input, args: [{ isSignal: true, alias: "lazy", required: false }] }], totalFlat: [{ type: i0.Input, args: [{ isSignal: true, alias: "total", required: false }] }], stateKeyFlat: [{ type: i0.Input, args: [{ isSignal: true, alias: "stateKey", required: false }] }], columnState: [{ type: i0.Input, args: [{ isSignal: true, alias: "columnState", required: false }] }, { type: i0.Output, args: ["columnStateChange"] }], reorderable: [{ type: i0.Input, args: [{ isSignal: true, alias: "reorderable", required: false }] }], groupByFlat: [{ type: i0.Input, args: [{ isSignal: true, alias: "groupBy", required: false }] }], groupsFlat: [{ type: i0.Input, args: [{ isSignal: true, alias: "groups", required: false }] }], showEmptyGroupsFlat: [{ type: i0.Input, args: [{ isSignal: true, alias: "showEmptyGroups", required: false }] }], collapsedGroups: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsedGroups", required: false }] }, { type: i0.Output, args: ["collapsedGroupsChange"] }], filters: [{ type: i0.Input, args: [{ isSignal: true, alias: "filters", required: false }] }, { type: i0.Output, args: ["filtersChange"] }], quickFilter: [{ type: i0.Input, args: [{ isSignal: true, alias: "quickFilter", required: false }] }, { type: i0.Output, args: ["quickFilterChange"] }], quickFilterFieldsFlat: [{ type: i0.Input, args: [{ isSignal: true, alias: "quickFilterFields", required: false }] }], quickFilterableFlat: [{ type: i0.Input, args: [{ isSignal: true, alias: "quickFilterable", required: false }] }], quickFilterAlignFlat: [{ type: i0.Input, args: [{ isSignal: true, alias: "quickFilterAlign", required: false }] }], childrenKeyFlat: [{ type: i0.Input, args: [{ isSignal: true, alias: "childrenKey", required: false }] }], interactionMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "interactionMode", required: false }] }], selectionColumnFlat: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectionColumn", required: false }] }], rowActivate: [{ type: i0.Output, args: ["rowActivate"] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }], syncChange: [{ type: i0.Output, args: ["syncChange"] }], rowAction: [{ type: i0.Output, args: ["rowAction"] }], lazyLoad: [{ type: i0.Output, args: ["lazyLoad"] }], cellEdit: [{ type: i0.Output, args: ["cellEdit"] }], selectionCfg: [{ type: i0.Input, args: [{ isSignal: true, alias: "selection", required: false }] }], paneFlat: [{ type: i0.Input, args: [{ isSignal: true, alias: "pane", required: false }] }], pagingFlat: [{ type: i0.Input, args: [{ isSignal: true, alias: "paging", required: false }] }], virtualFlat: [{ type: i0.Input, args: [{ isSignal: true, alias: "virtual", required: false }] }], groupingCfg: [{ type: i0.Input, args: [{ isSignal: true, alias: "grouping", required: false }] }], filteringCfg: [{ type: i0.Input, args: [{ isSignal: true, alias: "filtering", required: false }] }], stateCfg: [{ type: i0.Input, args: [{ isSignal: true, alias: "state", required: false }] }], detailDef: [{ type: i0.ContentChild, args: [i0.forwardRef(() => StrctRowDetailDef), { isSignal: true }] }], groupHeaderDef: [{ type: i0.ContentChild, args: [i0.forwardRef(() => StrctGroupHeaderDef), { isSignal: true }] }], paneTitleDef: [{ type: i0.ContentChild, args: [i0.forwardRef(() => StrctDetailPaneTitle), { isSignal: true }] }], actionBarDef: [{ type: i0.ContentChild, args: [i0.forwardRef(() => StrctDatagridActionBar), { isSignal: true }] }], actionBarCaptionDef: [{ type: i0.ContentChild, args: [i0.forwardRef(() => StrctDatagridActionBarCaption), { isSignal: true }] }], cellDefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => StrctCellDef), { isSignal: true }] }], onDocClickOverlays: [{
19223
17851
  type: HostListener,
19224
17852
  args: ['document:click', ['$event']]
19225
17853
  }], onEscapeOverlays: [{
@@ -31994,5 +30622,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImpor
31994
30622
  * Generated bundle index. Do not edit.
31995
30623
  */
31996
30624
 
31997
- export { FOCUSABLE_SELECTOR, STRCT_DP_DOW, STRCT_DP_DOW_FULL, STRCT_DP_MONTHS, STRCT_ICONS, STRCT_ICON_GROUPS, STRCT_ICON_NAMES, STRCT_MASKS, STRCT_MD_LIMITS, STRCT_NOTIFICATION_HISTORY_LIMIT, STRCT_PALETTES, STRCT_RAW_ICONS, STRCT_TIME_RANGE_PRESETS, STRCT_WIZARD_DEFAULTS, StrctAccordion, StrctAccordionPanel, StrctAccordionPanelMeta, StrctAlert, StrctAlertActions, StrctAnnouncer, StrctAvatar, StrctBadge, StrctBreadcrumb, StrctBreadcrumbItem, StrctButton, StrctButtonGroup, StrctBytesPipe, StrctCard, StrctCardBlock, StrctCardFooter, StrctCardHeader, StrctCardHeaderActions, StrctCardHeaderLeading, StrctCardHeaderMeta, StrctCardHeaderNote, StrctCascadeColumn, StrctCascadeHost, StrctCascadeSelect, StrctCellDef, StrctCellStatus, StrctChange, StrctChart, StrctChatAttachment, StrctChatComposer, StrctChatMessage, StrctChatMessageActions, StrctChatThread, StrctCheckbox, StrctChips, StrctCode, StrctCodeInline, StrctColorPicker, StrctCombobox, StrctCommandPalette, StrctConfirmOutlet, StrctConfirmService, StrctContextMenu, StrctContextMenuTrigger, StrctControlDescription, StrctCopy, StrctDatagrid, StrctDatagridActionBar, StrctDatagridActionBarCaption, StrctDatepicker, StrctDatetimePicker, StrctDesc, StrctDescriptionList, StrctDetailPaneActions, StrctDetailPaneMeta, StrctDetailPaneTitle, StrctDiff, StrctDivider, StrctDonut, StrctDrawer, StrctDrawerFooter, StrctDropdown, StrctDropdownDivider, StrctDropdownGroup, StrctDropdownItem, StrctDropdownItemAction, StrctDropdownItemDescription, StrctDropdownItemIcon, StrctDropdownItemLabel, StrctDropdownTrigger, StrctDurationPipe, StrctEmptyState, StrctField, StrctFieldGroup, StrctFieldHint, StrctFieldPrefix, StrctFieldSuffix, StrctFieldValue, StrctFile, StrctFilterBar, StrctFlow, StrctFlowNodeTemplate, StrctFooter, StrctGauge, StrctGroupHeaderDef, StrctHeader, StrctHeaderAction, StrctHeatmap, StrctHero, StrctHotkeysHelp, StrctHotkeysService, StrctIcon, StrctInlineEdit, StrctInput, StrctInputMask, StrctInputOtp, StrctKbd, StrctKnob, StrctLegend, StrctList, StrctListItem, StrctListItemDescription, StrctListItemLeading, StrctListItemMeta, StrctListItemTrailing, StrctLiveIndicator, StrctLogViewer, StrctLogin, StrctMarkdown, StrctMarkdownToken, StrctMediaFrame, StrctMenuPanel, StrctMenuService, StrctMenubar, StrctMetricTile, StrctModal, StrctModalContent, StrctNav, StrctNavItem, StrctNotificationCenter, StrctNumber, StrctOverlay, StrctPageHeader, StrctPageHeaderActions, StrctPageHeaderCrumbs, StrctPageHeaderTitleMeta, StrctPagination, StrctPassword, StrctPopover, StrctPopoverTrigger, StrctProductSwitcher, StrctProgress, StrctQr, StrctRadio, StrctRadioGroup, StrctRail, StrctRange, StrctRatePipe, StrctRating, StrctReorder, StrctReorderGroup, StrctReorderHandle, StrctReorderItem, StrctResizeHandle, StrctRowDetailDef, StrctSearchbox, StrctSectionHeader, StrctSectionHeaderActions, StrctSectionHeaderMeta, StrctSectionMenu, StrctSegmented, StrctSelect, StrctShell, StrctShellService, StrctSiPipe, StrctSignpost, StrctSignpostTrigger, StrctSkeleton, StrctSparkline, StrctSpeedDial, StrctSpinner, StrctSplitButton, StrctSplitter, StrctStack, StrctStackItem, StrctStatusDot, StrctStep, StrctStepAction, StrctSteps, StrctSubmenu, StrctTab, StrctTable, StrctTabs, StrctTag, StrctTagLeading, StrctThemeService, StrctThemeSwitcher, StrctTimeRangePicker, StrctTimeline, StrctTimelineItem, StrctToastOutlet, StrctToastService, StrctToggle, StrctToolbar, StrctToolbarCaption, StrctToolbarSpacer, StrctTooltip, StrctTour, StrctTransfer, StrctTree, StrctTreeNode, StrctTreeSelect, StrctVerticalNav, StrctWatermark, StrctWindow, StrctWindowDock, StrctWindowDockItem, StrctWindowService, StrctWizard, StrctWizardAside, __qrInternals, defaultStateWords, focusFirstIn, keepTabInside, lockBodyScroll, parseAnsi, parseInlines, placeTooltip, provideStrctWizardDefaults, registerStrctIcon, restoreFocus, saveFocusedElement, strctCheckHostDisplay, strctCheckHostInputs, strctClassifyHref, strctComputeDiff, strctDpPad, strctFormatBytes, strctFormatDuration, strctFormatRate, strctFormatSi, strctParseMarkdown, strctQrMatrix, strctShowTooltip, strctValidationIcon, strctValidationTone, unlockBodyScroll };
30625
+ export { DG_LABELS, FOCUSABLE_SELECTOR, STRCT_DP_DOW, STRCT_DP_DOW_FULL, STRCT_DP_MONTHS, STRCT_ICONS, STRCT_ICON_GROUPS, STRCT_ICON_NAMES, STRCT_MASKS, STRCT_MD_LIMITS, STRCT_NOTIFICATION_HISTORY_LIMIT, STRCT_PALETTES, STRCT_RAW_ICONS, STRCT_TIME_RANGE_PRESETS, STRCT_WIZARD_DEFAULTS, StrctAccordion, StrctAccordionPanel, StrctAccordionPanelMeta, StrctAlert, StrctAlertActions, StrctAnnouncer, StrctAvatar, StrctBadge, StrctBreadcrumb, StrctBreadcrumbItem, StrctButton, StrctButtonGroup, StrctBytesPipe, StrctCard, StrctCardBlock, StrctCardFooter, StrctCardHeader, StrctCardHeaderActions, StrctCardHeaderLeading, StrctCardHeaderMeta, StrctCardHeaderNote, StrctCascadeColumn, StrctCascadeHost, StrctCascadeSelect, StrctCellDef, StrctCellStatus, StrctChange, StrctChart, StrctChatAttachment, StrctChatComposer, StrctChatMessage, StrctChatMessageActions, StrctChatThread, StrctCheckbox, StrctChips, StrctCode, StrctCodeInline, StrctColorPicker, StrctCombobox, StrctCommandPalette, StrctConfirmOutlet, StrctConfirmService, StrctContextMenu, StrctContextMenuTrigger, StrctControlDescription, StrctCopy, StrctDatagrid, StrctDatagridActionBar, StrctDatagridActionBarCaption, StrctDatepicker, StrctDatetimePicker, StrctDesc, StrctDescriptionList, StrctDetailPaneActions, StrctDetailPaneMeta, StrctDetailPaneTitle, StrctDiff, StrctDivider, StrctDonut, StrctDrawer, StrctDrawerFooter, StrctDropdown, StrctDropdownDivider, StrctDropdownGroup, StrctDropdownItem, StrctDropdownItemAction, StrctDropdownItemDescription, StrctDropdownItemIcon, StrctDropdownItemLabel, StrctDropdownTrigger, StrctDurationPipe, StrctEmptyState, StrctField, StrctFieldGroup, StrctFieldHint, StrctFieldPrefix, StrctFieldSuffix, StrctFieldValue, StrctFile, StrctFilterBar, StrctFlow, StrctFlowNodeTemplate, StrctFooter, StrctGauge, StrctGroupHeaderDef, StrctHeader, StrctHeaderAction, StrctHeatmap, StrctHero, StrctHotkeysHelp, StrctHotkeysService, StrctIcon, StrctInlineEdit, StrctInput, StrctInputMask, StrctInputOtp, StrctKbd, StrctKnob, StrctLegend, StrctList, StrctListItem, StrctListItemDescription, StrctListItemLeading, StrctListItemMeta, StrctListItemTrailing, StrctLiveIndicator, StrctLogViewer, StrctLogin, StrctMarkdown, StrctMarkdownToken, StrctMediaFrame, StrctMenuPanel, StrctMenuService, StrctMenubar, StrctMetricTile, StrctModal, StrctModalContent, StrctNav, StrctNavItem, StrctNotificationCenter, StrctNumber, StrctOverlay, StrctPageHeader, StrctPageHeaderActions, StrctPageHeaderCrumbs, StrctPageHeaderTitleMeta, StrctPagination, StrctPassword, StrctPopover, StrctPopoverTrigger, StrctProductSwitcher, StrctProgress, StrctQr, StrctRadio, StrctRadioGroup, StrctRail, StrctRange, StrctRatePipe, StrctRating, StrctReorder, StrctReorderGroup, StrctReorderHandle, StrctReorderItem, StrctResizeHandle, StrctRowDetailDef, StrctSearchbox, StrctSectionHeader, StrctSectionHeaderActions, StrctSectionHeaderMeta, StrctSectionMenu, StrctSegmented, StrctSelect, StrctShell, StrctShellService, StrctSiPipe, StrctSignpost, StrctSignpostTrigger, StrctSkeleton, StrctSparkline, StrctSpeedDial, StrctSpinner, StrctSplitButton, StrctSplitter, StrctStack, StrctStackItem, StrctStatusDot, StrctStep, StrctStepAction, StrctSteps, StrctSubmenu, StrctTab, StrctTable, StrctTabs, StrctTag, StrctTagLeading, StrctThemeService, StrctThemeSwitcher, StrctTimeRangePicker, StrctTimeline, StrctTimelineItem, StrctToastOutlet, StrctToastService, StrctToggle, StrctToolbar, StrctToolbarCaption, StrctToolbarSpacer, StrctTooltip, StrctTour, StrctTransfer, StrctTree, StrctTreeNode, StrctTreeSelect, StrctVerticalNav, StrctWatermark, StrctWindow, StrctWindowDock, StrctWindowDockItem, StrctWindowService, StrctWizard, StrctWizardAside, __qrInternals, defaultStateWords, focusFirstIn, keepTabInside, lockBodyScroll, parseAnsi, parseInlines, placeTooltip, provideStrctWizardDefaults, registerStrctIcon, restoreFocus, saveFocusedElement, strctCheckHostDisplay, strctCheckHostInputs, strctClassifyHref, strctComputeDiff, strctDpPad, strctFormatBytes, strctFormatDuration, strctFormatRate, strctFormatSi, strctParseMarkdown, strctQrMatrix, strctShowTooltip, strctValidationIcon, strctValidationTone, unlockBodyScroll };
31998
30626
  //# sourceMappingURL=akcelik-strct.mjs.map