@ixfx/components 0.7.2 → 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 (82) hide show
  1. package/bundle/index.d.ts +805 -137
  2. package/bundle/index.d.ts.map +1 -1
  3. package/bundle/index.js +2168 -42
  4. package/bundle/index.js.map +1 -1
  5. package/dist/ac-text.js +1 -1
  6. package/dist/ac-token.js +1 -1
  7. package/dist/crumbs.js +3 -3
  8. package/dist/{data-grid-Bh4YrQGK.d.ts → data-grid-BddUjAti.d.ts} +33 -1
  9. package/dist/data-grid-BddUjAti.d.ts.map +1 -0
  10. package/dist/data-grid.d.ts +1 -1
  11. package/dist/data-grid.js +180 -7
  12. package/dist/data-grid.js.map +1 -1
  13. package/dist/detail-list-Br0WAnry.d.ts +167 -0
  14. package/dist/detail-list-Br0WAnry.d.ts.map +1 -0
  15. package/dist/detail-list.d.ts +3 -0
  16. package/dist/detail-list.js +1115 -0
  17. package/dist/detail-list.js.map +1 -0
  18. package/dist/grid-list-B_orPsot.d.ts +304 -0
  19. package/dist/grid-list-B_orPsot.d.ts.map +1 -0
  20. package/dist/grid-list.d.ts +3 -0
  21. package/dist/grid-list.js +933 -0
  22. package/dist/grid-list.js.map +1 -0
  23. package/dist/{grouped-item-lister-DnWB-eFT.d.ts → grouped-item-lister-fdZHVxFM.d.ts} +2 -2
  24. package/dist/{grouped-item-lister-DnWB-eFT.d.ts.map → grouped-item-lister-fdZHVxFM.d.ts.map} +1 -1
  25. package/dist/grouped-item-lister.d.ts +1 -1
  26. package/dist/index.d.ts +267 -124
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +469 -979
  29. package/dist/index.js.map +1 -1
  30. package/dist/interaction-Cr13azbM.js +3 -0
  31. package/dist/list-item-animation-DuiwgN3l.js +112 -0
  32. package/dist/list-item-animation-DuiwgN3l.js.map +1 -0
  33. package/dist/list-selection-controller-CUo-Hhcm.js +338 -0
  34. package/dist/list-selection-controller-CUo-Hhcm.js.map +1 -0
  35. package/dist/{list-selection-types-DSuRNWpx.d.ts → list-selection-types-B5ioL-dG.d.ts} +6 -1
  36. package/dist/list-selection-types-B5ioL-dG.d.ts.map +1 -0
  37. package/dist/{menu-CXDFJxNm.js → menu-Bg90Qr0B.js} +7 -7
  38. package/dist/menu-Bg90Qr0B.js.map +1 -0
  39. package/dist/{menu-item-D2S4i8u1.js → menu-item-Bx-Fs7jq.js} +3 -3
  40. package/dist/{menu-item-D2S4i8u1.js.map → menu-item-Bx-Fs7jq.js.map} +1 -1
  41. package/dist/menu.js +2 -2
  42. package/dist/miller.d.ts +1 -1
  43. package/dist/miller.js +2 -2
  44. package/dist/narrowed-text.js +2 -2
  45. package/dist/plots.js +1 -1
  46. package/dist/select-horiz.js +2 -2
  47. package/dist/{tickled-controller-CRzdIEjJ.d.ts → tickled-controller-Bn3YdQsM.d.ts} +13 -2
  48. package/dist/{tickled-controller-CRzdIEjJ.d.ts.map → tickled-controller-Bn3YdQsM.d.ts.map} +1 -1
  49. package/dist/{tickled-controller-h9GmJ_bU.js → tickled-controller-C66DgJHl.js} +16 -1
  50. package/dist/tickled-controller-C66DgJHl.js.map +1 -0
  51. package/dist/{tree-Ch4W8qXQ.js → tree-B74zdI60.js} +116 -17
  52. package/dist/tree-B74zdI60.js.map +1 -0
  53. package/dist/tree.d.ts +6 -3
  54. package/dist/tree.d.ts.map +1 -1
  55. package/dist/tree.js +1 -1
  56. package/dist/vertical-list-CQSEVeOT.d.ts +140 -0
  57. package/dist/vertical-list-CQSEVeOT.d.ts.map +1 -0
  58. package/dist/vertical-list.d.ts +3 -121
  59. package/dist/vertical-list.js +710 -1
  60. package/dist/vertical-list.js.map +1 -0
  61. package/dist/{xy-axis-DWizs0bt.js → xy-axis-VrX8dmvS.js} +2 -2
  62. package/dist/{xy-axis-DWizs0bt.js.map → xy-axis-VrX8dmvS.js.map} +1 -1
  63. package/docs-user/README.md +2 -0
  64. package/docs-user/data-grid.md +51 -2
  65. package/docs-user/detail-list.md +80 -15
  66. package/docs-user/grid-list.md +274 -0
  67. package/docs-user/index.json +11 -2
  68. package/docs-user/infra-tickled.md +163 -0
  69. package/docs-user/llms.txt +2 -0
  70. package/docs-user/tree.md +13 -0
  71. package/docs-user/vertical-list.md +51 -1
  72. package/llms.txt +2 -0
  73. package/package.json +17 -2
  74. package/dist/data-grid-Bh4YrQGK.d.ts.map +0 -1
  75. package/dist/interaction-D5XdKzbR.js +0 -2
  76. package/dist/list-selection-types-DSuRNWpx.d.ts.map +0 -1
  77. package/dist/menu-CXDFJxNm.js.map +0 -1
  78. package/dist/tickled-controller-h9GmJ_bU.js.map +0 -1
  79. package/dist/tree-Ch4W8qXQ.js.map +0 -1
  80. package/dist/vertical-list-ClnqTkF3.js +0 -814
  81. package/dist/vertical-list-ClnqTkF3.js.map +0 -1
  82. package/dist/vertical-list.d.ts.map +0 -1
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
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
+ import { n as tickledStyles, t as tickledItemStyles } from "./tickled-styles-BrAHEj1q.js";
3
4
  import { t as themeFallbacks } from "./fallbacks-DNI9xt0F.js";
4
5
  import { t as popupSurfaceStyles } from "./popup-D80sj2QC.js";
5
6
  import { n as safeCustomElement, t as __decorate } from "./decorate-VRK8rslU.js";
