@ixfx/components 0.8.8 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/bundle/index.d.ts +252 -43
  2. package/bundle/index.d.ts.map +1 -1
  3. package/bundle/index.js +1177 -39
  4. package/bundle/index.js.map +1 -1
  5. package/dist/crumbs.d.ts +1 -1
  6. package/dist/{data-grid-BKa9i86q.d.ts → data-grid-C588pTgt.d.ts} +3 -3
  7. package/dist/{data-grid-BKa9i86q.d.ts.map → data-grid-C588pTgt.d.ts.map} +1 -1
  8. package/dist/data-grid.d.ts +2 -2
  9. package/dist/data-grid.js +1 -1
  10. package/dist/{detail-list-CHTomlbR.d.ts → detail-list-IKl76Wnp.d.ts} +4 -4
  11. package/dist/{detail-list-CHTomlbR.d.ts.map → detail-list-IKl76Wnp.d.ts.map} +1 -1
  12. package/dist/detail-list.d.ts +3 -3
  13. package/dist/detail-list.js +1 -1
  14. package/dist/{grid-list-CsZcXxMC.d.ts → grid-list-C1zq5U1W.d.ts} +20 -56
  15. package/dist/grid-list-C1zq5U1W.d.ts.map +1 -0
  16. package/dist/{grid-list-C8oMxLl-.js → grid-list-zUZRI8hk.js} +39 -3
  17. package/dist/grid-list-zUZRI8hk.js.map +1 -0
  18. package/dist/grid-list.d.ts +2 -2
  19. package/dist/grid-list.js +1 -1
  20. package/dist/incr-search.d.ts +1 -1
  21. package/dist/{index-Dgrx-DIA.d.ts → index--qnhBWZJ.d.ts} +2 -2
  22. package/dist/{index-Dgrx-DIA.d.ts.map → index--qnhBWZJ.d.ts.map} +1 -1
  23. package/dist/index.d.ts +30 -9
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +11 -11
  26. package/dist/index.js.map +1 -1
  27. package/dist/{labelled-slider-input-BCH9968c.js → labelled-slider-input-aEYDvW2S.js} +2 -2
  28. package/dist/{labelled-slider-input-BCH9968c.js.map → labelled-slider-input-aEYDvW2S.js.map} +1 -1
  29. package/dist/labelled-slider-input.js +1 -1
  30. package/dist/list-native-drag-controller-Cmpx7jnx.d.ts +68 -0
  31. package/dist/list-native-drag-controller-Cmpx7jnx.d.ts.map +1 -0
  32. package/dist/{list-native-drag-controller-DrY3qeJM.js → list-native-drag-controller-DaBNcjgG.js} +28 -2
  33. package/dist/list-native-drag-controller-DaBNcjgG.js.map +1 -0
  34. package/dist/miller.d.ts +2 -2
  35. package/dist/plots.js +1 -1
  36. package/dist/slider-input-BxURAAmD.d.ts.map +1 -1
  37. package/dist/{slider-input-WR8_RwHg.js → slider-input-Cb5uSV42.js} +78 -11
  38. package/dist/slider-input-Cb5uSV42.js.map +1 -0
  39. package/dist/slider-input.js +1 -1
  40. package/dist/{split-layout-CA5jbNbG.js → split-layout-Bx0g8k8-.js} +24 -1
  41. package/dist/{split-layout-CA5jbNbG.js.map → split-layout-Bx0g8k8-.js.map} +1 -1
  42. package/dist/split-layout-JK3xWVX2.d.ts.map +1 -1
  43. package/dist/split-layout.js +1 -1
  44. package/dist/{tree-DSMn566I.js → tree-CwifXCzR.js} +1006 -19
  45. package/dist/tree-CwifXCzR.js.map +1 -0
  46. package/dist/{tree-component-nPP5Y-WS.d.ts → tree-component-BW2iCFq9.d.ts} +107 -3
  47. package/dist/tree-component-BW2iCFq9.d.ts.map +1 -0
  48. package/dist/tree.d.ts +102 -4
  49. package/dist/tree.d.ts.map +1 -1
  50. package/dist/tree.js +2 -2
  51. package/dist/{vertical-list-A6oWfivZ.d.ts → vertical-list-CHrXmmQ3.d.ts} +3 -3
  52. package/dist/{vertical-list-A6oWfivZ.d.ts.map → vertical-list-CHrXmmQ3.d.ts.map} +1 -1
  53. package/dist/vertical-list.d.ts +2 -2
  54. package/dist/vertical-list.js +1 -1
  55. package/dist/{xy-axis-CrrVdDFe.js → xy-axis-COKw338Q.js} +2 -2
  56. package/dist/xy-axis-COKw338Q.js.map +1 -0
  57. package/dist/xy-axis-DQqd2yXJ.d.ts.map +1 -1
  58. package/docs-user/index.json +2 -2
  59. package/docs-user/tree.md +75 -0
  60. package/package.json +1 -1
  61. package/dist/grid-list-C8oMxLl-.js.map +0 -1
  62. package/dist/grid-list-CsZcXxMC.d.ts.map +0 -1
  63. package/dist/list-drag-types-kpaXz2pZ.d.ts +0 -42
  64. package/dist/list-drag-types-kpaXz2pZ.d.ts.map +0 -1
  65. package/dist/list-native-drag-controller-DrY3qeJM.js.map +0 -1
  66. package/dist/slider-input-WR8_RwHg.js.map +0 -1
  67. package/dist/tree-DSMn566I.js.map +0 -1
  68. package/dist/tree-component-nPP5Y-WS.d.ts.map +0 -1
  69. package/dist/xy-axis-CrrVdDFe.js.map +0 -1
package/bundle/index.js CHANGED
@@ -6049,7 +6049,7 @@ const DEFAULT_THRESHOLD = 4;
6049
6049
  /** Pointer distance (px) from an edge that triggers auto-scroll. */
6050
6050
  const EDGE_ZONE = 28;
6051
6051
  /** Max auto-scroll speed in px per frame. */
