@ixfx/components 0.7.3 → 0.7.4

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 (121) hide show
  1. package/bundle/index.d.ts +727 -256
  2. package/bundle/index.d.ts.map +1 -1
  3. package/bundle/index.js +2046 -35
  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 +1 -1
  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 +1 -1
  15. package/dist/crumbs.js +3 -3
  16. package/dist/{data-grid-Db4QpnII.d.ts → data-grid-BddUjAti.d.ts} +34 -2
  17. package/dist/data-grid-BddUjAti.d.ts.map +1 -0
  18. package/dist/data-grid.d.ts +1 -1
  19. package/dist/data-grid.js +180 -7
  20. package/dist/data-grid.js.map +1 -1
  21. package/dist/detail-list-Br0WAnry.d.ts +167 -0
  22. package/dist/detail-list-Br0WAnry.d.ts.map +1 -0
  23. package/dist/detail-list.d.ts +3 -0
  24. package/dist/detail-list.js +1115 -0
  25. package/dist/detail-list.js.map +1 -0
  26. package/dist/grid-list-B_orPsot.d.ts +304 -0
  27. package/dist/grid-list-B_orPsot.d.ts.map +1 -0
  28. package/dist/grid-list.d.ts +3 -0
  29. package/dist/grid-list.js +933 -0
  30. package/dist/grid-list.js.map +1 -0
  31. package/dist/{grouped-item-lister-BWlC9S4e.d.ts → grouped-item-lister-fdZHVxFM.d.ts} +3 -3
  32. package/dist/{grouped-item-lister-BWlC9S4e.d.ts.map → grouped-item-lister-fdZHVxFM.d.ts.map} +1 -1
  33. package/dist/grouped-item-lister.d.ts +1 -1
  34. package/dist/{icon-DR_3rSyZ.d.ts → icon-CDGprSdE.d.ts} +2 -2
  35. package/dist/icon-CDGprSdE.d.ts.map +1 -0
  36. package/dist/icons.d.ts +2 -2
  37. package/dist/{index-C59-JEPl.d.ts → index-9AhaHIT2.d.ts} +3 -3
  38. package/dist/{index-C59-JEPl.d.ts.map → index-9AhaHIT2.d.ts.map} +1 -1
  39. package/dist/{index-DsjpDksO.d.ts → index-CS9yM8L-.d.ts} +2 -2
  40. package/dist/{index-DsjpDksO.d.ts.map → index-CS9yM8L-.d.ts.map} +1 -1
  41. package/dist/{index-BUB5SICW.d.ts → index-CoqtMSd9.d.ts} +2 -2
  42. package/dist/{index-BUB5SICW.d.ts.map → index-CoqtMSd9.d.ts.map} +1 -1
  43. package/dist/{index-CJ5fshD3.d.ts → index-UvnoZUAo.d.ts} +2 -2
  44. package/dist/{index-CJ5fshD3.d.ts.map → index-UvnoZUAo.d.ts.map} +1 -1
  45. package/dist/{index-BOUaDIW-.d.ts → index-tq6f8007.d.ts} +2 -2
  46. package/dist/{index-BOUaDIW-.d.ts.map → index-tq6f8007.d.ts.map} +1 -1
  47. package/dist/index.d.ts +185 -240
  48. package/dist/index.d.ts.map +1 -1
  49. package/dist/index.js +375 -1002
  50. package/dist/index.js.map +1 -1
  51. package/dist/interaction-Cr13azbM.js +3 -0
  52. package/dist/{labelled-input-base-Dp_9KP0G.d.ts → labelled-input-base-DiTme1JO.d.ts} +2 -2
  53. package/dist/{labelled-input-base-Dp_9KP0G.d.ts.map → labelled-input-base-DiTme1JO.d.ts.map} +1 -1
  54. package/dist/labelled-radial-input.d.ts +2 -2
  55. package/dist/labelled-radial-input.d.ts.map +1 -1
  56. package/dist/labelled-range-input.d.ts +1 -1
  57. package/dist/labelled-slider-input.d.ts +1 -1
  58. package/dist/list-item-animation-DuiwgN3l.js +112 -0
  59. package/dist/list-item-animation-DuiwgN3l.js.map +1 -0
  60. package/dist/list-selection-controller-CUo-Hhcm.js +338 -0
  61. package/dist/list-selection-controller-CUo-Hhcm.js.map +1 -0
  62. package/dist/{list-selection-types-DSuRNWpx.d.ts → list-selection-types-B5ioL-dG.d.ts} +6 -1
  63. package/dist/list-selection-types-B5ioL-dG.d.ts.map +1 -0
  64. package/dist/{menu-DMzjkVnK.js → menu-Bg90Qr0B.js} +3 -3
  65. package/dist/{menu-DMzjkVnK.js.map → menu-Bg90Qr0B.js.map} +1 -1
  66. package/dist/{menu-item-D2S4i8u1.js → menu-item-Bx-Fs7jq.js} +3 -3
  67. package/dist/{menu-item-D2S4i8u1.js.map → menu-item-Bx-Fs7jq.js.map} +1 -1
  68. package/dist/{menu-item-ZmkXNjek.d.ts → menu-item-CZi9vy5O.d.ts} +2 -2
  69. package/dist/{menu-item-ZmkXNjek.d.ts.map → menu-item-CZi9vy5O.d.ts.map} +1 -1
  70. package/dist/menu.d.ts +2 -2
  71. package/dist/menu.js +2 -2
  72. package/dist/miller.d.ts +1 -1
  73. package/dist/miller.js +2 -2
  74. package/dist/narrowed-text.js +2 -2
  75. package/dist/panel.d.ts +1 -1
  76. package/dist/plots.js +1 -1
  77. package/dist/select-horiz.js +2 -2
  78. package/dist/{split-layout-C5SYbDrq.d.ts → split-layout-JK3xWVX2.d.ts} +2 -2
  79. package/dist/{split-layout-C5SYbDrq.d.ts.map → split-layout-JK3xWVX2.d.ts.map} +1 -1
  80. package/dist/split-layout.d.ts +1 -1
  81. package/dist/{tab-list-DbMIL520.d.ts → tab-list-CGyPt07Z.d.ts} +2 -2
  82. package/dist/{tab-list-DbMIL520.d.ts.map → tab-list-CGyPt07Z.d.ts.map} +1 -1
  83. package/dist/tabs.d.ts +1 -1
  84. package/dist/{tickled-controller-Ds1DMyG7.d.ts → tickled-controller-Bn3YdQsM.d.ts} +12 -1
  85. package/dist/{tickled-controller-Ds1DMyG7.d.ts.map → tickled-controller-Bn3YdQsM.d.ts.map} +1 -1
  86. package/dist/{tickled-controller-h9GmJ_bU.js → tickled-controller-C66DgJHl.js} +16 -1
  87. package/dist/tickled-controller-C66DgJHl.js.map +1 -0
  88. package/dist/{titlebar-B23MrIbI.d.ts → titlebar-Bj5pfdw_.d.ts} +2 -2
  89. package/dist/{titlebar-B23MrIbI.d.ts.map → titlebar-Bj5pfdw_.d.ts.map} +1 -1
  90. package/dist/titlebar.d.ts +1 -1
  91. package/dist/{tree-Ch4W8qXQ.js → tree-B74zdI60.js} +116 -17
  92. package/dist/tree-B74zdI60.js.map +1 -0
  93. package/dist/tree.d.ts +6 -3
  94. package/dist/tree.d.ts.map +1 -1
  95. package/dist/tree.js +1 -1
  96. package/dist/vertical-list-CQSEVeOT.d.ts +140 -0
  97. package/dist/vertical-list-CQSEVeOT.d.ts.map +1 -0
  98. package/dist/vertical-list.d.ts +3 -121
  99. package/dist/vertical-list.js +710 -1
  100. package/dist/vertical-list.js.map +1 -0
  101. package/dist/{xy-axis-DWizs0bt.js → xy-axis-VrX8dmvS.js} +2 -2
  102. package/dist/{xy-axis-DWizs0bt.js.map → xy-axis-VrX8dmvS.js.map} +1 -1
  103. package/docs-user/README.md +1 -0
  104. package/docs-user/data-grid.md +51 -2
  105. package/docs-user/detail-list.md +38 -0
  106. package/docs-user/grid-list.md +274 -0
  107. package/docs-user/index.json +7 -2
  108. package/docs-user/llms.txt +1 -0
  109. package/docs-user/tree.md +13 -0
  110. package/docs-user/vertical-list.md +51 -1
  111. package/llms.txt +1 -0
  112. package/package.json +17 -2
  113. package/dist/data-grid-Db4QpnII.d.ts.map +0 -1
  114. package/dist/icon-DR_3rSyZ.d.ts.map +0 -1
  115. package/dist/interaction-D5XdKzbR.js +0 -2
  116. package/dist/list-selection-types-DSuRNWpx.d.ts.map +0 -1
  117. package/dist/tickled-controller-h9GmJ_bU.js.map +0 -1
  118. package/dist/tree-Ch4W8qXQ.js.map +0 -1
  119. package/dist/vertical-list-B4w_ksJk.js +0 -814
  120. package/dist/vertical-list-B4w_ksJk.js.map +0 -1
  121. 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";
