@ixfx/components 0.7.3 → 0.7.5

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 (141) hide show
  1. package/bundle/index.d.ts +1355 -526
  2. package/bundle/index.d.ts.map +1 -1
  3. package/bundle/index.js +3277 -384
  4. package/bundle/index.js.map +1 -1
  5. package/dist/ac-text.d.ts +1 -1
  6. package/dist/ac-text.js +1 -1
  7. package/dist/{ac-token-TCLAZeVg.d.ts → ac-token--_vCHXWw.d.ts} +2 -2
  8. package/dist/{ac-token-TCLAZeVg.d.ts.map → ac-token--_vCHXWw.d.ts.map} +1 -1
  9. package/dist/ac-token.d.ts +1 -1
  10. package/dist/ac-token.js +2 -2
  11. package/dist/button.d.ts +1 -1
  12. package/dist/button.d.ts.map +1 -1
  13. package/dist/colour-picker.d.ts +1 -1
  14. package/dist/crumbs.d.ts +6 -2
  15. package/dist/crumbs.d.ts.map +1 -1
  16. package/dist/crumbs.js +13 -3
  17. package/dist/crumbs.js.map +1 -1
  18. package/dist/{data-grid-Db4QpnII.d.ts → data-grid-CtJ35v8L.d.ts} +59 -2
  19. package/dist/data-grid-CtJ35v8L.d.ts.map +1 -0
  20. package/dist/data-grid.d.ts +1 -1
  21. package/dist/data-grid.js +268 -14
  22. package/dist/data-grid.js.map +1 -1
  23. package/dist/detail-list-BS5dRDda.d.ts +175 -0
  24. package/dist/detail-list-BS5dRDda.d.ts.map +1 -0
  25. package/dist/detail-list.d.ts +3 -0
  26. package/dist/detail-list.js +1031 -0
  27. package/dist/detail-list.js.map +1 -0
  28. package/dist/grid-list-D-GbghSH.d.ts +321 -0
  29. package/dist/grid-list-D-GbghSH.d.ts.map +1 -0
  30. package/dist/grid-list.d.ts +3 -0
  31. package/dist/grid-list.js +994 -0
  32. package/dist/grid-list.js.map +1 -0
  33. package/dist/{grouped-item-lister-BWlC9S4e.d.ts → grouped-item-lister-fdZHVxFM.d.ts} +3 -3
  34. package/dist/{grouped-item-lister-BWlC9S4e.d.ts.map → grouped-item-lister-fdZHVxFM.d.ts.map} +1 -1
  35. package/dist/grouped-item-lister.d.ts +1 -1
  36. package/dist/{icon-DR_3rSyZ.d.ts → icon-CDGprSdE.d.ts} +2 -2
  37. package/dist/icon-CDGprSdE.d.ts.map +1 -0
  38. package/dist/icons.d.ts +2 -2
  39. package/dist/incr-search-DpbSJtgj.js +962 -0
  40. package/dist/incr-search-DpbSJtgj.js.map +1 -0
  41. package/dist/incr-search.d.ts +2 -2
  42. package/dist/incr-search.js +2 -3
  43. package/dist/{index-C59-JEPl.d.ts → index-9AhaHIT2.d.ts} +3 -3
  44. package/dist/{index-C59-JEPl.d.ts.map → index-9AhaHIT2.d.ts.map} +1 -1
  45. package/dist/index-CBS4z1v-.d.ts +365 -0
  46. package/dist/index-CBS4z1v-.d.ts.map +1 -0
  47. package/dist/{index-DsjpDksO.d.ts → index-CS9yM8L-.d.ts} +2 -2
  48. package/dist/{index-DsjpDksO.d.ts.map → index-CS9yM8L-.d.ts.map} +1 -1
  49. package/dist/{index-BUB5SICW.d.ts → index-CoqtMSd9.d.ts} +2 -2
  50. package/dist/{index-BUB5SICW.d.ts.map → index-CoqtMSd9.d.ts.map} +1 -1
  51. package/dist/{index-CJ5fshD3.d.ts → index-UvnoZUAo.d.ts} +2 -2
  52. package/dist/{index-CJ5fshD3.d.ts.map → index-UvnoZUAo.d.ts.map} +1 -1
  53. package/dist/{index-BOUaDIW-.d.ts → index-tq6f8007.d.ts} +2 -2
  54. package/dist/{index-BOUaDIW-.d.ts.map → index-tq6f8007.d.ts.map} +1 -1
  55. package/dist/index.d.ts +188 -243
  56. package/dist/index.d.ts.map +1 -1
  57. package/dist/index.js +376 -1004
  58. package/dist/index.js.map +1 -1
  59. package/dist/interaction-Cr13azbM.js +3 -0
  60. package/dist/{labelled-input-base-Dp_9KP0G.d.ts → labelled-input-base-DiTme1JO.d.ts} +2 -2
  61. package/dist/{labelled-input-base-Dp_9KP0G.d.ts.map → labelled-input-base-DiTme1JO.d.ts.map} +1 -1
  62. package/dist/labelled-radial-input.d.ts +2 -2
  63. package/dist/labelled-radial-input.d.ts.map +1 -1
  64. package/dist/labelled-range-input.d.ts +1 -1
  65. package/dist/labelled-slider-input.d.ts +1 -1
  66. package/dist/list-item-animation-DuiwgN3l.js +112 -0
  67. package/dist/list-item-animation-DuiwgN3l.js.map +1 -0
  68. package/dist/list-selection-controller-DTFI40li.js +264 -0
  69. package/dist/list-selection-controller-DTFI40li.js.map +1 -0
  70. package/dist/{list-selection-types-DSuRNWpx.d.ts → list-selection-types-B5ioL-dG.d.ts} +6 -1
  71. package/dist/list-selection-types-B5ioL-dG.d.ts.map +1 -0
  72. package/dist/{menu-DMzjkVnK.js → menu-Bg90Qr0B.js} +3 -3
  73. package/dist/{menu-DMzjkVnK.js.map → menu-Bg90Qr0B.js.map} +1 -1
  74. package/dist/{menu-item-D2S4i8u1.js → menu-item-Bx-Fs7jq.js} +3 -3
  75. package/dist/{menu-item-D2S4i8u1.js.map → menu-item-Bx-Fs7jq.js.map} +1 -1
  76. package/dist/{menu-item-ZmkXNjek.d.ts → menu-item-CZi9vy5O.d.ts} +2 -2
  77. package/dist/{menu-item-ZmkXNjek.d.ts.map → menu-item-CZi9vy5O.d.ts.map} +1 -1
  78. package/dist/menu.d.ts +2 -2
  79. package/dist/menu.js +2 -2
  80. package/dist/miller.d.ts +82 -4
  81. package/dist/miller.d.ts.map +1 -1
  82. package/dist/miller.js +329 -70
  83. package/dist/miller.js.map +1 -1
  84. package/dist/narrowed-text.js +3 -4
  85. package/dist/narrowed-text.js.map +1 -1
  86. package/dist/panel.d.ts +1 -1
  87. package/dist/plots.js +1 -1
  88. package/dist/select-horiz.js +2 -2
  89. package/dist/{split-layout-C5SYbDrq.d.ts → split-layout-JK3xWVX2.d.ts} +2 -2
  90. package/dist/{split-layout-C5SYbDrq.d.ts.map → split-layout-JK3xWVX2.d.ts.map} +1 -1
  91. package/dist/split-layout.d.ts +1 -1
  92. package/dist/{tab-list-DbMIL520.d.ts → tab-list-CGyPt07Z.d.ts} +2 -2
  93. package/dist/{tab-list-DbMIL520.d.ts.map → tab-list-CGyPt07Z.d.ts.map} +1 -1
  94. package/dist/tabs.d.ts +1 -1
  95. package/dist/{tickled-controller-Ds1DMyG7.d.ts → tickled-controller-Bn3YdQsM.d.ts} +12 -1
  96. package/dist/{tickled-controller-Ds1DMyG7.d.ts.map → tickled-controller-Bn3YdQsM.d.ts.map} +1 -1
  97. package/dist/{tickled-controller-h9GmJ_bU.js → tickled-controller-C66DgJHl.js} +16 -1
  98. package/dist/tickled-controller-C66DgJHl.js.map +1 -0
  99. package/dist/{titlebar-B23MrIbI.d.ts → titlebar-Bj5pfdw_.d.ts} +2 -2
  100. package/dist/{titlebar-B23MrIbI.d.ts.map → titlebar-Bj5pfdw_.d.ts.map} +1 -1
  101. package/dist/titlebar.d.ts +1 -1
  102. package/dist/{tree-Ch4W8qXQ.js → tree-DAii2A1g.js} +190 -29
  103. package/dist/tree-DAii2A1g.js.map +1 -0
  104. package/dist/{tree-component-BvV4muxx.d.ts → tree-component-F6qZ0Gdv.d.ts} +9 -1
  105. package/dist/{tree-component-BvV4muxx.d.ts.map → tree-component-F6qZ0Gdv.d.ts.map} +1 -1
  106. package/dist/tree.d.ts +28 -4
  107. package/dist/tree.d.ts.map +1 -1
  108. package/dist/tree.js +1 -1
  109. package/dist/vertical-list-DwtCPy1b.d.ts +143 -0
  110. package/dist/vertical-list-DwtCPy1b.d.ts.map +1 -0
  111. package/dist/vertical-list.d.ts +3 -121
  112. package/dist/vertical-list.js +642 -1
  113. package/dist/vertical-list.js.map +1 -0
  114. package/dist/{xy-axis-DWizs0bt.js → xy-axis-VrX8dmvS.js} +2 -2
  115. package/dist/{xy-axis-DWizs0bt.js.map → xy-axis-VrX8dmvS.js.map} +1 -1
  116. package/docs-user/README.md +1 -0
  117. package/docs-user/data-grid.md +70 -2
  118. package/docs-user/detail-list.md +39 -1
  119. package/docs-user/grid-list.md +298 -0
  120. package/docs-user/incr-search.md +12 -2
  121. package/docs-user/index.json +7 -2
  122. package/docs-user/llms.txt +1 -0
  123. package/docs-user/tree.md +35 -1
  124. package/docs-user/vertical-list.md +58 -2
  125. package/llms.txt +1 -0
  126. package/package.json +17 -2
  127. package/dist/data-grid-Db4QpnII.d.ts.map +0 -1
  128. package/dist/element-search-Y8SRfPGM.js +0 -185
  129. package/dist/element-search-Y8SRfPGM.js.map +0 -1
  130. package/dist/icon-DR_3rSyZ.d.ts.map +0 -1
  131. package/dist/incr-search-vCzuao-2.js +0 -137
  132. package/dist/incr-search-vCzuao-2.js.map +0 -1
  133. package/dist/index-Cu5i8zZY.d.ts +0 -160
  134. package/dist/index-Cu5i8zZY.d.ts.map +0 -1
  135. package/dist/interaction-D5XdKzbR.js +0 -2
  136. package/dist/list-selection-types-DSuRNWpx.d.ts.map +0 -1
  137. package/dist/tickled-controller-h9GmJ_bU.js.map +0 -1
  138. package/dist/tree-Ch4W8qXQ.js.map +0 -1
  139. package/dist/vertical-list-B4w_ksJk.js +0 -814
  140. package/dist/vertical-list-B4w_ksJk.js.map +0 -1
  141. package/dist/vertical-list.d.ts.map +0 -1
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { n as buildHighlightTargets, r as clearHighlights, t as applyHighlights } from "./highlight-B-eQrhhb.js";
2
- import { t as TickledController } from "./tickled-controller-h9GmJ_bU.js";
2
+ import { t as TickledController } from "./tickled-controller-C66DgJHl.js";
3
3
  import { n as tickledStyles, t as tickledItemStyles } from "./tickled-styles-BrAHEj1q.js";