@@ -14,28 +15,33 @@ import { i as ICON_CLOSE, n as ICON_CHECK, r as ICON_CHEVRON_DOWN, t as ICON_CAR
14
15
  import "./icons.js";
15
16
  import { t as prominenceStyles } from "./prominence-DlFShic3.js";
16
17
  import { n as buttonStyles, t as ButtonElement } from "./button-DsFbV4XD.js";
17
- import { n as ListSelectionController, t as VerticalListElement } from "./vertical-list-ClnqTkF3.js";
18
- 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";
19
22
  import { Checkbox } from "./checkbox.js";
20
23
  import { n as ColourPicker, t as ColourPickerPopup } from "./colour-picker-Ddsk-7E_.js";
21
24
  import { LabelledRangeInput } from "./labelled-range-input.js";
22
25
  import { t as collectDataResult } from "./data-provider-35TyWclX.js";
23
- 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";
24
27
  import { t as scrollFadeStyles } from "./scroll-fade-CAs1EabA.js";
25
28
  import { NarrowedTextElement } from "./narrowed-text.js";
26
29
  import { CrumbNavigationElement } from "./crumbs.js";
27
30
  import { DataDisplayComponent } from "./data-display.js";
28
31
  import { t as createDragGhost } from "./drag-ghost-DtSDvjri.js";
29
- import { t as menu_exports } from "./menu-CXDFJxNm.js";
32
+ import { t as menu_exports } from "./menu-Bg90Qr0B.js";
30
33
  import { DataGridElement } from "./data-grid.js";
34
+ import { DetailListElement } from "./detail-list.js";
31
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";
32
36
  import { TabListElement } from "./tabs.js";
33
37
  import { TitlebarElement } from "./titlebar.js";
34
38
  import { EditableLabel } from "./editable-label.js";
35
39
  import { n as EditableNumber, t as LabelledSliderInput } from "./labelled-slider-input-BInB5GKL.js";
36
40
  import { t as FormElement } from "./form-CsXJX15p.js";
41
+ import { GridListElement } from "./grid-list.js";
37
42
  import { PanelElement } from "./panel.js";
38
43
  import { GroupedItemListerElement } from "./grouped-item-lister.js";
44
+ import { VerticalListElement } from "./vertical-list.js";
39
45
  import { t as IxfxHexEditorElement } from "./hex-editor-BiRfwTz1.js";
40
46
  import { t as RadialInputElement } from "./radial-input-BfA3GO0H.js";
41
47
  import { LabelledRadialInput } from "./labelled-radial-input.js";
@@ -43,7 +49,7 @@ import { t as SliderInputElement } from "./slider-input-Bl1hFE89.js";
43
49
  import { LedElement } from "./led.js";
44
50
  import { MillerBaseElement, MillerListElement, MillerTreeController } from "./miller.js";
45
51
  import { NotificationPillElement } from "./notifier.js";
46
- 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";
47
53
  import { PolarPad } from "./polar-pad.js";
48
54
  import { t as tooltipValueStyles } from "./tooltip-CJt3WLmN.js";
49
55
  import { RangeInput } from "./range-input.js";
@@ -55,12 +61,13 @@ import { SwipeElement } from "./swipe.js";
55
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";
56
62
  import { TransitoryLabelElement } from "./transitory-label.js";
57
63
  import { XyPad } from "./xy-pad.js";
58
- import { LitElement, css, html, nothing, render } from "lit";
64
+ import { LitElement, css, html, nothing } from "lit";
59
65
  import { customElement, eventOptions, property, query, queryAssignedElements, state } from "lit/decorators.js";
60
66
  import { unsafeHTML } from "lit/directives/unsafe-html.js";
61
67
  import { Trees } from "@ixfx/collections";
62
68
  import { clamp, clamper, scaler } from "@ixfx/numbers";
63
69
  import { repeat } from "lit/directives/repeat.js";
70
+ import { classMap } from "lit/directives/class-map.js";
64
71
  //#region src/button/radio-group.ts
65
72
  let RadioGroup = class RadioGroup extends LitElement {
66
73
  constructor(..._args) {
@@ -401,6 +408,99 @@ __decorate([state()], RadioGroup.prototype, "_icons", void 0);
401
408
  __decorate([query(`slot`)], RadioGroup.prototype, "_slot", void 0);
402
409
  RadioGroup = __decorate([safeCustomElement(`ixfx-radio-group`)], RadioGroup);
403
410
  //#endregion
411
+ //#region src/interaction/tree-keyboard-controller.ts
412
+ var TreeKeyboardNavController = class {
413
+ constructor(host, tickled, config) {
414
+ this.handleKeyDown = (event) => {
415
+ if (this.disableKeyboardNavigation()) return;
416
+ const { key } = event;
417
+ if ([
418
+ `ArrowDown`,
419
+ `ArrowUp`,
420
+ `ArrowLeft`,
421
+ `ArrowRight`,
422
+ `Enter`,
423
+ `Escape`
424
+ ].includes(key)) this.tickled.setInputMode(`keyboard`);
425
+ switch (key) {
426
+ case `ArrowDown`:
427
+ event.preventDefault();
428
+ this.#handleArrowDown();
429
+ break;
430
+ case `ArrowUp`:
431
+ event.preventDefault();
432
+ this.#handleArrowUp();
433
+ break;
434
+ case `ArrowRight`:
435
+ event.preventDefault();
436
+ this.#handleArrowRight();
437
+ break;
438
+ case `ArrowLeft`:
439
+ event.preventDefault();
440
+ this.#handleArrowLeft();
441
+ break;
442
+ case `Enter`:
443
+ event.preventDefault();
444
+ this.#handleEnter();
445
+ break;
446
+ case `Escape`:
447
+ event.preventDefault();
448
+ this.callbacks.onEscape?.();
449
+ }
450
+ };
451
+ this.host = host;
452
+ this.tickled = tickled;
453
+ this.callbacks = config.callbacks;
454
+ this.disableKeyboardNavigation = config.disableKeyboardNavigation ?? (() => false);
455
+ host.addController(this);
456
+ }
457
+ hostConnected() {}
458
+ hostDisconnected() {}
459
+ #handleArrowDown() {
460
+ this.tickled.handleKeyboardNavigation(`next`);
461
+ }
462
+ #handleArrowUp() {
463
+ this.tickled.handleKeyboardNavigation(`previous`);
464
+ }
465
+ #handleArrowRight() {
466
+ const currentId = this.tickled.tickledId;
467
+ if (!currentId) return;
468
+ if (this.callbacks.isLeaf(currentId)) return;
469
+ if (!this.callbacks.isExpanded(currentId)) {
470
+ this.callbacks.onExpand(currentId);
471
+ this.tickled.setTickled(currentId);
472
+ } else {
473
+ const items = this.callbacks.getItems();
474
+ const currentIdx = items.findIndex((item) => item.id === currentId);
475
+ if (currentIdx !== -1 && currentIdx + 1 < items.length) {
476
+ const nextItem = items[currentIdx + 1];
477
+ if (nextItem.enabled !== false) this.tickled.setTickled(nextItem.id);
478
+ }
479
+ }
480
+ }
481
+ #handleArrowLeft() {
482
+ const currentId = this.tickled.tickledId;
483
+ if (!currentId) return;
484
+ if (this.callbacks.isLeaf(currentId)) {
485
+ const parentId = this.callbacks.getParentId?.(currentId);
486
+ if (parentId) {
487
+ this.tickled.setTickled(parentId);
488
+ if (this.callbacks.isExpanded(parentId)) this.callbacks.onCollapse(parentId);
489
+ }
490
+ } else if (this.callbacks.isExpanded(currentId)) this.callbacks.onCollapse(currentId);
491
+ else {
492
+ const parentId = this.callbacks.getParentId?.(currentId);
493
+ if (parentId) this.tickled.setTickled(parentId);
494
+ }
495
+ }
496
+ #handleEnter() {
497
+ const currentId = this.tickled.tickledId;
498
+ if (!currentId) return;
499
+ this.callbacks.onActivate(currentId);
500
+ this.callbacks.onSelect?.(currentId);
501
+ }
502
+ };
503
+ //#endregion
404
504
  //#region src/button/split-button.ts
