@nldd/design-system 0.8.85 → 0.8.86

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/custom-elements.json +156 -18
  2. package/dist/components/components.js +406 -128
  3. package/dist/components/layout/collection/collection.d.ts +15 -8
  4. package/dist/components/layout/collection/collection.d.ts.map +1 -1
  5. package/dist/components/layout/collection/collection.js +38 -13
  6. package/dist/components/layout/collection/collection.js.map +1 -1
  7. package/dist/components/layout/collection/collection.styles.d.ts.map +1 -1
  8. package/dist/components/layout/collection/collection.styles.js +36 -23
  9. package/dist/components/layout/collection/collection.styles.js.map +1 -1
  10. package/dist/components/layout/container/container.d.ts +11 -3
  11. package/dist/components/layout/container/container.d.ts.map +1 -1
  12. package/dist/components/layout/container/container.js +30 -17
  13. package/dist/components/layout/container/container.js.map +1 -1
  14. package/dist/components/layout/container/container.styles.d.ts.map +1 -1
  15. package/dist/components/layout/container/container.styles.js +19 -21
  16. package/dist/components/layout/container/container.styles.js.map +1 -1
  17. package/dist/components/layout/page/page.d.ts +23 -2
  18. package/dist/components/layout/page/page.d.ts.map +1 -1
  19. package/dist/components/layout/page/page.js +56 -2
  20. package/dist/components/layout/page/page.js.map +1 -1
  21. package/dist/components/layout/page/page.styles.d.ts.map +1 -1
  22. package/dist/components/layout/page/page.styles.js +33 -8
  23. package/dist/components/layout/page/page.styles.js.map +1 -1
  24. package/dist/components/layout/page-sections/sidebar-section/sidebar-section.styles.d.ts.map +1 -1
  25. package/dist/components/layout/page-sections/sidebar-section/sidebar-section.styles.js +7 -5
  26. package/dist/components/layout/page-sections/sidebar-section/sidebar-section.styles.js.map +1 -1
  27. package/dist/components/layout/sheet/sheet.styles.js +2 -2
  28. package/dist/components/layout/split-views/bar-split-view/bar-split-view.d.ts +1 -1
  29. package/dist/components/layout/split-views/bar-split-view/bar-split-view.d.ts.map +1 -1
  30. package/dist/components/layout/split-views/bar-split-view/bar-split-view.js +13 -13
  31. package/dist/components/layout/split-views/bar-split-view/bar-split-view.js.map +1 -1
  32. package/dist/components/layout/split-views/bar-split-view/bar-split-view.styles.js +1 -1
  33. package/dist/components/lists-and-tables/list/list.d.ts +13 -3
  34. package/dist/components/lists-and-tables/list/list.d.ts.map +1 -1
  35. package/dist/components/lists-and-tables/list/list.js +39 -3
  36. package/dist/components/lists-and-tables/list/list.js.map +1 -1
  37. package/dist/components/lists-and-tables/list/list.styles.d.ts.map +1 -1
  38. package/dist/components/lists-and-tables/list/list.styles.js +8 -1
  39. package/dist/components/lists-and-tables/list/list.styles.js.map +1 -1
  40. package/dist/components/lists-and-tables/list/list.template.d.ts +4 -1
  41. package/dist/components/lists-and-tables/list/list.template.d.ts.map +1 -1
  42. package/dist/components/lists-and-tables/list/list.template.js +31 -12
  43. package/dist/components/lists-and-tables/list/list.template.js.map +1 -1
  44. package/dist/components/lists-and-tables/list-item/list-item.d.ts +8 -3
  45. package/dist/components/lists-and-tables/list-item/list-item.d.ts.map +1 -1
  46. package/dist/components/lists-and-tables/list-item/list-item.js +35 -9
  47. package/dist/components/lists-and-tables/list-item/list-item.js.map +1 -1
  48. package/dist/components/lists-and-tables/list-item/list-item.template.d.ts +1 -1
  49. package/dist/components/lists-and-tables/list-item/list-item.template.d.ts.map +1 -1
  50. package/dist/components/lists-and-tables/list-item/list-item.template.js +18 -1
  51. package/dist/components/lists-and-tables/list-item/list-item.template.js.map +1 -1
  52. package/dist/components/navigation/top-navigation-bar/top-navigation-bar.styles.d.ts.map +1 -1
  53. package/dist/components/navigation/top-navigation-bar/top-navigation-bar.styles.js +50 -12
  54. package/dist/components/navigation/top-navigation-bar/top-navigation-bar.styles.js.map +1 -1
  55. package/dist/components/status-and-feedback/modal-dialog/modal-dialog.styles.js +3 -3
  56. package/dist/components/status-and-feedback/progress-circle/progress-circle.styles.d.ts.map +1 -1
  57. package/dist/components/status-and-feedback/progress-circle/progress-circle.styles.js +15 -0
  58. package/dist/components/status-and-feedback/progress-circle/progress-circle.styles.js.map +1 -1
  59. package/dist/components/status-and-feedback/progress-circle/progress-circle.template.d.ts.map +1 -1
  60. package/dist/components/status-and-feedback/progress-circle/progress-circle.template.js +4 -3
  61. package/dist/components/status-and-feedback/progress-circle/progress-circle.template.js.map +1 -1
  62. package/dist/components/visibility-mixin-BU1vIMW7.js +161 -0
  63. package/dist/favicon.svg +4 -0
  64. package/dist/nldd.min.js +775 -646
  65. package/dist/touch-icon.png +0 -0
  66. package/dist/types/vue.d.ts +24 -10
  67. package/dist/utilities/spacing-scale.d.ts +26 -0
  68. package/dist/utilities/spacing-scale.d.ts.map +1 -0
  69. package/dist/utilities/spacing-scale.js +54 -0
  70. package/dist/utilities/spacing-scale.js.map +1 -0
  71. package/dist/utilities/visibility-mixin.d.ts +11 -8
  72. package/dist/utilities/visibility-mixin.d.ts.map +1 -1
  73. package/dist/utilities/visibility-mixin.js +58 -45
  74. package/dist/utilities/visibility-mixin.js.map +1 -1
  75. package/dist/utilities/visibility-mixin.styles.d.ts +14 -0
  76. package/dist/utilities/visibility-mixin.styles.d.ts.map +1 -0
  77. package/dist/utilities/visibility-mixin.styles.js +52 -0
  78. package/dist/utilities/visibility-mixin.styles.js.map +1 -0
  79. package/package.json +5 -2
  80. package/dist/components/visibility-mixin-vbfhyo7f.js +0 -104
