@unofficialbox/box-open-elements 0.9.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/dist/components/feedback/alert.js +1 -16
  2. package/dist/components/feedback/path.d.ts +84 -0
  3. package/dist/components/feedback/path.js +455 -0
  4. package/dist/components/feedback/skeleton.d.ts +70 -0
  5. package/dist/components/feedback/skeleton.js +231 -1
  6. package/dist/components/feedback/toast.d.ts +38 -0
  7. package/dist/components/feedback/toast.js +189 -13
  8. package/dist/components/feedback/tone.d.ts +16 -0
  9. package/dist/components/feedback/tone.js +31 -0
  10. package/dist/components/layout/grid.d.ts +41 -0
  11. package/dist/components/layout/grid.js +152 -0
  12. package/dist/components/overlays/command-palette.d.ts +1 -0
  13. package/dist/components/overlays/command-palette.js +42 -3
  14. package/dist/components/overlays/context-menu.js +11 -1
  15. package/dist/components/overlays/dialog.d.ts +1 -0
  16. package/dist/components/overlays/dialog.js +43 -3
  17. package/dist/components/overlays/drawer.d.ts +1 -3
  18. package/dist/components/overlays/drawer.js +4 -20
  19. package/dist/components/overlays/guide-tooltip.js +15 -1
  20. package/dist/components/overlays/popover.js +16 -1
  21. package/dist/components/overlays/shortcuts-overlay.d.ts +1 -0
  22. package/dist/components/overlays/shortcuts-overlay.js +40 -3
  23. package/dist/components/overlays/tooltip.js +15 -1
  24. package/dist/element-maps.d.ts +5 -3
  25. package/dist/entries/grid.d.ts +1 -0
  26. package/dist/entries/grid.js +1 -0
  27. package/dist/entries/path.d.ts +1 -0
  28. package/dist/entries/path.js +1 -0
  29. package/dist/foundations/layout/grid.d.ts +70 -0
  30. package/dist/foundations/layout/grid.js +84 -0
  31. package/dist/foundations/layout/index.d.ts +1 -0
  32. package/dist/foundations/layout/index.js +3 -0
  33. package/dist/foundations/overlay/index.d.ts +1 -0
  34. package/dist/foundations/overlay/index.js +1 -0
  35. package/dist/foundations/overlay/top-layer.d.ts +62 -0
  36. package/dist/foundations/overlay/top-layer.js +95 -0
  37. package/dist/index.d.ts +4 -1
  38. package/dist/index.js +5 -1
  39. package/dist/patterns/audit/audit-log.js +0 -1
  40. package/dist/patterns/lineage/lineage-graph.d.ts +7 -0
  41. package/dist/patterns/lineage/lineage-graph.js +67 -6
  42. package/dist/patterns/lineage/provenance-strip.js +106 -15
  43. package/dist/patterns/notifications/notification-inbox.js +2 -18
  44. package/dist/patterns/timeline/timeline.js +44 -12
  45. package/dist/patterns/versions/graph-arrows.d.ts +22 -0
  46. package/dist/patterns/versions/graph-arrows.js +13 -0
  47. package/dist/patterns/versions/index.d.ts +1 -0
  48. package/dist/patterns/versions/index.js +1 -0
  49. package/dist/patterns/versions/version-graph.d.ts +7 -0
  50. package/dist/patterns/versions/version-graph.js +54 -6
  51. package/package.json +1 -1
  52. package/dist/components/feedback/stage-path.d.ts +0 -44
  53. package/dist/components/feedback/stage-path.js +0 -235
  54. package/dist/entries/stage-path.d.ts +0 -1
  55. package/dist/entries/stage-path.js +0 -1
@@ -1,5 +1,6 @@
1
1
  import { groupShortcutCommands, isCommandDescriptorRecord, splitShortcutKeys, } from "./command-types.js";
2
2
  import { BaseElement } from "../../core/index.js";
3
+ import { dismissModal, promoteModal } from "../../foundations/overlay/index.js";
3
4
  import { FocusRestore, trapTabKey } from "../../foundations/a11y/index.js";
4
5
  import { boeOverlay, boePanel, boeRadius } from "../../foundations/geometry/index.js";
5
6
  const DEFAULT_TAG_NAME = "box-shortcuts-overlay";
@@ -37,6 +38,10 @@ const elementStyles = `
37
38
  display: contents;
38
39
  }
39
40
 
41
+ /* A <dialog> promoted with showModal() — see
42
+ foundations/overlay/top-layer.ts. The top layer keeps the scrim above
43
+ the page without moving the host node. UA border/margin/max-* reset
44
+ because this is the full-viewport scrim, not a centred card. */
40
45
  [part="backdrop"] {
41
46
  position: fixed;
42
47
  inset: 0;
@@ -46,6 +51,24 @@ const elementStyles = `
46
51
  padding: 1rem;
47
52
  background: ${boeOverlay.modalBackdrop};
48
53
  z-index: 1000;
54
+ border: 0;
55
+ margin: 0;
56
+ max-width: none;
57
+ max-height: none;
58
+ width: auto;
59
+ height: auto;
60
+ color: inherit;
61
+ }
62
+
63
+ /* The scrim paints itself; the UA pseudo must not double it up. */
64
+ [part="backdrop"]::backdrop {
65
+ background: transparent;
66
+ }
67
+
68
+ /* Without showModal support the element stays hidden, which is the
69
+ correct closed state anyway. */
70
+ [part="backdrop"]:not([open]) {
71
+ display: none;
49
72
  }
