@ixfx/components 0.12.0 → 0.13.1

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 (130) hide show
  1. package/bundle/index.d.ts +337 -73
  2. package/bundle/index.d.ts.map +1 -1
  3. package/bundle/index.js +7396 -6523
  4. package/bundle/index.js.map +1 -1
  5. package/dist/ac-text.d.ts +1 -1
  6. package/dist/ac-text.js +1 -1
  7. package/dist/{ac-token--_vCHXWw.d.ts → ac-token-TCLAZeVg.d.ts} +2 -2
  8. package/dist/{ac-token--_vCHXWw.d.ts.map → ac-token-TCLAZeVg.d.ts.map} +1 -1
  9. package/dist/ac-token.d.ts +1 -1
  10. package/dist/ac-token.js +1 -1
  11. package/dist/{button-BYuf72aR.d.ts → button-CTL45aBl.d.ts} +2 -2
  12. package/dist/{button-BYuf72aR.d.ts.map → button-CTL45aBl.d.ts.map} +1 -1
  13. package/dist/button.d.ts +1 -1
  14. package/dist/{checked-select-Dejl13qd.js → checked-select-lDgAQQ7v.js} +3 -3
  15. package/dist/{checked-select-Dejl13qd.js.map → checked-select-lDgAQQ7v.js.map} +1 -1
  16. package/dist/checked-select.js +1 -1
  17. package/dist/colour-picker.d.ts +1 -1
  18. package/dist/crumbs.d.ts +4 -4
  19. package/dist/crumbs.d.ts.map +1 -1
  20. package/dist/crumbs.js +26 -10
  21. package/dist/crumbs.js.map +1 -1
  22. package/dist/{data-grid-Cl_CPSD7.d.ts → data-grid-Ck1Qx2H1.d.ts} +5 -5
  23. package/dist/{data-grid-Cl_CPSD7.d.ts.map → data-grid-Ck1Qx2H1.d.ts.map} +1 -1
  24. package/dist/data-grid.d.ts +2 -2
  25. package/dist/data-grid.js +3 -3
  26. package/dist/{detail-list-lE_5UWVl.d.ts → detail-list-C1xzr1z7.d.ts} +5 -5
  27. package/dist/{detail-list-lE_5UWVl.d.ts.map → detail-list-C1xzr1z7.d.ts.map} +1 -1
  28. package/dist/detail-list.d.ts +3 -3
  29. package/dist/detail-list.js +1 -1
  30. package/dist/editable-label-rzXkjf1H.d.ts.map +1 -1
  31. package/dist/editable-label.js +2 -2
  32. package/dist/editable-label.js.map +1 -1
  33. package/dist/flat-list-DEDG6epz.d.ts +181 -0
  34. package/dist/flat-list-DEDG6epz.d.ts.map +1 -0
  35. package/dist/flat-list.d.ts +2 -0
  36. package/dist/flat-list.js +654 -0
  37. package/dist/flat-list.js.map +1 -0
  38. package/dist/{grid-list-Bwaf61is.js → grid-list-DjIl910A.js} +3 -3
  39. package/dist/{grid-list-Bwaf61is.js.map → grid-list-DjIl910A.js.map} +1 -1
  40. package/dist/{grid-list-Bc-RkDoO.d.ts → grid-list-pefLORIr.d.ts} +5 -5
  41. package/dist/{grid-list-Bc-RkDoO.d.ts.map → grid-list-pefLORIr.d.ts.map} +1 -1
  42. package/dist/grid-list.d.ts +2 -2
  43. package/dist/grid-list.js +1 -1
  44. package/dist/{grouped-item-lister-DETte0Dh.d.ts → grouped-item-lister-Bhyo-dkH.d.ts} +2 -2
  45. package/dist/{grouped-item-lister-DETte0Dh.d.ts.map → grouped-item-lister-Bhyo-dkH.d.ts.map} +1 -1
  46. package/dist/grouped-item-lister.d.ts +1 -1
  47. package/dist/{icon-CDGprSdE.d.ts → icon-DR_3rSyZ.d.ts} +2 -2
  48. package/dist/icon-DR_3rSyZ.d.ts.map +1 -0
  49. package/dist/icons.d.ts +2 -2
  50. package/dist/incr-search.d.ts +1 -1
  51. package/dist/{index-tq6f8007.d.ts → index-BOUaDIW-.d.ts} +2 -2
  52. package/dist/{index-tq6f8007.d.ts.map → index-BOUaDIW-.d.ts.map} +1 -1
  53. package/dist/{index-CoqtMSd9.d.ts → index-BUB5SICW.d.ts} +2 -2
  54. package/dist/{index-CoqtMSd9.d.ts.map → index-BUB5SICW.d.ts.map} +1 -1
  55. package/dist/{index-UvnoZUAo.d.ts → index-CJ5fshD3.d.ts} +2 -2
  56. package/dist/{index-UvnoZUAo.d.ts.map → index-CJ5fshD3.d.ts.map} +1 -1
  57. package/dist/{index-CMsmZ6Or.d.ts → index-CxZ61DO1.d.ts} +4 -4
  58. package/dist/{index-CMsmZ6Or.d.ts.map → index-CxZ61DO1.d.ts.map} +1 -1
  59. package/dist/{index-CSEtqm1T.d.ts → index-Dq30pOnn.d.ts} +4 -4
  60. package/dist/{index-CSEtqm1T.d.ts.map → index-Dq30pOnn.d.ts.map} +1 -1
  61. package/dist/{index-CYndvTWH.d.ts → index-garxxqgV.d.ts} +2 -2
  62. package/dist/{index-CYndvTWH.d.ts.map → index-garxxqgV.d.ts.map} +1 -1
  63. package/dist/index-o-CWDt11.d.ts +78 -0
  64. package/dist/index-o-CWDt11.d.ts.map +1 -0
  65. package/dist/index.d.ts +26 -25
  66. package/dist/index.d.ts.map +1 -1
  67. package/dist/index.js +10 -9
  68. package/dist/index.js.map +1 -1
  69. package/dist/{interaction-BskmlA9t.js → interaction-ZTh4htzr.js} +1 -1
  70. package/dist/{labelled-input-base-DiTme1JO.d.ts → labelled-input-base-Dp_9KP0G.d.ts} +2 -2
  71. package/dist/{labelled-input-base-DiTme1JO.d.ts.map → labelled-input-base-Dp_9KP0G.d.ts.map} +1 -1
  72. package/dist/labelled-radial-input.d.ts +2 -2
  73. package/dist/labelled-radial-input.d.ts.map +1 -1
  74. package/dist/labelled-range-input.d.ts +1 -1
  75. package/dist/labelled-slider-input.d.ts +1 -1
  76. package/dist/list-group-header.d.ts +1 -1
  77. package/dist/{list-native-drag-controller-CllsHLxw.d.ts → list-native-drag-controller-ChIi3eK5.d.ts} +2 -2
  78. package/dist/{list-native-drag-controller-CllsHLxw.d.ts.map → list-native-drag-controller-ChIi3eK5.d.ts.map} +1 -1
  79. package/dist/{menu-DaAlsyhf.js → menu-CwcXXeLM.js} +3 -3
  80. package/dist/{menu-DaAlsyhf.js.map → menu-CwcXXeLM.js.map} +1 -1
  81. package/dist/{menu-item-bF6narIz.d.ts → menu-item-B0zqw2xp.d.ts} +2 -2
  82. package/dist/{menu-item-bF6narIz.d.ts.map → menu-item-B0zqw2xp.d.ts.map} +1 -1
  83. package/dist/{menu-item-CDPFgaSF.js → menu-item-BDEekBVy.js} +3 -3
  84. package/dist/{menu-item-CDPFgaSF.js.map → menu-item-BDEekBVy.js.map} +1 -1
  85. package/dist/menu.d.ts +2 -2
  86. package/dist/menu.js +2 -2
  87. package/dist/miller.d.ts +4 -4
  88. package/dist/miller.js +2 -2
  89. package/dist/narrowed-text.d.ts +2 -78
  90. package/dist/narrowed-text.js +2 -2
  91. package/dist/panel.d.ts +1 -1
  92. package/dist/select-horiz.js +2 -2
  93. package/dist/{split-layout-JK3xWVX2.d.ts → split-layout-C5SYbDrq.d.ts} +2 -2
  94. package/dist/{split-layout-JK3xWVX2.d.ts.map → split-layout-C5SYbDrq.d.ts.map} +1 -1
  95. package/dist/split-layout.d.ts +1 -1
  96. package/dist/{tab-list-pqfKaVAE.d.ts → tab-list-v8mKX7Fu.d.ts} +2 -2
  97. package/dist/{tab-list-pqfKaVAE.d.ts.map → tab-list-v8mKX7Fu.d.ts.map} +1 -1
  98. package/dist/tabs.d.ts +1 -1
  99. package/dist/{tickled-controller-Dh1Uh_vG.js → tickled-controller-D-HZymaZ.js} +10 -1
  100. package/dist/tickled-controller-D-HZymaZ.js.map +1 -0
  101. package/dist/{tickled-controller-C1ZsHpyg.d.ts → tickled-controller-DodxD3eQ.d.ts} +7 -1
  102. package/dist/{tickled-controller-C1ZsHpyg.d.ts.map → tickled-controller-DodxD3eQ.d.ts.map} +1 -1
  103. package/dist/{titlebar-Tqu-VvMU.d.ts → titlebar-B5zSTZnF.d.ts} +2 -2
  104. package/dist/{titlebar-Tqu-VvMU.d.ts.map → titlebar-B5zSTZnF.d.ts.map} +1 -1
  105. package/dist/titlebar.d.ts +1 -1
  106. package/dist/{tree-Bm2TzTfe.js → tree-DGveys4K.js} +3 -3
  107. package/dist/{tree-Bm2TzTfe.js.map → tree-DGveys4K.js.map} +1 -1
  108. package/dist/{tree-component-DukLqesg.d.ts → tree-component-Cj8V3uar.d.ts} +2 -2
  109. package/dist/{tree-component-DukLqesg.d.ts.map → tree-component-Cj8V3uar.d.ts.map} +1 -1
  110. package/dist/tree.d.ts +5 -5
  111. package/dist/tree.js +1 -1
  112. package/dist/{types-D-kYF16b.d.ts → types-DQ7KOUIj.d.ts} +6 -1
  113. package/dist/types-DQ7KOUIj.d.ts.map +1 -0
  114. package/dist/{vertical-list-DTp-UIUE.d.ts → vertical-list-CqBc-t8w.d.ts} +86 -4
  115. package/dist/vertical-list-CqBc-t8w.d.ts.map +1 -0
  116. package/dist/vertical-list.d.ts +3 -3
  117. package/dist/vertical-list.js +211 -3
  118. package/dist/vertical-list.js.map +1 -1
  119. package/docs-user/README.md +1 -0
  120. package/docs-user/flat-list.md +110 -0
  121. package/docs-user/index.json +7 -2
  122. package/docs-user/llms.txt +1 -0
  123. package/docs-user/vertical-list.md +59 -3
  124. package/llms.txt +1 -0
  125. package/package.json +5 -1
  126. package/dist/icon-CDGprSdE.d.ts.map +0 -1
  127. package/dist/narrowed-text.d.ts.map +0 -1
  128. package/dist/tickled-controller-Dh1Uh_vG.js.map +0 -1
  129. package/dist/types-D-kYF16b.d.ts.map +0 -1
  130. package/dist/vertical-list-DTp-UIUE.d.ts.map +0 -1