@@ -7,7 +7,7 @@ import { i as PopupAnchorController, n as NLDDTooltip, r as NLDDActivityIndicato
7
7
  import { t as NLDDIcon } from "./icon-Cd4BloIM.js";
8
8
  import { n as isPointerMode, r as isTouchMode, t as isKeyboardMode } from "./input-modality-DhTeUxKi.js";
9
9
  import { t as breakpoints } from "./breakpoints-CkxsiRgH.js";
10
- import { t as VisibilityMixin } from "./visibility-mixin-vbfhyo7f.js";
10
+ import { t as VisibilityMixin } from "./visibility-mixin-BU1vIMW7.js";
11
11
  import { $ as placeholder, A as syntaxTree, B as Tree, C as indentNodeProp, D as matchBrackets, E as languageDataProp, F as NodeProp, G as ViewPlugin, H as Decoration, I as NodeSet, J as dropCursor, K as WidgetType, L as NodeType, M as styleTags, N as tags, S as getIndentation, T as indentUnit, U as Direction, V as parseMixed, W as EditorView, X as keymap, Z as lineNumbers, a as HighlightStyle, at as EditorSelection, b as foldService, bt as findClusterBreak, c as Language, d as ParseContext, f as StreamLanguage, ft as StateEffect, h as defineLanguageFacet, ht as Transaction, it as Compartment, j as Tag, k as syntaxHighlighting, l as LanguageDescription, lt as Prec, mt as Text, nt as ChangeDesc, o as IndentContext, ot as EditorState, pt as StateField, q as drawSelection, rt as ChangeSet, st as Facet, tt as Annotation, u as LanguageSupport, ut as RangeSetBuilder, vt as combineConfig, w as indentString, x as getIndentUnit, y as foldNodeProp, yt as countColumn, z as Parser } from "./dist-C6gnorjP.js";
12
12
  import { i as completionStatus, n as autocompletion, s as startCompletion, t as CompletionContext } from "./dist-B_3OW7pC.js";
13
13
  import { n as html$2, r as htmlCompletionSource } from "./dist-762GHPyH.js";
@@ -7468,10 +7468,10 @@ var containerStyles = css`
7468
7468
  --_justify-content: initial;
7469
7469
  --_justify-items: initial;
7470
7470
  --_align-items: initial;
7471
- --_gap: 0;
7472
- --_sm-gap: var(--_gap);
7473
- --_md-gap: var(--_gap);
7474
- --_lg-gap: var(--_gap);
7471
+ --_sm-gap: 0;
7472
+ --_md-gap: 0;
7473
+ --_lg-gap: 0;
7474
+ --_gap: var(--_sm-gap);
7475
7475
  --_padding-top: 0;
7476
7476
  --_padding-right: 0;
7477
7477
  --_padding-bottom: 0;
@@ -7492,19 +7492,17 @@ var containerStyles = css`
7492
7492
  --_slot-sm-order: var(--_slot-order);
7493
7493
  --_slot-md-order: var(--_slot-order);
7494
7494
  --_slot-lg-order: var(--_slot-order);
7495
- /* One resolved gap for every layout to read. Multicol splits the gap
7496
- over two properties (column-gap plus a margin on the items), so a
7497
- per-breakpoint declaration would have to be repeated in three
7498
- rules; swapping the var keeps that in one place. */
7499
- --_resolved-gap: var(--_gap);
7500
-
7501
- @media (max-width: ${smMax$17}) { --_resolved-gap: var(--_sm-gap); }
7502
- @media (min-width: ${mdMin$20}) and (max-width: ${mdMax$13}) { --_resolved-gap: var(--_md-gap); }
7503
- @media (min-width: ${lgMin$14}) { --_resolved-gap: var(--_lg-gap); }
7504
-
7505
- @container layout-container (max-width: ${smMax$17}) { --_resolved-gap: var(--_sm-gap); }
7506
- @container layout-container (min-width: ${mdMin$20}) and (max-width: ${mdMax$13}) { --_resolved-gap: var(--_md-gap); }
7507
- @container layout-container (min-width: ${lgMin$14}) { --_resolved-gap: var(--_lg-gap); }
7495
+ /* Two sets, because a container inside a layout-container follows that
7496
+ container and anywhere else the viewport. Multicol reads the gap in three
7497
+ rules, so the value is swapped here rather than declared in each. The
7498
+ bare --_gap is what stands when neither set matches. */
7499
+ @media (max-width: ${smMax$17}) { --_gap: var(--_sm-gap); }
7500
+ @media (min-width: ${mdMin$20}) and (max-width: ${mdMax$13}) { --_gap: var(--_md-gap); }
7501
+ @media (min-width: ${lgMin$14}) { --_gap: var(--_lg-gap); }
7502
+
7503
+ @container layout-container (max-width: ${smMax$17}) { --_gap: var(--_sm-gap); }
7504
+ @container layout-container (min-width: ${mdMin$20}) and (max-width: ${mdMax$13}) { --_gap: var(--_md-gap); }
7505
+ @container layout-container (min-width: ${lgMin$14}) { --_gap: var(--_lg-gap); }
7508
7506
 
7509
7507
  display: block;
7510
7508
  width: var(--_width);
@@ -7587,7 +7585,7 @@ var containerStyles = css`
7587
7585
  justify-content: var(--_justify-content);
7588
7586
  justify-items: var(--_justify-items);
7589
7587
  align-items: var(--_align-items);
7590
- gap: var(--_resolved-gap);
7588
+ gap: var(--_gap);
7591
7589
  }
7592
7590
 
7593
7591
  :host([layout="row"]) .container__inner {
@@ -7610,7 +7608,7 @@ var containerStyles = css`
7610
7608
  :host([layout="columns"]) .container__inner {
7611
7609
  display: block;
7612
7610
  columns: var(--_min-column-width);
7613
- column-gap: var(--_resolved-gap);
7611
+ column-gap: var(--_gap);
7614
7612
  }
7615
7613
 
7616
7614
  :host([layout="columns"]) ::slotted(*) {
@@ -7623,14 +7621,14 @@ var containerStyles = css`
7623
7621
  :host([layout="lanes"]) .container__inner {
7624
7622
  display: block;
7625
7623
  columns: var(--_min-column-width);
7626
- column-gap: var(--_resolved-gap);
7624
+ column-gap: var(--_gap);
7627
7625
  }
7628
7626
 
7629
7627
  :host([layout="lanes"]) ::slotted(*) {
7630
7628
  break-inside: avoid;
7631
7629
  /* multicol has no row-gap; item margin supplies the vertical gap. The
7632
7630
  native branch resets this (grid-lanes gap covers both axes). */
7633
- margin-bottom: var(--_resolved-gap);
7631
+ margin-bottom: var(--_gap);
7634
7632
  }
7635
7633
 
7636
7634
  @supports (display: grid-lanes) {
@@ -7754,6 +7752,57 @@ function containerTemplate(component) {
7754
7752
  `;
7755
7753
  }
7756
7754
  //#endregion
7755
+ //#region src/utilities/spacing-scale.ts
7756
+ /**
7757
+ * The spacing scale, for the attributes that take a step of it.
7758
+ *
7759
+ * Spacing is rhythm: the point of a scale is that the space between things is
7760
+ * the same handful of numbers on every page, so a value beside it is not a
7761
+ * finer choice but a break in the pattern. These attributes take a step and
7762
+ * nothing else, and this is the one place that turns a step into its token.
7763
+ *
7764
+ * Which also settles what the attribute means. `gap="16"` in a component that
7765
+ * expected a CSS length wrote `--_gap: 16`, which is not one: the declaration
7766
+ * fell away and the gap became zero with nothing said. Anything this cannot
7767
+ * place is refused and named in dev instead, a length included — writing an
7768
+ * unplaceable value is that same silent zero.
7769
+ */
7770
+ /** Every step there is. `'0'` is a value, not the absence of one. */
7771
+ var SPACING_SCALE = [
7772
+ "0",
7773
+ "2",
7774
+ "4",
7775
+ "6",
7776
+ "8",
7777
+ "10",
7778
+ "12",
7779
+ "16",
7780
+ "20",
7781
+ "24",
7782
+ "28",
7783
+ "32",
7784
+ "40",
7785
+ "44",
7786
+ "48",
7787
+ "56",
7788
+ "64",
7789
+ "80",
7790
+ "96"
7791
+ ];
7792
+ /**
7793
+ * A step of the scale as the token it stands for.
7794
+ *
7795
+ * Returns null when there is nothing to set, so a caller can remove the
7796
+ * property rather than write an empty one. `element` and `attribute` are only
7797
+ * there to name the offender in a dev warning.
7798
+ */
7799
+ function spacingToValue(value, element, attribute) {
7800
+ if (value === void 0 || value === "") return null;
7801
+ const step = String(value).trim();
7802
+ if (SPACING_SCALE.includes(step)) return step === "0" ? "0" : `var(--primitives-space-${step})`;
7803
+ return null;
7804
+ }
7805
+ //#endregion
7757
7806
  //#region src/components/layout/container/container.ts
7758
7807
  /**
7759
7808
  * Nederlandse Digitale Dienst Container Component (Lit + TypeScript)
@@ -7804,8 +7853,11 @@ function containerTemplate(component) {
7804
7853
  * independent of the surrounding page width.
7805
7854
  *
7806
7855
  * `layout="lanes"` packs items into balanced columns using native CSS grid
7807
- * lanes where supported, falling back to CSS multicol (column-order)
7808
- * elsewhere. CSS-only, no JS. Honours `gap` on both axes and `column-count`.
7856
+ * lanes where supported, falling back to CSS multicol (column-order) elsewhere.
7857
+ * CSS-only, no JS. Honours `gap` on both axes and `column-count`. Note that
7858
+ * nldd-collection's lanes falls back to its own grid rather than to multicol:
7859
+ * that component pages, and multicol redistributes the whole set every time
7860
+ * load-more adds to it.
7809
7861
  *
7810
7862
  * @element nldd-container
7811
7863
  *
@@ -7877,10 +7929,11 @@ var ORDER_ATTRS = [
7877
7929
  "md-order",
7878
7930
  "lg-order"
7879
7931
  ];
7880
- function sizeToValue(size) {
7881
- if (size === void 0) return null;
7882
- if (size === "0") return "0";
7883
- return `var(--primitives-space-${size})`;
7932
+ /** A step as its token. `attribute` only names the offender in a dev warning,
7933
+ * so it has to be the attribute the value was written on: with a dozen padding
7934
+ * attributes, "padding/gap is not a step" does not say which one to fix. */
7935
+ function sizeToValue(size, attribute) {
7936
+ return spacingToValue(size, "nldd-container", attribute);
7884
7937
  }
7885
7938
  var NLDDContainer = (_NLDDContainer = class NLDDContainer extends LitElement {
7886
7939
  constructor(..._args) {
@@ -7983,10 +8036,10 @@ var NLDDContainer = (_NLDDContainer = class NLDDContainer extends LitElement {
7983
8036
  setProp("--_align-items", horizontal);
7984
8037
  setProp("--_justify-items", null);
7985
8038
  }
7986
- setProp("--_gap", sizeToValue(this.gap));
7987
- setProp("--_sm-gap", sizeToValue(this.smGap));
7988
- setProp("--_md-gap", sizeToValue(this.mdGap));
7989
- setProp("--_lg-gap", sizeToValue(this.lgGap));
8039
+ const plainGap = sizeToValue(this.gap, "gap");
8040
+ setProp("--_sm-gap", sizeToValue(this.smGap, "sm-gap") ?? plainGap);
8041
+ setProp("--_md-gap", sizeToValue(this.mdGap, "md-gap") ?? plainGap);
8042
+ setProp("--_lg-gap", sizeToValue(this.lgGap, "lg-gap") ?? plainGap);
7990
8043
  for (const scope of [
7991
8044
  "",
7992
8045
  "sm",
@@ -7995,16 +8048,25 @@ var NLDDContainer = (_NLDDContainer = class NLDDContainer extends LitElement {
7995
8048
  ]) {
7996
8049
  const [top, right, bottom, left] = this.resolvePadding(scope);
7997
8050
  const prefix = scope ? `${scope}-` : "";
7998
- setProp(`--_${prefix}padding-top`, sizeToValue(top));
7999
- setProp(`--_${prefix}padding-right`, sizeToValue(right));
8000
- setProp(`--_${prefix}padding-bottom`, sizeToValue(bottom));
8001
- setProp(`--_${prefix}padding-left`, sizeToValue(left));
8051
+ setProp(`--_${prefix}padding-top`, sizeToValue(top?.size, top?.attribute ?? "padding"));
8052
+ setProp(`--_${prefix}padding-right`, sizeToValue(right?.size, right?.attribute ?? "padding"));
8053
+ setProp(`--_${prefix}padding-bottom`, sizeToValue(bottom?.size, bottom?.attribute ?? "padding"));
8054
+ setProp(`--_${prefix}padding-left`, sizeToValue(left?.size, left?.attribute ?? "padding"));
8002
8055
  }
8003
8056
  }
8057
+ /** The four sides, each with the attribute it came from: `padding-top` wins
8058
+ * over `padding-block`, which wins over `padding`, and a warning about a bad
8059
+ * value has to name the one that was actually written. */
8004
8060
  resolvePadding(scope) {
8005
8061
  const get = (key) => {
8006
8062
  const prop = scope ? `${scope}${key}` : key.charAt(0).toLowerCase() + key.slice(1);
8007
- return this[prop];
8063
+ const size = this[prop];
8064
+ if (size === void 0) return void 0;
8065
+ const name = key.replace(/([A-Z])/g, (match, letter, index) => (index ? "-" : "") + letter.toLowerCase());
8066
+ return {
8067
+ size,
8068
+ attribute: scope ? `${scope}-${name}` : name
8069
+ };
8008
8070
  };
8009
8071
  const all = get("Padding");
8010
8072
  const inline = get("PaddingInline");
@@ -8247,7 +8309,10 @@ var listStyles = css`
8247
8309
  box-sizing: border-box;
8248
8310
  }
8249
8311
 
8250
- /* Nothing in it and nothing said about it: not a thing on the page. */
8312
+ /* Nothing in it, nothing said about it and no controls of its own: not a
8313
+ thing on the page, so a parent that spaces its children keeps no room for
8314
+ it either. The surface is already gone with .list__main; this takes the
8315
+ box out of the flow. */
8251
8316
  :host(.is-blank) {
8252
8317
  display: none;
8253
8318
  }
@@ -8330,6 +8395,10 @@ var listStyles = css`
8330
8395
  display: none;
8331
8396
  }
8332
8397
 
8398
+ .list__search-bar[hidden] {
8399
+ display: none;
8400
+ }
8401
+
8333
8402
  .list__main {
8334
8403
  display: flex;
8335
8404
  flex-direction: column;
@@ -8528,18 +8597,22 @@ var listStyles = css`
8528
8597
  `;
8529
8598
  //#endregion
8530
8599
  //#region src/components/lists-and-tables/list/list.template.ts
8531
- var template$14 = ({ itemsLabel, hasToolbar, type, isEmpty, listbox }) => {
8600
+ var template$14 = ({ itemsLabel, hasToolbar, type, isEmpty, hasItems, hasEmptyState, hasNoResultsState, listbox }) => {
8532
8601
  const isNavigation = type === "navigation";
8533
8602
  const isListbox = type === "listbox";
8534
8603
  const skipItemsLabel = isNavigation || isListbox;
8535
- const itemsRole = isListbox ? "listbox" : type === "tree" ? "tree" : "list";
8536
- const showEmpty = isEmpty && !(isListbox && listbox.searchValue === "");
8537
- const showMain = !isEmpty || showEmpty;
8604
+ const itemsRole = isListbox ? "listbox" : type === "tree" ? "tree" : type === "radiogroup" ? "radiogroup" : "list";
8605
+ const isNoResults = isEmpty && hasItems;
8606
+ const noResultsFallsBack = isNoResults && !hasNoResultsState && hasEmptyState;
8607
+ const showNoResults = isNoResults && hasNoResultsState && !(isListbox && listbox.searchValue === "");
8608
+ const showEmpty = !isNoResults && isEmpty && hasEmptyState || noResultsFallsBack && !(isListbox && listbox.searchValue === "");
8609
+ const showMain = !isEmpty || showEmpty || showNoResults;
8610
+ const showControls = hasItems;
8538
8611
  return html`
8539
8612
  <div class="list">
8540
8613
  <div class="list__header">
8541
8614
  ${isListbox ? html`
8542
- <div class="list__search-bar">
8615
+ <div class="list__search-bar" ?hidden=${!showControls}>
8543
8616
  <div class="list__search-field">
8544
8617
  <label class="list__search-field-label">
8545
8618
  <div class="list__search-field-icon"
@@ -8583,7 +8656,7 @@ var template$14 = ({ itemsLabel, hasToolbar, type, isEmpty, listbox }) => {
8583
8656
  </div>
8584
8657
  </div>
8585
8658
  ` : nothing}
8586
- <div class="list__toolbar" ?hidden=${!hasToolbar}>
8659
+ <div class="list__toolbar" ?hidden=${!hasToolbar || !showControls}>
8587
8660
  <slot name="toolbar"></slot>
8588
8661
  </div>
8589
8662
  </div>
@@ -8597,9 +8670,10 @@ var template$14 = ({ itemsLabel, hasToolbar, type, isEmpty, listbox }) => {
8597
8670
  <slot></slot>
8598
8671
  </div>
8599
8672
  <div class="list__empty"
8600
- ?hidden=${!showEmpty}
8673
+ ?hidden=${!showEmpty && !showNoResults}
8601
8674
  >
8602
- <slot name="empty"></slot>
8675
+ <slot name="empty" ?hidden=${!showEmpty}></slot>
8676
+ <slot name="no-results" ?hidden=${!showNoResults}></slot>
8603
8677
  </div>
8604
8678
  </div>
8605
8679
  </div>
@@ -8646,6 +8720,9 @@ var NLDDList = (_NLDDList2 = class NLDDList extends LitElement {
8646
8720
  this._mergedTranslations = { ...nlddListTranslations };
8647
8721
  this._hasToolbar = false;
8648
8722
  this._hasSearchBarEnd = false;
8723
+ this._hasEmptyState = false;
8724
+ this._hasNoResultsState = false;
8725
+ this._hasItems = false;
8649
8726
  this._isEmpty = false;
8650
8727
  this._searchValue = "";
8651
8728
  this._activeId = "";
@@ -8861,6 +8938,8 @@ var NLDDList = (_NLDDList2 = class NLDDList extends LitElement {
8861
8938
  toolbarSlot?.addEventListener("slotchange", () => queueMicrotask(() => {
8862
8939
  this._hasToolbar = toolbarSlot.assignedElements().length > 0;
8863
8940
  }));
8941
+ (this.shadowRoot?.querySelector("slot[name=\"empty\"]"))?.addEventListener("slotchange", () => queueMicrotask(() => this._updateEmpty()));
8942
+ (this.shadowRoot?.querySelector("slot[name=\"no-results\"]"))?.addEventListener("slotchange", () => queueMicrotask(() => this._updateEmpty()));
8864
8943
  this._itemsObserver = new MutationObserver((mutations) => {
8865
8944
  const isRow = (node) => node instanceof Element && node.tagName.toLowerCase() === "nldd-list-item";
8866
8945
  if (!mutations.some((m) => {
@@ -9008,7 +9087,10 @@ var NLDDList = (_NLDDList2 = class NLDDList extends LitElement {
9008
9087
  _updateEmpty() {
9009
9088
  const items = this._getItems();
9010
9089
  this._isEmpty = items.length === 0 || items.every((item) => item.hasAttribute("hidden"));
9011
- this.classList.toggle("is-blank", this._isEmpty && !this.querySelector(":scope > [slot=\"empty\"]"));
9090
+ this._hasItems = items.length > 0;
9091
+ this._hasEmptyState = !!this.querySelector(":scope > [slot=\"empty\"]");
9092
+ this._hasNoResultsState = !!this.querySelector(":scope > [slot=\"no-results\"]");
9093
+ this.classList.toggle("is-blank", this._isEmpty && !this._hasItems && !this._hasEmptyState);
9012
9094
  this._warnSilentEmpty();
9013
9095
  }
9014
9096
  /**
@@ -9431,6 +9513,9 @@ var NLDDList = (_NLDDList2 = class NLDDList extends LitElement {
9431
9513
  hasToolbar: this._hasToolbar,
9432
9514
  type: this.type,
9433
9515
  isEmpty: this._isEmpty,
9516
+ hasItems: this._hasItems,
9517
+ hasEmptyState: this._hasEmptyState,
9518
+ hasNoResultsState: this._hasNoResultsState,
9434
9519
  listbox: {
9435
9520
  listboxId: this._listboxId,
9436
9521
  searchValue: this._searchValue,
@@ -9477,6 +9562,9 @@ __decorate([property({ type: Object })], NLDDList.prototype, "translations", voi
9477
9562
  __decorate([state()], NLDDList.prototype, "_mergedTranslations", void 0);
9478
9563
  __decorate([state()], NLDDList.prototype, "_hasToolbar", void 0);
9479
9564
  __decorate([state()], NLDDList.prototype, "_hasSearchBarEnd", void 0);
9565
+ __decorate([state()], NLDDList.prototype, "_hasEmptyState", void 0);
9566
+ __decorate([state()], NLDDList.prototype, "_hasNoResultsState", void 0);
9567
+ __decorate([state()], NLDDList.prototype, "_hasItems", void 0);
9480
9568
  __decorate([state()], NLDDList.prototype, "_isEmpty", void 0);
9481
9569
  __decorate([state()], NLDDList.prototype, "_searchValue", void 0);
9482
9570
  __decorate([state()], NLDDList.prototype, "_activeId", void 0);
@@ -9905,7 +9993,7 @@ var listItemStyles = css`
9905
9993
  //#region src/components/lists-and-tables/list-item/list-item.template.ts
9906
9994
  var content = html`<slot></slot>`;
9907
9995
  var divider = html`<div class="list-item__divider"></div>`;
9908
- var template$13 = (button, href, target, rel, opensInNewTabLabel, actionTabindex, isHighlighted = false, checkbox = false, checked = false, expanded, showChildren = false, hasCheckedSegment = false, disabled = false) => {
9996
+ var template$13 = (button, href, target, rel, opensInNewTabLabel, actionTabindex, isHighlighted = false, checkbox = false, radio = false, checked = false, expanded, showChildren = false, hasCheckedSegment = false, disabled = false) => {
9909
9997
  const ariaExpanded = expanded === void 0 ? nothing : String(expanded);
9910
9998
  const children = showChildren ? html`<div class="list-item__children"
9911
9999
  role="group"
@@ -9938,6 +10026,16 @@ var template$13 = (button, href, target, rel, opensInNewTabLabel, actionTabindex
9938
10026
  >${content}</button>
9939
10027
  ${divider}
9940
10028
  </div>${children}`;
10029
+ if (radio) return html`<div class=${blockClass}>
10030
+ <button class="list-item__action"
10031
+ type="button"
10032
+ role="radio"
10033
+ ?disabled=${disabled}
10034
+ aria-checked=${String(checked)}
10035
+ tabindex=${actionTabindex ?? nothing}
10036
+ >${content}</button>
10037
+ ${divider}
10038
+ </div>${children}`;
9941
10039
  if (button) return html`<div class=${blockClass}>
9942
10040
  <button class="list-item__action"
9943
10041
  type="button"
@@ -10018,6 +10116,7 @@ var NLDDListItem = (_NLDDListItem2 = class NLDDListItem extends withTranslations
10018
10116
  this.current = false;
10019
10117
  this.button = false;
10020
10118
  this.checkbox = false;
10119
+ this.radio = false;
10021
10120
  this.checked = false;
10022
10121
  this.disabled = false;
10023
10122
  this.reorderable = false;
@@ -10050,6 +10149,14 @@ var NLDDListItem = (_NLDDListItem2 = class NLDDListItem extends withTranslations
10050
10149
  composed: true
10051
10150
  }));
10052
10151
  }
10152
+ if (this.radio && !this.checkbox && !this.href && !this.checked && e.composedPath().includes(this._action)) {
10153
+ this.checked = true;
10154
+ this.dispatchEvent(new CustomEvent("change", {
10155
+ detail: { checked: true },
10156
+ bubbles: true,
10157
+ composed: true
10158
+ }));
10159
+ }
10053
10160
  };
10054
10161
  this._handleFocusIn = () => {
10055
10162
  this._action?.classList.toggle("is-pointer-focus", isPointerMode());
@@ -10171,7 +10278,7 @@ var NLDDListItem = (_NLDDListItem2 = class NLDDListItem extends withTranslations
10171
10278
  */
10172
10279
  get _isRovingStop() {
10173
10280
  if (this.disabled) return false;
10174
- if (this.href || this.button || this.checkbox) return true;
10281
+ if (this.href || this.button || this.checkbox || this.radio) return true;
10175
10282
  if (this._ownDescendants("nldd-list-item-segment:not([disabled])").length > 0) return true;
10176
10283
  return this._rovingControls().some((control) => !control.hasAttribute("disabled"));
10177
10284
  }
@@ -10205,7 +10312,7 @@ var NLDDListItem = (_NLDDListItem2 = class NLDDListItem extends withTranslations
10205
10312
  * inside `:host()`, and the row's own padding is what has to change — a
10206
10313
  * `::slotted()` rule could only reach the segment. */
10207
10314
  _updateInteractive() {
10208
- const interactive = !!this.href || this.button || this.checkbox || this._ownDescendants("nldd-list-item-segment").length > 0;
10315
+ const interactive = !!this.href || this.button || this.checkbox || this.radio || this._ownDescendants("nldd-list-item-segment").length > 0;
10209
10316
  this.classList.toggle("is-interactive", interactive);
10210
10317
  const ownChildren = Array.from(this.children).filter((el) => el.getAttribute("slot") !== "children");
10211
10318
  const isSegment = (el) => el?.tagName.toLowerCase() === "nldd-list-item-segment";
@@ -10262,8 +10369,8 @@ var NLDDListItem = (_NLDDListItem2 = class NLDDListItem extends withTranslations
10262
10369
  });
10263
10370
  }
10264
10371
  updated(changed) {
10265
- if (changed.has("selected") || changed.has("current") || changed.has("button") || changed.has("checkbox") || changed.has("href") || changed.has("_parentType")) this._updateAriaState();
10266
- if (changed.has("button") || changed.has("checkbox") || changed.has("href")) this._updateInteractive();
10372
+ if (changed.has("selected") || changed.has("current") || changed.has("button") || changed.has("checkbox") || changed.has("radio") || changed.has("href") || changed.has("_parentType")) this._updateAriaState();
10373
+ if (changed.has("button") || changed.has("checkbox") || changed.has("radio") || changed.has("href")) this._updateInteractive();
10267
10374
  if (changed.has("expanded")) {
10268
10375
  this._relayExpanded();
10269
10376
  this._warnOnStrayExpanded();
@@ -10334,7 +10441,10 @@ var NLDDListItem = (_NLDDListItem2 = class NLDDListItem extends withTranslations
10334
10441
  this.childRows.forEach(fn);
10335
10442
  }
10336
10443
  _updateAriaState() {
10337
- if (this._parentType === "tree") {
10444
+ if (this._parentType === "radiogroup") {
10445
+ this.setAttribute("role", "none");
10446
+ this.removeAttribute("aria-selected");
10447
+ } else if (this._parentType === "tree") {
10338
10448
  this.setAttribute("role", "treeitem");
10339
10449
  this.removeAttribute("aria-selected");
10340
10450
  } else if (this._isListboxOption) {
@@ -10577,7 +10687,7 @@ var NLDDListItem = (_NLDDListItem2 = class NLDDListItem extends withTranslations
10577
10687
  render() {
10578
10688
  const opensInNewTabLabel = this.href && this.target === "_blank" ? this._t("components.list-item.opens-in-new-tab-label") : void 0;
10579
10689
  const actionTabindex = this._isListboxOption ? "-1" : this._arrowNavigation ? this._rovingActive ? "0" : "-1" : void 0;
10580
- return template$13(this.button, this.href, this.target, this.rel, opensInNewTabLabel, actionTabindex, this._highlighted, this.checkbox, this.checked, this._resolvedExpanded, this._showChildren, this._hasCheckedSegment, this.disabled);
10690
+ return template$13(this.button, this.href, this.target, this.rel, opensInNewTabLabel, actionTabindex, this._highlighted, this.checkbox, this.radio, this.checked, this._resolvedExpanded, this._showChildren, this._hasCheckedSegment, this.disabled);
10581
10691
  }
10582
10692
  }, _NLDDListItem = _NLDDListItem2, _NLDDListItem2.styles = [listItemStyles], _NLDDListItem2.SIZED_CELLS = "nldd-text-cell, nldd-drag-handle-cell", _NLDDListItem2);
10583
10693
  __decorate([property({
@@ -10600,6 +10710,10 @@ __decorate([property({
10600
10710
  type: Boolean,
10601
10711
  reflect: true
10602
10712
  })], NLDDListItem.prototype, "checkbox", void 0);
10713
+ __decorate([property({
10714
+ type: Boolean,
10715
+ reflect: true
10716
+ })], NLDDListItem.prototype, "radio", void 0);
10603
10717
  __decorate([property({
10604
10718
  type: Boolean,
10605
10719
  reflect: true
@@ -35580,7 +35694,7 @@ var barSplitViewStyles = css`
35580
35694
  /* Root-scroll mode: the document scrolls (ScrollModeController /
35581
35695
  --context-scroll-mode). Bars become sticky layers that stay put while the
35582
35696
  main content scrolls under them; their measured heights are published into
35583
- --context-layer-top/bottom (see bar-split-view.ts) so a descendant
35697
+ --context-inset-top/bottom (see bar-split-view.ts) so a descendant
35584
35698
  nldd-page's sticky header/footer stack below/above them. The sticky insets
35585
35699
  and those offsets are set by JS; bars paint above the content. */
35586
35700
  :host([data-scroll="root"]) {
@@ -35876,12 +35990,12 @@ var mdMaxPx = parseInt(breakpoints.mdMax);
35876
35990
  var NLDDBarSplitView = (_NLDDBarSplitView = class NLDDBarSplitView extends LitElement {
35877
35991
  constructor(..._args) {
35878
35992
  super(..._args);
35879
- this._scrollMode = new ScrollModeController(this, () => this._updateLayerOffsets());
35993
+ this._scrollMode = new ScrollModeController(this, () => this._updateInsets());
35880
35994
  this.background = "inherit";
35881
35995
  this._currentBreakpoint = null;
35882
35996
  this._observer = null;
35883
35997
  this._resizeObserver = null;
35884
- this._updateLayerOffsets = () => {
35998
+ this._updateInsets = () => {
35885
35999
  const block = this.shadowRoot?.querySelector(".bar-split-view") ?? null;
35886
36000
  const main = block?.querySelector(".bar-split-view__main") ?? null;
35887
36001
  if (!block || !main) return;
@@ -35892,25 +36006,25 @@ var NLDDBarSplitView = (_NLDDBarSplitView = class NLDDBarSplitView extends LitEl
35892
36006
  el.style.top = "";
35893
36007
  el.style.bottom = "";
35894
36008
  });
35895
- main.style.removeProperty("--context-layer-top");
35896
- main.style.removeProperty("--context-layer-bottom");
36009
+ main.style.removeProperty("--context-inset-top");
36010
+ main.style.removeProperty("--context-inset-bottom");
35897
36011
  return;
35898
36012
  }
35899
36013
  const cs = getComputedStyle(this);
35900
- let top = parseFloat(cs.getPropertyValue("--context-layer-top")) || 0;
36014
+ let top = parseFloat(cs.getPropertyValue("--context-inset-top")) || 0;
35901
36015
  for (let i = 0; i < mainIndex; i++) {
35902
36016
  children[i].style.top = `${top}px`;
35903
36017
  children[i].style.bottom = "";
35904
36018
  top += children[i].offsetHeight;
35905
36019
  }
35906
- main.style.setProperty("--context-layer-top", `${top}px`);
35907
- let bottom = parseFloat(cs.getPropertyValue("--context-layer-bottom")) || 0;
36020
+ main.style.setProperty("--context-inset-top", `${top}px`);
36021
+ let bottom = parseFloat(cs.getPropertyValue("--context-inset-bottom")) || 0;
35908
36022
  for (let i = children.length - 1; i > mainIndex; i--) {
35909
36023
  children[i].style.bottom = `${bottom}px`;
35910
36024
  children[i].style.top = "";
35911
36025
  bottom += children[i].offsetHeight;
35912
36026
  }
35913
- main.style.setProperty("--context-layer-bottom", `${bottom}px`);
36027
+ main.style.setProperty("--context-inset-bottom", `${bottom}px`);
35914
36028
  };
35915
36029
  }
35916
36030
  connectedCallback() {
@@ -35936,7 +36050,7 @@ var NLDDBarSplitView = (_NLDDBarSplitView = class NLDDBarSplitView extends LitEl
35936
36050
  this._currentBreakpoint = bp;
35937
36051
  this.requestUpdate();
35938
36052
  }
35939
- this._updateLayerOffsets();
36053
+ this._updateInsets();
35940
36054
  });
35941
36055
  this._resizeObserver.observe(this);
35942
36056
  }
@@ -35981,7 +36095,7 @@ var NLDDBarSplitView = (_NLDDBarSplitView = class NLDDBarSplitView extends LitEl
35981
36095
  });
35982
36096
  }
35983
36097
  updated() {
35984
- this._updateLayerOffsets();
36098
+ this._updateInsets();
35985
36099
  }
35986
36100
  render() {
35987
36101
  return barSplitViewTemplate(this);
@@ -37370,6 +37484,16 @@ var pageStyles = css`
37370
37484
 
37371
37485
  :host {
37372
37486
  --_background-color: var(--context-parent-background-color, var(--semantics-surfaces-base-background-color));
37487
+ /* The insets this page arrived with, parked before it adds its own:
37488
+ republishing --context-inset-top on the element it reads from would be
37489
+ a cycle. */
37490
+ --_outer-inset-top: var(--context-inset-top, 0px);
37491
+ --_outer-inset-bottom: var(--context-inset-bottom, 0px);
37492
+ --_header-height: 0px;
37493
+ --_footer-height: 0px;
37494
+ /* Set from JS while the page owns the scroller. initial, not a length, so
37495
+ the fallback stands when the document scrolls instead. */
37496
+ --_scroll-height: initial;
37373
37497
 
37374
37498
  display: flex;
37375
37499
  background-color: var(--_background-color);
@@ -37408,9 +37532,8 @@ var pageStyles = css`
37408
37532
 
37409
37533
  /* Root-scroll mode: the DOCUMENT scrolls (see nldd-app-view), not the page.
37410
37534
  The page stops owning an inner scroll container and its sticky-header/footer
37411
- become real position:sticky layers against the document, offset by the
37412
- cumulative layer heights above/below (--context-layer-top/bottom, published
37413
- by any bars sitting outside the page). The mode is derived upstream and
37535
+ stick against the document, offset by the insets above and below
37536
+ (--context-inset-top/bottom, published by any bars outside the page). The mode is derived upstream and
37414
37537
  delivered as --context-scroll-mode; nldd-page reflects it to [data-scroll]
37415
37538
  so these (higher-specificity, later) rules win over the nested ones. */
37416
37539
  :host([data-scroll="root"]) {
@@ -37446,7 +37569,7 @@ var pageStyles = css`
37446
37569
  :host([data-scroll="root"]) .page {
37447
37570
  overflow: visible;
37448
37571
  flex-shrink: 0;
37449
- min-height: calc(100dvh - var(--context-layer-top, 0px) - var(--context-layer-bottom, 0px));
37572
+ min-height: calc(100dvh - var(--context-inset-top, 0px) - var(--context-inset-bottom, 0px));
37450
37573
  }
37451
37574
 
37452
37575
 
@@ -37481,7 +37604,7 @@ var pageStyles = css`
37481
37604
  opacity: 0;
37482
37605
  background: linear-gradient(to bottom, color-mix(in srgb, var(--_background-color) 95%, transparent), transparent);
37483
37606
  pointer-events: none;
37484
- height: var(--primitives-space-32);
37607
+ height: var(--primitives-space-24);
37485
37608
  transition: opacity var(--primitives-transition-duration-medium) var(--primitives-transition-easing-default);
37486
37609
  }
37487
37610
 
@@ -37495,18 +37618,34 @@ var pageStyles = css`
37495
37618
  only mattered for the absolute overlay. */
37496
37619
  :host([data-scroll="root"][sticky-header]) .page__header {
37497
37620
  position: sticky;
37498
- top: var(--context-layer-top, 0px);
37621
+ top: var(--context-inset-top, 0px);
37499
37622
  left: auto;
37500
37623
  right: auto;
37501
37624
  }
37502
37625
 
37626
+ /* The page adds its own bars to the insets it inherited, so sticky content
37627
+ inside clears them without knowing a number. The header sits outside this
37628
+ element and keeps the value without itself in it. */
37503
37629
  .page__scroll {
37630
+ --context-inset-top: calc(var(--_outer-inset-top) + var(--_header-height));
37631
+ --context-inset-bottom: calc(var(--_outer-inset-bottom) + var(--_footer-height));
37632
+ /* The height sticky content can cap itself on. Unset while the document
37633
+ scrolls, where the viewport is the whole story. */
37634
+ --context-scroller-height: var(--_scroll-height, 100dvh);
37635
+
37504
37636
  display: flex;
37505
37637
  min-height: 0;
37506
37638
  flex-direction: column;
37507
37639
  flex-grow: 1;
37508
37640
  }
37509
37641
 
37642
+ /* Nested scrolling: a sticky child is measured against this element, so the
37643
+ bars above the page fall outside it and only its own header counts. */
37644
+ :host(:not([data-scroll="root"])) .page__scroll {
37645
+ --context-inset-top: var(--_header-height);
37646
+ --context-inset-bottom: var(--_footer-height);
37647
+ }
37648
+
37510
37649
  :host([sticky-header]) .page__scroll {
37511
37650
  overflow-y: auto;
37512
37651
  overflow-x: hidden;
@@ -37545,12 +37684,12 @@ var pageStyles = css`
37545
37684
  right: 0;
37546
37685
  background: linear-gradient(to top, color-mix(in srgb, var(--_background-color) 95%, transparent), transparent);
37547
37686
  pointer-events: none;
37548
- height: var(--primitives-space-32);
37687
+ height: var(--primitives-space-24);
37549
37688
  }
37550
37689
 
37551
37690
  /* Root-scroll mode: footer sticks to the document bottom, above any bottom bars. */
37552
37691
  :host([data-scroll="root"][sticky-footer]) .page__footer {
37553
- bottom: var(--context-layer-bottom, 0px);
37692
+ bottom: var(--context-inset-bottom, 0px);
37554
37693
  }
37555
37694
  `;
37556
37695
  //#endregion
@@ -37585,10 +37724,18 @@ function pageTemplate(component) {
37585
37724
  *
37586
37725
  * In root-scroll mode (--context-scroll-mode: root, derived upstream by
37587
37726
  * nldd-app-view) the page stops owning a scroller: the document scrolls and the
37588
- * sticky header/footer become position:sticky layers offset by
37589
- * --context-layer-top/bottom. The mode is read on connect/resize and reflected
37727
+ * sticky header/footer stick against the document, offset by
37728
+ * --context-inset-top/bottom. The mode is read on connect/resize and reflected
37590
37729
  * to [data-scroll] so the CSS can branch.
37591
37730
  *
37731
+ * The page passes those insets on to its own content, with its sticky header
37732
+ * and footer added: anything sticky inside (an nldd-sidebar-section, a sticky
37733
+ * table head) reads --context-inset-top / --context-inset-bottom and clears
37734
+ * every bar above and below it without being told a number. The heights are
37735
+ * measured, because a top title bar shrinks as you scroll past its anchor. While
37736
+ * the page owns the scroller, only its own bars count: the chrome above the page
37737
+ * sits outside that scroller and does not push sticky content down.
37738
+ *
37592
37739
  * @element nldd-page
37593
37740
  *
37594
37741
  * @attr {boolean} sticky-header - Sticky header
@@ -37612,6 +37759,7 @@ var NLDDPage = (_NLDDPage = class NLDDPage extends LitElement {
37612
37759
  this._scrollTarget = null;
37613
37760
  this._scrollProvider = null;
37614
37761
  this._headerObserver = null;
37762
+ this._insetObserver = null;
37615
37763
  this._mainSlot = null;
37616
37764
  this._resizeRaf = 0;
37617
37765
  this._onResize = () => {
@@ -37660,6 +37808,9 @@ var NLDDPage = (_NLDDPage = class NLDDPage extends LitElement {
37660
37808
  get _scrollEl() {
37661
37809
  return this.shadowRoot?.querySelector(".page__scroll") ?? null;
37662
37810
  }
37811
+ get _footerEl() {
37812
+ return this.shadowRoot?.querySelector(".page__footer") ?? null;
37813
+ }
37663
37814
  connectedCallback() {
37664
37815
  super.connectedCallback();
37665
37816
  this.style.containerType = "inline-size";
@@ -37684,6 +37835,7 @@ var NLDDPage = (_NLDDPage = class NLDDPage extends LitElement {
37684
37835
  if (this._resizeRaf) cancelAnimationFrame(this._resizeRaf);
37685
37836
  this._teardownScrollListener();
37686
37837
  this._teardownHeaderObserver();
37838
+ this._teardownInsetObserver();
37687
37839
  this._teardownMainSlotListener();
37688
37840
  }
37689
37841
  firstUpdated() {
@@ -37714,8 +37866,41 @@ var NLDDPage = (_NLDDPage = class NLDDPage extends LitElement {
37714
37866
  if (this._scrollEl) this._scrollEl.style.paddingTop = "";
37715
37867
  this._setupScrollListener();
37716
37868
  if (this.stickyHeader && !this._isRoot) this._setupHeaderObserver();
37869
+ this._setupInsetObserver();
37717
37870
  this._onScroll();
37718
37871
  }
37872
+ /**
37873
+ * Publishes the page's own sticky bars as insets, so sticky content inside
37874
+ * the page knows how far to clear.
37875
+ *
37876
+ * Measured rather than assumed: a top title bar shrinks as you scroll past
37877
+ * its anchor, and a consumer that had written the number down would be wrong
37878
+ * from that moment on. A bar that is not sticky scrolls away and adds
37879
+ * nothing, so it stays at zero.
37880
+ */
37881
+ _setupInsetObserver() {
37882
+ this._teardownInsetObserver();
37883
+ const header = this._headerEl;
37884
+ const footer = this._footerEl;
37885
+ if (!header || !footer) return;
37886
+ const publish = () => {
37887
+ this.style.setProperty("--_header-height", `${this.stickyHeader ? header.offsetHeight : 0}px`);
37888
+ this.style.setProperty("--_footer-height", `${this.stickyFooter ? footer.offsetHeight : 0}px`);
37889
+ if (this._isRoot) this.style.removeProperty("--_scroll-height");
37890
+ else this.style.setProperty("--_scroll-height", `${this._scrollEl?.clientHeight ?? 0}px`);
37891
+ };
37892
+ publish();
37893
+ this._insetObserver = new ResizeObserver(publish);
37894
+ this._insetObserver.observe(header);
37895
+ this._insetObserver.observe(footer);
37896
+ if (this._scrollEl) this._insetObserver.observe(this._scrollEl);
37897
+ }
37898
+ _teardownInsetObserver() {
37899
+ if (this._insetObserver) {
37900
+ this._insetObserver.disconnect();
37901
+ this._insetObserver = null;
37902
+ }
37903
+ }
37719
37904
  _setupScrollListener() {
37720
37905
  const target = this.scrollEventTarget;
37721
37906
  target.addEventListener("scroll", this._onScroll, { passive: true });
@@ -39632,10 +39817,10 @@ var sidebarSectionStyles = css`
39632
39817
  --_lg-padding-top: initial;
39633
39818
  --_lg-padding-bottom: initial;
39634
39819
  --_max-width: var(--semantics-page-sections-body-max-width);
39635
- /* Sticky insets default to 16px top and bottom; override via sticky-top /
39636
- sticky-bottom to clear other sticky page elements (e.g. a sticky header). */
39637
- --_sticky-top: var(--primitives-space-16);
39638
- --_sticky-bottom: var(--primitives-space-16);
39820
+ /* 24px clear of the insets nldd-page publishes. 24 is the depth of a
39821
+ sticky header's fade, so the box starts where the fade ends. */
39822
+ --_sticky-top: calc(var(--context-inset-top, 0px) + var(--primitives-space-24));
39823
+ --_sticky-bottom: calc(var(--context-inset-bottom, 0px) + var(--primitives-space-24));
39639
39824
  --_sidebar-width: var(--primitives-area-320);
39640
39825
 
39641
39826
  display: flex;
@@ -39768,7 +39953,9 @@ var sidebarSectionStyles = css`
39768
39953
  position: sticky;
39769
39954
  top: var(--_sticky-top);
39770
39955
  bottom: var(--_sticky-bottom);
39771
- max-height: calc(100dvh - var(--_sticky-top) - var(--_sticky-bottom));
39956
+ /* What the scroller shows, not what the window is: a viewport-tall cap
39957
+ hangs out the bottom by the height of the chrome around the page. */
39958
+ max-height: calc(var(--context-scroller-height, 100dvh) - var(--_sticky-top) - var(--_sticky-bottom));
39772
39959
  overflow-y: auto;
39773
39960
  }
39774
39961
  }
@@ -39908,8 +40095,8 @@ var sheetStyles = css`
39908
40095
  sticky header sticks to that edge instead of to a bar that stands
39909
40096
  outside. See findScrollModeProvider for the other half. */
39910
40097
  --context-scroll-mode: nested;
39911
- --context-layer-top: 0px;
39912
- --context-layer-bottom: 0px;
40098
+ --context-inset-top: 0px;
40099
+ --context-inset-bottom: 0px;
39913
40100
 
39914
40101
  display: flex;
39915
40102
  position: fixed;
@@ -40922,29 +41109,21 @@ var collectionStyles = css`
40922
41109
  --_item-width: var(--primitives-area-280);
40923
41110
  --_focus-ring-z-index: 1;
40924
41111
 
40925
- @media (max-width: ${smMax$4}) {
40926
- --_gap: var(--components-collection-sm-gap);
40927
- }
41112
+ /* Two sets, because a collection inside a layout-container follows that
41113
+ container and anywhere else the viewport. The bare --_gap is what
41114
+ stands when neither set matches. */
41115
+ --_sm-gap: var(--components-collection-sm-gap);
41116
+ --_md-gap: var(--components-collection-md-gap);
41117
+ --_lg-gap: var(--components-collection-lg-gap);
41118
+ --_gap: var(--_sm-gap);
40928
41119
 
40929
- @media (min-width: ${mdMin$3}) and (max-width: ${mdMax$2}) {
40930
- --_gap: var(--components-collection-md-gap);
40931
- }
40932
-
40933
- @media (min-width: ${lgMin$2}) {
40934
- --_gap: var(--components-collection-lg-gap);
40935
- }
40936
-
40937
- @container layout-container (max-width: ${smMax$4}) {
40938
- --_gap: var(--components-collection-sm-gap);
40939
- }
40940
-
40941
- @container layout-container (min-width: ${mdMin$3}) and (max-width: ${mdMax$2}) {
40942
- --_gap: var(--components-collection-md-gap);
40943
- }
41120
+ @media (max-width: ${smMax$4}) { --_gap: var(--_sm-gap); }
41121
+ @media (min-width: ${mdMin$3}) and (max-width: ${mdMax$2}) { --_gap: var(--_md-gap); }
41122
+ @media (min-width: ${lgMin$2}) { --_gap: var(--_lg-gap); }
40944
41123
 
40945
- @container layout-container (min-width: ${lgMin$2}) {
40946
- --_gap: var(--components-collection-lg-gap);
40947
- }
41124
+ @container layout-container (max-width: ${smMax$4}) { --_gap: var(--_sm-gap); }
41125
+ @container layout-container (min-width: ${mdMin$3}) and (max-width: ${mdMax$2}) { --_gap: var(--_md-gap); }
41126
+ @container layout-container (min-width: ${lgMin$2}) { --_gap: var(--_lg-gap); }
40948
41127
 
40949
41128
  display: flex;
40950
41129
  width: 100%;
@@ -40991,6 +41170,27 @@ var collectionStyles = css`
40991
41170
  flex-direction: column;
40992
41171
  }
40993
41172
 
41173
+
41174
+ /* ## Lanes */
41175
+
41176
+ /* Falls back to the grid above, not to the multicol nldd-container uses:
41177
+ this component pages, and multicol redistributes the whole set every time
41178
+ load-more adds to it. */
41179
+ :host([layout="lanes"]) .collection__items {
41180
+ display: grid;
41181
+ grid-template-columns: repeat(auto-fill, minmax(min(var(--_item-width), 100%), 1fr));
41182
+ }
41183
+
41184
+ :host([layout="lanes"]) .collection__items ::slotted(*) {
41185
+ min-width: 0;
41186
+ }
41187
+
41188
+ @supports (display: grid-lanes) {
41189
+ :host([layout="lanes"]) .collection__items {
41190
+ display: grid-lanes;
41191
+ }
41192
+ }
41193
+
40994
41194
  /* ## Horizontal scroll */
40995
41195
 
40996
41196
  :host([layout="horizontal-scroll"]) .collection__items {
@@ -41141,7 +41341,7 @@ var nlddCollectionTranslations = {
41141
41341
  * Nederlandse Digitale Dienst Collection Component (Lit + TypeScript)
41142
41342
  *
41143
41343
  * A container for displaying collections of items.
41144
- * Supports grid, stack, and horizontal scroll layouts.
41344
+ * Supports grid, stack, lanes and horizontal scroll layouts.
41145
41345
  * In grid and stack modes, items are paginated via a load-more button. In
41146
41346
  * horizontal scroll, the prev/next controls and the edge fade appear only when
41147
41347
  * the items overflow the container.
@@ -41150,12 +41350,15 @@ var nlddCollectionTranslations = {
41150
41350
  *
41151
41351
  * @element nldd-collection
41152
41352
  *
41153
- * @attr {string} layout - Layout mode: 'grid' | 'stack' | 'horizontal-scroll' (default: 'grid')
41353
+ * @attr {string} layout - Layout mode: 'grid' | 'stack' | 'lanes' | 'horizontal-scroll' (default: 'grid')
41154
41354
  * @attr {boolean} show-load-more - Show load-more button in grid/stack (default: false)
41155
41355
  * @attr {number} max-items - Number of visible items per page (default: 24)
41156
41356
  * @attr {boolean} lazy-load - Automatically load more items when the button becomes visible
41157
- * @attr {string} item-width - Preferred width for each item (e.g. '280px', '20rem'). In grid layout used as the minimum column width (columns will be at least this wide; 1fr if container allows more). In horizontal scroll used as flex-basis. Never forces horizontal overflow — the value is clamped to container width.
41158
- * @attr {string} gap - Custom gap between items (any CSS length, e.g. '8px'). Overrides the responsive default at every breakpoint; unset keeps the default.
41357
+ * @attr {string} item-width - Preferred width for each item (e.g. '280px', '20rem'). In grid and lanes layouts used as the minimum column width (columns will be at least this wide; 1fr if container allows more). In horizontal scroll used as flex-basis. Never forces horizontal overflow — the value is clamped to container width.
41358
+ * @attr {string} gap - Gap between items, as a step of the spacing scale ('0', '2', '4', '6', '8', '10', '12', '16', '20', '24', '28', '32', '40', '44', '48', '56', '64', '80', '96'). Overrides the responsive default at every breakpoint; unset keeps the default.
41359
+ * @attr {string} sm-gap - Gap at sm, overriding `gap` there
41360
+ * @attr {string} md-gap - Gap at md, overriding `gap` there
41361
+ * @attr {string} lg-gap - Gap at lg, overriding `gap` there
41159
41362
  * @attr {object} translations - Translation overrides; unset keys fall back to Dutch. Available keys: 'components.collection.previous-action', 'components.collection.next-action', 'components.collection.load-more-action'
41160
41363
  *
41161
41364
  * @migration The `load-more-label` attribute has been removed.
@@ -41229,9 +41432,16 @@ var NLDDCollection = (_NLDDCollection = class NLDDCollection extends LitElement
41229
41432
  if (this.itemWidth) this.style.setProperty("--_item-width", this.itemWidth);
41230
41433
  else this.style.removeProperty("--_item-width");
41231
41434
  }
41232
- if (changedProperties.has("gap")) {
41233
- if (this.gap) this.style.setProperty("--_gap", this.gap);
41234
- else this.style.removeProperty("--_gap");
41435
+ if (changedProperties.has("gap") || changedProperties.has("smGap") || changedProperties.has("mdGap") || changedProperties.has("lgGap")) {
41436
+ const plain = spacingToValue(this.gap, "nldd-collection", "gap");
41437
+ const write = (name, size, attribute) => {
41438
+ const value = size === void 0 ? plain : spacingToValue(size, "nldd-collection", attribute) ?? plain;
41439
+ if (value === null) this.style.removeProperty(name);
41440
+ else this.style.setProperty(name, value);
41441
+ };
41442
+ write("--_sm-gap", this.smGap, "sm-gap");
41443
+ write("--_md-gap", this.mdGap, "md-gap");
41444
+ write("--_lg-gap", this.lgGap, "lg-gap");
41235
41445
  }
41236
41446
  if (this.lazyLoad && this._loadMoreBtn && !this._intersectionObserver) {
41237
41447
  this._intersectionObserver = new IntersectionObserver(([entry]) => {
@@ -41334,6 +41544,21 @@ __decorate([property({
41334
41544
  type: String,
41335
41545
  reflect: true
41336
41546
  })], NLDDCollection.prototype, "gap", void 0);
41547
+ __decorate([property({
41548
+ type: String,
41549
+ reflect: true,
41550
+ attribute: "sm-gap"
41551
+ })], NLDDCollection.prototype, "smGap", void 0);
41552
+ __decorate([property({
41553
+ type: String,
41554
+ reflect: true,
41555
+ attribute: "md-gap"
41556
+ })], NLDDCollection.prototype, "mdGap", void 0);
41557
+ __decorate([property({
41558
+ type: String,
41559
+ reflect: true,
41560
+ attribute: "lg-gap"
41561
+ })], NLDDCollection.prototype, "lgGap", void 0);
41337
41562
  __decorate([property({ type: Object })], NLDDCollection.prototype, "translations", void 0);
41338
41563
  __decorate([state()], NLDDCollection.prototype, "_visibleCount", void 0);
41339
41564
  __decorate([state()], NLDDCollection.prototype, "_totalCount", void 0);
@@ -45475,6 +45700,21 @@ var topNavigationBarStyles = css`
45475
45700
  /* # Block */
45476
45701
 
45477
45702
  .top-navigation-bar {
45703
+ /* The ribbon's width, and with it everything measured against the ribbon:
45704
+ its own height and the wordmark beside it. Here rather than on :host,
45705
+ because a container query cannot measure the container it sits on. */
45706
+ @container (max-width: ${smMax$2}) {
45707
+ --_logo-width: var(--semantics-brand-ribbon-sm-width);
45708
+ }
45709
+
45710
+ @container (min-width: ${mdMin$1}) and (max-width: ${mdMax}) {
45711
+ --_logo-width: var(--semantics-brand-ribbon-md-width);
45712
+ }
45713
+
45714
+ @container (min-width: ${lgMin}) {
45715
+ --_logo-width: var(--semantics-brand-ribbon-lg-width);
45716
+ }
45717
+
45478
45718
  box-sizing: border-box;
45479
45719
  display: flex;
45480
45720
  flex-direction: column;
@@ -45523,18 +45763,6 @@ var topNavigationBarStyles = css`
45523
45763
  align-self: start;
45524
45764
  align-items: center;
45525
45765
  justify-content: center;
45526
-
45527
- @container (max-width: ${smMax$2}) {
45528
- --_logo-width: var(--semantics-brand-ribbon-sm-width);
45529
- }
45530
-
45531
- @container (min-width: ${mdMin$1}) and (max-width: ${mdMax}) {
45532
- --_logo-width: var(--semantics-brand-ribbon-md-width);
45533
- }
45534
-
45535
- @container (min-width: ${lgMin}) {
45536
- --_logo-width: var(--semantics-brand-ribbon-lg-width);
45537
- }
45538
45766
  }
45539
45767
 
45540
45768
  .top-navigation-bar__logo svg {
@@ -45587,17 +45815,37 @@ var topNavigationBarStyles = css`
45587
45815
  /* ## Wordmark */
45588
45816
 
45589
45817
  .top-navigation-bar__wordmark {
45818
+ box-sizing: border-box;
45590
45819
  display: flex;
45820
+ /* A grid item is at least as wide as its longest word unless told
45821
+ otherwise, and the column it sits in is one of the two that keep the
45822
+ ribbon centred. One unbreakable name would push the ribbon off centre
45823
+ and the page past the screen. */
45824
+ min-width: 0;
45591
45825
  min-height: calc(var(--_logo-width) * 2);
45592
45826
  grid-column: 3;
45593
45827
  flex-direction: column;
45594
45828
  color: var(--_wordmark-content-color);
45829
+
45830
+ /* The distance the text keeps from the top edge once it outgrows the
45831
+ ribbon. Only at the top: space under it would raise the bar for
45832
+ nothing. The minimum gives up the same 12, so the text still centres
45833
+ on the middle of the ribbon rather than 6px below it. */
45834
+ @container (max-width: ${smMax$2}) {
45835
+ align-self: start;
45836
+ min-height: calc(var(--_logo-width) * 2 - var(--primitives-space-12));
45837
+ padding-block-start: var(--primitives-space-12);
45838
+ }
45595
45839
  }
45596
45840
 
45597
45841
  .top-navigation-bar__wordmark-spacer {
45598
45842
  height: var(--_logo-width);
45599
45843
  flex-grow: 0;
45600
45844
  flex-shrink: 0;
45845
+
45846
+ @container (max-width: ${smMax$2}) {
45847
+ display: none;
45848
+ }
45601
45849
  }
45602
45850
 
45603
45851
  .top-navigation-bar__wordmark-content {
@@ -45607,6 +45855,21 @@ var topNavigationBarStyles = css`
45607
45855
  flex-shrink: 1;
45608
45856
  flex-basis: 50%;
45609
45857
  max-width: var(--_wordmark-max-width);
45858
+ /* anywhere rather than break-word: only this one takes the break into
45859
+ the min-content width, which is what the track measures. A name that
45860
+ cannot break wraps mid-word here, because a name cut off by an
45861
+ ellipsis cannot be read at all. */
45862
+ overflow-wrap: anywhere;
45863
+
45864
+ /* Centred against the ribbon, and what does not fit grows downward: an
45865
+ auto margin takes positive free space and never negative, so the text
45866
+ cannot ride up past the top. Growing is the flex item's job here, and
45867
+ an item that grows leaves nothing for the margins to centre with. */
45868
+ @container (max-width: ${smMax$2}) {
45869
+ flex-grow: 0;
45870
+ flex-basis: auto;
45871
+ margin-block: auto;
45872
+ }
45610
45873
  }
45611
45874
 
45612
45875
  .top-navigation-bar__wordmark-title {
@@ -48741,10 +49004,10 @@ var modalDialogStyles = css`
48741
49004
  .modal-dialog {
48742
49005
  /* Where the app's layout context ends, as in nldd-sheet: the dialog scrolls
48743
49006
  itself and starts at its own top edge, so anything inside it stops
48744
- following the app's scroll mode and its layer offsets. */
49007
+ following the app's scroll mode and its insets. */
48745
49008
  --context-scroll-mode: nested;
48746
- --context-layer-top: 0px;
48747
- --context-layer-bottom: 0px;
49009
+ --context-inset-top: 0px;
49010
+ --context-inset-bottom: 0px;
48748
49011
 
48749
49012
  box-sizing: border-box;
48750
49013
  outline: none;
@@ -51460,6 +51723,21 @@ var progressCircleStyles = css`
51460
51723
  .progress-circle__segment-indicator--mintgroen { stroke: var(--semantics-categories-mintgroen-filled-background-color); }
51461
51724
 
51462
51725
 
51726
+ /* ## Filter flood color
51727
+
51728
+ Here rather than in a style attribute, which a strict style-src drops.
51729
+ The color of an arc varies per segment, so that one is set through the
51730
+ CSSOM instead. */
51731
+
51732
+ .progress-circle__flood--track {
51733
+ flood-color: var(--_track-border-color);
51734
+ }
51735
+
51736
+ .progress-circle__flood--indeterminate {
51737
+ flood-color: var(--_indeterminate-border-color);
51738
+ }
51739
+
51740
+
51463
51741
  /* # Spinner (indeterminate)
51464
51742
  GitHub-style: a fixed-width arc (25 / 100 = quarter turn, 90°) that
51465
51743
  simply rotates around the circle. Calmer than Material's elastic
@@ -51651,7 +51929,7 @@ function progressCircleTemplate(component, onSlotChange) {
51651
51929
  <filter id="progress-circle-border-track-${uid}" filterUnits="userSpaceOnUse" x="-10" y="-10" width="120" height="120" color-interpolation-filters="sRGB">
51652
51930
  <feMorphology in="SourceGraphic" operator="erode" radius=${borderErodeRadius} result="eroded"></feMorphology>
51653
51931
  <feComposite operator="out" in="SourceGraphic" in2="eroded" result="edge"></feComposite>
51654
- <feFlood style="flood-color: var(--_track-border-color)" result="flood"></feFlood>
51932
+ <feFlood class="progress-circle__flood progress-circle__flood--track" result="flood"></feFlood>
51655
51933
  <feComposite operator="in" in="flood" in2="edge" result="colored"></feComposite>
51656
51934
  <feComposite operator="over" in="colored" in2="SourceGraphic"></feComposite>
51657
51935
  </filter>
@@ -51659,7 +51937,7 @@ function progressCircleTemplate(component, onSlotChange) {
51659
51937
  <filter id="progress-circle-border-${color}-${uid}" filterUnits="userSpaceOnUse" x="-10" y="-10" width="120" height="120" color-interpolation-filters="sRGB">
51660
51938
  <feMorphology in="SourceGraphic" operator="erode" radius=${borderErodeRadius} result="eroded"></feMorphology>
51661
51939
  <feComposite operator="out" in="SourceGraphic" in2="eroded" result="edge"></feComposite>
51662
- <feFlood style="flood-color: var(--semantics-categories-${color}-filled-highlight-border-color)" result="flood"></feFlood>
51940
+ <feFlood class="progress-circle__flood" style=${styleMap({ floodColor: `var(--semantics-categories-${color}-filled-highlight-border-color)` })} result="flood"></feFlood>
51663
51941
  <feComposite operator="in" in="flood" in2="edge" result="colored"></feComposite>
51664
51942
  <feComposite operator="over" in="colored" in2="SourceGraphic"></feComposite>
51665
51943
  </filter>
@@ -51668,7 +51946,7 @@ function progressCircleTemplate(component, onSlotChange) {
51668
51946
  <filter id="progress-circle-border-indeterminate-${uid}" filterUnits="userSpaceOnUse" x="-10" y="-10" width="120" height="120" color-interpolation-filters="sRGB">
51669
51947
  <feMorphology in="SourceGraphic" operator="erode" radius=${borderErodeRadius} result="eroded"></feMorphology>
51670
51948
  <feComposite operator="out" in="SourceGraphic" in2="eroded" result="edge"></feComposite>
51671
- <feFlood style="flood-color: var(--_indeterminate-border-color)" result="flood"></feFlood>
51949
+ <feFlood class="progress-circle__flood progress-circle__flood--indeterminate" result="flood"></feFlood>
51672
51950
  <feComposite operator="in" in="flood" in2="edge" result="colored"></feComposite>
51673
51951
  <feComposite operator="over" in="colored" in2="SourceGraphic"></feComposite>
51674
51952
  </filter>