6052
- const MAX_SCROLL_SPEED = 16;
6052
+ const MAX_SCROLL_SPEED$1 = 16;
6053
6053
  /**
6054
6054
  * Shared pointer-drag (marquee / rubber-band) selection for list components.
6055
6055
  *
@@ -6315,10 +6315,10 @@ var ListDragSelectController = class {
6315
6315
  const py = this.#currentClientY - box.top;
6316
6316
  let dx = 0;
6317
6317
  let dy = 0;
6318
- if (px < EDGE_ZONE) dx = -Math.ceil(MAX_SCROLL_SPEED * (1 - Math.max(0, px) / EDGE_ZONE));
6319
- else if (px > box.width - EDGE_ZONE) dx = Math.ceil(MAX_SCROLL_SPEED * (1 - Math.max(0, box.width - px) / EDGE_ZONE));
6320
- if (py < EDGE_ZONE) dy = -Math.ceil(MAX_SCROLL_SPEED * (1 - Math.max(0, py) / EDGE_ZONE));
6321
- else if (py > box.height - EDGE_ZONE) dy = Math.ceil(MAX_SCROLL_SPEED * (1 - Math.max(0, box.height - py) / EDGE_ZONE));
6318
+ if (px < EDGE_ZONE) dx = -Math.ceil(MAX_SCROLL_SPEED$1 * (1 - Math.max(0, px) / EDGE_ZONE));
6319
+ else if (px > box.width - EDGE_ZONE) dx = Math.ceil(MAX_SCROLL_SPEED$1 * (1 - Math.max(0, box.width - px) / EDGE_ZONE));
6320
+ if (py < EDGE_ZONE) dy = -Math.ceil(MAX_SCROLL_SPEED$1 * (1 - Math.max(0, py) / EDGE_ZONE));
6321
+ else if (py > box.height - EDGE_ZONE) dy = Math.ceil(MAX_SCROLL_SPEED$1 * (1 - Math.max(0, box.height - py) / EDGE_ZONE));
6322
6322
  if (dx === 0 && dy === 0) return;
6323
6323
  const autoScroll = this.#config.autoScroll;
6324
6324
  if (!autoScroll) return;
@@ -6521,6 +6521,10 @@ var ListNativeDragController = class {
6521
6521
  } catch {}
6522
6522
  this.refreshDraggable();
6523
6523
  }
6524
+ /** `renderRoot` does not exist yet at `hostConnected` for a shadow-DOM host — rebind once it does. */
6525
+ hostUpdated() {
6526
+ this.#bind();
6527
+ }
6524
6528
  hostDisconnected() {
6525
6529
  this.#unbind();
6526
6530
  try {
@@ -6558,7 +6562,20 @@ var ListNativeDragController = class {
6558
6562
  #onDragStart = (event) => {
6559
6563
  if (!(event instanceof DragEvent)) return;
6560
6564
  if (this.#seenDragStarts.has(event)) return;
6561
- this.#seenDragStarts.add(event);
6565
+ if (this.#config.shouldStart) {
6566
+ let ok = true;
6567
+ try {
6568
+ ok = this.#config.shouldStart(event);
6569
+ } catch {
6570
+ ok = false;
6571
+ }
6572
+ if (!ok) {
6573
+ try {
6574
+ event.preventDefault();
6575
+ } catch {}
6576
+ return;
6577
+ }
6578
+ }
6562
6579
  let enabled = false;
6563
6580
  try {
6564
6581
  enabled = this.#config.isEnabled();
@@ -6572,6 +6589,7 @@ var ListNativeDragController = class {
6572
6589
  } catch {
6573
6590
  return;
6574
6591
  }
6592
+ if (originKey) this.#seenDragStarts.add(event);
6575
6593
  if (!originKey) return;
6576
6594
  let prepared;
6577
6595
  try {
@@ -6630,6 +6648,14 @@ var ListNativeDragController = class {
6630
6648
  notCancelled = true;
6631
6649
  }
6632
6650
  if (notCancelled) {
6651
+ if (event.dataTransfer) {
6652
+ try {
6653
+ if (event.dataTransfer.effectAllowed === `none` || event.dataTransfer.effectAllowed === `uninitialized`) event.dataTransfer.effectAllowed = `copyMove`;
6654
+ } catch {}
6655
+ try {
6656
+ this.#config.writeDataTransfer?.(event.dataTransfer, detail);
6657
+ } catch {}
6658
+ }
6633
6659
  const hotspot = preview.hotspot ?? {
6634
6660
  x: 16,
6635
6661
  y: 16
@@ -9736,15 +9762,138 @@ async function collectDataResult(result, signal) {
9736
9762
  return result;
9737
9763
  }
9738
9764
  //#endregion
9739
- //#region src/common/tree-data-model.ts
9765
+ //#region src/common/tree-drop-apply.ts
9740
9766
  function findNode$1(root, key) {
9741
9767
  if (root.item.key === key) return root;
9742
- if (!root.children) return void 0;
9743
- for (const child of root.children) {
9768
+ for (const child of root.children ?? []) {
9744
9769
  const found = findNode$1(child, key);
9745
9770
  if (found) return found;
9746
9771
  }
9747
9772
  }
9773
+ function findParent(root, key, parent = void 0) {
9774
+ for (const child of root.children ?? []) {
9775
+ if (child.item.key === key) return parent === void 0 ? root : parent;
9776
+ const found = findParent(child, key, child);
9777
+ if (found) return found;
9778
+ }
9779
+ }
9780
+ /** True when `key` is `ancestorKey`, or a descendant of it. */
9781
+ function isSelfOrDescendant(root, ancestorKey, key) {
9782
+ const ancestor = findNode$1(root, ancestorKey);
9783
+ return ancestor !== void 0 && findNode$1(ancestor, key) !== void 0;
9784
+ }
9785
+ /** Remove every node whose key is in `keys` (with its whole subtree), in one pass. */
9786
+ function removeKeys(node, keys) {
9787
+ if (!node.children) return node;
9788
+ return {
9789
+ ...node,
9790
+ children: node.children.filter((c) => !keys.has(c.item.key)).map((c) => removeKeys(c, keys))
9791
+ };
9792
+ }
9793
+ function childrenOf(root, parentKey) {
9794
+ if (parentKey === void 0) return root.children ?? [];
9795
+ return findNode$1(root, parentKey)?.children ?? [];
9796
+ }
9797
+ /** Return a new root with `parentKey`'s children replaced (root itself when `parentKey` is undefined). */
9798
+ function withChildren(node, parentKey, children) {
9799
+ if (parentKey === void 0) return {
9800
+ ...node,
9801
+ children
9802
+ };
9803
+ if (node.item.key === parentKey) return {
9804
+ ...node,
9805
+ children
9806
+ };
9807
+ if (!node.children) return node;
9808
+ return {
9809
+ ...node,
9810
+ children: node.children.map((c) => withChildren(c, parentKey, children))
9811
+ };
9812
+ }
9813
+ function clamp$5(n, min, max) {
9814
+ return Math.max(min, Math.min(max, n));
9815
+ }
9816
+ /**
9817
+ * Drop keys whose ancestor is also being dragged — only the top-most selected
9818
+ * nodes move; their subtrees travel with them. Preserves input order.
9819
+ */
9820
+ function pruneDraggedDescendants(root, keys) {
9821
+ const set = new Set(keys);
9822
+ return keys.filter((key) => {
9823
+ let parent = findParent(root, key);
9824
+ while (parent) {
9825
+ if (set.has(parent.item.key)) return false;
9826
+ parent = findParent(root, parent.item.key);
9827
+ }
9828
+ return true;
9829
+ });
9830
+ }
9831
+ /**
9832
+ * Framework-agnostic drop application. Given the current immutable `root` and a
9833
+ * resolved drop context, returns a **new** root with the drop applied, or the
9834
+ * unchanged `root` when the drop is invalid (dragged node into its own subtree,
9835
+ * missing reference, empty payload). Never throws, never mutates.
9836
+ *
9837
+ * The insertion point is re-derived from `target.referenceKey` + `target.position`
9838
+ * against the post-removal tree, so same-parent reorders are off-by-one-safe and
9839
+ * multi-node drops land as siblings in drag order. `target.index` is honoured only
9840
+ * for `position: 'inside'` (0 = first child, large = append).
9841
+ */
9842
+ function applyTreeDrop(root, ctx) {
9843
+ const { target, source } = ctx;
9844
+ let nodes;
9845
+ let removeSet = /* @__PURE__ */ new Set();
9846
+ if (source === `internal`) {
9847
+ const keys = pruneDraggedDescendants(root, [...ctx.draggedKeys]);
9848
+ if (keys.length === 0) return root;
9849
+ if (target.parentKey !== void 0) {
9850
+ for (const key of keys) if (isSelfOrDescendant(root, key, target.parentKey)) return root;
9851
+ }
9852
+ if (target.position !== `inside`) {
9853
+ for (const key of keys) if (isSelfOrDescendant(root, key, target.referenceKey)) return root;
9854
+ }
9855
+ const resolved = keys.map((k) => findNode$1(root, k));
9856
+ if (resolved.includes(void 0)) return root;
9857
+ nodes = resolved;
9858
+ removeSet = new Set(keys);
9859
+ } else {
9860
+ nodes = (ctx.dragged ?? []).map((n) => ({ ...n }));
9861
+ if (nodes.length === 0) return root;
9862
+ }
9863
+ const afterRemoval = removeSet.size > 0 ? removeKeys(root, removeSet) : root;
9864
+ let parentKey;
9865
+ let index;
9866
+ if (target.position === `inside`) {
9867
+ if (findNode$1(afterRemoval, target.referenceKey) === void 0) return root;
9868
+ parentKey = target.referenceKey;
9869
+ const kids = childrenOf(afterRemoval, parentKey);
9870
+ index = clamp$5(target.index, 0, kids.length);
9871
+ } else {
9872
+ parentKey = findParent(afterRemoval, target.referenceKey)?.item.key;
9873
+ const refIdx = childrenOf(afterRemoval, parentKey).findIndex((c) => c.item.key === target.referenceKey);
9874
+ if (refIdx < 0) return root;
9875
+ index = target.position === `before` ? refIdx : refIdx + 1;
9876
+ }
9877
+ const kids = [...childrenOf(afterRemoval, parentKey)];
9878
+ const next = [
9879
+ ...kids.slice(0, index),
9880
+ ...nodes,
9881
+ ...kids.slice(index)
9882
+ ];
9883
+ return withChildren(afterRemoval, parentKey, next);
9884
+ }
9885
+ /** Build a synthetic `TreeDropTarget` for programmatic moves (see `TreeDataModel`). */
9886
+ function dropTargetFor(referenceKey, position, index = position === `inside` ? Number.MAX_SAFE_INTEGER : 0) {
9887
+ return {
9888
+ referenceKey,
9889
+ position,
9890
+ index,
9891
+ parentKey: position === `inside` ? referenceKey : void 0,
9892
+ depth: 0
9893
+ };
9894
+ }
9895
+ //#endregion
9896
+ //#region src/common/tree-data-model.ts
9748
9897
  /** Return a new root with the node at `key` replaced by `replacer(node)`. */
9749
9898
  function mapNode(root, key, replacer) {
9750
9899
  if (root.item.key === key) return replacer(root);
@@ -9769,11 +9918,23 @@ function removeNodeFrom(root, key) {
9769
9918
  children: root.children.filter((c) => c.item.key !== key).map((c) => removeNodeFrom(c, key))
9770
9919
  };
9771
9920
  }
9772
- /** Return a new root with the node at `key` moved to become a child of `newParentKey`. */
9773
- function moveNodeTo(root, key, newParentKey) {
9774
- const target = findNode$1(root, key);
9775
- if (!target) return root;
9776
- return addChildTo(removeNodeFrom(root, key), newParentKey, target);
9921
+ /** Return a new root with `newChild` inserted at `index` among the children of `parentKey`. */
9922
+ function insertChildAt(root, parentKey, newChild, index) {
9923
+ return mapNode(root, parentKey, (parent) => {
9924
+ const children = parent.children ?? [];
9925
+ const clampedIndex = Math.max(0, Math.min(index, children.length));
9926
+ return {
9927
+ item: parent.item,
9928
+ children: [
9929
+ ...children.slice(0, clampedIndex),
9930
+ newChild,
9931
+ ...children.slice(clampedIndex)
9932
+ ]
9933
+ };
9934
+ });
9935
+ }
9936
+ function getRootChildren(root) {
9937
+ return root.children ?? [];
9777
9938
  }
9778
9939
  /**
9779
9940
  * Manages tree data for tree-like components.
@@ -9901,10 +10062,80 @@ var TreeDataModel = class {
9901
10062
  }
9902
10063
  /**
9903
10064
  * Moves the node with the given key to become a child of `newParentKey`.
10065
+ * When `index` is provided, inserts at that position; otherwise appends.
9904
10066
  * Returns the new root (connected components are notified automatically).
9905
10067
  */
9906
- moveNode(key, newParentKey) {
9907
- this.#root = moveNodeTo(this.#root, key, newParentKey);
10068
+ moveNode(key, newParentKey, index) {
10069
+ const next = applyTreeDrop(this.#root, {
10070
+ source: `internal`,
10071
+ draggedKeys: [key],
10072
+ target: dropTargetFor(newParentKey, `inside`, index ?? Number.MAX_SAFE_INTEGER)
10073
+ });
10074
+ if (next !== this.#root) {
10075
+ this.#root = next;
10076
+ this.#notify({
10077
+ type: `root`,
10078
+ root: this.#root
10079
+ });
10080
+ }
10081
+ return this.#root;
10082
+ }
10083
+ /**
10084
+ * Moves the node with the given key to become a sibling of `referenceKey`,
10085
+ * positioned `before` or `after` it. A no-op (returns the unchanged root) when
10086
+ * `key` is `referenceKey`, an ancestor of it, or either is missing.
10087
+ * Returns the new root (connected components are notified automatically).
10088
+ */
10089
+ moveNodeRelative(key, referenceKey, position) {
10090
+ const next = applyTreeDrop(this.#root, {
10091
+ source: `internal`,
10092
+ draggedKeys: [key],
10093
+ target: dropTargetFor(referenceKey, position)
10094
+ });
10095
+ if (next !== this.#root) {
10096
+ this.#root = next;
10097
+ this.#notify({
10098
+ type: `root`,
10099
+ root: this.#root
10100
+ });
10101
+ }
10102
+ return this.#root;
10103
+ }
10104
+ /**
10105
+ * Applies a resolved drop (from `ixfx-tree-list`'s `tree-drop` event or a
10106
+ * hand-built context) to the current root. No-op when the drop is invalid.
10107
+ * Returns the new root (connected components are notified automatically).
10108
+ */
10109
+ applyDrop(ctx) {
10110
+ const next = applyTreeDrop(this.#root, ctx);
10111
+ if (next !== this.#root) {
10112
+ this.#root = next;
10113
+ this.#notify({
10114
+ type: `root`,
10115
+ root: this.#root
10116
+ });
10117
+ }
10118
+ return this.#root;
10119
+ }
10120
+ /**
10121
+ * Inserts `node` as a child of `parentKey` (or at the root level when `parentKey` is undefined).
10122
+ * When `index` is provided, inserts at that position; otherwise appends.
10123
+ * Returns the new root (connected components are notified automatically).
10124
+ */
10125
+ insertNode(parentKey, node, index) {
10126
+ if (parentKey === void 0) {
10127
+ const children = getRootChildren(this.#root);
10128
+ const clampedIndex = Math.max(0, Math.min(index ?? children.length, children.length));
10129
+ this.#root = {
10130
+ ...this.#root,
10131
+ children: [
10132
+ ...children.slice(0, clampedIndex),
10133
+ node,
10134
+ ...children.slice(clampedIndex)
10135
+ ]
10136
+ };
10137
+ } else if (index !== void 0) this.#root = insertChildAt(this.#root, parentKey, node, index);
10138
+ else this.#root = addChildTo(this.#root, parentKey, node);
9908
10139
  this.#notify({
9909
10140
  type: `root`,
9910
10141
  root: this.#root
@@ -15244,6 +15475,7 @@ let DataTableElement = class DataTableElement extends i {
15244
15475
  this.styles = [
15245
15476
  tickledStyles,
15246
15477
  tickledItemStyles,
15478
+ popupSurfaceStyles,
15247
15479
  i$3`
15248
15480
  :host {
15249
15481
  display: block;
@@ -15383,7 +15615,6 @@ let DataTableElement = class DataTableElement extends i {
15383
15615
  width: max-content;
15384
15616
  max-width: 100cqi;
15385
15617
  padding: var(--space-xs);
15386
- background: var(--surface-2);
15387
15618
  visibility: hidden;
15388
15619
  transform: translateY(-4px);
15389
15620
  pointer-events: none;
@@ -15462,7 +15693,7 @@ let DataTableElement = class DataTableElement extends i {
15462
15693
  ?data-full=${r.fullWidth}
15463
15694
  @pointerenter=${() => this.#handlePointerEnter(i)}
15464
15695
  @click=${() => this.#select(i)}
15465
- ><div class="key">${r.label}</div><div class="value"><span class="value-text">${typeof r.value === `boolean` ? r.value ? `Yes` : `No` : r.value}</span>${r.detail === void 0 ? A : b`<div class="detail" part="detail">${r.detail}</div>`}</div></div>`)}</div>`;
15696
+ ><div class="key">${r.label}</div><div class="value"><span class="value-text">${typeof r.value === `boolean` ? r.value ? `Yes` : `No` : r.value}</span>${r.detail === void 0 ? A : b`<div class="detail popup-surface" part="detail">${r.detail}</div>`}</div></div>`)}</div>`;
15466
15697
  }
15467
15698
  };
15468
15699
  __decorate([property({
@@ -18289,6 +18520,7 @@ let SplitLayoutElement = class SplitLayoutElement extends i {
18289
18520
  width: 100%;
18290
18521
  height: 100%;
18291
18522
  --split-gutter-size: 6px;
18523
+ --split-gutter-hit-size: 12px;
18292
18524
  --split-gutter-color: transparent;
18293
18525
  --split-gutter-hover-color: var(--surface-6);
18294
18526
  --split-gutter-active-color: var(--accent);
@@ -18331,6 +18563,14 @@ let SplitLayoutElement = class SplitLayoutElement extends i {
18331
18563
  display: flex;
18332
18564
  align-items: center;
18333
18565
  justify-content: center;
18566
+ position: relative;
18567
+ }
18568
+
18569
+ .gutter::before {
18570
+ content: '';
18571
+ position: absolute;
18572
+ pointer-events: auto;
18573
+ background: transparent;
18334
18574
  }
18335
18575
 
18336
18576
  :host([direction="row"]) .gutter {
@@ -18339,12 +18579,26 @@ let SplitLayoutElement = class SplitLayoutElement extends i {
18339
18579
  min-height: 100%;
18340
18580
  }
18341
18581
 
18582
+ :host([direction="row"]) .gutter::before {
18583
+ width: var(--split-gutter-hit-size, 12px);
18584
+ height: 100%;
18585
+ left: 50%;
18586
+ transform: translateX(-50%);
18587
+ }
18588
+
18342
18589
  :host([direction="column"]) .gutter {
18343
18590
  height: var(--split-gutter-size);
18344
18591
  cursor: ns-resize;
18345
18592
  min-width: 100%;
18346
18593
  }
18347
18594
 
18595
+ :host([direction="column"]) .gutter::before {
18596
+ height: var(--split-gutter-hit-size, 12px);
18597
+ width: 100%;
18598
+ top: 50%;
18599
+ transform: translateY(-50%);
18600
+ }
18601
+
18348
18602
  .gutter:hover,
18349
18603
  .gutter[data-active] {
18350
18604
  background: var(--split-gutter-hover-color);
@@ -20645,7 +20899,7 @@ let DockHostElement = class DockHostElement extends i {
20645
20899
  class="split"
20646
20900
  direction=${node.direction}
20647
20901
  size=${size === void 0 ? A : String(size)}
20648
- style=${`--split-gutter-size: var(--dock-split-bar-size, 2px)`}
20902
+ style=${`--split-gutter-size: var(--dock-split-bar-size, 2px); --split-collapsed-size: var(--dock-collapsed-size, 40px)`}
20649
20903
  data-dock-path=${pathStr}
20650
20904
  @ixfx-split-resize=${this._onSplitWeightsChanged}
20651
20905
  @ixfx-split-change=${this._onSplitWeightsChanged}
@@ -22662,6 +22916,7 @@ let GridListElement = class GridListElement extends i {
22662
22916
  this.captionHeight = 24;
22663
22917
  this.itemWrap = `stop`;
22664
22918
  this.itemsDraggable = false;
22919
+ this.marqueeFromCellBlank = false;
22665
22920
  this.items = [];
22666
22921
  this._scrollTop = 0;
22667
22922
  this._scrollLeft = 0;
@@ -22745,7 +23000,8 @@ let GridListElement = class GridListElement extends i {
22745
23000
  },
22746
23001
  syncLightDomDraggable: () => {
22747
23002
  for (const child of this.children) if (child instanceof HTMLElement) child.draggable = this.itemsDraggable;
22748
- }
23003
+ },
23004
+ shouldStart: (event) => !(this.marqueeFromCellBlank && this.#isCellBlankPress(event))
22749
23005
  });
22750
23006
  this.#activeCaptionPan = null;
22751
23007
  this.#dragSelect = new ListDragSelectController(this, {
@@ -22777,6 +23033,7 @@ let GridListElement = class GridListElement extends i {
22777
23033
  }
22778
23034
  },
22779
23035
  shouldStart: (event) => {
23036
+ if (this.marqueeFromCellBlank && this.#isCellBlankPress(event)) return true;
22780
23037
  for (const node of event.composedPath()) if (node instanceof Element && node.classList.contains(`cell`)) return false;
22781
23038
  return true;
22782
23039
  }
@@ -22883,6 +23140,7 @@ let GridListElement = class GridListElement extends i {
22883
23140
  --grid-list-item-bg: var(--item-bg);
22884
23141
  --grid-list-radius: var(--radius-m);
22885
23142
  --grid-list-gap: var(--space-xs);
23143
+ --grid-list-padding: var(--space-xs);
22886
23144
  position: relative;
22887
23145
  display: block;
22888
23146
  height: 100%;
@@ -22903,6 +23161,7 @@ let GridListElement = class GridListElement extends i {
22903
23161
  height: 100%;
22904
23162
  overflow-y: auto;
22905
23163
  overflow-x: hidden;
23164
+ padding: var(--grid-list-padding);
22906
23165
  /* Consumer control over the native scrollbar (both orientations): set
22907
23166
  --grid-list-scrollbar-width to "none" to hide it or "thin" to slim it.
22908
23167
  The container is also exposed as ::part(viewport). */
@@ -23053,6 +23312,11 @@ let GridListElement = class GridListElement extends i {
23053
23312
  border-top: 1px solid var(--border, rgba(128, 128, 128, 0.3));
23054
23313
  background: var(--surface-4, transparent);
23055
23314
  }
23315
+ :host([caption-style="under"][caption-position="top"]) .caption {
23316
+ order: -1;
23317
+ border-top: none;
23318
+ border-bottom: 1px solid var(--border, rgba(128, 128, 128, 0.3));
23319
+ }
23056
23320
 
23057
23321
  /* overlaid: caption floats over the content */
23058
23322
  :host([caption-style="overlaid"]) .caption {
@@ -23119,6 +23383,11 @@ let GridListElement = class GridListElement extends i {
23119
23383
  }
23120
23384
  :host([caption-style="separate"][caption-align="left"]) .caption { align-self: flex-start; }
23121
23385
  :host([caption-style="separate"][caption-align="right"]) .caption { align-self: flex-end; }
23386
+ :host([caption-style="separate"][caption-position="top"]) .caption {
23387
+ order: -1;
23388
+ margin-top: 0;
23389
+ margin-bottom: 4px;
23390
+ }
23122
23391
  /* Inset the selection ring (negative offset) so it is never clipped by the
23123
23392
  scroll viewport's overflow when the cell sits against an edge. */
23124
23393
  :host([caption-style="separate"]) .cell.selected .thumb {
@@ -23265,6 +23534,22 @@ let GridListElement = class GridListElement extends i {
23265
23534
  #activeCaptionPan;
23266
23535
  #dragSelect;
23267
23536
  /**
23537
+ * True when a pointer / drag event landed on a cell's blank well: the composed
23538
+ * path reaches `.thumb` without first crossing a slotted node (the item's own
23539
+ * content), the `.caption`, or an interactive control.
23540
+ */
23541
+ #isCellBlankPress(event) {
23542
+ for (const node of event.composedPath()) {
23543
+ if (!(node instanceof Element)) continue;
23544
+ if (node.assignedSlot) return false;
23545
+ if (node.matches?.(`input,button,a,select,textarea,[contenteditable]`)) return false;
23546
+ if (node.classList.contains(`caption`)) return false;
23547
+ if (node.classList.contains(`thumb`)) return true;
23548
+ if (node.classList.contains(`cell`)) return false;
23549
+ }
23550
+ return false;
23551
+ }
23552
+ /**
23268
23553
  * Map a surface-relative (viewport-visible) rect to covered item keys using
23269
23554
  * the same `computeGridWindow` geometry the renderer uses — pure arithmetic,
23270
23555
  * so virtualized off-screen items are covered without DOM measurement.
@@ -24157,6 +24442,11 @@ __decorate([property({
24157
24442
  attribute: `items-draggable`,
24158
24443
  reflect: true
24159
24444
  })], GridListElement.prototype, "itemsDraggable", void 0);
24445
+ __decorate([property({
24446
+ type: Boolean,
24447
+ attribute: `marquee-from-cell-blank`,
24448
+ reflect: true
24449
+ })], GridListElement.prototype, "marqueeFromCellBlank", void 0);
24160
24450
  __decorate([property({ attribute: false })], GridListElement.prototype, "dragPreviewRenderer", void 0);
24161
24451
  __decorate([property({
24162
24452
  type: String,
@@ -24552,6 +24842,360 @@ __decorate([property({
24552
24842
  })], GroupedItemListerElement.prototype, "crossGroupSelection", void 0);
24553
24843
  GroupedItemListerElement = __decorate([safeCustomElement(`ixfx-grouped-item-lister`)], GroupedItemListerElement);
24554
24844
  //#endregion
24845
+ //#region src/tree/tree-drag-registry.ts
24846
+ const TREE_DRAG_MIME = `application/x-ixfx-tree`;
24847
+ const registry = /* @__PURE__ */ new Map();
24848
+ const weakRefs = /* @__PURE__ */ new Map();
24849
+ /**
24850
+ * The source id of the drag currently in flight, if any. There is only ever one
24851
+ * native drag at a time, so a drop target can resolve the dragged nodes during
24852
+ * `dragover` — when `DataTransfer.getData()` is unreadable (protected mode) — by
24853
+ * looking this up in the registry.
24854
+ */
24855
+ let currentSourceId;
24856
+ let sourceIdCounter = 0;
24857
+ function treeDragGenerateSourceId() {
24858
+ return `tree-drag-${++sourceIdCounter}-${Date.now()}`;
24859
+ }
24860
+ function treeDragRegister(sourceId, element, nodes, keys) {
24861
+ registry.set(sourceId, {
24862
+ sourceId,
24863
+ nodes,
24864
+ keys
24865
+ });
24866
+ weakRefs.set(sourceId, new WeakRef(element));
24867
+ currentSourceId = sourceId;
24868
+ }
24869
+ function treeDragUnregister(sourceId) {
24870
+ registry.delete(sourceId);
24871
+ weakRefs.delete(sourceId);
24872
+ if (currentSourceId === sourceId) currentSourceId = void 0;
24873
+ }
24874
+ /** The in-flight drag's `{ keys, nodes, element }`, or `undefined` when nothing is dragging. */
24875
+ function treeDragCurrent() {
24876
+ if (currentSourceId === void 0) return void 0;
24877
+ const entry = registry.get(currentSourceId);
24878
+ if (!entry) return void 0;
24879
+ return {
24880
+ sourceId: currentSourceId,
24881
+ keys: entry.keys,
24882
+ nodes: entry.nodes,
24883
+ element: weakRefs.get(currentSourceId)?.deref()
24884
+ };
24885
+ }
24886
+ //#endregion
24887
+ //#region src/tree/tree-drop-controller.ts
24888
+ const EDGE_ZONE_PX$1 = 24;
24889
+ const MAX_SCROLL_SPEED = 16;
24890
+ function clamp$4(value, min, max) {
24891
+ return Math.max(min, Math.min(max, value));
24892
+ }
24893
+ var TreeDropController = class {
24894
+ #host;
24895
+ #config;
24896
+ #active = false;
24897
+ #current;
24898
+ #source = `internal`;
24899
+ #dragKeys = [];
24900
+ #dragNodes = [];
24901
+ #expandTimer;
24902
+ #expandPendingKey;
24903
+ #scrollRaf = 0;
24904
+ #pointerX = 0;
24905
+ #pointerY = 0;
24906
+ #boundContainer;
24907
+ constructor(host, config) {
24908
+ this.#host = host;
24909
+ this.#config = config;
24910
+ host.addController(this);
24911
+ }
24912
+ /** A drag is currently over this tree. */
24913
+ get isDragging() {
24914
+ return this.#active;
24915
+ }
24916
+ /** A drag is over this tree but the current position is not a valid drop. */
24917
+ get isRejected() {
24918
+ return this.#active && this.#current === void 0;
24919
+ }
24920
+ get dropState() {
24921
+ return this.#current;
24922
+ }
24923
+ hostConnected() {
24924
+ this.#bind();
24925
+ }
24926
+ /** Bind once the shadow `#container` exists (it does not yet at `hostConnected`). */
24927
+ hostUpdated() {
24928
+ this.#bind();
24929
+ }
24930
+ #bind() {
24931
+ const container = this.#config.getScrollContainer();
24932
+ if (!container || container === this.#boundContainer) return;
24933
+ this.#unbind();
24934
+ this.#boundContainer = container;
24935
+ container.addEventListener(`dragenter`, this.#onDragEnter);
24936
+ container.addEventListener(`dragover`, this.#onDragOver);
24937
+ container.addEventListener(`dragleave`, this.#onDragLeave);
24938
+ container.addEventListener(`drop`, this.#onDrop);
24939
+ }
24940
+ #unbind() {
24941
+ const c = this.#boundContainer;
24942
+ if (!c) return;
24943
+ c.removeEventListener(`dragenter`, this.#onDragEnter);
24944
+ c.removeEventListener(`dragover`, this.#onDragOver);
24945
+ c.removeEventListener(`dragleave`, this.#onDragLeave);
24946
+ c.removeEventListener(`drop`, this.#onDrop);
24947
+ this.#boundContainer = void 0;
24948
+ }
24949
+ hostDisconnected() {
24950
+ this.#unbind();
24951
+ this.#clearExpand();
24952
+ this.#stopScroll();
24953
+ this.#active = false;
24954
+ this.#current = void 0;
24955
+ }
24956
+ #onDragEnter = (event) => {
24957
+ if (this.#config.getDndMode() === `none` || !event.dataTransfer) return;
24958
+ if (this.#config.resolveDrag(event.dataTransfer, `hover`) === void 0) return;
24959
+ event.preventDefault();
24960
+ if (!this.#active) {
24961
+ this.#active = true;
24962
+ this.#config.onActiveChange(true);
24963
+ }
24964
+ };
24965
+ #onDragOver = (event) => {
24966
+ if (this.#config.getDndMode() === `none` || !event.dataTransfer) return;
24967
+ this.#pointerX = event.clientX;
24968
+ this.#pointerY = event.clientY;
24969
+ const drag = this.#config.resolveDrag(event.dataTransfer, `hover`);
24970
+ if (!drag) {
24971
+ this.#reject(event);
24972
+ return;
24973
+ }
24974
+ if (!this.#active) {
24975
+ this.#active = true;
24976
+ this.#config.onActiveChange(true);
24977
+ }
24978
+ this.#source = drag.source;
24979
+ this.#dragKeys = drag.keys;
24980
+ this.#dragNodes = drag.nodes;
24981
+ const rows = this.#config.getVisibleRows();
24982
+ this.#handleExpandHover(this.#rowAtPointer(rows, event.clientY));
24983
+ this.#kickScroll();
24984
+ const target = this.#resolveIntent(event, rows, drag.source);
24985
+ if (!target) {
24986
+ this.#reject(event, false);
24987
+ return;
24988
+ }
24989
+ const ctx = {
24990
+ source: drag.source,
24991
+ dragged: drag.nodes.length > 0 ? drag.nodes : void 0,
24992
+ draggedKeys: drag.keys,
24993
+ target,
24994
+ dataTransfer: event.dataTransfer
24995
+ };
24996
+ if (!this.#config.vet(ctx)) {
24997
+ this.#reject(event, false);
24998
+ return;
24999
+ }
25000
+ event.preventDefault();
25001
+ event.dataTransfer.dropEffect = drag.source === `internal` ? `move` : `copy`;
25002
+ this.#setDropState(target);
25003
+ };
25004
+ /** Not a valid drop here: keep the drag active for feedback but accept nothing. */
25005
+ #reject(event, clearHover = true) {
25006
+ if (event.dataTransfer) event.dataTransfer.dropEffect = `none`;
25007
+ this.#setDropState(void 0);
25008
+ if (clearHover) this.#clearExpand();
25009
+ }
25010
+ #onDragLeave = (event) => {
25011
+ const container = this.#config.getScrollContainer();
25012
+ const related = event.relatedTarget;
25013
+ if (container && related && container.contains(related)) return;
25014
+ this.#endHover();
25015
+ };
25016
+ #onDrop = (event) => {
25017
+ if (!event.dataTransfer) {
25018
+ this.#endHover();
25019
+ return;
25020
+ }
25021
+ event.preventDefault();
25022
+ this.#stopScroll();
25023
+ this.#clearExpand();
25024
+ const finish = (dropped) => {
25025
+ this.#current = void 0;
25026
+ this.#active = false;
25027
+ this.#config.onDropStateChange(void 0);
25028
+ this.#config.onActiveChange(false);
25029
+ this.#config.onDragEnd(dropped);
25030
+ };
25031
+ if (this.#config.getDndMode() === `none`) {
25032
+ finish(false);
25033
+ return;
25034
+ }
25035
+ const drag = this.#config.resolveDrag(event.dataTransfer, `drop`);
25036
+ if (!drag) {
25037
+ finish(false);
25038
+ return;
25039
+ }
25040
+ this.#dragKeys = drag.keys;
25041
+ this.#dragNodes = drag.nodes;
25042
+ const target = this.#resolveIntent(event, this.#config.getVisibleRows(), drag.source);
25043
+ if (!target) {
25044
+ finish(false);
25045
+ return;
25046
+ }
25047
+ const ctx = {
25048
+ source: drag.source,
25049
+ dragged: drag.nodes.length > 0 ? drag.nodes : void 0,
25050
+ draggedKeys: drag.keys,
25051
+ target,
25052
+ dataTransfer: event.dataTransfer
25053
+ };
25054
+ if (!this.#config.vet(ctx)) {
25055
+ finish(false);
25056
+ return;
25057
+ }
25058
+ const detail = {
25059
+ ...ctx,
25060
+ external: drag.source === `external` ? drag.nodes : void 0
25061
+ };
25062
+ finish(this.#config.commitDrop(detail));
25063
+ };
25064
+ #endHover() {
25065
+ this.#clearExpand();
25066
+ this.#stopScroll();
25067
+ if (this.#current !== void 0) {
25068
+ this.#current = void 0;
25069
+ this.#config.onDropStateChange(void 0);
25070
+ }
25071
+ if (this.#active) {
25072
+ this.#active = false;
25073
+ this.#config.onActiveChange(false);
25074
+ }
25075
+ }
25076
+ #rowAtPointer(rows, clientY) {
25077
+ if (rows.length === 0) return void 0;
25078
+ if (clientY < rows[0].rect.top) return rows[0];
25079
+ if (clientY > rows[rows.length - 1].rect.bottom) return rows[rows.length - 1];
25080
+ return rows.find((r) => clientY >= r.rect.top && clientY <= r.rect.bottom) ?? rows[rows.length - 1];
25081
+ }
25082
+ #parentRow(rows, index) {
25083
+ const target = rows[index];
25084
+ for (let i = index - 1; i >= 0; i--) if (rows[i].depth === target.depth - 1) return rows[i];
25085
+ }
25086
+ #resolveIntent(event, rows, source) {
25087
+ if (rows.length === 0) return void 0;
25088
+ const row = this.#rowAtPointer(rows, event.clientY);
25089
+ if (!row) return void 0;
25090
+ const rowIndex = rows.indexOf(row);
25091
+ const f = clamp$4((event.clientY - row.rect.top) / row.rect.height, 0, 1);
25092
+ let position = f < .25 ? `before` : f > .75 ? `after` : `inside`;
25093
+ const isLeaf = row.node.item.isLeaf === true;
25094
+ if (position === `inside` && isLeaf) position = `after`;
25095
+ let insideFromExpandedAfter = false;
25096
+ if (position === `after` && !isLeaf && this.#config.isExpanded(row.key)) {
25097
+ position = `inside`;
25098
+ insideFromExpandedAfter = true;
25099
+ }
25100
+ let parentKey;
25101
+ let referenceKey;
25102
+ let depth;
25103
+ let index;
25104
+ if (position === `inside`) {
25105
+ parentKey = row.key;
25106
+ referenceKey = row.key;
25107
+ depth = row.depth + 1;
25108
+ index = insideFromExpandedAfter ? 0 : row.node.children?.length ?? 0;
25109
+ } else {
25110
+ const indent = this.#config.getIndentPx() || 1;
25111
+ const treeLeft = rows[0].rect.left;
25112
+ const effDepth = clamp$4(Math.round((event.clientX - treeLeft) / indent), 0, row.depth);
25113
+ let anchorIndex = rowIndex;
25114
+ for (let up = row.depth - effDepth; up > 0; up--) {
25115
+ const parent = this.#parentRow(rows, anchorIndex);
25116
+ if (!parent) break;
25117
+ anchorIndex = rows.indexOf(parent);
25118
+ }
25119
+ const anchor = rows[anchorIndex];
25120
+ referenceKey = anchor.key;
25121
+ depth = anchor.depth;
25122
+ const parent = this.#parentRow(rows, anchorIndex);
25123
+ parentKey = parent?.key;
25124
+ const refIdx = (parent ? parent.node.children ?? [] : rows.filter((r) => r.depth === 0).map((r) => r.node)).findIndex((n) => n.item.key === anchor.key);
25125
+ index = (refIdx < 0 ? 0 : refIdx) + (position === `after` ? 1 : 0);
25126
+ }
25127
+ if (source === `internal`) {
25128
+ for (const key of this.#dragKeys) {
25129
+ if (key === parentKey || key === referenceKey) return void 0;
25130
+ if (parentKey !== void 0 && this.#config.isSelfOrDescendant(key, parentKey)) return void 0;
25131
+ if (position !== `inside` && this.#config.isSelfOrDescendant(key, referenceKey)) return void 0;
25132
+ }
25133
+ if (this.#dragKeys.length > 0 && !this.#config.isReorderAllowed()) {
25134
+ if (!this.#dragKeys.some((k) => this.#config.getParentKey(k) !== parentKey)) return void 0;
25135
+ }
25136
+ }
25137
+ return {
25138
+ parentKey,
25139
+ index,
25140
+ referenceKey,
25141
+ position,
25142
+ depth
25143
+ };
25144
+ }
25145
+ #handleExpandHover(row) {
25146
+ if (!row || row.node.item.isLeaf === true || this.#config.isExpanded(row.key)) {
25147
+ this.#clearExpand();
25148
+ return;
25149
+ }
25150
+ if (row.key === this.#expandPendingKey) return;
25151
+ this.#clearExpand();
25152
+ const key = this.#expandPendingKey = row.key;
25153
+ this.#expandTimer = setTimeout(() => {
25154
+ this.#expandTimer = void 0;
25155
+ if (this.#expandPendingKey === key) {
25156
+ this.#expandPendingKey = void 0;
25157
+ this.#config.expandKey(key);
25158
+ }
25159
+ }, Math.max(0, this.#config.getExpandHoverMs()));
25160
+ }
25161
+ #clearExpand() {
25162
+ if (this.#expandTimer !== void 0) {
25163
+ clearTimeout(this.#expandTimer);
25164
+ this.#expandTimer = void 0;
25165
+ }
25166
+ this.#expandPendingKey = void 0;
25167
+ }
25168
+ #kickScroll() {
25169
+ if (this.#scrollRaf === 0) this.#scrollRaf = requestAnimationFrame(this.#scrollTick);
25170
+ }
25171
+ #scrollTick = () => {
25172
+ this.#scrollRaf = 0;
25173
+ if (!this.#active) return;
25174
+ const container = this.#config.getScrollContainer();
25175
+ if (!container) return;
25176
+ const rect = container.getBoundingClientRect();
25177
+ const y = this.#pointerY - rect.top;
25178
+ let dy = 0;
25179
+ if (y < EDGE_ZONE_PX$1) dy = -Math.ceil(MAX_SCROLL_SPEED * (1 - Math.max(0, y) / EDGE_ZONE_PX$1));
25180
+ else if (y > rect.height - EDGE_ZONE_PX$1) dy = Math.ceil(MAX_SCROLL_SPEED * (1 - Math.max(0, rect.height - y) / EDGE_ZONE_PX$1));
25181
+ if (dy !== 0 && this.#pointerX >= rect.left && this.#pointerX <= rect.right) {
25182
+ container.scrollTop += dy;
25183
+ this.#kickScroll();
25184
+ }
25185
+ };
25186
+ #stopScroll() {
25187
+ if (this.#scrollRaf !== 0) {
25188
+ cancelAnimationFrame(this.#scrollRaf);
25189
+ this.#scrollRaf = 0;
25190
+ }
25191
+ }
25192
+ #setDropState(state) {
25193
+ 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;
25194
+ this.#current = state;
25195
+ if (!unchanged || this.#active) this.#config.onDropStateChange(state);
25196
+ }
25197
+ };
25198
+ //#endregion
24555
25199
  //#region src/tree/tree-base.ts
24556
25200
  /** Above this many rows entering / leaving at once, skip the animation entirely. */
24557
25201
  const MAX_ANIMATED_ROWS = 50;
@@ -24564,6 +25208,10 @@ let TreeBaseElement = class TreeBaseElement extends i {
24564
25208
  this._selectedNodes = /* @__PURE__ */ new Set();
24565
25209
  this.disableKeyboardNavigation = false;
24566
25210
  this.registry = void 0;
25211
+ this.itemDnd = `none`;
25212
+ this.dndExpandHoverMs = 500;
25213
+ this.reorderable = true;
25214
+ this.applyInternalDrop = true;
24567
25215
  this._expandedKeys = /* @__PURE__ */ new Set();
24568
25216
  this.tickled = new TickledController(this, {
24569
25217
  getItems: () => {
@@ -24572,6 +25220,7 @@ let TreeBaseElement = class TreeBaseElement extends i {
24572
25220
  enabled: true
24573
25221
  }));
24574
25222
  },
25223
+ hoverDisabled: () => this.#drop?.isDragging === true || this.#nativeDrag?.isDragging === true,
24575
25224
  onStateChange: () => {
24576
25225
  this.setAttribute(`data-input-mode`, this.tickled.inputMode);
24577
25226
  }
@@ -24588,6 +25237,12 @@ let TreeBaseElement = class TreeBaseElement extends i {
24588
25237
  this.#leaving = /* @__PURE__ */ new Map();
24589
25238
  this.#leavingAnimations = /* @__PURE__ */ new Map();
24590
25239
  this.#treeAnimReady = false;
25240
+ this.#dragSourceId = ``;
25241
+ this.#nativeDragKeys = /* @__PURE__ */ new Set();
25242
+ this.#internalDropHandled = false;
25243
+ this.#onContainerScroll = () => {
25244
+ this.#rowsCache = void 0;
25245
+ };
24591
25246
  this.#onKeyDown = (event) => {
24592
25247
  if (this.search.isOpen) return;
24593
25248
  const nodes = this.#getAllNodes();
@@ -24741,6 +25396,28 @@ let TreeBaseElement = class TreeBaseElement extends i {
24741
25396
  #leaving;
24742
25397
  #leavingAnimations;
24743
25398
  #treeAnimReady;
25399
+ #dragSourceId;
25400
+ #postDragSuppressTimer;
25401
+ #dropState;
25402
+ #drop;
25403
+ #nativeDrag;
25404
+ /** Keys of the nodes currently being dragged out (for `.dragging` styling). */
25405
+ #nativeDragKeys;
25406
+ /** Set by `commitDrop` for a same-tree internal drop, read by the native `onEnd` to avoid a duplicate `tree-drag-end`. */
25407
+ #internalDropHandled;
25408
+ /** Per-drag cache of measured rows; invalidated on scroll / update. */
25409
+ #rowsCache;
25410
+ #onContainerScroll;
25411
+ get nativeDrag() {
25412
+ return this.#nativeDrag;
25413
+ }
25414
+ get dropState() {
25415
+ return this.#dropState;
25416
+ }
25417
+ /** Keys of the nodes currently being dragged out of this tree. */
25418
+ get nativeDragKeys() {
25419
+ return this.#nativeDragKeys;
25420
+ }
24744
25421
  #cancelLeavingAnimation(key) {
24745
25422
  this.#leavingAnimations.get(key)?.cancel();
24746
25423
  this.#leavingAnimations.delete(key);
@@ -24852,6 +25529,7 @@ let TreeBaseElement = class TreeBaseElement extends i {
24852
25529
  this.#treeAnimReady = true;
24853
25530
  }
24854
25531
  updated(changedProps) {
25532
+ this.#rowsCache = void 0;
24855
25533
  if (changedProps.has(`root`) && this.root) {
24856
25534
  this.#abortPending();
24857
25535
  this._expandedKeys.clear();
@@ -24908,6 +25586,291 @@ let TreeBaseElement = class TreeBaseElement extends i {
24908
25586
  });
24909
25587
  this.registry = registry;
24910
25588
  this.#syncSearchCommands();
25589
+ this.#initDnd();
25590
+ }
25591
+ #initDnd() {
25592
+ this.#dragSourceId = treeDragGenerateSourceId();
25593
+ this.#nativeDrag = new ListNativeDragController(this, {
25594
+ source: `tree-list`,
25595
+ isEnabled: () => this.itemDnd !== `none` && this.#anyRowDraggable(),
25596
+ resolveEventToKey: (target) => {
25597
+ if (!(target instanceof Element)) return void 0;
25598
+ return target.closest(`li[data-key]`)?.getAttribute(`data-key`) ?? void 0;
25599
+ },
25600
+ prepareSelection: (originKey) => {
25601
+ const allNodes = this.#getAllNodes();
25602
+ const originEntry = allNodes.find((n) => n.node.item.key === originKey);
25603
+ if (!originEntry) return void 0;
25604
+ const selectedNodes = new Set(this._selectedNodes);
25605
+ if (!selectedNodes.has(originEntry.node)) selectedNodes.clear();
25606
+ selectedNodes.add(originEntry.node);
25607
+ const orderedKeys = allNodes.filter((n) => selectedNodes.has(n.node)).map((n) => n.node.item.key);
25608
+ const topKeys = new Set(this.root ? pruneDraggedDescendants(this.root, orderedKeys) : orderedKeys);
25609
+ const selected = allNodes.filter((n) => topKeys.has(n.node.item.key)).map((n, i) => ({
25610
+ key: n.node.item.key,
25611
+ index: i,
25612
+ item: n.node,
25613
+ element: this.shadowRoot?.querySelector(`li[data-key="${CSS.escape(n.node.item.key)}"]`) ?? void 0
25614
+ }));
25615
+ const origin = selected.find((e) => e.key === originKey) ?? {
25616
+ key: originKey,
25617
+ index: 0,
25618
+ item: originEntry.node,
25619
+ element: void 0
25620
+ };
25621
+ if (!selected.some((e) => e.key === origin.key)) selected.unshift(origin);
25622
+ return {
25623
+ origin,
25624
+ selected
25625
+ };
25626
+ },
25627
+ getMetrics: () => ({ rowHeight: this.#getItemHeight() }),
25628
+ getRenderer: () => this.dragPreviewRenderer,
25629
+ dispatch: (detail) => {
25630
+ const keys = detail.selected.map((e) => e.key);
25631
+ const nodes = detail.selected.map((e) => e.item);
25632
+ const evt = new CustomEvent(`list-native-drag-start`, {
25633
+ detail,
25634
+ bubbles: true,
25635
+ composed: true,
25636
+ cancelable: true
25637
+ });
25638
+ const notCancelled = this.dispatchEvent(evt);
25639
+ if (notCancelled) {
25640
+ this.#nativeDragKeys = new Set(keys);
25641
+ this.#internalDropHandled = false;
25642
+ if (this.itemDnd === `internal` || this.itemDnd === `both`) treeDragRegister(this.#dragSourceId, this, nodes, keys);
25643
+ this.dispatchEvent(new CustomEvent(`tree-drag-start`, {
25644
+ detail: {
25645
+ nodes,
25646
+ keys
25647
+ },
25648
+ bubbles: true,
25649
+ composed: true
25650
+ }));
25651
+ }
25652
+ return notCancelled;
25653
+ },
25654
+ writeDataTransfer: (dataTransfer, detail) => {
25655
+ if (this.itemDnd !== `internal` && this.itemDnd !== `both`) return;
25656
+ const keys = detail.selected.map((e) => e.key);
25657
+ const nodes = detail.selected.map((e) => e.item);
25658
+ try {
25659
+ dataTransfer.setData(TREE_DRAG_MIME, JSON.stringify({
25660
+ sourceId: this.#dragSourceId,
25661
+ keys,
25662
+ nodes
25663
+ }));
25664
+ dataTransfer.setData(`text/plain`, nodes.map((n) => n.item.label).join(`\n`));
25665
+ } catch {}
25666
+ },
25667
+ onActiveChange: (active) => {
25668
+ this.toggleAttribute(`data-native-dragging`, active);
25669
+ if (!active) this.#nativeDragKeys = /* @__PURE__ */ new Set();
25670
+ this.requestUpdate();
25671
+ },
25672
+ onEnd: () => {
25673
+ treeDragUnregister(this.#dragSourceId);
25674
+ this.#nativeDragKeys = /* @__PURE__ */ new Set();
25675
+ this.#postDragSuppressTimer = setTimeout(() => {
25676
+ this.#postDragSuppressTimer = void 0;
25677
+ }, 150);
25678
+ if (!this.#internalDropHandled) this.dispatchEvent(new CustomEvent(`tree-drag-end`, {
25679
+ detail: { dropped: false },
25680
+ bubbles: true,
25681
+ composed: true
25682
+ }));
25683
+ this.#internalDropHandled = false;
25684
+ }
25685
+ });
25686
+ this.#drop = new TreeDropController(this, {
25687
+ getDndMode: () => this.itemDnd,
25688
+ getExpandHoverMs: () => this.dndExpandHoverMs,
25689
+ getVisibleRows: () => this.#getVisibleRows(),
25690
+ getIndentPx: () => this.#getIndentPx(),
25691
+ getScrollContainer: () => this.#container(),
25692
+ isSelfOrDescendant: (ancestorKey, key) => this.#isSelfOrDescendant(ancestorKey, key),
25693
+ isExpanded: (key) => this._expandedKeys.has(key),
25694
+ isReorderAllowed: () => this.reorderable,
25695
+ getParentKey: (key) => this.#findParentByKey(key)?.item.key,
25696
+ resolveDrag: (dataTransfer, phase) => this.#resolveDrag(dataTransfer, phase),
25697
+ vet: (ctx) => {
25698
+ if (this.canDrop && !this.canDrop(ctx)) return false;
25699
+ const evt = new CustomEvent(`tree-drag-over`, {
25700
+ detail: ctx,
25701
+ bubbles: true,
25702
+ composed: true,
25703
+ cancelable: true
25704
+ });
25705
+ return this.dispatchEvent(evt);
25706
+ },
25707
+ expandKey: (key) => this.#springLoadExpand(key),
25708
+ onDropStateChange: (state) => {
25709
+ this.#dropState = state;
25710
+ this.toggleAttribute(`data-drop-rejected`, this.#drop.isDragging && state === void 0);
25711
+ this.requestUpdate();
25712
+ },
25713
+ onActiveChange: (active) => {
25714
+ if (active) {
25715
+ this.#rowsCache = void 0;
25716
+ this.#container()?.addEventListener(`scroll`, this.#onContainerScroll, { passive: true });
25717
+ } else {
25718
+ this.#container()?.removeEventListener(`scroll`, this.#onContainerScroll);
25719
+ this.#rowsCache = void 0;
25720
+ this.toggleAttribute(`data-drop-rejected`, false);
25721
+ }
25722
+ this.requestUpdate();
25723
+ },
25724
+ commitDrop: (detail) => {
25725
+ if (!this.dispatchEvent(new CustomEvent(`tree-drop`, {
25726
+ detail,
25727
+ bubbles: true,
25728
+ composed: true,
25729
+ cancelable: true
25730
+ }))) return false;
25731
+ if (this.applyInternalDrop && detail.source === `internal` && this.model) this.model.applyDrop({
25732
+ source: `internal`,
25733
+ draggedKeys: detail.draggedKeys,
25734
+ target: detail.target
25735
+ });
25736
+ this.#internalDropHandled = true;
25737
+ return true;
25738
+ },
25739
+ onDragEnd: (dropped) => {
25740
+ this.#dropState = void 0;
25741
+ this.toggleAttribute(`data-drop-rejected`, false);
25742
+ this.requestUpdate();
25743
+ this.dispatchEvent(new CustomEvent(`tree-drag-end`, {
25744
+ detail: { dropped },
25745
+ bubbles: true,
25746
+ composed: true
25747
+ }));
25748
+ }
25749
+ });
25750
+ }
25751
+ #container() {
25752
+ return this.shadowRoot?.querySelector(`#container`) ?? void 0;
25753
+ }
25754
+ #getIndentPx() {
25755
+ const parsed = Number.parseInt(getComputedStyle(this).getPropertyValue(`--tree-indent`), 10);
25756
+ return Number.isNaN(parsed) ? 20 : parsed;
25757
+ }
25758
+ #getItemHeight() {
25759
+ const parsed = Number.parseInt(getComputedStyle(this).getPropertyValue(`--tree-item-height`), 10);
25760
+ return Number.isNaN(parsed) ? 30 : parsed;
25761
+ }
25762
+ /** Measured rows, cached for the duration of a drag and invalidated on scroll / update. */
25763
+ #getVisibleRows() {
25764
+ if (this.#rowsCache) return this.#rowsCache;
25765
+ const rows = [];
25766
+ for (const { node, depth } of this.#getAllNodes()) {
25767
+ const li = this.shadowRoot?.querySelector(`li[data-key="${CSS.escape(node.item.key)}"]`);
25768
+ if (li) rows.push({
25769
+ key: node.item.key,
25770
+ node,
25771
+ depth,
25772
+ rect: li.getBoundingClientRect()
25773
+ });
25774
+ }
25775
+ this.#rowsCache = rows;
25776
+ return rows;
25777
+ }
25778
+ /** Route spring-load through the real expand path so lazy children load and `expand` fires with the right depth. */
25779
+ #springLoadExpand(key) {
25780
+ if (this._expandedKeys.has(key)) return;
25781
+ const entry = this.#getAllNodes().find((n) => n.node.item.key === key);
25782
+ if (!entry || entry.node.item.isLeaf === true) return;
25783
+ this.#toggleExpand(entry.node, entry.depth);
25784
+ this.#rowsCache = void 0;
25785
+ this.requestUpdate();
25786
+ }
25787
+ #isSelfOrDescendant(ancestorKey, key) {
25788
+ if (ancestorKey === key) return true;
25789
+ let node = this.#getAllNodes().find((n) => n.node.item.key === key)?.node;
25790
+ let parent = node ? this.#findParentByKey(node.item.key) : void 0;
25791
+ while (parent) {
25792
+ if (parent.item.key === ancestorKey) return true;
25793
+ node = parent;
25794
+ parent = this.#findParentByKey(node.item.key);
25795
+ }
25796
+ return false;
25797
+ }
25798
+ #findParentByKey(key) {
25799
+ for (const { node } of this.#getAllNodes()) if (node.children?.some((c) => c.item.key === key)) return node;
25800
+ }
25801
+ /**
25802
+ * Decide whether a DataTransfer is acceptable and resolve the dragged set.
25803
+ * During `hover` the DataTransfer is read-protected, so an internal drag is
25804
+ * recovered from the module-level drag registry (there is only one at a time).
25805
+ */
25806
+ #resolveDrag(dataTransfer, phase) {
25807
+ const mode = this.itemDnd;
25808
+ if (mode === `none`) return void 0;
25809
+ const canInternal = mode === `internal` || mode === `both`;
25810
+ const canExternal = mode === `external` || mode === `both`;
25811
+ let types = [];
25812
+ try {
25813
+ types = Array.from(dataTransfer.types);
25814
+ } catch {}
25815
+ const current = treeDragCurrent();
25816
+ const looksInternal = types.includes(TREE_DRAG_MIME) || current !== void 0;
25817
+ if (canInternal && looksInternal) {
25818
+ if (current && current.keys.length > 0) {
25819
+ const keys = this.root ? pruneDraggedDescendants(this.root, current.keys) : current.keys;
25820
+ return {
25821
+ source: `internal`,
25822
+ keys,
25823
+ nodes: keys.map((k) => this.#getAllNodes().find((n) => n.node.item.key === k)?.node).filter((n) => n !== void 0)
25824
+ };
25825
+ }
25826
+ if (phase === `drop`) try {
25827
+ const raw = dataTransfer.getData(TREE_DRAG_MIME);
25828
+ if (raw) {
25829
+ const payload = JSON.parse(raw);
25830
+ return {
25831
+ source: `internal`,
25832
+ keys: payload.keys ?? [],
25833
+ nodes: payload.nodes ?? []
25834
+ };
25835
+ }
25836
+ } catch {}
25837
+ return {
25838
+ source: `internal`,
25839
+ keys: [],
25840
+ nodes: []
25841
+ };
25842
+ }
25843
+ if (canExternal && !looksInternal) {
25844
+ if (this.acceptDropTypes !== void 0) {
25845
+ const accepted = this.acceptDropTypes.trim().split(/\s+/).filter(Boolean);
25846
+ const present = Array.from(dataTransfer.types);
25847
+ if (accepted.length > 0 && !accepted.some((t) => present.includes(t))) return void 0;
25848
+ }
25849
+ if (phase === `drop`) {
25850
+ const nodes = this.parseExternalDrop?.(dataTransfer);
25851
+ if (!nodes || nodes.length === 0) return void 0;
25852
+ return {
25853
+ source: `external`,
25854
+ keys: nodes.map((n) => n.item.key),
25855
+ nodes
25856
+ };
25857
+ }
25858
+ return this.parseExternalDrop ? {
25859
+ source: `external`,
25860
+ keys: [],
25861
+ nodes: []
25862
+ } : void 0;
25863
+ }
25864
+ }
25865
+ #anyRowDraggable() {
25866
+ if (this.itemDnd === `none`) return false;
25867
+ const allNodes = this.#getAllNodes();
25868
+ if (allNodes.length === 0) return false;
25869
+ if (!this.canDrag) return true;
25870
+ return allNodes.some(({ node }) => this.canDrag({
25871
+ nodes: [node],
25872
+ keys: [node.item.key]
25873
+ }));
24911
25874
  }