405
505
  let SplitButton = class SplitButton extends ButtonElement {
406
506
  constructor(..._args) {
@@ -1507,1010 +1607,270 @@ __decorate([state()], CrumbPathElement.prototype, "_editStartIndex", void 0);
1507
1607
  __decorate([state()], CrumbPathElement.prototype, "_openCumPath", void 0);
1508
1608
  CrumbPathElement = __decorate([safeCustomElement(`ixfx-crumb-path`)], CrumbPathElement);
1509
1609
  //#endregion
1510
- //#region src/detail-list/detail-list.ts
1511
- const ITEM_SELECTOR = `:scope > li`;
1512
- const SEARCH_HIGHLIGHT_KEY = `detail-list-search`;
1513
- const LABEL_SPAN_CSS = `overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0`;
1514
- let DetailListElement = class DetailListElement extends LitElement {
1515
- constructor(..._args2) {
1516
- super(..._args2);
1517
- this.selectionMode = `single`;
1518
- this.interactionMode = `standard`;
1519
- this.sort = `none`;
1520
- this.sortBy = ``;
1521
- this.items = [];
1522
- this.displayProperty = ``;
1523
- this.iconProperty = ``;
1524
- this.keyProperty = ``;
1525
- this.itemWrap = `stop`;
1526
- this._searchVisible = false;
1527
- this._searchQuery = ``;
1528
- this.#itemData = /* @__PURE__ */ new WeakMap();
1529
- this.#grid = [];
1530
- this.#positions = /* @__PURE__ */ new Map();
1531
- this.#geometryScheduled = false;
1532
- this.#builtFromProvider = [];
1533
- this.#providerBuilt = 0;
1534
- this.#data = new DataController(this);
1535
- this.selection = new ListSelectionController({
1536
- getOrderedItems: () => this._getVisibleItems(),
1537
- isSelectable: (el) => this._isCheckable(el),
1538
- getModes: () => ({
1539
- selectionMode: this.selectionMode,
1540
- interactionMode: this.interactionMode
1541
- }),
1542
- onChange: (selected, previous) => {
1543
- this._syncItemStates();
1544
- this._dispatch(`list-select`, {
1545
- selected,
1546
- previous
1547
- });
1548
- }
1549
- });
1550
- this.tickled = new TickledController(this, {
1551
- getItems: () => this._getVisibleItems().map((el) => ({
1552
- id: el,
1553
- enabled: !el.hasAttribute(`not-checked`)
1554
- })),
1555
- onTickle: (id) => {
1556
- if (id !== void 0) this._dispatch(`list-tickle`, { item: id });
1557
- },
1558
- onStateChange: () => this._syncItemStates(),
1559
- itemWrap: this.itemWrap
1560
- });
1561
- this._onPointerOver = (e) => {
1562
- const item = this._findListItem(e.composedPath());
1563
- if (item) this.tickled.handlePointerEnter(item);
1564
- };
1565
- this._onPointerLeave = (e) => {
1566
- this.tickled.handlePointerLeave(e);
1567
- };
1568
- this._onKeyDown = (e) => {
1569
- if ((e.ctrlKey || e.metaKey) && e.key === `f`) {
1570
- e.preventDefault();
1571
- this._openSearch();
1572
- return;
1573
- }
1574
- if ((e.ctrlKey || e.metaKey) && e.key === `a`) {
1575
- if (this.selection.canSelectAll()) {
1576
- e.preventDefault();
1577
- this.selection.selectAll();
1578
- }
1579
- return;
1580
- }
1581
- if (this._searchVisible && e.key === `Escape`) {
1582
- e.preventDefault();
1583
- this._closeSearch();
1584
- return;
1585
- }
1586
- switch (e.key) {
1587
- case `ArrowDown`:
1588
- e.preventDefault();
1589
- this.#navigateDown();
1590
- if (e.shiftKey && this.interactionMode === `vscode`) {
1591
- const cur = this.tickled.tickledId;
1592
- if (cur) this.selection.rangeTo(cur);
1593
- }
1594
- break;
1595
- case `ArrowUp`:
1596
- e.preventDefault();
1597
- this.#navigateUp();
1598
- if (e.shiftKey && this.interactionMode === `vscode`) {
1599
- const cur = this.tickled.tickledId;
1600
- if (cur) this.selection.rangeTo(cur);
1601
- }
1602
- break;
1603
- case `ArrowRight`:
1604
- e.preventDefault();
1605
- this.#navigateRight();
1606
- break;
1607
- case `ArrowLeft`:
1608
- e.preventDefault();
1609
- this.#navigateLeft();
1610
- break;
1611
- case `Home`:
1612
- e.preventDefault();
1613
- this.#navigateTo(this.#findFirstCheckable());
1614
- break;
1615
- case `End`:
1616
- e.preventDefault();
1617
- this.#navigateTo(this.#findLastCheckable());
1618
- break;
1619
- case `Enter`: {
1620
- const id = this.tickled.tickledId;
1621
- if (id) {
1622
- e.preventDefault();
1623
- this._dispatch(`list-item-click`, { item: id });
1624
- this._activateItem(id);
1625
- }
1626
- break;
1627
- }
1628
- case ` `: {
1629
- const id = this.tickled.tickledId;
1630
- if (id) {
1631
- e.preventDefault();
1632
- this.selection.handleSpace(id);
1633
- }
1634
- break;
1635
- }
1636
- case `Escape`: this.selection.clear();
1637
- }
1638
- };
1639
- this._onClick = (e) => {
1640
- const item = this._findListItem(e.composedPath());
1641
- if (!item) return;
1642
- this.focus();
1643
- this._dispatch(`list-item-click`, { item });
1644
- this.selection.handleClick(item, e);
1645
- };
1646
- this._onDblClick = (e) => {
1647
- const item = this._findListItem(e.composedPath());
1648
- if (item) this._activateItem(item);
1649
- };
1650
- this._onWheel = (e) => {
1651
- e.preventDefault();
1652
- this._listEl.scrollLeft += e.deltaY;
1653
- };
1654
- this._onSearchInput = (e) => {
1655
- const input = e.target;
1656
- this._searchQuery = input.value;
1657
- this.#search?.setQuery(this._searchQuery).then(() => this.#recomputeGeometry());
1658
- };
1659
- this._onSearchKeydown = (e) => {
1660
- if (e.key === `Escape`) {
1661
- e.stopPropagation();
1662
- this._closeSearch();
1663
- } else if (e.key === `ArrowDown`) {
1664
- e.preventDefault();
1665
- this.focus();
1666
- this.#navigateDown();
1667
- } else if (e.key === `ArrowUp`) {
1668
- e.preventDefault();
1669
- this.focus();
1670
- this.#navigateUp();
1671
- } else if (e.key === `ArrowRight`) e.preventDefault();
1672
- else if (e.key === `ArrowLeft`) e.preventDefault();
1673
- else if (e.key === `Enter`) {
1674
- e.preventDefault();
1675
- e.stopPropagation();
1676
- const id = this.tickled.tickledId ?? this.#findFirstCheckable();
1677
- if (id) {
1678
- this._dispatch(`list-item-click`, { item: id });
1679
- this._activateItem(id);
1680
- this._closeSearch();
1681
- }
1682
- } else if (e.key === ` `) {
1683
- const id = this.tickled.tickledId;
1684
- if (id) {
1685
- e.preventDefault();
1686
- this.selection.handleSpace(id);
1687
- }
1688
- }
1689
- };
1690
- }
1691
- #search;
1692
- #itemData;
1693
- #grid;
1694
- #positions;
1695
- #geometryScheduled;
1696
- #resizeObserver;
1697
- #mutationObserver;
1698
- #builtFromProvider;
1699
- #providerBuilt;
1700
- #data;
1701
- connectedCallback() {
1702
- super.connectedCallback();
1703
- if (!this.hasAttribute(`tabindex`)) this.tabIndex = 0;
1704
- this.addEventListener(`pointerover`, this._onPointerOver);
1705
- this.addEventListener(`pointerleave`, this._onPointerLeave);
1706
- this.addEventListener(`keydown`, this._onKeyDown);
1707
- this.addEventListener(`click`, this._onClick);
1708
- this.addEventListener(`dblclick`, this._onDblClick);
1709
- this.addEventListener(`wheel`, this._onWheel);
1710
- this.#resizeObserver = new ResizeObserver(() => this.#scheduleGeometry());
1711
- this.#resizeObserver.observe(this);
1712
- this.#mutationObserver = new MutationObserver(() => {
1713
- this.#processChildren();
1714
- this.#scheduleGeometry();
1715
- });
1716
- this.#mutationObserver.observe(this, {
1717
- childList: true,
1718
- subtree: false,
1719
- attributeFilter: [`hidden`]
1720
- });
1721
- }
1722
- disconnectedCallback() {
1723
- super.disconnectedCallback();
1724
- this.removeEventListener(`pointerover`, this._onPointerOver);
1725
- this.removeEventListener(`pointerleave`, this._onPointerLeave);
1726
- this.removeEventListener(`keydown`, this._onKeyDown);
1727
- this.removeEventListener(`click`, this._onClick);
1728
- this.removeEventListener(`dblclick`, this._onDblClick);
1729
- this.removeEventListener(`wheel`, this._onWheel);
1730
- this.#resizeObserver?.disconnect();
1731
- this.#resizeObserver = void 0;
1732
- this.#mutationObserver?.disconnect();
1733
- this.#mutationObserver = void 0;
1734
- this.#search?.dispose();
1735
- this.#search = void 0;
1736
- }
1737
- firstUpdated() {
1738
- this.#processChildren();
1739
- if (this.filterPredicate) this.#applyFilterPredicate();
1740
- this.#search = createElementIncrSearch({
1741
- container: this,
1742
- itemSelector: ITEM_SELECTOR,
1743
- getLabel: (el) => el.getAttribute(`data-search-label`) ?? el.textContent?.trim() ?? ``,
1744
- getKey: (el) => el.getAttribute(`data-key`) ?? el.getAttribute(`data-search-label`) ?? el.textContent?.trim() ?? ``,
1745
- highlightKey: SEARCH_HIGHLIGHT_KEY
1746
- });
1747
- this.#recomputeGeometry();
1748
- }
1749
- updated(changed) {
1750
- super.updated(changed);
1751
- if (changed.has(`provider`)) {
1752
- this.#builtFromProvider = [];
1753
- this.#providerBuilt = 0;
1754
- this.#data.provider = this.provider;
1755
- queueMicrotask(() => {
1756
- if (this.provider) this.#data.request(void 0);
1757
- });
1758
- return;
1759
- }
1760
- if (changed.has(`items`) && this.provider === void 0 && this.items.length > 0) this.#rebuildFromObjects(this.items);
1761
- this.#syncFromProvider();
1762
- if (changed.has(`sort`) || changed.has(`sortBy`) || changed.has(`sortComparator`)) this.#applySort();
1763
- if (changed.has(`filterPredicate`)) {
1764
- this.#applyFilterPredicate();
1765
- this.#scheduleGeometry();
1766
- }
1767
- if (changed.has(`interactionMode`)) {
1768
- this.selection.clearAnchor();
1769
- this._syncItemStates();
1770
- } else if (changed.has(`checkboxRenderer`) && this.interactionMode === `checked`) this._syncCheckboxes();
1771
- }
1772
- /** The most recently selected item, or `undefined` if nothing is selected. */
1773
- get selectedItem() {
1774
- return [...this.selection.selected].at(-1);
1775
- }
1776
- /** The full current selection set (read-only). */
1777
- get selectedItems() {
1778
- return this.selection.selected;
1779
- }
1780
- /**
1781
- * Force an immediate geometry recompute. Called automatically on resize,
1782
- * DOM changes, sort/filter/search changes — only needed when layout is
1783
- * changed by outside forces (e.g. an external font load).
1784
- */
1785
- refreshLayout() {
1786
- this.#recomputeGeometry();
1787
- }
1788
- addItem(item, options) {
1789
- let el;
1790
- if (typeof item === `string`) {
1791
- const li = document.createElement(`li`);
1792
- this._setLabel(li, item);
1793
- el = li;
1794
- } else el = item;
1795
- if (options?.notChecked) el.setAttribute(`not-checked`, ``);
1796
- this.appendChild(el);
1797
- this.#ensureIcon(el);
1798
- if (this.sort !== `none`) this.#applySort();
1799
- if (this.interactionMode === `checked`) this._syncCheckboxes();
1800
- }
1801
- removeItem(item) {
1802
- item.remove();
1803
- }
1804
- clearItems() {
1805
- this.replaceChildren();
1806
- this.selection.reset();
1807
- this.#builtFromProvider = [];
1808
- this.#providerBuilt = 0;
1809
- this._syncItemStates();
1810
- }
1811
- /**
1812
- * Replace the entire item list in one call.
1813
- * Equivalent to `clearItems()` followed by `addItem()` for each element.
1814
- */
1815
- setItems(items) {
1816
- this.clearItems();
1817
- for (const item of items) {
1818
- this.appendChild(item);
1819
- this.#ensureIcon(item);
1820
- }
1821
- if (this.sort !== `none`) this.#applySort();
1822
- if (this.interactionMode === `checked`) this._syncCheckboxes();
1823
- }
1824
- /**
1825
- * Get the source object behind an `<li>` built from an `items` array or
1826
- * provider. Returns `undefined` for declarative / programmatic elements.
1827
- */
1828
- getItemData(item) {
1829
- return this.#itemData.get(item);
1830
- }
1831
- /** Dispatch a command to this list. No-op by default (grouped-lister parity). */
1832
- dispatch(_commandId, _args) {}
1833
- select(item) {
1834
- this.selection.replace(item);
1835
- }
1836
- selectMany(items) {
1837
- this.selection.selectMany(items);
1838
- }
1839
- deselect(item) {
1840
- this.selection.deselect(item);
1841
- }
1842
- clearSelection() {
1843
- this.selection.clear();
1844
- }
1845
- selectAll() {
1846
- if (this.selection.canSelectAll()) this.selection.selectAll();
1847
- }
1848
- *selectedFilter(kind) {
1849
- const wanted = (el) => {
1850
- switch (kind) {
1851
- case `selected`: return el.hasAttribute(`data-selected`);
1852
- case `checked`: return el.hasAttribute(`data-checked`);
1853
- case `either`: return el.hasAttribute(`data-selected`) || el.hasAttribute(`data-checked`);
1854
- }
1855
- };
1856
- for (const el of this.querySelectorAll(ITEM_SELECTOR)) {
1857
- if (el.hidden) continue;
1858
- if (wanted(el)) yield el;
1859
- }
1860
- }
1861
- #rebuildFromObjects(items) {
1862
- const keyOf = (el) => el.getAttribute(`data-key`) ?? el.getAttribute(`data-search-label`) ?? null;
1863
- const previousKeys = new Set([...this.selection.selected].map(keyOf).filter((k) => k !== null));
1864
- const anchorKey = this.selection.anchor ? keyOf(this.selection.anchor) : null;
1865
- this.replaceChildren(...items.map((item, index) => this.#createItemEl(item, index)));
1866
- if (previousKeys.size > 0 || anchorKey !== null) {
1867
- const next = [];
1868
- let anchor;
1869
- for (const el of this.querySelectorAll(ITEM_SELECTOR)) {
1870
- const key = keyOf(el);
1871
- if (key !== null && previousKeys.has(key)) next.push(el);
1872
- if (key !== null && key === anchorKey) anchor = el;
1873
- }
1874
- this.selection.seed(next, anchor);
1875
- }
1876
- if (this.sort !== `none`) this.#applySort();
1877
- if (this.interactionMode === `checked`) this._syncCheckboxes();
1878
- this._syncItemStates();
1879
- this.#scheduleGeometry();
1880
- }
1881
- #createItemEl(item, index) {
1882
- const li = document.createElement(`li`);
1883
- this.#itemData.set(li, item);
1884
- const content = this.formatter ? this.formatter(item, index) : this.displayProperty ? this.#propertyValue(item, this.displayProperty) : item;
1885
- if (typeof content === `string`) {
1886
- this._setLabel(li, content);
1887
- if (this.iconProperty) {
1888
- const icon = this.#propertyValue(item, this.iconProperty);
1889
- if (typeof icon === `string` && icon.length > 0) li.setAttribute(`data-icon`, icon);
1890
- }
1891
- } else if (content instanceof Node) li.appendChild(content);
1892
- if (this.keyProperty) {
1893
- const key = this.#propertyValue(item, this.keyProperty);
1894
- if (key !== void 0 && key !== null) li.setAttribute(`data-key`, String(key));
1895
- }
1896
- this.#ensureIcon(li);
1897
- return li;
1898
- }
1899
- #propertyValue(item, prop) {
1900
- if (typeof item !== `object` || item === null || !(prop in item)) return void 0;
1901
- return item[prop];
1902
- }
1903
- #syncFromProvider() {
1904
- if (this.provider === void 0) return;
1905
- const items = this.#data.items;
1906
- if (items === this.#builtFromProvider) return;
1907
- let prefix = 0;
1908
- while (prefix < this.#builtFromProvider.length && prefix < items.length && this.#builtFromProvider[prefix] === items[prefix]) prefix++;
1909
- if (prefix === this.#builtFromProvider.length && prefix <= items.length) {
1910
- const tail = items.slice(prefix);
1911
- for (const item of tail) this.appendChild(this.#createItemEl(item, this.#providerBuilt++));
1912
- if (this.sort !== `none`) this.#applySort();
1913
- if (this.interactionMode === `checked`) this._syncCheckboxes();
1914
- this._syncItemStates();
1915
- this.#scheduleGeometry();
1916
- } else {
1917
- this.#providerBuilt = 0;
1918
- this.#rebuildFromObjects(items);
1919
- }
1920
- this.#builtFromProvider = items;
1921
- }
1922
- #applyFilterPredicate() {
1923
- if (!this.filterPredicate) {
1924
- for (const el of this.querySelectorAll(ITEM_SELECTOR)) el.removeAttribute(`hidden`);
1925
- return;
1926
- }
1927
- for (const el of this.querySelectorAll(ITEM_SELECTOR)) if (this.filterPredicate(el)) el.removeAttribute(`hidden`);
1928
- else el.setAttribute(`hidden`, ``);
1929
- }
1930
- #applySort() {
1931
- if (this.sort === `none`) return;
1932
- const rows = [...this.querySelectorAll(ITEM_SELECTOR)];
1933
- if (rows.length < 2) return;
1934
- const dir = this.sort === `descending` ? -1 : 1;
1935
- const meta = (el) => ({
1936
- element: el,
1937
- data: this.getItemData(el),
1938
- label: el.getAttribute(`data-search-label`) ?? el.textContent?.trim() ?? ``
1939
- });
1940
- let cmp;
1941
- if (this.sortComparator) {
1942
- const user = this.sortComparator;
1943
- cmp = (a, b) => user(meta(a), meta(b));
1944
- } else if (this.sortBy) {
1945
- const prop = this.sortBy;
1946
- const key = (el) => {
1947
- const v = this.#propertyValue(this.getItemData(el), prop);
1948
- if (typeof v === `number`) return v;
1949
- return v === void 0 || v === null ? `` : String(v);
1950
- };
1951
- cmp = (a, b) => {
1952
- const ka = key(a);
1953
- const kb = key(b);
1954
- if (typeof ka === `number` && typeof kb === `number`) return ka - kb;
1955
- return String(ka).localeCompare(String(kb), void 0, { numeric: true });
1956
- };
1957
- } else cmp = (a, b) => meta(a).label.localeCompare(meta(b).label, void 0, { numeric: true });
1958
- const indexed = rows.map((el, i) => ({
1959
- el,
1960
- i
1961
- }));
1962
- indexed.sort((a, b) => cmp(a.el, b.el) * dir || a.i - b.i);
1963
- for (const { el } of indexed) this.appendChild(el);
1964
- this.#scheduleGeometry();
1965
- }
1966
- #scheduleGeometry() {
1967
- if (this.#geometryScheduled) return;
1968
- this.#geometryScheduled = true;
1969
- requestAnimationFrame(() => {
1970
- this.#geometryScheduled = false;
1971
- this.#recomputeGeometry();
1972
- });
1973
- }
1974
- #recomputeGeometry() {
1975
- this.#grid = [];
1976
- this.#positions.clear();
1977
- if (!this.isConnected) return;
1978
- const columns = [];
1979
- for (const el of this._getVisibleItems()) {
1980
- const rect = el.getBoundingClientRect();
1981
- if (rect.width === 0 && rect.height === 0) continue;
1982
- let ci = columns.findIndex((c) => Math.abs(c.left - rect.left) <= 1);
1983
- if (ci === -1) {
1984
- columns.push({
1985
- left: rect.left,
1986
- items: []
1987
- });
1988
- ci = columns.length - 1;
1989
- }
1990
- columns[ci].items.push(el);
1991
- }
1992
- columns.sort((a, b) => a.left - b.left);
1993
- for (const col of columns) col.items.sort((a, b) => a.getBoundingClientRect().top - b.getBoundingClientRect().top);
1994
- this.#grid = columns.map((c) => c.items);
1995
- this.#grid.forEach((col, colIdx) => col.forEach((el, rowIdx) => this.#positions.set(el, {
1996
- col: colIdx,
1997
- row: rowIdx
1998
- })));
1999
- }
2000
- #positionOf(el) {
2001
- let pos = this.#positions.get(el);
2002
- if (pos === void 0) {
2003
- this.#recomputeGeometry();
2004
- pos = this.#positions.get(el);
2005
- }
2006
- return pos;
2007
- }
2008
- #itemAt(col, row) {
2009
- return this.#grid[col]?.[row];
2010
- }
2011
- #gridSize() {
2012
- if (this.#grid.length === 0) this.#recomputeGeometry();
2013
- return {
2014
- cols: this.#grid.length,
2015
- rows: Math.max(0, ...this.#grid.map((c) => c.length))
2016
- };
2017
- }
2018
- #navigateTo(target) {
2019
- if (!target) return;
2020
- this.tickled.handleKeyboardNavigation(target);
2021
- this.tickled.tickledId?.scrollIntoView({
2022
- block: `nearest`,
2023
- inline: `nearest`
2024
- });
2025
- }
2026
- #findNextCheckable(fromCol, fromRow, deltaCol, deltaRow) {
2027
- const { cols } = this.#gridSize();
2028
- const maxIterations = this._getVisibleItems().length + 1;
2029
- let col = deltaCol !== 0 ? fromCol + deltaCol : fromCol;
2030
- let row = deltaRow !== 0 ? fromRow + deltaRow : fromRow;
2031
- let wrappedColumns = false;
2032
- for (let iterations = 0; iterations < maxIterations; iterations++) {
2033
- const colLength = this.#grid[col]?.length ?? 0;
2034
- if (row < 0 || row >= colLength) {
2035
- if (deltaCol !== 0 || wrappedColumns) {
2036
- if (this.itemWrap === `wrap`) {
2037
- if (deltaCol < 0) col = cols - 1;
2038
- else if (deltaCol > 0) col = 0;
2039
- wrappedColumns = true;
2040
- } else return;
2041
- }
2042
- if (deltaRow > 0) {
2043
- if (col + 1 < cols) {
2044
- col = col + 1;
2045
- row = 0;
2046
- wrappedColumns = true;
2047
- } else if (this.itemWrap === `wrap`) {
2048
- col = 0;
2049
- row = 0;
2050
- wrappedColumns = true;
2051
- } else return;
2052
- } else if (deltaRow < 0) {
2053
- if (col - 1 >= 0) {
2054
- col = col - 1;
2055
- row = (this.#grid[col]?.length ?? 1) - 1;
2056
- wrappedColumns = true;
2057
- } else if (this.itemWrap === `wrap`) {
2058
- col = cols - 1;
2059
- row = (this.#grid[col]?.length ?? 1) - 1;
2060
- wrappedColumns = true;
2061
- } else return;
2062
- } else if (deltaCol !== 0) {
2063
- const targetColLength = this.#grid[col]?.length ?? 0;
2064
- if (row >= targetColLength && targetColLength > 0) row = targetColLength - 1;
2065
- }
2066
- if (col === fromCol && row === fromRow + deltaRow) return;
2067
- continue;
2068
- }
2069
- const item = this.#itemAt(col, row);
2070
- if (item && this._isCheckable(item)) return item;
2071
- row += deltaRow;
2072
- }
2073
- }
2074
- #findFirstCheckable() {
2075
- return this._getVisibleItems().find((item) => this._isCheckable(item));
2076
- }
2077
- #findLastCheckable() {
2078
- const visibleItems = this._getVisibleItems();
2079
- for (let i = visibleItems.length - 1; i >= 0; i--) if (this._isCheckable(visibleItems[i])) return visibleItems[i];
2080
- }
2081
- #navigateDown() {
2082
- this.#recomputeGeometry();
2083
- const cur = this.tickled.tickledId ? this.#positionOf(this.tickled.tickledId) : void 0;
2084
- if (!cur) {
2085
- this.#navigateTo(this.#findFirstCheckable());
2086
- return;
2087
- }
2088
- const next = this.#findNextCheckable(cur.col, cur.row, 0, 1);
2089
- if (next) this.#navigateTo(next);
2090
- }
2091
- #navigateUp() {
2092
- this.#recomputeGeometry();
2093
- const cur = this.tickled.tickledId ? this.#positionOf(this.tickled.tickledId) : void 0;
2094
- if (!cur) {
2095
- this.#navigateTo(this.#findLastCheckable());
2096
- return;
2097
- }
2098
- const next = this.#findNextCheckable(cur.col, cur.row, 0, -1);
2099
- if (next) this.#navigateTo(next);
2100
- }
2101
- #navigateRight() {
2102
- this.#recomputeGeometry();
2103
- const cur = this.tickled.tickledId ? this.#positionOf(this.tickled.tickledId) : void 0;
2104
- if (!cur) {
2105
- this.#navigateTo(this.#findFirstCheckable());
2106
- return;
2107
- }
2108
- const next = this.#findNextCheckable(cur.col, cur.row, 1, 0);
2109
- if (next) this.#navigateTo(next);
2110
- else if (this.itemWrap === `wrap`) this.#navigateTo(this.#findFirstCheckable());
2111
- }
2112
- #navigateLeft() {
2113
- this.#recomputeGeometry();
2114
- const cur = this.tickled.tickledId ? this.#positionOf(this.tickled.tickledId) : void 0;
2115
- if (!cur) {
2116
- this.#navigateTo(this.#findFirstCheckable());
2117
- return;
2118
- }
2119
- const next = this.#findNextCheckable(cur.col, cur.row, -1, 0);
2120
- if (next) this.#navigateTo(next);
2121
- else if (this.itemWrap === `wrap`) this.#navigateTo(this.#findLastCheckable());
2122
- }
2123
- _getVisibleItems() {
2124
- return Array.from(this.querySelectorAll(ITEM_SELECTOR)).filter((el) => {
2125
- if (el.hidden) return false;
2126
- if (this.filterPredicate && !this.filterPredicate(el)) return false;
2127
- return true;
2128
- });
2129
- }
2130
- _isCheckable(item) {
2131
- if (this.interactionMode !== `checked`) return true;
2132
- return !item.hasAttribute(`not-checked`);
2133
- }
2134
- _setLabel(li, label) {
2135
- const span = document.createElement(`span`);
2136
- span.style.cssText = LABEL_SPAN_CSS;
2137
- span.textContent = label;
2138
- li.appendChild(span);
2139
- li.setAttribute(`data-search-label`, label);
2140
- }
2141
- /**
2142
- * Inject a leading `<ixfx-icon>` into an item carrying `data-icon`.
2143
- * Idempotent — skips when the item already starts with an icon.
2144
- */
2145
- #ensureIcon(li) {
2146
- const name = li.getAttribute(`data-icon`);
2147
- if (!name) return;
2148
- if (li.firstElementChild?.tagName === `IXFX-ICON`) return;
2149
- const icon = document.createElement(`ixfx-icon`);
2150
- icon.setAttribute(`name`, name);
2151
- icon.style.marginInlineEnd = `var(--detail-list-icon-gap, 6px)`;
2152
- icon.style.flexShrink = `0`;
2153
- li.prepend(icon);
2154
- }
2155
- /** Scan light-DOM children for unprocessed `data-icon` items. */
2156
- #processChildren() {
2157
- for (const el of this.querySelectorAll(ITEM_SELECTOR)) this.#ensureIcon(el);
2158
- }
2159
- _syncItemStates() {
2160
- for (const el of this.querySelectorAll(ITEM_SELECTOR)) {
2161
- el.toggleAttribute(`data-tickled`, this.tickled.isTickled(el));
2162
- el.toggleAttribute(`data-selected`, this.selection.selected.has(el));
2163
- if (this.interactionMode === `checked`) el.toggleAttribute(`data-checked`, !el.hasAttribute(`not-checked`) && this.selection.selected.has(el));
2164
- else el.removeAttribute(`data-checked`);
2165
- }
2166
- this.dataset.inputMode = this.tickled.inputMode;
2167
- this._syncCheckboxes();
2168
- }
2169
- _syncCheckboxes() {
2170
- const inCheckedMode = this.interactionMode === `checked`;
2171
- const wantsCustom = typeof this.checkboxRenderer === `function`;
2172
- for (const el of this.querySelectorAll(`li`)) {
2173
- const existing = el.querySelector(`.ixfx-list-checkbox`);
2174
- if (!inCheckedMode) {
2175
- if (existing) existing.remove();
2176
- continue;
2177
- }
2178
- if (el.hasAttribute(`not-checked`)) {
2179
- if (existing) existing.remove();
2180
- continue;
2181
- }
2182
- const existingIsCustom = existing?.classList.contains(`ixfx-list-checkbox-custom`) ?? false;
2183
- if (existing && existingIsCustom !== wantsCustom) existing.remove();
2184
- let cb = el.querySelector(`.ixfx-list-checkbox`);
2185
- if (!cb) {
2186
- if (wantsCustom) {
2187
- const opts = this.checkboxRenderer;
2188
- const margin = opts.margin ?? `0`;
2189
- const width = opts.width ?? `24px`;
2190
- cb = this._createCustomCheckbox(el, margin, width);
2191
- } else cb = this._createNativeCheckboxCell(el);
2192
- el.prepend(cb);
2193
- }
2194
- const checked = this.selection.selected.has(el);
2195
- if (wantsCustom) {
2196
- cb.setAttribute(`aria-checked`, String(checked));
2197
- cb.toggleAttribute(`data-checked`, checked);
2198
- if (this.checkboxRenderer) {
2199
- const renderer = typeof this.checkboxRenderer === `function` ? this.checkboxRenderer : this.checkboxRenderer.renderer;
2200
- render(renderer(checked), cb);
2201
- }
2202
- } else {
2203
- const input = cb.querySelector(`input.ixfx-list-checkbox`);
2204
- if (input) input.checked = checked;
2205
- }
2206
- }
2207
- }
2208
- /**
2209
- * Native checkbox cell: a span that stretches to the row's full cross-axis
2210
- * (so the entire column height is a click target) with a real `<input>`
2211
- * centred inside. The span owns the click; the input is purely visual and
2212
- * has `pointer-events:none` so a single click produces one toggle.
2213
- */
2214
- _createNativeCheckboxCell(el) {
2215
- const cell = document.createElement(`span`);
2216
- cell.className = `ixfx-list-checkbox ixfx-list-checkbox-native`;
2217
- 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`;
2218
- cell.tabIndex = -1;
2219
- cell.setAttribute(`role`, `checkbox`);
2220
- cell.setAttribute(`part`, `checkbox`);
2221
- cell.addEventListener(`click`, (e) => {
2222
- e.stopPropagation();
2223
- this.selection.toggle(el);
2224
- });
2225
- const input = document.createElement(`input`);
2226
- input.type = `checkbox`;
2227
- input.className = `ixfx-list-checkbox`;
2228
- input.style.cssText = `pointer-events:none;cursor:pointer;margin:0`;
2229
- cell.appendChild(input);
2230
- return cell;
2231
- }
2232
- /** Custom checkbox cell: same shape as the native cell; renderer content inside. */
2233
- _createCustomCheckbox(el, margin, width) {
2234
- const span = document.createElement(`span`);
2235
- span.className = `ixfx-list-checkbox ixfx-list-checkbox-custom`;
2236
- 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`;
2237
- span.tabIndex = -1;
2238
- span.setAttribute(`role`, `checkbox`);
2239
- span.setAttribute(`part`, `checkbox`);
2240
- span.addEventListener(`click`, (e) => {
2241
- e.stopPropagation();
2242
- this.selection.toggle(el);
2243
- });
2244
- return span;
2245
- }
2246
- _findListItem(path) {
2247
- for (const target of path) {
2248
- if (!(target instanceof Element)) continue;
2249
- if (target === this) break;
2250
- if (target.parentElement === this && target.matches(`li`)) return target;
2251
- }
2252
- }
2253
- _dispatch(name, detail) {
2254
- this.dispatchEvent(new CustomEvent(name, {
2255
- detail,
2256
- bubbles: true,
2257
- composed: true
2258
- }));
2259
- }
2260
- _openSearch() {
2261
- this._searchVisible = true;
2262
- this.updateComplete.then(() => this._searchInputEl?.focus());
2263
- }
2264
- _closeSearch() {
2265
- this._searchVisible = false;
2266
- this._searchQuery = ``;
2267
- this.#search?.clearQuery().then(() => this.#recomputeGeometry());
2268
- this.focus();
2269
- }
2270
- _activateItem(item) {
2271
- if (this.interactionMode !== `manual`) this.selection.replace(item);
2272
- 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;
2273
1624
  }