50
73
 
51
74
  [part="sheet"] {
@@ -183,6 +206,7 @@ export class ShortcutsOverlay extends BaseElement {
183
206
  return ["commands", "heading", "hotkey", "open"];
184
207
  }
185
208
  hostEl;
209
+ backdropEl = null;
186
210
  focusRestore = new FocusRestore();
187
211
  wasOpen = false;
188
212
  commandsRaw = null;
@@ -333,6 +357,9 @@ export class ShortcutsOverlay extends BaseElement {
333
357
  }
334
358
  if (!this.open) {
335
359
  const wasOpen = this.wasOpen;
360
+ // Leave the top layer before the element is discarded, not after.
361
+ dismissModal(this.backdropEl);
362
+ this.backdropEl = null;
336
363
  this.hostEl.innerHTML = "";
337
364
  this.wasOpen = false;
338
365
  if (wasOpen) {
@@ -365,8 +392,8 @@ export class ShortcutsOverlay extends BaseElement {
365
392
  })
366
393
  .join("");
367
394
  this.hostEl.innerHTML = `
368
- <div part="backdrop">
369
- <div part="sheet" role="dialog" aria-modal="true" aria-label="${escapeHtml(this.heading)}">
395
+ <dialog part="backdrop" aria-label="Keyboard shortcuts">
396
+ <div part="sheet">
370
397
  <div part="header">
371
398
  <h2 part="title">${escapeHtml(this.heading)}</h2>
372
399
  <button type="button" part="close">Close</button>
@@ -375,8 +402,18 @@ export class ShortcutsOverlay extends BaseElement {
375
402
  ? `<ul part="groups" role="list">${sections}</ul>`
376
403
  : `<div part="empty">No keyboard shortcuts are defined.</div>`}
377
404
  </div>
378
- </div>
405
+ </dialog>
379
406
  `;
407
+ this.backdropEl = this.hostEl.querySelector('[part="backdrop"]');
408
+ if (this.backdropEl) {
409
+ // A modal dialog closes itself on Escape and fires `cancel`; route it
410
+ // through the component's own dismissal so state stays in sync.
411
+ this.backdropEl.addEventListener("cancel", event => {
412
+ event.preventDefault();
413
+ this.dismiss();
414
+ });
415
+ }
416
+ promoteModal(this.backdropEl);
380
417
  if (justOpened) {
381
418
  this.hostEl.querySelector('[part="close"]')?.focus();
382
419
  }
@@ -1,4 +1,5 @@
1
1
  import { BaseElement } from "../../core/index.js";
2
+ import { dismissPopover, promotePopover } from "../../foundations/overlay/index.js";
2
3
  import { boeRadius } from "../../foundations/geometry/index.js";
3
4
  import { boeNeutralInteractiveStyles } from "../../foundations/tokens/index.js";
4
5
  import { parsePlacement, trackAnchor, } from "../../foundations/overlay/index.js";
@@ -40,7 +41,12 @@ const tooltipStyles = `
40
41
 
41
42
  /* Positioned by JS (foundations/overlay) as position: fixed, so it escapes
42
43
  ancestor overflow and flips/shifts to stay in the viewport. */
44
+ /* display is stated explicitly because the element carries
45
+ popover="manual": the UA sheet hides a popover that is not open, and an
46
+ author declaration outranks it. If showPopover() is unavailable or fails,
47
+ this still renders as before and only the top layer is lost. */
43
48
  [part="tooltip"] {
49
+ display: block;
44
50
  position: fixed;
45
51
  inset-block-start: 0;
46
52
  inset-inline-start: 0;
@@ -180,7 +186,7 @@ export class Tooltip extends BaseElement {
180
186
  <span part="trigger-host">
181
187
  <slot><button type="button" part="trigger">?</button></slot>
182
188
  </span>
183
- <div id="${this.tooltipId}" part="tooltip" role="tooltip" hidden><span part="label"></span><slot name="content"></slot></div>
189
+ <div id="${this.tooltipId}" part="tooltip" popover="manual" role="tooltip" hidden><span part="label"></span><slot name="content"></slot></div>
184
190
  </span>
185
191
  `;
186
192
  this.triggerHostEl = this.shadowRoot.querySelector('[part="trigger-host"]');
@@ -232,6 +238,14 @@ export class Tooltip extends BaseElement {
232
238
  this.tooltipEl.setAttribute("data-theme", theme);
233
239
  }
234
240
  this.tooltipEl.hidden = !this.openValue;
241
+ // Top layer keeps it above the page even inside an ancestor with a
242
+ // transform/filter/contain, which position:fixed alone cannot escape.
243
+ if (this.openValue) {
244
+ promotePopover(this.tooltipEl);
245
+ }
246
+ else {
247
+ dismissPopover(this.tooltipEl);
248
+ }
235
249
  const trigger = this.resolveDescribedTrigger();
236
250
  if (trigger === this.fallbackTriggerEl) {
237
251
  this.fallbackTriggerEl.setAttribute("aria-label", this.triggerLabel);
@@ -48,6 +48,7 @@ import type { DateField } from "./components/forms/date-field.js";
48
48
  import type { Dialog } from "./components/overlays/dialog.js";
49
49
  import type { DiffViewer } from "./patterns/diff/diff-viewer.js";
50
50
  import type { Divider } from "./components/layout/divider.js";
51
+ import type { Grid } from "./components/layout/grid.js";
51
52
  import type { DonutChart } from "./patterns/insights/donut-chart.js";
52
53
  import type { DraggableList } from "./components/collections/draggable-list.js";
53
54
  import type { Drawer } from "./components/overlays/drawer.js";
@@ -124,7 +125,7 @@ import type { Slider } from "./components/forms/slider.js";
124
125
  import type { SpinButton } from "./components/forms/spin-button.js";
125
126
  import type { Spinner } from "./components/feedback/spinner.js";
126
127
  import type { SplitView } from "./components/layout/split-view.js";
127
- import type { StagePath } from "./components/feedback/stage-path.js";
128
+ import type { Path } from "./components/feedback/path.js";
128
129
  import type { Switch } from "./components/forms/switch.js";
129
130
  import type { Table } from "./components/collections/table.js";
130
131
  import type { Tabs } from "./components/navigation/tabs.js";
@@ -189,6 +190,7 @@ declare global {
189
190
  "box-dialog": Dialog;
190
191
  "box-diff-viewer": DiffViewer;
191
192
  "box-divider": Divider;
193
+ "box-grid": Grid;
192
194
  "box-donut-chart": DonutChart;
193
195
  "box-draggable-list": DraggableList;
194
196
  "box-drawer": Drawer;
@@ -265,7 +267,7 @@ declare global {
265
267
  "box-spin-button": SpinButton;
266
268
  "box-spinner": Spinner;
267
269
  "box-split-view": SplitView;
268
- "box-stage-path": StagePath;
270
+ "box-path": Path;
269
271
  "box-switch": Switch;
270
272
  "box-table": Table;
271
273
  "box-tabs": Tabs;
@@ -289,4 +291,4 @@ declare global {
289
291
  }
290
292
  }
291
293
  /** Every element tag the library registers, for host-side narrowing. */
292
- export type BoxElementTagName = "box-access-stats" | "box-accordion" | "box-activity-density" | "box-agent-chat" | "box-alert" | "box-annotation-inspector" | "box-annotation-thread" | "box-annotation-toolbar" | "box-app-shell" | "box-audit-log" | "box-avatar" | "box-badge" | "box-badgeable" | "box-bar-chart" | "box-breadcrumb" | "box-bulk-action-bar" | "box-button" | "box-button-group" | "box-calendar" | "box-card" | "box-carousel" | "box-category-selector" | "box-chart-panel" | "box-checkbox" | "box-checkbox-group" | "box-chip" | "box-collaborator-avatars" | "box-color-picker" | "box-combobox" | "box-command-palette" | "box-compare-view" | "box-contact-datalist-item" | "box-content-explorer" | "box-content-picker" | "box-content-sidebar" | "box-content-uploader" | "box-context-menu" | "box-datalist-item" | "box-date-field" | "box-dialog" | "box-diff-viewer" | "box-divider" | "box-donut-chart" | "box-draggable-list" | "box-drawer" | "box-drop-zone" | "box-dropdown" | "box-dual-listbox" | "box-due-badge" | "box-empty-state" | "box-error-mask" | "box-explorer-action-menu" | "box-explorer-breadcrumbs" | "box-explorer-items" | "box-explorer-list" | "box-explorer-table" | "box-explorer-toolbar" | "box-fieldset" | "box-file-request-builder" | "box-filter-bar" | "box-form-wizard" | "box-governance-panel" | "box-grid-view" | "box-guide-tooltip" | "box-help-text" | "box-icon-button" | "box-illustration" | "box-invite-collaborators-modal" | "box-item-details-panel" | "box-item-form" | "box-line-chart" | "box-lineage-graph" | "box-link-button" | "box-menu" | "box-menu-item" | "box-metadata-filter-builder" | "box-metadata-inspector" | "box-metric-card" | "box-multi-select" | "box-nav-sidebar" | "box-notification-bell" | "box-notification-inbox" | "box-nudge" | "box-number-input" | "box-pagination" | "box-permission-matrix" | "box-persona" | "box-pill-cloud" | "box-pill-selector-dropdown" | "box-popover" | "box-presence" | "box-preview-element" | "box-preview-header" | "box-progress-bar" | "box-progress-ring" | "box-progress-steps" | "box-provenance-strip" | "box-radio-group" | "box-range-slider" | "box-rating" | "box-review-queue-item" | "box-rich-text-input" | "box-run-trace" | "box-saved-view-picker" | "box-search-field" | "box-search-results-header" | "box-section" | "box-segmented-control" | "box-select" | "box-share-panel" | "box-shortcuts-overlay" | "box-sidebar-toggle-button" | "box-signature-ceremony" | "box-skeleton" | "box-slider" | "box-spin-button" | "box-spinner" | "box-split-view" | "box-stage-path" | "box-switch" | "box-table" | "box-tabs" | "box-tag-input" | "box-task-assignment-panel" | "box-text-area" | "box-text-field" | "box-thumbnail-card" | "box-time-field" | "box-timeline" | "box-toast" | "box-tooltip" | "box-tree" | "box-tree-grid" | "box-unified-share-modal" | "box-version-graph" | "box-version-list" | "box-wizard-summary" | "box-work-queue" | "box-workload-board";
294
+ export type BoxElementTagName = "box-access-stats" | "box-accordion" | "box-activity-density" | "box-agent-chat" | "box-alert" | "box-annotation-inspector" | "box-annotation-thread" | "box-annotation-toolbar" | "box-app-shell" | "box-audit-log" | "box-avatar" | "box-badge" | "box-badgeable" | "box-bar-chart" | "box-breadcrumb" | "box-bulk-action-bar" | "box-button" | "box-button-group" | "box-calendar" | "box-card" | "box-carousel" | "box-category-selector" | "box-chart-panel" | "box-checkbox" | "box-checkbox-group" | "box-chip" | "box-collaborator-avatars" | "box-color-picker" | "box-combobox" | "box-command-palette" | "box-compare-view" | "box-contact-datalist-item" | "box-content-explorer" | "box-content-picker" | "box-content-sidebar" | "box-content-uploader" | "box-context-menu" | "box-datalist-item" | "box-date-field" | "box-dialog" | "box-diff-viewer" | "box-divider" | "box-grid" | "box-donut-chart" | "box-draggable-list" | "box-drawer" | "box-drop-zone" | "box-dropdown" | "box-dual-listbox" | "box-due-badge" | "box-empty-state" | "box-error-mask" | "box-explorer-action-menu" | "box-explorer-breadcrumbs" | "box-explorer-items" | "box-explorer-list" | "box-explorer-table" | "box-explorer-toolbar" | "box-fieldset" | "box-file-request-builder" | "box-filter-bar" | "box-form-wizard" | "box-governance-panel" | "box-grid-view" | "box-guide-tooltip" | "box-help-text" | "box-icon-button" | "box-illustration" | "box-invite-collaborators-modal" | "box-item-details-panel" | "box-item-form" | "box-line-chart" | "box-lineage-graph" | "box-link-button" | "box-menu" | "box-menu-item" | "box-metadata-filter-builder" | "box-metadata-inspector" | "box-metric-card" | "box-multi-select" | "box-nav-sidebar" | "box-notification-bell" | "box-notification-inbox" | "box-nudge" | "box-number-input" | "box-pagination" | "box-permission-matrix" | "box-persona" | "box-pill-cloud" | "box-pill-selector-dropdown" | "box-popover" | "box-presence" | "box-preview-element" | "box-preview-header" | "box-progress-bar" | "box-progress-ring" | "box-progress-steps" | "box-provenance-strip" | "box-radio-group" | "box-range-slider" | "box-rating" | "box-review-queue-item" | "box-rich-text-input" | "box-run-trace" | "box-saved-view-picker" | "box-search-field" | "box-search-results-header" | "box-section" | "box-segmented-control" | "box-select" | "box-share-panel" | "box-shortcuts-overlay" | "box-sidebar-toggle-button" | "box-signature-ceremony" | "box-skeleton" | "box-slider" | "box-spin-button" | "box-spinner" | "box-split-view" | "box-path" | "box-switch" | "box-table" | "box-tabs" | "box-tag-input" | "box-task-assignment-panel" | "box-text-area" | "box-text-field" | "box-thumbnail-card" | "box-time-field" | "box-timeline" | "box-toast" | "box-tooltip" | "box-tree" | "box-tree-grid" | "box-unified-share-modal" | "box-version-graph" | "box-version-list" | "box-wizard-summary" | "box-work-queue" | "box-workload-board";
@@ -0,0 +1 @@
1
+ export * from "../components/layout/grid.js";
@@ -0,0 +1 @@
1
+ export * from "../components/layout/grid.js";
@@ -0,0 +1 @@
1
+ export * from "../components/feedback/path.js";
@@ -0,0 +1 @@
1
+ export * from "../components/feedback/path.js";
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Adobe Spectrum's responsive grid, as a foundation.
3
+ *
4
+ * Two surfaces build on this: `box-grid`, which lays out slotted content, and
5
+ * `box-skeleton`'s grid variant, which fills the same regions with shimmer
6
+ * bars. They must agree on what a column is worth and how wide a gutter is at
7
+ * a given viewport, so the model lives here rather than in either of them.
8
+ */
9
+ /**
10
+ * Columns in the grid.
11
+ *
12
+ * Twelve divides by 2, 3, 4 and 6, so halves, thirds, quarters and
13
+ * sidebar-plus-content all land on whole columns.
14
+ */
15
+ export declare const BOE_GRID_COLUMNS = 12;
16
+ /**
17
+ * Spectrum's breakpoints and their gutters.
18
+ *
19
+ * Breakpoints are min-widths, so a viewport between two of them inherits the
20
+ * smaller one's dimensions. Gutters are fixed values per breakpoint rather
21
+ * than a proportion of the grid — that is Spectrum's rule, and it is why the
22
+ * gutter cannot simply be a percentage.
23
+ */
24
+ export declare const BOE_GRID_BREAKPOINTS: readonly {
25
+ minWidth: number;
26
+ gutter: number;
27
+ }[];
28
+ /** The custom property every grid surface reads its gutter from. */
29
+ export declare const BOE_GRID_GUTTER_PROPERTY = "--boe-grid-gutter";
30
+ /**
31
+ * The gutter scale as CSS, scoped to a selector.
32
+ *
33
+ * Emitted rather than hand-written in each component so the two cannot drift,
34
+ * and so adding a breakpoint is one edit. A consumer overriding the property
35
+ * replaces the whole scale in one declaration.
36
+ */
37
+ export declare const boeGridGutterStyles: (selector?: string) => string;
38
+ /**
39
+ * One region of the grid: a block spanning some columns and rows, optionally
40
+ * pushed right by an offset. Spectrum calls these layout regions.
41
+ */
42
+ export interface BoeGridPlacement {
43
+ /** Columns to span. Clamped to what is left after `offset`. */
44
+ span?: number;
45
+ /** Rows to span. Clamped to the grid's `rows`, so a region cannot exceed it. */
46
+ rowSpan?: number;
47
+ /** Empty columns before this region. Clamped to leave room for one column. */
48
+ offset?: number;
49
+ }
50
+ /** A resolved region: every field present, every value in range. */
51
+ export interface ResolvedBoeGridPlacement {
52
+ span: number;
53
+ rowSpan: number;
54
+ offset: number;
55
+ }
56
+ /** Attribute payloads are author input — validate every record. */
57
+ export declare const isBoeGridPlacementRecord: (value: unknown) => value is BoeGridPlacement;
58
+ /**
59
+ * Fit a region to the grid.
60
+ *
61
+ * Every value is clamped rather than rejected. A layout that renders slightly
62
+ * wrong is recoverable; one that vanishes because a number was out of range is
63
+ * not, and the caller usually cannot tell which region was dropped. The clamps
64
+ * are what confine a region to the totals the author declared: a span of 9 in a
65
+ * three-column grid becomes 3, and a `rowSpan` of 5 in a three-row grid
66
+ * becomes 3.
67
+ */
68
+ export declare const resolveBoeGridPlacement: (item: BoeGridPlacement, columns: number, rows: number) => ResolvedBoeGridPlacement;
69
+ /** Parse a positive integer attribute, falling back when it is absent or junk. */
70
+ export declare const resolveGridCount: (raw: string | null, fallback: number) => number;
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Adobe Spectrum's responsive grid, as a foundation.
3
+ *
4
+ * Two surfaces build on this: `box-grid`, which lays out slotted content, and
5
+ * `box-skeleton`'s grid variant, which fills the same regions with shimmer
6
+ * bars. They must agree on what a column is worth and how wide a gutter is at
7
+ * a given viewport, so the model lives here rather than in either of them.
8
+ */
9
+ /**
10
+ * Columns in the grid.
11
+ *
12
+ * Twelve divides by 2, 3, 4 and 6, so halves, thirds, quarters and
13
+ * sidebar-plus-content all land on whole columns.
14
+ */
15
+ export const BOE_GRID_COLUMNS = 12;
16
+ /**
17
+ * Spectrum's breakpoints and their gutters.
18
+ *
19
+ * Breakpoints are min-widths, so a viewport between two of them inherits the
20
+ * smaller one's dimensions. Gutters are fixed values per breakpoint rather
21
+ * than a proportion of the grid — that is Spectrum's rule, and it is why the
22
+ * gutter cannot simply be a percentage.
23
+ */
24
+ export const BOE_GRID_BREAKPOINTS = [
25
+ { minWidth: 0, gutter: 16 },
26
+ { minWidth: 768, gutter: 24 },
27
+ { minWidth: 1280, gutter: 32 },
28
+ { minWidth: 1768, gutter: 40 },
29
+ { minWidth: 2160, gutter: 48 },
30
+ ];
31
+ /** The custom property every grid surface reads its gutter from. */
32
+ export const BOE_GRID_GUTTER_PROPERTY = "--boe-grid-gutter";
33
+ /**
34
+ * The gutter scale as CSS, scoped to a selector.
35
+ *
36
+ * Emitted rather than hand-written in each component so the two cannot drift,
37
+ * and so adding a breakpoint is one edit. A consumer overriding the property
38
+ * replaces the whole scale in one declaration.
39
+ */
40
+ export const boeGridGutterStyles = (selector = ":host") => BOE_GRID_BREAKPOINTS.map(({ minWidth, gutter }) => {
41
+ const declaration = `${selector} {\n ${BOE_GRID_GUTTER_PROPERTY}: ${gutter}px;\n }`;
42
+ return minWidth === 0
43
+ ? ` ${declaration}`
44
+ : ` @media (min-width: ${minWidth}px) {\n ${declaration}\n }`;
45
+ }).join("\n\n");
46
+ /** Attribute payloads are author input — validate every record. */
47
+ export const isBoeGridPlacementRecord = (value) => {
48
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
49
+ return false;
50
+ }
51
+ const item = value;
52
+ // Absent is fine — each field has a default. Present-but-not-a-number is not:
53
+ // it means the host built the payload wrong, and silently substituting a
54
+ // default would hide that.
55
+ return ["span", "rowSpan", "offset"].every(key => {
56
+ const raw = item[key];
57
+ return raw === undefined || (typeof raw === "number" && Number.isFinite(raw));
58
+ });
59
+ };
60
+ const clamp = (value, min, max) => Math.min(Math.max(Math.trunc(value), min), max);
61
+ /**
62
+ * Fit a region to the grid.
63
+ *
64
+ * Every value is clamped rather than rejected. A layout that renders slightly
65
+ * wrong is recoverable; one that vanishes because a number was out of range is
66
+ * not, and the caller usually cannot tell which region was dropped. The clamps
67
+ * are what confine a region to the totals the author declared: a span of 9 in a
68
+ * three-column grid becomes 3, and a `rowSpan` of 5 in a three-row grid
69
+ * becomes 3.
70
+ */
71
+ export const resolveBoeGridPlacement = (item, columns, rows) => {
72
+ // Leave at least one column for the region itself.
73
+ const offset = clamp(item.offset ?? 0, 0, Math.max(columns - 1, 0));
74
+ return {
75
+ offset,
76
+ span: clamp(item.span ?? 1, 1, Math.max(columns - offset, 1)),
77
+ rowSpan: clamp(item.rowSpan ?? 1, 1, Math.max(rows, 1)),
78
+ };
79
+ };
80
+ /** Parse a positive integer attribute, falling back when it is absent or junk. */
81
+ export const resolveGridCount = (raw, fallback) => {
82
+ const parsed = Number.parseInt(raw ?? "", 10);
83
+ return Number.isFinite(parsed) ? Math.max(parsed, 1) : fallback;
84
+ };
@@ -0,0 +1 @@
1
+ export * from "./grid.js";
@@ -0,0 +1,3 @@
1
+ // Adobe Spectrum's responsive grid: columns, breakpoint gutters, and the
2
+ // clamping that confines a region to the totals its host declared.
3
+ export * from "./grid.js";
@@ -1,2 +1,3 @@
1
1
  export { anchorFloating, parsePlacement, resolvePosition, trackAnchor, } from "./positioning.js";
2
+ export { dismissModal, dismissPopover, promoteModal, promotePopover, } from "./top-layer.js";
2
3
  export type { OverlayAlign, OverlayPlacement, OverlaySide, PositionOptions, PositionResult, Rect, Size, } from "./positioning.js";
@@ -1 +1,2 @@
1
1
  export { anchorFloating, parsePlacement, resolvePosition, trackAnchor, } from "./positioning.js";
2
+ export { dismissModal, dismissPopover, promoteModal, promotePopover, } from "./top-layer.js";
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Top-layer promotion for overlays.
3
+ *
4
+ * An overlay has to paint above the page, and `position: fixed` alone cannot
5
+ * promise that: an ancestor with a `transform`, `filter`, `perspective`,
6
+ * `contain`, or `will-change` becomes the containing block, and the overlay is
7
+ * positioned — and clipped — against that ancestor instead of the viewport.
8
+ *
9
+ * The old answer was to move the overlay's host node to `document.body`.
10
+ * `box-drawer` did exactly that and it broke every framework that owns the
11
+ * node: React unmounting a tree containing an open drawer threw
12
+ * `NotFoundError`, because the node it tried to remove was no longer its child.
13
+ *
14
+ * The top layer solves the same problem without touching the DOM tree. It is a
15
+ * rendering concern, so the host stays exactly where its framework put it.
16
+ *
17
+ * Two primitives, and the difference is modality rather than taste:
18
+ *
19
+ * - `showModal()` on a `<dialog>` — for overlays that own the screen while they
20
+ * are up, with a scrim, a focus trap, and the rest of the page inert.
21
+ * - `showPopover()` on a `[popover]` element — for anchored, non-modal surfaces
22
+ * (tooltips, menus, popovers) that must **not** trap focus or block the page.
23
+ *
24
+ * Using the modal primitive for an anchored surface would be a behaviour
25
+ * regression, not just an overreach, which is why these are separate calls.
26
+ *
27
+ * Every entry point is guarded rather than assumed. jsdom implements neither
28
+ * API, so the unit suite exercises everything around the promotion and the
29
+ * promotion itself is verified in a browser; and a detached or hidden element
30
+ * throws rather than opening, which is a normal state during teardown.
31
+ */
32
+ /** An element that may be promotable as a modal dialog. */
33
+ type MaybeDialog = HTMLElement & {
34
+ showModal?: () => void;
35
+ close?: () => void;
36
+ open?: boolean;
37
+ };
38
+ /** An element that may be promotable as a popover. */
39
+ type MaybePopover = HTMLElement & {
40
+ showPopover?: () => void;
41
+ hidePopover?: () => void;
42
+ };
43
+ /**
44
+ * Promote a `<dialog>` into the top layer as a modal.
45
+ *
46
+ * Returns whether the promotion happened, so a caller can tell "not supported
47
+ * here" from "already open" if it needs to — most do not.
48
+ */
49
+ export declare const promoteModal: (element: MaybeDialog | null) => boolean;
50
+ /** Close a modal `<dialog>`, leaving the top layer. */
51
+ export declare const dismissModal: (element: MaybeDialog | null) => void;
52
+ /**
53
+ * Promote an element into the top layer as a **non-modal** popover.
54
+ *
55
+ * Callers are expected to set `popover="manual"` on the element: `auto`
56
+ * popovers light-dismiss and close each other, which fights components that
57
+ * already own their own dismissal rules.
58
+ */
59
+ export declare const promotePopover: (element: MaybePopover | null) => boolean;
60
+ /** Hide a popover, leaving the top layer. */
61
+ export declare const dismissPopover: (element: MaybePopover | null) => void;
62
+ export {};
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Top-layer promotion for overlays.
3
+ *
4
+ * An overlay has to paint above the page, and `position: fixed` alone cannot
5
+ * promise that: an ancestor with a `transform`, `filter`, `perspective`,
6
+ * `contain`, or `will-change` becomes the containing block, and the overlay is
7
+ * positioned — and clipped — against that ancestor instead of the viewport.
8
+ *
9
+ * The old answer was to move the overlay's host node to `document.body`.
10
+ * `box-drawer` did exactly that and it broke every framework that owns the
11
+ * node: React unmounting a tree containing an open drawer threw
12
+ * `NotFoundError`, because the node it tried to remove was no longer its child.
13
+ *
14
+ * The top layer solves the same problem without touching the DOM tree. It is a
15
+ * rendering concern, so the host stays exactly where its framework put it.
16
+ *
17
+ * Two primitives, and the difference is modality rather than taste:
18
+ *
19
+ * - `showModal()` on a `<dialog>` — for overlays that own the screen while they
20
+ * are up, with a scrim, a focus trap, and the rest of the page inert.
21
+ * - `showPopover()` on a `[popover]` element — for anchored, non-modal surfaces
22
+ * (tooltips, menus, popovers) that must **not** trap focus or block the page.
23
+ *
24
+ * Using the modal primitive for an anchored surface would be a behaviour
25
+ * regression, not just an overreach, which is why these are separate calls.
26
+ *
27
+ * Every entry point is guarded rather than assumed. jsdom implements neither
28
+ * API, so the unit suite exercises everything around the promotion and the
29
+ * promotion itself is verified in a browser; and a detached or hidden element
30
+ * throws rather than opening, which is a normal state during teardown.
31
+ */
32
+ /**
33
+ * Promote a `<dialog>` into the top layer as a modal.
34
+ *
35
+ * Returns whether the promotion happened, so a caller can tell "not supported
36
+ * here" from "already open" if it needs to — most do not.
37
+ */
38
+ export const promoteModal = (element) => {
39
+ if (!element || typeof element.showModal !== "function" || element.open) {
40
+ return false;
41
+ }
42
+ try {
43
+ element.showModal();
44
+ return true;
45
+ }
46
+ catch {
47
+ // Detached, hidden, or already promoted by another path. The overlay still
48
+ // renders and behaves; it simply does not get the top layer.
49
+ return false;
50
+ }
51
+ };
52
+ /** Close a modal `<dialog>`, leaving the top layer. */
53
+ export const dismissModal = (element) => {
54
+ if (!element || typeof element.close !== "function" || !element.open) {
55
+ return;
56
+ }
57
+ try {
58
+ element.close();
59
+ }
60
+ catch {
61
+ // Already closed or detached; nothing to undo.
62
+ }
63
+ };
64
+ /**
65
+ * Promote an element into the top layer as a **non-modal** popover.
66
+ *
67
+ * Callers are expected to set `popover="manual"` on the element: `auto`
68
+ * popovers light-dismiss and close each other, which fights components that
69
+ * already own their own dismissal rules.
70
+ */
71
+ export const promotePopover = (element) => {
72
+ if (!element || typeof element.showPopover !== "function") {
73
+ return false;
74
+ }
75
+ try {
76
+ element.showPopover();
77
+ return true;
78
+ }
79
+ catch {
80
+ // Throws if already showing, or if the element is not connected.
81
+ return false;
82
+ }
83
+ };
84
+ /** Hide a popover, leaving the top layer. */
85
+ export const dismissPopover = (element) => {
86
+ if (!element || typeof element.hidePopover !== "function") {
87
+ return;
88
+ }
89
+ try {
90
+ element.hidePopover();
91
+ }
92
+ catch {
93
+ // Not currently showing; nothing to undo.
94
+ }
95
+ };
package/dist/index.d.ts CHANGED
@@ -8,6 +8,7 @@ export type { ResolvedTheme, ThemeChangeDetail, ThemeController, ThemeController
8
8
  export { DESIGN_PROFILE_CHANGE_EVENT, DESIGN_PROFILE_VARIABLES, DEFAULT_DESIGN_PROFILE_STORAGE_KEY, applyDesignProfile, boxDefaultDesignProfile, compactNeutralDesignProfile, createDesignProfileController, createDesignProfileStyleText, getDesignProfile, listDesignProfiles, registerBuiltInDesignProfiles, registerDesignProfile, resolveDesignProfileProperties, } from "./foundations/profiles/index.js";
9
9
  export type { ApplyDesignProfileOptions, DesignProfileChangeDetail, DesignProfileController, DesignProfileControllerOptions, DesignProfileDefinition, DesignProfileDensity, DesignProfileElevation, DesignProfileMotion, DesignProfileRadius, DesignProfileSpacing, DesignProfileTypography, } from "./foundations/profiles/index.js";
10
10
  export { boxIconography, boxIconographyAliases } from "./foundations/icons/index.js";
11
+ export * from "./foundations/layout/index.js";
11
12
  export { anchorFloating, parsePlacement, resolvePosition, trackAnchor, } from "./foundations/overlay/index.js";
12
13
  export type { OverlayAlign, OverlayPlacement, OverlaySide, PositionOptions, PositionResult, } from "./foundations/overlay/index.js";
13
14
  export * from "./components/actions/button.js";
@@ -42,10 +43,11 @@ export * from "./components/feedback/progress-ring.js";
42
43
  export * from "./components/feedback/progress-steps.js";
43
44
  export * from "./components/feedback/skeleton.js";
44
45
  export * from "./components/feedback/spinner.js";
45
- export * from "./components/feedback/stage-path.js";
46
+ export * from "./components/feedback/path.js";
46
47
  export * from "./components/feedback/toast.js";
47
48
  export * from "./components/files/drop-zone.js";
48
49
  export * from "./components/identity/avatar.js";
50
+ export * from "./components/identity/contact-datalist-item.js";
49
51
  export * from "./components/identity/persona.js";
50
52
  export * from "./components/navigation/accordion.js";
51
53
  export * from "./components/navigation/breadcrumb.js";
@@ -80,6 +82,7 @@ export * from "./components/forms/text-area.js";
80
82
  export * from "./components/forms/text-field.js";
81
83
  export * from "./components/layout/app-shell.js";
82
84
  export * from "./components/layout/divider.js";
85
+ export * from "./components/layout/grid.js";
83
86
  export * from "./components/layout/nav-sidebar.js";
84
87
  export * from "./components/layout/section.js";
85
88
  export * from "./components/layout/sidebar-toggle-button.js";
package/dist/index.js CHANGED
@@ -5,6 +5,8 @@ export { DESIGN_SYSTEM_CHANGE_EVENT, SEMANTIC_TOKEN_ALIASES, applyDesignTokens,
5
5
  export { DARK_MODE_MEDIA_QUERY, DEFAULT_THEME_STORAGE_KEY, THEME_CHANGE_EVENT, createThemeController, createThemeInitializationScript, } from "./foundations/theming/index.js";
6
6
  export { DESIGN_PROFILE_CHANGE_EVENT, DESIGN_PROFILE_VARIABLES, DEFAULT_DESIGN_PROFILE_STORAGE_KEY, applyDesignProfile, boxDefaultDesignProfile, compactNeutralDesignProfile, createDesignProfileController, createDesignProfileStyleText, getDesignProfile, listDesignProfiles, registerBuiltInDesignProfiles, registerDesignProfile, resolveDesignProfileProperties, } from "./foundations/profiles/index.js";
7
7
  export { boxIconography, boxIconographyAliases } from "./foundations/icons/index.js";
8
+ // Foundations: Adobe Spectrum's responsive grid (shared by box-grid and box-skeleton)
9
+ export * from "./foundations/layout/index.js";
8
10
  // Foundations: viewport-aware overlay positioning (for building custom overlays)
9
11
  export { anchorFloating, parsePlacement, resolvePosition, trackAnchor, } from "./foundations/overlay/index.js";
10
12
  // Components: actions
@@ -42,12 +44,13 @@ export * from "./components/feedback/progress-ring.js";
42
44
  export * from "./components/feedback/progress-steps.js";
43
45
  export * from "./components/feedback/skeleton.js";
44
46
  export * from "./components/feedback/spinner.js";
45
- export * from "./components/feedback/stage-path.js";
47
+ export * from "./components/feedback/path.js";
46
48
  export * from "./components/feedback/toast.js";
47
49
  // Components: files
48
50
  export * from "./components/files/drop-zone.js";
49
51
  // Components: identity
50
52
  export * from "./components/identity/avatar.js";
53
+ export * from "./components/identity/contact-datalist-item.js";
51
54
  export * from "./components/identity/persona.js";
52
55
  // Components: navigation
53
56
  export * from "./components/navigation/accordion.js";
@@ -86,6 +89,7 @@ export * from "./components/forms/text-field.js";
86
89
  // Components: layout
87
90
  export * from "./components/layout/app-shell.js";
88
91
  export * from "./components/layout/divider.js";
92
+ export * from "./components/layout/grid.js";
89
93
  export * from "./components/layout/nav-sidebar.js";
90
94
  export * from "./components/layout/section.js";
91
95
  export * from "./components/layout/sidebar-toggle-button.js";
@@ -221,7 +221,6 @@ const elementStyles = `
221
221
  gap: 0.25rem;
222
222
  padding: 0.5rem 0.65rem;
223
223
  border-top: 1px solid color-mix(in srgb, var(--boe-token-stroke-stroke, #e8e8e8) 55%, transparent);
224
- border-left: 3px solid var(--audit-tone, transparent);
225
224
  }
226
225
 
227
226
  [part="event-topline"] {
@@ -1,4 +1,5 @@
1
1
  import type { LineageNode } from "./types.js";
2
+ import type { GraphArrows } from "../versions/graph-arrows.js";
2
3
  import { BaseElement } from "../../core/index.js";
3
4
  /**
4
5
  * Provenance DAG for clause lineage (opportunity 2): source clause →
@@ -13,6 +14,12 @@ import { BaseElement } from "../../core/index.js";
13
14
  export declare class LineageGraph extends BaseElement {
14
15
  static readonly tagName: string;
15
16
  static get observedAttributes(): string[];
17
+ /**
18
+ * Which ends of each edge carry an arrowhead. Defaults to `end`, the head on
19
+ * the derived node, because the layout builds edges source → derived.
20
+ */
21
+ get arrows(): GraphArrows;
22
+ set arrows(value: GraphArrows);
16
23
  private nodesValue;
17
24
  private rovingId;
18
25
  private hostEl;