@@ -15,29 +15,33 @@ import { i as ICON_CLOSE, n as ICON_CHECK, r as ICON_CHEVRON_DOWN, t as ICON_CAR
15
15
  import "./icons.js";
16
16
  import { t as prominenceStyles } from "./prominence-DlFShic3.js";
17
17
  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";
18
+ import { i as keyForElement, n as isMultiMode, r as KeyedElementIndex, t as ListSelectionController } from "./list-selection-controller-CUo-Hhcm.js";
19
+ 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";
20
+ import "./interaction-Cr13azbM.js";
21
+ import "./menu-item-Bx-Fs7jq.js";
21
22
  import { Checkbox } from "./checkbox.js";
22
23
  import { n as ColourPicker, t as ColourPickerPopup } from "./colour-picker-Ddsk-7E_.js";
23
24
  import { LabelledRangeInput } from "./labelled-range-input.js";
24
25
  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";
26
+ import { i as TreeDataModel, n as TreeListElement, r as TreeBaseElement, t as TreeController } from "./tree-B74zdI60.js";
26
27
  import { t as scrollFadeStyles } from "./scroll-fade-CAs1EabA.js";
27
28
  import { NarrowedTextElement } from "./narrowed-text.js";
28
29
  import { CrumbNavigationElement } from "./crumbs.js";
29
30
  import { DataDisplayComponent } from "./data-display.js";
30
31
  import { t as createDragGhost } from "./drag-ghost-DtSDvjri.js";
31
- import { t as menu_exports } from "./menu-DMzjkVnK.js";
32
+ import { t as menu_exports } from "./menu-Bg90Qr0B.js";
32
33
  import { DataGridElement } from "./data-grid.js";
34
+ import { DetailListElement } from "./detail-list.js";
33
35
  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
36
  import { TabListElement } from "./tabs.js";
35
37
  import { TitlebarElement } from "./titlebar.js";
36
38
  import { EditableLabel } from "./editable-label.js";
37
39
  import { n as EditableNumber, t as LabelledSliderInput } from "./labelled-slider-input-BInB5GKL.js";
38
40
  import { t as FormElement } from "./form-CsXJX15p.js";
41
+ import { GridListElement } from "./grid-list.js";
39
42
  import { PanelElement } from "./panel.js";
40
43
  import { GroupedItemListerElement } from "./grouped-item-lister.js";
44
+ import { VerticalListElement } from "./vertical-list.js";
41
45
  import { t as IxfxHexEditorElement } from "./hex-editor-BiRfwTz1.js";
42
46
  import { t as RadialInputElement } from "./radial-input-BfA3GO0H.js";
43
47
  import { LabelledRadialInput } from "./labelled-radial-input.js";
@@ -45,7 +49,7 @@ import { t as SliderInputElement } from "./slider-input-Bl1hFE89.js";
45
49
  import { LedElement } from "./led.js";
46
50
  import { MillerBaseElement, MillerListElement, MillerTreeController } from "./miller.js";
47
51
  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";
52
+ 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
53
  import { PolarPad } from "./polar-pad.js";
50
54
  import { t as tooltipValueStyles } from "./tooltip-CJt3WLmN.js";
51
55
  import { RangeInput } from "./range-input.js";
@@ -57,12 +61,13 @@ import { SwipeElement } from "./swipe.js";
57
61
  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
62
  import { TransitoryLabelElement } from "./transitory-label.js";
59
63
  import { XyPad } from "./xy-pad.js";
60
- import { LitElement, css, html, nothing, render } from "lit";
64
+ import { LitElement, css, html, nothing } from "lit";
61
65
  import { customElement, eventOptions, property, query, queryAssignedElements, state } from "lit/decorators.js";
62
66
  import { unsafeHTML } from "lit/directives/unsafe-html.js";
63
67
  import { Trees } from "@ixfx/collections";
64
68
  import { clamp, clamper, scaler } from "@ixfx/numbers";
65
69
  import { repeat } from "lit/directives/repeat.js";
70
+ import { classMap } from "lit/directives/class-map.js";
66
71
  //#region src/button/radio-group.ts
67
72
  let RadioGroup = class RadioGroup extends LitElement {
68
73
  constructor(..._args) {
@@ -1602,1032 +1607,270 @@ __decorate([state()], CrumbPathElement.prototype, "_editStartIndex", void 0);
1602
1607
  __decorate([state()], CrumbPathElement.prototype, "_openCumPath", void 0);
1603
1608
  CrumbPathElement = __decorate([safeCustomElement(`ixfx-crumb-path`)], CrumbPathElement);
1604
1609
  //#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 });
1610
+ //#region src/data-table/data-table.ts
1611
+ let DataTableElement = class DataTableElement extends LitElement {
1612
+ constructor(..._args) {
1613
+ super(..._args);
1614
+ this.rows = [];
1615
+ this.tickleDelay = 400;
1616
+ this.layout = `auto`;
1617
+ this.narrowBreakpoint = 420;
1618
+ this._tickled = new TickledController(this, { getItems: () => this.rows.map((_, i) => ({
1619
+ id: i,
1620
+ enabled: true
1621
+ })) });
1622
+ this._stacked = false;
1623
+ this.#lastWidth = 0;
2390
1624
  }
2391
1625
  static {
2392
1626
  this.styles = [
2393
- themeFallbacks,
2394
- scrollFadeStyles(`x`),
1627
+ tickledStyles,
1628
+ tickledItemStyles,
2395
1629
  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
- }
1630
+ :host {
1631
+ display: block;
1632
+ container-type: inline-size;
1633
+ }
2411
1634
 
2412
- :host(:focus-visible) {
2413
- outline: 2px solid var(--accent);
2414
- outline-offset: 2px;
2415
- }
1635
+ .properties {
1636
+ display: grid;
1637
+ grid-template-columns: var(--data-table-name-width, max-content)
1638
+ var(--data-table-value-width, minmax(0, 1fr));
1639
+ /* No column gap: the tickled highlight reads as one solid block.
1640
+ Separation comes from cell padding, which the highlight covers. */
1641
+ gap: 2px 0;
1642
+ }
2416
1643
 
2417
- .container {
2418
- position: relative;
2419
- width: 100%;
2420
- height: 100%;
2421
- }
1644
+ /* Widening: 2 then 3 property-pairs per row on roomy containers.
1645
+ Suppressed when stacked, or when locked to 'columns'. */
1646
+ @container (min-width: 650px) {
1647
+ :host(:not([layout='columns'])) .properties:not(.stacked) {
1648
+ grid-template-columns:
1649
+ var(--data-table-name-width, max-content) var(--data-table-value-width, minmax(0, 1fr))
1650
+ var(--data-table-name-width, max-content) var(--data-table-value-width, minmax(0, 1fr));
1651
+ }
1652
+ }
2422
1653
 
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
- }
1654
+ @container (min-width: 1000px) {
1655
+ :host(:not([layout='columns'])) .properties:not(.stacked) {
1656
+ grid-template-columns:
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
+ var(--data-table-name-width, max-content) var(--data-table-value-width, minmax(0, 1fr));
1660
+ }
1661
+ }
2444
1662
 
2445
- ::slotted([hidden]) {
2446
- display: none !important;
2447
- }
1663
+ /* Stacked (key above value). Threshold is JS-driven via 'narrowBreakpoint'.
1664
+ minmax(0, 1fr) — not 1fr — so a long value's min-content size can't
1665
+ blow the column out past the container; the value then ellipsises. */
1666
+ .properties.stacked {
1667
+ grid-template-columns: minmax(0, 1fr);
1668
+ }
2448
1669
 
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
- }
1670
+ /* A pair is its own subgrid so the whole row is one element:
1671
+ the tickled highlight can't split, and key/value share a baseline. */
1672
+ .pair {
1673
+ display: grid;
1674
+ grid-column: span 2;
1675
+ grid-template-columns: subgrid;
1676
+ align-items: var(--data-table-cell-align, baseline);
1677
+ cursor: pointer;
1678
+ }
2463
1679
 
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
- }
1680
+ .pair[data-full] {
1681
+ grid-column: 1 / -1;
1682
+ }
2469
1683
 
2470
- ::slotted([data-tickled]) {
2471
- background: var(--item-bg-tickled);
2472
- outline: var(--item-border-tickled);
2473
- }
1684
+ .pair[data-full] > .value {
1685
+ grid-column: 2 / -1;
1686
+ }
2474
1687
 
2475
- ::slotted([data-selected]) {
2476
- background: var(--item-bg-selected);
2477
- color: var(--item-text-selected);
2478
- outline: 1px solid transparent;
2479
- }
1688
+ .properties.stacked .pair {
1689
+ display: block;
1690
+ grid-column: 1 / -1;
1691
+ /* Let the block shrink below its content so .value-text can clip. */
1692
+ min-width: 0;
1693
+ }
2480
1694
 
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
- }
1695
+ .properties.stacked .pair:not(:last-child) {
1696
+ margin-bottom: var(--space-s);
1697
+ }
2486
1698
 
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
- }
1699
+ /* Stacked keys get their own line, so a long label must wrap rather than
1700
+ run off the edge (nowrap is only wanted for the max-content column). */
1701
+ .properties.stacked .key {
1702
+ white-space: normal;
1703
+ overflow-wrap: break-word;
1704
+ }
2493
1705
 
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
- }
1706
+ .key,
1707
+ .value {
1708
+ min-width: 0;
1709
+ /* Row rhythm comes from this padding (tunable), not line-height. */
1710
+ padding: var(--data-table-row-padding, 0.35em) 0;
1711
+ }
2499
1712
 
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
- }
1713
+ .key {
1714
+ padding-right: var(--space-m);
1715
+ opacity: 0.6;
1716
+ white-space: nowrap;
1717
+ text-align: var(--data-table-name-align, left);
1718
+ font-size: 10px;
1719
+ text-transform: uppercase;
1720
+ letter-spacing: 0.05em;
1721
+ /* Uppercase keys have no descenders, so both edges can be trimmed to
1722
+ the glyphs for tight baseline / centre alignment. */
1723
+ text-box: trim-both cap alphabetic;
1724
+ }
2512
1725
 
