@ixfx/components 0.13.0 → 0.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/bundle/index.d.ts +214 -73
  2. package/bundle/index.d.ts.map +1 -1
  3. package/bundle/index.js +347 -13
  4. package/bundle/index.js.map +1 -1
  5. package/dist/{button-BYuf72aR.d.ts → button-CTL45aBl.d.ts} +2 -2
  6. package/dist/{button-BYuf72aR.d.ts.map → button-CTL45aBl.d.ts.map} +1 -1
  7. package/dist/button.d.ts +1 -1
  8. package/dist/crumbs.d.ts +2 -2
  9. package/dist/{data-grid-BJ6XXvRc.d.ts → data-grid-Ck1Qx2H1.d.ts} +4 -4
  10. package/dist/{data-grid-BJ6XXvRc.d.ts.map → data-grid-Ck1Qx2H1.d.ts.map} +1 -1
  11. package/dist/data-grid.d.ts +2 -2
  12. package/dist/{detail-list-B0waVNeS.d.ts → detail-list-C1xzr1z7.d.ts} +4 -4
  13. package/dist/{detail-list-B0waVNeS.d.ts.map → detail-list-C1xzr1z7.d.ts.map} +1 -1
  14. package/dist/detail-list.d.ts +3 -3
  15. package/dist/editable-label-rzXkjf1H.d.ts.map +1 -1
  16. package/dist/editable-label.js +2 -2
  17. package/dist/editable-label.js.map +1 -1
  18. package/dist/{flat-list-eYItri8-.d.ts → flat-list-DEDG6epz.d.ts} +60 -5
  19. package/dist/flat-list-DEDG6epz.d.ts.map +1 -0
  20. package/dist/flat-list.d.ts +2 -2
  21. package/dist/flat-list.js +144 -9
  22. package/dist/flat-list.js.map +1 -1
  23. package/dist/{grid-list-_gI9ckcT.d.ts → grid-list-pefLORIr.d.ts} +4 -4
  24. package/dist/{grid-list-_gI9ckcT.d.ts.map → grid-list-pefLORIr.d.ts.map} +1 -1
  25. package/dist/grid-list.d.ts +2 -2
  26. package/dist/incr-search.d.ts +1 -1
  27. package/dist/{index-BvxHQUME.d.ts → index-CxZ61DO1.d.ts} +3 -3
  28. package/dist/{index-BvxHQUME.d.ts.map → index-CxZ61DO1.d.ts.map} +1 -1
  29. package/dist/{index-CW2QL33o.d.ts → index-Dq30pOnn.d.ts} +2 -2
  30. package/dist/{index-CW2QL33o.d.ts.map → index-Dq30pOnn.d.ts.map} +1 -1
  31. package/dist/index.d.ts +13 -13
  32. package/dist/{list-native-drag-controller-CllsHLxw.d.ts → list-native-drag-controller-ChIi3eK5.d.ts} +2 -2
  33. package/dist/{list-native-drag-controller-CllsHLxw.d.ts.map → list-native-drag-controller-ChIi3eK5.d.ts.map} +1 -1
  34. package/dist/menu.d.ts +1 -1
  35. package/dist/miller.d.ts +3 -3
  36. package/dist/{tree-component-DukLqesg.d.ts → tree-component-Cj8V3uar.d.ts} +2 -2
  37. package/dist/{tree-component-DukLqesg.d.ts.map → tree-component-Cj8V3uar.d.ts.map} +1 -1
  38. package/dist/tree.d.ts +4 -4
  39. package/dist/{types-D-kYF16b.d.ts → types-DQ7KOUIj.d.ts} +6 -1
  40. package/dist/types-DQ7KOUIj.d.ts.map +1 -0
  41. package/dist/{vertical-list-DheZSEmB.d.ts → vertical-list-CqBc-t8w.d.ts} +86 -4
  42. package/dist/vertical-list-CqBc-t8w.d.ts.map +1 -0
  43. package/dist/vertical-list.d.ts +3 -3
  44. package/dist/vertical-list.js +202 -2
  45. package/dist/vertical-list.js.map +1 -1
  46. package/docs-user/flat-list.md +32 -2
  47. package/docs-user/index.json +2 -2
  48. package/docs-user/vertical-list.md +59 -3
  49. package/package.json +1 -1
  50. package/dist/flat-list-eYItri8-.d.ts.map +0 -1
  51. package/dist/types-D-kYF16b.d.ts.map +0 -1
  52. package/dist/vertical-list-DheZSEmB.d.ts.map +0 -1