4
4
  import { t as themeFallbacks } from "./fallbacks-DNI9xt0F.js";
5
5
  import { t as popupSurfaceStyles } from "./popup-D80sj2QC.js";
@@ -8,36 +8,39 @@ import { a as iconBus, c as setIconFont, i as hasIcon, l as svgToDataUri, n as g
8
8
  import { t as IxfxIconElement } from "./icon-DcEEGQa_.js";
9
9
  import { AcTextElement } from "./ac-text.js";
10
10
  import { t as DataController } from "./data-controller-DOlmqNKb.js";
11
- import { n as createIncrSearch, r as createFuzzySearch, t as createElementIncrSearch } from "./element-search-Y8SRfPGM.js";
12
- import { n as createAcCompletionProvider, t as IncrSearchTreeController } from "./incr-search-vCzuao-2.js";
11
+ import { a as LIST_SEARCH_COMMAND_IDS, c as createElementListSearchAdapter, d as IncrSearchTreeController, f as createIncrSearch, i as ListSearchController, l as KeyedElementIndex, m as createFuzzySearch, n as listSearchStyles, o as registerListSearchCommands, p as createAcCompletionProvider, r as LIST_SEARCH_MODES, s as createElementIncrSearch, t as createTreeListSearchAdapter, u as keyForElement } from "./incr-search-DpbSJtgj.js";
13
12
  import { AcTokenElement } from "./ac-token.js";
14
13
  import { i as ICON_CLOSE, n as ICON_CHECK, r as ICON_CHEVRON_DOWN, t as ICON_CARET_RIGHT } from "./defaults-BJwDsMH4.js";
15
14
  import "./icons.js";
16
15
  import { t as prominenceStyles } from "./prominence-DlFShic3.js";
17
16
  import { n as buttonStyles, t as ButtonElement } from "./button-DsFbV4XD.js";
18
- import { n as ListSelectionController, r as isMultiMode, t as VerticalListElement } from "./vertical-list-B4w_ksJk.js";
19
- import "./interaction-D5XdKzbR.js";
20
- import "./menu-item-D2S4i8u1.js";
17
+ import { a as prefersReducedMotion, i as listItemFadeKeyframes, n as animateListItemIn, r as animateListItemOut, t as LIST_ITEM_MOTION_MS } from "./list-item-animation-DuiwgN3l.js";
18
+ import { n as isMultiMode, t as ListSelectionController } from "./list-selection-controller-DTFI40li.js";
19
+ import "./interaction-Cr13azbM.js";
20
+ import "./menu-item-Bx-Fs7jq.js";
21
21
  import { Checkbox } from "./checkbox.js";
22
22
  import { n as ColourPicker, t as ColourPickerPopup } from "./colour-picker-Ddsk-7E_.js";
23
23
  import { LabelledRangeInput } from "./labelled-range-input.js";
24
24
  import { t as collectDataResult } from "./data-provider-35TyWclX.js";
25
- import { i as TreeDataModel, n as TreeListElement, r as TreeBaseElement, t as TreeController } from "./tree-Ch4W8qXQ.js";
25
+ import { i as TreeDataModel, n as TreeListElement, r as TreeBaseElement, t as TreeController } from "./tree-DAii2A1g.js";
26
26
  import { t as scrollFadeStyles } from "./scroll-fade-CAs1EabA.js";
27
27
  import { NarrowedTextElement } from "./narrowed-text.js";
28
28
  import { CrumbNavigationElement } from "./crumbs.js";
29
29
  import { DataDisplayComponent } from "./data-display.js";
30
30
  import { t as createDragGhost } from "./drag-ghost-DtSDvjri.js";
31
- import { t as menu_exports } from "./menu-DMzjkVnK.js";
31
+ import { t as menu_exports } from "./menu-Bg90Qr0B.js";
32
32
  import { DataGridElement } from "./data-grid.js";
33
+ import { DetailListElement } from "./detail-list.js";
33
34
  import { a as distribute, c as toSnapshot, i as buildModel, l as walk, n as SplitPanelElement, o as normalise, r as applySnapshot, s as resolveTracks, t as SplitLayoutElement } from "./split-layout-BFSbUZQv.js";
34
35
  import { TabListElement } from "./tabs.js";
35
36
  import { TitlebarElement } from "./titlebar.js";
36
37
  import { EditableLabel } from "./editable-label.js";
37
38
  import { n as EditableNumber, t as LabelledSliderInput } from "./labelled-slider-input-BInB5GKL.js";
38
39
  import { t as FormElement } from "./form-CsXJX15p.js";
40
+ import { GridListElement } from "./grid-list.js";
39
41
  import { PanelElement } from "./panel.js";
40
42
  import { GroupedItemListerElement } from "./grouped-item-lister.js";
43
+ import { VerticalListElement } from "./vertical-list.js";
41
44
  import { t as IxfxHexEditorElement } from "./hex-editor-BiRfwTz1.js";
42
45
  import { t as RadialInputElement } from "./radial-input-BfA3GO0H.js";
43
46
  import { LabelledRadialInput } from "./labelled-radial-input.js";
@@ -45,7 +48,7 @@ import { t as SliderInputElement } from "./slider-input-Bl1hFE89.js";
45
48
  import { LedElement } from "./led.js";
46
49
  import { MillerBaseElement, MillerListElement, MillerTreeController } from "./miller.js";
47
50
  import { NotificationPillElement } from "./notifier.js";
48
- import { a as TooltipElement, c as computeColour, i as PlotMultiAxis, l as parseColourScaleAttr, n as PlotSingleAxis, o as PlotDataSeries, r as PlotHistogram, s as applyAgeModification, t as PlotXyAxis, u as readBaseColour } from "./xy-axis-DWizs0bt.js";
51
+ import { a as TooltipElement, c as computeColour, i as PlotMultiAxis, l as parseColourScaleAttr, n as PlotSingleAxis, o as PlotDataSeries, r as PlotHistogram, s as applyAgeModification, t as PlotXyAxis, u as readBaseColour } from "./xy-axis-VrX8dmvS.js";
49
52
  import { PolarPad } from "./polar-pad.js";
50
53
  import { t as tooltipValueStyles } from "./tooltip-CJt3WLmN.js";
51
54
  import { RangeInput } from "./range-input.js";
@@ -57,12 +60,13 @@ import { SwipeElement } from "./swipe.js";
57
60
  import { a as resolveCssVars, i as attachTimelineInteractions, n as TimelineTrackTabElement, o as resolveGrid, r as timelineStyles, s as TimelineController, t as IxfxTimelineElement } from "./timeline-B2fgDL4U.js";
58
61
  import { TransitoryLabelElement } from "./transitory-label.js";
59
62
  import { XyPad } from "./xy-pad.js";
60
- import { LitElement, css, html, nothing, render } from "lit";
63
+ import { LitElement, css, html, nothing } from "lit";
61
64
  import { customElement, eventOptions, property, query, queryAssignedElements, state } from "lit/decorators.js";
62
65
  import { unsafeHTML } from "lit/directives/unsafe-html.js";
63
66
  import { Trees } from "@ixfx/collections";
64
67
  import { clamp, clamper, scaler } from "@ixfx/numbers";
65
68
  import { repeat } from "lit/directives/repeat.js";
69
+ import { classMap } from "lit/directives/class-map.js";
66
70
  //#region src/button/radio-group.ts
67
71
  let RadioGroup = class RadioGroup extends LitElement {
68
72
  constructor(..._args) {
@@ -1602,1032 +1606,270 @@ __decorate([state()], CrumbPathElement.prototype, "_editStartIndex", void 0);
1602
1606
  __decorate([state()], CrumbPathElement.prototype, "_openCumPath", void 0);
1603
1607
  CrumbPathElement = __decorate([safeCustomElement(`ixfx-crumb-path`)], CrumbPathElement);
1604
1608
  //#endregion
1605
- //#region src/detail-list/detail-list.ts
1606
- const ITEM_SELECTOR = `:scope > li`;
1607
- const SEARCH_HIGHLIGHT_KEY = `detail-list-search`;
1608
- const LABEL_SPAN_CSS = `overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0`;
1609
- let DetailListElement = class DetailListElement extends LitElement {
1610
- constructor(..._args2) {
1611
- super(..._args2);
1612
- this.selectionMode = `single`;
1613
- this.interactionMode = `standard`;
1614
- this.sort = `none`;
1615
- this.sortBy = ``;
1616
- this.items = [];
1617
- this.displayProperty = ``;
1618
- this.iconProperty = ``;
1619
- this.keyProperty = ``;
1620
- this.itemWrap = `stop`;
1621
- this._searchVisible = false;
1622
- this._searchQuery = ``;
1623
- this.#itemData = /* @__PURE__ */ new WeakMap();
1624
- this.#grid = [];
1625
- this.#positions = /* @__PURE__ */ new Map();
1626
- this.#geometryScheduled = false;
1627
- this.#builtFromProvider = [];
1628
- this.#providerBuilt = 0;
1629
- this.#data = new DataController(this);
1630
- this.selection = new ListSelectionController({
1631
- getOrderedItems: () => this._getVisibleItems(),
1632
- isSelectable: (el) => this._isCheckable(el),
1633
- getModes: () => ({
1634
- selectionMode: this.selectionMode,
1635
- interactionMode: this.interactionMode
1636
- }),
1637
- onChange: (selected, previous) => {
1638
- this._syncItemStates();
1639
- this._dispatch(`list-select`, {
1640
- selected,
1641
- previous
1642
- });
1643
- }
1644
- });
1645
- this.tickled = new TickledController(this, {
1646
- getItems: () => this._getVisibleItems().map((el) => ({
1647
- id: el,
1648
- enabled: !el.hasAttribute(`not-checked`)
1649
- })),
1650
- onTickle: (id) => {
1651
- if (id !== void 0) this._dispatch(`list-tickle`, { item: id });
1652
- },
1653
- onStateChange: () => this._syncItemStates(),
1654
- itemWrap: this.itemWrap
1655
- });
1656
- this._onPointerOver = (e) => {
1657
- const item = this._findListItem(e.composedPath());
1658
- if (item) this.tickled.handlePointerEnter(item);
1659
- };
1660
- this._onPointerLeave = (e) => {
1661
- this.tickled.handlePointerLeave(e);
1662
- };
1663
- this._onKeyDown = (e) => {
1664
- if ((e.ctrlKey || e.metaKey) && e.key === `f`) {
1665
- e.preventDefault();
1666
- this._openSearch();
1667
- return;
1668
- }
1669
- if ((e.ctrlKey || e.metaKey) && e.key === `a`) {
1670
- if (this.selection.canSelectAll()) {
1671
- e.preventDefault();
1672
- this.selection.selectAll();
1673
- }
1674
- return;
1675
- }
1676
- if (this._searchVisible && e.key === `Escape`) {
1677
- e.preventDefault();
1678
- this._closeSearch();
1679
- return;
1680
- }
1681
- switch (e.key) {
1682
- case `ArrowDown`:
1683
- e.preventDefault();
1684
- this.#navigateDown();
1685
- if (e.shiftKey && this.interactionMode === `vscode`) {
1686
- const cur = this.tickled.tickledId;
1687
- if (cur) this.selection.rangeTo(cur);
1688
- }
1689
- break;
1690
- case `ArrowUp`:
1691
- e.preventDefault();
1692
- this.#navigateUp();
1693
- if (e.shiftKey && this.interactionMode === `vscode`) {
1694
- const cur = this.tickled.tickledId;
1695
- if (cur) this.selection.rangeTo(cur);
1696
- }
1697
- break;
1698
- case `ArrowRight`:
1699
- e.preventDefault();
1700
- this.#navigateRight();
1701
- break;
1702
- case `ArrowLeft`:
1703
- e.preventDefault();
1704
- this.#navigateLeft();
1705
- break;
1706
- case `Home`:
1707
- e.preventDefault();
1708
- this.#navigateTo(this.#findFirstCheckable());
1709
- break;
1710
- case `End`:
1711
- e.preventDefault();
1712
- this.#navigateTo(this.#findLastCheckable());
1713
- break;
1714
- case `Enter`: {
1715
- const id = this.tickled.tickledId;
1716
- if (id) {
1717
- e.preventDefault();
1718
- this._dispatch(`list-item-click`, { item: id });
1719
- this._activateItem(id);
1720
- }
1721
- break;
1722
- }
1723
- case ` `: {
1724
- const id = this.tickled.tickledId;
1725
- if (id) {
1726
- e.preventDefault();
1727
- this.selection.handleSpace(id);
1728
- }
1729
- break;
1730
- }
1731
- case `Escape`: this.selection.clear();
1732
- }
1733
- };
1734
- this._onClick = (e) => {
1735
- const item = this._findListItem(e.composedPath());
1736
- if (!item) return;
1737
- this.focus();
1738
- this._dispatch(`list-item-click`, { item });
1739
- this.selection.handleClick(item, e);
1740
- };
1741
- this._onDblClick = (e) => {
1742
- const item = this._findListItem(e.composedPath());
1743
- if (item) this._activateItem(item);
1744
- };
1745
- this._onWheel = (e) => {
1746
- e.preventDefault();
1747
- this._listEl.scrollLeft += e.deltaY;
1748
- };
1749
- this._onSearchInput = (e) => {
1750
- const input = e.target;
1751
- this._searchQuery = input.value;
1752
- this.#search?.setQuery(this._searchQuery).then(() => this.#recomputeGeometry());
1753
- };
1754
- this._onSearchKeydown = (e) => {
1755
- if (e.key === `Escape`) {
1756
- e.stopPropagation();
1757
- this._closeSearch();
1758
- } else if (e.key === `ArrowDown`) {
1759
- e.preventDefault();
1760
- this.focus();
1761
- this.#navigateDown();
1762
- } else if (e.key === `ArrowUp`) {
1763
- e.preventDefault();
1764
- this.focus();
1765
- this.#navigateUp();
1766
- } else if (e.key === `ArrowRight`) e.preventDefault();
1767
- else if (e.key === `ArrowLeft`) e.preventDefault();
1768
- else if (e.key === `Enter`) {
1769
- e.preventDefault();
1770
- e.stopPropagation();
1771
- const id = this.tickled.tickledId ?? this.#findFirstCheckable();
1772
- if (id) {
1773
- this._dispatch(`list-item-click`, { item: id });
1774
- this._activateItem(id);
1775
- this._closeSearch();
1776
- }
1777
- } else if (e.key === ` `) {
1778
- const id = this.tickled.tickledId;
1779
- if (id) {
1780
- e.preventDefault();
1781
- this.selection.handleSpace(id);
1782
- }
1783
- }
1784
- };
1785
- }
1786
- #search;
1787
- #itemData;
1788
- #grid;
1789
- #positions;
1790
- #geometryScheduled;
1791
- #resizeObserver;
1792
- #mutationObserver;
1793
- #builtFromProvider;
1794
- #providerBuilt;
1795
- #data;
1796
- connectedCallback() {
1797
- super.connectedCallback();
1798
- if (!this.hasAttribute(`tabindex`)) this.tabIndex = 0;
1799
- this.addEventListener(`pointerover`, this._onPointerOver);
1800
- this.addEventListener(`pointerleave`, this._onPointerLeave);
1801
- this.addEventListener(`keydown`, this._onKeyDown);
1802
- this.addEventListener(`click`, this._onClick);
1803
- this.addEventListener(`dblclick`, this._onDblClick);
1804
- this.addEventListener(`wheel`, this._onWheel);
1805
- this.#resizeObserver = new ResizeObserver(() => this.#scheduleGeometry());
1806
- this.#resizeObserver.observe(this);
1807
- this.#mutationObserver = new MutationObserver(() => {
1808
- this.#processChildren();
1809
- this.#scheduleGeometry();
1810
- });
1811
- this.#mutationObserver.observe(this, {
1812
- childList: true,
1813
- subtree: false,
1814
- attributeFilter: [`hidden`]
1815
- });
1816
- }
1817
- disconnectedCallback() {
1818
- super.disconnectedCallback();
1819
- this.removeEventListener(`pointerover`, this._onPointerOver);
1820
- this.removeEventListener(`pointerleave`, this._onPointerLeave);
1821
- this.removeEventListener(`keydown`, this._onKeyDown);
1822
- this.removeEventListener(`click`, this._onClick);
1823
- this.removeEventListener(`dblclick`, this._onDblClick);
1824
- this.removeEventListener(`wheel`, this._onWheel);
1825
- this.#resizeObserver?.disconnect();
1826
- this.#resizeObserver = void 0;
1827
- this.#mutationObserver?.disconnect();
1828
- this.#mutationObserver = void 0;
1829
- this.#search?.dispose();
1830
- this.#search = void 0;
1831
- }
1832
- firstUpdated() {
1833
- this.#processChildren();
1834
- if (this.filterPredicate) this.#applyFilterPredicate();
1835
- this.#search = createElementIncrSearch({
1836
- container: this,
1837
- itemSelector: ITEM_SELECTOR,
1838
- getLabel: (el) => el.getAttribute(`data-search-label`) ?? el.textContent?.trim() ?? ``,
1839
- getKey: (el) => el.getAttribute(`data-key`) ?? el.getAttribute(`data-search-label`) ?? el.textContent?.trim() ?? ``,
1840
- highlightKey: SEARCH_HIGHLIGHT_KEY
1841
- });
1842
- this.#recomputeGeometry();
1843
- }
1844
- updated(changed) {
1845
- super.updated(changed);
1846
- if (changed.has(`provider`)) {
1847
- this.#builtFromProvider = [];
1848
- this.#providerBuilt = 0;
1849
- this.#data.provider = this.provider;
1850
- queueMicrotask(() => {
1851
- if (this.provider) this.#data.request(void 0);
1852
- });
1853
- return;
1854
- }
1855
- if (changed.has(`items`) && this.provider === void 0 && this.items.length > 0) this.#rebuildFromObjects(this.items);
1856
- this.#syncFromProvider();
1857
- if (changed.has(`sort`) || changed.has(`sortBy`) || changed.has(`sortComparator`)) this.#applySort();
1858
- if (changed.has(`filterPredicate`)) {
1859
- this.#applyFilterPredicate();
1860
- this.#scheduleGeometry();
1861
- }
1862
- if (changed.has(`interactionMode`)) {
1863
- this.selection.clearAnchor();
1864
- this._syncItemStates();
1865
- } else if (changed.has(`checkboxRenderer`) && this.interactionMode === `checked`) this._syncCheckboxes();
1866
- }
1867
- /** The most recently selected item, or `undefined` if nothing is selected. */
1868
- get selectedItem() {
1869
- return [...this.selection.selected].at(-1);
1870
- }
1871
- /** The full current selection set (read-only). */
1872
- get selectedItems() {
1873
- return this.selection.selected;
1874
- }
1875
- /**
1876
- * Force an immediate geometry recompute. Called automatically on resize,
1877
- * DOM changes, sort/filter/search changes — only needed when layout is
1878
- * changed by outside forces (e.g. an external font load).
1879
- */
1880
- refreshLayout() {
1881
- this.#recomputeGeometry();
1882
- }
1883
- addItem(item, options) {
1884
- let el;
1885
- if (typeof item === `string`) {
1886
- const li = document.createElement(`li`);
1887
- this._setLabel(li, item);
1888
- el = li;
1889
- } else el = item;
1890
- if (options?.notChecked) el.setAttribute(`not-checked`, ``);
1891
- this.appendChild(el);
1892
- this.#ensureIcon(el);
1893
- if (this.sort !== `none`) this.#applySort();
1894
- if (this.interactionMode === `checked`) this._syncCheckboxes();
1895
- }
1896
- removeItem(item) {
1897
- item.remove();
1898
- }
1899
- clearItems() {
1900
- this.replaceChildren();
1901
- this.selection.reset();
1902
- this.#builtFromProvider = [];
1903
- this.#providerBuilt = 0;
1904
- this._syncItemStates();
1905
- }
1906
- /**
1907
- * Replace the entire item list in one call.
1908
- * Equivalent to `clearItems()` followed by `addItem()` for each element.
1909
- */
1910
- setItems(items) {
1911
- this.clearItems();
1912
- for (const item of items) {
1913
- this.appendChild(item);
1914
- this.#ensureIcon(item);
1915
- }
1916
- if (this.sort !== `none`) this.#applySort();
1917
- if (this.interactionMode === `checked`) this._syncCheckboxes();
1918
- }
1919
- /**
1920
- * Get the source object behind an `<li>` built from an `items` array or
1921
- * provider. Returns `undefined` for declarative / programmatic elements.
1922
- */
1923
- getItemData(item) {
1924
- return this.#itemData.get(item);
1925
- }
1926
- /** Dispatch a command to this list. No-op by default (grouped-lister parity). */
1927
- dispatch(_commandId, _args) {}
1928
- select(item) {
1929
- this.selection.replace(item);
1930
- }
1931
- selectMany(items) {
1932
- this.selection.selectMany(items);
1933
- }
1934
- deselect(item) {
1935
- this.selection.deselect(item);
1936
- }
1937
- clearSelection() {
1938
- this.selection.clear();
1939
- }
1940
- selectAll() {
1941
- if (this.selection.canSelectAll()) this.selection.selectAll();
1942
- }
1943
- *selectedFilter(kind) {
1944
- const wanted = (el) => {
1945
- switch (kind) {
1946
- case `selected`: return el.hasAttribute(`data-selected`);
1947
- case `checked`: return el.hasAttribute(`data-checked`);
1948
- case `either`: return el.hasAttribute(`data-selected`) || el.hasAttribute(`data-checked`);
1949
- }
1950
- };
1951
- for (const el of this.querySelectorAll(ITEM_SELECTOR)) {
1952
- if (el.hidden) continue;
1953
- if (wanted(el)) yield el;
1954
- }
1955
- }
1956
- #rebuildFromObjects(items) {
1957
- const keyOf = (el) => el.getAttribute(`data-key`) ?? el.getAttribute(`data-search-label`) ?? null;
1958
- const previousKeys = new Set([...this.selection.selected].map(keyOf).filter((k) => k !== null));
1959
- const anchorKey = this.selection.anchor ? keyOf(this.selection.anchor) : null;
1960
- this.replaceChildren(...items.map((item, index) => this.#createItemEl(item, index)));
1961
- if (previousKeys.size > 0 || anchorKey !== null) {
1962
- const next = [];
1963
- let anchor;
1964
- for (const el of this.querySelectorAll(ITEM_SELECTOR)) {
1965
- const key = keyOf(el);
1966
- if (key !== null && previousKeys.has(key)) next.push(el);
1967
- if (key !== null && key === anchorKey) anchor = el;
1968
- }
1969
- this.selection.seed(next, anchor);
1970
- }
1971
- if (this.sort !== `none`) this.#applySort();
1972
- if (this.interactionMode === `checked`) this._syncCheckboxes();
1973
- this._syncItemStates();
1974
- this.#scheduleGeometry();
1975
- }
1976
- #createItemEl(item, index) {
1977
- const li = document.createElement(`li`);
1978
- this.#itemData.set(li, item);
1979
- const content = this.formatter ? this.formatter(item, index) : this.displayProperty ? this.#propertyValue(item, this.displayProperty) : item;
1980
- if (typeof content === `string`) {
1981
- this._setLabel(li, content);
1982
- if (this.iconProperty) {
1983
- const icon = this.#propertyValue(item, this.iconProperty);
1984
- if (typeof icon === `string` && icon.length > 0) li.setAttribute(`data-icon`, icon);
1985
- }
1986
- } else if (content instanceof Node) li.appendChild(content);
1987
- if (this.keyProperty) {
1988
- const key = this.#propertyValue(item, this.keyProperty);
1989
- if (key !== void 0 && key !== null) li.setAttribute(`data-key`, String(key));
1990
- }
1991
- this.#ensureIcon(li);
1992
- return li;
1993
- }
1994
- #propertyValue(item, prop) {
1995
- if (typeof item !== `object` || item === null || !(prop in item)) return void 0;
1996
- return item[prop];
1997
- }
1998
- #syncFromProvider() {
1999
- if (this.provider === void 0) return;
2000
- const items = this.#data.items;
2001
- if (items === this.#builtFromProvider) return;
2002
- let prefix = 0;
2003
- while (prefix < this.#builtFromProvider.length && prefix < items.length && this.#builtFromProvider[prefix] === items[prefix]) prefix++;
2004
- if (prefix === this.#builtFromProvider.length && prefix <= items.length) {
2005
- const tail = items.slice(prefix);
2006
- for (const item of tail) this.appendChild(this.#createItemEl(item, this.#providerBuilt++));
2007
- if (this.sort !== `none`) this.#applySort();
2008
- if (this.interactionMode === `checked`) this._syncCheckboxes();
2009
- this._syncItemStates();
2010
- this.#scheduleGeometry();
2011
- } else {
2012
- this.#providerBuilt = 0;
2013
- this.#rebuildFromObjects(items);
2014
- }
2015
- this.#builtFromProvider = items;
2016
- }
2017
- #applyFilterPredicate() {
2018
- if (!this.filterPredicate) {
2019
- for (const el of this.querySelectorAll(ITEM_SELECTOR)) el.removeAttribute(`hidden`);
2020
- return;
2021
- }
2022
- for (const el of this.querySelectorAll(ITEM_SELECTOR)) if (this.filterPredicate(el)) el.removeAttribute(`hidden`);
2023
- else el.setAttribute(`hidden`, ``);
2024
- }
2025
- #applySort() {
2026
- if (this.sort === `none`) return;
2027
- const rows = [...this.querySelectorAll(ITEM_SELECTOR)];
2028
- if (rows.length < 2) return;
2029
- const dir = this.sort === `descending` ? -1 : 1;
2030
- const meta = (el) => ({
2031
- element: el,
2032
- data: this.getItemData(el),
2033
- label: el.getAttribute(`data-search-label`) ?? el.textContent?.trim() ?? ``
2034
- });
2035
- let cmp;
2036
- if (this.sortComparator) {
2037
- const user = this.sortComparator;
2038
- cmp = (a, b) => user(meta(a), meta(b));
2039
- } else if (this.sortBy) {
2040
- const prop = this.sortBy;
2041
- const key = (el) => {
2042
- const v = this.#propertyValue(this.getItemData(el), prop);
2043
- if (typeof v === `number`) return v;
2044
- return v === void 0 || v === null ? `` : String(v);
2045
- };
2046
- cmp = (a, b) => {
2047
- const ka = key(a);
2048
- const kb = key(b);
2049
- if (typeof ka === `number` && typeof kb === `number`) return ka - kb;
2050
- return String(ka).localeCompare(String(kb), void 0, { numeric: true });
2051
- };
2052
- } else cmp = (a, b) => meta(a).label.localeCompare(meta(b).label, void 0, { numeric: true });
2053
- const indexed = rows.map((el, i) => ({
2054
- el,
2055
- i
2056
- }));
2057
- indexed.sort((a, b) => cmp(a.el, b.el) * dir || a.i - b.i);
2058
- for (const { el } of indexed) this.appendChild(el);
2059
- this.#scheduleGeometry();
2060
- }
2061
- #scheduleGeometry() {
2062
- if (this.#geometryScheduled) return;
2063
- this.#geometryScheduled = true;
2064
- requestAnimationFrame(() => {
2065
- this.#geometryScheduled = false;
2066
- this.#recomputeGeometry();
2067
- });
2068
- }
2069
- #recomputeGeometry() {
2070
- this.#grid = [];
2071
- this.#positions.clear();
2072
- if (!this.isConnected) return;
2073
- const columns = [];
2074
- for (const el of this._getVisibleItems()) {
2075
- const rect = el.getBoundingClientRect();
2076
- if (rect.width === 0 && rect.height === 0) continue;
2077
- let ci = columns.findIndex((c) => Math.abs(c.left - rect.left) <= 1);
2078
- if (ci === -1) {
2079
- columns.push({
2080
- left: rect.left,
2081
- items: []
2082
- });
2083
- ci = columns.length - 1;
2084
- }
2085
- columns[ci].items.push(el);
2086
- }
2087
- columns.sort((a, b) => a.left - b.left);
2088
- for (const col of columns) col.items.sort((a, b) => a.getBoundingClientRect().top - b.getBoundingClientRect().top);
2089
- this.#grid = columns.map((c) => c.items);
2090
- this.#grid.forEach((col, colIdx) => col.forEach((el, rowIdx) => this.#positions.set(el, {
2091
- col: colIdx,
2092
- row: rowIdx
2093
- })));
2094
- }
2095
- #positionOf(el) {
2096
- let pos = this.#positions.get(el);
2097
- if (pos === void 0) {
2098
- this.#recomputeGeometry();
2099
- pos = this.#positions.get(el);
2100
- }
2101
- return pos;
2102
- }
2103
- #itemAt(col, row) {
2104
- return this.#grid[col]?.[row];
2105
- }
2106
- #gridSize() {
2107
- if (this.#grid.length === 0) this.#recomputeGeometry();
2108
- return {
2109
- cols: this.#grid.length,
2110
- rows: Math.max(0, ...this.#grid.map((c) => c.length))
2111
- };
2112
- }
2113
- #navigateTo(target) {
2114
- if (!target) return;
2115
- this.tickled.handleKeyboardNavigation(target);
2116
- this.tickled.tickledId?.scrollIntoView({
2117
- block: `nearest`,
2118
- inline: `nearest`
2119
- });
2120
- }
2121
- #findNextCheckable(fromCol, fromRow, deltaCol, deltaRow) {
2122
- const { cols } = this.#gridSize();
2123
- const maxIterations = this._getVisibleItems().length + 1;
2124
- let col = deltaCol !== 0 ? fromCol + deltaCol : fromCol;
2125
- let row = deltaRow !== 0 ? fromRow + deltaRow : fromRow;
2126
- let wrappedColumns = false;
2127
- for (let iterations = 0; iterations < maxIterations; iterations++) {
2128
- const colLength = this.#grid[col]?.length ?? 0;
2129
- if (row < 0 || row >= colLength) {
2130
- if (deltaCol !== 0 || wrappedColumns) {
2131
- if (this.itemWrap === `wrap`) {
2132
- if (deltaCol < 0) col = cols - 1;
2133
- else if (deltaCol > 0) col = 0;
2134
- wrappedColumns = true;
2135
- } else return;
2136
- }
2137
- if (deltaRow > 0) {
2138
- if (col + 1 < cols) {
2139
- col = col + 1;
2140
- row = 0;
2141
- wrappedColumns = true;
2142
- } else if (this.itemWrap === `wrap`) {
2143
- col = 0;
2144
- row = 0;
2145
- wrappedColumns = true;
2146
- } else return;
2147
- } else if (deltaRow < 0) {
2148
- if (col - 1 >= 0) {
2149
- col = col - 1;
2150
- row = (this.#grid[col]?.length ?? 1) - 1;
2151
- wrappedColumns = true;
2152
- } else if (this.itemWrap === `wrap`) {
2153
- col = cols - 1;
2154
- row = (this.#grid[col]?.length ?? 1) - 1;
2155
- wrappedColumns = true;
2156
- } else return;
2157
- } else if (deltaCol !== 0) {
2158
- const targetColLength = this.#grid[col]?.length ?? 0;
2159
- if (row >= targetColLength && targetColLength > 0) row = targetColLength - 1;
2160
- }
2161
- if (col === fromCol && row === fromRow + deltaRow) return;
2162
- continue;
2163
- }
2164
- const item = this.#itemAt(col, row);
2165
- if (item && this._isCheckable(item)) return item;
2166
- row += deltaRow;
2167
- }
2168
- }
2169
- #findFirstCheckable() {
2170
- return this._getVisibleItems().find((item) => this._isCheckable(item));
2171
- }
2172
- #findLastCheckable() {
2173
- const visibleItems = this._getVisibleItems();
2174
- for (let i = visibleItems.length - 1; i >= 0; i--) if (this._isCheckable(visibleItems[i])) return visibleItems[i];
2175
- }
2176
- #navigateDown() {
2177
- this.#recomputeGeometry();
2178
- const cur = this.tickled.tickledId ? this.#positionOf(this.tickled.tickledId) : void 0;
2179
- if (!cur) {
2180
- this.#navigateTo(this.#findFirstCheckable());
2181
- return;
2182
- }
2183
- const next = this.#findNextCheckable(cur.col, cur.row, 0, 1);
2184
- if (next) this.#navigateTo(next);
2185
- }
2186
- #navigateUp() {
2187
- this.#recomputeGeometry();
2188
- const cur = this.tickled.tickledId ? this.#positionOf(this.tickled.tickledId) : void 0;
2189
- if (!cur) {
2190
- this.#navigateTo(this.#findLastCheckable());
2191
- return;
2192
- }
2193
- const next = this.#findNextCheckable(cur.col, cur.row, 0, -1);
2194
- if (next) this.#navigateTo(next);
2195
- }
2196
- #navigateRight() {
2197
- this.#recomputeGeometry();
2198
- const cur = this.tickled.tickledId ? this.#positionOf(this.tickled.tickledId) : void 0;
2199
- if (!cur) {
2200
- this.#navigateTo(this.#findFirstCheckable());
2201
- return;
2202
- }
2203
- const next = this.#findNextCheckable(cur.col, cur.row, 1, 0);
2204
- if (next) this.#navigateTo(next);
2205
- else if (this.itemWrap === `wrap`) this.#navigateTo(this.#findFirstCheckable());
2206
- }
2207
- #navigateLeft() {
2208
- this.#recomputeGeometry();
2209
- const cur = this.tickled.tickledId ? this.#positionOf(this.tickled.tickledId) : void 0;
2210
- if (!cur) {
2211
- this.#navigateTo(this.#findFirstCheckable());
2212
- return;
2213
- }
2214
- const next = this.#findNextCheckable(cur.col, cur.row, -1, 0);
2215
- if (next) this.#navigateTo(next);
2216
- else if (this.itemWrap === `wrap`) this.#navigateTo(this.#findLastCheckable());
2217
- }
2218
- _getVisibleItems() {
2219
- return Array.from(this.querySelectorAll(ITEM_SELECTOR)).filter((el) => {
2220
- if (el.hidden) return false;
2221
- if (this.filterPredicate && !this.filterPredicate(el)) return false;
2222
- return true;
2223
- });
2224
- }
2225
- /**
2226
- * Set the column width to fit the widest visible item.
2227
- *
2228
- * This is opt-in; detail lists continue to use `--detail-list-column-width`
2229
- * (or its 220px fallback) until this method is called. The measured value is
2230
- * set directly on this element, so it takes precedence over an inherited
2231
- * value. Returns the applied width in pixels, or 0 when there are no items.
2232
- */
2233
- async fitColumnWidth() {
2234
- await this.updateComplete;
2235
- const items = this._getVisibleItems();
2236
- if (items.length === 0) return 0;
2237
- const previousWidth = this.style.getPropertyValue(`--detail-list-column-width`);
2238
- const previousPriority = this.style.getPropertyPriority(`--detail-list-column-width`);
2239
- this.style.setProperty(`--detail-list-column-width`, `max-content`);
2240
- const width = Math.ceil(Math.max(...items.map((item) => item.getBoundingClientRect().width)));
2241
- if (previousWidth) this.style.setProperty(`--detail-list-column-width`, previousWidth, previousPriority);
2242
- else this.style.removeProperty(`--detail-list-column-width`);
2243
- this.style.setProperty(`--detail-list-column-width`, `${width}px`);
2244
- this.#scheduleGeometry();
2245
- return width;
2246
- }
2247
- _isCheckable(item) {
2248
- if (this.interactionMode !== `checked`) return true;
2249
- return !item.hasAttribute(`not-checked`);
2250
- }
2251
- _setLabel(li, label) {
2252
- const span = document.createElement(`span`);
2253
- span.style.cssText = LABEL_SPAN_CSS;
2254
- span.textContent = label;
2255
- li.appendChild(span);
2256
- li.setAttribute(`data-search-label`, label);
2257
- }
2258
- /**
2259
- * Inject a leading `<ixfx-icon>` into an item carrying `data-icon`.
2260
- * Idempotent — skips when the item already starts with an icon.
2261
- */
2262
- #ensureIcon(li) {
2263
- const name = li.getAttribute(`data-icon`);
2264
- if (!name) return;
2265
- if (li.firstElementChild?.tagName === `IXFX-ICON`) return;
2266
- const icon = document.createElement(`ixfx-icon`);
2267
- icon.setAttribute(`name`, name);
2268
- icon.style.marginInlineEnd = `var(--detail-list-icon-gap, 6px)`;
2269
- icon.style.flexShrink = `0`;
2270
- li.prepend(icon);
2271
- }
2272
- /** Scan light-DOM children for unprocessed `data-icon` items. */
2273
- #processChildren() {
2274
- for (const el of this.querySelectorAll(ITEM_SELECTOR)) this.#ensureIcon(el);
2275
- }
2276
- _syncItemStates() {
2277
- for (const el of this.querySelectorAll(ITEM_SELECTOR)) {
2278
- el.toggleAttribute(`data-tickled`, this.tickled.isTickled(el));
2279
- el.toggleAttribute(`data-selected`, this.selection.selected.has(el));
2280
- if (this.interactionMode === `checked`) el.toggleAttribute(`data-checked`, !el.hasAttribute(`not-checked`) && this.selection.selected.has(el));
2281
- else el.removeAttribute(`data-checked`);
2282
- }
2283
- this.dataset.inputMode = this.tickled.inputMode;
2284
- this._syncCheckboxes();
2285
- }
2286
- _syncCheckboxes() {
2287
- const inCheckedMode = this.interactionMode === `checked`;
2288
- const wantsCustom = typeof this.checkboxRenderer === `function`;
2289
- for (const el of this.querySelectorAll(`li`)) {
2290
- const existing = el.querySelector(`.ixfx-list-checkbox`);
2291
- if (!inCheckedMode) {
2292
- if (existing) existing.remove();
2293
- continue;
2294
- }
2295
- if (el.hasAttribute(`not-checked`)) {
2296
- if (existing) existing.remove();
2297
- continue;
2298
- }
2299
- const existingIsCustom = existing?.classList.contains(`ixfx-list-checkbox-custom`) ?? false;
2300
- if (existing && existingIsCustom !== wantsCustom) existing.remove();
2301
- let cb = el.querySelector(`.ixfx-list-checkbox`);
2302
- if (!cb) {
2303
- if (wantsCustom) {
2304
- const opts = this.checkboxRenderer;
2305
- const margin = opts.margin ?? `0`;
2306
- const width = opts.width ?? `24px`;
2307
- cb = this._createCustomCheckbox(el, margin, width);
2308
- } else cb = this._createNativeCheckboxCell(el);
2309
- el.prepend(cb);
2310
- }
2311
- const checked = this.selection.selected.has(el);
2312
- if (wantsCustom) {
2313
- cb.setAttribute(`aria-checked`, String(checked));
2314
- cb.toggleAttribute(`data-checked`, checked);
2315
- if (this.checkboxRenderer) {
2316
- const renderer = typeof this.checkboxRenderer === `function` ? this.checkboxRenderer : this.checkboxRenderer.renderer;
2317
- render(renderer(checked), cb);
2318
- }
2319
- } else {
2320
- const input = cb.querySelector(`input.ixfx-list-checkbox`);
2321
- if (input) input.checked = checked;
2322
- }
2323
- }
2324
- }
2325
- /**
2326
- * Native checkbox cell: a span that stretches to the row's full cross-axis
2327
- * (so the entire column height is a click target) with a real `<input>`
2328
- * centred inside. The span owns the click; the input is purely visual and
2329
- * has `pointer-events:none` so a single click produces one toggle.
2330
- */
2331
- _createNativeCheckboxCell(el) {
2332
- const cell = document.createElement(`span`);
2333
- cell.className = `ixfx-list-checkbox ixfx-list-checkbox-native`;
2334
- cell.style.cssText = `display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;align-self:stretch;width:18px;margin:0 6px 0 0;cursor:pointer;pointer-events:auto`;
2335
- cell.tabIndex = -1;
2336
- cell.setAttribute(`role`, `checkbox`);
2337
- cell.setAttribute(`part`, `checkbox`);
2338
- cell.addEventListener(`click`, (e) => {
2339
- e.stopPropagation();
2340
- this.selection.toggle(el);
2341
- });
2342
- const input = document.createElement(`input`);
2343
- input.type = `checkbox`;
2344
- input.className = `ixfx-list-checkbox`;
2345
- input.style.cssText = `pointer-events:none;cursor:pointer;margin:0`;
2346
- cell.appendChild(input);
2347
- return cell;
2348
- }
2349
- /** Custom checkbox cell: same shape as the native cell; renderer content inside. */
2350
- _createCustomCheckbox(el, margin, width) {
2351
- const span = document.createElement(`span`);
2352
- span.className = `ixfx-list-checkbox ixfx-list-checkbox-custom`;
2353
- span.style.cssText = `display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;align-self:stretch;width:${width};margin:${margin};cursor:pointer;pointer-events:auto`;
2354
- span.tabIndex = -1;
2355
- span.setAttribute(`role`, `checkbox`);
2356
- span.setAttribute(`part`, `checkbox`);
2357
- span.addEventListener(`click`, (e) => {
2358
- e.stopPropagation();
2359
- this.selection.toggle(el);
2360
- });
2361
- return span;
2362
- }
2363
- _findListItem(path) {
2364
- for (const target of path) {
2365
- if (!(target instanceof Element)) continue;
2366
- if (target === this) break;
2367
- if (target.parentElement === this && target.matches(`li`)) return target;
2368
- }
2369
- }
2370
- _dispatch(name, detail) {
2371
- this.dispatchEvent(new CustomEvent(name, {
2372
- detail,
2373
- bubbles: true,
2374
- composed: true
2375
- }));
2376
- }
2377
- _openSearch() {
2378
- this._searchVisible = true;
2379
- this.updateComplete.then(() => this._searchInputEl?.focus());
2380
- }
2381
- _closeSearch() {
2382
- this._searchVisible = false;
2383
- this._searchQuery = ``;
2384
- this.#search?.clearQuery().then(() => this.#recomputeGeometry());
2385
- this.focus();
2386
- }
2387
- _activateItem(item) {
2388
- if (this.interactionMode !== `manual`) this.selection.replace(item);
2389
- this._dispatch(`list-activate`, { item });
1609
+ //#region src/data-table/data-table.ts
1610
+ let DataTableElement = class DataTableElement extends LitElement {
1611
+ constructor(..._args) {
1612
+ super(..._args);
1613
+ this.rows = [];
1614
+ this.tickleDelay = 400;
1615
+ this.layout = `auto`;
1616
+ this.narrowBreakpoint = 420;
1617
+ this._tickled = new TickledController(this, { getItems: () => this.rows.map((_, i) => ({
1618
+ id: i,
1619
+ enabled: true
1620
+ })) });
1621
+ this._stacked = false;
1622
+ this.#lastWidth = 0;
2390
1623
  }
2391
1624
  static {
2392
1625
  this.styles = [
2393
- themeFallbacks,
2394
- scrollFadeStyles(`x`),
1626
+ tickledStyles,
1627
+ tickledItemStyles,
2395
1628
  css`
2396
- :host {
2397
- display: block;
2398
- position: relative;
2399
- background: var(--detail-list-bg, var(--surface-3));
2400
- color: var(--detail-list-text, var(--surface-3-text));
2401
- border-radius: var(--detail-list-radius, var(--radius-s));
2402
- border: 1px solid var(--detail-list-border, var(--border));
2403
- width: 100%;
2404
- height: 100%;
2405
- overflow: hidden;
2406
- box-sizing: border-box;
2407
- outline: none;
2408
- --_detail-list-item-padding:
2409
- var(--ixfx-detail-list-item-padding, var(--detail-list-item-padding, 0 var(--space-xs, 3px)));
2410
- }
1629
+ :host {
1630
+ display: block;
1631
+ container-type: inline-size;
1632
+ }
2411
1633
 
2412
- :host(:focus-visible) {
2413
- outline: 2px solid var(--accent);
2414
- outline-offset: 2px;
2415
- }
1634
+ .properties {
1635
+ display: grid;
1636
+ grid-template-columns: var(--data-table-name-width, max-content)
1637
+ var(--data-table-value-width, minmax(0, 1fr));
1638
+ /* No column gap: the tickled highlight reads as one solid block.
1639
+ Separation comes from cell padding, which the highlight covers. */
1640
+ gap: 2px 0;
1641
+ }
2416
1642
 
2417
- .container {
2418
- position: relative;
2419
- width: 100%;
2420
- height: 100%;
2421
- }
1643
+ /* Widening: 2 then 3 property-pairs per row on roomy containers.
1644
+ Suppressed when stacked, or when locked to 'columns'. */
1645
+ @container (min-width: 650px) {
1646
+ :host(:not([layout='columns'])) .properties:not(.stacked) {
1647
+ grid-template-columns:
1648
+ var(--data-table-name-width, max-content) var(--data-table-value-width, minmax(0, 1fr))
1649
+ var(--data-table-name-width, max-content) var(--data-table-value-width, minmax(0, 1fr));
1650
+ }
1651
+ }
2422
1652
 
2423
- ul {
2424
- list-style: none;
2425
- margin: 0;
2426
- padding: 0;
2427
- /* keep the last column clear of the scrollbar / right edge */
2428
- padding-inline-end: calc(var(--detail-list-column-gap, 8px) + 4px);
2429
- width: 100%;
2430
- height: 100%;
2431
- display: flex;
2432
- flex-flow: column wrap;
2433
- align-content: flex-start;
2434
- column-gap: var(--detail-list-column-gap, 8px);
2435
- overflow-x: auto;
2436
- overflow-y: hidden;
2437
- box-sizing: border-box;
2438
- /* Scrollbar theming: consumers override these CSS variables to control
2439
- the native scrollbar (scrollbar-width, scrollbar-gutter, scrollbar-color). */
2440
- scrollbar-width: var(--detail-list-scrollbar-width, auto);
2441
- scrollbar-gutter: var(--detail-list-scrollbar-gutter, auto);
2442
- scrollbar-color: var(--detail-list-scrollbar-color, auto);
2443
- }
1653
+ @container (min-width: 1000px) {
1654
+ :host(:not([layout='columns'])) .properties:not(.stacked) {
1655
+ grid-template-columns:
1656
+ var(--data-table-name-width, max-content) var(--data-table-value-width, minmax(0, 1fr))
1657
+ var(--data-table-name-width, max-content) var(--data-table-value-width, minmax(0, 1fr))
1658
+ var(--data-table-name-width, max-content) var(--data-table-value-width, minmax(0, 1fr));
1659
+ }
1660
+ }
2444
1661
 
2445
- ::slotted([hidden]) {
2446
- display: none !important;
2447
- }
1662
+ /* Stacked (key above value). Threshold is JS-driven via 'narrowBreakpoint'.
1663
+ minmax(0, 1fr) — not 1fr — so a long value's min-content size can't
1664
+ blow the column out past the container; the value then ellipsises. */
1665
+ .properties.stacked {
1666
+ grid-template-columns: minmax(0, 1fr);
1667
+ }
2448
1668
 
2449
- ::slotted(li) {
2450
- box-sizing: border-box;
2451
- display: flex;
2452
- align-items: center;
2453
- flex: none;
2454
- width: var(--detail-list-column-width, 220px);
2455
- height: var(--detail-list-item-height, 28px);
2456
- padding: var(--_detail-list-item-padding);
2457
- cursor: pointer;
2458
- user-select: none;
2459
- transition: background var(--transition, 0.1s ease-out),
2460
- outline-color var(--transition, 0.1s ease-out);
2461
- outline: 1px solid transparent;
2462
- }
1669
+ /* A pair is its own subgrid so the whole row is one element:
1670
+ the tickled highlight can't split, and key/value share a baseline. */
1671
+ .pair {
1672
+ display: grid;
1673
+ grid-column: span 2;
1674
+ grid-template-columns: subgrid;
1675
+ align-items: var(--data-table-cell-align, baseline);
1676
+ cursor: pointer;
1677
+ }
2463
1678
 
2464
- /* In checked mode, only the checkbox column is clickable; the row body
2465
- gets a default cursor so it doesn't suggest otherwise. */
2466
- :host([interaction-mode="checked"]) ::slotted(li) {
2467
- cursor: default;
2468
- }
1679
+ .pair[data-full] {
1680
+ grid-column: 1 / -1;
1681
+ }
2469
1682
 
2470
- ::slotted([data-tickled]) {
2471
- background: var(--item-bg-tickled);
2472
- outline: var(--item-border-tickled);
2473
- }
1683
+ .pair[data-full] > .value {
1684
+ grid-column: 2 / -1;
1685
+ }
2474
1686
 
2475
- ::slotted([data-selected]) {
2476
- background: var(--item-bg-selected);
2477
- color: var(--item-text-selected);
2478
- outline: 1px solid transparent;
2479
- }
1687
+ .properties.stacked .pair {
1688
+ display: block;
1689
+ grid-column: 1 / -1;
1690
+ /* Let the block shrink below its content so .value-text can clip. */
1691
+ min-width: 0;
1692
+ }
2480
1693
 
2481
- ::slotted([data-selected][data-tickled]) {
2482
- background: var(--item-bg-selected-tickled);
2483
- outline: 2px dashed var(--surface-3);
2484
- outline-offset: -2px;
2485
- }
1694
+ .properties.stacked .pair:not(:last-child) {
1695
+ margin-bottom: var(--space-s);
1696
+ }
2486
1697
 
2487
- /* checked mode: the injected checkbox communicates selection suppress bg */
2488
- :host([interaction-mode="checked"]) ::slotted([data-selected]) {
2489
- background: transparent;
2490
- color: inherit;
2491
- outline: 1px solid transparent;
2492
- }
1698
+ /* Stacked keys get their own line, so a long label must wrap rather than
1699
+ run off the edge (nowrap is only wanted for the max-content column). */
1700
+ .properties.stacked .key {
1701
+ white-space: normal;
1702
+ overflow-wrap: break-word;
1703
+ }
2493
1704
 
2494
- :host([interaction-mode="checked"]) ::slotted([data-selected][data-tickled]) {
2495
- background: var(--item-bg-tickled);
2496
- color: inherit;
2497
- outline: var(--item-border-tickled);
2498
- }
1705
+ .key,
1706
+ .value {
1707
+ min-width: 0;
1708
+ /* Row rhythm comes from this padding (tunable), not line-height. */
1709
+ padding: var(--data-table-row-padding, 0.35em) 0;
1710
+ }
2499
1711
 
2500
- .loading {
2501
- position: absolute;
2502
- bottom: 8px;
2503
- right: 8px;
2504
- z-index: 10;
2505
- background: var(--surface-4, #2a2a2a);
2506
- border: 1px solid var(--border, #444);
2507
- border-radius: var(--radius-m, 4px);
2508
- padding: 2px 8px;
2509
- font-size: var(--text-s, 11px);
2510
- color: var(--surface-muted-text, #888);
2511
- }
1712
+ .key {
1713
+ padding-right: var(--space-m);
1714
+ opacity: 0.6;
1715
+ white-space: nowrap;
1716
+ text-align: var(--data-table-name-align, left);
1717
+ font-size: 10px;
1718
+ text-transform: uppercase;
1719
+ letter-spacing: 0.05em;
1720
+ /* Uppercase keys have no descenders, so both edges can be trimmed to
1721
+ the glyphs for tight baseline / centre alignment. */
1722
+ text-box: trim-both cap alphabetic;
1723
+ }
2512
1724
 
2513
- .loading[hidden] {
2514
- display: none;
2515
- }
1725
+ .value {
1726
+ position: relative;
1727
+ padding-right: var(--space-l);
1728
+ font: 12px ui-monospace, SFMono-Regular, Menlo, monospace;
1729
+ }
2516
1730
 
2517
- .search-overlay {
2518
- position: absolute;
2519
- top: 8px;
2520
- right: 8px;
2521
- z-index: 10;
2522
- display: flex;
2523
- align-items: center;
2524
- gap: 4px;
2525
- background: var(--surface-4, #2a2a2a);
2526
- border: 1px solid var(--border, #444);
2527
- border-radius: var(--radius-m, 4px);
2528
- padding: 4px 6px;
2529
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
2530
- }
1731
+ /* The value text is clipped here — never on .value itself — so a long
1732
+ value stays within its cell even while tickled, and the detail
1733
+ popover (a child of .value) can still escape the cell. */
1734
+ .value-text {
1735
+ display: block;
1736
+ overflow: hidden;
1737
+ text-overflow: ellipsis;
1738
+ white-space: nowrap;
1739
+ /* No text-box trim here: the natural line box gives descenders the room
1740
+ overflow:hidden would otherwise clip. Baseline alignment of key and
1741
+ value comes from align-items on .pair, which needs no trimming. */
1742
+ }
2531
1743
 
2532
- .search-overlay[hidden] {
2533
- display: none;
2534
- }
1744
+ .pair.tickled {
1745
+ background: var(--item-bg-tickled, color-mix(in oklch, var(--surface-2), var(--accent) 10%));
1746
+ }
2535
1747
 
2536
- .search-input {
2537
- background: transparent;
2538
- border: none;
2539
- outline: none;
2540
- color: inherit;
2541
- font-size: var(--text-m, 0.875em);
2542
- min-width: 140px;
2543
- }
1748
+ /* Lift the row whose detail is open above the rows it slides over. */
1749
+ .pair.show-detail {
1750
+ position: relative;
1751
+ z-index: 1;
1752
+ }
2544
1753
 
2545
- .search-close {
2546
- background: transparent;
2547
- border: none;
2548
- color: var(--surface-muted-text, #888);
2549
- cursor: pointer;
2550
- font-size: 1.1em;
2551
- line-height: 1;
2552
- padding: 0 2px;
2553
- }
1754
+ .detail {
1755
+ position: absolute;
1756
+ z-index: 2;
1757
+ /* Flush with the bottom of the value (no gap) so the pointer can travel
1758
+ from value to panel without crossing a dead zone that would dismiss it.
1759
+ Left-aligned with the value; only as wide as its content, capped at
1760
+ the table width. */
1761
+ top: 100%;
1762
+ left: 0;
1763
+ right: auto;
1764
+ width: max-content;
1765
+ max-width: 100cqi;
1766
+ padding: var(--space-xs);
1767
+ background: var(--surface-2);
1768
+ visibility: hidden;
1769
+ transform: translateY(-4px);
1770
+ pointer-events: none;
1771
+ transition: transform var(--transition);
1772
+ }
2554
1773
 
2555
- .search-close:hover {
2556
- color: inherit;
2557
- }
2558
- `
1774
+ .pair.show-detail .detail {
1775
+ visibility: visible;
1776
+ transform: translateY(0);
1777
+ pointer-events: auto;
1778
+ }
1779
+ `
2559
1780
  ];
2560
1781
  }
1782
+ #detailTimer;
1783
+ #resizeObserver;
1784
+ #lastWidth;
1785
+ connectedCallback() {
1786
+ super.connectedCallback();
1787
+ this.#resizeObserver = new ResizeObserver((entries) => {
1788
+ const entry = entries[0];
1789
+ if (!entry) return;
1790
+ this.#lastWidth = entry.borderBoxSize?.[0]?.inlineSize ?? entry.contentRect.width;
1791
+ this.#updateStacked();
1792
+ });
1793
+ this.#resizeObserver.observe(this);
1794
+ }
1795
+ disconnectedCallback() {
1796
+ super.disconnectedCallback();
1797
+ clearTimeout(this.#detailTimer);
1798
+ this.#resizeObserver?.disconnect();
1799
+ this.#resizeObserver = void 0;
1800
+ }
1801
+ willUpdate(changed) {
1802
+ if (changed.has(`layout`) || changed.has(`narrowBreakpoint`)) this.#updateStacked();
1803
+ }
1804
+ #updateStacked() {
1805
+ const stacked = this.layout === `rows` ? true : this.layout === `columns` ? false : this.#lastWidth > 0 && this.#lastWidth <= this.narrowBreakpoint;
1806
+ if (stacked !== this._stacked) this._stacked = stacked;
1807
+ }
1808
+ #handlePointerEnter(i) {
1809
+ this._tickled.handlePointerEnter(i);
1810
+ clearTimeout(this.#detailTimer);
1811
+ if (this._detailIndex !== void 0) this._detailIndex = void 0;
1812
+ this.#detailTimer = setTimeout(() => {
1813
+ if (this._tickled.isTickled(i)) this._detailIndex = i;
1814
+ }, this.tickleDelay);
1815
+ }
1816
+ #handlePointerLeave(e) {
1817
+ clearTimeout(this.#detailTimer);
1818
+ this._detailIndex = void 0;
1819
+ this._tickled.handlePointerLeave(e);
1820
+ }
1821
+ #select(i) {
1822
+ clearTimeout(this.#detailTimer);
1823
+ this.selectedIndex = this.selectedIndex === i ? void 0 : i;
1824
+ this._tickled.setTickled(i);
1825
+ this._detailIndex = i;
1826
+ this.dispatchEvent(new CustomEvent(`data-table-row-click`, {
1827
+ bubbles: true,
1828
+ composed: true,
1829
+ detail: {
1830
+ row: this.rows[i],
1831
+ index: i,
1832
+ selected: this.selectedIndex === i
1833
+ }
1834
+ }));
1835
+ }
2561
1836
  render() {
2562
- const loading = this.#data.loading;
2563
- const progress = this.#data.progress;
2564
- return html`
2565
- <div class="container">
2566
- <ul part="list" class="scroll-fade-x">
2567
- <slot></slot>
2568
- </ul>
2569
- <div class="loading" part="loading" ?hidden=${!loading}>
2570
- ${loading ? progress !== void 0 ? `${Math.round(progress * 100)}%` : `loading…` : nothing}
2571
- </div>
2572
- <div
2573
- class="search-overlay"
2574
- part="search-overlay"
2575
- ?hidden=${!this._searchVisible}
2576
- >
2577
- <input
2578
- type="text"
2579
- class="search-input"
2580
- placeholder="Search…"
2581
- .value=${this._searchQuery}
2582
- @input=${this._onSearchInput}
2583
- @keydown=${this._onSearchKeydown}
2584
- />
2585
- <button class="search-close" @click=${this._closeSearch} title="Close search (Esc)">×</button>
2586
- </div>
2587
- </div>
2588
- `;
1837
+ return html`<div
1838
+ class="properties ${this._stacked ? `stacked` : ``}"
1839
+ @pointerleave=${(e) => this.#handlePointerLeave(e)}
1840
+ >
1841
+ ${this.rows.map((r, i) => html`<div
1842
+ class="pair ${this._tickled.isTickled(i) ? `tickled` : ``} ${this._detailIndex === i ? `show-detail` : ``}"
1843
+ ?data-full=${r.fullWidth}
1844
+ @pointerenter=${() => this.#handlePointerEnter(i)}
1845
+ @click=${() => this.#select(i)}
1846
+ ><div class="key">${r.label}</div><div class="value"><span class="value-text">${typeof r.value === `boolean` ? r.value ? `Yes` : `No` : r.value}</span>${r.detail === void 0 ? nothing : html`<div class="detail" part="detail">${r.detail}</div>`}</div></div>`)}</div>`;
2589
1847
  }
2590
1848
  };
2591
1849
  __decorate([property({
2592
- type: String,
2593
- attribute: `selection-mode`
2594
- })], DetailListElement.prototype, "selectionMode", void 0);
2595
- __decorate([property({
2596
- type: String,
2597
- attribute: `interaction-mode`
2598
- })], DetailListElement.prototype, "interactionMode", void 0);
2599
- __decorate([property({ type: String })], DetailListElement.prototype, "sort", void 0);
2600
- __decorate([property({
2601
- type: String,
2602
- attribute: `sort-by`
2603
- })], DetailListElement.prototype, "sortBy", void 0);
2604
- __decorate([property({ attribute: false })], DetailListElement.prototype, "sortComparator", void 0);
2605
- __decorate([property({ attribute: false })], DetailListElement.prototype, "items", void 0);
2606
- __decorate([property({ attribute: false })], DetailListElement.prototype, "provider", void 0);
1850
+ type: Array,
1851
+ attribute: false
1852
+ })], DataTableElement.prototype, "rows", void 0);
2607
1853
  __decorate([property({
2608
- type: String,
2609
- attribute: `display-property`
2610
- })], DetailListElement.prototype, "displayProperty", void 0);
2611
- __decorate([property({ attribute: false })], DetailListElement.prototype, "formatter", void 0);
1854
+ type: Number,
1855
+ attribute: false
1856
+ })], DataTableElement.prototype, "selectedIndex", void 0);
2612
1857
  __decorate([property({
2613
- type: String,
2614
- attribute: `icon-property`
2615
- })], DetailListElement.prototype, "iconProperty", void 0);
1858
+ type: Number,
1859
+ attribute: `tickle-delay`
1860
+ })], DataTableElement.prototype, "tickleDelay", void 0);
2616
1861
  __decorate([property({
2617
1862
  type: String,
2618
- attribute: `key-property`
2619
- })], DetailListElement.prototype, "keyProperty", void 0);
2620
- __decorate([property({ attribute: false })], DetailListElement.prototype, "filterPredicate", void 0);
1863
+ reflect: true
1864
+ })], DataTableElement.prototype, "layout", void 0);
2621
1865
  __decorate([property({
2622
- type: String,
2623
- attribute: `item-wrap`
2624
- })], DetailListElement.prototype, "itemWrap", void 0);
2625
- __decorate([property({ attribute: false })], DetailListElement.prototype, "checkboxRenderer", void 0);
2626
- __decorate([state()], DetailListElement.prototype, "_searchVisible", void 0);
2627
- __decorate([state()], DetailListElement.prototype, "_searchQuery", void 0);
2628
- __decorate([query(`.search-input`)], DetailListElement.prototype, "_searchInputEl", void 0);
2629
- __decorate([query(`ul`)], DetailListElement.prototype, "_listEl", void 0);
2630
- DetailListElement = __decorate([safeCustomElement(`ixfx-detail-list`)], DetailListElement);
1866
+ type: Number,
1867
+ attribute: `narrow-breakpoint`
1868
+ })], DataTableElement.prototype, "narrowBreakpoint", void 0);
1869
+ __decorate([state()], DataTableElement.prototype, "_tickled", void 0);
1870
+ __decorate([state()], DataTableElement.prototype, "_detailIndex", void 0);
1871
+ __decorate([state()], DataTableElement.prototype, "_stacked", void 0);
1872
+ DataTableElement = __decorate([safeCustomElement(`ixfx-data-table`)], DataTableElement);
2631
1873
  //#endregion
2632
1874
  //#region src/dock/dock-drag.ts
2633
1875
  let activeDrag = null;
@@ -5918,6 +5160,136 @@ function dismiss(id) {
5918
5160
  syncRenderer();
5919
5161
  }
5920
5162
  //#endregion
5163
+ //#region src/pan-text/pan-text.ts
5164
+ const SPEED = 45;
5165
+ const END_PADDING = 4;
5166
+ const START_DELAY = 300;
5167
+ const PAN_EASING = `cubic-bezier(0.25, 0.46, 0.45, 0.94)`;
5168
+ let PanTextElement = class PanTextElement extends LitElement {
5169
+ constructor(..._args) {
5170
+ super(..._args);
5171
+ this.direction = `end`;
5172
+ this.disabled = false;
5173
+ this._isOverflowing = false;
5174
+ this._isHovering = false;
5175
+ this._overflow = 0;
5176
+ this._resizeObserver = null;
5177
+ this._onMouseEnter = () => {
5178
+ this._isHovering = true;
5179
+ };
5180
+ this._onMouseLeave = () => {
5181
+ this._isHovering = false;
5182
+ };
5183
+ this._measure = () => {
5184
+ if (this._isHovering) return;
5185
+ const el = this._textEl;
5186
+ if (!el) return;
5187
+ this._overflow = Math.max(0, el.scrollWidth - el.clientWidth);
5188
+ const overflowing = this._overflow > 0;
5189
+ this.classList.toggle(`is-overflowing`, overflowing);
5190
+ if (overflowing !== this._isOverflowing) this._isOverflowing = overflowing;
5191
+ };
5192
+ }
5193
+ connectedCallback() {
5194
+ super.connectedCallback();
5195
+ this._resizeObserver = new ResizeObserver(() => this._measure());
5196
+ this.addEventListener(`mouseenter`, this._onMouseEnter);
5197
+ this.addEventListener(`mouseleave`, this._onMouseLeave);
5198
+ }
5199
+ disconnectedCallback() {
5200
+ super.disconnectedCallback();
5201
+ this._resizeObserver?.disconnect();
5202
+ this._resizeObserver = null;
5203
+ this.removeEventListener(`mouseenter`, this._onMouseEnter);
5204
+ this.removeEventListener(`mouseleave`, this._onMouseLeave);
5205
+ }
5206
+ firstUpdated() {
5207
+ this._measure();
5208
+ this._resizeObserver?.observe(this);
5209
+ document.fonts?.ready.then(() => this._measure());
5210
+ }
5211
+ updated() {
5212
+ this._measure();
5213
+ }
5214
+ _textStyle() {
5215
+ if (!this._isOverflowing) return ``;
5216
+ const distance = this._overflow + END_PADDING;
5217
+ const duration = Math.max(1.5, distance / SPEED);
5218
+ const revealEnd = this.direction === `end`;
5219
+ const restX = revealEnd ? 0 : -distance;
5220
+ const hoverX = revealEnd ? -distance : 0;
5221
+ if (this._isHovering) return [
5222
+ `transform: translateX(${hoverX}px)`,
5223
+ `transition: transform ${duration}s ${PAN_EASING} ${START_DELAY}ms`,
5224
+ `overflow: visible`,
5225
+ `text-overflow: clip`
5226
+ ].join(`; `);
5227
+ return [
5228
+ `transform: translateX(${restX}px)`,
5229
+ `transition: transform 400ms ${PAN_EASING}`,
5230
+ revealEnd ? `` : `overflow: visible; text-overflow: clip`
5231
+ ].filter(Boolean).join(`; `);
5232
+ }
5233
+ render() {
5234
+ const textClasses = {
5235
+ "is-overflowing": this._isOverflowing,
5236
+ "is-hovering": this._isHovering
5237
+ };
5238
+ return html`
5239
+ <span class="text ${classMap(textClasses)}" part="text" style=${this._textStyle()}>
5240
+ <slot @slotchange=${this._measure}></slot>
5241
+ </span>
5242
+ `;
5243
+ }
5244
+ static {
5245
+ this.styles = [themeFallbacks, css`
5246
+ /* Renders like plain inline text: no colours, border, padding or margin
5247
+ of its own — it only adds the clipping needed to pan on hover. */
5248
+ :host {
5249
+ display: inline-block;
5250
+ overflow: hidden;
5251
+ white-space: nowrap;
5252
+ max-width: 100%;
5253
+ box-sizing: border-box;
5254
+ font: inherit;
5255
+ color: inherit;
5256
+ }
5257
+
5258
+ :host([disabled]) {
5259
+ opacity: var(--disabled-opacity, 0.5);
5260
+ pointer-events: none;
5261
+ }
5262
+
5263
+ .text {
5264
+ display: block;
5265
+ overflow: hidden;
5266
+ text-overflow: ellipsis;
5267
+ white-space: nowrap;
5268
+ will-change: transform;
5269
+ }
5270
+
5271
+ @media (prefers-reduced-motion: reduce) {
5272
+ .text {
5273
+ transition: none !important;
5274
+ transform: none !important;
5275
+ }
5276
+ }
5277
+ `];
5278
+ }
5279
+ };
5280
+ __decorate([property({
5281
+ type: String,
5282
+ attribute: `direction`
5283
+ })], PanTextElement.prototype, "direction", void 0);
5284
+ __decorate([property({
5285
+ type: Boolean,
5286
+ attribute: `disabled`
5287
+ })], PanTextElement.prototype, "disabled", void 0);
5288
+ __decorate([state()], PanTextElement.prototype, "_isOverflowing", void 0);
5289
+ __decorate([state()], PanTextElement.prototype, "_isHovering", void 0);
5290
+ __decorate([query(`.text`)], PanTextElement.prototype, "_textEl", void 0);
5291
+ PanTextElement = __decorate([safeCustomElement(`ixfx-pan-text`)], PanTextElement);
5292
+ //#endregion
5921
5293
  //#region src/panel/panel-group.ts
5922
5294
  let PanelGroupElement = class PanelGroupElement extends LitElement {
5923
5295
  constructor(..._args) {
@@ -8608,6 +7980,6 @@ function init() {
8608
7980
  console.log(`Init`);
8609
7981
  }
8610
7982
  //#endregion
8611
- export { AcTextElement, AcTokenElement, ButtonElement, Checkbox, ColourPicker, ColourPickerPopup, CrumbNavigationElement, CrumbPathController, CrumbPathElement, DataController, DataDisplayComponent, DataGridElement, DetailListElement, DockHostElement, DockPanelElement, EditableLabel, EditableNumber, FormElement, FormGroupElement, FormPartElement, GroupedItemListerElement, ICON_CARET_RIGHT, ICON_CHECK, ICON_CHEVRON_DOWN, ICON_CLOSE, IncrSearchTreeController as IncrSearchMillerController, IncrSearchTreeController, IxfxHexEditorElement, IxfxIconElement, IxfxTimelineElement, LabelledRadialInput, LabelledRangeInput, LabelledSliderInput, LedElement, ListSelectionController, menu_exports as Menu, MillerBaseElement, MillerListElement, MillerTreeController, NarrowedTextElement, NotificationManager, NotificationPillElement, PanelElement, PanelGroupElement, PlotDataSeries, PlotHistogram, PlotMultiAxis, PlotSingleAxis, PlotXyAxis, PolarPad, RadialInputElement, RadioGroup, RangeElement, RangeInput, RangeMultiElement, RatingElement, SelectHorizElement, SliderInputElement, SlidingNumberElement, SnackbarElement, SnapContainer, SplitButton, SplitLayoutElement, SplitPanelElement, SwipeElement, TabController, TabListElement, TabListItemElement, TabPanelElement, TabPanelsElement, TextShimmerElement, TickledController, TimelineController, TimelineTrackTabElement, TitlebarElement, ToolbarElement, ToolbarGroupElement, TooltipElement, TransitoryLabelElement, TreeBaseElement, TreeController, TreeDataModel, TreeKeyboardNavController, TreeListElement, TwoSplitLayoutElement, VerticalListElement, XyPad, addPanelToStack, applyAgeModification, applyHighlights, applySnapshot, attachTimelineInteractions, buildHighlightTargets, buildModel, clearHighlights, collectContainerNames, collectDataResult, collectPanelIds, collectStackPaths, computeColour, computeLeafGeoms, createAcCompletionProvider, createElementIncrSearch, createEmptyLayout, createEmptyStack, createFuzzySearch, createIncrSearch, createPillRenderer, createSplit, createStack, createTreeListAdapter, createVerticalListAdapter, distribute, extractStack, findNode, findParentOf, findStackOf, findStackPathByName, getActiveDragPanelId, getIcon, getIconEntry, getIconFont, getLeafNeighbours, hasIcon, iconBus, init, insertBeside, isDragging, isMultiMode, isSplit, isStack, monitorIndeterminate, moveLeafNodeToEdge, moveLeafStep, normalise, normalize, parseColourScaleAttr, parseLayout, pointerOverAnyHost, readBaseColour, registerFontIcon, registerIcon, removePanelFromStack, resolveCssVars, resolveGrid, resolveLeafPath, resolveTracks, serializeLayout, setActive, setIconFont, svgToDataUri, swapLeaves, tickledItemStyles, tickledStyles, timelineStyles, toSnapshot, walk, wrapRoot };
7983
+ export { AcTextElement, AcTokenElement, ButtonElement, Checkbox, ColourPicker, ColourPickerPopup, CrumbNavigationElement, CrumbPathController, CrumbPathElement, DataController, DataDisplayComponent, DataGridElement, DataTableElement, DetailListElement, DockHostElement, DockPanelElement, EditableLabel, EditableNumber, FormElement, FormGroupElement, FormPartElement, GridListElement, GroupedItemListerElement, ICON_CARET_RIGHT, ICON_CHECK, ICON_CHEVRON_DOWN, ICON_CLOSE, IncrSearchTreeController as IncrSearchMillerController, IncrSearchTreeController, IxfxHexEditorElement, IxfxIconElement, IxfxTimelineElement, KeyedElementIndex, LIST_ITEM_MOTION_MS, LIST_SEARCH_COMMAND_IDS, LIST_SEARCH_MODES, LabelledRadialInput, LabelledRangeInput, LabelledSliderInput, LedElement, ListSearchController, ListSelectionController, menu_exports as Menu, MillerBaseElement, MillerListElement, MillerTreeController, NarrowedTextElement, NotificationManager, NotificationPillElement, PanTextElement, PanelElement, PanelGroupElement, PlotDataSeries, PlotHistogram, PlotMultiAxis, PlotSingleAxis, PlotXyAxis, PolarPad, RadialInputElement, RadioGroup, RangeElement, RangeInput, RangeMultiElement, RatingElement, SelectHorizElement, SliderInputElement, SlidingNumberElement, SnackbarElement, SnapContainer, SplitButton, SplitLayoutElement, SplitPanelElement, SwipeElement, TabController, TabListElement, TabListItemElement, TabPanelElement, TabPanelsElement, TextShimmerElement, TickledController, TimelineController, TimelineTrackTabElement, TitlebarElement, ToolbarElement, ToolbarGroupElement, TooltipElement, TransitoryLabelElement, TreeBaseElement, TreeController, TreeDataModel, TreeKeyboardNavController, TreeListElement, TwoSplitLayoutElement, VerticalListElement, XyPad, addPanelToStack, animateListItemIn, animateListItemOut, applyAgeModification, applyHighlights, applySnapshot, attachTimelineInteractions, buildHighlightTargets, buildModel, clearHighlights, collectContainerNames, collectDataResult, collectPanelIds, collectStackPaths, computeColour, computeLeafGeoms, createAcCompletionProvider, createElementIncrSearch, createElementListSearchAdapter, createEmptyLayout, createEmptyStack, createFuzzySearch, createIncrSearch, createPillRenderer, createSplit, createStack, createTreeListAdapter, createTreeListSearchAdapter, createVerticalListAdapter, distribute, extractStack, findNode, findParentOf, findStackOf, findStackPathByName, getActiveDragPanelId, getIcon, getIconEntry, getIconFont, getLeafNeighbours, hasIcon, iconBus, init, insertBeside, isDragging, isMultiMode, isSplit, isStack, keyForElement, listItemFadeKeyframes, listSearchStyles, monitorIndeterminate, moveLeafNodeToEdge, moveLeafStep, normalise, normalize, parseColourScaleAttr, parseLayout, pointerOverAnyHost, prefersReducedMotion, readBaseColour, registerFontIcon, registerIcon, registerListSearchCommands, removePanelFromStack, resolveCssVars, resolveGrid, resolveLeafPath, resolveTracks, serializeLayout, setActive, setIconFont, svgToDataUri, swapLeaves, tickledItemStyles, tickledStyles, timelineStyles, toSnapshot, walk, wrapRoot };
8612
7984
 
8613
7985
  //# sourceMappingURL=index.js.map