2513
- .loading[hidden] {
2514
- display: none;
2515
- }
1726
+ .value {
1727
+ position: relative;
1728
+ padding-right: var(--space-l);
1729
+ font: 12px ui-monospace, SFMono-Regular, Menlo, monospace;
1730
+ }
2516
1731
 
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
- }
1732
+ /* The value text is clipped here — never on .value itself — so a long
1733
+ value stays within its cell even while tickled, and the detail
1734
+ popover (a child of .value) can still escape the cell. */
1735
+ .value-text {
1736
+ display: block;
1737
+ overflow: hidden;
1738
+ text-overflow: ellipsis;
1739
+ white-space: nowrap;
1740
+ /* No text-box trim here: the natural line box gives descenders the room
1741
+ overflow:hidden would otherwise clip. Baseline alignment of key and
1742
+ value comes from align-items on .pair, which needs no trimming. */
1743
+ }
2531
1744
 
2532
- .search-overlay[hidden] {
2533
- display: none;
2534
- }
1745
+ .pair.tickled {
1746
+ background: var(--item-bg-tickled, color-mix(in oklch, var(--surface-2), var(--accent) 10%));
1747
+ }
2535
1748
 
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
- }
1749
+ /* Lift the row whose detail is open above the rows it slides over. */
1750
+ .pair.show-detail {
1751
+ position: relative;
1752
+ z-index: 1;
1753
+ }
2544
1754
 
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
- }
1755
+ .detail {
1756
+ position: absolute;
1757
+ z-index: 2;
1758
+ /* Flush with the bottom of the value (no gap) so the pointer can travel
1759
+ from value to panel without crossing a dead zone that would dismiss it.
1760
+ Left-aligned with the value; only as wide as its content, capped at
1761
+ the table width. */
1762
+ top: 100%;
1763
+ left: 0;
1764
+ right: auto;
1765
+ width: max-content;
1766
+ max-width: 100cqi;
1767
+ padding: var(--space-xs);
1768
+ background: var(--surface-2);
1769
+ visibility: hidden;
1770
+ transform: translateY(-4px);
1771
+ pointer-events: none;
1772
+ transition: transform var(--transition);
1773
+ }
2554
1774
 