2274
1625
  static {
2275
1626
  this.styles = [
2276
- themeFallbacks,
2277
- scrollFadeStyles(`x`),
1627
+ tickledStyles,
1628
+ tickledItemStyles,
2278
1629
  css`
2279
- :host {
2280
- display: block;
2281
- position: relative;
2282
- background: var(--detail-list-bg, var(--surface-3));
2283
- color: var(--detail-list-text, var(--surface-3-text));
2284
- border-radius: var(--detail-list-radius, var(--radius-s));
2285
- border: 1px solid var(--detail-list-border, var(--border));
2286
- width: 100%;
2287
- height: 100%;
2288
- overflow: hidden;
2289
- box-sizing: border-box;
2290
- outline: none;
2291
- --ixfx-detail-list-item-padding:
2292
- var(--ixfx-detail-list-item-padding, var(--detail-list-item-padding, 0 var(--space-m, 8px)));
2293
- }
1630
+ :host {
1631
+ display: block;
1632
+ container-type: inline-size;
1633
+ }
2294
1634
 
2295
- :host(:focus-visible) {
2296
- outline: 2px solid var(--accent);
2297
- outline-offset: 2px;
2298
- }
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
+ }
2299
1643
 
2300
- .container {
2301
- position: relative;
2302
- width: 100%;
2303
- height: 100%;
2304
- }
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
+ }
2305
1653
 
2306
- ul {
2307
- list-style: none;
2308
- margin: 0;
2309
- padding: 0;
2310
- /* keep the last column clear of the scrollbar / right edge */
2311
- padding-inline-end: calc(var(--detail-list-column-gap, 8px) + 4px);
2312
- width: 100%;
2313
- height: 100%;
2314
- display: flex;
2315
- flex-flow: column wrap;
2316
- align-content: flex-start;
2317
- column-gap: var(--detail-list-column-gap, 8px);
2318
- overflow-x: auto;
2319
- overflow-y: hidden;
2320
- box-sizing: border-box;
2321
- /* Scrollbar theming: consumers override these CSS variables to control
2322
- the native scrollbar (scrollbar-width, scrollbar-gutter, scrollbar-color). */
2323
- scrollbar-width: var(--detail-list-scrollbar-width, auto);
2324
- scrollbar-gutter: var(--detail-list-scrollbar-gutter, auto);
2325
- scrollbar-color: var(--detail-list-scrollbar-color, auto);
2326
- }
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
+ }
2327
1662
 
2328
- ::slotted([hidden]) {
2329
- display: none !important;
2330
- }
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
+ }
2331
1669
 
2332
- ::slotted(li) {
2333
- box-sizing: border-box;
2334
- display: flex;
2335
- align-items: center;
2336
- flex: none;
2337
- width: var(--detail-list-column-width, 220px);
2338
- height: var(--detail-list-item-height, 28px);
2339
- padding: var(--ixfx-detail-list-item-padding);
2340
- cursor: pointer;
2341
- user-select: none;
2342
- transition: background var(--transition, 0.1s ease-out),
2343
- outline-color var(--transition, 0.1s ease-out);
2344
- outline: 1px solid transparent;
2345
- }
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
+ }
2346
1679
 
2347
- /* In checked mode, only the checkbox column is clickable; the row body
2348
- gets a default cursor so it doesn't suggest otherwise. */
2349
- :host([interaction-mode="checked"]) ::slotted(li) {
2350
- cursor: default;
2351
- }
1680
+ .pair[data-full] {
1681
+ grid-column: 1 / -1;
1682
+ }
2352
1683
 
2353
- ::slotted([data-tickled]) {
2354
- background: var(--item-bg-tickled);
2355
- outline: var(--item-border-tickled);
2356
- }
1684
+ .pair[data-full] > .value {
1685
+ grid-column: 2 / -1;
1686
+ }
2357
1687
 
2358
- ::slotted([data-selected]) {
2359
- background: var(--item-bg-selected);
2360
- color: var(--item-text-selected);
2361
- outline: 1px solid transparent;
2362
- }
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
+ }
2363
1694
 
2364
- ::slotted([data-selected][data-tickled]) {
2365
- background: var(--item-bg-selected-tickled);
2366
- outline: 2px dashed var(--surface-3);
2367
- outline-offset: -2px;
2368
- }
1695
+ .properties.stacked .pair:not(:last-child) {
1696
+ margin-bottom: var(--space-s);
1697
+ }
2369
1698
 
2370
- /* checked mode: the injected checkbox communicates selection suppress bg */
2371
- :host([interaction-mode="checked"]) ::slotted([data-selected]) {
2372
- background: transparent;
2373
- color: inherit;
2374
- outline: 1px solid transparent;
2375
- }
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
+ }
2376
1705
 
2377
- :host([interaction-mode="checked"]) ::slotted([data-selected][data-tickled]) {
2378
- background: var(--item-bg-tickled);
2379
- color: inherit;
2380
- outline: var(--item-border-tickled);
2381
- }
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
+ }
2382
1712
 
2383
- .loading {
2384
- position: absolute;
2385
- bottom: 8px;
2386
- right: 8px;
2387
- z-index: 10;
2388
- background: var(--surface-4, #2a2a2a);
2389
- border: 1px solid var(--border, #444);
2390
- border-radius: var(--radius-m, 4px);
2391
- padding: 2px 8px;
2392
- font-size: var(--text-s, 11px);
2393
- color: var(--surface-muted-text, #888);
2394
- }
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
+ }
2395
1725
 
2396
- .loading[hidden] {
2397
- display: none;
2398
- }
1726
+ .value {
1727
+ position: relative;
1728
+ padding-right: var(--space-l);
1729
+ font: 12px ui-monospace, SFMono-Regular, Menlo, monospace;
1730
+ }
2399
1731
 
2400
- .search-overlay {
2401
- position: absolute;
2402
- top: 8px;
2403
- right: 8px;
2404
- z-index: 10;
2405
- display: flex;
2406
- align-items: center;
2407
- gap: 4px;
2408
- background: var(--surface-4, #2a2a2a);
2409
- border: 1px solid var(--border, #444);
2410
- border-radius: var(--radius-m, 4px);
2411
- padding: 4px 6px;
2412
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
2413
- }
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
+ }
2414
1744
 
2415
- .search-overlay[hidden] {
2416
- display: none;
2417
- }
1745
+ .pair.tickled {
1746
+ background: var(--item-bg-tickled, color-mix(in oklch, var(--surface-2), var(--accent) 10%));
1747
+ }
2418
1748
 
2419
- .search-input {
2420
- background: transparent;
2421
- border: none;
2422
- outline: none;
2423
- color: inherit;
2424
- font-size: var(--text-m, 0.875em);
2425
- min-width: 140px;
2426
- }
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
+ }
2427
1754
 
2428
- .search-close {
2429
- background: transparent;
2430
- border: none;
2431
- color: var(--surface-muted-text, #888);
2432
- cursor: pointer;
2433
- font-size: 1.1em;
2434
- line-height: 1;
2435
- padding: 0 2px;
2436
- }
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
+ }
2437
1774
 
2438
- .search-close:hover {
2439
- color: inherit;
2440
- }
2441
- `
1775
+ .pair.show-detail .detail {
1776
+ visibility: visible;
1777
+ transform: translateY(0);
1778
+ pointer-events: auto;
1779
+ }
1780
+ `
2442
1781
  ];
2443
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
+ }
2444
1837
  render() {
2445
- const loading = this.#data.loading;
2446
- const progress = this.#data.progress;
2447
- return html`
2448
- <div class="container">
2449
- <ul part="list" class="scroll-fade-x">
2450
- <slot></slot>
2451
- </ul>
2452
- <div class="loading" part="loading" ?hidden=${!loading}>
2453
- ${loading ? progress !== void 0 ? `${Math.round(progress * 100)}%` : `loading…` : nothing}
2454
- </div>
2455
- <div
2456
- class="search-overlay"
2457
- part="search-overlay"
2458
- ?hidden=${!this._searchVisible}
2459
- >
2460
- <input
2461
- type="text"
2462
- class="search-input"
2463
- placeholder="Search…"
2464
- .value=${this._searchQuery}
2465
- @input=${this._onSearchInput}
2466
- @keydown=${this._onSearchKeydown}
2467
- />
2468
- <button class="search-close" @click=${this._closeSearch} title="Close search (Esc)">×</button>
2469
- </div>
2470
- </div>
2471
- `;
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>`;
2472
1848
  }
2473
1849
  };
