@ixfx/components 0.8.7 → 0.8.9
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 +743 -383
- package/bundle/index.d.ts.map +1 -1
- package/bundle/index.js +2093 -222
- package/bundle/index.js.map +1 -1
- package/dist/crumbs.d.ts +1 -1
- package/dist/crumbs.js +2 -2
- package/dist/{data-grid-CYVPSUwh.d.ts → data-grid-C588pTgt.d.ts} +15 -2
- package/dist/data-grid-C588pTgt.d.ts.map +1 -0
- package/dist/data-grid.d.ts +3 -2
- package/dist/data-grid.js +95 -5
- package/dist/data-grid.js.map +1 -1
- package/dist/{detail-list-vH56z7WE.d.ts → detail-list-IKl76Wnp.d.ts} +22 -5
- package/dist/detail-list-IKl76Wnp.d.ts.map +1 -0
- package/dist/detail-list.d.ts +4 -3
- package/dist/detail-list.js +103 -3
- package/dist/detail-list.js.map +1 -1
- package/dist/{grid-list-Cq8ga2WZ.d.ts → grid-list-BUlUnisU.d.ts} +214 -112
- package/dist/grid-list-BUlUnisU.d.ts.map +1 -0
- package/dist/{grid-list-j5C7qtlG.js → grid-list-D18FNJRH.js} +77 -164
- package/dist/grid-list-D18FNJRH.js.map +1 -0
- package/dist/grid-list.d.ts +3 -2
- package/dist/grid-list.js +1 -1
- package/dist/incr-search.d.ts +1 -1
- package/dist/{index-DDREWqMm.d.ts → index--qnhBWZJ.d.ts} +2 -2
- package/dist/{index-DDREWqMm.d.ts.map → index--qnhBWZJ.d.ts.map} +1 -1
- package/dist/index.d.ts +212 -253
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -12
- package/dist/index.js.map +1 -1
- package/dist/{interaction-B9AAlsFO.js → interaction-BskmlA9t.js} +1 -1
- package/dist/{labelled-slider-input-BCH9968c.js → labelled-slider-input-aEYDvW2S.js} +2 -2
- package/dist/{labelled-slider-input-BCH9968c.js.map → labelled-slider-input-aEYDvW2S.js.map} +1 -1
- package/dist/labelled-slider-input.js +1 -1
- package/dist/{list-item-animation-DuiwgN3l.js → list-item-animation-BUoEKi5s.js} +29 -10
- package/dist/list-item-animation-BUoEKi5s.js.map +1 -0
- package/dist/list-native-drag-controller-CFyrkKUj.d.ts +61 -0
- package/dist/list-native-drag-controller-CFyrkKUj.d.ts.map +1 -0
- package/dist/list-native-drag-controller-DxD6Yjci.js +567 -0
- package/dist/list-native-drag-controller-DxD6Yjci.js.map +1 -0
- package/dist/{menu-HMoxCPY2.js → menu-CMXhIaNZ.js} +3 -3
- package/dist/{menu-HMoxCPY2.js.map → menu-CMXhIaNZ.js.map} +1 -1
- package/dist/{menu-item-BQDYxk5A.js → menu-item-BYGQzyCe.js} +2 -2
- package/dist/{menu-item-BQDYxk5A.js.map → menu-item-BYGQzyCe.js.map} +1 -1
- package/dist/menu.js +2 -2
- package/dist/miller.d.ts +2 -2
- package/dist/miller.js +1 -1
- package/dist/narrowed-text.js +1 -1
- package/dist/plots.js +1 -1
- package/dist/select-horiz.js +1 -1
- package/dist/slider-input-BxURAAmD.d.ts.map +1 -1
- package/dist/{slider-input-WR8_RwHg.js → slider-input-Cb5uSV42.js} +78 -11
- package/dist/slider-input-Cb5uSV42.js.map +1 -0
- package/dist/slider-input.js +1 -1
- package/dist/{tree-CozQj01a.js → tree-a14n9y7i.js} +1055 -32
- package/dist/tree-a14n9y7i.js.map +1 -0
- package/dist/{tree-component-BMpxpdTi.d.ts → tree-component-BW2iCFq9.d.ts} +108 -4
- package/dist/tree-component-BW2iCFq9.d.ts.map +1 -0
- package/dist/tree.d.ts +102 -4
- package/dist/tree.d.ts.map +1 -1
- package/dist/tree.js +2 -2
- package/dist/{vertical-list-BGn9nyKh.d.ts → vertical-list-CHrXmmQ3.d.ts} +21 -4
- package/dist/vertical-list-CHrXmmQ3.d.ts.map +1 -0
- package/dist/vertical-list.d.ts +3 -2
- package/dist/vertical-list.js +103 -3
- package/dist/vertical-list.js.map +1 -1
- package/dist/{xy-axis-CrrVdDFe.js → xy-axis-COKw338Q.js} +2 -2
- package/dist/xy-axis-COKw338Q.js.map +1 -0
- package/dist/xy-axis-DQqd2yXJ.d.ts.map +1 -1
- package/docs-user/data-grid.md +30 -0
- package/docs-user/detail-list.md +29 -0
- package/docs-user/grid-list.md +34 -4
- package/docs-user/index.json +2 -2
- package/docs-user/tree.md +76 -1
- package/docs-user/vertical-list.md +30 -1
- package/package.json +1 -1
- package/dist/data-grid-CYVPSUwh.d.ts.map +0 -1
- package/dist/detail-list-vH56z7WE.d.ts.map +0 -1
- package/dist/grid-list-Cq8ga2WZ.d.ts.map +0 -1
- package/dist/grid-list-j5C7qtlG.js.map +0 -1
- package/dist/list-item-animation-DuiwgN3l.js.map +0 -1
- package/dist/slider-input-WR8_RwHg.js.map +0 -1
- package/dist/tree-CozQj01a.js.map +0 -1
- package/dist/tree-component-BMpxpdTi.d.ts.map +0 -1
- package/dist/vertical-list-BGn9nyKh.d.ts.map +0 -1
- package/dist/xy-axis-CrrVdDFe.js.map +0 -1
|
@@ -3,22 +3,146 @@ import { n as tickledStyles, t as tickledItemStyles } from "./tickled-styles-BrA
|
|
|
3
3
|
import { t as themeFallbacks } from "./fallbacks-BcKGq-2F.js";
|
|
4
4
|
import { n as safeCustomElement, t as __decorate } from "./decorate-VRK8rslU.js";
|
|
5
5
|
import { i as ListSearchController, n as listSearchStyles, o as registerListSearchCommands, r as LIST_SEARCH_MODES, t as createTreeListSearchAdapter } from "./incr-search-C05WjbZh.js";
|
|
6
|
-
import {
|
|
7
|
-
import "./
|
|
6
|
+
import { t as ListNativeDragController } from "./list-native-drag-controller-DxD6Yjci.js";
|
|
7
|
+
import { n as animateListItemIn, r as animateListItemOut } from "./list-item-animation-BUoEKi5s.js";
|
|
8
|
+
import "./interaction-BskmlA9t.js";
|
|
8
9
|
import { t as collectDataResult } from "./data-provider-35TyWclX.js";
|
|
9
10
|
import { LitElement, css, html, nothing } from "lit";
|
|
10
11
|
import { property, state } from "lit/decorators.js";
|
|
11
12
|
import { createCommandRegistry } from "@clinth/ui-commands";
|
|
12
13
|
import { repeat } from "lit/directives/repeat.js";
|
|
13
|
-
//#region src/common/tree-
|
|
14
|
+
//#region src/common/tree-drop-apply.ts
|
|
14
15
|
function findNode(root, key) {
|
|
15
16
|
if (root.item.key === key) return root;
|
|
16
|
-
|
|
17
|
-
for (const child of root.children) {
|
|
17
|
+
for (const child of root.children ?? []) {
|
|
18
18
|
const found = findNode(child, key);
|
|
19
19
|
if (found) return found;
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
|
+
function findParent(root, key, parent = void 0) {
|
|
23
|
+
for (const child of root.children ?? []) {
|
|
24
|
+
if (child.item.key === key) return parent === void 0 ? root : parent;
|
|
25
|
+
const found = findParent(child, key, child);
|
|
26
|
+
if (found) return found;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/** True when `key` is `ancestorKey`, or a descendant of it. */
|
|
30
|
+
function isSelfOrDescendant(root, ancestorKey, key) {
|
|
31
|
+
const ancestor = findNode(root, ancestorKey);
|
|
32
|
+
return ancestor !== void 0 && findNode(ancestor, key) !== void 0;
|
|
33
|
+
}
|
|
34
|
+
/** Remove every node whose key is in `keys` (with its whole subtree), in one pass. */
|
|
35
|
+
function removeKeys(node, keys) {
|
|
36
|
+
if (!node.children) return node;
|
|
37
|
+
return {
|
|
38
|
+
...node,
|
|
39
|
+
children: node.children.filter((c) => !keys.has(c.item.key)).map((c) => removeKeys(c, keys))
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function childrenOf(root, parentKey) {
|
|
43
|
+
if (parentKey === void 0) return root.children ?? [];
|
|
44
|
+
return findNode(root, parentKey)?.children ?? [];
|
|
45
|
+
}
|
|
46
|
+
/** Return a new root with `parentKey`'s children replaced (root itself when `parentKey` is undefined). */
|
|
47
|
+
function withChildren(node, parentKey, children) {
|
|
48
|
+
if (parentKey === void 0) return {
|
|
49
|
+
...node,
|
|
50
|
+
children
|
|
51
|
+
};
|
|
52
|
+
if (node.item.key === parentKey) return {
|
|
53
|
+
...node,
|
|
54
|
+
children
|
|
55
|
+
};
|
|
56
|
+
if (!node.children) return node;
|
|
57
|
+
return {
|
|
58
|
+
...node,
|
|
59
|
+
children: node.children.map((c) => withChildren(c, parentKey, children))
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
function clamp$1(n, min, max) {
|
|
63
|
+
return Math.max(min, Math.min(max, n));
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Drop keys whose ancestor is also being dragged — only the top-most selected
|
|
67
|
+
* nodes move; their subtrees travel with them. Preserves input order.
|
|
68
|
+
*/
|
|
69
|
+
function pruneDraggedDescendants(root, keys) {
|
|
70
|
+
const set = new Set(keys);
|
|
71
|
+
return keys.filter((key) => {
|
|
72
|
+
let parent = findParent(root, key);
|
|
73
|
+
while (parent) {
|
|
74
|
+
if (set.has(parent.item.key)) return false;
|
|
75
|
+
parent = findParent(root, parent.item.key);
|
|
76
|
+
}
|
|
77
|
+
return true;
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Framework-agnostic drop application. Given the current immutable `root` and a
|
|
82
|
+
* resolved drop context, returns a **new** root with the drop applied, or the
|
|
83
|
+
* unchanged `root` when the drop is invalid (dragged node into its own subtree,
|
|
84
|
+
* missing reference, empty payload). Never throws, never mutates.
|
|
85
|
+
*
|
|
86
|
+
* The insertion point is re-derived from `target.referenceKey` + `target.position`
|
|
87
|
+
* against the post-removal tree, so same-parent reorders are off-by-one-safe and
|
|
88
|
+
* multi-node drops land as siblings in drag order. `target.index` is honoured only
|
|
89
|
+
* for `position: 'inside'` (0 = first child, large = append).
|
|
90
|
+
*/
|
|
91
|
+
function applyTreeDrop(root, ctx) {
|
|
92
|
+
const { target, source } = ctx;
|
|
93
|
+
let nodes;
|
|
94
|
+
let removeSet = /* @__PURE__ */ new Set();
|
|
95
|
+
if (source === `internal`) {
|
|
96
|
+
const keys = pruneDraggedDescendants(root, [...ctx.draggedKeys]);
|
|
97
|
+
if (keys.length === 0) return root;
|
|
98
|
+
if (target.parentKey !== void 0) {
|
|
99
|
+
for (const key of keys) if (isSelfOrDescendant(root, key, target.parentKey)) return root;
|
|
100
|
+
}
|
|
101
|
+
if (target.position !== `inside`) {
|
|
102
|
+
for (const key of keys) if (isSelfOrDescendant(root, key, target.referenceKey)) return root;
|
|
103
|
+
}
|
|
104
|
+
const resolved = keys.map((k) => findNode(root, k));
|
|
105
|
+
if (resolved.includes(void 0)) return root;
|
|
106
|
+
nodes = resolved;
|
|
107
|
+
removeSet = new Set(keys);
|
|
108
|
+
} else {
|
|
109
|
+
nodes = (ctx.dragged ?? []).map((n) => ({ ...n }));
|
|
110
|
+
if (nodes.length === 0) return root;
|
|
111
|
+
}
|
|
112
|
+
const afterRemoval = removeSet.size > 0 ? removeKeys(root, removeSet) : root;
|
|
113
|
+
let parentKey;
|
|
114
|
+
let index;
|
|
115
|
+
if (target.position === `inside`) {
|
|
116
|
+
if (findNode(afterRemoval, target.referenceKey) === void 0) return root;
|
|
117
|
+
parentKey = target.referenceKey;
|
|
118
|
+
const kids = childrenOf(afterRemoval, parentKey);
|
|
119
|
+
index = clamp$1(target.index, 0, kids.length);
|
|
120
|
+
} else {
|
|
121
|
+
parentKey = findParent(afterRemoval, target.referenceKey)?.item.key;
|
|
122
|
+
const refIdx = childrenOf(afterRemoval, parentKey).findIndex((c) => c.item.key === target.referenceKey);
|
|
123
|
+
if (refIdx < 0) return root;
|
|
124
|
+
index = target.position === `before` ? refIdx : refIdx + 1;
|
|
125
|
+
}
|
|
126
|
+
const kids = [...childrenOf(afterRemoval, parentKey)];
|
|
127
|
+
const next = [
|
|
128
|
+
...kids.slice(0, index),
|
|
129
|
+
...nodes,
|
|
130
|
+
...kids.slice(index)
|
|
131
|
+
];
|
|
132
|
+
return withChildren(afterRemoval, parentKey, next);
|
|
133
|
+
}
|
|
134
|
+
/** Build a synthetic `TreeDropTarget` for programmatic moves (see `TreeDataModel`). */
|
|
135
|
+
function dropTargetFor(referenceKey, position, index = position === `inside` ? Number.MAX_SAFE_INTEGER : 0) {
|
|
136
|
+
return {
|
|
137
|
+
referenceKey,
|
|
138
|
+
position,
|
|
139
|
+
index,
|
|
140
|
+
parentKey: position === `inside` ? referenceKey : void 0,
|
|
141
|
+
depth: 0
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
//#endregion
|
|
145
|
+
//#region src/common/tree-data-model.ts
|
|
22
146
|
/** Return a new root with the node at `key` replaced by `replacer(node)`. */
|
|
23
147
|
function mapNode(root, key, replacer) {
|
|
24
148
|
if (root.item.key === key) return replacer(root);
|
|
@@ -43,11 +167,23 @@ function removeNodeFrom(root, key) {
|
|
|
43
167
|
children: root.children.filter((c) => c.item.key !== key).map((c) => removeNodeFrom(c, key))
|
|
44
168
|
};
|
|
45
169
|
}
|
|
46
|
-
/** Return a new root with
|
|
47
|
-
function
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
170
|
+
/** Return a new root with `newChild` inserted at `index` among the children of `parentKey`. */
|
|
171
|
+
function insertChildAt(root, parentKey, newChild, index) {
|
|
172
|
+
return mapNode(root, parentKey, (parent) => {
|
|
173
|
+
const children = parent.children ?? [];
|
|
174
|
+
const clampedIndex = Math.max(0, Math.min(index, children.length));
|
|
175
|
+
return {
|
|
176
|
+
item: parent.item,
|
|
177
|
+
children: [
|
|
178
|
+
...children.slice(0, clampedIndex),
|
|
179
|
+
newChild,
|
|
180
|
+
...children.slice(clampedIndex)
|
|
181
|
+
]
|
|
182
|
+
};
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
function getRootChildren(root) {
|
|
186
|
+
return root.children ?? [];
|
|
51
187
|
}
|
|
52
188
|
/**
|
|
53
189
|
* Manages tree data for tree-like components.
|
|
@@ -175,10 +311,80 @@ var TreeDataModel = class {
|
|
|
175
311
|
}
|
|
176
312
|
/**
|
|
177
313
|
* Moves the node with the given key to become a child of `newParentKey`.
|
|
314
|
+
* When `index` is provided, inserts at that position; otherwise appends.
|
|
315
|
+
* Returns the new root (connected components are notified automatically).
|
|
316
|
+
*/
|
|
317
|
+
moveNode(key, newParentKey, index) {
|
|
318
|
+
const next = applyTreeDrop(this.#root, {
|
|
319
|
+
source: `internal`,
|
|
320
|
+
draggedKeys: [key],
|
|
321
|
+
target: dropTargetFor(newParentKey, `inside`, index ?? Number.MAX_SAFE_INTEGER)
|
|
322
|
+
});
|
|
323
|
+
if (next !== this.#root) {
|
|
324
|
+
this.#root = next;
|
|
325
|
+
this.#notify({
|
|
326
|
+
type: `root`,
|
|
327
|
+
root: this.#root
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
return this.#root;
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Moves the node with the given key to become a sibling of `referenceKey`,
|
|
334
|
+
* positioned `before` or `after` it. A no-op (returns the unchanged root) when
|
|
335
|
+
* `key` is `referenceKey`, an ancestor of it, or either is missing.
|
|
336
|
+
* Returns the new root (connected components are notified automatically).
|
|
337
|
+
*/
|
|
338
|
+
moveNodeRelative(key, referenceKey, position) {
|
|
339
|
+
const next = applyTreeDrop(this.#root, {
|
|
340
|
+
source: `internal`,
|
|
341
|
+
draggedKeys: [key],
|
|
342
|
+
target: dropTargetFor(referenceKey, position)
|
|
343
|
+
});
|
|
344
|
+
if (next !== this.#root) {
|
|
345
|
+
this.#root = next;
|
|
346
|
+
this.#notify({
|
|
347
|
+
type: `root`,
|
|
348
|
+
root: this.#root
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
return this.#root;
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* Applies a resolved drop (from `ixfx-tree-list`'s `tree-drop` event or a
|
|
355
|
+
* hand-built context) to the current root. No-op when the drop is invalid.
|
|
356
|
+
* Returns the new root (connected components are notified automatically).
|
|
357
|
+
*/
|
|
358
|
+
applyDrop(ctx) {
|
|
359
|
+
const next = applyTreeDrop(this.#root, ctx);
|
|
360
|
+
if (next !== this.#root) {
|
|
361
|
+
this.#root = next;
|
|
362
|
+
this.#notify({
|
|
363
|
+
type: `root`,
|
|
364
|
+
root: this.#root
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
return this.#root;
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Inserts `node` as a child of `parentKey` (or at the root level when `parentKey` is undefined).
|
|
371
|
+
* When `index` is provided, inserts at that position; otherwise appends.
|
|
178
372
|
* Returns the new root (connected components are notified automatically).
|
|
179
373
|
*/
|
|
180
|
-
|
|
181
|
-
|
|
374
|
+
insertNode(parentKey, node, index) {
|
|
375
|
+
if (parentKey === void 0) {
|
|
376
|
+
const children = getRootChildren(this.#root);
|
|
377
|
+
const clampedIndex = Math.max(0, Math.min(index ?? children.length, children.length));
|
|
378
|
+
this.#root = {
|
|
379
|
+
...this.#root,
|
|
380
|
+
children: [
|
|
381
|
+
...children.slice(0, clampedIndex),
|
|
382
|
+
node,
|
|
383
|
+
...children.slice(clampedIndex)
|
|
384
|
+
]
|
|
385
|
+
};
|
|
386
|
+
} else if (index !== void 0) this.#root = insertChildAt(this.#root, parentKey, node, index);
|
|
387
|
+
else this.#root = addChildTo(this.#root, parentKey, node);
|
|
182
388
|
this.#notify({
|
|
183
389
|
type: `root`,
|
|
184
390
|
root: this.#root
|
|
@@ -200,6 +406,360 @@ var TreeDataModel = class {
|
|
|
200
406
|
}
|
|
201
407
|
};
|
|
202
408
|
//#endregion
|
|
409
|
+
//#region src/tree/tree-drag-registry.ts
|
|
410
|
+
const TREE_DRAG_MIME = `application/x-ixfx-tree`;
|
|
411
|
+
const registry = /* @__PURE__ */ new Map();
|
|
412
|
+
const weakRefs = /* @__PURE__ */ new Map();
|
|
413
|
+
/**
|
|
414
|
+
* The source id of the drag currently in flight, if any. There is only ever one
|
|
415
|
+
* native drag at a time, so a drop target can resolve the dragged nodes during
|
|
416
|
+
* `dragover` — when `DataTransfer.getData()` is unreadable (protected mode) — by
|
|
417
|
+
* looking this up in the registry.
|
|
418
|
+
*/
|
|
419
|
+
let currentSourceId;
|
|
420
|
+
let sourceIdCounter = 0;
|
|
421
|
+
function treeDragGenerateSourceId() {
|
|
422
|
+
return `tree-drag-${++sourceIdCounter}-${Date.now()}`;
|
|
423
|
+
}
|
|
424
|
+
function treeDragRegister(sourceId, element, nodes, keys) {
|
|
425
|
+
registry.set(sourceId, {
|
|
426
|
+
sourceId,
|
|
427
|
+
nodes,
|
|
428
|
+
keys
|
|
429
|
+
});
|
|
430
|
+
weakRefs.set(sourceId, new WeakRef(element));
|
|
431
|
+
currentSourceId = sourceId;
|
|
432
|
+
}
|
|
433
|
+
function treeDragUnregister(sourceId) {
|
|
434
|
+
registry.delete(sourceId);
|
|
435
|
+
weakRefs.delete(sourceId);
|
|
436
|
+
if (currentSourceId === sourceId) currentSourceId = void 0;
|
|
437
|
+
}
|
|
438
|
+
/** The in-flight drag's `{ keys, nodes, element }`, or `undefined` when nothing is dragging. */
|
|
439
|
+
function treeDragCurrent() {
|
|
440
|
+
if (currentSourceId === void 0) return void 0;
|
|
441
|
+
const entry = registry.get(currentSourceId);
|
|
442
|
+
if (!entry) return void 0;
|
|
443
|
+
return {
|
|
444
|
+
sourceId: currentSourceId,
|
|
445
|
+
keys: entry.keys,
|
|
446
|
+
nodes: entry.nodes,
|
|
447
|
+
element: weakRefs.get(currentSourceId)?.deref()
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
//#endregion
|
|
451
|
+
//#region src/tree/tree-drop-controller.ts
|
|
452
|
+
const EDGE_ZONE_PX = 24;
|
|
453
|
+
const MAX_SCROLL_SPEED = 16;
|
|
454
|
+
function clamp(value, min, max) {
|
|
455
|
+
return Math.max(min, Math.min(max, value));
|
|
456
|
+
}
|
|
457
|
+
var TreeDropController = class {
|
|
458
|
+
#host;
|
|
459
|
+
#config;
|
|
460
|
+
#active = false;
|
|
461
|
+
#current;
|
|
462
|
+
#source = `internal`;
|
|
463
|
+
#dragKeys = [];
|
|
464
|
+
#dragNodes = [];
|
|
465
|
+
#expandTimer;
|
|
466
|
+
#expandPendingKey;
|
|
467
|
+
#scrollRaf = 0;
|
|
468
|
+
#pointerX = 0;
|
|
469
|
+
#pointerY = 0;
|
|
470
|
+
#boundContainer;
|
|
471
|
+
constructor(host, config) {
|
|
472
|
+
this.#host = host;
|
|
473
|
+
this.#config = config;
|
|
474
|
+
host.addController(this);
|
|
475
|
+
}
|
|
476
|
+
/** A drag is currently over this tree. */
|
|
477
|
+
get isDragging() {
|
|
478
|
+
return this.#active;
|
|
479
|
+
}
|
|
480
|
+
/** A drag is over this tree but the current position is not a valid drop. */
|
|
481
|
+
get isRejected() {
|
|
482
|
+
return this.#active && this.#current === void 0;
|
|
483
|
+
}
|
|
484
|
+
get dropState() {
|
|
485
|
+
return this.#current;
|
|
486
|
+
}
|
|
487
|
+
hostConnected() {
|
|
488
|
+
this.#bind();
|
|
489
|
+
}
|
|
490
|
+
/** Bind once the shadow `#container` exists (it does not yet at `hostConnected`). */
|
|
491
|
+
hostUpdated() {
|
|
492
|
+
this.#bind();
|
|
493
|
+
}
|
|
494
|
+
#bind() {
|
|
495
|
+
const container = this.#config.getScrollContainer();
|
|
496
|
+
if (!container || container === this.#boundContainer) return;
|
|
497
|
+
this.#unbind();
|
|
498
|
+
this.#boundContainer = container;
|
|
499
|
+
container.addEventListener(`dragenter`, this.#onDragEnter);
|
|
500
|
+
container.addEventListener(`dragover`, this.#onDragOver);
|
|
501
|
+
container.addEventListener(`dragleave`, this.#onDragLeave);
|
|
502
|
+
container.addEventListener(`drop`, this.#onDrop);
|
|
503
|
+
}
|
|
504
|
+
#unbind() {
|
|
505
|
+
const c = this.#boundContainer;
|
|
506
|
+
if (!c) return;
|
|
507
|
+
c.removeEventListener(`dragenter`, this.#onDragEnter);
|
|
508
|
+
c.removeEventListener(`dragover`, this.#onDragOver);
|
|
509
|
+
c.removeEventListener(`dragleave`, this.#onDragLeave);
|
|
510
|
+
c.removeEventListener(`drop`, this.#onDrop);
|
|
511
|
+
this.#boundContainer = void 0;
|
|
512
|
+
}
|
|
513
|
+
hostDisconnected() {
|
|
514
|
+
this.#unbind();
|
|
515
|
+
this.#clearExpand();
|
|
516
|
+
this.#stopScroll();
|
|
517
|
+
this.#active = false;
|
|
518
|
+
this.#current = void 0;
|
|
519
|
+
}
|
|
520
|
+
#onDragEnter = (event) => {
|
|
521
|
+
if (this.#config.getDndMode() === `none` || !event.dataTransfer) return;
|
|
522
|
+
if (this.#config.resolveDrag(event.dataTransfer, `hover`) === void 0) return;
|
|
523
|
+
event.preventDefault();
|
|
524
|
+
if (!this.#active) {
|
|
525
|
+
this.#active = true;
|
|
526
|
+
this.#config.onActiveChange(true);
|
|
527
|
+
}
|
|
528
|
+
};
|
|
529
|
+
#onDragOver = (event) => {
|
|
530
|
+
if (this.#config.getDndMode() === `none` || !event.dataTransfer) return;
|
|
531
|
+
this.#pointerX = event.clientX;
|
|
532
|
+
this.#pointerY = event.clientY;
|
|
533
|
+
const drag = this.#config.resolveDrag(event.dataTransfer, `hover`);
|
|
534
|
+
if (!drag) {
|
|
535
|
+
this.#reject(event);
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
if (!this.#active) {
|
|
539
|
+
this.#active = true;
|
|
540
|
+
this.#config.onActiveChange(true);
|
|
541
|
+
}
|
|
542
|
+
this.#source = drag.source;
|
|
543
|
+
this.#dragKeys = drag.keys;
|
|
544
|
+
this.#dragNodes = drag.nodes;
|
|
545
|
+
const rows = this.#config.getVisibleRows();
|
|
546
|
+
this.#handleExpandHover(this.#rowAtPointer(rows, event.clientY));
|
|
547
|
+
this.#kickScroll();
|
|
548
|
+
const target = this.#resolveIntent(event, rows, drag.source);
|
|
549
|
+
if (!target) {
|
|
550
|
+
this.#reject(event, false);
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
const ctx = {
|
|
554
|
+
source: drag.source,
|
|
555
|
+
dragged: drag.nodes.length > 0 ? drag.nodes : void 0,
|
|
556
|
+
draggedKeys: drag.keys,
|
|
557
|
+
target,
|
|
558
|
+
dataTransfer: event.dataTransfer
|
|
559
|
+
};
|
|
560
|
+
if (!this.#config.vet(ctx)) {
|
|
561
|
+
this.#reject(event, false);
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
event.preventDefault();
|
|
565
|
+
event.dataTransfer.dropEffect = drag.source === `internal` ? `move` : `copy`;
|
|
566
|
+
this.#setDropState(target);
|
|
567
|
+
};
|
|
568
|
+
/** Not a valid drop here: keep the drag active for feedback but accept nothing. */
|
|
569
|
+
#reject(event, clearHover = true) {
|
|
570
|
+
if (event.dataTransfer) event.dataTransfer.dropEffect = `none`;
|
|
571
|
+
this.#setDropState(void 0);
|
|
572
|
+
if (clearHover) this.#clearExpand();
|
|
573
|
+
}
|
|
574
|
+
#onDragLeave = (event) => {
|
|
575
|
+
const container = this.#config.getScrollContainer();
|
|
576
|
+
const related = event.relatedTarget;
|
|
577
|
+
if (container && related && container.contains(related)) return;
|
|
578
|
+
this.#endHover();
|
|
579
|
+
};
|
|
580
|
+
#onDrop = (event) => {
|
|
581
|
+
if (!event.dataTransfer) {
|
|
582
|
+
this.#endHover();
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
585
|
+
event.preventDefault();
|
|
586
|
+
this.#stopScroll();
|
|
587
|
+
this.#clearExpand();
|
|
588
|
+
const finish = (dropped) => {
|
|
589
|
+
this.#current = void 0;
|
|
590
|
+
this.#active = false;
|
|
591
|
+
this.#config.onDropStateChange(void 0);
|
|
592
|
+
this.#config.onActiveChange(false);
|
|
593
|
+
this.#config.onDragEnd(dropped);
|
|
594
|
+
};
|
|
595
|
+
if (this.#config.getDndMode() === `none`) {
|
|
596
|
+
finish(false);
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
599
|
+
const drag = this.#config.resolveDrag(event.dataTransfer, `drop`);
|
|
600
|
+
if (!drag) {
|
|
601
|
+
finish(false);
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
604
|
+
this.#dragKeys = drag.keys;
|
|
605
|
+
this.#dragNodes = drag.nodes;
|
|
606
|
+
const target = this.#resolveIntent(event, this.#config.getVisibleRows(), drag.source);
|
|
607
|
+
if (!target) {
|
|
608
|
+
finish(false);
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
const ctx = {
|
|
612
|
+
source: drag.source,
|
|
613
|
+
dragged: drag.nodes.length > 0 ? drag.nodes : void 0,
|
|
614
|
+
draggedKeys: drag.keys,
|
|
615
|
+
target,
|
|
616
|
+
dataTransfer: event.dataTransfer
|
|
617
|
+
};
|
|
618
|
+
if (!this.#config.vet(ctx)) {
|
|
619
|
+
finish(false);
|
|
620
|
+
return;
|
|
621
|
+
}
|
|
622
|
+
const detail = {
|
|
623
|
+
...ctx,
|
|
624
|
+
external: drag.source === `external` ? drag.nodes : void 0
|
|
625
|
+
};
|
|
626
|
+
finish(this.#config.commitDrop(detail));
|
|
627
|
+
};
|
|
628
|
+
#endHover() {
|
|
629
|
+
this.#clearExpand();
|
|
630
|
+
this.#stopScroll();
|
|
631
|
+
if (this.#current !== void 0) {
|
|
632
|
+
this.#current = void 0;
|
|
633
|
+
this.#config.onDropStateChange(void 0);
|
|
634
|
+
}
|
|
635
|
+
if (this.#active) {
|
|
636
|
+
this.#active = false;
|
|
637
|
+
this.#config.onActiveChange(false);
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
#rowAtPointer(rows, clientY) {
|
|
641
|
+
if (rows.length === 0) return void 0;
|
|
642
|
+
if (clientY < rows[0].rect.top) return rows[0];
|
|
643
|
+
if (clientY > rows[rows.length - 1].rect.bottom) return rows[rows.length - 1];
|
|
644
|
+
return rows.find((r) => clientY >= r.rect.top && clientY <= r.rect.bottom) ?? rows[rows.length - 1];
|
|
645
|
+
}
|
|
646
|
+
#parentRow(rows, index) {
|
|
647
|
+
const target = rows[index];
|
|
648
|
+
for (let i = index - 1; i >= 0; i--) if (rows[i].depth === target.depth - 1) return rows[i];
|
|
649
|
+
}
|
|
650
|
+
#resolveIntent(event, rows, source) {
|
|
651
|
+
if (rows.length === 0) return void 0;
|
|
652
|
+
const row = this.#rowAtPointer(rows, event.clientY);
|
|
653
|
+
if (!row) return void 0;
|
|
654
|
+
const rowIndex = rows.indexOf(row);
|
|
655
|
+
const f = clamp((event.clientY - row.rect.top) / row.rect.height, 0, 1);
|
|
656
|
+
let position = f < .25 ? `before` : f > .75 ? `after` : `inside`;
|
|
657
|
+
const isLeaf = row.node.item.isLeaf === true;
|
|
658
|
+
if (position === `inside` && isLeaf) position = `after`;
|
|
659
|
+
let insideFromExpandedAfter = false;
|
|
660
|
+
if (position === `after` && !isLeaf && this.#config.isExpanded(row.key)) {
|
|
661
|
+
position = `inside`;
|
|
662
|
+
insideFromExpandedAfter = true;
|
|
663
|
+
}
|
|
664
|
+
let parentKey;
|
|
665
|
+
let referenceKey;
|
|
666
|
+
let depth;
|
|
667
|
+
let index;
|
|
668
|
+
if (position === `inside`) {
|
|
669
|
+
parentKey = row.key;
|
|
670
|
+
referenceKey = row.key;
|
|
671
|
+
depth = row.depth + 1;
|
|
672
|
+
index = insideFromExpandedAfter ? 0 : row.node.children?.length ?? 0;
|
|
673
|
+
} else {
|
|
674
|
+
const indent = this.#config.getIndentPx() || 1;
|
|
675
|
+
const treeLeft = rows[0].rect.left;
|
|
676
|
+
const effDepth = clamp(Math.round((event.clientX - treeLeft) / indent), 0, row.depth);
|
|
677
|
+
let anchorIndex = rowIndex;
|
|
678
|
+
for (let up = row.depth - effDepth; up > 0; up--) {
|
|
679
|
+
const parent = this.#parentRow(rows, anchorIndex);
|
|
680
|
+
if (!parent) break;
|
|
681
|
+
anchorIndex = rows.indexOf(parent);
|
|
682
|
+
}
|
|
683
|
+
const anchor = rows[anchorIndex];
|
|
684
|
+
referenceKey = anchor.key;
|
|
685
|
+
depth = anchor.depth;
|
|
686
|
+
const parent = this.#parentRow(rows, anchorIndex);
|
|
687
|
+
parentKey = parent?.key;
|
|
688
|
+
const refIdx = (parent ? parent.node.children ?? [] : rows.filter((r) => r.depth === 0).map((r) => r.node)).findIndex((n) => n.item.key === anchor.key);
|
|
689
|
+
index = (refIdx < 0 ? 0 : refIdx) + (position === `after` ? 1 : 0);
|
|
690
|
+
}
|
|
691
|
+
if (source === `internal`) {
|
|
692
|
+
for (const key of this.#dragKeys) {
|
|
693
|
+
if (key === parentKey || key === referenceKey) return void 0;
|
|
694
|
+
if (parentKey !== void 0 && this.#config.isSelfOrDescendant(key, parentKey)) return void 0;
|
|
695
|
+
if (position !== `inside` && this.#config.isSelfOrDescendant(key, referenceKey)) return void 0;
|
|
696
|
+
}
|
|
697
|
+
if (this.#dragKeys.length > 0 && !this.#config.isReorderAllowed()) {
|
|
698
|
+
if (!this.#dragKeys.some((k) => this.#config.getParentKey(k) !== parentKey)) return void 0;
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
return {
|
|
702
|
+
parentKey,
|
|
703
|
+
index,
|
|
704
|
+
referenceKey,
|
|
705
|
+
position,
|
|
706
|
+
depth
|
|
707
|
+
};
|
|
708
|
+
}
|
|
709
|
+
#handleExpandHover(row) {
|
|
710
|
+
if (!row || row.node.item.isLeaf === true || this.#config.isExpanded(row.key)) {
|
|
711
|
+
this.#clearExpand();
|
|
712
|
+
return;
|
|
713
|
+
}
|
|
714
|
+
if (row.key === this.#expandPendingKey) return;
|
|
715
|
+
this.#clearExpand();
|
|
716
|
+
const key = this.#expandPendingKey = row.key;
|
|
717
|
+
this.#expandTimer = setTimeout(() => {
|
|
718
|
+
this.#expandTimer = void 0;
|
|
719
|
+
if (this.#expandPendingKey === key) {
|
|
720
|
+
this.#expandPendingKey = void 0;
|
|
721
|
+
this.#config.expandKey(key);
|
|
722
|
+
}
|
|
723
|
+
}, Math.max(0, this.#config.getExpandHoverMs()));
|
|
724
|
+
}
|
|
725
|
+
#clearExpand() {
|
|
726
|
+
if (this.#expandTimer !== void 0) {
|
|
727
|
+
clearTimeout(this.#expandTimer);
|
|
728
|
+
this.#expandTimer = void 0;
|
|
729
|
+
}
|
|
730
|
+
this.#expandPendingKey = void 0;
|
|
731
|
+
}
|
|
732
|
+
#kickScroll() {
|
|
733
|
+
if (this.#scrollRaf === 0) this.#scrollRaf = requestAnimationFrame(this.#scrollTick);
|
|
734
|
+
}
|
|
735
|
+
#scrollTick = () => {
|
|
736
|
+
this.#scrollRaf = 0;
|
|
737
|
+
if (!this.#active) return;
|
|
738
|
+
const container = this.#config.getScrollContainer();
|
|
739
|
+
if (!container) return;
|
|
740
|
+
const rect = container.getBoundingClientRect();
|
|
741
|
+
const y = this.#pointerY - rect.top;
|
|
742
|
+
let dy = 0;
|
|
743
|
+
if (y < EDGE_ZONE_PX) dy = -Math.ceil(MAX_SCROLL_SPEED * (1 - Math.max(0, y) / EDGE_ZONE_PX));
|
|
744
|
+
else if (y > rect.height - EDGE_ZONE_PX) dy = Math.ceil(MAX_SCROLL_SPEED * (1 - Math.max(0, rect.height - y) / EDGE_ZONE_PX));
|
|
745
|
+
if (dy !== 0 && this.#pointerX >= rect.left && this.#pointerX <= rect.right) {
|
|
746
|
+
container.scrollTop += dy;
|
|
747
|
+
this.#kickScroll();
|
|
748
|
+
}
|
|
749
|
+
};
|
|
750
|
+
#stopScroll() {
|
|
751
|
+
if (this.#scrollRaf !== 0) {
|
|
752
|
+
cancelAnimationFrame(this.#scrollRaf);
|
|
753
|
+
this.#scrollRaf = 0;
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
#setDropState(state) {
|
|
757
|
+
const unchanged = state === void 0 ? this.#current === void 0 : state.referenceKey === this.#current?.referenceKey && state.position === this.#current?.position && state.depth === this.#current?.depth && state.index === this.#current?.index;
|
|
758
|
+
this.#current = state;
|
|
759
|
+
if (!unchanged || this.#active) this.#config.onDropStateChange(state);
|
|
760
|
+
}
|
|
761
|
+
};
|
|
762
|
+
//#endregion
|
|
203
763
|
//#region src/tree/tree-base.ts
|
|
204
764
|
/** Above this many rows entering / leaving at once, skip the animation entirely. */
|
|
205
765
|
const MAX_ANIMATED_ROWS = 50;
|
|
@@ -212,6 +772,10 @@ let TreeBaseElement = class TreeBaseElement extends LitElement {
|
|
|
212
772
|
this._selectedNodes = /* @__PURE__ */ new Set();
|
|
213
773
|
this.disableKeyboardNavigation = false;
|
|
214
774
|
this.registry = void 0;
|
|
775
|
+
this.itemDnd = `none`;
|
|
776
|
+
this.dndExpandHoverMs = 500;
|
|
777
|
+
this.reorderable = true;
|
|
778
|
+
this.applyInternalDrop = true;
|
|
215
779
|
this._expandedKeys = /* @__PURE__ */ new Set();
|
|
216
780
|
this.tickled = new TickledController(this, {
|
|
217
781
|
getItems: () => {
|
|
@@ -220,6 +784,7 @@ let TreeBaseElement = class TreeBaseElement extends LitElement {
|
|
|
220
784
|
enabled: true
|
|
221
785
|
}));
|
|
222
786
|
},
|
|
787
|
+
hoverDisabled: () => this.#drop?.isDragging === true || this.#nativeDrag?.isDragging === true,
|
|
223
788
|
onStateChange: () => {
|
|
224
789
|
this.setAttribute(`data-input-mode`, this.tickled.inputMode);
|
|
225
790
|
}
|
|
@@ -234,8 +799,14 @@ let TreeBaseElement = class TreeBaseElement extends LitElement {
|
|
|
234
799
|
this.#lastNodeByKey = /* @__PURE__ */ new Map();
|
|
235
800
|
this.#enteringKeys = /* @__PURE__ */ new Set();
|
|
236
801
|
this.#leaving = /* @__PURE__ */ new Map();
|
|
237
|
-
this.#
|
|
802
|
+
this.#leavingAnimations = /* @__PURE__ */ new Map();
|
|
238
803
|
this.#treeAnimReady = false;
|
|
804
|
+
this.#dragSourceId = ``;
|
|
805
|
+
this.#nativeDragKeys = /* @__PURE__ */ new Set();
|
|
806
|
+
this.#internalDropHandled = false;
|
|
807
|
+
this.#onContainerScroll = () => {
|
|
808
|
+
this.#rowsCache = void 0;
|
|
809
|
+
};
|
|
239
810
|
this.#onKeyDown = (event) => {
|
|
240
811
|
if (this.search.isOpen) return;
|
|
241
812
|
const nodes = this.#getAllNodes();
|
|
@@ -387,8 +958,40 @@ let TreeBaseElement = class TreeBaseElement extends LitElement {
|
|
|
387
958
|
#lastNodeByKey;
|
|
388
959
|
#enteringKeys;
|
|
389
960
|
#leaving;
|
|
390
|
-
#
|
|
961
|
+
#leavingAnimations;
|
|
391
962
|
#treeAnimReady;
|
|
963
|
+
#dragSourceId;
|
|
964
|
+
#postDragSuppressTimer;
|
|
965
|
+
#dropState;
|
|
966
|
+
#drop;
|
|
967
|
+
#nativeDrag;
|
|
968
|
+
/** Keys of the nodes currently being dragged out (for `.dragging` styling). */
|
|
969
|
+
#nativeDragKeys;
|
|
970
|
+
/** Set by `commitDrop` for a same-tree internal drop, read by the native `onEnd` to avoid a duplicate `tree-drag-end`. */
|
|
971
|
+
#internalDropHandled;
|
|
972
|
+
/** Per-drag cache of measured rows; invalidated on scroll / update. */
|
|
973
|
+
#rowsCache;
|
|
974
|
+
#onContainerScroll;
|
|
975
|
+
get nativeDrag() {
|
|
976
|
+
return this.#nativeDrag;
|
|
977
|
+
}
|
|
978
|
+
get dropState() {
|
|
979
|
+
return this.#dropState;
|
|
980
|
+
}
|
|
981
|
+
/** Keys of the nodes currently being dragged out of this tree. */
|
|
982
|
+
get nativeDragKeys() {
|
|
983
|
+
return this.#nativeDragKeys;
|
|
984
|
+
}
|
|
985
|
+
#cancelLeavingAnimation(key) {
|
|
986
|
+
this.#leavingAnimations.get(key)?.cancel();
|
|
987
|
+
this.#leavingAnimations.delete(key);
|
|
988
|
+
}
|
|
989
|
+
#cancelAllLeavingAnimations() {
|
|
990
|
+
for (const animation of this.#leavingAnimations.values()) try {
|
|
991
|
+
animation.cancel();
|
|
992
|
+
} catch {}
|
|
993
|
+
this.#leavingAnimations.clear();
|
|
994
|
+
}
|
|
392
995
|
willUpdate(changedProps) {
|
|
393
996
|
super.willUpdate(changedProps);
|
|
394
997
|
this.#diffVisibleNodes();
|
|
@@ -407,12 +1010,12 @@ let TreeBaseElement = class TreeBaseElement extends LitElement {
|
|
|
407
1010
|
}
|
|
408
1011
|
}
|
|
409
1012
|
for (const key of [...this.#leaving.keys()]) if (nowKeys.has(key)) {
|
|
1013
|
+
this.#cancelLeavingAnimation(key);
|
|
410
1014
|
this.#leaving.delete(key);
|
|
411
|
-
this.#leavingAnimating.delete(key);
|
|
412
1015
|
}
|
|
413
1016
|
if (this.#leaving.size > MAX_ANIMATED_ROWS) {
|
|
1017
|
+
this.#cancelAllLeavingAnimations();
|
|
414
1018
|
this.#leaving.clear();
|
|
415
|
-
this.#leavingAnimating.clear();
|
|
416
1019
|
}
|
|
417
1020
|
}
|
|
418
1021
|
/** True while `node` is a fading ghost row (removed / collapsed, still animating out). */
|
|
@@ -449,18 +1052,38 @@ let TreeBaseElement = class TreeBaseElement extends LitElement {
|
|
|
449
1052
|
if (this.#enteringKeys.size > 0) {
|
|
450
1053
|
for (const el of this.renderRoot.querySelectorAll(`.item`)) {
|
|
451
1054
|
const key = el.dataset.key;
|
|
452
|
-
if (key
|
|
1055
|
+
if (key === void 0 || !this.#enteringKeys.has(key)) continue;
|
|
1056
|
+
if (this.#leavingAnimations.has(key)) this.#cancelLeavingAnimation(key);
|
|
1057
|
+
else for (const animation of el.getAnimations()) animation.cancel();
|
|
1058
|
+
animateListItemIn(el);
|
|
453
1059
|
}
|
|
454
1060
|
this.#enteringKeys.clear();
|
|
455
1061
|
}
|
|
456
1062
|
if (this.#leaving.size > 0) for (const el of this.renderRoot.querySelectorAll(`.item.leaving`)) {
|
|
457
1063
|
const key = el.dataset.key;
|
|
458
|
-
if (key === void 0 || this.#
|
|
459
|
-
|
|
460
|
-
|
|
1064
|
+
if (key === void 0 || this.#leavingAnimations.has(key)) continue;
|
|
1065
|
+
const result = animateListItemOut(el);
|
|
1066
|
+
const animation = result.animation;
|
|
1067
|
+
if (!animation) {
|
|
1068
|
+
result.then(() => {
|
|
1069
|
+
if (!this.#leaving.has(key)) return;
|
|
1070
|
+
this.#leaving.delete(key);
|
|
1071
|
+
this.requestUpdate();
|
|
1072
|
+
});
|
|
1073
|
+
continue;
|
|
1074
|
+
}
|
|
1075
|
+
this.#cancelLeavingAnimation(key);
|
|
1076
|
+
this.#leavingAnimations.set(key, animation);
|
|
1077
|
+
result.then(() => {
|
|
1078
|
+
if (this.#leavingAnimations.get(key) !== animation) return;
|
|
1079
|
+
this.#leavingAnimations.delete(key);
|
|
461
1080
|
this.#leaving.delete(key);
|
|
462
|
-
this.#leavingAnimating.delete(key);
|
|
463
1081
|
this.requestUpdate();
|
|
1082
|
+
this.updateComplete.then(() => {
|
|
1083
|
+
try {
|
|
1084
|
+
animation.cancel();
|
|
1085
|
+
} catch {}
|
|
1086
|
+
});
|
|
464
1087
|
});
|
|
465
1088
|
}
|
|
466
1089
|
const visible = this.root ? this.#getAllNodes() : [];
|
|
@@ -470,12 +1093,16 @@ let TreeBaseElement = class TreeBaseElement extends LitElement {
|
|
|
470
1093
|
this.#treeAnimReady = true;
|
|
471
1094
|
}
|
|
472
1095
|
updated(changedProps) {
|
|
1096
|
+
this.#rowsCache = void 0;
|
|
473
1097
|
if (changedProps.has(`root`) && this.root) {
|
|
474
1098
|
this.#abortPending();
|
|
475
1099
|
this._expandedKeys.clear();
|
|
476
1100
|
this.#clearSelectionInternal();
|
|
477
1101
|
this.tickled.clearTickled();
|
|
478
1102
|
this._focusedDepth = 0;
|
|
1103
|
+
this.#cancelAllLeavingAnimations();
|
|
1104
|
+
this.#leaving.clear();
|
|
1105
|
+
this.#enteringKeys.clear();
|
|
479
1106
|
}
|
|
480
1107
|
if (changedProps.has(`model`)) {
|
|
481
1108
|
this.#modelUnsubscribe?.();
|
|
@@ -523,13 +1150,307 @@ let TreeBaseElement = class TreeBaseElement extends LitElement {
|
|
|
523
1150
|
});
|
|
524
1151
|
this.registry = registry;
|
|
525
1152
|
this.#syncSearchCommands();
|
|
1153
|
+
this.#initDnd();
|
|
1154
|
+
}
|
|
1155
|
+
#initDnd() {
|
|
1156
|
+
this.#dragSourceId = treeDragGenerateSourceId();
|
|
1157
|
+
this.#nativeDrag = new ListNativeDragController(this, {
|
|
1158
|
+
source: `tree-list`,
|
|
1159
|
+
isEnabled: () => this.itemDnd !== `none` && this.#anyRowDraggable(),
|
|
1160
|
+
resolveEventToKey: (target) => {
|
|
1161
|
+
if (!(target instanceof Element)) return void 0;
|
|
1162
|
+
return target.closest(`li[data-key]`)?.getAttribute(`data-key`) ?? void 0;
|
|
1163
|
+
},
|
|
1164
|
+
prepareSelection: (originKey) => {
|
|
1165
|
+
const allNodes = this.#getAllNodes();
|
|
1166
|
+
const originEntry = allNodes.find((n) => n.node.item.key === originKey);
|
|
1167
|
+
if (!originEntry) return void 0;
|
|
1168
|
+
const selectedNodes = new Set(this._selectedNodes);
|
|
1169
|
+
if (!selectedNodes.has(originEntry.node)) selectedNodes.clear();
|
|
1170
|
+
selectedNodes.add(originEntry.node);
|
|
1171
|
+
const orderedKeys = allNodes.filter((n) => selectedNodes.has(n.node)).map((n) => n.node.item.key);
|
|
1172
|
+
const topKeys = new Set(this.root ? pruneDraggedDescendants(this.root, orderedKeys) : orderedKeys);
|
|
1173
|
+
const selected = allNodes.filter((n) => topKeys.has(n.node.item.key)).map((n, i) => ({
|
|
1174
|
+
key: n.node.item.key,
|
|
1175
|
+
index: i,
|
|
1176
|
+
item: n.node,
|
|
1177
|
+
element: this.shadowRoot?.querySelector(`li[data-key="${CSS.escape(n.node.item.key)}"]`) ?? void 0
|
|
1178
|
+
}));
|
|
1179
|
+
const origin = selected.find((e) => e.key === originKey) ?? {
|
|
1180
|
+
key: originKey,
|
|
1181
|
+
index: 0,
|
|
1182
|
+
item: originEntry.node,
|
|
1183
|
+
element: void 0
|
|
1184
|
+
};
|
|
1185
|
+
if (!selected.some((e) => e.key === origin.key)) selected.unshift(origin);
|
|
1186
|
+
return {
|
|
1187
|
+
origin,
|
|
1188
|
+
selected
|
|
1189
|
+
};
|
|
1190
|
+
},
|
|
1191
|
+
getMetrics: () => ({ rowHeight: this.#getItemHeight() }),
|
|
1192
|
+
getRenderer: () => this.dragPreviewRenderer,
|
|
1193
|
+
dispatch: (detail) => {
|
|
1194
|
+
const keys = detail.selected.map((e) => e.key);
|
|
1195
|
+
const nodes = detail.selected.map((e) => e.item);
|
|
1196
|
+
const evt = new CustomEvent(`list-native-drag-start`, {
|
|
1197
|
+
detail,
|
|
1198
|
+
bubbles: true,
|
|
1199
|
+
composed: true,
|
|
1200
|
+
cancelable: true
|
|
1201
|
+
});
|
|
1202
|
+
const notCancelled = this.dispatchEvent(evt);
|
|
1203
|
+
if (notCancelled) {
|
|
1204
|
+
this.#nativeDragKeys = new Set(keys);
|
|
1205
|
+
this.#internalDropHandled = false;
|
|
1206
|
+
if (this.itemDnd === `internal` || this.itemDnd === `both`) treeDragRegister(this.#dragSourceId, this, nodes, keys);
|
|
1207
|
+
this.dispatchEvent(new CustomEvent(`tree-drag-start`, {
|
|
1208
|
+
detail: {
|
|
1209
|
+
nodes,
|
|
1210
|
+
keys
|
|
1211
|
+
},
|
|
1212
|
+
bubbles: true,
|
|
1213
|
+
composed: true
|
|
1214
|
+
}));
|
|
1215
|
+
}
|
|
1216
|
+
return notCancelled;
|
|
1217
|
+
},
|
|
1218
|
+
writeDataTransfer: (dataTransfer, detail) => {
|
|
1219
|
+
if (this.itemDnd !== `internal` && this.itemDnd !== `both`) return;
|
|
1220
|
+
const keys = detail.selected.map((e) => e.key);
|
|
1221
|
+
const nodes = detail.selected.map((e) => e.item);
|
|
1222
|
+
try {
|
|
1223
|
+
dataTransfer.setData(TREE_DRAG_MIME, JSON.stringify({
|
|
1224
|
+
sourceId: this.#dragSourceId,
|
|
1225
|
+
keys,
|
|
1226
|
+
nodes
|
|
1227
|
+
}));
|
|
1228
|
+
dataTransfer.setData(`text/plain`, nodes.map((n) => n.item.label).join(`\n`));
|
|
1229
|
+
} catch {}
|
|
1230
|
+
},
|
|
1231
|
+
onActiveChange: (active) => {
|
|
1232
|
+
this.toggleAttribute(`data-native-dragging`, active);
|
|
1233
|
+
if (!active) this.#nativeDragKeys = /* @__PURE__ */ new Set();
|
|
1234
|
+
this.requestUpdate();
|
|
1235
|
+
},
|
|
1236
|
+
onEnd: () => {
|
|
1237
|
+
treeDragUnregister(this.#dragSourceId);
|
|
1238
|
+
this.#nativeDragKeys = /* @__PURE__ */ new Set();
|
|
1239
|
+
this.#postDragSuppressTimer = setTimeout(() => {
|
|
1240
|
+
this.#postDragSuppressTimer = void 0;
|
|
1241
|
+
}, 150);
|
|
1242
|
+
if (!this.#internalDropHandled) this.dispatchEvent(new CustomEvent(`tree-drag-end`, {
|
|
1243
|
+
detail: { dropped: false },
|
|
1244
|
+
bubbles: true,
|
|
1245
|
+
composed: true
|
|
1246
|
+
}));
|
|
1247
|
+
this.#internalDropHandled = false;
|
|
1248
|
+
}
|
|
1249
|
+
});
|
|
1250
|
+
this.#drop = new TreeDropController(this, {
|
|
1251
|
+
getDndMode: () => this.itemDnd,
|
|
1252
|
+
getExpandHoverMs: () => this.dndExpandHoverMs,
|
|
1253
|
+
getVisibleRows: () => this.#getVisibleRows(),
|
|
1254
|
+
getIndentPx: () => this.#getIndentPx(),
|
|
1255
|
+
getScrollContainer: () => this.#container(),
|
|
1256
|
+
isSelfOrDescendant: (ancestorKey, key) => this.#isSelfOrDescendant(ancestorKey, key),
|
|
1257
|
+
isExpanded: (key) => this._expandedKeys.has(key),
|
|
1258
|
+
isReorderAllowed: () => this.reorderable,
|
|
1259
|
+
getParentKey: (key) => this.#findParentByKey(key)?.item.key,
|
|
1260
|
+
resolveDrag: (dataTransfer, phase) => this.#resolveDrag(dataTransfer, phase),
|
|
1261
|
+
vet: (ctx) => {
|
|
1262
|
+
if (this.canDrop && !this.canDrop(ctx)) return false;
|
|
1263
|
+
const evt = new CustomEvent(`tree-drag-over`, {
|
|
1264
|
+
detail: ctx,
|
|
1265
|
+
bubbles: true,
|
|
1266
|
+
composed: true,
|
|
1267
|
+
cancelable: true
|
|
1268
|
+
});
|
|
1269
|
+
return this.dispatchEvent(evt);
|
|
1270
|
+
},
|
|
1271
|
+
expandKey: (key) => this.#springLoadExpand(key),
|
|
1272
|
+
onDropStateChange: (state) => {
|
|
1273
|
+
this.#dropState = state;
|
|
1274
|
+
this.toggleAttribute(`data-drop-rejected`, this.#drop.isDragging && state === void 0);
|
|
1275
|
+
this.requestUpdate();
|
|
1276
|
+
},
|
|
1277
|
+
onActiveChange: (active) => {
|
|
1278
|
+
if (active) {
|
|
1279
|
+
this.#rowsCache = void 0;
|
|
1280
|
+
this.#container()?.addEventListener(`scroll`, this.#onContainerScroll, { passive: true });
|
|
1281
|
+
} else {
|
|
1282
|
+
this.#container()?.removeEventListener(`scroll`, this.#onContainerScroll);
|
|
1283
|
+
this.#rowsCache = void 0;
|
|
1284
|
+
this.toggleAttribute(`data-drop-rejected`, false);
|
|
1285
|
+
}
|
|
1286
|
+
this.requestUpdate();
|
|
1287
|
+
},
|
|
1288
|
+
commitDrop: (detail) => {
|
|
1289
|
+
if (!this.dispatchEvent(new CustomEvent(`tree-drop`, {
|
|
1290
|
+
detail,
|
|
1291
|
+
bubbles: true,
|
|
1292
|
+
composed: true,
|
|
1293
|
+
cancelable: true
|
|
1294
|
+
}))) return false;
|
|
1295
|
+
if (this.applyInternalDrop && detail.source === `internal` && this.model) this.model.applyDrop({
|
|
1296
|
+
source: `internal`,
|
|
1297
|
+
draggedKeys: detail.draggedKeys,
|
|
1298
|
+
target: detail.target
|
|
1299
|
+
});
|
|
1300
|
+
this.#internalDropHandled = true;
|
|
1301
|
+
return true;
|
|
1302
|
+
},
|
|
1303
|
+
onDragEnd: (dropped) => {
|
|
1304
|
+
this.#dropState = void 0;
|
|
1305
|
+
this.toggleAttribute(`data-drop-rejected`, false);
|
|
1306
|
+
this.requestUpdate();
|
|
1307
|
+
this.dispatchEvent(new CustomEvent(`tree-drag-end`, {
|
|
1308
|
+
detail: { dropped },
|
|
1309
|
+
bubbles: true,
|
|
1310
|
+
composed: true
|
|
1311
|
+
}));
|
|
1312
|
+
}
|
|
1313
|
+
});
|
|
1314
|
+
}
|
|
1315
|
+
#container() {
|
|
1316
|
+
return this.shadowRoot?.querySelector(`#container`) ?? void 0;
|
|
1317
|
+
}
|
|
1318
|
+
#getIndentPx() {
|
|
1319
|
+
const parsed = Number.parseInt(getComputedStyle(this).getPropertyValue(`--tree-indent`), 10);
|
|
1320
|
+
return Number.isNaN(parsed) ? 20 : parsed;
|
|
1321
|
+
}
|
|
1322
|
+
#getItemHeight() {
|
|
1323
|
+
const parsed = Number.parseInt(getComputedStyle(this).getPropertyValue(`--tree-item-height`), 10);
|
|
1324
|
+
return Number.isNaN(parsed) ? 30 : parsed;
|
|
1325
|
+
}
|
|
1326
|
+
/** Measured rows, cached for the duration of a drag and invalidated on scroll / update. */
|
|
1327
|
+
#getVisibleRows() {
|
|
1328
|
+
if (this.#rowsCache) return this.#rowsCache;
|
|
1329
|
+
const rows = [];
|
|
1330
|
+
for (const { node, depth } of this.#getAllNodes()) {
|
|
1331
|
+
const li = this.shadowRoot?.querySelector(`li[data-key="${CSS.escape(node.item.key)}"]`);
|
|
1332
|
+
if (li) rows.push({
|
|
1333
|
+
key: node.item.key,
|
|
1334
|
+
node,
|
|
1335
|
+
depth,
|
|
1336
|
+
rect: li.getBoundingClientRect()
|
|
1337
|
+
});
|
|
1338
|
+
}
|
|
1339
|
+
this.#rowsCache = rows;
|
|
1340
|
+
return rows;
|
|
1341
|
+
}
|
|
1342
|
+
/** Route spring-load through the real expand path so lazy children load and `expand` fires with the right depth. */
|
|
1343
|
+
#springLoadExpand(key) {
|
|
1344
|
+
if (this._expandedKeys.has(key)) return;
|
|
1345
|
+
const entry = this.#getAllNodes().find((n) => n.node.item.key === key);
|
|
1346
|
+
if (!entry || entry.node.item.isLeaf === true) return;
|
|
1347
|
+
this.#toggleExpand(entry.node, entry.depth);
|
|
1348
|
+
this.#rowsCache = void 0;
|
|
1349
|
+
this.requestUpdate();
|
|
1350
|
+
}
|
|
1351
|
+
#isSelfOrDescendant(ancestorKey, key) {
|
|
1352
|
+
if (ancestorKey === key) return true;
|
|
1353
|
+
let node = this.#getAllNodes().find((n) => n.node.item.key === key)?.node;
|
|
1354
|
+
let parent = node ? this.#findParentByKey(node.item.key) : void 0;
|
|
1355
|
+
while (parent) {
|
|
1356
|
+
if (parent.item.key === ancestorKey) return true;
|
|
1357
|
+
node = parent;
|
|
1358
|
+
parent = this.#findParentByKey(node.item.key);
|
|
1359
|
+
}
|
|
1360
|
+
return false;
|
|
1361
|
+
}
|
|
1362
|
+
#findParentByKey(key) {
|
|
1363
|
+
for (const { node } of this.#getAllNodes()) if (node.children?.some((c) => c.item.key === key)) return node;
|
|
1364
|
+
}
|
|
1365
|
+
/**
|
|
1366
|
+
* Decide whether a DataTransfer is acceptable and resolve the dragged set.
|
|
1367
|
+
* During `hover` the DataTransfer is read-protected, so an internal drag is
|
|
1368
|
+
* recovered from the module-level drag registry (there is only one at a time).
|
|
1369
|
+
*/
|
|
1370
|
+
#resolveDrag(dataTransfer, phase) {
|
|
1371
|
+
const mode = this.itemDnd;
|
|
1372
|
+
if (mode === `none`) return void 0;
|
|
1373
|
+
const canInternal = mode === `internal` || mode === `both`;
|
|
1374
|
+
const canExternal = mode === `external` || mode === `both`;
|
|
1375
|
+
let types = [];
|
|
1376
|
+
try {
|
|
1377
|
+
types = Array.from(dataTransfer.types);
|
|
1378
|
+
} catch {}
|
|
1379
|
+
const current = treeDragCurrent();
|
|
1380
|
+
const looksInternal = types.includes(TREE_DRAG_MIME) || current !== void 0;
|
|
1381
|
+
if (canInternal && looksInternal) {
|
|
1382
|
+
if (current && current.keys.length > 0) {
|
|
1383
|
+
const keys = this.root ? pruneDraggedDescendants(this.root, current.keys) : current.keys;
|
|
1384
|
+
return {
|
|
1385
|
+
source: `internal`,
|
|
1386
|
+
keys,
|
|
1387
|
+
nodes: keys.map((k) => this.#getAllNodes().find((n) => n.node.item.key === k)?.node).filter((n) => n !== void 0)
|
|
1388
|
+
};
|
|
1389
|
+
}
|
|
1390
|
+
if (phase === `drop`) try {
|
|
1391
|
+
const raw = dataTransfer.getData(TREE_DRAG_MIME);
|
|
1392
|
+
if (raw) {
|
|
1393
|
+
const payload = JSON.parse(raw);
|
|
1394
|
+
return {
|
|
1395
|
+
source: `internal`,
|
|
1396
|
+
keys: payload.keys ?? [],
|
|
1397
|
+
nodes: payload.nodes ?? []
|
|
1398
|
+
};
|
|
1399
|
+
}
|
|
1400
|
+
} catch {}
|
|
1401
|
+
return {
|
|
1402
|
+
source: `internal`,
|
|
1403
|
+
keys: [],
|
|
1404
|
+
nodes: []
|
|
1405
|
+
};
|
|
1406
|
+
}
|
|
1407
|
+
if (canExternal && !looksInternal) {
|
|
1408
|
+
if (this.acceptDropTypes !== void 0) {
|
|
1409
|
+
const accepted = this.acceptDropTypes.trim().split(/\s+/).filter(Boolean);
|
|
1410
|
+
const present = Array.from(dataTransfer.types);
|
|
1411
|
+
if (accepted.length > 0 && !accepted.some((t) => present.includes(t))) return void 0;
|
|
1412
|
+
}
|
|
1413
|
+
if (phase === `drop`) {
|
|
1414
|
+
const nodes = this.parseExternalDrop?.(dataTransfer);
|
|
1415
|
+
if (!nodes || nodes.length === 0) return void 0;
|
|
1416
|
+
return {
|
|
1417
|
+
source: `external`,
|
|
1418
|
+
keys: nodes.map((n) => n.item.key),
|
|
1419
|
+
nodes
|
|
1420
|
+
};
|
|
1421
|
+
}
|
|
1422
|
+
return this.parseExternalDrop ? {
|
|
1423
|
+
source: `external`,
|
|
1424
|
+
keys: [],
|
|
1425
|
+
nodes: []
|
|
1426
|
+
} : void 0;
|
|
1427
|
+
}
|
|
1428
|
+
}
|
|
1429
|
+
#anyRowDraggable() {
|
|
1430
|
+
if (this.itemDnd === `none`) return false;
|
|
1431
|
+
const allNodes = this.#getAllNodes();
|
|
1432
|
+
if (allNodes.length === 0) return false;
|
|
1433
|
+
if (!this.canDrag) return true;
|
|
1434
|
+
return allNodes.some(({ node }) => this.canDrag({
|
|
1435
|
+
nodes: [node],
|
|
1436
|
+
keys: [node.item.key]
|
|
1437
|
+
}));
|
|
526
1438
|
}
|
|
527
1439
|
disconnectedCallback() {
|
|
528
1440
|
super.disconnectedCallback();
|
|
1441
|
+
this.#cancelAllLeavingAnimations();
|
|
1442
|
+
this.#leaving.clear();
|
|
1443
|
+
this.#enteringKeys.clear();
|
|
529
1444
|
this.#modelUnsubscribe?.();
|
|
530
1445
|
this.#modelUnsubscribe = void 0;
|
|
531
1446
|
this.#unregisterSearchCommands?.();
|
|
532
1447
|
this.#unregisterSearchCommands = void 0;
|
|
1448
|
+
if (this.#postDragSuppressTimer !== void 0) {
|
|
1449
|
+
clearTimeout(this.#postDragSuppressTimer);
|
|
1450
|
+
this.#postDragSuppressTimer = void 0;
|
|
1451
|
+
}
|
|
1452
|
+
this.#container()?.removeEventListener(`scroll`, this.#onContainerScroll);
|
|
1453
|
+
treeDragUnregister(this.#dragSourceId);
|
|
533
1454
|
if (this.#ownedRegistry) {
|
|
534
1455
|
this.#ownedRegistry.unregister(`select-all`);
|
|
535
1456
|
this.#ownedRegistry.unregister(`deselect-all`);
|
|
@@ -704,6 +1625,7 @@ let TreeBaseElement = class TreeBaseElement extends LitElement {
|
|
|
704
1625
|
`;
|
|
705
1626
|
}
|
|
706
1627
|
_handleItemClick(node, depth, event) {
|
|
1628
|
+
if (this.#drop?.isDragging || this.#nativeDrag?.isDragging || this.#postDragSuppressTimer !== void 0) return;
|
|
707
1629
|
if (event && event.detail >= 2) return;
|
|
708
1630
|
if (this.#pendingNode && this._expandedKeys.has(this.#pendingNode.item.key)) this.#abortPending();
|
|
709
1631
|
this.tickled.handlePointerEnter(node.item.key);
|
|
@@ -1138,6 +2060,23 @@ let TreeBaseElement = class TreeBaseElement extends LitElement {
|
|
|
1138
2060
|
</ul>
|
|
1139
2061
|
`;
|
|
1140
2062
|
}
|
|
2063
|
+
/** Shared drag-and-drop render state for a row, used by every `_renderNode` variant. */
|
|
2064
|
+
_dndRowState(node) {
|
|
2065
|
+
const draggable = this.itemDnd !== `none` && (this.root?.children?.length ?? 0) > 0 && (this.itemDnd === `internal` || this.itemDnd === `both` || this.itemDnd === `external`) && (this.canDrag?.({
|
|
2066
|
+
nodes: [node],
|
|
2067
|
+
keys: [node.item.key]
|
|
2068
|
+
}) ?? true);
|
|
2069
|
+
const dragging = this.#nativeDragKeys.has(node.item.key);
|
|
2070
|
+
const state = this.#dropState;
|
|
2071
|
+
const dropTarget = state !== void 0 && state.referenceKey === node.item.key;
|
|
2072
|
+
return {
|
|
2073
|
+
draggable,
|
|
2074
|
+
dragging,
|
|
2075
|
+
dropTarget,
|
|
2076
|
+
dropPosition: dropTarget ? state.position : void 0,
|
|
2077
|
+
dropDepth: dropTarget ? state.depth : void 0
|
|
2078
|
+
};
|
|
2079
|
+
}
|
|
1141
2080
|
_renderNode(node, depth) {
|
|
1142
2081
|
const selected = this._isSelected(node);
|
|
1143
2082
|
const tickled = this._isFocused(node);
|
|
@@ -1146,9 +2085,10 @@ let TreeBaseElement = class TreeBaseElement extends LitElement {
|
|
|
1146
2085
|
const isPending = this.#pendingNode === node;
|
|
1147
2086
|
const isFiltered = this._isFilteredOut(node, depth);
|
|
1148
2087
|
const isLeaving = this._isLeaving(node);
|
|
2088
|
+
const dnd = this._dndRowState(node);
|
|
1149
2089
|
return html`
|
|
1150
2090
|
<li
|
|
1151
|
-
class="item ${selected ? `selected` : ``} ${tickled ? `tickled` : ``} ${isBranch ? `branch` : `leaf`} ${isFiltered ? `filtered` : ``} ${isLeaving ? `leaving` : ``}"
|
|
2091
|
+
class="item ${selected ? `selected` : ``} ${tickled ? `tickled` : ``} ${isBranch ? `branch` : `leaf`} ${isFiltered ? `filtered` : ``} ${isLeaving ? `leaving` : ``} ${dnd.dragging ? `dragging` : ``} ${dnd.dropTarget && dnd.dropPosition === `inside` ? `drop-into` : ``}"
|
|
1152
2092
|
role="treeitem"
|
|
1153
2093
|
aria-selected=${selected}
|
|
1154
2094
|
aria-expanded=${isBranch ? isExpanded : nothing}
|
|
@@ -1156,6 +2096,8 @@ let TreeBaseElement = class TreeBaseElement extends LitElement {
|
|
|
1156
2096
|
aria-level=${depth + 1}
|
|
1157
2097
|
data-key=${node.item.key}
|
|
1158
2098
|
style=${`--depth: ${depth}`}
|
|
2099
|
+
draggable=${dnd.draggable ? `true` : nothing}
|
|
2100
|
+
part=${dnd.dropTarget ? `drop-target` : nothing}
|
|
1159
2101
|
@click=${(e) => this._handleItemClick(node, depth, e)}
|
|
1160
2102
|
@pointerenter=${() => {
|
|
1161
2103
|
this.tickled.handlePointerEnter(node.item.key);
|
|
@@ -1169,6 +2111,11 @@ let TreeBaseElement = class TreeBaseElement extends LitElement {
|
|
|
1169
2111
|
}));
|
|
1170
2112
|
}}
|
|
1171
2113
|
>
|
|
2114
|
+
${dnd.dropTarget && (dnd.dropPosition === `before` || dnd.dropPosition === `after`) ? html`<div
|
|
2115
|
+
class="drop-line drop-line-${dnd.dropPosition}"
|
|
2116
|
+
style="--drop-depth: ${dnd.dropDepth ?? depth}"
|
|
2117
|
+
part="drop-indicator"
|
|
2118
|
+
></div>` : nothing}
|
|
1172
2119
|
${this.renderNodeContent(node, depth)}
|
|
1173
2120
|
${isPending ? this.renderLoadingIndicator() : nothing}
|
|
1174
2121
|
</li>
|
|
@@ -1186,9 +2133,13 @@ let TreeBaseElement = class TreeBaseElement extends LitElement {
|
|
|
1186
2133
|
--tree-text: var(--surface-3-text);
|
|
1187
2134
|
--tree-muted-text: var(--surface-muted-text);
|
|
1188
2135
|
--tree-indent: 1ch;
|
|
1189
|
-
--tree-item-height:
|
|
2136
|
+
--tree-item-height: 30px;
|
|
1190
2137
|
--tree-caret-size: 30px;
|
|
1191
2138
|
--tree-radius: var(--radius-s);
|
|
2139
|
+
--tree-drop-line: var(--accent);
|
|
2140
|
+
--tree-drop-nub-size: 5px;
|
|
2141
|
+
--tree-drop-into-outline: 2px solid var(--accent);
|
|
2142
|
+
--tree-drop-into-bg: color-mix(in srgb, var(--accent) 14%, transparent);
|
|
1192
2143
|
|
|
1193
2144
|
position: relative;
|
|
1194
2145
|
display: block;
|
|
@@ -1216,10 +2167,11 @@ let TreeBaseElement = class TreeBaseElement extends LitElement {
|
|
|
1216
2167
|
}
|
|
1217
2168
|
|
|
1218
2169
|
.item {
|
|
2170
|
+
position: relative; /* anchor for the .drop-line before/after indicator */
|
|
1219
2171
|
display: flex;
|
|
1220
2172
|
align-items: center;
|
|
1221
2173
|
height: var(--tree-item-height);
|
|
1222
|
-
padding-left: calc(var(--tree-indent) * var(--depth, 0)
|
|
2174
|
+
padding-left: calc(var(--tree-indent) * var(--depth, 0));
|
|
1223
2175
|
cursor: pointer;
|
|
1224
2176
|
user-select: none;
|
|
1225
2177
|
font-family: var(--font-family);
|
|
@@ -1291,11 +2243,49 @@ let TreeBaseElement = class TreeBaseElement extends LitElement {
|
|
|
1291
2243
|
overflow: hidden;
|
|
1292
2244
|
}
|
|
1293
2245
|
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
2246
|
+
/* Drag and drop styles */
|
|
2247
|
+
.item.dragging {
|
|
2248
|
+
opacity: 0.4;
|
|
2249
|
+
}
|
|
2250
|
+
|
|
2251
|
+
.item.drop-into {
|
|
2252
|
+
outline: var(--tree-drop-into-outline);
|
|
2253
|
+
outline-offset: -2px;
|
|
2254
|
+
background: var(--tree-drop-into-bg);
|
|
2255
|
+
}
|
|
2256
|
+
|
|
2257
|
+
.drop-line {
|
|
2258
|
+
position: absolute;
|
|
2259
|
+
left: calc(var(--tree-indent) * var(--drop-depth, 0) + var(--space-xs));
|
|
2260
|
+
right: var(--space-xs);
|
|
2261
|
+
height: 2px;
|
|
2262
|
+
background: var(--tree-drop-line);
|
|
2263
|
+
pointer-events: none;
|
|
2264
|
+
z-index: 10;
|
|
2265
|
+
}
|
|
2266
|
+
|
|
2267
|
+
/* Nub at the left end of the insertion line (VS Code style). */
|
|
2268
|
+
.drop-line::before {
|
|
2269
|
+
content: "";
|
|
2270
|
+
position: absolute;
|
|
2271
|
+
left: calc(-1 * var(--tree-drop-nub-size) / 2);
|
|
2272
|
+
top: calc(50% - var(--tree-drop-nub-size) / 2);
|
|
2273
|
+
width: var(--tree-drop-nub-size);
|
|
2274
|
+
height: var(--tree-drop-nub-size);
|
|
2275
|
+
border-radius: 50%;
|
|
2276
|
+
background: var(--tree-drop-line);
|
|
2277
|
+
}
|
|
2278
|
+
|
|
2279
|
+
.drop-line-before {
|
|
2280
|
+
top: -1px;
|
|
2281
|
+
}
|
|
2282
|
+
|
|
2283
|
+
.drop-line-after {
|
|
2284
|
+
bottom: -1px;
|
|
2285
|
+
}
|
|
2286
|
+
|
|
2287
|
+
:host([data-drop-rejected]) {
|
|
2288
|
+
cursor: not-allowed;
|
|
1299
2289
|
}
|
|
1300
2290
|
`
|
|
1301
2291
|
];
|
|
@@ -1329,6 +2319,31 @@ __decorate([property({
|
|
|
1329
2319
|
attribute: `search-modes`,
|
|
1330
2320
|
reflect: true
|
|
1331
2321
|
})], TreeBaseElement.prototype, "searchModes", void 0);
|
|
2322
|
+
__decorate([property({
|
|
2323
|
+
reflect: true,
|
|
2324
|
+
attribute: `item-dnd`
|
|
2325
|
+
})], TreeBaseElement.prototype, "itemDnd", void 0);
|
|
2326
|
+
__decorate([property({
|
|
2327
|
+
type: String,
|
|
2328
|
+
attribute: `accept-drop-types`
|
|
2329
|
+
})], TreeBaseElement.prototype, "acceptDropTypes", void 0);
|
|
2330
|
+
__decorate([property({
|
|
2331
|
+
type: Number,
|
|
2332
|
+
attribute: `dnd-expand-hover-ms`
|
|
2333
|
+
})], TreeBaseElement.prototype, "dndExpandHoverMs", void 0);
|
|
2334
|
+
__decorate([property({
|
|
2335
|
+
reflect: true,
|
|
2336
|
+
attribute: `reorderable`,
|
|
2337
|
+
converter: {
|
|
2338
|
+
fromAttribute: (value) => value !== `false`,
|
|
2339
|
+
toAttribute: (value) => value ? null : `false`
|
|
2340
|
+
}
|
|
2341
|
+
})], TreeBaseElement.prototype, "reorderable", void 0);
|
|
2342
|
+
__decorate([property({ attribute: false })], TreeBaseElement.prototype, "dragPreviewRenderer", void 0);
|
|
2343
|
+
__decorate([property({ attribute: false })], TreeBaseElement.prototype, "canDrag", void 0);
|
|
2344
|
+
__decorate([property({ attribute: false })], TreeBaseElement.prototype, "canDrop", void 0);
|
|
2345
|
+
__decorate([property({ attribute: false })], TreeBaseElement.prototype, "parseExternalDrop", void 0);
|
|
2346
|
+
__decorate([property({ attribute: false })], TreeBaseElement.prototype, "applyInternalDrop", void 0);
|
|
1332
2347
|
__decorate([state()], TreeBaseElement.prototype, "_expandedKeys", void 0);
|
|
1333
2348
|
__decorate([state()], TreeBaseElement.prototype, "_focusedDepth", void 0);
|
|
1334
2349
|
TreeBaseElement = __decorate([safeCustomElement(`ixfx-tree-base`)], TreeBaseElement);
|
|
@@ -1410,9 +2425,10 @@ let TreeListElement = class TreeListElement extends TreeBaseElement {
|
|
|
1410
2425
|
const hasIconAtDepth = this.#depthsWithIcons.has(depth);
|
|
1411
2426
|
const isLeaving = this._isLeaving(node);
|
|
1412
2427
|
const isFiltered = this._isFilteredOut(node, depth);
|
|
2428
|
+
const dnd = this._dndRowState(node);
|
|
1413
2429
|
return html`
|
|
1414
2430
|
<li
|
|
1415
|
-
class="item ${selected ? `selected` : ``} ${tickled ? `tickled` : ``} ${isBranch ? `branch` : `leaf`} ${hasIconAtDepth ? `has-icon-at-depth` : ``} ${isLeaving ? `leaving` : ``} ${isFiltered ? `filtered` : ``}"
|
|
2431
|
+
class="item ${selected ? `selected` : ``} ${tickled ? `tickled` : ``} ${isBranch ? `branch` : `leaf`} ${hasIconAtDepth ? `has-icon-at-depth` : ``} ${isLeaving ? `leaving` : ``} ${isFiltered ? `filtered` : ``} ${dnd.dragging ? `dragging` : ``} ${dnd.dropTarget && dnd.dropPosition === `inside` ? `drop-into` : ``}"
|
|
1416
2432
|
data-key=${node.item.key}
|
|
1417
2433
|
role="treeitem"
|
|
1418
2434
|
aria-selected=${selected}
|
|
@@ -1420,9 +2436,16 @@ let TreeListElement = class TreeListElement extends TreeBaseElement {
|
|
|
1420
2436
|
aria-hidden=${isLeaving ? `true` : nothing}
|
|
1421
2437
|
aria-level=${depth + 1}
|
|
1422
2438
|
style=${`--depth: ${depth}`}
|
|
2439
|
+
draggable=${dnd.draggable ? `true` : nothing}
|
|
2440
|
+
part=${dnd.dropTarget ? `drop-target` : nothing}
|
|
1423
2441
|
@click=${(e) => this._handleItemClick(node, depth, e)}
|
|
1424
2442
|
@pointerenter=${() => this.tickled.handlePointerEnter(node.item.key)}
|
|
1425
2443
|
>
|
|
2444
|
+
${dnd.dropTarget && (dnd.dropPosition === `before` || dnd.dropPosition === `after`) ? html`<div
|
|
2445
|
+
class="drop-line drop-line-${dnd.dropPosition}"
|
|
2446
|
+
style="--drop-depth: ${dnd.dropDepth ?? depth}"
|
|
2447
|
+
part="drop-indicator"
|
|
2448
|
+
></div>` : nothing}
|
|
1426
2449
|
${this.renderNodeContent(node, depth)}
|
|
1427
2450
|
${isPending ? this.renderLoadingIndicator() : nothing}
|
|
1428
2451
|
</li>
|
|
@@ -1605,6 +2628,6 @@ var TreeController = class {
|
|
|
1605
2628
|
}
|
|
1606
2629
|
};
|
|
1607
2630
|
//#endregion
|
|
1608
|
-
export { TreeDataModel as i, TreeListElement as n, TreeBaseElement as r, TreeController as t };
|
|
2631
|
+
export { TreeDataModel as a, pruneDraggedDescendants as c, TreeDropController as i, TreeListElement as n, applyTreeDrop as o, TreeBaseElement as r, dropTargetFor as s, TreeController as t };
|
|
1609
2632
|
|
|
1610
|
-
//# sourceMappingURL=tree-
|
|
2633
|
+
//# sourceMappingURL=tree-a14n9y7i.js.map
|