2555
- .search-close:hover {
2556
- color: inherit;
2557
- }
2558
- `
1775
+ .pair.show-detail .detail {
1776
+ visibility: visible;
1777
+ transform: translateY(0);
1778
+ pointer-events: auto;
1779
+ }
1780
+ `
2559
1781
  ];
2560
1782
  }
1783
+ #detailTimer;
1784
+ #resizeObserver;
1785
+ #lastWidth;
1786
+ connectedCallback() {
1787
+ super.connectedCallback();
1788
+ this.#resizeObserver = new ResizeObserver((entries) => {
1789
+ const entry = entries[0];
1790
+ if (!entry) return;
1791
+ this.#lastWidth = entry.borderBoxSize?.[0]?.inlineSize ?? entry.contentRect.width;
1792
+ this.#updateStacked();
1793
+ });
1794
+ this.#resizeObserver.observe(this);
1795
+ }
1796
+ disconnectedCallback() {
1797
+ super.disconnectedCallback();
1798
+ clearTimeout(this.#detailTimer);
1799
+ this.#resizeObserver?.disconnect();
1800
+ this.#resizeObserver = void 0;
1801
+ }
1802
+ willUpdate(changed) {
1803
+ if (changed.has(`layout`) || changed.has(`narrowBreakpoint`)) this.#updateStacked();
1804
+ }
1805
+ #updateStacked() {
1806
+ const stacked = this.layout === `rows` ? true : this.layout === `columns` ? false : this.#lastWidth > 0 && this.#lastWidth <= this.narrowBreakpoint;
1807
+ if (stacked !== this._stacked) this._stacked = stacked;
1808
+ }
1809
+ #handlePointerEnter(i) {
1810
+ this._tickled.handlePointerEnter(i);
1811
+ clearTimeout(this.#detailTimer);
1812
+ if (this._detailIndex !== void 0) this._detailIndex = void 0;
1813
+ this.#detailTimer = setTimeout(() => {
1814
+ if (this._tickled.isTickled(i)) this._detailIndex = i;
1815
+ }, this.tickleDelay);
1816
+ }
1817
+ #handlePointerLeave(e) {
1818
+ clearTimeout(this.#detailTimer);
1819
+ this._detailIndex = void 0;
1820
+ this._tickled.handlePointerLeave(e);
1821
+ }
1822
+ #select(i) {
1823
+ clearTimeout(this.#detailTimer);
1824
+ this.selectedIndex = this.selectedIndex === i ? void 0 : i;
1825
+ this._tickled.setTickled(i);
1826
+ this._detailIndex = i;
1827
+ this.dispatchEvent(new CustomEvent(`data-table-row-click`, {
1828
+ bubbles: true,
1829
+ composed: true,
1830
+ detail: {
1831
+ row: this.rows[i],
1832
+ index: i,
1833
+ selected: this.selectedIndex === i
1834
+ }
1835
+ }));
1836
+ }
2561
1837
  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
