@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
@@ -23,6 +23,7 @@ import '@ixfx/components/data-grid';
23
23
  - [dock](docs-user/dock.md): Dockable, movable panels like VS Code / Photoshop. Drag a panel by its
24
24
  - [editable-label](docs-user/editable-label.md): Inline editable text components. `ixfx-editable-number` extends `ixfx-editable-label` with drag-to-adjust and fill-bar visualization.
25
25
  - [form](docs-user/form.md): Primitives for typical form layout with responsive label positioning and data binding.
26
+ - [grid-list](docs-user/grid-list.md): A virtualised grid of items — thumbnails, cards, or arbitrary slotted markup —
26
27
  - [grouped-item-lister](docs-user/grouped-item-lister.md): A meta-component that renders a typed item list using any sub-component you supply. Items can be displayed in a single sub-component (_ungrouped_ mode) or split into labelled groups, each with its own
27
28
  - [icons](docs-user/icons.md): A centralized SVG icon system for `@ixfx/components`. Icons are stored by name, can be overridden globally, and all components that use a given icon re-render automatically when it changes.
28
29
  - [incr-search](docs-user/incr-search.md): Utilities for incremental (type-ahead) search over data collections or DOM-backed lists.
@@ -59,5 +60,6 @@ import '@ixfx/components/data-grid';
59
60
  - [xy-pad](docs-user/xy-pad.md): A 2D touch-friendly XY pad for controlling two parameters simultaneously. Each axis supports independent linear/logarithmic scaling.
60
61
 
61
62
  ## Overviews
63
+ - [infra-tickled.md](docs-user/infra-tickled.md)
62
64
  - [user-catalog.md](docs-user/user-catalog.md)
63
65
  - [user-theming.md](docs-user/user-theming.md)
package/docs-user/tree.md CHANGED
@@ -463,6 +463,18 @@ Each column is a separate `role="listbox"` with `aria-label` showing the parent
463
463
 
464
464
  ---
465
465
 
466
+ ## Row animation
467
+
468
+ Rows that appear — a branch expanded, a node added to `root` / the model —
469
+ collapse + fade in, and the rows below slide down to make room. Rows that
470
+ disappear — a branch collapsed, a node removed — are kept as fading "ghost" rows
471
+ until they've collapsed out, so the gap closes smoothly. Above ~50 rows changing
472
+ at once the animation is skipped, as it is entirely under
473
+ `prefers-reduced-motion: reduce`. Duration: `--ixfx-list-item-move-duration`
474
+ (default `200ms`).
475
+
476
+ ---
477
+
466
478
  ## CSS variables
467
479
 
468
480
  ### Shared / themed
@@ -493,6 +505,7 @@ These are set by the global theme and inherited by all tree components.
493
505
  | `--tree-indent` | `20px` | Indentation per depth level |
494
506
  | `--tree-item-height` | `28px` | Height of each row |
495
507
  | `--tree-caret-size` | `10px` | Expand/collapse caret size |
508
+ | `--ixfx-list-item-move-duration` | `200ms` | Row enter / leave animation duration |
496
509
 
497
510
  ### `ixfx-miller-list` specific
498
511
 
@@ -313,13 +313,21 @@ list.addItem(myElement);
313
313
  // In checked mode, add a row that shows no checkbox and can't be checked
314
314
  list.addItem('Header', { notChecked: true });
315
315
 
316
- // Remove a specific element
316
+ // Remove a specific element (returns a Promise that resolves once it's gone)
317
317
  list.removeItem(element);
318
+ list.removeItems(list.selectedItems);
318
319
 
319
320
  // Remove all items and clear selection
320
321
  list.clearItems();
321
322
  ```
322
323
 
324
+ Items added mid-list — via `addItem`, a direct `appendChild`, or a framework
325
+ binding — collapse + fade in, and the rows below slide down to make room.
326
+ `removeItem` / `removeItems` play the reverse (collapse + fade out) and then
327
+ detach, so the gap closes smoothly. A bulk populate isn't animated, and
328
+ everything is skipped under `prefers-reduced-motion: reduce`. Tune the timing
329
+ with the `--ixfx-list-item-move-duration` custom property (default `200ms`).
330
+
323
331
  ### Reading the selection
324
332
 
325
333
  ```typescript
@@ -341,6 +349,48 @@ list.addEventListener('list-select', ({ detail }) => {
341
349
  });