2474
1850
  __decorate([property({
2475
- type: String,
2476
- attribute: `selection-mode`
2477
- })], DetailListElement.prototype, "selectionMode", void 0);
2478
- __decorate([property({
2479
- type: String,
2480
- attribute: `interaction-mode`
2481
- })], DetailListElement.prototype, "interactionMode", void 0);
2482
- __decorate([property({ type: String })], DetailListElement.prototype, "sort", void 0);
1851
+ type: Array,
1852
+ attribute: false
1853
+ })], DataTableElement.prototype, "rows", void 0);
2483
1854
  __decorate([property({
2484
- type: String,
2485
- attribute: `sort-by`
2486
- })], DetailListElement.prototype, "sortBy", void 0);
2487
- __decorate([property({ attribute: false })], DetailListElement.prototype, "sortComparator", void 0);
2488
- __decorate([property({ attribute: false })], DetailListElement.prototype, "items", void 0);
2489
- __decorate([property({ attribute: false })], DetailListElement.prototype, "provider", void 0);
1855
+ type: Number,
1856
+ attribute: false
1857
+ })], DataTableElement.prototype, "selectedIndex", void 0);
2490
1858
  __decorate([property({
2491
- type: String,
2492
- attribute: `display-property`
2493
- })], DetailListElement.prototype, "displayProperty", void 0);
2494
- __decorate([property({ attribute: false })], DetailListElement.prototype, "formatter", void 0);
1859
+ type: Number,
1860
+ attribute: `tickle-delay`
1861
+ })], DataTableElement.prototype, "tickleDelay", void 0);
2495
1862
  __decorate([property({
2496
1863
  type: String,
2497
- attribute: `icon-property`
2498
- })], DetailListElement.prototype, "iconProperty", void 0);
2499
- __decorate([property({
2500
- type: String,
2501
- attribute: `key-property`
2502
- })], DetailListElement.prototype, "keyProperty", void 0);
2503
- __decorate([property({ attribute: false })], DetailListElement.prototype, "filterPredicate", void 0);
1864
+ reflect: true
1865
+ })], DataTableElement.prototype, "layout", void 0);
2504
1866
  __decorate([property({
2505
- type: String,
2506
- attribute: `item-wrap`
2507
- })], DetailListElement.prototype, "itemWrap", void 0);
2508
- __decorate([property({ attribute: false })], DetailListElement.prototype, "checkboxRenderer", void 0);
2509
- __decorate([state()], DetailListElement.prototype, "_searchVisible", void 0);
2510
- __decorate([state()], DetailListElement.prototype, "_searchQuery", void 0);
2511
- __decorate([query(`.search-input`)], DetailListElement.prototype, "_searchInputEl", void 0);
2512
- __decorate([query(`ul`)], DetailListElement.prototype, "_listEl", void 0);
2513
- 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);
2514
1874
  //#endregion