- `;
1838
+ return html`<div
1839
+ class="properties ${this._stacked ? `stacked` : ``}"
1840
+ @pointerleave=${(e) => this.#handlePointerLeave(e)}
1841
+ >
1842
+ ${this.rows.map((r, i) => html`<div
1843
+ class="pair ${this._tickled.isTickled(i) ? `tickled` : ``} ${this._detailIndex === i ? `show-detail` : ``}"
1844
+ ?data-full=${r.fullWidth}
1845
+ @pointerenter=${() => this.#handlePointerEnter(i)}
1846
+ @click=${() => this.#select(i)}
1847
+ ><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
1848
  }
2590
1849
  };
2591
1850
  __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);
1851
+ type: Array,
1852
+ attribute: false
1853
+ })], DataTableElement.prototype, "rows", void 0);
2607
1854
  __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);
1855
+ type: Number,
1856
+ attribute: false
1857
+ })], DataTableElement.prototype, "selectedIndex", void 0);
2612
1858
  __decorate([property({
2613
- type: String,
2614
- attribute: `icon-property`
2615
- })], DetailListElement.prototype, "iconProperty", void 0);
1859
+ type: Number,
1860
+ attribute: `tickle-delay`
1861
+ })], DataTableElement.prototype, "tickleDelay", void 0);
2616
1862
  __decorate([property({
2617
1863
  type: String,
2618
- attribute: `key-property`
2619
- })], DetailListElement.prototype, "keyProperty", void 0);
2620
- __decorate([property({ attribute: false })], DetailListElement.prototype, "filterPredicate", void 0);
1864
+ reflect: true
1865
+ })], DataTableElement.prototype, "layout", void 0);
2621
1866
  __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);
1867
+ type: Number,
1868
+ attribute: `narrow-breakpoint`
1869
+ })], DataTableElement.prototype, "narrowBreakpoint", void 0);
1870
+ __decorate([state()], DataTableElement.prototype, "_tickled", void 0);
1871
+ __decorate([state()], DataTableElement.prototype, "_detailIndex", void 0);
1872
+ __decorate([state()], DataTableElement.prototype, "_stacked", void 0);
1873
+ DataTableElement = __decorate([safeCustomElement(`ixfx-data-table`)], DataTableElement);
2631
1874
  //#endregion
2632
1875
  //#region src/dock/dock-drag.ts
2633
1876
  let activeDrag = null;
@@ -5918,6 +5161,136 @@ function dismiss(id) {
5918
5161
  syncRenderer();
5919
5162
  }
5920
5163
  //#endregion
5164
+ //#region src/pan-text/pan-text.ts
5165
+ const SPEED = 45;
5166
+ const END_PADDING = 4;
5167
+ const START_DELAY = 300;
5168
+ const PAN_EASING = `cubic-bezier(0.25, 0.46, 0.45, 0.94)`;
5169
+ let PanTextElement = class PanTextElement extends LitElement {
5170
+ constructor(..._args) {
5171
+ super(..._args);
5172
+ this.direction = `end`;
5173
+ this.disabled = false;
5174
+ this._isOverflowing = false;
5175
+ this._isHovering = false;
5176
+ this._overflow = 0;
5177
+ this._resizeObserver = null;
5178
+ this._onMouseEnter = () => {
5179
+ this._isHovering = true;
5180
+ };
5181
+ this._onMouseLeave = () => {
5182
+ this._isHovering = false;
5183
+ };
5184
+ this._measure = () => {
5185
+ if (this._isHovering) return;
5186
+ const el = this._textEl;
5187
+ if (!el) return;
5188
+ this._overflow = Math.max(0, el.scrollWidth - el.clientWidth);
5189
+ const overflowing = this._overflow > 0;
5190
+ this.classList.toggle(`is-overflowing`, overflowing);
5191
+ if (overflowing !== this._isOverflowing) this._isOverflowing = overflowing;
5192
+ };
5193
+ }
5194
+ connectedCallback() {
5195
+ super.connectedCallback();
5196
+ this._resizeObserver = new ResizeObserver(() => this._measure());
5197
+ this.addEventListener(`mouseenter`, this._onMouseEnter);
5198
+ this.addEventListener(`mouseleave`, this._onMouseLeave);
5199
+ }
5200
+ disconnectedCallback() {
5201
+ super.disconnectedCallback();
5202
+ this._resizeObserver?.disconnect();
5203
+ this._resizeObserver = null;
5204
+ this.removeEventListener(`mouseenter`, this._onMouseEnter);
5205
+ this.removeEventListener(`mouseleave`, this._onMouseLeave);
5206
+ }
5207
+ firstUpdated() {
5208
+ this._measure();
5209
+ this._resizeObserver?.observe(this);
5210
+ document.fonts?.ready.then(() => this._measure());
5211
+ }
5212
+ updated() {
5213
+ this._measure();
5214
+ }
5215
+ _textStyle() {
5216
+ if (!this._isOverflowing) return ``;
5217
+ const distance = this._overflow + END_PADDING;
5218
+ const duration = Math.max(1.5, distance / SPEED);
5219
+ const revealEnd = this.direction === `end`;
5220
+ const restX = revealEnd ? 0 : -distance;
5221
+ const hoverX = revealEnd ? -distance : 0;
5222
+ if (this._isHovering) return [
5223
+ `transform: translateX(${hoverX}px)`,
5224
+ `transition: transform ${duration}s ${PAN_EASING} ${START_DELAY}ms`,
5225
+ `overflow: visible`,
5226
+ `text-overflow: clip`
5227
+ ].join(`; `);
5228
+ return [
5229
+ `transform: translateX(${restX}px)`,
5230
+ `transition: transform 400ms ${PAN_EASING}`,
5231
+ revealEnd ? `` : `overflow: visible; text-overflow: clip`
5232
+ ].filter(Boolean).join(`; `);
5233
+ }
5234
+ render() {
5235
+ const textClasses = {
5236
+ "is-overflowing": this._isOverflowing,
5237
+ "is-hovering": this._isHovering
5238
+ };
5239
+ return html`
5240
+ <span class="text ${classMap(textClasses)}" part="text" style=${this._textStyle()}>
5241
+ <slot @slotchange=${this._measure}></slot>
5242
+ </span>
5243
+ `;
5244
+ }
5245
+ static {
5246
+ this.styles = [themeFallbacks, css`
5247
+ /* Renders like plain inline text: no colours, border, padding or margin
5248
+ of its own — it only adds the clipping needed to pan on hover. */
5249
+ :host {
5250
+ display: inline-block;
5251
+ overflow: hidden;
5252
+ white-space: nowrap;
5253
+ max-width: 100%;
5254
+ box-sizing: border-box;
5255
+ font: inherit;
5256
+ color: inherit;
5257
+ }
5258
+
5259
+ :host([disabled]) {
5260
+ opacity: var(--disabled-opacity, 0.5);
5261
+ pointer-events: none;
5262
+ }
5263
+
5264
+ .text {
5265
+ display: block;
5266
+ overflow: hidden;
5267
+ text-overflow: ellipsis;
5268
+ white-space: nowrap;
5269
+ will-change: transform;
5270
+ }
5271
+
5272
+ @media (prefers-reduced-motion: reduce) {
5273
+ .text {
5274
+ transition: none !important;
5275
+ transform: none !important;
5276
+ }
5277
+ }
5278
+ `];
5279
+ }
5280
+ };
5281
+ __decorate([property({
5282
+ type: String,
5283
+ attribute: `direction`
5284
+ })], PanTextElement.prototype, "direction", void 0);
5285
+ __decorate([property({
5286
+ type: Boolean,
5287
+ attribute: `disabled`
5288
+ })], PanTextElement.prototype, "disabled", void 0);
5289
+ __decorate([state()], PanTextElement.prototype, "_isOverflowing", void 0);
5290
+ __decorate([state()], PanTextElement.prototype, "_isHovering", void 0);
5291
+ __decorate([query(`.text`)], PanTextElement.prototype, "_textEl", void 0);
5292
+ PanTextElement = __decorate([safeCustomElement(`ixfx-pan-text`)], PanTextElement);
5293
+ //#endregion
5921
5294
  //#region src/panel/panel-group.ts
5922
5295
  let PanelGroupElement = class PanelGroupElement extends LitElement {
5923
5296
  constructor(..._args) {
@@ -8608,6 +7981,6 @@ function init() {
8608
7981
  console.log(`Init`);
8609
7982
  }
8610
7983
  //#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 };
7984
+ 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, LabelledRadialInput, LabelledRangeInput, LabelledSliderInput, LedElement, 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, 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, keyForElement, listItemFadeKeyframes, monitorIndeterminate, moveLeafNodeToEdge, moveLeafStep, normalise, normalize, parseColourScaleAttr, parseLayout, pointerOverAnyHost, prefersReducedMotion, readBaseColour, registerFontIcon, registerIcon, removePanelFromStack, resolveCssVars, resolveGrid, resolveLeafPath, resolveTracks, serializeLayout, setActive, setIconFont, svgToDataUri, swapLeaves, tickledItemStyles, tickledStyles, timelineStyles, toSnapshot, walk, wrapRoot };
8612
7985
 
8613
7986
  //# sourceMappingURL=index.js.map