342
350
  ```
343
351
 
352
+ ### Key-based selection
353
+
354
+ `Element` references die the moment you rebuild the item set (re-fetch, external
355
+ re-sort, filter). Give each item a **`data-key`** (or `data-value`) and drive
356
+ selection by that stable string instead. The Element API keeps working
357
+ unchanged — the key API is additive.
358
+
359
+ ```html
360
+ <ixfx-vertical-list selection-mode="multiple">
361
+ <li data-key="a">Apple</li>
362
+ <li data-key="b">Banana</li>
363
+ <li data-key="c">Cherry</li>
364
+ </ixfx-vertical-list>
365
+ ```
366
+
367
+ ```typescript
368
+ list.selectKeys(['a', 'c']); // replace selection; unknown keys ignored
369
+ list.addSelectedKeys(['b']); // multi-select modes only
370
+ list.removeSelectedKeys(['a']);
371
+ list.selectedKeys; // ReadonlySet<string>, e.g. Set { 'b', 'c' }
372
+
373
+ list.focusKey('b'); // move the cursor to an item + scroll it into view
374
+ list.tickledKey; // key under the cursor, or undefined
375
+
376
+ // list-select carries keys too
377
+ list.addEventListener('list-select', ({ detail }) => {
378
+ console.log([...detail.selectedKeys]);
379
+ });
380
+ ```
381
+
382
+ Restore a selection across a data reload:
383
+
384
+ ```typescript
385
+ const keep = [...list.selectedKeys];
386
+ list.setItems(await fetchRows()); // Element refs are now stale
387
+ list.selectKeys(keep); // ...but the keys still resolve
388
+ ```
389
+
390
+ The `data-key` requirement is documented, not enforced: items without a key
391
+ simply never appear in `selectedKeys` and can't be targeted by `selectKeys` /
392
+ `focusKey`.
393
+
344
394
  ### Filtering by state
345
395
 
346
396
  Use `selectedFilter()` to iterate over items based on their selection or checked state. Pass `'selected'`, `'checked'`, or `'either'` to filter accordingly:
package/llms.txt CHANGED
@@ -23,6 +23,7 @@ import '@ixfx/components/data-grid';
23
23
  - [dock](docs-user/dock.md): Dockable, movable panels like VS Code / Photoshop. Drag a panel by its
24
24
  - [editable-label](docs-user/editable-label.md): Inline editable text components. `ixfx-editable-number` extends `ixfx-editable-label` with drag-to-adjust and fill-bar visualization.
25
25
  - [form](docs-user/form.md): Primitives for typical form layout with responsive label positioning and data binding.
26
+ - [grid-list](docs-user/grid-list.md): A virtualised grid of items — thumbnails, cards, or arbitrary slotted markup —
26
27
  - [grouped-item-lister](docs-user/grouped-item-lister.md): A meta-component that renders a typed item list using any sub-component you supply. Items can be displayed in a single sub-component (_ungrouped_ mode) or split into labelled groups, each with its own
27
28
  - [icons](docs-user/icons.md): A centralized SVG icon system for `@ixfx/components`. Icons are stored by name, can be overridden globally, and all components that use a given icon re-render automatically when it changes.
28
29
  - [incr-search](docs-user/incr-search.md): Utilities for incremental (type-ahead) search over data collections or DOM-backed lists.
@@ -59,5 +60,6 @@ import '@ixfx/components/data-grid';
59
60
  - [xy-pad](docs-user/xy-pad.md): A 2D touch-friendly XY pad for controlling two parameters simultaneously. Each axis supports independent linear/logarithmic scaling.
60
61
 
61
62
  ## Overviews
63
+ - [infra-tickled.md](docs-user/infra-tickled.md)
62
64
  - [user-catalog.md](docs-user/user-catalog.md)
63
65
  - [user-theming.md](docs-user/user-theming.md)
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ixfx/components",
3
3
  "type": "module",
4
- "version": "0.7.2",
4
+ "version": "0.7.4",
5
5
  "packageManager": "pnpm@10.0.0",
6
6
  "description": "Web components for ixfx — Lit-based UI toolkit",
7
7
  "author": "Clint Heyer",
@@ -20,6 +20,18 @@
20
20
  "types": "./dist/incr-search.d.ts",
21
21
  "import": "./dist/incr-search.js"
22
22
  },
23
+ "./grid-list": {
24
+ "types": "./dist/grid-list.d.ts",
25
+ "import": "./dist/grid-list.js"
26
+ },
27
+ "./detail-list": {
28
+ "types": "./dist/detail-list.d.ts",
29
+ "import": "./dist/detail-list.js"
30
+ },
31
+ "./vertical-list": {
32
+ "types": "./dist/vertical-list.d.ts",
33
+ "import": "./dist/vertical-list.js"
34
+ },
23
35
  "./*.js": {
24
36
  "types": "./dist/*.d.ts",
25
37
  "import": "./dist/*.js"
@@ -35,6 +47,7 @@
35
47
  "engines": { "node": ">=20" },
36
48
  "scripts": {
37
49
  "typecheck": "tsc --noEmit",
50
+ "test": "vitest run",
38
51
  "start": "vite serve",
39
52
  "dev": "vite serve",
40
53
  "lint": "eslint",
@@ -76,11 +89,13 @@
76
89
  "eslint": "10.8.1",
77
90
  "eslint-plugin-format": "2.0.1",
78
91
  "globals": "17.9.0",
92
+ "happy-dom": "^20.14.0",
79
93
  "knip": "6.32.0",
80
94
  "lit": "3.3.3",
81
95
  "tsdown": "0.22.14",
82
96
  "typescript": "npm:@typescript/typescript6@6.0.2",
83
97
  "typescript-eslint": "8.66.0",
84
- "vite": "8.2.1"
98
+ "vite": "8.2.1",
99
+ "vitest": "^5.0.0"
85
100
  }
86
101
  }
@@ -1 +0,0 @@
1
- {"version":3,"file":"data-grid-Bh4YrQGK.d.ts","names":[],"sources":["../src/data-grid/types.ts","../src/data-grid/data-grid.ts"],"mappings":";;;KAAY;KACA;KACA;KACA;WAA+B;WAAsB,WAAW;;KAChE;KAEA,sBAAsB;WACvB,KAAK;WACL,QAAQ,eAAe;WACvB;WACA;WACA;EACT,SAAS;EACT;;KAEU,sBAAsB,gBAAgB,KAAK,sBAAsB;KAEjE,eAAe;EACzB;EACA;EACA;EACA;EACA;EACA;EACA;EACA,UAAU,GAAG,GAAG,GAAG;EACnB;EACA;EACA;EACA,YAAY,KAAK,GAAG,KAAK,eAAe,IAAI;EAC5C;EACA;EACA;EACA,SAAS,qBAAqB,sBAAsB;EACpD,gBAAgB;IAAyB;IAAe;;EACxD,aAAa,gBAAgB,KAAK,GAAG,KAAK,eAAe;;KAG/C,mBAAmB,gBAAgB,oBAAoB;KAEvD;KAEA;KACA;EAA4B;;KAE5B,kBAAkB,MAAM,KAAK,GAAG;KAChC,gBAAgB,gBAAgB,KAAK;KACrC,sBAAsB,kBAAkB;;;KClCxC;WAAgC,MAAM;WAA4B,UAAU;;KAC5E,oBAAoB,YAAY;KAChC;WAAkC,cAAc;WAA8B,UAAU;;KACxF,sBAAsB,YAAY;KAClC;WAAyC;WAAuB;WAAqB,kBAAkB;;KACvG,6BAA6B,YAAY;KACzC;WAAwC;WAAsB;;KAC9D,4BAA4B,YAAY;KACxC;WAA4C;WAAsB;;KAClE,gCAAgC,YAAY;KAC5C;WAAqC;WAAyB;WAAyB;WAA2B;;KAClH,yBAAyB,YAAY;KACrC;WAAoC;WAAyB;WAAyB;WAA4B;;KAClH,wBAAwB,YAAY;KAEpC;WAAuC,SAAS;;KAChD,2BAA2B,YAAY;UAClC;EACf,iBAAiB;EACjB,mBAAmB;EACnB,2BAA2B;EAC3B,0BAA0B;EAC1B,8BAA8B;EAC9B,uBAAuB;EACvB,sBAAsB;EACtB,yBAAyB;;cAId,wBAAwB;SACnB,sBAAM;EAoFK,SAAS;EACT;EAC8B,eAAe;EACb,iBAAiB;EAC5C,WAAW;EACU;EACrB,WAAW;EACX,oBAAoB;EACpB,sBAAsB,cAAc,KAAK;EAC9B,aAAa;EAExB,SAAS;EACiB;EAC1B,YAAY;;EAEZ;EACgD;EAChD,kBAAkB;UAC1C;UAES;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UAEW;UACK;UAEzB;UAKA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;EAEC;EAQA;UAOD;YAgBW;YAaA,QAAQ,SAAS;YAmBjB,WAAW,SAAS;UAmB/B;UAQA;UAiCA;UAOA;UAoBA;UAYA;UAcA;UA0BA;UAKA;UAMA;UAsFA;UAYA;UAEA;UAcA;UAKA;UAiBA;UAsBA;UAmCA;UAsKA;UAUA;UAKA;UAQA;;EAeR,UAAU,gBAAgB;;EAK1B,aAAa;EAWb;EAIA,mBAAmB,mBAAmB,aAAa,8BAA8B,6BAA6B,UAAU;UAkBhH;UAWA;UAYA;UAyBA;UACA;UA2BA;UAYA;UAkCA;EAoBR;IAAqB;IAAkB;IAAgB;IAAkB;;UASjE;UAaA;YAQW,wBAAM;;QAsMnB;YACI;IAAwB,kBAAkB;;;UAErC;EACf,oBAAoB,gBAAgB,yBAAyB,MAAM,GAAG,WAAW,MAAM,iBAAiB,OAAO,wBAAwB,aAAa,oBAAoB,sCAAsC,cAAc,UAAU,oCAAoC,oBAAoB;EAC9R,uBAAuB,gBAAgB,yBAAyB,MAAM,GAAG,WAAW,MAAM,iBAAiB,OAAO,wBAAwB,aAAa,oBAAoB,mCAAmC,cAAc,UAAU,oCAAoC,oBAAoB"}
@@ -1,2 +0,0 @@
1
- import "./tickled-controller-h9GmJ_bU.js";
2
- import "./tickled-styles-BrAHEj1q.js";
@@ -1 +0,0 @@
1
- {"version":3,"file":"list-selection-types-DSuRNWpx.d.ts","names":[],"sources":["../src/interaction/list-selection-types.ts"],"mappings":";;;;;;;;;KASY;;;;;;;;;;;;;KAcA;KAEA;WACD,UAAU,YAAY;WACtB,UAAU,YAAY;;KAGrB;WACD,MAAM;;KAGL;WACD,MAAM;;KAGL;WACD,MAAM;;KAGL,kBAAkB,YAAY;KAC9B,kBAAkB,YAAY;KAC9B,oBAAoB,YAAY;KAChC,qBAAqB,YAAY;UAE5B;EACf,eAAe;EACf,eAAe;EACf,iBAAiB;EACjB,mBAAmB"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"menu-CXDFJxNm.js","names":["customElement","customElement","customElement","customElement","customElement","customElement","customElement"],"sources":["../src/menu/menu-bar.ts","../src/menu/menu-check-item.ts","../src/menu/menu-command-controller.ts","../src/menu/menu-sub-trigger.ts","../src/menu/menu-separator.ts","../src/menu/menu-header.ts","../src/menu/menu-from-tree.ts","../src/menu/menu-radio-item.ts","../src/menu/menu-radio-group.ts","../src/menu/index.ts"],"sourcesContent":["import type { MenuTrigger } from './menu-trigger.js';\nimport { css, html, LitElement } from 'lit';\nimport { safeCustomElement as customElement } from '../util/custom-elements.js';\nimport './menu-trigger.js';\n\n@customElement(`ixfx-menu-bar`)\nexport class MenuBar extends LitElement {\n private _openTrigger: MenuTrigger | null = null;\n private _inputMode: `keyboard` | `mouse` = `mouse`;\n\n connectedCallback(): void {\n super.connectedCallback();\n this.addEventListener(`menu-trigger-open`, this._handleTriggerOpen as EventListener);\n this.addEventListener(`menu-trigger-close`, this._handleTriggerClose as EventListener);\n this.addEventListener(`focusin`, this._handleFocusIn as EventListener);\n this.addEventListener(`keydown`, this._handleKeydown);\n this.addEventListener(`pointermove`, this._handlePointerMove);\n }\n\n disconnectedCallback(): void {\n super.disconnectedCallback();\n this.removeEventListener(`menu-trigger-open`, this._handleTriggerOpen as EventListener);\n this.removeEventListener(`menu-trigger-close`, this._handleTriggerClose as EventListener);\n this.removeEventListener(`focusin`, this._handleFocusIn as EventListener);\n this.removeEventListener(`keydown`, this._handleKeydown);\n this.removeEventListener(`pointermove`, this._handlePointerMove);\n }\n\n private _setInputMode(mode: `keyboard` | `mouse`): void {\n if (this._inputMode === mode)\n return;\n this._inputMode = mode;\n this.setAttribute(`data-input-mode`, mode);\n }\n\n private _handlePointerMove = (): void => {\n this._setInputMode(`mouse`);\n };\n\n private _getTriggers(): MenuTrigger[] {\n return [...this.children].filter(\n el => el.tagName === `IXFX-MENU-TRIGGER`,\n ) as MenuTrigger[];\n }\n\n private _handleTriggerOpen = (e: Event): void => {\n this._openTrigger = e.target as MenuTrigger;\n };\n\n private _handleTriggerClose = (e: Event): void => {\n if (e.target === this._openTrigger) {\n this._openTrigger = null;\n }\n };\n\n // When focus moves to a trigger button while another menu is open, switch menus.\n // focusin is composed, so e.target is retargeted to the ixfx-menu-trigger host.\n private _handleFocusIn = (e: FocusEvent): void => {\n const triggers = this._getTriggers();\n const trigger = triggers.find(t => t === e.target);\n if (!trigger || !this._openTrigger || trigger === this._openTrigger)\n return;\n this._openTrigger.closeMenu();\n trigger.openMenu();\n };\n\n private _handleKeydown = (e: KeyboardEvent): void => {\n if (e.key !== `ArrowLeft` && e.key !== `ArrowRight`)\n return;\n\n const triggers = this._getTriggers();\n if (triggers.length === 0)\n return;\n\n // When a menu is open, navigate from the open trigger.\n // When no menu is open, navigate from the focused trigger button\n // (e.target is retargeted to ixfx-menu-trigger when focus is on its inner button).\n const current = this._openTrigger ?? triggers.find(t => t === e.target);\n if (!current)\n return;\n\n const idx = triggers.indexOf(current);\n if (idx === -1)\n return;\n\n e.preventDefault();\n e.stopPropagation();\n this._setInputMode(`keyboard`);\n\n const dir = e.key === `ArrowRight` ? 1 : -1;\n const next = triggers[(idx + dir + triggers.length) % triggers.length];\n\n if (this._openTrigger) {\n this._openTrigger.closeMenu();\n next.openMenu();\n } else {\n next.focusTrigger();\n }\n };\n\n static styles = css`\n :host {\n display: flex;\n align-items: center;\n }\n\n /* Keyboard mode: suppress hover highlight on trigger buttons */\n :host([data-input-mode=\"keyboard\"]) {\n --_trigger-hover-bg: transparent;\n --_trigger-hover-opacity: 0.6;\n }\n `;\n\n render() {\n return html`<slot role=\"menubar\"></slot>`;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'ixfx-menu-bar': MenuBar;\n }\n}\n","import type { CommandRegistry, KeyboardManager } from '@clinth/ui-commands';\nimport { css, html, LitElement } from 'lit';\nimport { property } from 'lit/decorators.js';\nimport { tickledItemStyles } from '../interaction/index.js';\nimport { themeFallbacks } from '../styles/fallbacks.js';\nimport { safeCustomElement as customElement } from '../util/custom-elements.js';\nimport '../icons/icon.js';\n\nexport type MenuCheckItemCommandDetail = {\n readonly command: string;\n readonly checked: boolean;\n readonly args: string;\n};\n\nexport type MenuCheckItemCommandEvent = CustomEvent<MenuCheckItemCommandDetail>;\n\n@customElement(`ixfx-menu-check-item`)\nexport class MenuCheckItem extends LitElement {\n @property({ type: String }) command = ``;\n @property({ type: String, attribute: `command-args` }) commandArgs = ``;\n @property({ type: String }) label = ``;\n @property({ type: Boolean }) checked = false;\n @property({ type: Boolean }) disabled = false;\n @property({ type: String, attribute: `icon-name` }) iconName = ``;\n @property({ attribute: false }) registry?: CommandRegistry<unknown>;\n @property({ attribute: false }) keyboardManager?: KeyboardManager;\n\n private handleClick(): void {\n if (this.disabled)\n return;\n\n this.checked = !this.checked;\n this.dispatchEvent(new CustomEvent<MenuCheckItemCommandDetail>(`command`, {\n detail: { command: this.command, checked: this.checked, args: this.commandArgs },\n bubbles: true,\n composed: true,\n }));\n }\n\n private _resolvedLabel(): string {\n if (this.label)\n return this.label;\n if (this.registry && this.command) {\n return this.registry.get(this.command)?.label ?? ``;\n }\n return ``;\n }\n\n private _resolvedIcon(): string | null {\n if (this.iconName)\n return this.iconName;\n if (this.registry && this.command) {\n return this.registry.get(this.command)?.icon ?? null;\n }\n return null;\n }\n\n willUpdate(changedProperties: Map<string, unknown>): void {\n super.willUpdate(changedProperties);\n const resolvedIcon = this._resolvedIcon();\n if (resolvedIcon && !this.iconName) {\n this.setAttribute(`data-icon-section`, ``);\n } else if (!resolvedIcon && !this.iconName) {\n this.removeAttribute(`data-icon-section`);\n }\n }\n\n updated(changedProperties: Map<string, unknown>): void {\n super.updated(changedProperties);\n }\n\n private _getShortcut(): string | null {\n if (!this.keyboardManager || !this.command)\n return null;\n const bindings = (this.keyboardManager as any).getBindings?.() ?? [];\n const binding = bindings.find((b: any) => b.commandId === this.command);\n if (!binding)\n return null;\n const mods = Array.from(binding.modifiers ?? [], (m: string) =>\n ({ ctrl: `⌃`, shift: `⇧`, alt: `⌥`, meta: `⌘` })[m] ?? m);\n const key = binding.key?.length === 1 ? binding.key.toUpperCase() : binding.key;\n return [...mods, key].join(``);\n }\n\n static override styles = [themeFallbacks, tickledItemStyles, css`\n :host {\n display: block;\n }\n\n .item {\n display: flex;\n padding: 6px var(--space-m);\n font-size: var(--text-m);\n }\n\n :host(:focus) {\n outline: none;\n }\n\n .item.disabled {\n opacity: var(--disabled-opacity);\n cursor: default;\n }\n\n .item.disabled:hover {\n background: transparent;\n }\n\n .check {\n width: 16px;\n margin-right: var(--space-s);\n font-size: var(--text-m);\n color: var(--accent);\n }\n\n .icon-slot {\n width: 0;\n overflow: hidden;\n flex-shrink: 0;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n }\n\n :host([icon-name]) .icon-slot,\n :host([data-icon-section]) .icon-slot {\n width: 1em;\n height: 1em;\n margin-right: var(--space-s);\n }\n\n .label {\n flex: 1;\n }\n\n .shortcut {\n margin-left: var(--space-l);\n color: var(--surface-muted-text);\n font-size: var(--text-s);\n }\n `];\n\n render() {\n const shortcut = this._getShortcut();\n const resolvedIcon = this._resolvedIcon();\n return html`\n <div \n class=\"item ${this.disabled ? `disabled` : ``}\"\n @click=${this.handleClick}\n tabindex=\"${this.disabled ? -1 : 0}\"\n role=\"menuitemcheckbox\"\n aria-checked=\"${this.checked}\"\n >\n <span class=\"check\">${this.checked ? `✓` : ``}</span>\n <span class=\"icon-slot\">${resolvedIcon ? html`<ixfx-icon name=\"${resolvedIcon}\"></ixfx-icon>` : null}</span>\n <span class=\"label\">${this._resolvedLabel()}</span>\n ${shortcut ? html`<span class=\"shortcut\">${shortcut}</span>` : null}\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'ixfx-menu-check-item': MenuCheckItem;\n }\n}\n","import type { CommandRegistry } from '@clinth/ui-commands';\n\nexport class MenuCommandController {\n private registry: CommandRegistry<unknown>;\n private target: EventTarget;\n private boundHandler: (e: Event) => void;\n\n constructor(options: { registry: CommandRegistry<unknown>; target?: EventTarget }) {\n this.registry = options.registry;\n this.target = options.target ?? document;\n this.boundHandler = this.handleCommand.bind(this);\n }\n\n start(): void {\n this.target.addEventListener(`command`, this.boundHandler);\n }\n\n stop(): void {\n this.target.removeEventListener(`command`, this.boundHandler);\n }\n\n private handleCommand(e: Event): void {\n const target = e.target as any;\n const command = target?.command;\n if (!command)\n return;\n\n const args = target?.commandArgs ?? ``;\n this.registry.invoke(command, { args });\n }\n}\n","import type { CommandRegistry, KeyboardManager } from '@clinth/ui-commands';\nimport { css, html, LitElement, nothing } from 'lit';\nimport { property, query, state } from 'lit/decorators.js';\nimport { tickledItemStyles } from '../interaction/index.js';\nimport { themeFallbacks } from '../styles/fallbacks.js';\nimport { safeCustomElement as customElement } from '../util/custom-elements.js';\nimport '../icons/icon.js';\n\n@customElement(`ixfx-menu-sub-trigger`)\nexport class MenuSubTrigger extends LitElement {\n @property({ type: String }) command = ``;\n @property({ type: String, attribute: `command-args` }) commandArgs = ``;\n @property({ type: String }) label = ``;\n @property({ type: Boolean }) disabled = false;\n @property({ type: String, attribute: `icon-name` }) iconName = ``;\n @property({ attribute: false }) registry?: CommandRegistry<unknown>;\n @property({ attribute: false }) keyboardManager?: KeyboardManager;\n\n @state() private _hasSubmenu = false;\n @query(`slot[name=\"submenu\"]`) private _slot!: HTMLSlotElement;\n @query(`.submenu-popover`) private _submenuPopover!: HTMLElement;\n @query(`.item`) private _itemEl!: HTMLElement;\n\n private static _count = 0;\n private _anchorName = `--mst-${MenuSubTrigger._count++}`;\n private _hoverTimeout: number | null = null;\n private _hideTimeout: number | null = null;\n\n connectedCallback(): void {\n super.connectedCallback();\n this.addEventListener(`keydown`, this._handleKeydown);\n }\n\n firstUpdated(): void {\n this.style.setProperty(`--anchor-name`, this._anchorName);\n }\n\n disconnectedCallback(): void {\n super.disconnectedCallback();\n this.removeEventListener(`keydown`, this._handleKeydown);\n if (this._hoverTimeout) {\n clearTimeout(this._hoverTimeout);\n }\n if (this._hideTimeout) {\n clearTimeout(this._hideTimeout);\n }\n }\n\n private _propagateContext(): void {\n const items = this._slot?.assignedElements({ flatten: true }) ?? [];\n for (const el of items) {\n if (`registry` in el)\n (el as any).registry = this.registry;\n if (`keyboardManager` in el)\n (el as any).keyboardManager = this.keyboardManager;\n }\n }\n\n override updated(changedProperties: Map<string, unknown>): void {\n if (changedProperties.has(`registry`) || changedProperties.has(`keyboardManager`)) {\n this._propagateContext();\n }\n }\n\n willUpdate(changedProperties: Map<string, unknown>): void {\n super.willUpdate(changedProperties);\n const resolvedIcon = this._resolvedIcon();\n if (resolvedIcon && !this.iconName) {\n this.setAttribute(`data-icon-section`, ``);\n } else if (!resolvedIcon && !this.iconName) {\n this.removeAttribute(`data-icon-section`);\n }\n }\n\n private _resolvedIcon(): string | null {\n if (this.iconName)\n return this.iconName;\n if (this.registry && this.command) {\n return this.registry.get(this.command)?.icon ?? null;\n }\n return null;\n }\n\n private _handleSlotChange(): void {\n this._hasSubmenu = this._slot?.assignedElements().length > 0;\n this._propagateContext();\n }\n\n private _showSubmenu(): void {\n if (!this._submenuPopover?.popover)\n return;\n try {\n (this._submenuPopover as any).showPopover();\n } catch { /* ignore */ }\n }\n\n private _hideSubmenu(): void {\n if (!this._submenuPopover?.popover)\n return;\n try {\n (this._submenuPopover as any).hidePopover();\n } catch { /* ignore */ }\n }\n\n private _cancelHide(): void {\n if (this._hideTimeout) {\n clearTimeout(this._hideTimeout);\n this._hideTimeout = null;\n }\n }\n\n private _scheduleHide(): void {\n this._cancelHide();\n this._hideTimeout = window.setTimeout(() => {\n this._hideSubmenu();\n this._hideTimeout = null;\n }, 100);\n }\n\n private _handleMouseEnter(): void {\n if (this.disabled || !this._hasSubmenu)\n return;\n this._cancelHide();\n if (this._hoverTimeout) {\n clearTimeout(this._hoverTimeout);\n }\n this._hoverTimeout = window.setTimeout(() => {\n this._showSubmenu();\n }, 150);\n }\n\n private _handleMouseLeave(): void {\n if (this._hoverTimeout) {\n clearTimeout(this._hoverTimeout);\n this._hoverTimeout = null;\n }\n this._scheduleHide();\n }\n\n private _handleSubmenuMouseEnter(): void {\n this._cancelHide();\n }\n\n private _handleSubmenuMouseLeave(): void {\n this._scheduleHide();\n }\n\n private _handleClick(): void {\n if (this.disabled || !this.command)\n return;\n\n this.dispatchEvent(new CustomEvent(`command`, {\n detail: { command: this.command, args: this.commandArgs },\n bubbles: true,\n composed: true,\n }));\n }\n\n private _handleKeydown(e: KeyboardEvent): void {\n if (this.disabled)\n return;\n\n switch (e.key) {\n case `ArrowRight`:\n case `Enter`:\n case ` `:\n e.preventDefault();\n e.stopPropagation();\n if (this._hasSubmenu) {\n this._showSubmenu();\n requestAnimationFrame(() => {\n const container = this._slot?.assignedElements({ flatten: true })\n .find(el => el.tagName === `IXFX-MENU-CONTAINER`) as (HTMLElement & { focusFirst: () => void }) | undefined;\n container?.focusFirst();\n });\n }\n break;\n case `ArrowLeft`:\n e.preventDefault();\n e.stopPropagation();\n this._hideSubmenu();\n this._itemEl?.focus();\n break;\n case `Escape`:\n e.preventDefault();\n e.stopPropagation();\n this._hideSubmenu();\n break;\n }\n }\n\n static override styles = [themeFallbacks, tickledItemStyles, css`\n :host {\n display: block;\n }\n\n .item {\n anchor-name: var(--anchor-name);\n display: flex;\n padding: 6px var(--space-m);\n font-size: var(--text-m);\n }\n\n .submenu-popover {\n position: fixed;\n position-anchor: var(--anchor-name);\n inset: auto;\n top: anchor(top);\n left: anchor(right);\n margin: 0;\n padding: 0;\n border: none;\n background: transparent;\n /* UA stylesheet sets overflow:auto on [popover], which clips the box-shadow\n of the inner ixfx-menu-container child. Override to let the shadow render. */\n overflow: visible;\n position-try-fallbacks: flip-inline, flip-block, flip-block flip-inline;\n }\n\n :host(:focus) {\n outline: none;\n }\n\n .item.disabled {\n opacity: var(--disabled-opacity);\n cursor: default;\n }\n\n .item.disabled:hover {\n background: transparent;\n }\n\n .icon-slot {\n width: 0;\n overflow: hidden;\n flex-shrink: 0;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n }\n\n :host([icon-name]) .icon-slot,\n :host([data-icon-section]) .icon-slot {\n width: 1em;\n height: 1em;\n margin-right: var(--space-s);\n }\n\n .label {\n flex: 1;\n }\n\n .arrow {\n color: var(--surface-muted-text);\n margin-left: var(--space-m);\n }\n `];\n\n render() {\n const resolvedIcon = this._resolvedIcon();\n return html`\n <div\n class=\"item ${this.disabled ? `disabled` : ``}\"\n @click=${this._handleClick}\n @mouseenter=${this._handleMouseEnter}\n @mouseleave=${this._handleMouseLeave}\n tabindex=\"${this.disabled ? -1 : 0}\"\n role=\"menuitem\"\n aria-haspopup=\"true\"\n aria-expanded=\"false\"\n >\n <span class=\"icon-slot\">${resolvedIcon ? html`<ixfx-icon name=\"${resolvedIcon}\"></ixfx-icon>` : null}</span>\n <span class=\"label\"><slot></slot>${this.label}</span>\n <span class=\"arrow\">${this._hasSubmenu ? html`<ixfx-icon name=\"chevron-right\"></ixfx-icon>` : nothing}</span>\n </div>\n <div class=\"submenu-popover\" popover=\"auto\"\n @mouseenter=${this._handleSubmenuMouseEnter}\n @mouseleave=${this._handleSubmenuMouseLeave}\n >\n <slot name=\"submenu\" @slotchange=${this._handleSlotChange}></slot>\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'ixfx-menu-sub-trigger': MenuSubTrigger;\n }\n}\n","import { css, html, LitElement } from 'lit';\nimport { themeFallbacks } from '../styles/fallbacks.js';\nimport { safeCustomElement as customElement } from '../util/custom-elements.js';\n\n@customElement(`ixfx-menu-separator`)\nexport class MenuSeparator extends LitElement {\n static styles = [themeFallbacks, css`\n :host {\n display: block;\n margin: var(--space-xs) 0;\n }\n\n .separator {\n height: 1px;\n background: var(--surface-4);\n }\n `];\n\n render() {\n return html`<div class=\"separator\"></div>`;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'ixfx-menu-separator': MenuSeparator;\n }\n}\n","import { css, html, LitElement } from 'lit';\nimport { property } from 'lit/decorators.js';\nimport { themeFallbacks } from '../styles/fallbacks.js';\nimport { safeCustomElement as customElement } from '../util/custom-elements.js';\n\n@customElement(`ixfx-menu-header`)\nexport class MenuHeader extends LitElement {\n @property({ type: String }) label = ``;\n\n static styles = [themeFallbacks, css`\n :host {\n display: block;\n padding: 6px var(--space-m) var(--space-xs);\n font-size: var(--text-s);\n font-weight: var(--font-weight-bold);\n text-transform: uppercase;\n color: var(--surface-muted-text);\n letter-spacing: 0.5px;\n }\n `];\n\n render() {\n return html`<div>${this.label}</div>`;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'ixfx-menu-header': MenuHeader;\n }\n}\n","import type { CommandRegistry, KeyboardManager } from '@clinth/ui-commands';\nimport type { TemplateResult } from 'lit';\nimport type { TreeNode } from '../tree/types.js';\nimport type { MenuTriggerCommandDetail } from './menu-trigger.js';\nimport { html, nothing } from 'lit';\nimport './menu-container.js';\nimport './menu-item.js';\nimport './menu-sub-trigger.js';\nimport './menu-separator.js';\nimport './menu-header.js';\n\nexport type MenuFromTreeOptions = {\n onCommand?: (detail: MenuTriggerCommandDetail) => void;\n registry?: CommandRegistry<unknown>;\n keyboardManager?: KeyboardManager;\n};\n\nfunction isLeaf(node: TreeNode): boolean {\n return node.children === undefined || node.children.length === 0;\n}\n\nexport function menuFromTree(\n nodes: TreeNode[],\n options: MenuFromTreeOptions = {},\n): TemplateResult {\n const { onCommand, registry, keyboardManager } = options;\n\n return html`${nodes.map((node) => {\n const label = node.item.label;\n\n if (label === `-`) {\n return html`<ixfx-menu-separator></ixfx-menu-separator>`;\n }\n\n if (label.startsWith(`# `)) {\n return html`<ixfx-menu-header label=\"${label.slice(2)}\"></ixfx-menu-header>`;\n }\n\n if (!isLeaf(node)) {\n const submenu = menuFromTree(node.children!, options);\n return html`\n <ixfx-menu-sub-trigger label=\"${label}\">\n <ixfx-menu-container\n slot=\"submenu\"\n .registry=${registry ?? nothing}\n .keyboardManager=${keyboardManager ?? nothing}\n >${submenu}</ixfx-menu-container>\n </ixfx-menu-sub-trigger>\n `;\n }\n\n return html`\n <ixfx-menu-item\n command=\"${node.item.key}\"\n label=\"${label}\"\n icon-name=\"${node.item.icon ?? nothing}\"\n @command=${onCommand ? (e: CustomEvent) => onCommand(e.detail) : nothing}\n ></ixfx-menu-item>\n `;\n })}`;\n}\n\nfunction buildMenuItemEl(\n node: TreeNode,\n options: MenuFromTreeOptions,\n): HTMLElement {\n const { onCommand, registry } = options;\n const label = node.item.label;\n\n const el = document.createElement(`ixfx-menu-item`);\n el.setAttribute(`command`, node.item.key);\n el.setAttribute(`label`, label);\n if (node.item.icon) {\n el.setAttribute(`icon-name`, node.item.icon);\n }\n if (onCommand) {\n el.addEventListener(`command`, e => onCommand((e as CustomEvent).detail));\n }\n if (registry) {\n (el as any).registry = registry;\n }\n return el;\n}\n\nfunction buildMenuSeparatorEl(): HTMLElement {\n return document.createElement(`ixfx-menu-separator`);\n}\n\nfunction buildMenuHeaderEl(label: string): HTMLElement {\n const el = document.createElement(`ixfx-menu-header`);\n el.setAttribute(`label`, label);\n return el;\n}\n\nfunction buildMenuSubTriggerEl(\n node: TreeNode,\n options: MenuFromTreeOptions,\n): HTMLElement {\n const { registry, keyboardManager } = options;\n\n const el = document.createElement(`ixfx-menu-sub-trigger`);\n el.setAttribute(`label`, node.item.label);\n if (registry)\n (el as any).registry = registry;\n if (keyboardManager)\n (el as any).keyboardManager = keyboardManager;\n\n const container = document.createElement(`ixfx-menu-container`);\n container.setAttribute(`slot`, `submenu`);\n if (registry)\n (container as any).registry = registry;\n if (keyboardManager)\n (container as any).keyboardManager = keyboardManager;\n\n const childEls = menuFromTreeToElements(node.children!, options);\n for (const child of childEls) {\n container.appendChild(child);\n }\n\n el.appendChild(container);\n return el;\n}\n\nexport function menuFromTreeToElements(\n nodes: TreeNode[],\n options: MenuFromTreeOptions = {},\n): HTMLElement[] {\n return nodes.map((node) => {\n const label = node.item.label;\n\n if (label === `-`) {\n return buildMenuSeparatorEl();\n }\n\n if (label.startsWith(`# `)) {\n return buildMenuHeaderEl(label.slice(2));\n }\n\n if (!isLeaf(node)) {\n return buildMenuSubTriggerEl(node, options);\n }\n\n return buildMenuItemEl(node, options);\n });\n}\n","import { css, html, LitElement } from 'lit';\nimport { property } from 'lit/decorators.js';\nimport { tickledItemStyles } from '../interaction/index.js';\nimport { themeFallbacks } from '../styles/fallbacks.js';\nimport { safeCustomElement as customElement } from '../util/custom-elements.js';\nimport '../icons/icon.js';\n\nexport type MenuRadioItemSelectDetail = {\n readonly value: string;\n};\n\nexport type MenuRadioItemSelectEvent = CustomEvent<MenuRadioItemSelectDetail>;\n\n@customElement(`ixfx-menu-radio-item`)\nexport class MenuRadioItem extends LitElement {\n @property({ type: String }) value = ``;\n @property({ type: String }) label = ``;\n @property({ type: Boolean }) disabled = false;\n @property({ type: String }) groupValue = ``;\n @property({ type: String, attribute: `icon-name` }) iconName = ``;\n\n private handleClick(): void {\n if (this.disabled)\n return;\n\n this.dispatchEvent(new CustomEvent<MenuRadioItemSelectDetail>(`radio-select`, {\n detail: { value: this.value },\n bubbles: true,\n composed: true,\n }));\n }\n\n static override styles = [themeFallbacks, tickledItemStyles, css`\n :host {\n display: block;\n }\n\n .item {\n display: flex;\n padding: 6px var(--space-m);\n font-size: var(--text-m);\n }\n\n :host(:focus) {\n outline: none;\n }\n\n .item.disabled {\n opacity: var(--disabled-opacity);\n cursor: default;\n }\n\n .item.disabled:hover {\n background: transparent;\n }\n\n .radio {\n width: 16px;\n margin-right: var(--space-s);\n font-size: var(--text-m);\n color: var(--accent);\n }\n\n .icon-slot {\n width: 0;\n overflow: hidden;\n flex-shrink: 0;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n }\n\n :host([icon-name]) .icon-slot,\n :host([data-icon-section]) .icon-slot {\n width: 1em;\n height: 1em;\n margin-right: var(--space-s);\n }\n\n .label {\n flex: 1;\n }\n `];\n\n render() {\n const isSelected = this.groupValue === this.value;\n\n return html`\n <div \n class=\"item ${this.disabled ? `disabled` : ``}\"\n @click=${this.handleClick}\n tabindex=\"${this.disabled ? -1 : 0}\"\n role=\"menuitemradio\"\n aria-checked=\"${isSelected}\"\n >\n <span class=\"radio\">${isSelected ? `●` : ``}</span>\n <span class=\"icon-slot\">${this.iconName ? html`<ixfx-icon name=\"${this.iconName}\"></ixfx-icon>` : null}</span>\n <span class=\"label\">${this.label}</span>\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'ixfx-menu-radio-item': MenuRadioItem;\n }\n}\n","import { css, html, LitElement } from 'lit';\nimport { property, state } from 'lit/decorators.js';\nimport { safeCustomElement as customElement } from '../util/custom-elements.js';\nimport './menu-radio-item.js';\n\nexport type MenuRadioGroupChangeDetail = {\n readonly name: string;\n readonly value: string;\n};\n\nexport type MenuRadioGroupChangeEvent = CustomEvent<MenuRadioGroupChangeDetail>;\n\n@customElement(`ixfx-menu-radio-group`)\nexport class MenuRadioGroup extends LitElement {\n @property({ type: String }) name = ``;\n @property({ type: String }) value = ``;\n\n @state() private slottedElements: Element[] = [];\n\n private handleSlotChange(e: Event): void {\n const slot = e.target as HTMLSlotElement;\n this.slottedElements = slot.assignedElements();\n this.updateSlottedValues();\n }\n\n private updateSlottedValues(): void {\n for (const el of this.slottedElements) {\n (el as any).groupValue = this.value;\n }\n }\n\n private handleRadioSelect(e: CustomEvent): void {\n this.value = e.detail.value;\n this.dispatchEvent(new CustomEvent<MenuRadioGroupChangeDetail>(`radio-change`, {\n detail: {\n name: this.name,\n value: this.value,\n },\n bubbles: true,\n composed: true,\n }));\n }\n\n updated(changedProperties: Map<string, unknown>): void {\n if (changedProperties.has(`value`)) {\n this.updateSlottedValues();\n }\n }\n\n static styles = css`\n :host {\n display: block;\n }\n\n ::slotted(menu-radio-item) {\n display: block;\n }\n `;\n\n render() {\n return html`\n <slot @slotchange=${this.handleSlotChange} @radio-select=${this.handleRadioSelect}></slot>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'ixfx-menu-radio-group': MenuRadioGroup;\n }\n}\n","export { MenuBar } from './menu-bar.js';\nexport { MenuCheckItem } from './menu-check-item.js';\nexport { MenuCommandController } from './menu-command-controller.js';\nexport { MenuContainer } from './menu-container.js';\nexport { menuFromTree, menuFromTreeToElements } from './menu-from-tree.js';\nexport type { MenuFromTreeOptions } from './menu-from-tree.js';\nexport { MenuHeader } from './menu-header.js';\nexport { MenuItem } from './menu-item.js';\nexport { MenuRadioGroup } from './menu-radio-group.js';\nexport { MenuRadioItem } from './menu-radio-item.js';\nexport { MenuSeparator } from './menu-separator.js';\nexport { MenuSubTrigger } from './menu-sub-trigger.js';\nexport { MenuTrigger } from './menu-trigger.js';\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"tickled-controller-h9GmJ_bU.js","names":[],"sources":["../src/interaction/tickled-controller.ts"],"sourcesContent":["import type { ReactiveController, ReactiveControllerHost } from 'lit';\n\n/**\n * Represents an item that can be tickled (highlighted via hover or keyboard navigation)\n */\nexport type TickledItem<T = unknown> = {\n /**\n * Unique identifier for the item (e.g., index, id, path)\n */\n readonly id: T;\n\n /**\n * Whether the item is currently enabled/interactive\n */\n readonly enabled?: boolean;\n};\n\n/**\n * Input mode determines which mechanism controls the tickled state\n */\nexport type InputMode = `keyboard` | `mouse`;\n\n/**\n * Configuration for TickledController\n */\nexport type TickledControllerConfig<T> = {\n /**\n * Function to retrieve all items that can be tickled\n */\n getItems: () => ReadonlyArray<TickledItem<T>>;\n\n /**\n * Optional callback when an item becomes tickled\n * Use this for side effects like opening submenus\n * @param id - The id of the newly tickled item, or undefined if nothing is tickled\n */\n onTickle?: (id: T | undefined) => void;\n\n /**\n * Optional callback fired after any state change (tickled id or input mode)\n * Use this to sync DOM attributes for slotted content that can't use reactive rendering\n *\n * For components that render items directly, this is usually not needed as\n * Lit's reactive rendering will handle updates automatically via requestUpdate()\n *\n * @example\n * ```ts\n * onStateChange: () => {\n * // Sync data-tickled attributes for slotted items\n * const items = this.getItems();\n * items.forEach((item, i) => {\n * if (this.tickled.isTickled(i)) {\n * item.setAttribute('data-tickled', '');\n * } else {\n * item.removeAttribute('data-tickled');\n * }\n * });\n * }\n * ```\n */\n onStateChange?: () => void;\n\n /**\n * Optional initial input mode (defaults to 'mouse')\n */\n initialInputMode?: InputMode;\n\n /**\n * Whether keyboard navigation wraps from last to first and vice versa.\n * - 'stop' (default): navigation stops at the boundary\n * - 'wrap': navigation wraps around\n */\n itemWrap?: `stop` | `wrap`;\n};\n\n/**\n * ReactiveController that manages \"tickled\" state for interactive lists.\n *\n * A \"tickled\" item is one that is under active attention via hover (mouse mode)\n * or keyboard focus (keyboard mode). Only one item can be tickled at a time.\n *\n * The input mode determines which mechanism controls tickled state:\n * - Mouse mode: hover controls tickled, keyboard navigation ignored\n * - Keyboard mode: keyboard navigation controls tickled, hover ignored\n *\n * Last interaction wins - any mouse hover switches to mouse mode, any keyboard\n * navigation switches to keyboard mode.\n *\n * ## Global Mouse Tracking\n *\n * This controller uses a single, ref-counted global `pointermove` listener (shared\n * across all instances) to track actual mouse movement. This prevents `pointerenter`\n * events caused by content scrolling from incorrectly switching to mouse mode when\n * the user is actually using keyboard navigation. The listener is attached when the\n * first host connects and removed once the last host disconnects.\n *\n * @example\n * ```ts\n * class MyList extends LitElement {\n * private tickled = new TickledController(this, {\n * getItems: () => this.items.map((item, i) => ({ id: i, enabled: !item.disabled })),\n * onTickle: (id) => {\n * // Optional: perform side effect when item is tickled\n * console.log('Tickled item:', id);\n * }\n * });\n *\n * render() {\n * return html`\n * <ul @pointerleave=${(e) => this.tickled.handlePointerLeave(e)}>\n * ${this.items.map((item, i) => html`\n * <li\n * class=\"item ${this.tickled.isTickled(i) ? 'tickled' : ''}\"\n * @pointerenter=${() => this.tickled.handlePointerEnter(i)}\n * >${item.label}</li>\n * `)}\n * </ul>\n * `;\n * }\n *\n * private handleKeyDown(e: KeyboardEvent) {\n * if (e.key === 'ArrowDown') {\n * this.tickled.handleKeyboardNavigation('next');\n * } else if (e.key === 'ArrowUp') {\n * this.tickled.handleKeyboardNavigation('previous');\n * }\n * }\n * }\n * ```\n */\nexport class TickledController<T = number> implements ReactiveController {\n private host: ReactiveControllerHost;\n private config: TickledControllerConfig<T>;\n private _tickledId: T | undefined = undefined;\n private _inputMode: InputMode;\n\n private static _lastMouseClientX: number = Number.NaN;\n private static _lastMouseClientY: number = Number.NaN;\n private static _mouseMovedSinceKeyboard: boolean = true;\n private static _listenerRefCount: number = 0;\n\n private static readonly _onGlobalPointerMove = (e: PointerEvent): void => {\n TickledController.handleGlobalPointerMove(e.clientX, e.clientY);\n };\n\n constructor(host: ReactiveControllerHost, config: TickledControllerConfig<T>) {\n this.host = host;\n this.config = config;\n this._inputMode = config.initialInputMode ?? `mouse`;\n host.addController(this);\n }\n\n hostConnected(): void {\n if (TickledController._listenerRefCount === 0) {\n document.addEventListener(`pointermove`, TickledController._onGlobalPointerMove, { passive: true });\n }\n TickledController._listenerRefCount++;\n }\n\n hostDisconnected(): void {\n TickledController._listenerRefCount--;\n if (TickledController._listenerRefCount === 0) {\n document.removeEventListener(`pointermove`, TickledController._onGlobalPointerMove);\n }\n }\n\n /**\n * Get the current input mode\n */\n get inputMode(): InputMode {\n return this._inputMode;\n }\n\n /**\n * Get the currently tickled item id\n */\n get tickledId(): T | undefined {\n return this._tickledId;\n }\n\n /**\n * Check if a specific item is tickled\n */\n isTickled(id: T): boolean {\n return this._tickledId === id;\n }\n\n /**\n * Handle global pointer move event.\n * Called automatically by the global listener.\n * Tracks mouse movement to distinguish real hover from content scrolling.\n */\n private static handleGlobalPointerMove(clientX: number, clientY: number): void {\n if (!Number.isNaN(TickledController._lastMouseClientX)) {\n if (\n clientX !== TickledController._lastMouseClientX\n || clientY !== TickledController._lastMouseClientY\n ) {\n TickledController._mouseMovedSinceKeyboard = true;\n }\n }\n TickledController._lastMouseClientX = clientX;\n TickledController._lastMouseClientY = clientY;\n }\n\n /**\n * Check if actual mouse movement has occurred since last keyboard interaction.\n * Used to distinguish real hover/leave from content-scrolling-induced events.\n */\n static get hasMouseMovedSinceKeyboard(): boolean {\n return TickledController._mouseMovedSinceKeyboard;\n }\n\n /**\n * Handle pointer enter event on an item (mouse hover).\n * Only processes if actual mouse movement is detected (not content scrolling).\n * Switches to mouse mode and tickles the item.\n */\n handlePointerEnter(id: T): void {\n if (!TickledController._mouseMovedSinceKeyboard) {\n return;\n }\n\n const items = this.config.getItems();\n const item = items.find(item => item.id === id);\n if (item && item.enabled === false) {\n return;\n }\n\n const modeChanged = this._inputMode !== `mouse`;\n this._inputMode = `mouse`;\n\n const previousId = this._tickledId;\n this._tickledId = id;\n\n this.host.requestUpdate();\n\n if (previousId !== id && this.config.onTickle) {\n this.config.onTickle(id);\n }\n\n if ((previousId !== id || modeChanged) && this.config.onStateChange) {\n this.config.onStateChange();\n }\n }\n\n /**\n * Handle pointer leave event on an item.\n * Only clears tickled state if actual mouse movement is detected.\n * @param _e The pointer event (not used, but kept for API compatibility)\n */\n handlePointerLeave(_e: PointerEvent): void {\n if (TickledController._mouseMovedSinceKeyboard) {\n this.clearTickled();\n }\n }\n\n /**\n * Handle keyboard navigation.\n * Switches to keyboard mode and moves tickled state.\n *\n * @param direction - 'next', 'previous', 'first', 'last', or specific id\n */\n handleKeyboardNavigation(direction: `next` | `previous` | `first` | `last` | T): void {\n const modeChanged = this._inputMode !== `keyboard`;\n this._inputMode = `keyboard`;\n\n const items = this.config.getItems();\n const enabledItems = items.filter(item => item.enabled !== false);\n\n if (enabledItems.length === 0) {\n return;\n }\n\n let newId: T | undefined;\n\n if (direction === `first`) {\n newId = enabledItems[0].id;\n } else if (direction === `last`) {\n newId = enabledItems.at(-1)?.id;\n } else if (direction === `next` || direction === `previous`) {\n const currentIndex = enabledItems.findIndex(item => item.id === this._tickledId);\n const wrap = this.config.itemWrap === `wrap`;\n\n if (currentIndex === -1) {\n newId = direction === `next` ? enabledItems[0].id : enabledItems.at(-1)?.id;\n } else {\n const delta = direction === `next` ? 1 : -1;\n let newIndex = currentIndex + delta;\n\n if (newIndex >= 0 && newIndex < enabledItems.length) {\n newId = enabledItems[newIndex].id;\n } else if (wrap) {\n // Wrap around\n if (newIndex < 0) {\n newIndex = enabledItems.length - 1;\n } else {\n newIndex = 0;\n }\n newId = enabledItems[newIndex].id;\n }\n // If newIndex is out of bounds and wrap is disabled, keep current tickled item\n }\n } else {\n const item = enabledItems.find(item => item.id === direction);\n if (item) {\n newId = item.id;\n }\n }\n\n if (newId !== undefined) {\n const previousId = this._tickledId;\n this._tickledId = newId;\n this.host.requestUpdate();\n\n if (previousId !== newId && this.config.onTickle) {\n this.config.onTickle(newId);\n }\n\n if ((previousId !== newId || modeChanged) && this.config.onStateChange) {\n this.config.onStateChange();\n }\n }\n }\n\n /**\n * Manually set the tickled item (useful for programmatic control).\n * Does NOT change input mode.\n */\n setTickled(id: T | undefined): void {\n const previousId = this._tickledId;\n this._tickledId = id;\n this.host.requestUpdate();\n\n if (previousId !== id && this.config.onTickle) {\n this.config.onTickle(id);\n }\n\n if (previousId !== id && this.config.onStateChange) {\n this.config.onStateChange();\n }\n }\n\n /**\n * Clear the tickled state (e.g., on pointer leave).\n * Note: For pointer leave events, prefer using handlePointerLeave() which\n * checks for actual mouse movement.\n */\n clearTickled(): void {\n if (this._tickledId !== undefined) {\n this._tickledId = undefined;\n this.host.requestUpdate();\n\n if (this.config.onTickle) {\n this.config.onTickle(undefined);\n }\n\n if (this.config.onStateChange) {\n this.config.onStateChange();\n }\n }\n }\n\n /**\n * Manually set the input mode.\n * When switching to keyboard mode, resets the mouse movement tracking.\n */\n setInputMode(mode: InputMode): void {\n if (this._inputMode !== mode) {\n this._inputMode = mode;\n this.host.requestUpdate();\n\n if (mode === `keyboard`) {\n TickledController._mouseMovedSinceKeyboard = false;\n }\n\n if (this.config.onStateChange) {\n this.config.onStateChange();\n }\n }\n }\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"tree-Ch4W8qXQ.js","names":["#root","#loadChildren","#cache","#notify","#subscribers","#getAllNodes","#getFocusedIndex","#setFocusedIndex","#extendRangeSelection","#anchor","#toggleExpand","#toggleSelection","#abortPending","#clearSelectionInternal","#modelUnsubscribe","#ownedRegistry","#config","#canSelect","#dispatchSelect","#applyCheckedToggle","#applyExclusivity","#scrollKeyIntoView","#findNodePath","#pendingNode","#handleImplicitClick","#handleStandardClick","#handleManualClick","#handleExpandOnly","#getFlatIndexOf","#allDescendantsSelected","#pendingController","#invokeLoadChildren","#matchesFilter","#collapseSiblingsAtDepth","#findAncestorKeys","#findParent","#getVisibleNodes","#scrollTickledIntoView","#onKeyDown","#onDblClick","#renderNodes","customElement","#detectIcons","#depthsWithIcons","#renderCheckbox","customElement","#root","#element","#options","#connectSignal","#childrenOf","#nodeToTree","#findTreePath","#findInChildren","#toTreeNode","#identityToTreeNode"],"sources":["../src/common/tree-data-model.ts","../src/tree/tree-base.ts","../src/tree/tree-list.ts","../src/tree/tree-controller.ts"],"sourcesContent":["import type { LoadChildrenCallback, TreeItem, TreeNode } from './types.js';\nimport { collectDataResult } from './data-provider.js';\n\n// ---------------------------------------------------------------------------\n// Subscription event types (internal — not part of the public API)\n// ---------------------------------------------------------------------------\n\ntype InvalidateEvent = { type: `invalidate`; key: string };\ntype RootEvent = { type: `root`; root: TreeNode };\ntype ModelEvent = InvalidateEvent | RootEvent;\n\ntype ModelSubscriber = (event: ModelEvent) => void;\n\n// ---------------------------------------------------------------------------\n// Pure tree-mutation helpers (functional, produce new roots)\n// ---------------------------------------------------------------------------\n\n// function cloneNode(node: TreeNode): TreeNode {\n// if (node.children === undefined)\n// return { item: { ...node.item } };\n// return { item: { ...node.item }, children: node.children.map(cloneNode) };\n// }\n\nfunction findNode(root: TreeNode, key: string): TreeNode | undefined {\n if (root.item.key === key)\n return root;\n if (!root.children)\n return undefined;\n for (const child of root.children) {\n const found = findNode(child, key);\n if (found)\n return found;\n }\n return undefined;\n}\n\n/** Return a new root with the node at `key` replaced by `replacer(node)`. */\nfunction mapNode(root: TreeNode, key: string, replacer: (n: TreeNode) => TreeNode): TreeNode {\n if (root.item.key === key)\n return replacer(root);\n if (!root.children)\n return root;\n return { item: root.item, children: root.children.map(c => mapNode(c, key, replacer)) };\n}\n\n/** Return a new root with `newChild` appended to the children of the node at `parentKey`. */\nfunction addChildTo(root: TreeNode, parentKey: string, newChild: TreeNode): TreeNode {\n return mapNode(root, parentKey, parent => ({\n item: parent.item,\n children: [...(parent.children ?? []), newChild],\n }));\n}\n\n/** Return a new root with the node at `key` removed (along with all its descendants). */\nfunction removeNodeFrom(root: TreeNode, key: string): TreeNode {\n if (!root.children)\n return root;\n return {\n item: root.item,\n children: root.children\n .filter(c => c.item.key !== key)\n .map(c => removeNodeFrom(c, key)),\n };\n}\n\n/** Return a new root with the node at `key` moved to become a child of `newParentKey`. */\nfunction moveNodeTo(root: TreeNode, key: string, newParentKey: string): TreeNode {\n const target = findNode(root, key);\n if (!target)\n return root;\n const withoutTarget = removeNodeFrom(root, key);\n return addChildTo(withoutTarget, newParentKey, target);\n}\n\n// ---------------------------------------------------------------------------\n// TreeDataModelOptions\n// ---------------------------------------------------------------------------\n\nexport type TreeDataModelOptions = {\n /** The initial root node. Required. */\n root: TreeNode;\n /**\n * Optional callback for lazily loading children.\n * If omitted, nodes with `children === undefined` are treated as empty branches.\n */\n loadChildren?: LoadChildrenCallback;\n};\n\n// ---------------------------------------------------------------------------\n// TreeDataModel\n// ---------------------------------------------------------------------------\n\n/**\n * Manages tree data for tree-like components.\n *\n * Handles two concerns:\n * 1. **Lazy loading** — caches children fetched via `loadChildren` and can invalidate\n * specific branches so they are re-fetched on next expand.\n * 2. **Structural mutations** — convenience methods to add, remove, update, or move\n * nodes. Each mutation produces a new immutable root and notifies connected components.\n *\n * Assign an instance to a component's `model` property instead of setting `root`/`loadChildren`\n * directly. The same model instance can be shared across multiple components.\n *\n * @example\n * ```typescript\n * const model = new TreeDataModel({\n * root: { item: { key: 'root', label: 'Root' }, children: [] },\n * loadChildren: async (node, depth, signal) => fetchChildren(node.item.key),\n * });\n * myTreeList.model = model;\n * myMillerList.model = model;\n *\n * // Later — invalidate a branch to force a re-fetch on next expand:\n * model.invalidate('some-key');\n * ```\n */\nexport class TreeDataModel {\n #root: TreeNode;\n #loadChildren: LoadChildrenCallback | undefined;\n #cache = new Map<string, TreeNode[]>();\n #subscribers = new Set<ModelSubscriber>();\n\n constructor(options: TreeDataModelOptions) {\n this.#root = options.root;\n this.#loadChildren = options.loadChildren;\n }\n\n // ---------------------------------------------------------------------------\n // Root\n // ---------------------------------------------------------------------------\n\n /** The current immutable root snapshot. */\n get root(): TreeNode {\n return this.#root;\n }\n\n /**\n * Replace the entire root with a new immutable snapshot.\n * All cached children are cleared. Connected components re-render.\n */\n setRoot(root: TreeNode): void {\n this.#root = root;\n this.#cache.clear();\n this.#notify({ type: `root`, root });\n }\n\n // ---------------------------------------------------------------------------\n // Child loading\n // ---------------------------------------------------------------------------\n\n /**\n * Returns children for the given node.\n * If children are cached, they are returned immediately (as a resolved Promise).\n * Otherwise the `loadChildren` callback is invoked and the result is cached.\n * If no `loadChildren` is set and no cache entry exists, returns `[]`.\n *\n * Called by components internally — not typically called by application code.\n */\n async getChildren(node: TreeNode, depth: number, signal: AbortSignal): Promise<TreeNode[]> {\n const key = node.item.key;\n if (this.#cache.has(key)) {\n return this.#cache.get(key)!;\n }\n if (!this.#loadChildren) {\n return [];\n }\n const items = await collectDataResult(this.#loadChildren({ node, depth }, signal), signal);\n const children = [...items] as TreeNode[];\n if (!signal.aborted) {\n this.#cache.set(key, children);\n }\n return children;\n }\n\n // ---------------------------------------------------------------------------\n // Cache invalidation\n // ---------------------------------------------------------------------------\n\n /**\n * Clears the cached children for the node with the given key.\n * Connected components will collapse that node (if currently expanded), so that\n * the next expand triggers a fresh `loadChildren` call.\n */\n invalidate(key: string): void {\n this.#cache.delete(key);\n this.#notify({ type: `invalidate`, key });\n }\n\n // ---------------------------------------------------------------------------\n // Convenience mutators\n // ---------------------------------------------------------------------------\n\n /**\n * Adds `node` as a child of the node identified by `parentKey`.\n * Returns the new root (connected components are notified automatically).\n */\n addNode(parentKey: string, node: TreeNode): TreeNode {\n this.#root = addChildTo(this.#root, parentKey, node);\n this.#notify({ type: `root`, root: this.#root });\n return this.#root;\n }\n\n /**\n * Removes the node with the given key and all its descendants.\n * Returns the new root (connected components are notified automatically).\n */\n removeNode(key: string): TreeNode {\n this.#cache.delete(key);\n this.#root = removeNodeFrom(this.#root, key);\n this.#notify({ type: `root`, root: this.#root });\n return this.#root;\n }\n\n /**\n * Applies a partial update to the `item` of the node with the given key.\n * Returns the new root (connected components are notified automatically).\n */\n updateItem(key: string, patch: Partial<TreeItem>): TreeNode {\n this.#root = mapNode(this.#root, key, n => ({\n item: { ...n.item, ...patch },\n children: n.children,\n }));\n this.#notify({ type: `root`, root: this.#root });\n return this.#root;\n }\n\n /**\n * Moves the node with the given key to become a child of `newParentKey`.\n * Returns the new root (connected components are notified automatically).\n */\n moveNode(key: string, newParentKey: string): TreeNode {\n this.#root = moveNodeTo(this.#root, key, newParentKey);\n this.#notify({ type: `root`, root: this.#root });\n return this.#root;\n }\n\n // ---------------------------------------------------------------------------\n // Internal subscription (used by components — not part of the public API)\n // ---------------------------------------------------------------------------\n\n /**\n * Subscribe to model events. Returns an unsubscribe function.\n * Components call this when the `model` property is set, and unsubscribe when it is unset.\n *\n * @internal\n */\n _subscribe(callback: ModelSubscriber): () => void {\n this.#subscribers.add(callback);\n return () => this.#subscribers.delete(callback);\n }\n\n #notify(event: ModelEvent): void {\n for (const sub of this.#subscribers) {\n sub(event);\n }\n }\n}\n","/* eslint-disable ts/no-unsafe-declaration-merging */\nimport type { CommandRegistry } from '@clinth/ui-commands';\nimport type { PropertyValues, TemplateResult } from 'lit';\nimport type { TreeSearchHost } from '../common/searchable.js';\nimport type {\n TreeActivateDetail,\n TreeActivateEvent,\n TreeCollapseDetail,\n TreeCollapseEvent,\n TreeComponent,\n TreeComponentEventMap,\n TreeExpandDetail,\n TreeExpandEvent,\n TreeItemClickDetail,\n TreeItemClickEvent,\n TreeSelectDetail,\n TreeSelectEvent,\n TreeTickleDetail,\n TreeTickleEvent,\n} from '../common/tree-component.js';\nimport type { TreeDataModel } from '../common/tree-data-model.js';\nimport type {\n LoadChildrenCallback,\n TreeExclusivity,\n TreeFilterPredicate,\n TreeInteractionMode,\n TreeNode,\n TreeSelectionFilter,\n} from '../common/types.js';\nimport type { TreeConfig } from './types.js';\nimport { createCommandRegistry } from '@clinth/ui-commands';\nimport { css, html, LitElement, nothing } from 'lit';\nimport { property, state } from 'lit/decorators.js';\nimport { collectDataResult } from '../common/data-provider.js';\nimport { tickledItemStyles, tickledStyles } from '../interaction/index.js';\nimport { TickledController } from '../interaction/tickled-controller.js';\nimport { themeFallbacks } from '../styles/fallbacks.js';\nimport { safeCustomElement as customElement } from '../util/custom-elements.js';\n\n@customElement(`ixfx-tree-base`)\nexport class TreeBaseElement extends LitElement implements TreeComponent, TreeSearchHost {\n @property({ type: Object })\n root: TreeNode | undefined;\n\n @property({ attribute: false })\n model: TreeDataModel | undefined;\n\n /** Callback to lazily load children for a node. Superseded by `model` when both are set. */\n @property({ attribute: false })\n loadChildren: LoadChildrenCallback | undefined;\n\n @property({ reflect: true, attribute: `exclusivity` })\n exclusivity: TreeExclusivity = `none`;\n\n @property({ reflect: true, attribute: `selection-filter` })\n selectionFilter: TreeSelectionFilter = `all`;\n\n /** Controls how pointer/keyboard gestures map to selection changes. Default: `'standard'`. */\n @property({ type: String, attribute: `interaction-mode` })\n interactionMode: TreeInteractionMode = `standard`;\n\n @property({ attribute: false })\n selectedNode: TreeNode | undefined;\n\n @state()\n protected _selectedNodes = new Set<TreeNode>();\n\n get selectedNodes(): ReadonlySet<TreeNode> {\n return this._selectedNodes;\n }\n\n @property({ attribute: false })\n filterPredicate: TreeFilterPredicate | undefined;\n\n @property({ type: Boolean, attribute: `disable-keyboard-navigation` })\n disableKeyboardNavigation = false;\n\n @property({ attribute: false })\n registry: CommandRegistry<unknown> | undefined = undefined;\n\n @state()\n protected _expandedKeys = new Set<string>();\n\n protected tickled = new TickledController<string>(this, {\n getItems: () => {\n const nodes = this.#getAllNodes();\n return nodes.map(({ node }) => ({ id: node.item.key, enabled: true }));\n },\n onStateChange: () => {\n this.setAttribute(`data-input-mode`, this.tickled.inputMode);\n },\n });\n\n @state()\n protected _focusedDepth = 0;\n\n #pendingNode: TreeNode | undefined;\n #pendingController: AbortController | undefined;\n #modelUnsubscribe: (() => void) | undefined;\n // Range-select anchor: the flat-list index and node from which a Shift-range is measured.\n #anchor: { node: TreeNode; flatIndex: number; selectionSnapshot?: ReadonlySet<TreeNode> } | undefined;\n #ownedRegistry: CommandRegistry<unknown> | undefined;\n\n #config: TreeConfig = {\n indent: 20,\n };\n\n override updated(changedProps: PropertyValues) {\n if (changedProps.has(`root`) && this.root) {\n this.#abortPending();\n this._expandedKeys.clear();\n this.#clearSelectionInternal();\n this.tickled.clearTickled();\n this._focusedDepth = 0;\n }\n\n if (changedProps.has(`model`)) {\n this.#modelUnsubscribe?.();\n this.#modelUnsubscribe = undefined;\n const model = this.model;\n if (model) {\n this.root = model.root;\n this.#modelUnsubscribe = model._subscribe((event) => {\n if (event.type === `root`) {\n this.root = event.root;\n this.requestUpdate();\n } else if (event.type === `invalidate`) {\n // If the invalidated node is currently expanded, collapse it\n if (this._expandedKeys.has(event.key)) {\n this._expandedKeys.delete(event.key);\n this.requestUpdate();\n }\n }\n });\n }\n }\n\n if (changedProps.has(`interactionMode`)) {\n this.#anchor = undefined;\n this.clearSelection();\n }\n\n // When selectedNode is set externally, sync focus to match\n if (changedProps.has(`selectedNode`) && this.selectedNode) {\n const prev = changedProps.get(`selectedNode`) as TreeNode | undefined;\n if (prev !== this.selectedNode) {\n this.tickled.setTickled(this.selectedNode.item.key);\n }\n }\n }\n\n override connectedCallback(): void {\n super.connectedCallback();\n this.setAttribute(`data-input-mode`, `mouse`);\n const registry = this.#ownedRegistry = createCommandRegistry();\n registry.register({\n id: `select-all`,\n label: `Select All`,\n description: `Select all visible items`,\n execute: () => this.selectAll(),\n });\n registry.register({\n id: `deselect-all`,\n label: `Deselect All`,\n description: `Clear all selection`,\n execute: () => this.deselectAll(),\n });\n this.registry = registry;\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback();\n this.#modelUnsubscribe?.();\n this.#modelUnsubscribe = undefined;\n if (this.#ownedRegistry) {\n this.#ownedRegistry.unregister(`select-all`);\n this.#ownedRegistry.unregister(`deselect-all`);\n this.#ownedRegistry = undefined;\n this.registry = undefined;\n }\n }\n\n getConfig(): TreeConfig {\n return { ...this.#config };\n }\n\n setConfig(config: Partial<TreeConfig>) {\n this.#config = { ...this.#config, ...config };\n this.requestUpdate();\n }\n\n // --- Programmatic selection ---\n\n select(node: TreeNode): void {\n if (!this.#canSelect(node))\n return;\n const prev = new Set(this._selectedNodes);\n this._selectedNodes.clear();\n this._selectedNodes.add(node);\n this.selectedNode = node;\n this.#dispatchSelect(prev);\n this.requestUpdate();\n }\n\n deselect(node: TreeNode): void {\n if (!this._selectedNodes.has(node))\n return;\n const prev = new Set(this._selectedNodes);\n this._selectedNodes.delete(node);\n if (this.selectedNode === node) {\n this.selectedNode = [...this._selectedNodes].at(-1);\n }\n this.#dispatchSelect(prev);\n this.requestUpdate();\n }\n\n clearSelection(): void {\n if (this._selectedNodes.size === 0)\n return;\n const prev = new Set(this._selectedNodes);\n this.#clearSelectionInternal();\n this.#dispatchSelect(prev);\n this.requestUpdate();\n }\n\n selectAll(): void {\n const prev = new Set(this._selectedNodes);\n if (this.interactionMode === `checked`) {\n for (const { node } of this.#getAllNodes()) {\n if (!this.#canSelect(node))\n continue;\n this.#applyCheckedToggle(node, true);\n }\n } else {\n for (const { node } of this.#getAllNodes()) {\n if (!this.#canSelect(node))\n continue;\n this._selectedNodes.add(node);\n }\n this.selectedNode = [...this._selectedNodes].at(-1);\n }\n this.#dispatchSelect(prev);\n this.requestUpdate();\n }\n\n deselectAll(): void {\n const prev = new Set(this._selectedNodes);\n if (this.interactionMode === `checked`) {\n for (const { node } of this.#getAllNodes()) {\n if (!this.#canSelect(node))\n continue;\n this.#applyCheckedToggle(node, false);\n }\n } else {\n this.#clearSelectionInternal();\n }\n this.#dispatchSelect(prev);\n this.requestUpdate();\n }\n\n // --- Navigation ---\n\n navigateTo(keys: readonly string[]): boolean {\n if (!this.root?.children)\n return false;\n\n this.#abortPending();\n this.#clearSelectionInternal();\n this.tickled.clearTickled();\n this._focusedDepth = 0;\n\n let currentNode: TreeNode | undefined = this.root;\n\n for (const key of keys) {\n const children: TreeNode[] | undefined = currentNode?.children;\n if (!children)\n return false;\n\n const found: TreeNode | undefined = children.find(n => n.item.key === key);\n if (!found)\n return false;\n\n this.#applyExclusivity(found.item.key);\n this._expandedKeys.add(found.item.key);\n\n if (key === keys.at(-1)) {\n this.select(found);\n this.tickled.setTickled(found.item.key);\n }\n\n currentNode = found;\n }\n\n this.requestUpdate();\n this.#scrollKeyIntoView(keys.at(-1)!);\n return true;\n }\n\n navigateToNode(predicate: (node: TreeNode) => boolean): boolean {\n if (!this.root?.children)\n return false;\n const keyPath = this.#findNodePath(this.root.children, predicate);\n if (!keyPath)\n return false;\n return this.navigateTo(keyPath);\n }\n\n getNodes(): ReadonlyArray<{ node: TreeNode; depth: number }> {\n if (!this.root?.children)\n return [];\n const result: Array<{ node: TreeNode; depth: number }> = [];\n const visit = (node: TreeNode, depth: number) => {\n result.push({ node, depth });\n if (node.children) {\n for (const child of node.children) visit(child, depth + 1);\n }\n };\n for (const child of this.root.children) visit(child, 0);\n return result;\n }\n\n // --- Internal helpers ---\n\n #canSelect(node: TreeNode): boolean {\n if (this.selectionFilter === `none`)\n return false;\n if (this.selectionFilter === `leaf`)\n return node.item.isLeaf === true;\n if (this.selectionFilter === `branch`)\n return node.item.isLeaf === false || (node.children !== undefined && node.children.length > 0);\n return true;\n }\n\n #clearSelectionInternal(): void {\n this._selectedNodes.clear();\n this.selectedNode = undefined;\n }\n\n #dispatchSelect(previous: ReadonlySet<TreeNode>): void {\n const detail: TreeSelectDetail = {\n selected: new Set(this._selectedNodes),\n previous,\n };\n this.dispatchEvent(new CustomEvent<TreeSelectDetail>(`select`, {\n detail,\n bubbles: true,\n composed: true,\n }));\n }\n\n #findNodePath(\n nodes: TreeNode[],\n predicate: (node: TreeNode) => boolean,\n prefix: string[] = [],\n ): string[] | undefined {\n for (const node of nodes) {\n const path = [...prefix, node.item.key];\n if (predicate(node))\n return path;\n if (node.children?.length) {\n const result = this.#findNodePath(node.children, predicate, path);\n if (result)\n return result;\n }\n }\n return undefined;\n }\n\n protected renderNodeContent(node: TreeNode, _depth: number): TemplateResult | typeof nothing {\n const isBranch = node.item.isLeaf !== true;\n const isExpanded = this._isExpanded(node);\n return html`\n <span class=\"tree-caret\">\n ${isBranch ? html`<ixfx-icon class=\"caret-icon\" name=\"chevron-down\" .rotate=${isExpanded ? nothing : `-90deg`}></ixfx-icon>` : ``}\n </span>\n <span class=\"tree-label\">${node.item.label}</span>\n `;\n }\n\n protected _handleItemClick(node: TreeNode, depth: number, event?: MouseEvent) {\n if (this.#pendingNode && this._expandedKeys.has(this.#pendingNode.item.key)) {\n this.#abortPending();\n }\n\n this.tickled.handlePointerEnter(node.item.key);\n this._focusedDepth = depth;\n\n this.dispatchEvent(new CustomEvent<TreeItemClickDetail>(`item-click`, {\n detail: { node, depth },\n bubbles: true,\n composed: true,\n }));\n\n switch (this.interactionMode) {\n case `implicit`: this.#handleImplicitClick(node, depth); break;\n case `standard`: this.#handleStandardClick(node, depth, event); break;\n case `vscode`: this.#handleStandardClick(node, depth, event); break;\n case `manual`: this.#handleManualClick(node, depth, event); break;\n case `checked`: this.#handleExpandOnly(node, depth); break;\n }\n\n this.requestUpdate();\n }\n\n #handleImplicitClick(node: TreeNode, depth: number): void {\n if (this.#canSelect(node)) {\n const prev = new Set(this._selectedNodes);\n this._selectedNodes.clear();\n this._selectedNodes.add(node);\n this.selectedNode = node;\n if (!prev.has(node) || prev.size !== 1)\n this.#dispatchSelect(prev);\n }\n this.#toggleExpand(node, depth);\n }\n\n #handleStandardClick(node: TreeNode, depth: number, event?: MouseEvent): void {\n const isMeta = event ? (event.metaKey || event.ctrlKey) : false;\n const isShift = event ? event.shiftKey : false;\n\n if (isMeta) {\n if (this.#canSelect(node)) {\n const prev = new Set(this._selectedNodes);\n if (this._selectedNodes.has(node)) {\n this._selectedNodes.delete(node);\n if (this.selectedNode === node)\n this.selectedNode = [...this._selectedNodes].at(-1);\n } else {\n this._selectedNodes.add(node);\n this.selectedNode = node;\n this.#anchor = { node, flatIndex: this.#getFlatIndexOf(node) };\n }\n this.#dispatchSelect(prev);\n }\n return; // no expand on Ctrl+click\n }\n\n if (isShift && this.#anchor) {\n const visible = this.#getAllNodes();\n const anchorIdx = visible.findIndex(n => n.node === this.#anchor!.node);\n const targetIdx = visible.findIndex(n => n.node === node);\n if (anchorIdx >= 0 && targetIdx >= 0) {\n const prev = new Set(this._selectedNodes);\n const from = Math.min(anchorIdx, targetIdx);\n const to = Math.max(anchorIdx, targetIdx);\n const rangeNodes = visible.slice(from, to + 1)\n .map(n => n.node)\n .filter(n => this.#canSelect(n));\n this._selectedNodes = new Set(rangeNodes);\n this.selectedNode = node;\n this.#dispatchSelect(prev);\n }\n return; // no expand on Shift+click\n }\n\n // Plain click: select + toggle expand\n if (this.#canSelect(node)) {\n const prev = new Set(this._selectedNodes);\n this._selectedNodes.clear();\n this._selectedNodes.add(node);\n this.selectedNode = node;\n this.#anchor = { node, flatIndex: this.#getFlatIndexOf(node) };\n if (!prev.has(node) || prev.size !== 1)\n this.#dispatchSelect(prev);\n }\n this.#toggleExpand(node, depth);\n }\n\n #handleExpandOnly(node: TreeNode, depth: number): void {\n this.#toggleExpand(node, depth);\n }\n\n #handleManualClick(node: TreeNode, depth: number, event?: MouseEvent): void {\n const isMeta = event ? (event.metaKey || event.ctrlKey) : false;\n const isShift = event ? event.shiftKey : false;\n\n if (isMeta) {\n if (this.#canSelect(node)) {\n const prev = new Set(this._selectedNodes);\n if (this._selectedNodes.has(node)) {\n this._selectedNodes.delete(node);\n if (this.selectedNode === node)\n this.selectedNode = [...this._selectedNodes].at(-1);\n } else {\n this._selectedNodes.add(node);\n this.selectedNode = node;\n this.#anchor = { node, flatIndex: this.#getFlatIndexOf(node) };\n }\n this.#dispatchSelect(prev);\n }\n return; // no expand on Meta+click\n }\n\n if (isShift && this.#anchor) {\n const visible = this.#getAllNodes();\n const anchorIdx = visible.findIndex(n => n.node === this.#anchor!.node);\n const targetIdx = visible.findIndex(n => n.node === node);\n if (anchorIdx >= 0 && targetIdx >= 0) {\n const prev = new Set(this._selectedNodes);\n const from = Math.min(anchorIdx, targetIdx);\n const to = Math.max(anchorIdx, targetIdx);\n const rangeNodes = visible.slice(from, to + 1)\n .map(n => n.node)\n .filter(n => this.#canSelect(n));\n this._selectedNodes = new Set(rangeNodes);\n this.selectedNode = node;\n this.#dispatchSelect(prev);\n }\n return; // no expand on Shift+click\n }\n\n this.#toggleExpand(node, depth);\n }\n\n #toggleSelection(node: TreeNode, _depth: number): void {\n if (!this.#canSelect(node))\n return;\n const prev = new Set(this._selectedNodes);\n if (this._selectedNodes.has(node)) {\n this._selectedNodes.delete(node);\n if (this.selectedNode === node)\n this.selectedNode = [...this._selectedNodes].at(-1);\n } else {\n this._selectedNodes.add(node);\n this.selectedNode = node;\n this.#anchor = { node, flatIndex: this.#getFlatIndexOf(node) };\n }\n this.#dispatchSelect(prev);\n }\n\n protected _handleCheckboxClick(event: MouseEvent, node: TreeNode, _depth: number): void {\n event.stopPropagation();\n const prev = new Set(this._selectedNodes);\n const allSelected = this.#allDescendantsSelected(node);\n this.#applyCheckedToggle(node, !allSelected);\n if (this.selectedNode === undefined || !this._selectedNodes.has(this.selectedNode)) {\n this.selectedNode = [...this._selectedNodes].at(-1);\n }\n this.#dispatchSelect(prev);\n this.requestUpdate();\n }\n\n #toggleExpand(node: TreeNode, depth: number): void {\n const isExpanded = this._expandedKeys.has(node.item.key);\n if (isExpanded) {\n this._expandedKeys.delete(node.item.key);\n this.dispatchEvent(new CustomEvent<TreeCollapseDetail>(`collapse`, {\n detail: { node, depth },\n bubbles: true,\n composed: true,\n }));\n } else {\n if (node.children === undefined) {\n if (node.item.isLeaf !== false)\n return;\n const loader = this.model ?? (this.loadChildren ? this : undefined);\n if (loader) {\n this.#pendingNode = node;\n const controller = new AbortController();\n this.#pendingController = controller;\n this.#invokeLoadChildren(node, depth, controller).catch(() => {});\n }\n } else {\n this.#applyExclusivity(node.item.key);\n this._expandedKeys.add(node.item.key);\n this.dispatchEvent(new CustomEvent<TreeExpandDetail>(`expand`, {\n detail: { node, depth },\n bubbles: true,\n composed: true,\n }));\n }\n }\n }\n\n #allDescendantsSelected(node: TreeNode): boolean {\n if (!this._selectedNodes.has(node))\n return false;\n if (!node.children?.length)\n return true;\n return node.children.every(child => this.#allDescendantsSelected(child));\n }\n\n #applyCheckedToggle(node: TreeNode, selected: boolean): void {\n if (this.#canSelect(node)) {\n if (selected) {\n this._selectedNodes.add(node);\n } else {\n this._selectedNodes.delete(node);\n }\n }\n if (node.children) {\n for (const child of node.children) {\n this.#applyCheckedToggle(child, selected);\n }\n }\n }\n\n #getFlatIndexOf(node: TreeNode): number {\n return this.#getAllNodes().findIndex(n => n.node === node);\n }\n\n #extendRangeSelection(nodes: Array<{ node: TreeNode; depth: number }>, fromIdx: number, toIdx: number): void {\n // Set anchor at `fromIdx` if not already anchored\n if (!this.#anchor) {\n const anchorNode = nodes[fromIdx]?.node;\n if (anchorNode)\n this.#anchor = { node: anchorNode, flatIndex: fromIdx };\n }\n if (!this.#anchor)\n return;\n\n const anchorIdx = nodes.findIndex(n => n.node === this.#anchor!.node);\n if (anchorIdx < 0)\n return;\n\n const from = Math.min(anchorIdx, toIdx);\n const to = Math.max(anchorIdx, toIdx);\n\n if (this.interactionMode === `vscode`) {\n // On first Shift+arrow: snapshot current selection\n if (!this.#anchor.selectionSnapshot) {\n this.#anchor = { ...this.#anchor, selectionSnapshot: new Set(this._selectedNodes) };\n }\n const snap = this.#anchor.selectionSnapshot!;\n const rangeNodes = nodes.slice(from, to + 1).map(n => n.node).filter(n => this.#canSelect(n));\n // Toggle: items in snapshot stay toggled based on presence in snapshot\n const next = new Set(snap);\n for (const n of rangeNodes) {\n if (snap.has(n))\n next.delete(n);\n else next.add(n);\n }\n const prev = new Set(this._selectedNodes);\n this._selectedNodes = next;\n this.selectedNode = nodes[toIdx]?.node ?? this.selectedNode;\n this.#dispatchSelect(prev);\n } else {\n // standard: range replaces selection\n const prev = new Set(this._selectedNodes);\n const rangeNodes = nodes.slice(from, to + 1).map(n => n.node).filter(n => this.#canSelect(n));\n this._selectedNodes = new Set(rangeNodes);\n this.selectedNode = nodes[toIdx]?.node ?? this.selectedNode;\n this.#dispatchSelect(prev);\n }\n }\n\n async #invokeLoadChildren(node: TreeNode, depth: number, controller: AbortController): Promise<void> {\n try {\n let children: TreeNode[];\n if (this.model) {\n children = await this.model.getChildren(node, depth, controller.signal);\n } else {\n children = [...await collectDataResult(\n this.loadChildren!({ node, depth }, controller.signal),\n controller.signal,\n )];\n }\n if (controller.signal.aborted)\n return;\n node.children = children;\n this.#applyExclusivity(node.item.key);\n this._expandedKeys.add(node.item.key);\n this.#pendingNode = undefined;\n this.#pendingController = undefined;\n this.dispatchEvent(new CustomEvent<TreeExpandDetail>(`expand`, {\n detail: { node, depth },\n bubbles: true,\n composed: true,\n }));\n this.requestUpdate();\n } catch {\n if (controller.signal.aborted)\n return;\n node.children = [];\n this.#pendingNode = undefined;\n this.#pendingController = undefined;\n this.requestUpdate();\n }\n }\n\n protected _isSelected(node: TreeNode): boolean {\n return this._selectedNodes.has(node);\n }\n\n protected _isFocused(node: TreeNode): boolean {\n return this.tickled.isTickled(node.item.key);\n }\n\n protected _isExpanded(node: TreeNode): boolean {\n if (this._expandedKeys.has(node.item.key))\n return true;\n if (this.filterPredicate && node.children) {\n return node.children.some(child => this.#matchesFilter(child, 0));\n }\n return false;\n }\n\n #matchesFilter(node: TreeNode, depth: number): boolean {\n if (!this.filterPredicate)\n return true;\n if (this.filterPredicate(node, depth))\n return true;\n return node.children?.some(child => this.#matchesFilter(child, depth + 1)) ?? false;\n }\n\n protected get _pendingNode(): TreeNode | undefined {\n return this.#pendingNode;\n }\n\n #scrollKeyIntoView(key: string) {\n requestAnimationFrame(() => {\n const el = this.shadowRoot?.querySelector(`[data-key=\"${CSS.escape(key)}\"]`) as HTMLElement | null;\n if (!el)\n return;\n const elRect = el.getBoundingClientRect();\n const hostRect = this.getBoundingClientRect();\n const relativeTop = elRect.top - hostRect.top;\n const relativeBottom = relativeTop + elRect.height;\n if (relativeTop < 0) {\n this.scrollTop += relativeTop;\n } else if (relativeBottom > this.clientHeight) {\n this.scrollTop += relativeBottom - this.clientHeight;\n }\n });\n }\n\n #abortPending() {\n if (this.#pendingController) {\n this.#pendingController.abort();\n this.#pendingController = undefined;\n this.#pendingNode = undefined;\n }\n }\n\n /** Apply exclusivity rules when a node is about to be expanded. */\n #applyExclusivity(expandKey: string): void {\n if (this.exclusivity === `none`)\n return;\n\n if (this.exclusivity === `depth`) {\n this.#collapseSiblingsAtDepth(expandKey);\n } else if (this.exclusivity === `global`) {\n // Collapse all expanded nodes except direct ancestors of the expanding node\n const ancestorKeys = this.#findAncestorKeys(expandKey);\n for (const key of [...this._expandedKeys]) {\n if (!ancestorKeys.has(key) && key !== expandKey) {\n this._expandedKeys.delete(key);\n }\n }\n }\n }\n\n #collapseSiblingsAtDepth(expandKey: string): void {\n if (!this.root)\n return;\n\n const parent = this.#findParent(this.root, expandKey);\n if (!parent?.children)\n return;\n\n for (const child of parent.children) {\n if (child.item.key !== expandKey && this._expandedKeys.has(child.item.key)) {\n this._expandedKeys.delete(child.item.key);\n }\n }\n }\n\n #findAncestorKeys(targetKey: string): Set<string> {\n const keys = new Set<string>();\n const walk = (node: TreeNode): boolean => {\n if (node.item.key === targetKey)\n return true;\n if (!node.children)\n return false;\n for (const child of node.children) {\n if (walk(child)) {\n keys.add(node.item.key);\n return true;\n }\n }\n return false;\n };\n if (this.root)\n walk(this.root);\n return keys;\n }\n\n #findParent(node: TreeNode, targetKey: string, parent: TreeNode | undefined = undefined): TreeNode | undefined {\n if (!node.children)\n return undefined;\n\n for (const child of node.children) {\n if (child.item.key === targetKey) {\n return parent ?? node;\n }\n const found = this.#findParent(child, targetKey, child);\n if (found)\n return found;\n }\n return undefined;\n }\n\n #getVisibleNodes(node: TreeNode, depth: number): Array<{ node: TreeNode; depth: number }> {\n const result: Array<{ node: TreeNode; depth: number }> = [];\n\n const visit = (n: TreeNode, d: number) => {\n result.push({ node: n, depth: d });\n if (this._expandedKeys.has(n.item.key) && n.children) {\n for (const child of n.children) {\n visit(child, d + 1);\n }\n }\n };\n\n if (node.children) {\n for (const child of node.children) {\n visit(child, depth + 1);\n }\n }\n\n return result;\n }\n\n #getAllNodes(): Array<{ node: TreeNode; depth: number }> {\n if (!this.root)\n return [];\n return this.#getVisibleNodes(this.root, 0);\n }\n\n #getFocusedIndex(): number {\n const nodes = this.#getAllNodes();\n const tickledKey = this.tickled.tickledId;\n if (!tickledKey)\n return 0;\n return nodes.findIndex(n => n.node.item.key === tickledKey);\n }\n\n #setFocusedIndex(index: number) {\n const nodes = this.#getAllNodes();\n if (index < 0 || index >= nodes.length)\n return;\n const { node, depth } = nodes[index];\n this.tickled.setTickled(node.item.key);\n this._focusedDepth = depth;\n // Emit tickle for keyboard cursor movement\n this.dispatchEvent(new CustomEvent<TreeTickleDetail>(`tickle`, {\n detail: { node, depth },\n bubbles: true,\n composed: true,\n }));\n this.requestUpdate();\n this.updateComplete.then(() => this.#scrollTickledIntoView());\n }\n\n #scrollTickledIntoView() {\n const tickledKey = this.tickled.tickledId;\n if (tickledKey === undefined)\n return;\n const item = this.shadowRoot?.querySelector(`[data-key=\"${tickledKey}\"]`);\n item?.scrollIntoView({ block: `nearest`, behavior: `smooth` });\n }\n\n #onKeyDown = (event: KeyboardEvent) => {\n const nodes = this.#getAllNodes();\n if (nodes.length === 0)\n return;\n\n if ((event.ctrlKey || event.metaKey) && event.key === `a`) {\n event.preventDefault();\n this.selectAll();\n return;\n }\n\n // Switch to keyboard mode for any navigation key\n if ([`ArrowDown`, `ArrowUp`, `ArrowLeft`, `ArrowRight`, `Enter`, ` `].includes(event.key)) {\n this.tickled.setInputMode(`keyboard`);\n }\n\n const currentIdx = this.#getFocusedIndex();\n\n switch (event.key) {\n case `ArrowDown`: {\n event.preventDefault();\n const nextIdx = Math.min(currentIdx + 1, nodes.length - 1);\n if (this.interactionMode === `implicit`) {\n this.select(nodes[nextIdx].node);\n }\n this.#setFocusedIndex(nextIdx);\n if (event.shiftKey && (this.interactionMode === `standard` || this.interactionMode === `vscode`)) {\n this.#extendRangeSelection(nodes, currentIdx, nextIdx);\n } else if (this.interactionMode === `vscode`) {\n this.#anchor = undefined; // clear snapshot on plain navigation\n }\n this.requestUpdate();\n break;\n }\n case `ArrowUp`: {\n event.preventDefault();\n const prevIdx = Math.max(currentIdx - 1, 0);\n if (this.interactionMode === `implicit`) {\n this.select(nodes[prevIdx].node);\n }\n this.#setFocusedIndex(prevIdx);\n if (event.shiftKey && (this.interactionMode === `standard` || this.interactionMode === `vscode`)) {\n this.#extendRangeSelection(nodes, currentIdx, prevIdx);\n } else if (this.interactionMode === `vscode`) {\n this.#anchor = undefined; // clear snapshot on plain navigation\n }\n this.requestUpdate();\n break;\n }\n case `ArrowRight`: {\n event.preventDefault();\n const current = nodes[currentIdx];\n if (current && current.node.item.isLeaf !== true) {\n if (!this._expandedKeys.has(current.node.item.key)) {\n this.#toggleExpand(current.node, current.depth);\n } else if (currentIdx + 1 < nodes.length) {\n this.#setFocusedIndex(currentIdx + 1);\n }\n }\n this.requestUpdate();\n break;\n }\n case `ArrowLeft`: {\n event.preventDefault();\n const current = nodes[currentIdx];\n if (current && this._expandedKeys.has(current.node.item.key)) {\n this._expandedKeys.delete(current.node.item.key);\n this.dispatchEvent(new CustomEvent(`collapse`, {\n detail: { node: current.node, depth: current.depth },\n bubbles: true,\n composed: true,\n }));\n } else if (current.depth > 1) {\n const parentDepth = current.depth - 1;\n for (let i = currentIdx - 1; i >= 0; i--) {\n if (nodes[i].depth === parentDepth) {\n this.#setFocusedIndex(i);\n break;\n }\n }\n }\n this.requestUpdate();\n break;\n }\n case `Enter`: {\n event.preventDefault();\n const current = nodes[currentIdx];\n if (current) {\n if (this.interactionMode !== `manual` && this.interactionMode !== `checked`) {\n this.select(current.node);\n }\n this.dispatchEvent(new CustomEvent<TreeActivateDetail>(`activate`, {\n detail: { node: current.node, depth: current.depth },\n bubbles: true,\n composed: true,\n }));\n this.requestUpdate();\n }\n break;\n }\n case ` `: {\n event.preventDefault();\n const current = nodes[currentIdx];\n if (!current)\n break;\n\n if (this.interactionMode === `checked`) {\n this._handleCheckboxClick(new MouseEvent(`click`), current.node, current.depth);\n } else if (this.interactionMode === `standard` || this.interactionMode === `vscode` || this.interactionMode === `manual`) {\n this.#toggleSelection(current.node, current.depth);\n }\n break;\n }\n case `Escape`: {\n event.preventDefault();\n this.#abortPending();\n break;\n }\n }\n };\n\n protected renderLoadingIndicator(): TemplateResult | typeof nothing {\n return html`<span class=\"loading-spinner\"></span>`;\n }\n\n protected override render() {\n const nodes = this.#getAllNodes();\n\n return html`\n <div\n id=\"container\"\n data-input-mode=${this.tickled.inputMode}\n @keydown=${this.#onKeyDown}\n @dblclick=${this.#onDblClick}\n @pointerleave=${(e: PointerEvent) => this.tickled.handlePointerLeave(e)}\n tabindex=\"0\"\n >\n ${this.root ? this.#renderNodes(nodes) : nothing}\n </div>\n `;\n }\n\n #onDblClick = (e: MouseEvent) => {\n const target = e.target as HTMLElement;\n const li = target.closest(`li`) as HTMLElement | null;\n if (!li)\n return;\n const key = li.dataset.key;\n if (!key)\n return;\n const nodes = this.#getAllNodes();\n const node = nodes.find(n => n.node.item.key === key);\n if (!node)\n return;\n this.dispatchEvent(new CustomEvent<TreeActivateDetail>(`activate`, {\n detail: { node: node.node, depth: node.depth },\n bubbles: true,\n composed: true,\n }));\n };\n\n #renderNodes(nodes: Array<{ node: TreeNode; depth: number }>) {\n if (nodes.length === 0 && this.#pendingNode) {\n return html`\n <div class=\"tree-loading\">\n ${this.renderLoadingIndicator()}\n </div>\n `;\n }\n\n return html`\n <ul class=\"tree-list\" role=\"tree\">\n ${nodes.map(({ node, depth }) => this._renderNode(node, depth))}\n </ul>\n `;\n }\n\n protected _renderNode(node: TreeNode, depth: number): TemplateResult {\n const selected = this._isSelected(node);\n const tickled = this._isFocused(node);\n const isBranch = node.item.isLeaf !== true;\n const isExpanded = this._isExpanded(node);\n const isPending = this.#pendingNode === node;\n const isFiltered = this.filterPredicate !== undefined && !this.#matchesFilter(node, depth);\n\n return html`\n <li\n class=\"item ${selected ? `selected` : ``} ${tickled ? `tickled` : ``} ${isBranch ? `branch` : `leaf`} ${isFiltered ? `filtered` : ``}\"\n role=\"treeitem\"\n aria-selected=${selected}\n aria-expanded=${isBranch ? isExpanded : nothing}\n aria-level=${depth + 1}\n data-key=${node.item.key}\n style=${`--depth: ${depth}`}\n @click=${(e: MouseEvent) => this._handleItemClick(node, depth, e)}\n @pointerenter=${() => {\n this.tickled.handlePointerEnter(node.item.key);\n this.dispatchEvent(new CustomEvent<TreeTickleDetail>(`tickle`, {\n detail: { node, depth },\n bubbles: true,\n composed: true,\n }));\n }}\n >\n ${this.renderNodeContent(node, depth)}\n ${isPending ? this.renderLoadingIndicator() : nothing}\n </li>\n `;\n }\n\n static override styles = [\n themeFallbacks,\n tickledStyles,\n tickledItemStyles,\n css`\n :host {\n --tree-bg: var(--surface-3);\n --tree-text: var(--surface-3-text);\n --tree-muted-text: var(--surface-muted-text);\n --tree-indent: 1ch;\n --tree-item-height: 28px;\n --tree-caret-size: 10px;\n --tree-radius: var(--radius-s);\n\n display: block;\n overflow: auto;\n box-sizing: border-box;\n background: var(--tree-bg);\n color: var(--tree-text);\n }\n\n #container {\n outline: none;\n min-height: 100%;\n }\n\n .tree-list {\n list-style: none;\n margin: 0;\n padding: var(--space-xs);\n }\n\n .item {\n display: flex;\n align-items: center;\n height: var(--tree-item-height);\n padding-left: calc(var(--tree-indent) * var(--depth, 0));\n cursor: pointer;\n user-select: none;\n font-family: var(--font-family);\n font-size: var(--text-m);\n border-radius: var(--tree-radius);\n transition: background var(--transition, 0.15s ease-out);\n margin: 1px 0;\n }\n\n .tree-caret {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: var(--tree-caret-size);\n height: var(--tree-caret-size);\n flex-shrink: 0;\n margin-right: var(--space-xs);\n }\n\n .caret-icon {\n width: var(--tree-caret-size);\n height: var(--tree-caret-size);\n color: var(--tree-muted-text);\n transition: transform var(--transition, 0.15s ease-out);\n }\n\n .item.selected .caret-icon {\n color: var(--accent-text);\n }\n\n .tree-label {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .tree-loading {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: var(--space-m);\n }\n\n .loading-spinner {\n width: 16px;\n height: 16px;\n border: 2px solid var(--surface-5);\n border-top-color: var(--accent);\n border-radius: 50%;\n animation: tree-spin 0.7s linear infinite;\n }\n\n @keyframes tree-spin {\n to { transform: rotate(360deg); }\n }\n\n .item.filtered {\n display: none;\n }\n\n ::highlight(incr-search-highlight) {\n background: var(--search-highlight-bg, var(--accent));\n color: var(--search-highlight-color, var(--accent-text, #fff));\n border-radius: 2px;\n padding: 0 1px;\n }\n `,\n ];\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'ixfx-tree-base': TreeBaseElement;\n }\n}\n\nexport interface TreeBaseElementEventMap extends TreeComponentEventMap {\n 'item-click': TreeItemClickEvent;\n 'expand': TreeExpandEvent;\n 'collapse': TreeCollapseEvent;\n 'select': TreeSelectEvent;\n 'tickle': TreeTickleEvent;\n 'activate': TreeActivateEvent;\n}\n\nexport interface TreeBaseElement {\n addEventListener: (<K extends keyof TreeBaseElementEventMap>(\n type: K,\n listener: (this: TreeBaseElement, event: TreeBaseElementEventMap[K]) => void,\n options?: boolean | AddEventListenerOptions,\n ) => void) & ((\n type: string,\n listener: EventListenerOrEventListenerObject,\n options?: boolean | AddEventListenerOptions,\n ) => void);\n removeEventListener: (<K extends keyof TreeBaseElementEventMap>(\n type: K,\n listener: (this: TreeBaseElement, event: TreeBaseElementEventMap[K]) => void,\n options?: boolean | EventListenerOptions,\n ) => void) & ((\n type: string,\n listener: EventListenerOrEventListenerObject,\n options?: boolean | EventListenerOptions,\n ) => void);\n}\n","/* eslint-disable ts/no-unsafe-declaration-merging */\nimport type { TemplateResult } from 'lit';\nimport type { TreeBaseElementEventMap } from './tree-base.js';\nimport type { TreeNode } from './types.js';\nimport { css, html, nothing } from 'lit';\nimport { property } from 'lit/decorators.js';\nimport { themeFallbacks } from '../styles/fallbacks.js';\nimport { safeCustomElement as customElement } from '../util/custom-elements.js';\nimport { TreeBaseElement } from './tree-base.js';\n\n/** Checkbox tri-state in `checked` interactionMode: a branch node is `indeterminate` when some but not all of its descendants are selected. */\nexport type TreeCheckboxState = `checked` | `unchecked` | `indeterminate`;\n\nfunction computeCheckboxState(node: TreeNode, selectedNodes: ReadonlySet<TreeNode>): TreeCheckboxState {\n if (node.item.isLeaf || !node.children?.length) {\n return selectedNodes.has(node) ? `checked` : `unchecked`;\n }\n const childStates = node.children.map(c => computeCheckboxState(c, selectedNodes));\n const allChecked = childStates.every(s => s === `checked`);\n const allUnchecked = childStates.every(s => s === `unchecked`);\n if (allChecked)\n return `checked`;\n if (allUnchecked)\n return `unchecked`;\n return `indeterminate`;\n}\n\n@customElement(`ixfx-tree-list`)\nexport class TreeListElement extends TreeBaseElement {\n /**\n * Optional renderer for the checkbox shown in `checked` interactionMode.\n * Receives the node's current tri-state (`checked` / `unchecked` / `indeterminate`,\n * the latter when some but not all descendants are selected) and returns a Lit\n * template.\n *\n * When set, the checkbox becomes a `<span role=\"checkbox\">` whose contents are\n * whatever this returns. When unset, a native `<input type=\"checkbox\">` is used.\n *\n * @example\n * list.checkboxRenderer = (state) => state === `unchecked`\n * ? html``\n * : html`<svg viewBox=\"0 0 16 16\" width=\"14\" height=\"14\"\n * fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"\n * stroke-linecap=\"round\" stroke-linejoin=\"round\"\n * style=\"opacity:${state === `indeterminate` ? 0.35 : 0.6}\">\n * <polyline points=\"3,8 7,12 13,4\" />\n * </svg>`;\n */\n @property({ attribute: false })\n checkboxRenderer: ((state: TreeCheckboxState) => unknown) | undefined;\n\n #depthsWithIcons = new Set<number>();\n\n protected override render() {\n this.#detectIcons();\n return super.render();\n }\n\n #detectIcons() {\n this.#depthsWithIcons.clear();\n const visit = (node: TreeNode, depth: number) => {\n if (node.item.icon) {\n this.#depthsWithIcons.add(depth);\n }\n if (node.children) {\n for (const child of node.children) {\n visit(child, depth + 1);\n }\n }\n };\n if (this.root) {\n visit(this.root, 0);\n }\n }\n\n protected override renderNodeContent(node: TreeNode, depth: number): TemplateResult {\n const isBranch = node.item.isLeaf !== true;\n const isExpanded = this._isExpanded(node);\n const icon = node.item.icon;\n const tooltip = node.item.tooltip;\n\n const caretAndLabel = html`\n <span class=\"tree-caret\">\n ${isBranch ? html`<ixfx-icon class=\"caret-icon\" name=\"chevron-down\" .rotate=${isExpanded ? nothing : `-90deg`}></ixfx-icon>` : ``}\n </span>\n ${icon ? html`<ixfx-icon class=\"tree-icon\" name=\"${icon}\"></ixfx-icon>` : nothing}\n <span class=\"tree-label\">\n ${node.item.label}\n ${tooltip ? html`<ixfx-tooltip class=\"tree-tooltip\" anchor=\"--tooltip-tree-${node.item.key}\" .value=${tooltip}></ixfx-tooltip>` : nothing}\n </span>\n `;\n\n if (this.interactionMode === `checked`) {\n const state = computeCheckboxState(node, this._selectedNodes);\n return html`${this.#renderCheckbox(node, depth, state)}${caretAndLabel}`;\n }\n\n return caretAndLabel;\n }\n\n #renderCheckbox(node: TreeNode, depth: number, state: TreeCheckboxState): TemplateResult {\n const onClick = (e: MouseEvent) => this._handleCheckboxClick(e, node, depth);\n\n if (this.checkboxRenderer) {\n const ariaChecked = state === `indeterminate` ? `mixed` : state === `checked` ? `true` : `false`;\n return html`\n <span\n class=\"tree-checkbox tree-checkbox-custom\"\n part=\"checkbox\"\n role=\"checkbox\"\n tabindex=\"-1\"\n aria-checked=${ariaChecked}\n aria-label=\"Select ${node.item.label}\"\n @click=${onClick}\n >${this.checkboxRenderer(state)}</span>`;\n }\n\n return html`\n <input\n type=\"checkbox\"\n class=\"tree-checkbox\"\n part=\"checkbox\"\n .checked=${state === `checked`}\n .indeterminate=${state === `indeterminate`}\n aria-label=\"Select ${node.item.label}\"\n @click=${onClick}\n />`;\n }\n\n protected override _renderNode(node: TreeNode, depth: number): TemplateResult {\n const selected = this._isSelected(node);\n const tickled = this._isFocused(node);\n const isBranch = node.item.isLeaf !== true;\n const isExpanded = this._isExpanded(node);\n const isPending = this._pendingNode === node;\n const hasIconAtDepth = this.#depthsWithIcons.has(depth);\n\n return html`\n <li\n class=\"item ${selected ? `selected` : ``} ${tickled ? `tickled` : ``} ${isBranch ? `branch` : `leaf`} ${hasIconAtDepth ? `has-icon-at-depth` : ``}\"\n data-key=${node.item.key}\n role=\"treeitem\"\n aria-selected=${selected}\n aria-expanded=${isBranch ? isExpanded : nothing}\n aria-level=${depth + 1}\n style=${`--depth: ${depth}`}\n @click=${(e: MouseEvent) => this._handleItemClick(node, depth, e)}\n @pointerenter=${() => this.tickled.handlePointerEnter(node.item.key)}\n >\n ${this.renderNodeContent(node, depth)}\n ${isPending ? this.renderLoadingIndicator() : nothing}\n </li>\n `;\n }\n\n static override styles = [\n themeFallbacks,\n ...TreeBaseElement.styles,\n css`\n .tree-caret {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: var(--tree-caret-size);\n height: var(--tree-caret-size);\n flex-shrink: 0;\n margin-right: var(--space-xs);\n }\n\n .caret-icon {\n width: var(--tree-caret-size);\n height: var(--tree-caret-size);\n color: var(--tree-muted-text);\n transition: transform var(--transition, 0.15s ease-out);\n }\n\n .item.selected .caret-icon {\n color: var(--accent-text);\n }\n\n .tree-icon {\n flex-shrink: 0;\n width: var(--icon-size, 1em);\n height: var(--icon-size, 1em);\n margin-right: var(--icon-spacing, 0.3em);\n }\n\n .item:not(.has-icon-at-depth) .tree-icon {\n display: none;\n }\n\n .item.has-icon-at-depth .tree-label {\n margin-left: var(--icon-size, 1em);\n }\n\n .tree-tooltip {\n position: absolute;\n anchor-name: --tooltip-tree;\n }\n\n .tree-checkbox {\n flex-shrink: 0;\n margin-right: var(--space-xs, 3px);\n cursor: pointer;\n accent-color: var(--accent);\n pointer-events: auto;\n }\n\n .tree-checkbox-custom {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n /* Reserves a click target even when the renderer returns empty content\n (e.g. the \"nothing when unchecked\" subtle style) — without an explicit\n size the empty span collapses to 0×0 and becomes unclickable. */\n width: var(--tree-checkbox-size, 14px);\n height: var(--tree-checkbox-size, 14px);\n }\n\n /* In checked mode the checkbox communicates selection state —\n suppress the row highlight so it doesn't double-signal */\n :host([interaction-mode=\"checked\"]) .item.selected {\n background: transparent;\n color: inherit;\n outline: none;\n }\n\n \n\n .tree-label {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n position: relative;\n }\n `,\n ];\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'ixfx-tree-list': TreeListElement;\n }\n}\n\nexport interface TreeListElement {\n addEventListener: (<K extends keyof TreeBaseElementEventMap>(\n type: K,\n listener: (this: TreeListElement, event: TreeBaseElementEventMap[K]) => void,\n options?: boolean | AddEventListenerOptions,\n ) => void) & ((\n type: string,\n listener: EventListenerOrEventListenerObject,\n options?: boolean | AddEventListenerOptions,\n ) => void);\n}\n","import type { TraversableTree } from '@ixfx/collections';\nimport type { TreeBaseElement } from './tree-base.js';\nimport type { TreeItem, TreeNode } from './types.js';\n\nexport type TreeControllerOptions<T> = {\n valueToItem: (value: T, node: TraversableTree<T>) => TreeItem;\n};\n\nexport class TreeController<T> {\n #root: TraversableTree<T>;\n #element: TreeBaseElement;\n #options: TreeControllerOptions<T>;\n\n #nodeToTree = new WeakMap<TreeNode, TraversableTree<T>>();\n #identityToTreeNode = new Map<unknown, TraversableTree<T>>();\n\n #connectSignal: AbortController | undefined;\n\n constructor(\n root: TraversableTree<T>,\n element: TreeBaseElement,\n options: TreeControllerOptions<T>,\n ) {\n this.#root = root;\n this.#element = element;\n this.#options = options;\n }\n\n connect() {\n this.disconnect();\n this.#connectSignal = new AbortController();\n const { signal } = this.#connectSignal;\n\n const rootValue = this.#root.getValue();\n const rootItem = this.#options.valueToItem(rootValue, this.#root);\n const rootChildren = this.#childrenOf(this.#root);\n\n this.#element.root = { item: rootItem, children: rootChildren };\n\n this.#element.addEventListener(`expand`, (e: Event) => {\n const { node } = (e as CustomEvent<{ node: TreeNode }>).detail;\n const treeNode = this.#nodeToTree.get(node);\n if (!treeNode)\n return;\n node.children = this.#childrenOf(treeNode);\n this.#element.requestUpdate();\n }, { signal });\n\n this.#element.addEventListener(`collapse`, (_e: Event) => {\n }, { signal });\n }\n\n disconnect() {\n this.#connectSignal?.abort();\n this.#connectSignal = undefined;\n }\n\n navigateToValue(predicate: (value: T) => boolean): boolean {\n const treePath = this.#findTreePath(this.#root, predicate);\n if (!treePath)\n return false;\n\n const keys: string[] = [];\n\n for (let i = 1; i < treePath.length; i++) {\n const treeNode = treePath[i];\n const key = this.#options.valueToItem(treeNode.getValue(), treeNode).key;\n keys.push(key);\n\n if (i < treePath.length - 1) {\n const currentChildren = i === 1\n ? this.#element.root?.children ?? []\n : this.#findInChildren(this.#element.root?.children ?? [], keys);\n\n const treeNodeMap = this.#nodeToTree;\n const node = currentChildren.find(n => n.item.key === key);\n if (!node)\n return false;\n\n if (node.children === undefined) {\n const mapped = treeNodeMap.get(node);\n if (mapped)\n node.children = this.#childrenOf(mapped);\n }\n }\n }\n\n return this.#element.navigateTo(keys);\n }\n\n #findInChildren(children: TreeNode[], keys: string[]): TreeNode[] {\n if (keys.length === 0)\n return children;\n const key = keys[0];\n const found = children.find(n => n.item.key === key);\n return found?.children ?? [];\n }\n\n #childrenOf(treeNode: TraversableTree<T>): TreeNode[] {\n return Array.from(treeNode.children(), child => this.#toTreeNode(child));\n }\n\n #toTreeNode(treeNode: TraversableTree<T>): TreeNode {\n const value = treeNode.getValue();\n const childList = [...treeNode.children()];\n const userItem = this.#options.valueToItem(value, treeNode);\n\n const isLeaf = userItem.isLeaf ?? childList.length === 0;\n\n const treeNodeMap: TreeNode = {\n item: { ...userItem, isLeaf },\n };\n\n this.#nodeToTree.set(treeNodeMap, treeNode);\n this.#identityToTreeNode.set(treeNode.getIdentity(), treeNode);\n return treeNodeMap;\n }\n\n #findTreePath(\n node: TraversableTree<T>,\n predicate: (value: T) => boolean,\n ancestors: Array<TraversableTree<T>> = [],\n ): Array<TraversableTree<T>> | undefined {\n const path = [...ancestors, node];\n if (predicate(node.getValue()))\n return path;\n for (const child of node.children()) {\n const result = this.#findTreePath(child, predicate, path);\n if (result)\n return result;\n }\n return undefined;\n }\n}\n"],"mappings":"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"}