24912
25875
  disconnectedCallback() {
24913
25876
  super.disconnectedCallback();
@@ -24918,6 +25881,12 @@ let TreeBaseElement = class TreeBaseElement extends i {
24918
25881
  this.#modelUnsubscribe = void 0;
24919
25882
  this.#unregisterSearchCommands?.();
24920
25883
  this.#unregisterSearchCommands = void 0;
25884
+ if (this.#postDragSuppressTimer !== void 0) {
25885
+ clearTimeout(this.#postDragSuppressTimer);
25886
+ this.#postDragSuppressTimer = void 0;
25887
+ }
25888
+ this.#container()?.removeEventListener(`scroll`, this.#onContainerScroll);
25889
+ treeDragUnregister(this.#dragSourceId);
24921
25890
  if (this.#ownedRegistry) {
24922
25891
  this.#ownedRegistry.unregister(`select-all`);
24923
25892
  this.#ownedRegistry.unregister(`deselect-all`);
@@ -25092,6 +26061,7 @@ let TreeBaseElement = class TreeBaseElement extends i {
25092
26061
  `;
25093
26062
  }
25094
26063
  _handleItemClick(node, depth, event) {
26064
+ if (this.#drop?.isDragging || this.#nativeDrag?.isDragging || this.#postDragSuppressTimer !== void 0) return;
25095
26065
  if (event && event.detail >= 2) return;
25096
26066
  if (this.#pendingNode && this._expandedKeys.has(this.#pendingNode.item.key)) this.#abortPending();
25097
26067
  this.tickled.handlePointerEnter(node.item.key);
@@ -25526,6 +26496,23 @@ let TreeBaseElement = class TreeBaseElement extends i {
25526
26496
  </ul>
25527
26497
  `;
25528
26498
  }
26499
+ /** Shared drag-and-drop render state for a row, used by every `_renderNode` variant. */
26500
+ _dndRowState(node) {
26501
+ const draggable = this.itemDnd !== `none` && (this.root?.children?.length ?? 0) > 0 && (this.itemDnd === `internal` || this.itemDnd === `both` || this.itemDnd === `external`) && (this.canDrag?.({
26502
+ nodes: [node],
26503
+ keys: [node.item.key]
26504
+ }) ?? true);
26505
+ const dragging = this.#nativeDragKeys.has(node.item.key);
26506
+ const state = this.#dropState;
26507
+ const dropTarget = state !== void 0 && state.referenceKey === node.item.key;
26508
+ return {
26509
+ draggable,
26510
+ dragging,
26511
+ dropTarget,
26512
+ dropPosition: dropTarget ? state.position : void 0,
26513
+ dropDepth: dropTarget ? state.depth : void 0
26514
+ };
26515
+ }
25529
26516
  _renderNode(node, depth) {
25530
26517
  const selected = this._isSelected(node);
25531
26518
  const tickled = this._isFocused(node);
@@ -25534,9 +26521,10 @@ let TreeBaseElement = class TreeBaseElement extends i {
25534
26521
  const isPending = this.#pendingNode === node;
25535
26522
  const isFiltered = this._isFilteredOut(node, depth);
25536
26523
  const isLeaving = this._isLeaving(node);
26524
+ const dnd = this._dndRowState(node);
25537
26525
  return b`
25538
26526
  <li
25539
- class="item ${selected ? `selected` : ``} ${tickled ? `tickled` : ``} ${isBranch ? `branch` : `leaf`} ${isFiltered ? `filtered` : ``} ${isLeaving ? `leaving` : ``}"
26527
+ class="item ${selected ? `selected` : ``} ${tickled ? `tickled` : ``} ${isBranch ? `branch` : `leaf`} ${isFiltered ? `filtered` : ``} ${isLeaving ? `leaving` : ``} ${dnd.dragging ? `dragging` : ``} ${dnd.dropTarget && dnd.dropPosition === `inside` ? `drop-into` : ``}"
25540
26528
  role="treeitem"
25541
26529
  aria-selected=${selected}
25542
26530
  aria-expanded=${isBranch ? isExpanded : A}
@@ -25544,6 +26532,8 @@ let TreeBaseElement = class TreeBaseElement extends i {
25544
26532
  aria-level=${depth + 1}
25545
26533
  data-key=${node.item.key}
25546
26534
  style=${`--depth: ${depth}`}
26535
+ draggable=${dnd.draggable ? `true` : A}
26536
+ part=${dnd.dropTarget ? `drop-target` : A}
25547
26537
  @click=${(e) => this._handleItemClick(node, depth, e)}
25548
26538
  @pointerenter=${() => {
25549
26539
  this.tickled.handlePointerEnter(node.item.key);
@@ -25557,6 +26547,11 @@ let TreeBaseElement = class TreeBaseElement extends i {
25557
26547
  }));
25558
26548
  }}
25559
26549
  >
26550
+ ${dnd.dropTarget && (dnd.dropPosition === `before` || dnd.dropPosition === `after`) ? b`<div
26551
+ class="drop-line drop-line-${dnd.dropPosition}"
26552
+ style="--drop-depth: ${dnd.dropDepth ?? depth}"
26553
+ part="drop-indicator"
26554
+ ></div>` : A}
25560
26555
  ${this.renderNodeContent(node, depth)}
25561
26556
  ${isPending ? this.renderLoadingIndicator() : A}
25562
26557
  </li>
@@ -25577,6 +26572,10 @@ let TreeBaseElement = class TreeBaseElement extends i {
25577
26572
  --tree-item-height: 30px;
25578
26573
  --tree-caret-size: 30px;
25579
26574
  --tree-radius: var(--radius-s);
26575
+ --tree-drop-line: var(--accent);
26576
+ --tree-drop-nub-size: 5px;
26577
+ --tree-drop-into-outline: 2px solid var(--accent);
26578
+ --tree-drop-into-bg: color-mix(in srgb, var(--accent) 14%, transparent);
25580
26579
 
25581
26580
  position: relative;
25582
26581
  display: block;
@@ -25604,6 +26603,7 @@ let TreeBaseElement = class TreeBaseElement extends i {
25604
26603
  }
25605
26604
 
25606
26605
  .item {
26606
+ position: relative; /* anchor for the .drop-line before/after indicator */
25607
26607
  display: flex;
25608
26608
  align-items: center;
25609
26609
  height: var(--tree-item-height);
@@ -25679,11 +26679,49 @@ let TreeBaseElement = class TreeBaseElement extends i {
25679
26679
  overflow: hidden;
25680
26680
  }
25681
26681
 
25682
- ::highlight(incr-search-highlight) {
25683
- background: var(--search-highlight-bg, var(--accent));
25684
- color: var(--search-highlight-color, var(--accent-text, #fff));
25685
- border-radius: 2px;
25686
- padding: 0 1px;
26682
+ /* Drag and drop styles */
26683
+ .item.dragging {
26684
+ opacity: 0.4;
26685
+ }
26686
+
26687
+ .item.drop-into {
26688
+ outline: var(--tree-drop-into-outline);
26689
+ outline-offset: -2px;
26690
+ background: var(--tree-drop-into-bg);
26691
+ }
26692
+
26693
+ .drop-line {
26694
+ position: absolute;
26695
+ left: calc(var(--tree-indent) * var(--drop-depth, 0) + var(--space-xs));
26696
+ right: var(--space-xs);
26697
+ height: 2px;
26698
+ background: var(--tree-drop-line);
26699
+ pointer-events: none;
26700
+ z-index: 10;
26701
+ }
26702
+
26703
+ /* Nub at the left end of the insertion line (VS Code style). */
26704
+ .drop-line::before {
26705
+ content: "";
26706
+ position: absolute;
26707
+ left: calc(-1 * var(--tree-drop-nub-size) / 2);
26708
+ top: calc(50% - var(--tree-drop-nub-size) / 2);
26709
+ width: var(--tree-drop-nub-size);
26710
+ height: var(--tree-drop-nub-size);
26711
+ border-radius: 50%;
26712
+ background: var(--tree-drop-line);
26713
+ }
26714
+
26715
+ .drop-line-before {
26716
+ top: -1px;
26717
+ }
26718
+
26719
+ .drop-line-after {
26720
+ bottom: -1px;
26721
+ }
26722
+
26723
+ :host([data-drop-rejected]) {
26724
+ cursor: not-allowed;
25687
26725
  }
25688
26726
  `
25689
26727
  ];
@@ -25717,6 +26755,31 @@ __decorate([property({
25717
26755
  attribute: `search-modes`,
25718
26756
  reflect: true
25719
26757
  })], TreeBaseElement.prototype, "searchModes", void 0);
26758
+ __decorate([property({
26759
+ reflect: true,
26760
+ attribute: `item-dnd`
26761
+ })], TreeBaseElement.prototype, "itemDnd", void 0);
26762
+ __decorate([property({
26763
+ type: String,
26764
+ attribute: `accept-drop-types`
26765
+ })], TreeBaseElement.prototype, "acceptDropTypes", void 0);
26766
+ __decorate([property({
26767
+ type: Number,
26768
+ attribute: `dnd-expand-hover-ms`
26769
+ })], TreeBaseElement.prototype, "dndExpandHoverMs", void 0);
26770
+ __decorate([property({
26771
+ reflect: true,
26772
+ attribute: `reorderable`,
26773
+ converter: {
26774
+ fromAttribute: (value) => value !== `false`,
26775
+ toAttribute: (value) => value ? null : `false`
26776
+ }
26777
+ })], TreeBaseElement.prototype, "reorderable", void 0);
26778
+ __decorate([property({ attribute: false })], TreeBaseElement.prototype, "dragPreviewRenderer", void 0);
26779
+ __decorate([property({ attribute: false })], TreeBaseElement.prototype, "canDrag", void 0);
26780
+ __decorate([property({ attribute: false })], TreeBaseElement.prototype, "canDrop", void 0);
26781
+ __decorate([property({ attribute: false })], TreeBaseElement.prototype, "parseExternalDrop", void 0);
26782
+ __decorate([property({ attribute: false })], TreeBaseElement.prototype, "applyInternalDrop", void 0);
25720
26783
  __decorate([state()], TreeBaseElement.prototype, "_expandedKeys", void 0);
25721
26784
  __decorate([state()], TreeBaseElement.prototype, "_focusedDepth", void 0);
25722
26785
  TreeBaseElement = __decorate([safeCustomElement(`ixfx-tree-base`)], TreeBaseElement);
@@ -25798,9 +26861,10 @@ let TreeListElement = class TreeListElement extends TreeBaseElement {
25798
26861
  const hasIconAtDepth = this.#depthsWithIcons.has(depth);
25799
26862
  const isLeaving = this._isLeaving(node);
25800
26863
  const isFiltered = this._isFilteredOut(node, depth);
26864
+ const dnd = this._dndRowState(node);
25801
26865
  return b`
25802
26866
  <li
25803
- class="item ${selected ? `selected` : ``} ${tickled ? `tickled` : ``} ${isBranch ? `branch` : `leaf`} ${hasIconAtDepth ? `has-icon-at-depth` : ``} ${isLeaving ? `leaving` : ``} ${isFiltered ? `filtered` : ``}"
26867
+ 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` : ``}"
25804
26868
  data-key=${node.item.key}
25805
26869
  role="treeitem"
25806
26870
  aria-selected=${selected}
@@ -25808,9 +26872,16 @@ let TreeListElement = class TreeListElement extends TreeBaseElement {
25808
26872
  aria-hidden=${isLeaving ? `true` : A}
25809
26873
  aria-level=${depth + 1}
25810
26874
  style=${`--depth: ${depth}`}
26875
+ draggable=${dnd.draggable ? `true` : A}
26876
+ part=${dnd.dropTarget ? `drop-target` : A}
25811
26877
  @click=${(e) => this._handleItemClick(node, depth, e)}
25812
26878
  @pointerenter=${() => this.tickled.handlePointerEnter(node.item.key)}
25813
26879
  >
26880
+ ${dnd.dropTarget && (dnd.dropPosition === `before` || dnd.dropPosition === `after`) ? b`<div
26881
+ class="drop-line drop-line-${dnd.dropPosition}"
26882
+ style="--drop-depth: ${dnd.dropDepth ?? depth}"
26883
+ part="drop-indicator"
26884
+ ></div>` : A}
25814
26885
  ${this.renderNodeContent(node, depth)}
25815
26886
  ${isPending ? this.renderLoadingIndicator() : A}
25816
26887
  </li>
@@ -29348,6 +30419,77 @@ function valueToRelative(value, min, max) {
29348
30419
  const HANDLE_HIT_RADIUS = .06;
29349
30420
  const DEFAULT_VALUE_MIN_FRACTION = .01;
29350
30421
  const CHANGE_DEBOUNCE_MS = 150;
30422
+ /**
30423
+ * Evaluate a simple arithmetic expression (decimal numbers, `+ - * /` and
30424
+ * parentheses). Returns `undefined` when the string contains anything else.
30425
+ * Used for CSS `calc()`-derived values — a safe substitute for `new Function`.
30426
+ */
30427
+ function evaluateArithmetic(source) {
30428
+ let pos = 0;
30429
+ const skipSpaces = () => {
30430
+ while (pos < source.length) {
30431
+ const c = source[pos];
30432
+ if (c !== ` ` && c !== `\t` && c !== `\n` && c !== `\r`) break;
30433
+ pos++;
30434
+ }
30435
+ };
30436
+ function parseNumber() {
30437
+ skipSpaces();
30438
+ const match = /^(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?/i.exec(source.slice(pos));
30439
+ if (!match) return void 0;
30440
+ pos += match[0].length;
30441
+ return Number.parseFloat(match[0]);
30442
+ }
30443
+ function parseFactor() {
30444
+ skipSpaces();
30445
+ const c = source[pos];
30446
+ if (c === `+` || c === `-`) {
30447
+ pos++;
30448
+ const value = parseFactor();
30449
+ return value === void 0 ? void 0 : c === `-` ? -value : value;
30450
+ }
30451
+ if (c === `(`) {
30452
+ pos++;
30453
+ const value = parseSum();
30454
+ if (value === void 0) return void 0;
30455
+ skipSpaces();
30456
+ if (source[pos] !== `)`) return void 0;
30457
+ pos++;
30458
+ return value;
30459
+ }
30460
+ return parseNumber();
30461
+ }
30462
+ function parseProduct() {
30463
+ let value = parseFactor();
30464
+ if (value === void 0) return void 0;
30465
+ while (true) {
30466
+ skipSpaces();
30467
+ const op = source[pos];
30468
+ if (op !== `*` && op !== `/`) return value;
30469
+ pos++;
30470
+ const rhs = parseFactor();
30471
+ if (rhs === void 0) return void 0;
30472
+ value = op === `*` ? value * rhs : value / rhs;
30473
+ }
30474
+ }
30475
+ function parseSum() {
30476
+ let value = parseProduct();
30477
+ if (value === void 0) return void 0;
30478
+ while (true) {
30479
+ skipSpaces();
30480
+ const op = source[pos];
30481
+ if (op !== `+` && op !== `-`) return value;
30482
+ pos++;
30483
+ const rhs = parseProduct();
30484
+ if (rhs === void 0) return void 0;
30485
+ value = op === `+` ? value + rhs : value - rhs;
30486
+ }
30487
+ }
30488
+ const value = parseSum();
30489
+ if (value === void 0) return void 0;
30490
+ skipSpaces();
30491
+ return pos === source.length ? value : void 0;
30492
+ }
29351
30493
  let SliderInputElement = class SliderInputElement extends WheelNudgeMixin(i) {
29352
30494
  #lanes;
29353
30495
  /** Raw pole values: independent of which is "low" or "high". */
@@ -30261,14 +31403,10 @@ let SliderInputElement = class SliderInputElement extends WheelNudgeMixin(i) {
30261
31403
  const parsed = Number.parseFloat(val.replace(/[^0-9.-]/g, ``));
30262
31404
  return Number.isNaN(parsed) ? defaultValue : parsed;
30263
31405
  }
30264
- const evalStr = val.replace(/var\(--value-relative\)/g, valueRelative);
30265
- try {
30266
- const result = new Function(`return ${evalStr.replace(/calc\s*\(([^)]+)\)/g, `$1`)}`)();
30267
- return typeof result === `number` && !Number.isNaN(result) ? result : defaultValue;
30268
- } catch {
30269
- const parsed = Number.parseFloat(val.replace(/[^0-9.-]/g, ``));
30270
- return Number.isNaN(parsed) ? defaultValue : parsed;
30271
- }
31406
+ const result = evaluateArithmetic(val.replace(/var\(--value-relative\)/g, valueRelative).replace(/calc\s*\(([^)]+)\)/g, `$1`));
31407
+ if (result !== void 0) return Number.isNaN(result) ? defaultValue : result;
31408
+ const parsed = Number.parseFloat(val.replace(/[^0-9.-]/g, ``));
31409
+ return Number.isNaN(parsed) ? defaultValue : parsed;
30272
31410
  }
30273
31411
  const parsed = Number.parseFloat(val);
30274
31412
  return Number.isNaN(parsed) ? defaultValue : parsed;
@@ -30345,10 +31483,10 @@ let SliderInputElement = class SliderInputElement extends WheelNudgeMixin(i) {
30345
31483
  proposedHigh = clamp$1(proposedHigh, this.min, this.max);
30346
31484
  }
30347
31485
  const lowKey = this.#lengthDragLowKey ?? this.#lowKey();
30348
- this.#lengthDragHighKey ?? this.#highKey();
31486
+ const highKey = this.#lengthDragHighKey ?? this.#highKey();
30349
31487
  const previous = this.#poles();
30350
31488
  const newPoleA = lowKey === `a` ? proposedLow : proposedHigh;
30351
- const newPoleB = lowKey === `b` ? proposedLow : proposedHigh;
31489
+ const newPoleB = highKey === `b` ? proposedHigh : proposedLow;
30352
31490
  this.#poleA = newPoleA;
30353
31491
  this.#poleB = newPoleB;
30354
31492
  if (this.valueMode === `triple` && this.#poleC !== void 0) {
@@ -33967,7 +35105,7 @@ function getScaleSteps(palette) {
33967
35105
  function resolveCssVariable(el, prop) {
33968
35106
  const style = getComputedStyle(el);
33969
35107
  let value = style.getPropertyValue(prop).trim();
33970
- const varRegex = /var\((--[\w-]+)(?:,\s*(.*))?\)/g;
35108
+ const varRegex = /var\((--[\w-]+)(?:,(.*))?\)/g;
33971
35109
  for (let i = 0; i < 5 && value.includes(`var(`); i++) value = value.replace(varRegex, (_, varName, fallback) => {
33972
35110
  const resolved = style.getPropertyValue(varName).trim();
33973
35111
  if (resolved) return resolved;
@@ -43393,6 +44531,6 @@ function init() {
43393
44531
  console.log(`Init`);
43394
44532
  }
43395
44533
  //#endregion
43396
- export { AcTextElement, AcTokenElement, BottomDrawerElement, ButtonElement, Checkbox, ColourPicker, ColourPickerPopup, CrumbNavigationElement, CrumbPathController, CrumbPathElement, DataController, DataDisplayComponent, DataGridElement, DataTableElement, DetailListElement, DockHostElement, DockPanelElement, EditableLabel, EditableNumber, FormElement, FormGroupElement, FormPartElement, GridListElement, GroupedItemListerElement, ICON_CARET_RIGHT, ICON_CHECK, ICON_CHEVRON_DOWN, ICON_CLOSE, IncrSearchTreeController as IncrSearchMillerController, IncrSearchTreeController, IxfxHexEditorElement, IxfxIconElement, IxfxTimelineElement, KeyedElementIndex, LIST_ITEM_MOTION_MS, LIST_SEARCH_COMMAND_IDS, LIST_SEARCH_MODES, LabelledRadialInput, LabelledRangeInput, LabelledSliderInput, LedElement, ListDragSelectController, ListNativeDragController, ListSearchController, ListSelectionController, menu_exports as Menu, MillerBaseElement, MillerListElement, MillerTreeController, NarrowedTextElement, NotificationManager, NotificationPillElement, PanTextElement, PanelElement, PanelGroupElement, PlotDataSeries, PlotHistogram, PlotMultiAxis, PlotSingleAxis, PlotXyAxis, PolarPad, RadialInputElement, RadioGroup, RangeElement, RangeInput, RangeMultiElement, RatingElement, SelectHorizElement, SliderInputElement, SlidingNumberElement, SnackbarElement, SnapContainer, SplitButton, SplitLayoutElement, SplitPanelElement, SwipeElement, TabController, TabListElement, TabListItemElement, TabPanelElement, TabPanelsElement, TextShimmerElement, TickledController, TimelineController, TimelineTrackTabElement, TitlebarElement, ToolbarElement, ToolbarGroupElement, TooltipElement, TransitoryLabelElement, TreeBaseElement, TreeController, TreeDataModel, TreeKeyboardNavController, TreeListElement, TwoSplitLayoutElement, VerticalListElement, XyPad, addPanelToStack, animateListItemIn, animateListItemOut, applyAgeModification, applyHighlights, applySnapshot, attachTimelineInteractions, bottomDrawerCommandId, buildHighlightTargets, buildModel, captionForEntry, clearHighlights, collectContainerNames, collectDataResult, collectPanelIds, collectStackPaths, computeColour, computeLeafGeoms, createAcCompletionProvider, createDataListSearchAdapter, createDefaultListDragPreview, createElementIncrSearch, createElementListSearchAdapter, createEmptyLayout, createEmptyStack, createFuzzySearch, createIncrSearch, createPillRenderer, createSplit, createStack, createTreeListAdapter, createTreeListSearchAdapter, createVerticalListAdapter, distribute, extractStack, findNode, findParentOf, findStackOf, findStackPathByName, getActiveDragPanelId, getIcon, getIconEntry, getIconFont, getLeafNeighbours, hasIcon, iconBus, init, insertBeside, isDragging, isMultiMode, isSplit, isStack, keyForElement, listItemFadeKeyframes, listSearchStyles, monitorIndeterminate, moveLeafNodeToEdge, moveLeafStep, normalise, normalize, panelGapIndex, parseColourScaleAttr, parseLayout, pointerOverAnyHost, prefersReducedMotion, readBaseColour, registerFontIcon, registerIcon, registerListSearchCommands, removePanelFromStack, resolveCssVars, resolveGrid, resolveLeafPath, resolveTracks, serializeLayout, setActive, setIconFont, startListItemOutAnimation, svgToDataUri, swapLeaves, tickledItemStyles, tickledStyles, timelineStyles, toSnapshot, walk, wrapRoot };
44534
+ export { AcTextElement, AcTokenElement, BottomDrawerElement, ButtonElement, Checkbox, ColourPicker, ColourPickerPopup, CrumbNavigationElement, CrumbPathController, CrumbPathElement, DataController, DataDisplayComponent, DataGridElement, DataTableElement, DetailListElement, DockHostElement, DockPanelElement, EditableLabel, EditableNumber, FormElement, FormGroupElement, FormPartElement, GridListElement, GroupedItemListerElement, ICON_CARET_RIGHT, ICON_CHECK, ICON_CHEVRON_DOWN, ICON_CLOSE, IncrSearchTreeController as IncrSearchMillerController, IncrSearchTreeController, IxfxHexEditorElement, IxfxIconElement, IxfxTimelineElement, KeyedElementIndex, LIST_ITEM_MOTION_MS, LIST_SEARCH_COMMAND_IDS, LIST_SEARCH_MODES, LabelledRadialInput, LabelledRangeInput, LabelledSliderInput, LedElement, ListDragSelectController, ListNativeDragController, ListSearchController, ListSelectionController, menu_exports as Menu, MillerBaseElement, MillerListElement, MillerTreeController, NarrowedTextElement, NotificationManager, NotificationPillElement, PanTextElement, PanelElement, PanelGroupElement, PlotDataSeries, PlotHistogram, PlotMultiAxis, PlotSingleAxis, PlotXyAxis, PolarPad, RadialInputElement, RadioGroup, RangeElement, RangeInput, RangeMultiElement, RatingElement, SelectHorizElement, SliderInputElement, SlidingNumberElement, SnackbarElement, SnapContainer, SplitButton, SplitLayoutElement, SplitPanelElement, SwipeElement, TabController, TabListElement, TabListItemElement, TabPanelElement, TabPanelsElement, TextShimmerElement, TickledController, TimelineController, TimelineTrackTabElement, TitlebarElement, ToolbarElement, ToolbarGroupElement, TooltipElement, TransitoryLabelElement, TreeBaseElement, TreeController, TreeDataModel, TreeDropController, TreeKeyboardNavController, TreeListElement, TwoSplitLayoutElement, VerticalListElement, XyPad, addPanelToStack, animateListItemIn, animateListItemOut, applyAgeModification, applyHighlights, applySnapshot, applyTreeDrop, attachTimelineInteractions, bottomDrawerCommandId, buildHighlightTargets, buildModel, captionForEntry, clearHighlights, collectContainerNames, collectDataResult, collectPanelIds, collectStackPaths, computeColour, computeLeafGeoms, createAcCompletionProvider, createDataListSearchAdapter, createDefaultListDragPreview, createElementIncrSearch, createElementListSearchAdapter, createEmptyLayout, createEmptyStack, createFuzzySearch, createIncrSearch, createPillRenderer, createSplit, createStack, createTreeListAdapter, createTreeListSearchAdapter, createVerticalListAdapter, distribute, dropTargetFor, extractStack, findNode, findParentOf, findStackOf, findStackPathByName, getActiveDragPanelId, getIcon, getIconEntry, getIconFont, getLeafNeighbours, hasIcon, iconBus, init, insertBeside, isDragging, isMultiMode, isSplit, isStack, keyForElement, listItemFadeKeyframes, listSearchStyles, monitorIndeterminate, moveLeafNodeToEdge, moveLeafStep, normalise, normalize, panelGapIndex, parseColourScaleAttr, parseLayout, pointerOverAnyHost, prefersReducedMotion, pruneDraggedDescendants, readBaseColour, registerFontIcon, registerIcon, registerListSearchCommands, removePanelFromStack, resolveCssVars, resolveGrid, resolveLeafPath, resolveTracks, serializeLayout, setActive, setIconFont, startListItemOutAnimation, svgToDataUri, swapLeaves, tickledItemStyles, tickledStyles, timelineStyles, toSnapshot, walk, wrapRoot };
43397
44535
 
43398
44536
  //# sourceMappingURL=index.js.map