2515
1875
  //#region src/dock/dock-drag.ts
2516
1876
  let activeDrag = null;
@@ -5801,6 +5161,136 @@ function dismiss(id) {
5801
5161
  syncRenderer();
5802
5162
  }
5803
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
5804
5294
  //#region src/panel/panel-group.ts
5805
5295
  let PanelGroupElement = class PanelGroupElement extends LitElement {
5806
5296
  constructor(..._args) {
@@ -8491,6 +7981,6 @@ function init() {
8491
7981
  console.log(`Init`);
8492
7982
  }
8493
7983
  //#endregion
8494
- 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, 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, TimelineController, TimelineTrackTabElement, TitlebarElement, ToolbarElement, ToolbarGroupElement, TooltipElement, TransitoryLabelElement, TreeBaseElement, TreeController, TreeDataModel, 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, isSplit, isStack, monitorIndeterminate, moveLeafNodeToEdge, moveLeafStep, normalise, normalize, parseColourScaleAttr, parseLayout, pointerOverAnyHost, readBaseColour, registerFontIcon, registerIcon, removePanelFromStack, resolveCssVars, resolveGrid, resolveLeafPath, resolveTracks, serializeLayout, setActive, setIconFont, svgToDataUri, swapLeaves, 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 };
8495
7985
 
8496
7986
  //# sourceMappingURL=index.js.map