@@ -0,0 +1,654 @@
1
+ import { t as TickledController } from "./tickled-controller-D-HZymaZ.js";
2
+ import { t as themeFallbacks } from "./fallbacks-DQ_5OsmR.js";
3
+ import { n as safeCustomElement, t as __decorate } from "./decorate-VRK8rslU.js";
4
+ import "./icon-1gpwtPx8.js";
5
+ import { t as collectDataResult } from "./data-provider-35TyWclX.js";
6
+ import "./crumbs.js";
7
+ import { n as appearanceColor, t as appearanceAbsent } from "./item-appearance-BjcMyauw.js";
8
+ import "./vertical-list.js";
9
+ import { LitElement, css, html, nothing } from "lit";
10
+ import { property, state } from "lit/decorators.js";
11
+ //#region src/flat-list/flat-list.ts
12
+ /**
13
+ * A node with children, or whose children are still to be loaded.
14
+ * `isLeaf: false` forces a branch even when `children` is `[]` (an empty
15
+ * folder that can still be entered); `isLeaf: true` forces a leaf.
16
+ */
17
+ function isBranch(node) {
18
+ return node.item.isLeaf === false || node.item.isLeaf !== true && node.children?.length !== 0;
19
+ }
20
+ function sameKeys(a, b) {
21
+ return a.length === b.length && a.every((k, i) => k === b[i]);
22
+ }
23
+ function sameNodes(a, b) {
24
+ return a.length === b.length && a.every((row, i) => row.node === b[i].node && row.branch === b[i].branch);
25
+ }
26
+ /**
27
+ * Walk `keys` down from `root`, stopping at the first key that no longer
28
+ * resolves. Returns the nodes visited (root first), so the valid path is
29
+ * `nodes.length - 1` keys long.
30
+ */
31
+ function resolvePath(root, keys) {
32
+ if (!root) return [];
33
+ const nodes = [root];
34
+ for (const key of keys) {
35
+ const next = nodes.at(-1).children?.find((n) => n.item.key === key);
36
+ if (!next) break;
37
+ nodes.push(next);
38
+ }
39
+ return nodes;
40
+ }
41
+ /** Key path (excluding root) to `target`, or `undefined` if it is not in the loaded tree. */
42
+ function findKeyPath(root, target) {
43
+ if (root === target) return [];
44
+ for (const child of root.children ?? []) {
45
+ const sub = findKeyPath(child, target);
46
+ if (sub) return [child.item.key, ...sub];
47
+ }
48
+ }
49
+ function orderRows(children, branchOrder, sort) {
50
+ const rows = children.map((node) => ({
51
+ key: node.item.key,
52
+ node,
53
+ branch: isBranch(node)
54
+ }));
55
+ const sorted = sort ? [...rows].sort((a, b) => sort(a.node, b.node)) : rows;
56
+ if (branchOrder === `mixed`) return sorted;
57
+ return [...sorted.filter((r) => r.branch), ...sorted.filter((r) => !r.branch)];
58
+ }
59
+ let FlatListElement = class FlatListElement extends LitElement {
60
+ constructor(..._args) {
61
+ super(..._args);
62
+ this.path = [];
63
+ this.branchOrder = `first`;
64
+ this.selectionMode = `single`;
65
+ this.interactionMode = `standard`;
66
+ this.itemWrap = `stop`;
67
+ this.itemsDraggable = false;
68
+ this.itemsDroppable = false;
69
+ this.virtualize = false;
70
+ this.itemHeight = 0;
71
+ this.withUp = true;
72
+ this.hideRoot = false;
73
+ this.emptyText = `Empty`;
74
+ this._focusVisible = false;
75
+ this._rows = [];
76
+ this._loading = false;
77
+ this.#lastPath = [];
78
+ this.#selected = /* @__PURE__ */ new Set();
79
+ this.#direction = `jump`;
80
+ this.#canDropOn = (el, dataTransfer) => {
81
+ const node = this.#nodeForElement(el);
82
+ return !!node && (this.canDrop?.(node, dataTransfer) ?? true);
83
+ };
84
+ this.#onListDragOver = (e) => {
85
+ if (this.itemsDroppable) e.preventDefault();
86
+ };
87
+ this.#onListDrop = (e) => {
88
+ const node = this.#nodeForKey(e.detail.key) ?? this.currentNode;
89
+ if (!node) return;
90
+ this.dispatchEvent(new CustomEvent(`flat-list-drop`, {
91
+ detail: {
92
+ node,
93
+ dataTransfer: e.detail.dataTransfer
94
+ },
95
+ bubbles: true,
96
+ composed: true
97
+ }));
98
+ };
99
+ this.#onListContextMenu = (e) => {
100
+ const node = this.#nodeForKey(e.detail.key);
101
+ if (!node) return;
102
+ const nodes = [...e.detail.selectedKeys].map((k) => this.#nodeForKey(k)).filter((n) => n !== void 0);
103
+ if (!this.dispatchEvent(new CustomEvent(`flat-list-context-menu`, {
104
+ detail: {
105
+ node,
106
+ nodes,
107
+ x: e.detail.x,
108
+ y: e.detail.y
109
+ },
110
+ bubbles: true,
111
+ composed: true,
112
+ cancelable: true
113
+ }))) e.preventDefault();
114
+ };
115
+ this.#validateEditKey = (value, key) => {
116
+ const node = this.#nodeForKey(key);
117
+ return !!node && (this.validateEdit?.(value, node) ?? false);
118
+ };
119
+ this.#onListEditCommit = (e) => {
120
+ const node = this.#nodeForKey(e.detail.key);
121
+ if (node) this.dispatchEvent(new CustomEvent(`flat-list-edit-commit`, {
122
+ detail: {
123
+ node,
124
+ value: e.detail.value
125
+ },
126
+ bubbles: true,
127
+ composed: true
128
+ }));
129
+ };
130
+ this.#onListEditCancel = (e) => {
131
+ const node = this.#nodeForKey(e.detail.key);
132
+ if (node) this.dispatchEvent(new CustomEvent(`flat-list-edit-cancel`, {
133
+ detail: { node },
134
+ bubbles: true,
135
+ composed: true
136
+ }));
137
+ };
138
+ this.#onListSelect = (e) => {
139
+ const previous = this.#selected;
140
+ const nodes = [...e.detail.selectedKeys].map((k) => this.#nodeForKey(k)).filter((n) => n !== void 0);
141
+ this.#selected = new Set(nodes);
142
+ this.dispatchEvent(new CustomEvent(`flat-list-select`, {
143
+ detail: {
144
+ selected: this.#selected,
145
+ selectedKeys: e.detail.selectedKeys,
146
+ previous
147
+ },
148
+ bubbles: true,
149
+ composed: true
150
+ }));
151
+ };
152
+ this.#onListActivate = (e) => {
153
+ const key = e.detail.item.getAttribute(`data-key`) ?? void 0;
154
+ const node = this.#nodeForKey(key);
155
+ if (!node) return;
156
+ if (isBranch(node)) {
157
+ this.navigateInto(node.item.key);
158
+ return;
159
+ }
160
+ this.dispatchEvent(new CustomEvent(`flat-list-activate`, {
161
+ detail: { node },
162
+ bubbles: true,
163
+ composed: true
164
+ }));
165
+ };
166
+ this.#syncFocusVisible = () => {
167
+ this._focusVisible = this.list?.matches(`:focus-visible`) ?? false;
168
+ };
169
+ this.#onKeyDown = (e) => {
170
+ if (e.defaultPrevented || e.ctrlKey || e.metaKey || e.altKey || e.shiftKey) return;
171
+ const origin = e.composedPath()[0];
172
+ if (origin instanceof HTMLInputElement || origin instanceof HTMLTextAreaElement) return;
173
+ if (e.key === `ArrowRight` || e.key === `ArrowLeft` || e.key === `Backspace`) TickledController.ignoreHoverUntilPointerMoves();
174
+ if (e.key === `ArrowRight`) {
175
+ const node = this.#nodeForKey(this.list?.tickledKey);
176
+ if (node && isBranch(node)) {
177
+ e.preventDefault();
178
+ this.navigateInto(node.item.key);
179
+ }
180
+ } else if (e.key === `ArrowLeft` || e.key === `Backspace`) {
181
+ if (this.navigateUp()) e.preventDefault();
182
+ }
183
+ };
184
+ this.#onCrumbSelect = (e) => {
185
+ e.stopPropagation();
186
+ const target = [...e.detail.selected].at(-1);
187
+ const root = this.#root;
188
+ if (!target || !root) return;
189
+ const keys = findKeyPath(root, target);
190
+ if (!keys) return;
191
+ if (isBranch(target)) {
192
+ const direction = keys.length < this.path.length && keys.every((k, i) => k === this.path[i]) ? `up` : `jump`;
193
+ this.#setPath(keys, direction);
194
+ } else {
195
+ const levelPath = keys.slice(0, -1);
196
+ const sameLevel = sameKeys(levelPath, this.path);
197
+ this.#setPath(levelPath, `jump`);
198
+ this.#pendingSelectKey = target.item.key;
199
+ if (sameLevel) this.#settleList();
200
+ }
201
+ };
202
+ this.#renderPlainRow = (row) => this.#renderRow(row, false);
203
+ this.#renderEditableRow = (row) => this.#renderRow(row, true);
204
+ this.#renderIcon = (row) => {
205
+ const { node } = row;
206
+ return node.item.icon ? html`<ixfx-icon name=${node.item.icon}></ixfx-icon>` : void 0;
207
+ };
208
+ this.#renderBadge = (row) => {
209
+ const { node, branch } = row;
210
+ const badge = appearanceAbsent(node.item.badge) ? nothing : node.item.badge;
211
+ const textColor = appearanceColor(node.item.textColor);
212
+ return html`${badge}${branch ? html`<span
213
+ class="chevron"
214
+ aria-hidden="true"
215
+ style=${textColor ? `color:${textColor}` : nothing}
216
+ @pointerdown=${(e) => e.stopPropagation()}
217
+ @click=${(e) => this.#onChevronClick(e, node)}
218
+ >›</span>` : nothing}`;
219
+ };
220
+ }
221
+ #modelUnsubscribe;
222
+ #loadController;
223
+ #loadingNode;
224
+ #lastPath;
225
+ /** Branch key to put the cursor on after the next rows arrive (set when going up). */
226
+ #pendingFocusKey;
227
+ #selected;
228
+ #direction;
229
+ /** Leaf key to select once the list shows its level (set when a leaf crumb is chosen). */
230
+ #pendingSelectKey;
231
+ /** The inner list, for registry / search commands and other advanced use. */
232
+ get list() {
233
+ return this.renderRoot.querySelector(`ixfx-vertical-list`) ?? void 0;
234
+ }
235
+ /** Command registry carrying the inner list's `list.search.*` commands. */
236
+ get registry() {
237
+ return this.list?.registry;
238
+ }
239
+ /** Run a command on the inner list, e.g. `dispatch('list.search.filter')`. */
240
+ dispatch(commandId, args) {
241
+ this.list?.dispatch(commandId, args);
242
+ }
243
+ /**
244
+ * Rename a row in place. Needs `validateEdit`, and only rows whose
245
+ * `item.editable` is true can be edited. Ends with `flat-list-edit-commit` or `flat-list-edit-cancel`; the
246
+ * label is not rewritten — update the tree in response. Returns false if
247
+ * the row is unknown, not editable, or not on screen.
248
+ */
249
+ beginEdit(key) {
250
+ const node = this.#nodeForKey(key);
251
+ if (!this.validateEdit || !node?.item.editable) return false;
252
+ return this.list?.beginEdit(key) ?? false;
253
+ }
254
+ /** The node whose children are listed. */
255
+ get currentNode() {
256
+ return resolvePath(this.#root, this.path).at(-1);
257
+ }
258
+ get selectedNodes() {
259
+ return this.#selected;
260
+ }
261
+ get #root() {
262
+ return this.model?.root ?? this.root;
263
+ }
264
+ /** Show the children of the node at this key path. Returns false if the path does not resolve. */
265
+ navigateTo(keys) {
266
+ if (resolvePath(this.#root, keys).length !== keys.length + 1) return false;
267
+ this.#setPath(keys, `jump`);
268
+ return true;
269
+ }
270
+ /** Enter the child branch with this key. */
271
+ navigateInto(key) {
272
+ const node = this.currentNode?.children?.find((n) => n.item.key === key);
273
+ if (!node || !isBranch(node)) return false;
274
+ this.#setPath([...this.path, key], `into`);
275
+ return true;
276
+ }
277
+ /** Move to the parent level. Returns false at the root. */
278
+ navigateUp() {
279
+ if (this.path.length === 0) return false;
280
+ this.#pendingFocusKey = this.path.at(-1);
281
+ this.#setPath(this.path.slice(0, -1), `up`);
282
+ return true;
283
+ }
284
+ #setPath(path, direction) {
285
+ if (sameKeys(path, this.path)) return;
286
+ this.#lastPath = this.path;
287
+ this.#direction = direction;
288
+ this.list?.search.close();
289
+ this.list?.clearSelection();
290
+ this.path = path;
291
+ }
292
+ disconnectedCallback() {
293
+ super.disconnectedCallback();
294
+ this.#modelUnsubscribe?.();
295
+ this.#modelUnsubscribe = void 0;
296
+ this.#loadController?.abort();
297
+ this.#loadingNode = void 0;
298
+ }
299
+ willUpdate(changed) {
300
+ if (changed.has(`model`)) {
301
+ this.#modelUnsubscribe?.();
302
+ this.#modelUnsubscribe = this.model?._subscribe((event) => {
303
+ if (event.type === `invalidate` && event.key === this.currentNode?.item.key) {
304
+ const node = this.currentNode;
305
+ if (node) node.children = void 0;
306
+ }
307
+ this.requestUpdate();
308
+ this.#refreshRows(true);
309
+ });
310
+ }
311
+ const valid = resolvePath(this.#root, this.path).length - 1;
312
+ if (this.#root && valid < this.path.length) this.path = this.path.slice(0, valid);
313
+ if ([
314
+ `root`,
315
+ `model`,
316
+ `path`,
317
+ `branchOrder`,
318
+ `sort`,
319
+ `filterPredicate`,
320
+ `loadChildren`
321
+ ].some((p) => changed.has(p))) this.#refreshRows(changed.has(`root`) || changed.has(`model`) || changed.has(`loadChildren`));
322
+ }
323
+ updated(changed) {
324
+ if (changed.has(`path`) && !sameKeys(this.path, this.#lastPath) && this.#root) {
325
+ const node = this.currentNode;
326
+ if (node) {
327
+ const previousPath = this.#lastPath;
328
+ this.#lastPath = this.path;
329
+ this.dispatchEvent(new CustomEvent(`flat-list-navigate`, {
330
+ detail: {
331
+ path: this.path,
332
+ previousPath,
333
+ node,
334
+ direction: this.#direction
335
+ },
336
+ bubbles: true,
337
+ composed: true
338
+ }));
339
+ this.#direction = `jump`;
340
+ }
341
+ }
342
+ if (changed.has(`_rows`)) this.#settleList();
343
+ if (changed.has(`_rows`) || changed.has(`markedKey`)) this.#applyMarked();
344
+ }
345
+ /** Recompute the level's rows, loading children first when they are not yet known. */
346
+ #refreshRows(reload) {
347
+ const node = this.currentNode;
348
+ if (!node) {
349
+ this._rows = [];
350
+ return;
351
+ }
352
+ if (node.children === void 0 && (this.model || this.loadChildren) && (reload || this.#loadingNode !== node)) {
353
+ this.#load(node);
354
+ return;
355
+ }
356
+ this.#applyRows(node);
357
+ }
358
+ #applyRows(node) {
359
+ const filter = this.filterPredicate;
360
+ const depth = this.path.length;
361
+ const rows = orderRows((node.children ?? []).filter((c) => !filter || filter(c, depth)), this.branchOrder, this.sort);
362
+ if (!sameNodes(rows, this._rows)) this._rows = rows;
363
+ }
364
+ async #load(node) {
365
+ this.#loadController?.abort();
366
+ const controller = new AbortController();
367
+ this.#loadController = controller;
368
+ this.#loadingNode = node;
369
+ this._loading = true;
370
+ this._rows = [];
371
+ try {
372
+ const depth = this.path.length;
373
+ const children = this.model ? await this.model.getChildren(node, depth, controller.signal) : [...await collectDataResult(this.loadChildren({
374
+ node,
375
+ depth
376
+ }, controller.signal), controller.signal)];
377
+ if (controller.signal.aborted) return;
378
+ node.children = [...children];
379
+ } catch {
380
+ if (controller.signal.aborted) return;
381
+ node.children = [];
382
+ }
383
+ this.#loadingNode = void 0;
384
+ this._loading = false;
385
+ if (node === this.currentNode) this.#applyRows(node);
386
+ }
387
+ #nodeForKey(key) {
388
+ return key === void 0 ? void 0 : this._rows.find((r) => r.key === key)?.node;
389
+ }
390
+ /** A single click on a branch's chevron enters it, without selecting the row. */
391
+ #onChevronClick(e, node) {
392
+ e.stopPropagation();
393
+ this.navigateInto(node.item.key);
394
+ }
395
+ #nodeForElement(el) {
396
+ return el ? this.#nodeForKey(el.getAttribute(`data-key`) ?? void 0) : this.currentNode;
397
+ }
398
+ #canDropOn;
399
+ /** Accept every drag that got past `canDrop`; the consumer only hears about the drop. */
400
+ #onListDragOver;
401
+ #onListDrop;
402
+ #onListContextMenu;
403
+ #validateEditKey;
404
+ #onListEditCommit;
405
+ #onListEditCancel;
406
+ #onListSelect;
407
+ #onListActivate;
408
+ #syncFocusVisible;
409
+ #onKeyDown;
410
+ /** A crumb was chosen (or the Up button used): go to that node, or to its parent and select it if it is a leaf. */
411
+ #onCrumbSelect;
412
+ /** Flag the marked row. Rows are rebuilt with the level, so this runs after each rebuild. */
413
+ async #applyMarked() {
414
+ const list = this.list;
415
+ if (!list) return;
416
+ await list.updateComplete;
417
+ for (const row of list.querySelectorAll(`li[data-key]`)) row.toggleAttribute(`data-marked`, row.getAttribute(`data-key`) === this.markedKey);
418
+ }
419
+ /** After the inner list has rebuilt, place the cursor / selection / focus. */
420
+ async #settleList() {
421
+ const list = this.list;
422
+ if (!list) return;
423
+ const hadFocus = this.matches(`:focus-within`);
424
+ await list.updateComplete;
425
+ const select = this.#pendingSelectKey;
426
+ const focusKey = this.#pendingFocusKey ?? select ?? this._rows[0]?.key;
427
+ this.#pendingSelectKey = void 0;
428
+ this.#pendingFocusKey = void 0;
429
+ if (select) list.selectKeys([select]);
430
+ if (focusKey !== void 0) list.focusKey(focusKey);
431
+ if (hadFocus) list.focus();
432
+ }
433
+ /** Two stable formatters, so toggling `validateEdit` rebuilds the rows with or without edit hooks. */
434
+ #renderPlainRow;
435
+ #renderEditableRow;
436
+ #renderRow(row, editingEnabled) {
437
+ const { node } = row;
438
+ const textColor = appearanceColor(node.item.textColor);
439
+ const editable = editingEnabled && node.item.editable === true;
440
+ return html`<span class="label" data-edit-label=${editable ? `` : nothing} data-edit-value=${editable ? node.item.label : nothing} style=${textColor ? `color:${textColor}` : nothing}>${node.item.label}</span>`;
441
+ }
442
+ #renderIcon;
443
+ #renderBadge;
444
+ render() {
445
+ const root = this.#root;
446
+ return html`
447
+ <div class="crumbs" part="crumbs">
448
+ <ixfx-crumb-navigation
449
+ .root=${root}
450
+ .selectedNode=${this.currentNode}
451
+ .loadChildren=${this.loadChildren}
452
+ selection-filter="all"
453
+ pad-ending
454
+ leading-caret
455
+ ?with-up=${this.withUp}
456
+ ?hide-root=${this.hideRoot}
457
+ @select=${this.#onCrumbSelect}
458
+ ></ixfx-crumb-navigation>
459
+ </div>
460
+ <div class="body">
461
+ <ixfx-vertical-list
462
+ .items=${this._rows}
463
+ .formatter=${this.validateEdit ? this.#renderEditableRow : this.#renderPlainRow}
464
+ .icon=${this.#renderIcon}
465
+ .iconColor=${(row) => appearanceColor(row.node.item.iconColor)}
466
+ .badge=${this.#renderBadge}
467
+ key-property="key"
468
+ .selectionMode=${this.selectionMode}
469
+ .interactionMode=${this.interactionMode}
470
+ .itemWrap=${this.itemWrap}
471
+ .searchModes=${this.searchModes}
472
+ ?items-draggable=${this.itemsDraggable}
473
+ ?items-droppable=${this.itemsDroppable}
474
+ .canDrop=${this.#canDropOn}
475
+ .editValidator=${this.#validateEditKey}
476
+ .virtualize=${this.virtualize}
477
+ .itemHeight=${this.itemHeight}
478
+ @list-drag-over=${this.#onListDragOver}
479
+ @list-drop=${this.#onListDrop}
480
+ @list-context-menu=${this.#onListContextMenu}
481
+ @list-edit-commit=${this.#onListEditCommit}
482
+ @list-edit-cancel=${this.#onListEditCancel}
483
+ @list-select=${this.#onListSelect}
484
+ @list-activate=${this.#onListActivate}
485
+ @keydown=${this.#onKeyDown}
486
+ @focusin=${this.#syncFocusVisible}
487
+ @focusout=${this.#syncFocusVisible}
488
+ ></ixfx-vertical-list>
489
+ ${this._rows.length === 0 ? html`<div class="empty" part="empty">${this._loading ? `Loading…` : this.emptyText}</div>` : nothing}
490
+ </div>
491
+ `;
492
+ }
493
+ static {
494
+ this.styles = [themeFallbacks, css`
495
+ :host {
496
+ display: flex;
497
+ flex-direction: column;
498
+ height: 100%;
499
+ min-height: 0;
500
+ box-sizing: border-box;
501
+ overflow: hidden;
502
+ background: var(--flat-list-bg, var(--list-bg, var(--surface-3)));
503
+ color: var(--flat-list-text, var(--list-text, var(--surface-3-text)));
504
+ border: 1px solid var(--flat-list-border, var(--list-border, var(--border)));
505
+ border-radius: var(--flat-list-radius, var(--list-radius, var(--radius-s)));
506
+ }
507
+
508
+ .crumbs {
509
+ flex: none;
510
+ font-size: var(--flat-list-crumb-font-size, var(--text-s));
511
+ padding: var(--flat-list-crumb-padding, var(--space-xs));
512
+ border-bottom: 1px solid var(--flat-list-border, var(--list-border, var(--border)));
513
+ }
514
+
515
+ .body {
516
+ position: relative;
517
+ flex: 1 1 auto;
518
+ min-height: 0;
519
+ }
520
+
521
+ :host([data-focus-visible]) {
522
+ outline: 2px solid var(--accent);
523
+ outline-offset: 2px;
524
+ }
525
+
526
+ ixfx-vertical-list {
527
+ /* The ring is drawn round the whole component instead. */
528
+ outline: none;
529
+ /* Fill .body absolutely: a percentage height does not reliably resolve
530
+ inside a flex item, and an unbounded list never scrolls the cursor into view. */
531
+ position: absolute;
532
+ inset: 0;
533
+ --list-bg: transparent;
534
+ --list-border: none;
535
+ --list-radius: 0;
536
+ }
537
+
538
+ .empty {
539
+ position: absolute;
540
+ inset: 0;
541
+ display: grid;
542
+ place-items: center;
543
+ pointer-events: none;
544
+ color: var(--flat-list-empty-text, var(--surface-muted-text));
545
+ }
546
+
547
+ li[data-marked] {
548
+ background: var(--flat-list-marked-bg, color-mix(in srgb, var(--accent) 18%, transparent));
549
+ box-shadow: inset 3px 0 0 var(--flat-list-marked-bar, var(--accent));
550
+ }
551
+
552
+ .label {
553
+ flex: 1;
554
+ overflow: hidden;
555
+ text-overflow: ellipsis;
556
+ white-space: nowrap;
557
+ }
558
+
559
+ /* Highlight rule for incremental search: the rows live in this shadow root,
560
+ so the page-level ::highlight() rule cannot reach them. */
561
+ ::highlight(vertical-list-search) {
562
+ background: var(--accent);
563
+ color: var(--accent-text, #fff);
564
+ }
565
+
566
+ .chevron {
567
+ margin-inline-start: auto;
568
+ padding-inline-start: 0.5em;
569
+ padding-inline-end: var(--flat-list-chevron-gap, 0.4em);
570
+ /* Inherit the row's text colour, so it follows selected / tickled states. */
571
+ color: inherit;
572
+ }
573
+ `];
574
+ }
575
+ };
576
+ __decorate([property({ attribute: false })], FlatListElement.prototype, "root", void 0);
577
+ __decorate([property({ attribute: false })], FlatListElement.prototype, "model", void 0);
578
+ __decorate([property({ attribute: false })], FlatListElement.prototype, "path", void 0);
579
+ __decorate([property({ attribute: false })], FlatListElement.prototype, "loadChildren", void 0);
580
+ __decorate([property({ attribute: false })], FlatListElement.prototype, "filterPredicate", void 0);
581
+ __decorate([property({ attribute: false })], FlatListElement.prototype, "sort", void 0);
582
+ __decorate([property({
583
+ type: String,
584
+ attribute: `branch-order`,
585
+ reflect: true
586
+ })], FlatListElement.prototype, "branchOrder", void 0);
587
+ __decorate([property({
588
+ type: String,
589
+ attribute: `selection-mode`
590
+ })], FlatListElement.prototype, "selectionMode", void 0);
591
+ __decorate([property({
592
+ type: String,
593
+ attribute: `interaction-mode`
594
+ })], FlatListElement.prototype, "interactionMode", void 0);
595
+ __decorate([property({
596
+ type: String,
597
+ attribute: `item-wrap`
598
+ })], FlatListElement.prototype, "itemWrap", void 0);
599
+ __decorate([property({
600
+ type: String,
601
+ attribute: `search-modes`,
602
+ reflect: true
603
+ })], FlatListElement.prototype, "searchModes", void 0);
604
+ __decorate([property({
605
+ type: Boolean,
606
+ attribute: `items-draggable`,
607
+ reflect: true
608
+ })], FlatListElement.prototype, "itemsDraggable", void 0);
609
+ __decorate([property({
610
+ type: Boolean,
611
+ attribute: `items-droppable`,
612
+ reflect: true
613
+ })], FlatListElement.prototype, "itemsDroppable", void 0);
614
+ __decorate([property({ attribute: false })], FlatListElement.prototype, "canDrop", void 0);
615
+ __decorate([property({ attribute: false })], FlatListElement.prototype, "validateEdit", void 0);
616
+ __decorate([property({
617
+ type: String,
618
+ attribute: `marked-key`,
619
+ reflect: true
620
+ })], FlatListElement.prototype, "markedKey", void 0);
621
+ __decorate([property({
622
+ type: Boolean,
623
+ reflect: true
624
+ })], FlatListElement.prototype, "virtualize", void 0);
625
+ __decorate([property({
626
+ type: Number,
627
+ attribute: `item-height`
628
+ })], FlatListElement.prototype, "itemHeight", void 0);
629
+ __decorate([property({
630
+ type: Boolean,
631
+ attribute: `with-up`,
632
+ reflect: true
633
+ })], FlatListElement.prototype, "withUp", void 0);
634
+ __decorate([property({
635
+ type: Boolean,
636
+ attribute: `hide-root`,
637
+ reflect: true
638
+ })], FlatListElement.prototype, "hideRoot", void 0);
639
+ __decorate([property({
640
+ type: String,
641
+ attribute: `empty-text`
642
+ })], FlatListElement.prototype, "emptyText", void 0);
643
+ __decorate([property({
644
+ type: Boolean,
645
+ reflect: true,
646
+ attribute: `data-focus-visible`
647
+ })], FlatListElement.prototype, "_focusVisible", void 0);
648
+ __decorate([state()], FlatListElement.prototype, "_rows", void 0);
649
+ __decorate([state()], FlatListElement.prototype, "_loading", void 0);
650
+ FlatListElement = __decorate([safeCustomElement(`ixfx-flat-list`)], FlatListElement);
651
+ //#endregion
652
+ export { FlatListElement };
653
+
654
+ //# sourceMappingURL=flat-list.js.map