package/bundle/index.js CHANGED
@@ -23809,7 +23809,7 @@ let EditableLabel = class EditableLabel extends LitElement {
23809
23809
  @keydown=${this._handleKeydown}
23810
23810
  @beforeinput=${this._handleBeforeInput}
23811
23811
  @input=${this._handleInput}
23812
- >${this._editing ? nothing : this._getDisplayText()}</div>`;
23812
+ ></div>`;
23813
23813
  }
23814
23814
  updated(changed) {
23815
23815
  super.updated(changed);
@@ -23821,7 +23821,7 @@ let EditableLabel = class EditableLabel extends LitElement {
23821
23821
  _selectAll(el);
23822
23822
  }
23823
23823
  }
23824
- if (changed.has(`displayTextLogic`) || changed.has(`displayText`) || changed.has(`formatter`) || changed.has(`value`)) this._requestDisplayUpdate();
23824
+ if (changed.has(`_editing`) && !this._editing || changed.has(`displayTextLogic`) || changed.has(`displayText`) || changed.has(`formatter`) || changed.has(`value`)) this._requestDisplayUpdate();
23825
23825
  }
23826
23826
  static {
23827
23827
  this.styles = [themeFallbacks, css`
@@ -24695,6 +24695,7 @@ let VerticalListElement = class VerticalListElement extends LitElement {
24695
24695
  this.keyProperty = ``;
24696
24696
  this.virtualize = false;
24697
24697
  this.itemHeight = 0;
24698
+ this.itemsDroppable = false;
24698
24699
  this.#customWrappers = /* @__PURE__ */ new Map();
24699
24700
  this.#wrappedElements = /* @__PURE__ */ new Set();
24700
24701
  this.#itemData = /* @__PURE__ */ new WeakMap();
@@ -24814,7 +24815,16 @@ let VerticalListElement = class VerticalListElement extends LitElement {
24814
24815
  this.tickled.handlePointerLeave(e);
24815
24816
  };
24816
24817
  this._onKeyDown = (e) => {
24817
- if (this.search.isOpen) return;
24818
+ if (this.search.isOpen || this.#isEditEvent(e)) return;
24819
+ if (e.key === `ContextMenu` || e.shiftKey && e.key === `F10`) {
24820
+ const id = this.tickled.tickledId;
24821
+ if (id) {
24822
+ e.preventDefault();
24823
+ const rect = id.getBoundingClientRect();
24824
+ this.#requestContextMenu(id, rect.left, rect.bottom);
24825
+ }
24826
+ return;
24827
+ }
24818
24828
  if ((e.ctrlKey || e.metaKey) && e.key === `a`) {
24819
24829
  if (this.selection.canSelectAll()) {
24820
24830
  e.preventDefault();
@@ -24868,6 +24878,7 @@ let VerticalListElement = class VerticalListElement extends LitElement {
24868
24878
  }
24869
24879
  };
24870
24880
  this._onClick = (e) => {
24881
+ if (this.#isEditEvent(e)) return;
24871
24882
  if (this.#dragSelect.isDragging || this.#nativeDrag.isDragging || this.#postDragSuppressTimer !== 0) return;
24872
24883
  const item = this._findListItem(e.composedPath());
24873
24884
  if (!item) return;
@@ -24875,9 +24886,60 @@ let VerticalListElement = class VerticalListElement extends LitElement {
24875
24886
  this.selection.handleClick(item, e);
24876
24887
  };
24877
24888
  this._onDblClick = (e) => {
24889
+ if (this.#isEditEvent(e)) return;
24878
24890
  const item = this._findListItem(e.composedPath());
24879
24891
  if (item) this._activateItem(item);
24880
24892
  };
24893
+ this._onDragOver = (e) => {
24894
+ const dt = e.dataTransfer;
24895
+ if (!this.itemsDroppable || !dt || this.#isEditEvent(e)) return;
24896
+ const item = this._findListItem(e.composedPath());
24897
+ if (this.canDrop && !this.canDrop(item, dt)) {
24898
+ this.#clearDropTarget();
24899
+ return;
24900
+ }
24901
+ if (this.#dragOver && this.#dragOver.item !== item) this.#clearDropTarget();
24902
+ this.#dragOver = { item };
24903
+ if (!!this._dispatchCancelable(`list-drag-over`, {
24904
+ key: item ? keyForElement(item) : void 0,
24905
+ item,
24906
+ dataTransfer: dt
24907
+ })) {
24908
+ this.#highlightDropTarget(void 0, false);
24909
+ return;
24910
+ }
24911
+ e.preventDefault();
24912
+ if (dt.dropEffect === `none`) dt.dropEffect = `copy`;
24913
+ this.#highlightDropTarget(item, true);
24914
+ };
24915
+ this._onDragLeave = (e) => {
24916
+ const next = e.relatedTarget;
24917
+ if (next instanceof Node && this.contains(next)) return;
24918
+ this.#clearDropTarget();
24919
+ };
24920
+ this._onDrop = (e) => {
24921
+ const dt = e.dataTransfer;
24922
+ if (!this.itemsDroppable || !dt || this.#isEditEvent(e)) return;
24923
+ const item = this._findListItem(e.composedPath());
24924
+ if (this.canDrop && !this.canDrop(item, dt)) {
24925
+ this.#clearDropTarget();
24926
+ return;
24927
+ }
24928
+ e.preventDefault();
24929
+ this.#dragOver = void 0;
24930
+ this.#highlightDropTarget(void 0, false);
24931
+ this._dispatch(`list-drop`, {
24932
+ key: item ? keyForElement(item) : void 0,
24933
+ item,
24934
+ dataTransfer: dt
24935
+ });
24936
+ };
24937
+ this._onContextMenu = (e) => {
24938
+ if (this.#isEditEvent(e)) return;
24939
+ const item = this._findListItem(e.composedPath());
24940
+ if (!item) return;
24941
+ if (!this.#requestContextMenu(item, e.clientX, e.clientY)) e.preventDefault();
24942
+ };
24881
24943
  }
24882
24944
  #customWrappers;
24883
24945
  #wrappedElements;
@@ -25004,6 +25066,10 @@ let VerticalListElement = class VerticalListElement extends LitElement {
25004
25066
  this.addEventListener(`keydown`, this._onKeyDown);
25005
25067
  this.addEventListener(`click`, this._onClick);
25006
25068
  this.addEventListener(`dblclick`, this._onDblClick);
25069
+ this.addEventListener(`dragover`, this._onDragOver);
25070
+ this.addEventListener(`dragleave`, this._onDragLeave);
25071
+ this.addEventListener(`drop`, this._onDrop);
25072
+ this.addEventListener(`contextmenu`, this._onContextMenu);
25007
25073
  for (const el of this.querySelectorAll(ITEM_SELECTOR$2)) this.#animatedItems.add(el);
25008
25074
  this.#itemObserver ??= new MutationObserver((records) => this.#onItemsMutated(records));
25009
25075
  this.#itemObserver.observe(this, { childList: true });
@@ -25018,6 +25084,12 @@ let VerticalListElement = class VerticalListElement extends LitElement {
25018
25084
  this.removeEventListener(`keydown`, this._onKeyDown);
25019
25085
  this.removeEventListener(`click`, this._onClick);
25020
25086
  this.removeEventListener(`dblclick`, this._onDblClick);
25087
+ this.removeEventListener(`dragover`, this._onDragOver);
25088
+ this.removeEventListener(`dragleave`, this._onDragLeave);
25089
+ this.removeEventListener(`drop`, this._onDrop);
25090
+ this.removeEventListener(`contextmenu`, this._onContextMenu);
25091
+ this.#clearDropTarget();
25092
+ this.#edit = void 0;
25021
25093
  this.#itemObserver?.disconnect();
25022
25094
  this.#resizeObserver?.disconnect();
25023
25095
  this.#resizeObserver = void 0;
@@ -25704,7 +25776,7 @@ let VerticalListElement = class VerticalListElement extends LitElement {
25704
25776
  }
25705
25777
  /** Per-attach state the attached sweep can't cover (it only sees the window). */
25706
25778
  #applyRowState(el) {
25707
- if (el instanceof HTMLElement) el.draggable = this.itemsDraggable && this._isCheckable(el);
25779
+ if (el instanceof HTMLElement) el.draggable = this.itemsDraggable && this._isCheckable(el) && !el.hasAttribute(`data-editing`);
25708
25780
  el.removeAttribute(`data-marquee`);
25709
25781
  el.removeAttribute(`data-drag-origin`);
25710
25782
  }
@@ -25914,6 +25986,114 @@ let VerticalListElement = class VerticalListElement extends LitElement {
25914
25986
  if (this.interactionMode !== `manual`) this.selection.replace(item);
25915
25987
  this._dispatch(`list-activate`, { item });
25916
25988
  }
25989
+ /** The row (or `undefined` for empty space) the last `list-drag-over` was fired for. */
25990
+ #dragOver;
25991
+ /** Tell the consumer the drag left its last target and drop the highlight. */
25992
+ #clearDropTarget() {
25993
+ const last = this.#dragOver;
25994
+ this.#dragOver = void 0;
25995
+ this.#highlightDropTarget(void 0, false);
25996
+ if (last) this._dispatch(`list-drag-leave`, { key: last.item ? keyForElement(last.item) : void 0 });
25997
+ }
25998
+ /** Mark one row (or, for empty space, the host) as the drop target. */
25999
+ #highlightDropTarget(item, active) {
26000
+ for (const el of this.querySelectorAll(`[data-drop-target]`)) el.removeAttribute(`data-drop-target`);
26001
+ this.toggleAttribute(`data-drop-target`, active && item === void 0);
26002
+ if (active && item) item.setAttribute(`data-drop-target`, ``);
26003
+ }
26004
+ /**
26005
+ * Select the row if it is not already, then fire `list-context-menu`.
26006
+ * Returns false when a listener cancelled it (the native menu is then suppressed).
26007
+ */
26008
+ #requestContextMenu(item, x, y) {
26009
+ if (!this.selection.selected.has(item) && this.interactionMode !== `manual` && this._isCheckable(item)) this.selection.replace(item);
26010
+ return this._dispatchCancelable(`list-context-menu`, {
26011
+ key: keyForElement(item),
26012
+ item,
26013
+ x,
26014
+ y,
26015
+ selectedKeys: this.selection.selectedKeys
26016
+ });
26017
+ }
26018
+ #edit;
26019
+ /** The key of the row being edited, if any. */
26020
+ get editingKey() {
26021
+ return this.#edit?.key;
26022
+ }
26023
+ #isEditEvent(e) {
26024
+ return this.#edit !== void 0 && e.composedPath().includes(this.#edit.label);
26025
+ }
26026
+ /**
26027
+ * Edit a row's label in place with an `ixfx-editable-label`. The editor
26028
+ * replaces the row's `[data-edit-label]` element (else the whole row) and
26029
+ * the original content returns when editing ends. Fires `list-edit-commit`
26030
+ * `{ key, value }` when the text changed, otherwise `list-edit-cancel`
26031
+ * `{ key }` (Escape, an unchanged value, or one the label rejects). The list
26032
+ * never rewrites the label itself — update your data in response.
26033
+ *
26034
+ * Returns false when `editValidator` is not set, or the row is unknown or
26035
+ * not currently rendered.
26036
+ */
26037
+ beginEdit(key) {
26038
+ this.#finishEdit();
26039
+ const validate = this.editValidator;
26040
+ if (!validate) return false;
26041
+ const row = this.#itemForKey(key);
26042
+ if (!(row instanceof HTMLElement) || row.parentNode !== this) return false;
26043
+ const container = row.querySelector(`[data-edit-label]`) ?? row;
26044
+ const value = container.getAttribute(`data-edit-value`) ?? container.textContent?.trim() ?? ``;
26045
+ const label = document.createElement(`ixfx-editable-label`);
26046
+ label.mode = `label`;
26047
+ label.value = value;
26048
+ label.validator = (text) => validate(text, key);
26049
+ label.style.cssText = `flex:1;min-width:0;--label-align:left;--label-padding:0 var(--space-xs,3px)`;
26050
+ const edit = {
26051
+ key,
26052
+ row,
26053
+ container,
26054
+ saved: [...container.childNodes],
26055
+ label,
26056
+ committed: void 0,
26057
+ refocus: false
26058
+ };
26059
+ this.#edit = edit;
26060
+ label.addEventListener(`change`, (e) => {
26061
+ e.stopPropagation();
26062
+ edit.committed = e.detail.value;
26063
+ });
26064
+ label.addEventListener(`input`, (e) => e.stopPropagation());
26065
+ label.addEventListener(`keydown`, (e) => {
26066
+ edit.refocus = e.key === `Enter` || e.key === `Escape`;
26067
+ });
26068
+ for (const type of [`pointerdown`, `dragstart`]) label.addEventListener(type, (e) => e.stopPropagation());
26069
+ label.addEventListener(`focusout`, () => this.#finishEdit());
26070
+ container.replaceChildren(label);
26071
+ row.setAttribute(`data-editing`, ``);
26072
+ row.draggable = false;
26073
+ row.scrollIntoView({
26074
+ block: `nearest`,
26075
+ inline: `nearest`
26076
+ });
26077
+ label.updateComplete.then(() => {
26078
+ if (this.#edit === edit) label.shadowRoot?.querySelector(`.content`)?.click();
26079
+ });
26080
+ return true;
26081
+ }
26082
+ /** Restore the row's content and report how the edit ended. */
26083
+ #finishEdit() {
26084
+ const edit = this.#edit;
26085
+ if (!edit) return;
26086
+ this.#edit = void 0;
26087
+ if (edit.container.isConnected) edit.container.replaceChildren(...edit.saved);
26088
+ edit.row.removeAttribute(`data-editing`);
26089
+ this.#applyRowState(edit.row);
26090
+ if (edit.refocus) this.focus();
26091
+ if (edit.committed === void 0) this._dispatch(`list-edit-cancel`, { key: edit.key });
26092
+ else this._dispatch(`list-edit-commit`, {
26093
+ key: edit.key,
26094
+ value: edit.committed
26095
+ });
26096
+ }
25917
26097
  static {
25918
26098
  this.styles = [
25919
26099
  themeFallbacks,
@@ -26074,6 +26254,18 @@ let VerticalListElement = class VerticalListElement extends LitElement {
26074
26254
  opacity: 0.6;
26075
26255
  }
26076
26256
 
26257
+ /* Native drop target: the row under a drag, or the whole list over empty space. */
26258
+ ::slotted([data-drop-target]) {
26259
+ background: var(--list-drop-target-bg, color-mix(in srgb, var(--accent) 25%, transparent));
26260
+ outline: 2px solid var(--list-drop-target-outline, var(--accent));
26261
+ outline-offset: -2px;
26262
+ }
26263
+
26264
+ :host([data-drop-target]) {
26265
+ outline: 2px solid var(--list-drop-target-outline, var(--accent));
26266
+ outline-offset: -2px;
26267
+ }
26268
+
26077
26269
  /* checked mode: checkbox (injected into li) communicates selection — suppress bg highlight */
26078
26270
  :host([interaction-mode="checked"]) ::slotted([data-selected]) {
26079
26271
  background: transparent;
@@ -26146,12 +26338,23 @@ __decorate([property({
26146
26338
  type: Number,
26147
26339
  attribute: `item-height`
26148
26340
  })], VerticalListElement.prototype, "itemHeight", void 0);
26341
+ __decorate([property({
26342
+ type: Boolean,
26343
+ attribute: `items-droppable`,
26344
+ reflect: true
26345
+ })], VerticalListElement.prototype, "itemsDroppable", void 0);
26346
+ __decorate([property({ attribute: false })], VerticalListElement.prototype, "editValidator", void 0);
26347
+ __decorate([property({ attribute: false })], VerticalListElement.prototype, "canDrop", void 0);
26149
26348
  VerticalListElement = __decorate([safeCustomElement(`ixfx-vertical-list`)], VerticalListElement);
26150
26349
  //#endregion
26151
26350
  //#region src/flat-list/flat-list.ts
26152
- /** A node with children, or whose children are still to be loaded. */
26351
+ /**
26352
+ * A node with children, or whose children are still to be loaded.
26353
+ * `isLeaf: false` forces a branch even when `children` is `[]` (an empty
26354
+ * folder that can still be entered); `isLeaf: true` forces a leaf.
26355
+ */
26153
26356
  function isBranch(node) {
26154
- return node.item.isLeaf !== true && node.children?.length !== 0;
26357
+ return node.item.isLeaf === false || node.item.isLeaf !== true && node.children?.length !== 0;
26155
26358
  }
26156
26359
  function sameKeys$1(a, b) {
26157
26360
  return a.length === b.length && a.every((k, i) => k === b[i]);
@@ -26201,6 +26404,9 @@ let FlatListElement = class FlatListElement extends LitElement {
26201
26404
  this.interactionMode = `standard`;
26202
26405
  this.itemWrap = `stop`;
26203
26406
  this.itemsDraggable = false;
26407
+ this.itemsDroppable = false;
26408
+ this.virtualize = false;
26409
+ this.itemHeight = 0;
26204
26410
  this.withUp = true;
26205
26411
  this.hideRoot = false;
26206
26412
  this.emptyText = `Empty`;
@@ -26210,6 +26416,64 @@ let FlatListElement = class FlatListElement extends LitElement {
26210
26416
  this.#lastPath = [];
26211
26417
  this.#selected = /* @__PURE__ */ new Set();
26212
26418
  this.#direction = `jump`;
26419
+ this.#canDropOn = (el, dataTransfer) => {
26420
+ const node = this.#nodeForElement(el);
26421
+ return !!node && (this.canDrop?.(node, dataTransfer) ?? true);
26422
+ };
26423
+ this.#onListDragOver = (e) => {
26424
+ if (this.itemsDroppable) e.preventDefault();
26425
+ };
26426
+ this.#onListDrop = (e) => {
26427
+ const node = this.#nodeForKey(e.detail.key) ?? this.currentNode;
26428
+ if (!node) return;
26429
+ this.dispatchEvent(new CustomEvent(`flat-list-drop`, {
26430
+ detail: {
26431
+ node,
26432
+ dataTransfer: e.detail.dataTransfer
26433
+ },
26434
+ bubbles: true,
26435
+ composed: true
26436
+ }));
26437
+ };
26438
+ this.#onListContextMenu = (e) => {
26439
+ const node = this.#nodeForKey(e.detail.key);
26440
+ if (!node) return;
26441
+ const nodes = [...e.detail.selectedKeys].map((k) => this.#nodeForKey(k)).filter((n) => n !== void 0);
26442
+ if (!this.dispatchEvent(new CustomEvent(`flat-list-context-menu`, {
26443
+ detail: {
26444
+ node,
26445
+ nodes,
26446
+ x: e.detail.x,
26447
+ y: e.detail.y
26448
+ },
26449
+ bubbles: true,
26450
+ composed: true,
26451
+ cancelable: true
26452
+ }))) e.preventDefault();
26453
+ };
26454
+ this.#validateEditKey = (value, key) => {
26455
+ const node = this.#nodeForKey(key);
26456
+ return !!node && (this.validateEdit?.(value, node) ?? false);
26457
+ };
26458
+ this.#onListEditCommit = (e) => {
26459
+ const node = this.#nodeForKey(e.detail.key);
26460
+ if (node) this.dispatchEvent(new CustomEvent(`flat-list-edit-commit`, {
26461
+ detail: {
26462
+ node,
26463
+ value: e.detail.value
26464
+ },
26465
+ bubbles: true,
26466
+ composed: true
26467
+ }));
26468
+ };
26469
+ this.#onListEditCancel = (e) => {
26470
+ const node = this.#nodeForKey(e.detail.key);
26471
+ if (node) this.dispatchEvent(new CustomEvent(`flat-list-edit-cancel`, {
26472
+ detail: { node },
26473
+ bubbles: true,
26474
+ composed: true
26475
+ }));
26476
+ };
26213
26477
  this.#onListSelect = (e) => {
26214
26478
  const previous = this.#selected;
26215
26479
  const nodes = [...e.detail.selectedKeys].map((k) => this.#nodeForKey(k)).filter((n) => n !== void 0);
@@ -26274,11 +26538,8 @@ let FlatListElement = class FlatListElement extends LitElement {
26274
26538
  if (sameLevel) this.#settleList();
26275
26539
  }
26276
26540
  };
26277
- this.#renderRow = (row) => {
26278
- const { node } = row;
26279
- const textColor = appearanceColor(node.item.textColor);
26280
- return html`<span class="label" style=${textColor ? `color:${textColor}` : nothing}>${node.item.label}</span>`;
26281
- };
26541
+ this.#renderPlainRow = (row) => this.#renderRow(row, false);
26542
+ this.#renderEditableRow = (row) => this.#renderRow(row, true);
26282
26543
  this.#renderIcon = (row) => {
26283
26544
  const { node } = row;
26284
26545
  return node.item.icon ? html`<ixfx-icon name=${node.item.icon}></ixfx-icon>` : void 0;
@@ -26318,6 +26579,17 @@ let FlatListElement = class FlatListElement extends LitElement {
26318
26579
  dispatch(commandId, args) {
26319
26580
  this.list?.dispatch(commandId, args);
26320
26581
  }
26582
+ /**
26583
+ * Rename a row in place. Needs `validateEdit`, and only rows whose
26584
+ * `item.editable` is true can be edited. Ends with `flat-list-edit-commit` or `flat-list-edit-cancel`; the
26585
+ * label is not rewritten — update the tree in response. Returns false if
26586
+ * the row is unknown, not editable, or not on screen.
26587
+ */
26588
+ beginEdit(key) {
26589
+ const node = this.#nodeForKey(key);
26590
+ if (!this.validateEdit || !node?.item.editable) return false;
26591
+ return this.list?.beginEdit(key) ?? false;
26592
+ }
26321
26593
  /** The node whose children are listed. */
26322
26594
  get currentNode() {
26323
26595
  return resolvePath(this.#root, this.path).at(-1);
@@ -26407,6 +26679,7 @@ let FlatListElement = class FlatListElement extends LitElement {
26407
26679
  }
26408
26680
  }
26409
26681
  if (changed.has(`_rows`)) this.#settleList();
26682
+ if (changed.has(`_rows`) || changed.has(`markedKey`)) this.#applyMarked();
26410
26683
  }
26411
26684
  /** Recompute the level's rows, loading children first when they are not yet known. */
26412
26685
  #refreshRows(reload) {
@@ -26458,12 +26731,30 @@ let FlatListElement = class FlatListElement extends LitElement {
26458
26731
  e.stopPropagation();
26459
26732
  this.navigateInto(node.item.key);
26460
26733
  }
26734
+ #nodeForElement(el) {
26735
+ return el ? this.#nodeForKey(el.getAttribute(`data-key`) ?? void 0) : this.currentNode;
26736
+ }
26737
+ #canDropOn;
26738
+ /** Accept every drag that got past `canDrop`; the consumer only hears about the drop. */
26739
+ #onListDragOver;
26740
+ #onListDrop;
26741
+ #onListContextMenu;
26742
+ #validateEditKey;
26743
+ #onListEditCommit;
26744
+ #onListEditCancel;
26461
26745
  #onListSelect;
26462
26746
  #onListActivate;
26463
26747
  #syncFocusVisible;
26464
26748
  #onKeyDown;
26465
26749
  /** A crumb was chosen (or the Up button used): go to that node, or to its parent and select it if it is a leaf. */
26466
26750
  #onCrumbSelect;
26751
+ /** Flag the marked row. Rows are rebuilt with the level, so this runs after each rebuild. */
26752
+ async #applyMarked() {
26753
+ const list = this.list;
26754
+ if (!list) return;
26755
+ await list.updateComplete;
26756
+ for (const row of list.querySelectorAll(`li[data-key]`)) row.toggleAttribute(`data-marked`, row.getAttribute(`data-key`) === this.markedKey);
26757
+ }
26467
26758
  /** After the inner list has rebuilt, place the cursor / selection / focus. */
26468
26759
  async #settleList() {
26469
26760
  const list = this.list;
@@ -26478,7 +26769,15 @@ let FlatListElement = class FlatListElement extends LitElement {
26478
26769
  if (focusKey !== void 0) list.focusKey(focusKey);
26479
26770
  if (hadFocus) list.focus();
26480
26771
  }
26481
- #renderRow;
26772
+ /** Two stable formatters, so toggling `validateEdit` rebuilds the rows with or without edit hooks. */
26773
+ #renderPlainRow;
26774
+ #renderEditableRow;
26775
+ #renderRow(row, editingEnabled) {
26776
+ const { node } = row;
26777
+ const textColor = appearanceColor(node.item.textColor);
26778
+ const editable = editingEnabled && node.item.editable === true;
26779
+ return html`<span class="label" data-edit-label=${editable ? `` : nothing} data-edit-value=${editable ? node.item.label : nothing} style=${textColor ? `color:${textColor}` : nothing}>${node.item.label}</span>`;
26780
+ }
26482
26781
  #renderIcon;
26483
26782
  #renderBadge;
26484
26783
  render() {
@@ -26500,7 +26799,7 @@ let FlatListElement = class FlatListElement extends LitElement {
26500
26799
  <div class="body">
26501
26800
  <ixfx-vertical-list
26502
26801
  .items=${this._rows}
26503
- .formatter=${this.#renderRow}
26802
+ .formatter=${this.validateEdit ? this.#renderEditableRow : this.#renderPlainRow}
26504
26803
  .icon=${this.#renderIcon}
26505
26804
  .iconColor=${(row) => appearanceColor(row.node.item.iconColor)}
26506
26805
  .badge=${this.#renderBadge}
@@ -26510,6 +26809,16 @@ let FlatListElement = class FlatListElement extends LitElement {
26510
26809
  .itemWrap=${this.itemWrap}
26511
26810
  .searchModes=${this.searchModes}
26512
26811
  ?items-draggable=${this.itemsDraggable}
26812
+ ?items-droppable=${this.itemsDroppable}
26813
+ .canDrop=${this.#canDropOn}
26814
+ .editValidator=${this.#validateEditKey}
26815
+ .virtualize=${this.virtualize}
26816
+ .itemHeight=${this.itemHeight}
26817
+ @list-drag-over=${this.#onListDragOver}
26818
+ @list-drop=${this.#onListDrop}
26819
+ @list-context-menu=${this.#onListContextMenu}
26820
+ @list-edit-commit=${this.#onListEditCommit}
26821
+ @list-edit-cancel=${this.#onListEditCancel}
26513
26822
  @list-select=${this.#onListSelect}
26514
26823
  @list-activate=${this.#onListActivate}
26515
26824
  @keydown=${this.#onKeyDown}
@@ -26574,6 +26883,11 @@ let FlatListElement = class FlatListElement extends LitElement {
26574
26883
  color: var(--flat-list-empty-text, var(--surface-muted-text));
26575
26884
  }
26576
26885
 
26886
+ li[data-marked] {
26887
+ background: var(--flat-list-marked-bg, color-mix(in srgb, var(--accent) 18%, transparent));
26888
+ box-shadow: inset 3px 0 0 var(--flat-list-marked-bar, var(--accent));
26889
+ }
26890
+
26577
26891
  .label {
26578
26892
  flex: 1;
26579
26893
  overflow: hidden;
@@ -26631,6 +26945,26 @@ __decorate([property({
26631
26945
  attribute: `items-draggable`,
26632
26946
  reflect: true
26633
26947
  })], FlatListElement.prototype, "itemsDraggable", void 0);
26948
+ __decorate([property({
26949
+ type: Boolean,
26950
+ attribute: `items-droppable`,
26951
+ reflect: true
26952
+ })], FlatListElement.prototype, "itemsDroppable", void 0);
26953
+ __decorate([property({ attribute: false })], FlatListElement.prototype, "canDrop", void 0);
26954
+ __decorate([property({ attribute: false })], FlatListElement.prototype, "validateEdit", void 0);
26955
+ __decorate([property({
26956
+ type: String,
26957
+ attribute: `marked-key`,
26958
+ reflect: true
26959
+ })], FlatListElement.prototype, "markedKey", void 0);
26960
+ __decorate([property({
26961
+ type: Boolean,
26962
+ reflect: true
26963
+ })], FlatListElement.prototype, "virtualize", void 0);
26964
+ __decorate([property({
26965
+ type: Number,
26966
+ attribute: `item-height`
26967
+ })], FlatListElement.prototype, "itemHeight", void 0);
26634
26968
  __decorate([property({
26635
26969
  type: Boolean,
26636
26970
  attribute: `with-up`,