@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.
- package/bundle/index.d.ts +337 -73
- package/bundle/index.d.ts.map +1 -1
- package/bundle/index.js +7396 -6523
- package/bundle/index.js.map +1 -1
- package/dist/ac-text.d.ts +1 -1
- package/dist/ac-text.js +1 -1
- package/dist/{ac-token--_vCHXWw.d.ts → ac-token-TCLAZeVg.d.ts} +2 -2
- package/dist/{ac-token--_vCHXWw.d.ts.map → ac-token-TCLAZeVg.d.ts.map} +1 -1
- package/dist/ac-token.d.ts +1 -1
- package/dist/ac-token.js +1 -1
- package/dist/{button-BYuf72aR.d.ts → button-CTL45aBl.d.ts} +2 -2
- package/dist/{button-BYuf72aR.d.ts.map → button-CTL45aBl.d.ts.map} +1 -1
- package/dist/button.d.ts +1 -1
- package/dist/{checked-select-Dejl13qd.js → checked-select-lDgAQQ7v.js} +3 -3
- package/dist/{checked-select-Dejl13qd.js.map → checked-select-lDgAQQ7v.js.map} +1 -1
- package/dist/checked-select.js +1 -1
- package/dist/colour-picker.d.ts +1 -1
- package/dist/crumbs.d.ts +4 -4
- package/dist/crumbs.d.ts.map +1 -1
- package/dist/crumbs.js +26 -10
- package/dist/crumbs.js.map +1 -1
- package/dist/{data-grid-Cl_CPSD7.d.ts → data-grid-Ck1Qx2H1.d.ts} +5 -5
- package/dist/{data-grid-Cl_CPSD7.d.ts.map → data-grid-Ck1Qx2H1.d.ts.map} +1 -1
- package/dist/data-grid.d.ts +2 -2
- package/dist/data-grid.js +3 -3
- package/dist/{detail-list-lE_5UWVl.d.ts → detail-list-C1xzr1z7.d.ts} +5 -5
- package/dist/{detail-list-lE_5UWVl.d.ts.map → detail-list-C1xzr1z7.d.ts.map} +1 -1
- package/dist/detail-list.d.ts +3 -3
- package/dist/detail-list.js +1 -1
- package/dist/editable-label-rzXkjf1H.d.ts.map +1 -1
- package/dist/editable-label.js +2 -2
- package/dist/editable-label.js.map +1 -1
- package/dist/flat-list-DEDG6epz.d.ts +181 -0
- package/dist/flat-list-DEDG6epz.d.ts.map +1 -0
- package/dist/flat-list.d.ts +2 -0
- package/dist/flat-list.js +654 -0
- package/dist/flat-list.js.map +1 -0
- package/dist/{grid-list-Bwaf61is.js → grid-list-DjIl910A.js} +3 -3
- package/dist/{grid-list-Bwaf61is.js.map → grid-list-DjIl910A.js.map} +1 -1
- package/dist/{grid-list-Bc-RkDoO.d.ts → grid-list-pefLORIr.d.ts} +5 -5
- package/dist/{grid-list-Bc-RkDoO.d.ts.map → grid-list-pefLORIr.d.ts.map} +1 -1
- package/dist/grid-list.d.ts +2 -2
- package/dist/grid-list.js +1 -1
- package/dist/{grouped-item-lister-DETte0Dh.d.ts → grouped-item-lister-Bhyo-dkH.d.ts} +2 -2
- package/dist/{grouped-item-lister-DETte0Dh.d.ts.map → grouped-item-lister-Bhyo-dkH.d.ts.map} +1 -1
- package/dist/grouped-item-lister.d.ts +1 -1
- package/dist/{icon-CDGprSdE.d.ts → icon-DR_3rSyZ.d.ts} +2 -2
- package/dist/icon-DR_3rSyZ.d.ts.map +1 -0
- package/dist/icons.d.ts +2 -2
- package/dist/incr-search.d.ts +1 -1
- package/dist/{index-tq6f8007.d.ts → index-BOUaDIW-.d.ts} +2 -2
- package/dist/{index-tq6f8007.d.ts.map → index-BOUaDIW-.d.ts.map} +1 -1
- package/dist/{index-CoqtMSd9.d.ts → index-BUB5SICW.d.ts} +2 -2
- package/dist/{index-CoqtMSd9.d.ts.map → index-BUB5SICW.d.ts.map} +1 -1
- package/dist/{index-UvnoZUAo.d.ts → index-CJ5fshD3.d.ts} +2 -2
- package/dist/{index-UvnoZUAo.d.ts.map → index-CJ5fshD3.d.ts.map} +1 -1
- package/dist/{index-CMsmZ6Or.d.ts → index-CxZ61DO1.d.ts} +4 -4
- package/dist/{index-CMsmZ6Or.d.ts.map → index-CxZ61DO1.d.ts.map} +1 -1
- package/dist/{index-CSEtqm1T.d.ts → index-Dq30pOnn.d.ts} +4 -4
- package/dist/{index-CSEtqm1T.d.ts.map → index-Dq30pOnn.d.ts.map} +1 -1
- package/dist/{index-CYndvTWH.d.ts → index-garxxqgV.d.ts} +2 -2
- package/dist/{index-CYndvTWH.d.ts.map → index-garxxqgV.d.ts.map} +1 -1
- package/dist/index-o-CWDt11.d.ts +78 -0
- package/dist/index-o-CWDt11.d.ts.map +1 -0
- package/dist/index.d.ts +26 -25
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -9
- package/dist/index.js.map +1 -1
- package/dist/{interaction-BskmlA9t.js → interaction-ZTh4htzr.js} +1 -1
- package/dist/{labelled-input-base-DiTme1JO.d.ts → labelled-input-base-Dp_9KP0G.d.ts} +2 -2
- package/dist/{labelled-input-base-DiTme1JO.d.ts.map → labelled-input-base-Dp_9KP0G.d.ts.map} +1 -1
- package/dist/labelled-radial-input.d.ts +2 -2
- package/dist/labelled-radial-input.d.ts.map +1 -1
- package/dist/labelled-range-input.d.ts +1 -1
- package/dist/labelled-slider-input.d.ts +1 -1
- package/dist/list-group-header.d.ts +1 -1
- package/dist/{list-native-drag-controller-CllsHLxw.d.ts → list-native-drag-controller-ChIi3eK5.d.ts} +2 -2
- package/dist/{list-native-drag-controller-CllsHLxw.d.ts.map → list-native-drag-controller-ChIi3eK5.d.ts.map} +1 -1
- package/dist/{menu-DaAlsyhf.js → menu-CwcXXeLM.js} +3 -3
- package/dist/{menu-DaAlsyhf.js.map → menu-CwcXXeLM.js.map} +1 -1
- package/dist/{menu-item-bF6narIz.d.ts → menu-item-B0zqw2xp.d.ts} +2 -2
- package/dist/{menu-item-bF6narIz.d.ts.map → menu-item-B0zqw2xp.d.ts.map} +1 -1
- package/dist/{menu-item-CDPFgaSF.js → menu-item-BDEekBVy.js} +3 -3
- package/dist/{menu-item-CDPFgaSF.js.map → menu-item-BDEekBVy.js.map} +1 -1
- package/dist/menu.d.ts +2 -2
- package/dist/menu.js +2 -2
- package/dist/miller.d.ts +4 -4
- package/dist/miller.js +2 -2
- package/dist/narrowed-text.d.ts +2 -78
- package/dist/narrowed-text.js +2 -2
- package/dist/panel.d.ts +1 -1
- package/dist/select-horiz.js +2 -2
- package/dist/{split-layout-JK3xWVX2.d.ts → split-layout-C5SYbDrq.d.ts} +2 -2
- package/dist/{split-layout-JK3xWVX2.d.ts.map → split-layout-C5SYbDrq.d.ts.map} +1 -1
- package/dist/split-layout.d.ts +1 -1
- package/dist/{tab-list-pqfKaVAE.d.ts → tab-list-v8mKX7Fu.d.ts} +2 -2
- package/dist/{tab-list-pqfKaVAE.d.ts.map → tab-list-v8mKX7Fu.d.ts.map} +1 -1
- package/dist/tabs.d.ts +1 -1
- package/dist/{tickled-controller-Dh1Uh_vG.js → tickled-controller-D-HZymaZ.js} +10 -1
- package/dist/tickled-controller-D-HZymaZ.js.map +1 -0
- package/dist/{tickled-controller-C1ZsHpyg.d.ts → tickled-controller-DodxD3eQ.d.ts} +7 -1
- package/dist/{tickled-controller-C1ZsHpyg.d.ts.map → tickled-controller-DodxD3eQ.d.ts.map} +1 -1
- package/dist/{titlebar-Tqu-VvMU.d.ts → titlebar-B5zSTZnF.d.ts} +2 -2
- package/dist/{titlebar-Tqu-VvMU.d.ts.map → titlebar-B5zSTZnF.d.ts.map} +1 -1
- package/dist/titlebar.d.ts +1 -1
- package/dist/{tree-Bm2TzTfe.js → tree-DGveys4K.js} +3 -3
- package/dist/{tree-Bm2TzTfe.js.map → tree-DGveys4K.js.map} +1 -1
- package/dist/{tree-component-DukLqesg.d.ts → tree-component-Cj8V3uar.d.ts} +2 -2
- package/dist/{tree-component-DukLqesg.d.ts.map → tree-component-Cj8V3uar.d.ts.map} +1 -1
- package/dist/tree.d.ts +5 -5
- package/dist/tree.js +1 -1
- package/dist/{types-D-kYF16b.d.ts → types-DQ7KOUIj.d.ts} +6 -1
- package/dist/types-DQ7KOUIj.d.ts.map +1 -0
- package/dist/{vertical-list-DTp-UIUE.d.ts → vertical-list-CqBc-t8w.d.ts} +86 -4
- package/dist/vertical-list-CqBc-t8w.d.ts.map +1 -0
- package/dist/vertical-list.d.ts +3 -3
- package/dist/vertical-list.js +211 -3
- package/dist/vertical-list.js.map +1 -1
- package/docs-user/README.md +1 -0
- package/docs-user/flat-list.md +110 -0
- package/docs-user/index.json +7 -2
- package/docs-user/llms.txt +1 -0
- package/docs-user/vertical-list.md +59 -3
- package/llms.txt +1 -0
- package/package.json +5 -1
- package/dist/icon-CDGprSdE.d.ts.map +0 -1
- package/dist/narrowed-text.d.ts.map +0 -1
- package/dist/tickled-controller-Dh1Uh_vG.js.map +0 -1
- package/dist/types-D-kYF16b.d.ts.map +0 -1
- 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
|