@ixfx/components 0.8.6 → 0.8.8

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 (76) hide show
  1. package/bundle/index.d.ts +570 -389
  2. package/bundle/index.d.ts.map +1 -1
  3. package/bundle/index.js +1033 -200
  4. package/bundle/index.js.map +1 -1
  5. package/dist/crumbs.d.ts +1 -1
  6. package/dist/crumbs.js +2 -2
  7. package/dist/{data-grid-D-z7bx5-.d.ts → data-grid-BKa9i86q.d.ts} +15 -2
  8. package/dist/data-grid-BKa9i86q.d.ts.map +1 -0
  9. package/dist/data-grid.d.ts +3 -2
  10. package/dist/data-grid.js +95 -5
  11. package/dist/data-grid.js.map +1 -1
  12. package/dist/{detail-list-_ZJHYEBr.d.ts → detail-list-CHTomlbR.d.ts} +22 -5
  13. package/dist/detail-list-CHTomlbR.d.ts.map +1 -0
  14. package/dist/detail-list.d.ts +4 -3
  15. package/dist/detail-list.js +103 -3
  16. package/dist/detail-list.js.map +1 -1
  17. package/dist/{grid-list-j5C7qtlG.js → grid-list-C8oMxLl-.js} +77 -164
  18. package/dist/grid-list-C8oMxLl-.js.map +1 -0
  19. package/dist/{grid-list-x550wJ0X.d.ts → grid-list-CsZcXxMC.d.ts} +259 -109
  20. package/dist/grid-list-CsZcXxMC.d.ts.map +1 -0
  21. package/dist/grid-list.d.ts +3 -2
  22. package/dist/grid-list.js +1 -1
  23. package/dist/incr-search.d.ts +1 -1
  24. package/dist/{index-h6gxtfHH.d.ts → index-Dgrx-DIA.d.ts} +2 -2
  25. package/dist/{index-h6gxtfHH.d.ts.map → index-Dgrx-DIA.d.ts.map} +1 -1
  26. package/dist/index.d.ts +190 -252
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +8 -7
  29. package/dist/index.js.map +1 -1
  30. package/dist/{interaction-B9AAlsFO.js → interaction-BskmlA9t.js} +1 -1
  31. package/dist/list-drag-types-kpaXz2pZ.d.ts +42 -0
  32. package/dist/list-drag-types-kpaXz2pZ.d.ts.map +1 -0
  33. package/dist/{list-item-animation-DuiwgN3l.js → list-item-animation-BUoEKi5s.js} +29 -10
  34. package/dist/list-item-animation-BUoEKi5s.js.map +1 -0
  35. package/dist/list-native-drag-controller-DrY3qeJM.js +555 -0
  36. package/dist/list-native-drag-controller-DrY3qeJM.js.map +1 -0
  37. package/dist/{menu-HMoxCPY2.js → menu-CMXhIaNZ.js} +3 -3
  38. package/dist/{menu-HMoxCPY2.js.map → menu-CMXhIaNZ.js.map} +1 -1
  39. package/dist/{menu-item-BQDYxk5A.js → menu-item-BYGQzyCe.js} +2 -2
  40. package/dist/{menu-item-BQDYxk5A.js.map → menu-item-BYGQzyCe.js.map} +1 -1
  41. package/dist/menu.js +2 -2
  42. package/dist/miller.d.ts +2 -2
  43. package/dist/miller.js +1 -1
  44. package/dist/narrowed-text.js +1 -1
  45. package/dist/select-horiz.js +1 -1
  46. package/dist/{tree-DTQon838.js → tree-DSMn566I.js} +90 -27
  47. package/dist/tree-DSMn566I.js.map +1 -0
  48. package/dist/{tree-component-F6qZ0Gdv.d.ts → tree-component-nPP5Y-WS.d.ts} +12 -2
  49. package/dist/tree-component-nPP5Y-WS.d.ts.map +1 -0
  50. package/dist/tree.d.ts +3 -2
  51. package/dist/tree.d.ts.map +1 -1
  52. package/dist/tree.js +1 -1
  53. package/dist/{vertical-list-CFuFDygi.d.ts → vertical-list-A6oWfivZ.d.ts} +21 -4
  54. package/dist/vertical-list-A6oWfivZ.d.ts.map +1 -0
  55. package/dist/vertical-list.d.ts +3 -2
  56. package/dist/vertical-list.js +103 -3
  57. package/dist/vertical-list.js.map +1 -1
  58. package/docs-user/README.md +1 -0
  59. package/docs-user/data-grid.md +30 -0
  60. package/docs-user/data-table.md +104 -0
  61. package/docs-user/detail-list.md +29 -0
  62. package/docs-user/grid-list.md +34 -4
  63. package/docs-user/index.json +7 -2
  64. package/docs-user/llms.txt +1 -0
  65. package/docs-user/tree.md +5 -4
  66. package/docs-user/vertical-list.md +30 -1
  67. package/llms.txt +1 -0
  68. package/package.json +1 -1
  69. package/dist/data-grid-D-z7bx5-.d.ts.map +0 -1
  70. package/dist/detail-list-_ZJHYEBr.d.ts.map +0 -1
  71. package/dist/grid-list-j5C7qtlG.js.map +0 -1
  72. package/dist/grid-list-x550wJ0X.d.ts.map +0 -1
  73. package/dist/list-item-animation-DuiwgN3l.js.map +0 -1
  74. package/dist/tree-DTQon838.js.map +0 -1
  75. package/dist/tree-component-F6qZ0Gdv.d.ts.map +0 -1
  76. package/dist/vertical-list-CFuFDygi.d.ts.map +0 -1
@@ -1,4 +1,5 @@
1
- import { S as ListSearchController } from "./index-h6gxtfHH.js";
1
+ import { i as ListDragPreviewRenderer, s as ListNativeDragStartEvent } from "./list-drag-types-kpaXz2pZ.js";
2
+ import { S as ListSearchController } from "./index-Dgrx-DIA.js";
2
3
  import { i as ListInteractionMode, l as ListSelectionMode, r as ListElementEventMap } from "./list-selection-types-B5ioL-dG.js";
3
4
  import { LitElement, PropertyValues } from "lit";
4
5
  import { CommandId, CommandRegistry } from "@clinth/ui-commands";
@@ -52,8 +53,20 @@ declare class VerticalListElement extends LitElement {
52
53
  * disabled. See `docs/infra-incremental.md`.
53
54
  */
54
55
  searchModes: string | undefined;
56
+ /**
57
+ * Enable native browser drag-and-drop for selectable items. When true,
58
+ * selectable rows become draggable and the list emits
59
+ * `list-native-drag-start` when a drag begins.
60
+ */
61
+ itemsDraggable: boolean;
62
+ /**
63
+ * Custom drag preview renderer (synchronous). Defaults to the shared
64
+ * built-in HTML+canvas preview.
65
+ */
66
+ dragPreviewRenderer: ListDragPreviewRenderer<Element> | undefined;
55
67
  private readonly selection;
56
68
  private tickled;
69
+ private _syncNativeDraggable;
57
70
  /** Incremental search (navigate / select / filter). See `docs/infra-incremental.md`. */
58
71
  readonly search: ListSearchController<Element>;
59
72
  /** Command registry carrying this list's `list.search.*` commands. */
@@ -123,6 +136,7 @@ declare class VerticalListElement extends LitElement {
123
136
  private _createCustomCheckbox;
124
137
  private _findListItem;
125
138
  private _dispatch;
139
+ private _dispatchCancelable;
126
140
  private _onPointerOver;
127
141
  private _onPointerLeave;
128
142
  private _onKeyDown;
@@ -136,8 +150,11 @@ declare global {
136
150
  interface HTMLElementTagNameMap {
137
151
  'ixfx-vertical-list': VerticalListElement;
138
152
  }
139
- interface HTMLElementEventMap extends ListElementEventMap {}
153
+ interface HTMLElementEventMap extends VerticalListElementEventMap {}
154
+ }
155
+ interface VerticalListElementEventMap extends ListElementEventMap {
156
+ 'list-native-drag-start': ListNativeDragStartEvent<Element>;
140
157
  }
141
158
  //#endregion
142
- export { CustomCheckboxOptions as n, VerticalListElement as r, AddItemOptions as t };
143
- //# sourceMappingURL=vertical-list-CFuFDygi.d.ts.map
159
+ export { VerticalListElementEventMap as i, CustomCheckboxOptions as n, VerticalListElement as r, AddItemOptions as t };
160
+ //# sourceMappingURL=vertical-list-A6oWfivZ.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vertical-list-A6oWfivZ.d.ts","names":[],"sources":["../src/vertical-list/vertical-list.ts"],"mappings":";;;;;;UAwDiB;EACf,WAAW;EACX;EACA;;UAGe;;;;;;EAMf;;cAMW,4BAA4B;;EAEvC,eAAe;EAGf,iBAAiB;EAGjB,mBAAmB,IAAI;;;;;;EAQvB;;;;;;;;;;;;;;;;;;;EAqBA,oBAAoB,gCAAgC;;;;;;EAQpD;;;;;;EAQA;;;;;EAOA,qBAAqB,wBAAwB;mBAU5B;UAeT;UA4EA;;WA4EC,QAAM,qBAAA;;MAYX,YAAY;EAoBP;EAmBA;EAgDA,QAAQ,SAAS;;MAuCtB,gBAAgB;;MAKhB,iBAAiB,YAAY;EAIjC,QAAQ,MAAM,kBAAkB,UAAU;;;;;;EAqB1C,WAAW,MAAM,UAAU;;EAW3B,YAAY,OAAO,SAAS,WAAW;EAIvC;;;;;;EAYA,SAAS,gBAAgB;;;;;;EAczB,SAAS,WAAW,WAAW,OAAO;EAItC,OAAO,MAAM;EAIb,WAAW,OAAO,SAAS;EAI3B,SAAS,MAAM;EAIf;EAIA;;MAQI,gBAAgB;;MAKhB;;EAMJ,WAAW,MAAM;;EAKjB,gBAAgB,MAAM;;EAKtB,mBAAmB,MAAM;;EAKzB,SAAS;EAkBP,eAAe,0CAA0C,UAAU;UAqB7D;UAgBA;UAOA;UAiBA;;;;;;;UA6FA;;;;;UAuBA;UAcA;UAYA;UAQA;UAWA;UAMA;UAIA;UAwDA;UAUA;UAMA;SAUQ,sBAAM;EAmJb,wBAAM;;QAeT;YACI;IACR,sBAAsB;;YAGd,4BAA4B;;UAGvB,oCAAoC;EACnD,0BAA0B,yBAAyB"}
@@ -1,3 +1,4 @@
1
+ import { a as ListDragSource, i as ListDragPreviewRenderer, n as ListDragPreview, o as ListNativeDragStartDetail, r as ListDragPreviewContext, s as ListNativeDragStartEvent, t as ListDragEntry } from "./list-drag-types-kpaXz2pZ.js";
1
2
  import { a as ListItemClickDetail, c as ListSelectEvent, d as ListTickleEvent, i as ListInteractionMode, l as ListSelectionMode, n as ListActivateEvent, o as ListItemClickEvent, r as ListElementEventMap, s as ListSelectDetail, t as ListActivateDetail, u as ListTickleDetail } from "./list-selection-types-B5ioL-dG.js";
2
- import { n as CustomCheckboxOptions, r as VerticalListElement, t as AddItemOptions } from "./vertical-list-CFuFDygi.js";
3
- export { AddItemOptions, CustomCheckboxOptions, type ListActivateDetail, type ListActivateEvent, type ListElementEventMap, type ListInteractionMode, type ListItemClickDetail, type ListItemClickEvent, type ListSelectDetail, type ListSelectEvent, type ListSelectionMode, type ListTickleDetail, type ListTickleEvent, VerticalListElement };
3
+ import { i as VerticalListElementEventMap, n as CustomCheckboxOptions, r as VerticalListElement, t as AddItemOptions } from "./vertical-list-A6oWfivZ.js";
4
+ export { AddItemOptions, CustomCheckboxOptions, type ListActivateDetail, type ListActivateEvent, type ListDragEntry, type ListDragPreview, type ListDragPreviewContext, type ListDragPreviewRenderer, type ListDragSource, type ListElementEventMap, type ListInteractionMode, type ListItemClickDetail, type ListItemClickEvent, type ListNativeDragStartDetail, type ListNativeDragStartEvent, type ListSelectDetail, type ListSelectEvent, type ListSelectionMode, type ListTickleDetail, type ListTickleEvent, VerticalListElement, VerticalListElementEventMap };
@@ -2,8 +2,9 @@ import { t as TickledController } from "./tickled-controller-Dh1Uh_vG.js";
2
2
  import { t as themeFallbacks } from "./fallbacks-BcKGq-2F.js";
3
3
  import { n as safeCustomElement, t as __decorate } from "./decorate-VRK8rslU.js";
4
4
  import { c as createElementListSearchAdapter, i as ListSearchController, n as listSearchStyles, o as registerListSearchCommands, r as LIST_SEARCH_MODES, u as keyForElement } from "./incr-search-C05WjbZh.js";
5
+ import { t as ListNativeDragController } from "./list-native-drag-controller-DrY3qeJM.js";
5
6
  import { r as ListDragSelectController, t as ListSelectionController } from "./list-selection-controller-SA4piSBi.js";
6
- import { n as animateListItemIn, r as animateListItemOut } from "./list-item-animation-DuiwgN3l.js";
7
+ import { n as animateListItemIn, r as animateListItemOut } from "./list-item-animation-BUoEKi5s.js";
7
8
  import { LitElement, css, html, render } from "lit";
8
9
  import { property } from "lit/decorators.js";
9
10
  import { createCommandRegistry } from "@clinth/ui-commands";
@@ -15,6 +16,7 @@ let VerticalListElement = class VerticalListElement extends LitElement {
15
16
  this.selectionMode = `single`;
16
17
  this.interactionMode = `standard`;
17
18
  this.itemWrap = `stop`;
19
+ this.itemsDraggable = false;
18
20
  this.#customWrappers = /* @__PURE__ */ new Map();
19
21
  this.#wrappedElements = /* @__PURE__ */ new Set();
20
22
  this.#animatedItems = /* @__PURE__ */ new WeakSet();
@@ -45,7 +47,41 @@ let VerticalListElement = class VerticalListElement extends LitElement {
45
47
  },
46
48
  onStateChange: () => this._syncItemStates(),
47
49
  itemWrap: this.itemWrap,
48
- hoverDisabled: () => this.#dragSelect.isDragging
50
+ hoverDisabled: () => this.#dragSelect.isDragging || this.#nativeDrag.isDragging
51
+ });
52
+ this.#nativeDragging = false;
53
+ this.#postDragSuppressTimer = 0;
54
+ this.#nativeDrag = new ListNativeDragController(this, {
55
+ source: `vertical-list`,
56
+ isEnabled: () => this.itemsDraggable && this.selectionMode !== `none` && this._getVisibleItems().length > 0,
57
+ resolveEventToKey: (target) => {
58
+ if (!(target instanceof Element)) return void 0;
59
+ const item = target.closest?.(ITEM_SELECTOR);
60
+ if (!item || !this.contains(item) || this.#wrappedElements.has(item)) return void 0;
61
+ return keyForElement(item);
62
+ },
63
+ prepareSelection: (originKey) => this.#prepareNativeDrag(originKey),
64
+ getMetrics: () => ({}),
65
+ getRenderer: () => this.dragPreviewRenderer,
66
+ dispatch: (detail) => this._dispatchCancelable(`list-native-drag-start`, detail),
67
+ onActiveChange: (active, originKey) => {
68
+ this.#nativeDragging = active;
69
+ this.toggleAttribute(`data-native-dragging`, active);
70
+ for (const el of this._getVisibleItems()) el.toggleAttribute(`data-drag-origin`, active && originKey !== void 0 && keyForElement(el) === originKey);
71
+ if (active) {
72
+ const count = this.selection.selected.size;
73
+ this.setAttribute(`aria-label`, count <= 1 ? `Dragging 1 item` : `Dragging ${count} items`);
74
+ } else this.removeAttribute(`aria-label`);
75
+ this.requestUpdate();
76
+ },
77
+ onEnd: () => {
78
+ this.#nativeDragging = false;
79
+ this.toggleAttribute(`data-native-dragging`, false);
80
+ this.removeAttribute(`aria-label`);
81
+ for (const el of this._getVisibleItems()) el.removeAttribute(`data-drag-origin`);
82
+ this.#startPostDragSuppressTimer();
83
+ },
84
+ syncLightDomDraggable: () => this._syncNativeDraggable()
49
85
  });
50
86
  this.#dragSelect = new ListDragSelectController(this, {
51
87
  getSurface: () => this.renderRoot.querySelector(`.container`),
@@ -127,7 +163,7 @@ let VerticalListElement = class VerticalListElement extends LitElement {
127
163
  }
128
164
  };
129
165
  this._onClick = (e) => {
130
- if (this.#dragSelect.isDragging) return;
166
+ if (this.#dragSelect.isDragging || this.#nativeDrag.isDragging || this.#postDragSuppressTimer !== 0) return;
131
167
  const item = this._findListItem(e.composedPath());
132
168
  if (!item) return;
133
169
  this._dispatch(`list-item-click`, { item });
@@ -144,6 +180,44 @@ let VerticalListElement = class VerticalListElement extends LitElement {
144
180
  #itemObserver;
145
181
  /** Items that have already played (or been skipped for) their enter animation. */
146
182
  #animatedItems;
183
+ #nativeDragging;
184
+ #postDragSuppressTimer;
185
+ #nativeDrag;
186
+ #prepareNativeDrag(originKey) {
187
+ const visible = this._getVisibleItems();
188
+ const originEl = visible.find((el) => keyForElement(el) === originKey);
189
+ if (!originEl || !this._isCheckable(originEl)) return void 0;
190
+ if (!this.selection.selected.has(originEl)) this.selection.replace(originEl);
191
+ const selected = [];
192
+ visible.forEach((el, index) => {
193
+ if (!this.selection.selected.has(el)) return;
194
+ selected.push({
195
+ key: keyForElement(el) ?? originKey,
196
+ index,
197
+ item: el,
198
+ element: el
199
+ });
200
+ });
201
+ const origin = selected.find((entry) => entry.key === originKey);
202
+ if (!origin) return void 0;
203
+ return {
204
+ origin,
205
+ selected
206
+ };
207
+ }
208
+ _syncNativeDraggable() {
209
+ if (!this.isConnected) return;
210
+ for (const el of this.querySelectorAll(ITEM_SELECTOR)) {
211
+ if (this.#wrappedElements.has(el)) continue;
212
+ if (el instanceof HTMLElement) el.draggable = this.itemsDraggable && this._isCheckable(el);
213
+ }
214
+ }
215
+ #startPostDragSuppressTimer() {
216
+ if (this.#postDragSuppressTimer) clearTimeout(this.#postDragSuppressTimer);
217
+ this.#postDragSuppressTimer = window.setTimeout(() => {
218
+ this.#postDragSuppressTimer = 0;
219
+ }, 0);
220
+ }
147
221
  /** Live drag-select preview rect (surface-relative); the covered items carry `data-marquee`. */
148
222
  #marqueeRect;
149
223
  #dragSelect;
@@ -211,6 +285,10 @@ let VerticalListElement = class VerticalListElement extends LitElement {
211
285
  this.#unregisterSearchCommands?.();
212
286
  this.#unregisterSearchCommands = void 0;
213
287
  this.#searchRegistry = void 0;
288
+ if (this.#postDragSuppressTimer) {
289
+ clearTimeout(this.#postDragSuppressTimer);
290
+ this.#postDragSuppressTimer = 0;
291
+ }
214
292
  }
215
293
  /**
216
294
  * Animate in items appended to the light DOM (however they got there —
@@ -250,6 +328,10 @@ let VerticalListElement = class VerticalListElement extends LitElement {
250
328
  }
251
329
  this._syncItemStates();
252
330
  } else if (changed.has(`checkboxRenderer`) && this.interactionMode === `checked`) this._syncCheckboxes();
331
+ if (changed.has(`itemsDraggable`) || changed.has(`interactionMode`)) {
332
+ this._syncNativeDraggable();
333
+ this.#nativeDrag.refreshDraggable();
334
+ }
253
335
  }
254
336
  /** The most recently selected item, or `undefined` if nothing is selected. */
255
337
  get selectedItem() {
@@ -519,6 +601,14 @@ let VerticalListElement = class VerticalListElement extends LitElement {
519
601
  composed: true
520
602
  }));
521
603
  }
604
+ _dispatchCancelable(name, detail) {
605
+ return this.dispatchEvent(new CustomEvent(name, {
606
+ detail,
607
+ bubbles: true,
608
+ composed: true,
609
+ cancelable: true
610
+ }));
611
+ }
522
612
  _activateItem(item) {
523
613
  if (this.interactionMode !== `manual`) this.selection.replace(item);
524
614
  this._dispatch(`list-activate`, { item });
@@ -654,6 +744,10 @@ let VerticalListElement = class VerticalListElement extends LitElement {
654
744
  color: var(--item-text-selected);
655
745
  }
656
746
 
747
+ ::slotted([data-drag-origin]) {
748
+ opacity: 0.6;
749
+ }
750
+
657
751
  /* checked mode: checkbox (injected into li) communicates selection — suppress bg highlight */
658
752
  :host([interaction-mode="checked"]) ::slotted([data-selected]) {
659
753
  background: transparent;
@@ -700,6 +794,12 @@ __decorate([property({
700
794
  attribute: `search-modes`,
701
795
  reflect: true
702
796
  })], VerticalListElement.prototype, "searchModes", void 0);
797
+ __decorate([property({
798
+ type: Boolean,
799
+ attribute: `items-draggable`,
800
+ reflect: true
801
+ })], VerticalListElement.prototype, "itemsDraggable", void 0);
802
+ __decorate([property({ attribute: false })], VerticalListElement.prototype, "dragPreviewRenderer", void 0);
703
803
  VerticalListElement = __decorate([safeCustomElement(`ixfx-vertical-list`)], VerticalListElement);
704
804
  //#endregion
705
805
  export { VerticalListElement };
@@ -1 +1 @@
1
- {"version":3,"file":"vertical-list.js","names":["#dragSelect","#marqueeHitTest","#marqueeRect","#searchRegistry","#effectiveSearchModes","#unregisterSearchCommands","#animatedItems","#itemObserver","#onItemsMutated","#syncSearchCommands","#customWrappers","#wrappedElements","#itemForKey","#marqueeStyle","customElement"],"sources":["../src/vertical-list/vertical-list.ts"],"sourcesContent":["import type { CommandId, CommandRegistry } from '@clinth/ui-commands';\nimport type { PropertyValues } from 'lit';\nimport type { ListSearchMode } from '../incr-search/index.js';\nimport type { DragSelectRect } from '../interaction/list-drag-select-controller.js';\nimport type {\n ListActivateDetail,\n ListElementEventMap,\n ListInteractionMode,\n ListItemClickDetail,\n ListSelectDetail,\n ListSelectionMode,\n ListTickleDetail,\n} from '../interaction/list-selection-types.js';\nimport { createCommandRegistry } from '@clinth/ui-commands';\nimport { css, html, LitElement, render } from 'lit';\nimport { property } from 'lit/decorators.js';\nimport {\n createElementListSearchAdapter,\n LIST_SEARCH_MODES,\n ListSearchController,\n listSearchStyles,\n registerListSearchCommands,\n} from '../incr-search/index.js';\nimport { keyForElement } from '../interaction/keyed-list.js';\nimport { ListDragSelectController } from '../interaction/list-drag-select-controller.js';\nimport { animateListItemIn, animateListItemOut } from '../interaction/list-item-animation.js';\nimport { ListSelectionController } from '../interaction/list-selection-controller.js';\nimport { TickledController } from '../interaction/tickled-controller.js';\nimport { themeFallbacks } from '../styles/fallbacks.js';\nimport { safeCustomElement as customElement } from '../util/custom-elements.js';\n\nexport type {\n ListActivateDetail,\n ListActivateEvent,\n ListElementEventMap,\n ListInteractionMode,\n ListItemClickDetail,\n ListItemClickEvent,\n ListSelectDetail,\n ListSelectEvent,\n ListSelectionMode,\n ListTickleDetail,\n ListTickleEvent,\n} from './types.js';\n\nexport interface CustomCheckboxOptions {\n renderer: (checked: boolean) => unknown;\n margin?: string;\n width?: string;\n}\n\nexport interface AddItemOptions {\n /**\n * In `checked` interaction mode, adds the item without a checkbox column and\n * without the ability to be checked/selected. Equivalent to setting the\n * `not-checked` attribute on the item. Ignored outside `checked` mode.\n */\n notChecked?: boolean;\n}\n\nconst ITEM_SELECTOR = `li, [data-list-item]`;\n\n@customElement(`ixfx-vertical-list`)\nexport class VerticalListElement extends LitElement {\n @property({ type: String, attribute: `selection-mode` })\n selectionMode: ListSelectionMode = `single`;\n\n @property({ type: String, attribute: `interaction-mode` })\n interactionMode: ListInteractionMode = `standard`;\n\n @property({ attribute: false })\n filterPredicate: ((el: Element) => boolean) | undefined;\n\n /**\n * Whether keyboard navigation wraps from last to first and vice versa.\n * - 'stop' (default): navigation stops at the boundary\n * - 'wrap': navigation wraps around\n */\n @property({ type: String, attribute: `item-wrap` })\n itemWrap: `stop` | `wrap` = `stop`;\n\n /**\n * Optional renderer for the checkbox shown in `checked` interactionMode.\n * Receives the current `checked` state and returns a Lit template result.\n *\n * When set, the per-row checkbox becomes a `<span role=\"checkbox\">` whose\n * contents are whatever this returns (rendered with `lit`'s `render()`).\n * When unset, a native `<input type=\"checkbox\">` is used.\n *\n * @example\n * list.checkboxRenderer = (checked) => checked\n * ? html`<svg viewBox=\"0 0 16 16\" width=\"14\" height=\"14\"\n * fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"\n * stroke-linecap=\"round\" stroke-linejoin=\"round\"\n * style=\"opacity:0.5\">\n * <polyline points=\"3,8 7,12 13,4\" />\n * </svg>`\n * : html``;\n */\n @property({ attribute: false })\n checkboxRenderer: ((checked: boolean) => unknown) | CustomCheckboxOptions | undefined;\n\n /**\n * Space-separated subset of `navigate select filter` — limits which\n * incremental-search modes are offered. Unset ⇒ all three; empty ⇒ search\n * disabled. See `docs/infra-incremental.md`.\n */\n @property({ type: String, attribute: `search-modes`, reflect: true })\n searchModes: string | undefined;\n\n #customWrappers = new Map<HTMLLIElement, Element>();\n #wrappedElements = new Set<Element>();\n\n /** Watches for items added to the light DOM so they can be animated in. */\n #itemObserver: MutationObserver | undefined;\n /** Items that have already played (or been skipped for) their enter animation. */\n #animatedItems = new WeakSet<Element>();\n\n private readonly selection = new ListSelectionController<Element>({\n getOrderedItems: () => this._getVisibleItems(),\n isSelectable: el => this._isCheckable(el),\n getModes: () => ({ selectionMode: this.selectionMode, interactionMode: this.interactionMode }),\n getKey: keyForElement,\n onChange: (selected, previous) => {\n this._syncItemStates();\n this._dispatch<ListSelectDetail>(`list-select`, {\n selected,\n previous,\n selectedKeys: this.selection.selectedKeys,\n });\n },\n });\n\n private tickled = new TickledController<Element>(this, {\n getItems: () => this._getVisibleItems().map(el => ({\n id: el,\n enabled: !el.hasAttribute(`not-checked`),\n })),\n onTickle: (id) => {\n if (id !== undefined) {\n this._dispatch<ListTickleDetail>(`list-tickle`, { item: id });\n }\n },\n onStateChange: () => this._syncItemStates(),\n itemWrap: this.itemWrap,\n hoverDisabled: () => this.#dragSelect.isDragging,\n });\n\n /** Live drag-select preview rect (surface-relative); the covered items carry `data-marquee`. */\n #marqueeRect: DragSelectRect | undefined;\n\n readonly #dragSelect = new ListDragSelectController<Element>(this, {\n getSurface: () => this.renderRoot.querySelector(`.container`) as HTMLElement | undefined,\n isEnabled: () =>\n this.selection.canDragSelect()\n && this._getVisibleItems().length > 0\n && !this.hasAttribute(`disable-drag-select`),\n hitTest: rect => this.#marqueeHitTest(rect),\n getBaseSelection: () => this.selection.selected,\n onPreview: (preview, rect) => {\n this.#marqueeRect = rect;\n for (const el of this._getVisibleItems())\n el.toggleAttribute(`data-marquee`, preview.has(el));\n this.requestUpdate();\n },\n onCommit: (finalSet) => {\n this.selection.applyDragSelection(finalSet);\n for (const el of this._getVisibleItems())\n el.removeAttribute(`data-marquee`);\n this.#marqueeRect = undefined;\n this.requestUpdate();\n },\n autoScroll: (_dx, dy) => {\n const list = this.renderRoot.querySelector(`ul`);\n if (list)\n list.scrollTop += dy;\n },\n });\n\n /** Collect visible items whose surface-relative box intersects `rect`. */\n #marqueeHitTest(rect: DragSelectRect): Element[] {\n const surface = this.renderRoot.querySelector(`.container`);\n if (!surface)\n return [];\n const box = surface.getBoundingClientRect();\n const rx0 = rect.x;\n const ry0 = rect.y;\n const rx1 = rect.x + rect.width;\n const ry1 = rect.y + rect.height;\n const out: Element[] = [];\n for (const el of this._getVisibleItems()) {\n const r = el.getBoundingClientRect();\n const left = r.left - box.left;\n const top = r.top - box.top;\n if (left <= rx1 && left + r.width >= rx0 && top <= ry1 && top + r.height >= ry0)\n out.push(el);\n }\n return out;\n }\n\n #marqueeStyle(rect: DragSelectRect): string {\n return `left:${rect.x}px;top:${rect.y}px;width:${Math.max(0, rect.width)}px;height:${Math.max(0, rect.height)}px`;\n }\n\n /** Incremental search (navigate / select / filter). See `docs/infra-incremental.md`. */\n readonly search = new ListSearchController<Element>(this, {\n adapter: createElementListSearchAdapter(this, {\n itemSelector: ITEM_SELECTOR,\n getLabel: el => el.getAttribute(`data-search-label`) ?? el.textContent?.trim() ?? ``,\n highlightKey: `vertical-list-search`,\n }),\n });\n\n #searchRegistry: CommandRegistry<unknown> | undefined;\n #unregisterSearchCommands: (() => void) | undefined;\n\n /** Command registry carrying this list's `list.search.*` commands. */\n get registry(): CommandRegistry<unknown> | undefined {\n return this.#searchRegistry;\n }\n\n #effectiveSearchModes(): ListSearchMode[] {\n const raw = this.searchModes?.trim();\n if (raw === undefined)\n return [...LIST_SEARCH_MODES];\n const requested = new Set(raw.split(/\\s+/));\n return LIST_SEARCH_MODES.filter(m => requested.has(m));\n }\n\n #syncSearchCommands(): void {\n if (!this.#searchRegistry)\n return;\n this.search.setModes(this.#effectiveSearchModes());\n this.#unregisterSearchCommands?.();\n this.#unregisterSearchCommands = registerListSearchCommands(this.#searchRegistry, this.search);\n }\n\n override connectedCallback(): void {\n super.connectedCallback();\n if (!this.hasAttribute(`tabindex`))\n this.tabIndex = 0;\n this.addEventListener(`pointerover`, this._onPointerOver);\n this.addEventListener(`pointerleave`, this._onPointerLeave);\n this.addEventListener(`keydown`, this._onKeyDown);\n this.addEventListener(`click`, this._onClick);\n this.addEventListener(`dblclick`, this._onDblClick);\n // Mark whatever's already here as \"known\" so the first populate doesn't\n // animate; anything added later collapses in.\n for (const el of this.querySelectorAll(ITEM_SELECTOR))\n this.#animatedItems.add(el);\n this.#itemObserver ??= new MutationObserver(records => this.#onItemsMutated(records));\n this.#itemObserver.observe(this, { childList: true });\n this.#searchRegistry ??= createCommandRegistry();\n this.#syncSearchCommands();\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n this.removeEventListener(`pointerover`, this._onPointerOver);\n this.removeEventListener(`pointerleave`, this._onPointerLeave);\n this.removeEventListener(`keydown`, this._onKeyDown);\n this.removeEventListener(`click`, this._onClick);\n this.removeEventListener(`dblclick`, this._onDblClick);\n this.#itemObserver?.disconnect();\n this.#unregisterSearchCommands?.();\n this.#unregisterSearchCommands = undefined;\n this.#searchRegistry = undefined;\n }\n\n /**\n * Animate in items appended to the light DOM (however they got there —\n * {@link addItem}, direct `appendChild`, a framework binding). A bulk populate\n * (roughly everything at once) is left alone, as are the wrapper `<li>`s the\n * checked-mode plumbing shuffles around.\n */\n #onItemsMutated(records: MutationRecord[]): void {\n if (!this.isConnected)\n return;\n const fresh: HTMLElement[] = [];\n for (const record of records) {\n for (const node of record.addedNodes) {\n if (!(node instanceof HTMLElement) || node.parentElement !== this)\n continue;\n if (!node.matches(ITEM_SELECTOR))\n continue;\n if (this.#animatedItems.has(node) || this.#customWrappers.has(node as HTMLLIElement) || this.#wrappedElements.has(node))\n continue;\n this.#animatedItems.add(node);\n fresh.push(node);\n }\n }\n if (fresh.length === 0)\n return;\n const total = this.querySelectorAll(ITEM_SELECTOR).length;\n if (fresh.length >= total)\n return; // first populate / wholesale replace — don't animate\n for (const el of fresh)\n animateListItemIn(el);\n }\n\n override updated(changed: PropertyValues): void {\n super.updated(changed);\n if (changed.has(`searchModes`)) {\n this.#syncSearchCommands();\n }\n if (changed.has(`interactionMode`)) {\n this.selection.clearAnchor();\n const previousSelected = this.selection.selected;\n // Save wrapper→inner map before _syncCheckboxes() clears it on unwrap\n const leavingChecked = (changed.get(`interactionMode`) as ListInteractionMode) === `checked`;\n const savedWrappers = leavingChecked ? new Map(this.#customWrappers) : null;\n\n this._syncCheckboxes();\n\n // Remap the selection across the wrap/unwrap element-reference boundary\n if (previousSelected.size > 0) {\n if (savedWrappers && savedWrappers.size > 0) {\n // Leaving checked: wrapper refs → inner element refs\n this.selection.remap(el => savedWrappers.get(el as HTMLLIElement) ?? el);\n } else if (this.#customWrappers.size > 0) {\n // Entering checked: inner element refs → wrapper refs\n const innerToWrapper = new Map([...this.#customWrappers].map(([w, i]) => [i, w]));\n this.selection.remap(el => innerToWrapper.get(el) ?? el);\n }\n }\n\n this._syncItemStates();\n } else if (changed.has(`checkboxRenderer`) && this.interactionMode === `checked`) {\n this._syncCheckboxes();\n }\n }\n\n // --- Public API ---\n\n /** The most recently selected item, or `undefined` if nothing is selected. */\n get selectedItem(): Element | undefined {\n return [...this.selection.selected].at(-1);\n }\n\n /** The full current selection set (read-only). */\n get selectedItems(): ReadonlySet<Element> {\n return this.selection.selected;\n }\n\n addItem(item: Element | string, options?: AddItemOptions): void {\n let el: Element;\n if (typeof item === `string`) {\n const li = document.createElement(`li`);\n li.textContent = item;\n el = li;\n } else {\n el = item;\n }\n if (options?.notChecked)\n el.setAttribute(`not-checked`, ``);\n this.appendChild(el);\n if (this.interactionMode === `checked`)\n this._syncCheckboxes();\n }\n\n /**\n * Collapse + fade `item` out, then detach it. The rows below slide up to\n * close the gap. Resolves once the element has actually been removed.\n * Under `prefers-reduced-motion: reduce` it's removed immediately.\n */\n removeItem(item: Element): Promise<void> {\n this.selection.deselect(item);\n const done = animateListItemOut(item as HTMLElement).then(() => {\n item.remove();\n if (this.interactionMode === `checked`)\n this._syncCheckboxes();\n });\n return done;\n }\n\n /** {@link removeItem} for several items at once. */\n removeItems(items: Iterable<Element>): Promise<void> {\n return Promise.all([...items].map(item => this.removeItem(item))).then(() => undefined);\n }\n\n clearItems(): void {\n this.#customWrappers.clear();\n this.replaceChildren();\n this.selection.reset();\n this._syncItemStates();\n }\n\n /**\n * Replace the entire item list in one call.\n * Equivalent to `clearItems()` followed by `addItem()` for each element.\n * Preserves `interactionMode` checkbox wrapping.\n */\n setItems(items: readonly Element[]): void {\n this.clearItems();\n for (const item of items) {\n this.appendChild(item);\n }\n if (this.interactionMode === `checked`)\n this._syncCheckboxes();\n }\n\n /**\n * Dispatch a command to this list — used by `ixfx-grouped-item-lister` to\n * forward commands to all sub-components. Routes through the command registry\n * (so `list.search.*` and any other registered commands work).\n */\n dispatch(commandId: CommandId, args?: Record<string, unknown>): void {\n this.#searchRegistry?.invoke(commandId, args);\n }\n\n select(item: Element): void {\n this.selection.replace(item);\n }\n\n selectMany(items: Iterable<Element>): void {\n this.selection.selectMany(items);\n }\n\n deselect(item: Element): void {\n this.selection.deselect(item);\n }\n\n clearSelection(): void {\n this.selection.clear();\n }\n\n selectAll(): void {\n if (this.selection.canSelectAll())\n this.selection.selectAll();\n }\n\n // --- Key-based selection & cursor ---\n\n /** The selection as stable `data-key` / `data-value` strings. */\n get selectedKeys(): ReadonlySet<string> {\n return this.selection.selectedKeys;\n }\n\n /** The key of the item under the cursor, or `undefined`. */\n get tickledKey(): string | undefined {\n const id = this.tickled.tickledId;\n return id ? keyForElement(id) : undefined;\n }\n\n /** Replace the selection with the items carrying these keys (unknown keys ignored). */\n selectKeys(keys: Iterable<string>): void {\n this.selection.selectKeys(keys, key => this.#itemForKey(key));\n }\n\n /** Add items by key to the selection (multi-select modes only). */\n addSelectedKeys(keys: Iterable<string>): void {\n this.selection.addKeys(keys, key => this.#itemForKey(key));\n }\n\n /** Remove items by key from the selection. */\n removeSelectedKeys(keys: Iterable<string>): void {\n this.selection.removeKeys(keys, key => this.#itemForKey(key));\n }\n\n /** Move the cursor to the item with this key and scroll it into view. */\n focusKey(key: string): void {\n const el = this.#itemForKey(key);\n if (!el)\n return;\n this.tickled.focus(el);\n el.scrollIntoView({ block: `nearest`, inline: `nearest` });\n }\n\n #itemForKey(key: string): Element | undefined {\n for (const el of this.querySelectorAll(ITEM_SELECTOR)) {\n if (this.#wrappedElements.has(el))\n continue;\n if (keyForElement(el) === key)\n return el;\n }\n return undefined;\n }\n\n * selectedFilter(kind: `selected` | `checked` | `either`): Generator<Element> {\n const wanted = (el: Element) => {\n switch (kind) {\n case `selected`: return el.hasAttribute(`data-selected`);\n case `checked`: return el.hasAttribute(`data-checked`);\n case `either`: return el.hasAttribute(`data-selected`) || el.hasAttribute(`data-checked`);\n }\n };\n\n for (const el of this.querySelectorAll(ITEM_SELECTOR)) {\n if (this.#wrappedElements.has(el))\n continue;\n if ((el as HTMLElement).hidden)\n continue;\n if (wanted(el))\n yield el;\n }\n }\n\n // --- Helpers ---\n\n private _getVisibleItems(): Element[] {\n return Array.from(this.querySelectorAll(ITEM_SELECTOR)).filter((el) => {\n if (this.#wrappedElements.has(el))\n return false;\n if ((el as HTMLElement).hidden)\n return false;\n if (this.filterPredicate && !this.filterPredicate(el))\n return false;\n return true;\n });\n }\n\n // Whether an item participates in checked-mode selection. Items carrying the\n // `not-checked` attribute are skipped; outside `checked` mode it has no effect.\n // For wrapped custom elements the flag lives on the inner element, so resolve\n // through the wrapper map.\n private _isCheckable(item: Element): boolean {\n if (this.interactionMode !== `checked`)\n return true;\n const real = this.#customWrappers.get(item as HTMLLIElement) ?? item;\n return !real.hasAttribute(`not-checked`);\n }\n\n private _syncItemStates(): void {\n for (const el of this.querySelectorAll(ITEM_SELECTOR)) {\n if (this.#wrappedElements.has(el))\n continue;\n const item = this.#customWrappers.get(el as HTMLLIElement) ?? el;\n el.toggleAttribute(`data-tickled`, this.tickled.isTickled(el));\n el.toggleAttribute(`data-selected`, this.selection.selected.has(el));\n if (this.interactionMode === `checked`) {\n el.toggleAttribute(`data-checked`, !item.hasAttribute(`not-checked`) && this.selection.selected.has(el));\n } else {\n el.removeAttribute(`data-checked`);\n }\n }\n this.dataset.inputMode = this.tickled.inputMode;\n this._syncCheckboxes();\n }\n\n private _syncCheckboxes(): void {\n const inCheckedMode = this.interactionMode === `checked`;\n const wantsCustom = typeof this.checkboxRenderer === `function`;\n\n if (inCheckedMode) {\n for (const inner of Array.from(this.children)) {\n if (!inner.hasAttribute(`data-list-item`))\n continue;\n if (inner.parentElement !== this)\n continue; // idempotency: already wrapped\n const wrapper = document.createElement(`li`);\n wrapper.className = `ixfx-custom-row`;\n const key = inner.getAttribute(`data-key`);\n const label = inner.getAttribute(`data-search-label`);\n if (key !== null)\n wrapper.setAttribute(`data-key`, key);\n if (label !== null)\n wrapper.setAttribute(`data-search-label`, label);\n inner.removeAttribute(`data-list-item`);\n this.insertBefore(wrapper, inner);\n wrapper.appendChild(inner);\n this.#customWrappers.set(wrapper, inner);\n this.#wrappedElements.add(inner);\n }\n } else {\n for (const [wrapper, inner] of this.#customWrappers) {\n if (wrapper.parentElement !== this)\n continue;\n inner.setAttribute(`data-list-item`, ``);\n this.insertBefore(inner, wrapper);\n wrapper.remove();\n }\n this.#customWrappers.clear();\n this.#wrappedElements.clear();\n }\n\n for (const el of this.querySelectorAll<HTMLElement>(`li`)) {\n const existing = el.querySelector<HTMLElement>(`.ixfx-list-checkbox`);\n if (!inCheckedMode) {\n if (existing)\n existing.remove();\n continue;\n }\n // Rows marked `not-checked` get no checkbox column and can't be checked\n const itemEl = this.#customWrappers.get(el as HTMLLIElement) ?? el;\n if (itemEl.hasAttribute(`not-checked`)) {\n if (existing)\n existing.remove();\n continue;\n }\n\n // In checked mode: if the existing checkbox is the wrong variant, drop it.\n const existingIsCustom = existing?.classList.contains(`ixfx-list-checkbox-custom`) ?? false;\n if (existing && existingIsCustom !== wantsCustom)\n existing.remove();\n\n let cb: HTMLElement | null = el.querySelector<HTMLElement>(`.ixfx-list-checkbox`);\n if (!cb) {\n if (wantsCustom) {\n const opts = this.checkboxRenderer as CustomCheckboxOptions;\n const margin = opts.margin ?? `0`;\n const width = opts.width ?? `24px`;\n cb = this._createCustomCheckbox(el, margin, width);\n } else {\n cb = this._createNativeCheckboxCell(el);\n }\n el.prepend(cb);\n }\n\n const checked = this.selection.selected.has(el);\n if (wantsCustom) {\n cb.setAttribute(`aria-checked`, String(checked));\n cb.toggleAttribute(`data-checked`, checked);\n if (this.checkboxRenderer) {\n const renderer = typeof this.checkboxRenderer === `function`\n ? this.checkboxRenderer\n : (this.checkboxRenderer as CustomCheckboxOptions).renderer;\n render(renderer(checked), cb);\n }\n } else {\n const input = cb.querySelector<HTMLInputElement>(`input.ixfx-list-checkbox`);\n if (input)\n input.checked = checked;\n }\n }\n }\n\n /**\n * Native checkbox cell: a span that stretches to the row's full cross-axis\n * (so the entire column height is a click target) with a real `<input>`\n * centred inside. The span owns the click; the input is purely visual and\n * has `pointer-events:none` so a single click produces one toggle.\n */\n private _createNativeCheckboxCell(el: HTMLElement): HTMLSpanElement {\n const cell = document.createElement(`span`);\n cell.className = `ixfx-list-checkbox ixfx-list-checkbox-native`;\n cell.style.cssText = `display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;align-self:stretch;width:18px;margin:0 6px 0 0;cursor:pointer;pointer-events:auto`;\n cell.tabIndex = -1;\n cell.setAttribute(`role`, `checkbox`);\n cell.setAttribute(`part`, `checkbox`);\n cell.addEventListener(`click`, (e) => {\n e.stopPropagation();\n this.selection.toggle(el);\n });\n const input = document.createElement(`input`);\n input.type = `checkbox`;\n input.className = `ixfx-list-checkbox`;\n input.style.cssText = `pointer-events:none;cursor:pointer;margin:0`;\n cell.appendChild(input);\n return cell;\n }\n\n /**\n * Custom checkbox cell: same shape as the native cell (a stretching span)\n * so behaviour is identical. The renderer's content goes inside.\n */\n private _createCustomCheckbox(el: HTMLElement, margin: string, width: string): HTMLSpanElement {\n const span = document.createElement(`span`);\n span.className = `ixfx-list-checkbox ixfx-list-checkbox-custom`;\n span.style.cssText = `display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;align-self:stretch;width:${width};margin:${margin};cursor:pointer;pointer-events:auto`;\n span.tabIndex = -1;\n span.setAttribute(`role`, `checkbox`);\n span.setAttribute(`part`, `checkbox`);\n span.addEventListener(`click`, (e) => {\n e.stopPropagation();\n this.selection.toggle(el);\n });\n return span;\n }\n\n private _findListItem(path: EventTarget[]): Element | undefined {\n for (const target of path) {\n if (!(target instanceof Element))\n continue;\n if (target === this)\n break;\n if (target.parentElement === this && target.matches(ITEM_SELECTOR))\n return target;\n }\n return undefined;\n }\n\n private _dispatch<T>(name: keyof ListElementEventMap, detail: T): void {\n this.dispatchEvent(new CustomEvent<T>(name as string, {\n detail,\n bubbles: true,\n composed: true,\n }));\n }\n\n // --- Event handlers ---\n\n private _onPointerOver = (e: PointerEvent): void => {\n const item = this._findListItem(e.composedPath());\n if (item)\n this.tickled.handlePointerEnter(item);\n };\n\n private _onPointerLeave = (e: PointerEvent): void => {\n this.tickled.handlePointerLeave(e);\n };\n\n private _onKeyDown = (e: KeyboardEvent): void => {\n if (this.search.isOpen)\n return;\n if ((e.ctrlKey || e.metaKey) && e.key === `a`) {\n if (this.selection.canSelectAll()) {\n e.preventDefault();\n this.selection.selectAll();\n }\n return;\n }\n\n switch (e.key) {\n case `ArrowDown`: {\n e.preventDefault();\n this.tickled.handleKeyboardNavigation(`next`);\n // vscode: Shift+Arrow extends range from anchor\n if (e.shiftKey && this.interactionMode === `vscode`) {\n const cur = this.tickled.tickledId;\n if (cur)\n this.selection.rangeTo(cur);\n }\n break;\n }\n case `ArrowUp`: {\n e.preventDefault();\n this.tickled.handleKeyboardNavigation(`previous`);\n if (e.shiftKey && this.interactionMode === `vscode`) {\n const cur = this.tickled.tickledId;\n if (cur)\n this.selection.rangeTo(cur);\n }\n break;\n }\n case `Enter`: {\n const id = this.tickled.tickledId;\n if (id) {\n e.preventDefault();\n this._dispatch<ListItemClickDetail>(`list-item-click`, { item: id });\n this._activateItem(id);\n }\n break;\n }\n case ` `: {\n const id = this.tickled.tickledId;\n if (id) {\n e.preventDefault();\n this.selection.handleSpace(id);\n }\n break;\n }\n case `Escape`:\n this.selection.clear();\n break;\n }\n };\n\n private _onClick = (e: MouseEvent): void => {\n if (this.#dragSelect.isDragging)\n return;\n const item = this._findListItem(e.composedPath());\n if (!item)\n return;\n this._dispatch<ListItemClickDetail>(`list-item-click`, { item });\n this.selection.handleClick(item, e);\n };\n\n private _onDblClick = (e: MouseEvent): void => {\n const item = this._findListItem(e.composedPath());\n if (item)\n this._activateItem(item);\n };\n\n private _activateItem(item: Element): void {\n // manual mode: never auto-select; just fire the event\n if (this.interactionMode !== `manual`) {\n this.selection.replace(item);\n }\n this._dispatch<ListActivateDetail>(`list-activate`, { item });\n }\n\n // --- Styles ---\n\n static override styles = [themeFallbacks, listSearchStyles, css`\n :host {\n display: block;\n position: relative;\n background: var(--list-bg, var(--surface-3));\n color: var(--list-text, var(--surface-3-text));\n border-radius: var(--list-radius, var(--radius-s));\n border: 1px solid var(--list-border, var(--border));\n width: 100%;\n height: 100%;\n overflow: hidden;\n box-sizing: border-box;\n outline: none;\n --ixfx-list-item-padding: var(--ixfx-list-item-padding, var(--list-item-padding, 4px var(--space-m, 8px)));\n }\n\n :host(:focus-visible) {\n outline: 2px solid var(--accent);\n outline-offset: 2px;\n }\n\n .container {\n position: relative;\n width: 100%;\n height: 100%;\n }\n\n ul {\n list-style: none;\n margin: 0;\n padding: 0;\n width: 100%;\n height: 100%;\n overflow-x: hidden;\n overflow-y: auto;\n box-sizing: border-box;\n /* Scrollbar theming: end users override these CSS variables to control\n the native scrollbar (scrollbar-width, scrollbar-gutter, scrollbar-color). */\n scrollbar-width: var(--list-scrollbar-width, auto);\n scrollbar-gutter: var(--list-scrollbar-gutter, auto);\n scrollbar-color: var(--list-scrollbar-color, auto);\n }\n\n ::slotted([hidden]) {\n display: none !important;\n }\n\n ::slotted(li) {\n display: flex;\n align-items: center;\n padding: var(--ixfx-list-item-padding);\n /* Inset each row from the scroll container's edges (margin, not padding:\n padding is inside the border box, so the tickle/selection outlines\n would still sit flush against the edge and be clipped). */\n margin: 0 var(--list-gutter, 4px);\n min-height: var(--list-item-height, 28px);\n cursor: pointer;\n user-select: none;\n transition: background var(--transition, 0.1s ease-out),\n outline-color var(--transition, 0.1s ease-out);\n box-sizing: border-box;\n outline: 1px solid transparent;\n }\n\n /* Custom shadow-DOM components handle their own internal layout/padding */\n ::slotted([data-list-item]) {\n display: block;\n margin: 0 var(--list-gutter, 4px);\n cursor: pointer;\n user-select: none;\n transition: background var(--transition, 0.1s ease-out),\n outline-color var(--transition, 0.1s ease-out);\n outline: 1px solid transparent;\n }\n\n /* In checked mode, only the checkbox column is clickable; the row body\n gets a default cursor so it doesn't suggest otherwise. */\n :host([interaction-mode=\"checked\"]) ::slotted(li),\n :host([interaction-mode=\"checked\"]) ::slotted([data-list-item]) {\n cursor: default;\n }\n\n /* Wrapper inserted around custom [data-list-item] elements in checked mode.\n Provides the flex layout so the injected checkbox can stretch to the\n row's full height via align-self:stretch. */\n .ixfx-custom-row {\n display: flex;\n align-items: center;\n min-height: var(--list-item-height, 28px);\n box-sizing: border-box;\n cursor: default;\n user-select: none;\n }\n\n ::slotted([data-tickled]) {\n background: var(--item-bg-tickled);\n outline: var(--item-border-tickled);\n }\n\n ::slotted([data-selected]) {\n background: var(--item-bg-selected);\n color: var(--item-text-selected);\n outline: 1px solid transparent;\n }\n\n ::slotted([data-selected][data-tickled]) {\n background: var(--item-bg-selected-tickled);\n outline: 2px dashed var(--surface-3);\n outline-offset: -2px;\n }\n\n /* Drag-select (marquee) rectangle + covered-row preview.\n Style hooks: part=\"marquee\", --vertical-list-marquee-border,\n --vertical-list-marquee-fill. */\n .marquee {\n position: absolute;\n pointer-events: none;\n z-index: 5;\n border: 1px solid var(--vertical-list-marquee-border, var(--accent));\n background: var(--vertical-list-marquee-fill, color-mix(in srgb, var(--accent) 15%, transparent));\n }\n\n ::slotted([data-marquee]) {\n background: var(--item-bg-selected);\n color: var(--item-text-selected);\n }\n\n /* checked mode: checkbox (injected into li) communicates selection — suppress bg highlight */\n :host([interaction-mode=\"checked\"]) ::slotted([data-selected]) {\n background: transparent;\n color: inherit;\n outline: 1px solid transparent;\n }\n\n :host([interaction-mode=\"checked\"]) ::slotted([data-selected][data-tickled]) {\n background: var(--item-bg-tickled);\n color: inherit;\n outline: var(--item-border-tickled);\n }\n `];\n\n // --- Render ---\n\n override render() {\n return html`\n <div class=\"container\">\n <ul part=\"list\">\n <slot></slot>\n </ul>\n ${this.#marqueeRect\n ? html`<div class=\"marquee\" part=\"marquee\" style=${this.#marqueeStyle(this.#marqueeRect)}></div>`\n : ``}\n ${this.search.renderField()}\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'ixfx-vertical-list': VerticalListElement;\n }\n\n interface HTMLElementEventMap extends ListElementEventMap {}\n}\n"],"mappings":";;;;;;;;;;AA4DA,MAAM,gBAAgB;AAGf,IAAM,sBAAN,MAAM,4BAA4B,WAAW;;;EAEf,KAAA,gBAAA;EAGI,KAAA,kBAAA;EAWX,KAAA,WAAA;EA+BV,KAAA,kCAAA,IAAI,IAA4B;EAC/B,KAAA,mCAAA,IAAI,IAAa;EAKnB,KAAA,iCAAA,IAAI,QAAiB;EAET,KAAA,YAAA,IAAI,wBAAiC;GAChE,uBAAuB,KAAK,iBAAiB;GAC7C,eAAc,OAAM,KAAK,aAAa,EAAE;GACxC,iBAAiB;IAAE,eAAe,KAAK;IAAe,iBAAiB,KAAK;GAAgB;GAC5F,QAAQ;GACR,WAAW,UAAU,aAAa;IAChC,KAAK,gBAAgB;IACrB,KAAK,UAA4B,eAAe;KAC9C;KACA;KACA,cAAc,KAAK,UAAU;IAC/B,CAAC;GACH;EACF,CAAC;EAEiB,KAAA,UAAA,IAAI,kBAA2B,MAAM;GACrD,gBAAgB,KAAK,iBAAiB,CAAC,CAAC,KAAI,QAAO;IACjD,IAAI;IACJ,SAAS,CAAC,GAAG,aAAa,aAAa;GACzC,EAAE;GACF,WAAW,OAAO;IAChB,IAAI,OAAO,KAAA,GACT,KAAK,UAA4B,eAAe,EAAE,MAAM,GAAG,CAAC;GAEhE;GACA,qBAAqB,KAAK,gBAAgB;GAC1C,UAAU,KAAK;GACf,qBAAqB,KAAKA,YAAY;EACxC,CAAC;EAKsB,KAAA,cAAA,IAAI,yBAAkC,MAAM;GACjE,kBAAkB,KAAK,WAAW,cAAc,YAAY;GAC5D,iBACE,KAAK,UAAU,cAAc,KAC1B,KAAK,iBAAiB,CAAC,CAAC,SAAS,KACjC,CAAC,KAAK,aAAa,qBAAqB;GAC7C,UAAS,SAAQ,KAAKC,gBAAgB,IAAI;GAC1C,wBAAwB,KAAK,UAAU;GACvC,YAAY,SAAS,SAAS;IAC5B,KAAKC,eAAe;IACpB,KAAK,MAAM,MAAM,KAAK,iBAAiB,GACrC,GAAG,gBAAgB,gBAAgB,QAAQ,IAAI,EAAE,CAAC;IACpD,KAAK,cAAc;GACrB;GACA,WAAW,aAAa;IACtB,KAAK,UAAU,mBAAmB,QAAQ;IAC1C,KAAK,MAAM,MAAM,KAAK,iBAAiB,GACrC,GAAG,gBAAgB,cAAc;IACnC,KAAKA,eAAe,KAAA;IACpB,KAAK,cAAc;GACrB;GACA,aAAa,KAAK,OAAO;IACvB,MAAM,OAAO,KAAK,WAAW,cAAc,IAAI;IAC/C,IAAI,MACF,KAAK,aAAa;GACtB;EACF,CAAC;EA4BiB,KAAA,SAAA,IAAI,qBAA8B,MAAM,EACxD,SAAS,+BAA+B,MAAM;GAC5C,cAAc;GACd,WAAU,OAAM,GAAG,aAAa,mBAAmB,KAAK,GAAG,aAAa,KAAK,KAAK;GAClF,cAAc;EAChB,CAAC,EACH,CAAC;EAgeyB,KAAA,kBAAA,MAA0B;GAClD,MAAM,OAAO,KAAK,cAAc,EAAE,aAAa,CAAC;GAChD,IAAI,MACF,KAAK,QAAQ,mBAAmB,IAAI;EACxC;EAE2B,KAAA,mBAAA,MAA0B;GACnD,KAAK,QAAQ,mBAAmB,CAAC;EACnC;EAEsB,KAAA,cAAA,MAA2B;GAC/C,IAAI,KAAK,OAAO,QACd;GACF,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,KAAK;IAC7C,IAAI,KAAK,UAAU,aAAa,GAAG;KACjC,EAAE,eAAe;KACjB,KAAK,UAAU,UAAU;IAC3B;IACA;GACF;GAEA,QAAQ,EAAE,KAAV;IACE,KAAK;KACH,EAAE,eAAe;KACjB,KAAK,QAAQ,yBAAyB,MAAM;KAE5C,IAAI,EAAE,YAAY,KAAK,oBAAoB,UAAU;MACnD,MAAM,MAAM,KAAK,QAAQ;MACzB,IAAI,KACF,KAAK,UAAU,QAAQ,GAAG;KAC9B;KACA;IAEF,KAAK;KACH,EAAE,eAAe;KACjB,KAAK,QAAQ,yBAAyB,UAAU;KAChD,IAAI,EAAE,YAAY,KAAK,oBAAoB,UAAU;MACnD,MAAM,MAAM,KAAK,QAAQ;MACzB,IAAI,KACF,KAAK,UAAU,QAAQ,GAAG;KAC9B;KACA;IAEF,KAAK,SAAS;KACZ,MAAM,KAAK,KAAK,QAAQ;KACxB,IAAI,IAAI;MACN,EAAE,eAAe;MACjB,KAAK,UAA+B,mBAAmB,EAAE,MAAM,GAAG,CAAC;MACnE,KAAK,cAAc,EAAE;KACvB;KACA;IACF;IACA,KAAK,KAAK;KACR,MAAM,KAAK,KAAK,QAAQ;KACxB,IAAI,IAAI;MACN,EAAE,eAAe;MACjB,KAAK,UAAU,YAAY,EAAE;KAC/B;KACA;IACF;IACA,KAAK,UACH,KAAK,UAAU,MAAM;GAEzB;EACF;EAEoB,KAAA,YAAA,MAAwB;GAC1C,IAAI,KAAKF,YAAY,YACnB;GACF,MAAM,OAAO,KAAK,cAAc,EAAE,aAAa,CAAC;GAChD,IAAI,CAAC,MACH;GACF,KAAK,UAA+B,mBAAmB,EAAE,KAAK,CAAC;GAC/D,KAAK,UAAU,YAAY,MAAM,CAAC;EACpC;EAEuB,KAAA,eAAA,MAAwB;GAC7C,MAAM,OAAO,KAAK,cAAc,EAAE,aAAa,CAAC;GAChD,IAAI,MACF,KAAK,cAAc,IAAI;EAC3B;;CArpBA;CACA;;CAGA;;CAEA;;CAiCA;CAEA;;CA6BA,gBAAgB,MAAiC;EAC/C,MAAM,UAAU,KAAK,WAAW,cAAc,YAAY;EAC1D,IAAI,CAAC,SACH,OAAO,CAAC;EACV,MAAM,MAAM,QAAQ,sBAAsB;EAC1C,MAAM,MAAM,KAAK;EACjB,MAAM,MAAM,KAAK;EACjB,MAAM,MAAM,KAAK,IAAI,KAAK;EAC1B,MAAM,MAAM,KAAK,IAAI,KAAK;EAC1B,MAAM,MAAiB,CAAC;EACxB,KAAK,MAAM,MAAM,KAAK,iBAAiB,GAAG;GACxC,MAAM,IAAI,GAAG,sBAAsB;GACnC,MAAM,OAAO,EAAE,OAAO,IAAI;GAC1B,MAAM,MAAM,EAAE,MAAM,IAAI;GACxB,IAAI,QAAQ,OAAO,OAAO,EAAE,SAAS,OAAO,OAAO,OAAO,MAAM,EAAE,UAAU,KAC1E,IAAI,KAAK,EAAE;EACf;EACA,OAAO;CACT;CAEA,cAAc,MAA8B;EAC1C,OAAO,QAAQ,KAAK,EAAE,SAAS,KAAK,EAAE,WAAW,KAAK,IAAI,GAAG,KAAK,KAAK,EAAE,YAAY,KAAK,IAAI,GAAG,KAAK,MAAM,EAAE;CAChH;CAWA;CACA;;CAGA,IAAI,WAAiD;EACnD,OAAO,KAAKG;CACd;CAEA,wBAA0C;EACxC,MAAM,MAAM,KAAK,aAAa,KAAK;EACnC,IAAI,QAAQ,KAAA,GACV,OAAO,CAAC,GAAG,iBAAiB;EAC9B,MAAM,YAAY,IAAI,IAAI,IAAI,MAAM,KAAK,CAAC;EAC1C,OAAO,kBAAkB,QAAO,MAAK,UAAU,IAAI,CAAC,CAAC;CACvD;CAEA,sBAA4B;EAC1B,IAAI,CAAC,KAAKA,iBACR;EACF,KAAK,OAAO,SAAS,KAAKC,sBAAsB,CAAC;EACjD,KAAKC,4BAA4B;EACjC,KAAKA,4BAA4B,2BAA2B,KAAKF,iBAAiB,KAAK,MAAM;CAC/F;CAEA,oBAAmC;EACjC,MAAM,kBAAkB;EACxB,IAAI,CAAC,KAAK,aAAa,UAAU,GAC/B,KAAK,WAAW;EAClB,KAAK,iBAAiB,eAAe,KAAK,cAAc;EACxD,KAAK,iBAAiB,gBAAgB,KAAK,eAAe;EAC1D,KAAK,iBAAiB,WAAW,KAAK,UAAU;EAChD,KAAK,iBAAiB,SAAS,KAAK,QAAQ;EAC5C,KAAK,iBAAiB,YAAY,KAAK,WAAW;EAGlD,KAAK,MAAM,MAAM,KAAK,iBAAiB,aAAa,GAClD,KAAKG,eAAe,IAAI,EAAE;EAC5B,KAAKC,kBAAkB,IAAI,kBAAiB,YAAW,KAAKC,gBAAgB,OAAO,CAAC;EACpF,KAAKD,cAAc,QAAQ,MAAM,EAAE,WAAW,KAAK,CAAC;EACpD,KAAKJ,oBAAoB,sBAAsB;EAC/C,KAAKM,oBAAoB;CAC3B;CAEA,uBAAsC;EACpC,MAAM,qBAAqB;EAC3B,KAAK,oBAAoB,eAAe,KAAK,cAAc;EAC3D,KAAK,oBAAoB,gBAAgB,KAAK,eAAe;EAC7D,KAAK,oBAAoB,WAAW,KAAK,UAAU;EACnD,KAAK,oBAAoB,SAAS,KAAK,QAAQ;EAC/C,KAAK,oBAAoB,YAAY,KAAK,WAAW;EACrD,KAAKF,eAAe,WAAW;EAC/B,KAAKF,4BAA4B;EACjC,KAAKA,4BAA4B,KAAA;EACjC,KAAKF,kBAAkB,KAAA;CACzB;;;;;;;CAQA,gBAAgB,SAAiC;EAC/C,IAAI,CAAC,KAAK,aACR;EACF,MAAM,QAAuB,CAAC;EAC9B,KAAK,MAAM,UAAU,SACnB,KAAK,MAAM,QAAQ,OAAO,YAAY;GACpC,IAAI,EAAE,gBAAgB,gBAAgB,KAAK,kBAAkB,MAC3D;GACF,IAAI,CAAC,KAAK,QAAQ,aAAa,GAC7B;GACF,IAAI,KAAKG,eAAe,IAAI,IAAI,KAAK,KAAKI,gBAAgB,IAAI,IAAqB,KAAK,KAAKC,iBAAiB,IAAI,IAAI,GACpH;GACF,KAAKL,eAAe,IAAI,IAAI;GAC5B,MAAM,KAAK,IAAI;EACjB;EAEF,IAAI,MAAM,WAAW,GACnB;EACF,MAAM,QAAQ,KAAK,iBAAiB,aAAa,CAAC,CAAC;EACnD,IAAI,MAAM,UAAU,OAClB;EACF,KAAK,MAAM,MAAM,OACf,kBAAkB,EAAE;CACxB;CAEA,QAAiB,SAA+B;EAC9C,MAAM,QAAQ,OAAO;EACrB,IAAI,QAAQ,IAAI,aAAa,GAC3B,KAAKG,oBAAoB;EAE3B,IAAI,QAAQ,IAAI,iBAAiB,GAAG;GAClC,KAAK,UAAU,YAAY;GAC3B,MAAM,mBAAmB,KAAK,UAAU;GAGxC,MAAM,gBADkB,QAAQ,IAAI,iBAAiB,MAA8B,YAC5C,IAAI,IAAI,KAAKC,eAAe,IAAI;GAEvE,KAAK,gBAAgB;GAGrB,IAAI,iBAAiB,OAAO,GAAG;IAC7B,IAAI,iBAAiB,cAAc,OAAO,GAExC,KAAK,UAAU,OAAM,OAAM,cAAc,IAAI,EAAmB,KAAK,EAAE;SAClE,IAAI,KAAKA,gBAAgB,OAAO,GAAG;KAExC,MAAM,iBAAiB,IAAI,IAAI,CAAC,GAAG,KAAKA,eAAe,CAAC,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;KAChF,KAAK,UAAU,OAAM,OAAM,eAAe,IAAI,EAAE,KAAK,EAAE;IACzD;GACF;GAEA,KAAK,gBAAgB;EACvB,OAAO,IAAI,QAAQ,IAAI,kBAAkB,KAAK,KAAK,oBAAoB,WACrE,KAAK,gBAAgB;CAEzB;;CAKA,IAAI,eAAoC;EACtC,OAAO,CAAC,GAAG,KAAK,UAAU,QAAQ,CAAC,CAAC,GAAG,EAAE;CAC3C;;CAGA,IAAI,gBAAsC;EACxC,OAAO,KAAK,UAAU;CACxB;CAEA,QAAQ,MAAwB,SAAgC;EAC9D,IAAI;EACJ,IAAI,OAAO,SAAS,UAAU;GAC5B,MAAM,KAAK,SAAS,cAAc,IAAI;GACtC,GAAG,cAAc;GACjB,KAAK;EACP,OACE,KAAK;EAEP,IAAI,SAAS,YACX,GAAG,aAAa,eAAe,EAAE;EACnC,KAAK,YAAY,EAAE;EACnB,IAAI,KAAK,oBAAoB,WAC3B,KAAK,gBAAgB;CACzB;;;;;;CAOA,WAAW,MAA8B;EACvC,KAAK,UAAU,SAAS,IAAI;EAM5B,OALa,mBAAmB,IAAmB,CAAC,CAAC,WAAW;GAC9D,KAAK,OAAO;GACZ,IAAI,KAAK,oBAAoB,WAC3B,KAAK,gBAAgB;EACzB,CACU;CACZ;;CAGA,YAAY,OAAyC;EACnD,OAAO,QAAQ,IAAI,CAAC,GAAG,KAAK,CAAC,CAAC,KAAI,SAAQ,KAAK,WAAW,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,KAAA,CAAS;CACxF;CAEA,aAAmB;EACjB,KAAKA,gBAAgB,MAAM;EAC3B,KAAK,gBAAgB;EACrB,KAAK,UAAU,MAAM;EACrB,KAAK,gBAAgB;CACvB;;;;;;CAOA,SAAS,OAAiC;EACxC,KAAK,WAAW;EAChB,KAAK,MAAM,QAAQ,OACjB,KAAK,YAAY,IAAI;EAEvB,IAAI,KAAK,oBAAoB,WAC3B,KAAK,gBAAgB;CACzB;;;;;;CAOA,SAAS,WAAsB,MAAsC;EACnE,KAAKP,iBAAiB,OAAO,WAAW,IAAI;CAC9C;CAEA,OAAO,MAAqB;EAC1B,KAAK,UAAU,QAAQ,IAAI;CAC7B;CAEA,WAAW,OAAgC;EACzC,KAAK,UAAU,WAAW,KAAK;CACjC;CAEA,SAAS,MAAqB;EAC5B,KAAK,UAAU,SAAS,IAAI;CAC9B;CAEA,iBAAuB;EACrB,KAAK,UAAU,MAAM;CACvB;CAEA,YAAkB;EAChB,IAAI,KAAK,UAAU,aAAa,GAC9B,KAAK,UAAU,UAAU;CAC7B;;CAKA,IAAI,eAAoC;EACtC,OAAO,KAAK,UAAU;CACxB;;CAGA,IAAI,aAAiC;EACnC,MAAM,KAAK,KAAK,QAAQ;EACxB,OAAO,KAAK,cAAc,EAAE,IAAI,KAAA;CAClC;;CAGA,WAAW,MAA8B;EACvC,KAAK,UAAU,WAAW,OAAM,QAAO,KAAKS,YAAY,GAAG,CAAC;CAC9D;;CAGA,gBAAgB,MAA8B;EAC5C,KAAK,UAAU,QAAQ,OAAM,QAAO,KAAKA,YAAY,GAAG,CAAC;CAC3D;;CAGA,mBAAmB,MAA8B;EAC/C,KAAK,UAAU,WAAW,OAAM,QAAO,KAAKA,YAAY,GAAG,CAAC;CAC9D;;CAGA,SAAS,KAAmB;EAC1B,MAAM,KAAK,KAAKA,YAAY,GAAG;EAC/B,IAAI,CAAC,IACH;EACF,KAAK,QAAQ,MAAM,EAAE;EACrB,GAAG,eAAe;GAAE,OAAO;GAAW,QAAQ;EAAU,CAAC;CAC3D;CAEA,YAAY,KAAkC;EAC5C,KAAK,MAAM,MAAM,KAAK,iBAAiB,aAAa,GAAG;GACrD,IAAI,KAAKD,iBAAiB,IAAI,EAAE,GAC9B;GACF,IAAI,cAAc,EAAE,MAAM,KACxB,OAAO;EACX;CAEF;CAEA,CAAE,eAAe,MAA6D;EAC5E,MAAM,UAAU,OAAgB;GAC9B,QAAQ,MAAR;IACE,KAAK,YAAY,OAAO,GAAG,aAAa,eAAe;IACvD,KAAK,WAAW,OAAO,GAAG,aAAa,cAAc;IACrD,KAAK,UAAU,OAAO,GAAG,aAAa,eAAe,KAAK,GAAG,aAAa,cAAc;GAC1F;EACF;EAEA,KAAK,MAAM,MAAM,KAAK,iBAAiB,aAAa,GAAG;GACrD,IAAI,KAAKA,iBAAiB,IAAI,EAAE,GAC9B;GACF,IAAK,GAAmB,QACtB;GACF,IAAI,OAAO,EAAE,GACX,MAAM;EACV;CACF;CAIA,mBAAsC;EACpC,OAAO,MAAM,KAAK,KAAK,iBAAiB,aAAa,CAAC,CAAC,CAAC,QAAQ,OAAO;GACrE,IAAI,KAAKA,iBAAiB,IAAI,EAAE,GAC9B,OAAO;GACT,IAAK,GAAmB,QACtB,OAAO;GACT,IAAI,KAAK,mBAAmB,CAAC,KAAK,gBAAgB,EAAE,GAClD,OAAO;GACT,OAAO;EACT,CAAC;CACH;CAMA,aAAqB,MAAwB;EAC3C,IAAI,KAAK,oBAAoB,WAC3B,OAAO;EAET,OAAO,EADM,KAAKD,gBAAgB,IAAI,IAAqB,KAAK,KAAA,CACnD,aAAa,aAAa;CACzC;CAEA,kBAAgC;EAC9B,KAAK,MAAM,MAAM,KAAK,iBAAiB,aAAa,GAAG;GACrD,IAAI,KAAKC,iBAAiB,IAAI,EAAE,GAC9B;GACF,MAAM,OAAO,KAAKD,gBAAgB,IAAI,EAAmB,KAAK;GAC9D,GAAG,gBAAgB,gBAAgB,KAAK,QAAQ,UAAU,EAAE,CAAC;GAC7D,GAAG,gBAAgB,iBAAiB,KAAK,UAAU,SAAS,IAAI,EAAE,CAAC;GACnE,IAAI,KAAK,oBAAoB,WAC3B,GAAG,gBAAgB,gBAAgB,CAAC,KAAK,aAAa,aAAa,KAAK,KAAK,UAAU,SAAS,IAAI,EAAE,CAAC;QAEvG,GAAG,gBAAgB,cAAc;EAErC;EACA,KAAK,QAAQ,YAAY,KAAK,QAAQ;EACtC,KAAK,gBAAgB;CACvB;CAEA,kBAAgC;EAC9B,MAAM,gBAAgB,KAAK,oBAAoB;EAC/C,MAAM,cAAc,OAAO,KAAK,qBAAqB;EAErD,IAAI,eACF,KAAK,MAAM,SAAS,MAAM,KAAK,KAAK,QAAQ,GAAG;GAC7C,IAAI,CAAC,MAAM,aAAa,gBAAgB,GACtC;GACF,IAAI,MAAM,kBAAkB,MAC1B;GACF,MAAM,UAAU,SAAS,cAAc,IAAI;GAC3C,QAAQ,YAAY;GACpB,MAAM,MAAM,MAAM,aAAa,UAAU;GACzC,MAAM,QAAQ,MAAM,aAAa,mBAAmB;GACpD,IAAI,QAAQ,MACV,QAAQ,aAAa,YAAY,GAAG;GACtC,IAAI,UAAU,MACZ,QAAQ,aAAa,qBAAqB,KAAK;GACjD,MAAM,gBAAgB,gBAAgB;GACtC,KAAK,aAAa,SAAS,KAAK;GAChC,QAAQ,YAAY,KAAK;GACzB,KAAKA,gBAAgB,IAAI,SAAS,KAAK;GACvC,KAAKC,iBAAiB,IAAI,KAAK;EACjC;OACK;GACL,KAAK,MAAM,CAAC,SAAS,UAAU,KAAKD,iBAAiB;IACnD,IAAI,QAAQ,kBAAkB,MAC5B;IACF,MAAM,aAAa,kBAAkB,EAAE;IACvC,KAAK,aAAa,OAAO,OAAO;IAChC,QAAQ,OAAO;GACjB;GACA,KAAKA,gBAAgB,MAAM;GAC3B,KAAKC,iBAAiB,MAAM;EAC9B;EAEA,KAAK,MAAM,MAAM,KAAK,iBAA8B,IAAI,GAAG;GACzD,MAAM,WAAW,GAAG,cAA2B,qBAAqB;GACpE,IAAI,CAAC,eAAe;IAClB,IAAI,UACF,SAAS,OAAO;IAClB;GACF;GAGA,KADe,KAAKD,gBAAgB,IAAI,EAAmB,KAAK,GAAA,CACrD,aAAa,aAAa,GAAG;IACtC,IAAI,UACF,SAAS,OAAO;IAClB;GACF;GAGA,MAAM,mBAAmB,UAAU,UAAU,SAAS,2BAA2B,KAAK;GACtF,IAAI,YAAY,qBAAqB,aACnC,SAAS,OAAO;GAElB,IAAI,KAAyB,GAAG,cAA2B,qBAAqB;GAChF,IAAI,CAAC,IAAI;IACP,IAAI,aAAa;KACf,MAAM,OAAO,KAAK;KAClB,MAAM,SAAS,KAAK,UAAU;KAC9B,MAAM,QAAQ,KAAK,SAAS;KAC5B,KAAK,KAAK,sBAAsB,IAAI,QAAQ,KAAK;IACnD,OACE,KAAK,KAAK,0BAA0B,EAAE;IAExC,GAAG,QAAQ,EAAE;GACf;GAEA,MAAM,UAAU,KAAK,UAAU,SAAS,IAAI,EAAE;GAC9C,IAAI,aAAa;IACf,GAAG,aAAa,gBAAgB,OAAO,OAAO,CAAC;IAC/C,GAAG,gBAAgB,gBAAgB,OAAO;IAC1C,IAAI,KAAK,kBAAkB;KACzB,MAAM,WAAW,OAAO,KAAK,qBAAqB,aAC9C,KAAK,mBACJ,KAAK,iBAA2C;KACrD,OAAO,SAAS,OAAO,GAAG,EAAE;IAC9B;GACF,OAAO;IACL,MAAM,QAAQ,GAAG,cAAgC,0BAA0B;IAC3E,IAAI,OACF,MAAM,UAAU;GACpB;EACF;CACF;;;;;;;CAQA,0BAAkC,IAAkC;EAClE,MAAM,OAAO,SAAS,cAAc,MAAM;EAC1C,KAAK,YAAY;EACjB,KAAK,MAAM,UAAU;EACrB,KAAK,WAAW;EAChB,KAAK,aAAa,QAAQ,UAAU;EACpC,KAAK,aAAa,QAAQ,UAAU;EACpC,KAAK,iBAAiB,UAAU,MAAM;GACpC,EAAE,gBAAgB;GAClB,KAAK,UAAU,OAAO,EAAE;EAC1B,CAAC;EACD,MAAM,QAAQ,SAAS,cAAc,OAAO;EAC5C,MAAM,OAAO;EACb,MAAM,YAAY;EAClB,MAAM,MAAM,UAAU;EACtB,KAAK,YAAY,KAAK;EACtB,OAAO;CACT;;;;;CAMA,sBAA8B,IAAiB,QAAgB,OAAgC;EAC7F,MAAM,OAAO,SAAS,cAAc,MAAM;EAC1C,KAAK,YAAY;EACjB,KAAK,MAAM,UAAU,wGAAwG,MAAM,UAAU,OAAO;EACpJ,KAAK,WAAW;EAChB,KAAK,aAAa,QAAQ,UAAU;EACpC,KAAK,aAAa,QAAQ,UAAU;EACpC,KAAK,iBAAiB,UAAU,MAAM;GACpC,EAAE,gBAAgB;GAClB,KAAK,UAAU,OAAO,EAAE;EAC1B,CAAC;EACD,OAAO;CACT;CAEA,cAAsB,MAA0C;EAC9D,KAAK,MAAM,UAAU,MAAM;GACzB,IAAI,EAAE,kBAAkB,UACtB;GACF,IAAI,WAAW,MACb;GACF,IAAI,OAAO,kBAAkB,QAAQ,OAAO,QAAQ,aAAa,GAC/D,OAAO;EACX;CAEF;CAEA,UAAqB,MAAiC,QAAiB;EACrE,KAAK,cAAc,IAAI,YAAe,MAAgB;GACpD;GACA,SAAS;GACT,UAAU;EACZ,CAAC,CAAC;CACJ;CAsFA,cAAsB,MAAqB;EAEzC,IAAI,KAAK,oBAAoB,UAC3B,KAAK,UAAU,QAAQ,IAAI;EAE7B,KAAK,UAA8B,iBAAiB,EAAE,KAAK,CAAC;CAC9D;;EAIyB,KAAA,SAAA;GAAC;GAAgB;GAAkB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA2I9D;;CAID,SAAkB;EAChB,OAAO,IAAI;;;;;UAKL,KAAKR,eACH,IAAI,6CAA6C,KAAKW,cAAc,KAAKX,YAAY,EAAE,WACvF,GAAG;UACL,KAAK,OAAO,YAAY,EAAE;;;CAGlC;AACF;AA32BG,WAAA,CAAA,SAAS;CAAE,MAAM;CAAQ,WAAW;AAAiB,CAAC,CAAA,GAAA,oBAAA,WAAA,iBAAA,KAAA,CAAA;AAGtD,WAAA,CAAA,SAAS;CAAE,MAAM;CAAQ,WAAW;AAAmB,CAAC,CAAA,GAAA,oBAAA,WAAA,mBAAA,KAAA,CAAA;AAGxD,WAAA,CAAA,SAAS,EAAE,WAAW,MAAM,CAAC,CAAA,GAAA,oBAAA,WAAA,mBAAA,KAAA,CAAA;AAQ7B,WAAA,CAAA,SAAS;CAAE,MAAM;CAAQ,WAAW;AAAY,CAAC,CAAA,GAAA,oBAAA,WAAA,YAAA,KAAA,CAAA;AAqBjD,WAAA,CAAA,SAAS,EAAE,WAAW,MAAM,CAAC,CAAA,GAAA,oBAAA,WAAA,oBAAA,KAAA,CAAA;AAQ7B,WAAA,CAAA,SAAS;CAAE,MAAM;CAAQ,WAAW;CAAgB,SAAS;AAAK,CAAC,CAAA,GAAA,oBAAA,WAAA,eAAA,KAAA,CAAA;AA7CrEY,sBAAAA,WAAAA,CAAAA,kBAAc,oBAAoB,CAAA,GAAA,mBAAA"}
1
+ {"version":3,"file":"vertical-list.js","names":["#dragSelect","#nativeDrag","#wrappedElements","#prepareNativeDrag","#nativeDragging","#startPostDragSuppressTimer","#marqueeHitTest","#marqueeRect","#postDragSuppressTimer","#searchRegistry","#effectiveSearchModes","#unregisterSearchCommands","#animatedItems","#itemObserver","#onItemsMutated","#syncSearchCommands","#customWrappers","#itemForKey","#marqueeStyle","customElement"],"sources":["../src/vertical-list/vertical-list.ts"],"sourcesContent":["import type { CommandId, CommandRegistry } from '@clinth/ui-commands';\nimport type { PropertyValues } from 'lit';\nimport type { ListSearchMode } from '../incr-search/index.js';\nimport type { DragSelectRect } from '../interaction/list-drag-select-controller.js';\nimport type { ListDragEntry, ListDragPreviewRenderer, ListNativeDragStartEvent } from '../interaction/list-drag-types.js';\nimport type {\n ListActivateDetail,\n ListElementEventMap,\n ListInteractionMode,\n ListItemClickDetail,\n ListSelectDetail,\n ListSelectionMode,\n ListTickleDetail,\n} from '../interaction/list-selection-types.js';\nimport { createCommandRegistry } from '@clinth/ui-commands';\nimport { css, html, LitElement, render } from 'lit';\nimport { property } from 'lit/decorators.js';\nimport {\n createElementListSearchAdapter,\n LIST_SEARCH_MODES,\n ListSearchController,\n listSearchStyles,\n registerListSearchCommands,\n} from '../incr-search/index.js';\nimport { keyForElement } from '../interaction/keyed-list.js';\nimport { ListDragSelectController } from '../interaction/list-drag-select-controller.js';\nimport { animateListItemIn, animateListItemOut } from '../interaction/list-item-animation.js';\nimport { ListNativeDragController } from '../interaction/list-native-drag-controller.js';\nimport { ListSelectionController } from '../interaction/list-selection-controller.js';\nimport { TickledController } from '../interaction/tickled-controller.js';\nimport { themeFallbacks } from '../styles/fallbacks.js';\nimport { safeCustomElement as customElement } from '../util/custom-elements.js';\n\nexport type {\n ListDragEntry,\n ListDragPreview,\n ListDragPreviewContext,\n ListDragPreviewRenderer,\n ListDragSource,\n ListNativeDragStartDetail,\n ListNativeDragStartEvent,\n} from '../interaction/list-drag-types.js';\nexport type {\n ListActivateDetail,\n ListActivateEvent,\n ListElementEventMap,\n ListInteractionMode,\n ListItemClickDetail,\n ListItemClickEvent,\n ListSelectDetail,\n ListSelectEvent,\n ListSelectionMode,\n ListTickleDetail,\n ListTickleEvent,\n} from './types.js';\n\nexport interface CustomCheckboxOptions {\n renderer: (checked: boolean) => unknown;\n margin?: string;\n width?: string;\n}\n\nexport interface AddItemOptions {\n /**\n * In `checked` interaction mode, adds the item without a checkbox column and\n * without the ability to be checked/selected. Equivalent to setting the\n * `not-checked` attribute on the item. Ignored outside `checked` mode.\n */\n notChecked?: boolean;\n}\n\nconst ITEM_SELECTOR = `li, [data-list-item]`;\n\n@customElement(`ixfx-vertical-list`)\nexport class VerticalListElement extends LitElement {\n @property({ type: String, attribute: `selection-mode` })\n selectionMode: ListSelectionMode = `single`;\n\n @property({ type: String, attribute: `interaction-mode` })\n interactionMode: ListInteractionMode = `standard`;\n\n @property({ attribute: false })\n filterPredicate: ((el: Element) => boolean) | undefined;\n\n /**\n * Whether keyboard navigation wraps from last to first and vice versa.\n * - 'stop' (default): navigation stops at the boundary\n * - 'wrap': navigation wraps around\n */\n @property({ type: String, attribute: `item-wrap` })\n itemWrap: `stop` | `wrap` = `stop`;\n\n /**\n * Optional renderer for the checkbox shown in `checked` interactionMode.\n * Receives the current `checked` state and returns a Lit template result.\n *\n * When set, the per-row checkbox becomes a `<span role=\"checkbox\">` whose\n * contents are whatever this returns (rendered with `lit`'s `render()`).\n * When unset, a native `<input type=\"checkbox\">` is used.\n *\n * @example\n * list.checkboxRenderer = (checked) => checked\n * ? html`<svg viewBox=\"0 0 16 16\" width=\"14\" height=\"14\"\n * fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"\n * stroke-linecap=\"round\" stroke-linejoin=\"round\"\n * style=\"opacity:0.5\">\n * <polyline points=\"3,8 7,12 13,4\" />\n * </svg>`\n * : html``;\n */\n @property({ attribute: false })\n checkboxRenderer: ((checked: boolean) => unknown) | CustomCheckboxOptions | undefined;\n\n /**\n * Space-separated subset of `navigate select filter` — limits which\n * incremental-search modes are offered. Unset ⇒ all three; empty ⇒ search\n * disabled. See `docs/infra-incremental.md`.\n */\n @property({ type: String, attribute: `search-modes`, reflect: true })\n searchModes: string | undefined;\n\n /**\n * Enable native browser drag-and-drop for selectable items. When true,\n * selectable rows become draggable and the list emits\n * `list-native-drag-start` when a drag begins.\n */\n @property({ type: Boolean, attribute: `items-draggable`, reflect: true })\n itemsDraggable = false;\n\n /**\n * Custom drag preview renderer (synchronous). Defaults to the shared\n * built-in HTML+canvas preview.\n */\n @property({ attribute: false })\n dragPreviewRenderer: ListDragPreviewRenderer<Element> | undefined;\n\n #customWrappers = new Map<HTMLLIElement, Element>();\n #wrappedElements = new Set<Element>();\n\n /** Watches for items added to the light DOM so they can be animated in. */\n #itemObserver: MutationObserver | undefined;\n /** Items that have already played (or been skipped for) their enter animation. */\n #animatedItems = new WeakSet<Element>();\n\n private readonly selection = new ListSelectionController<Element>({\n getOrderedItems: () => this._getVisibleItems(),\n isSelectable: el => this._isCheckable(el),\n getModes: () => ({ selectionMode: this.selectionMode, interactionMode: this.interactionMode }),\n getKey: keyForElement,\n onChange: (selected, previous) => {\n this._syncItemStates();\n this._dispatch<ListSelectDetail>(`list-select`, {\n selected,\n previous,\n selectedKeys: this.selection.selectedKeys,\n });\n },\n });\n\n private tickled = new TickledController<Element>(this, {\n getItems: () => this._getVisibleItems().map(el => ({\n id: el,\n enabled: !el.hasAttribute(`not-checked`),\n })),\n onTickle: (id) => {\n if (id !== undefined) {\n this._dispatch<ListTickleDetail>(`list-tickle`, { item: id });\n }\n },\n onStateChange: () => this._syncItemStates(),\n itemWrap: this.itemWrap,\n hoverDisabled: () => this.#dragSelect.isDragging || this.#nativeDrag.isDragging,\n });\n\n #nativeDragging = false;\n #postDragSuppressTimer = 0;\n\n readonly #nativeDrag = new ListNativeDragController<Element>(this, {\n source: `vertical-list`,\n isEnabled: () => this.itemsDraggable && this.selectionMode !== `none` && this._getVisibleItems().length > 0,\n resolveEventToKey: (target) => {\n if (!(target instanceof Element))\n return undefined;\n const item = target.closest?.(ITEM_SELECTOR) as Element | null;\n if (!item || !this.contains(item) || this.#wrappedElements.has(item))\n return undefined;\n return keyForElement(item);\n },\n prepareSelection: originKey => this.#prepareNativeDrag(originKey),\n getMetrics: () => ({}),\n getRenderer: () => this.dragPreviewRenderer,\n dispatch: detail => this._dispatchCancelable(`list-native-drag-start`, detail),\n onActiveChange: (active, originKey) => {\n this.#nativeDragging = active;\n this.toggleAttribute(`data-native-dragging`, active);\n for (const el of this._getVisibleItems())\n el.toggleAttribute(`data-drag-origin`, active && originKey !== undefined && keyForElement(el) === originKey);\n if (active) {\n const count = this.selection.selected.size;\n this.setAttribute(`aria-label`, count <= 1 ? `Dragging 1 item` : `Dragging ${count} items`);\n } else {\n this.removeAttribute(`aria-label`);\n }\n this.requestUpdate();\n },\n onEnd: () => {\n this.#nativeDragging = false;\n this.toggleAttribute(`data-native-dragging`, false);\n this.removeAttribute(`aria-label`);\n for (const el of this._getVisibleItems())\n el.removeAttribute(`data-drag-origin`);\n this.#startPostDragSuppressTimer();\n },\n syncLightDomDraggable: () => this._syncNativeDraggable(),\n });\n\n #prepareNativeDrag(originKey: string): { origin: ListDragEntry<Element>; selected: Array<ListDragEntry<Element>> } | undefined {\n const visible = this._getVisibleItems();\n const originEl = visible.find(el => keyForElement(el) === originKey);\n if (!originEl || !this._isCheckable(originEl))\n return undefined;\n if (!this.selection.selected.has(originEl))\n this.selection.replace(originEl);\n const selected: Array<ListDragEntry<Element>> = [];\n visible.forEach((el, index) => {\n if (!this.selection.selected.has(el))\n return;\n selected.push({ key: keyForElement(el) ?? originKey, index, item: el, element: el });\n });\n const origin = selected.find(entry => entry.key === originKey);\n if (!origin)\n return undefined;\n return { origin, selected };\n }\n\n private _syncNativeDraggable(): void {\n if (!this.isConnected)\n return;\n for (const el of this.querySelectorAll(ITEM_SELECTOR)) {\n if (this.#wrappedElements.has(el))\n continue;\n if (el instanceof HTMLElement)\n el.draggable = this.itemsDraggable && this._isCheckable(el);\n }\n }\n\n #startPostDragSuppressTimer(): void {\n if (this.#postDragSuppressTimer)\n clearTimeout(this.#postDragSuppressTimer);\n this.#postDragSuppressTimer = window.setTimeout(() => {\n this.#postDragSuppressTimer = 0;\n }, 0);\n }\n\n /** Live drag-select preview rect (surface-relative); the covered items carry `data-marquee`. */\n #marqueeRect: DragSelectRect | undefined;\n\n readonly #dragSelect = new ListDragSelectController<Element>(this, {\n getSurface: () => this.renderRoot.querySelector(`.container`) as HTMLElement | undefined,\n isEnabled: () =>\n this.selection.canDragSelect()\n && this._getVisibleItems().length > 0\n && !this.hasAttribute(`disable-drag-select`),\n hitTest: rect => this.#marqueeHitTest(rect),\n getBaseSelection: () => this.selection.selected,\n onPreview: (preview, rect) => {\n this.#marqueeRect = rect;\n for (const el of this._getVisibleItems())\n el.toggleAttribute(`data-marquee`, preview.has(el));\n this.requestUpdate();\n },\n onCommit: (finalSet) => {\n this.selection.applyDragSelection(finalSet);\n for (const el of this._getVisibleItems())\n el.removeAttribute(`data-marquee`);\n this.#marqueeRect = undefined;\n this.requestUpdate();\n },\n autoScroll: (_dx, dy) => {\n const list = this.renderRoot.querySelector(`ul`);\n if (list)\n list.scrollTop += dy;\n },\n });\n\n /** Collect visible items whose surface-relative box intersects `rect`. */\n #marqueeHitTest(rect: DragSelectRect): Element[] {\n const surface = this.renderRoot.querySelector(`.container`);\n if (!surface)\n return [];\n const box = surface.getBoundingClientRect();\n const rx0 = rect.x;\n const ry0 = rect.y;\n const rx1 = rect.x + rect.width;\n const ry1 = rect.y + rect.height;\n const out: Element[] = [];\n for (const el of this._getVisibleItems()) {\n const r = el.getBoundingClientRect();\n const left = r.left - box.left;\n const top = r.top - box.top;\n if (left <= rx1 && left + r.width >= rx0 && top <= ry1 && top + r.height >= ry0)\n out.push(el);\n }\n return out;\n }\n\n #marqueeStyle(rect: DragSelectRect): string {\n return `left:${rect.x}px;top:${rect.y}px;width:${Math.max(0, rect.width)}px;height:${Math.max(0, rect.height)}px`;\n }\n\n /** Incremental search (navigate / select / filter). See `docs/infra-incremental.md`. */\n readonly search = new ListSearchController<Element>(this, {\n adapter: createElementListSearchAdapter(this, {\n itemSelector: ITEM_SELECTOR,\n getLabel: el => el.getAttribute(`data-search-label`) ?? el.textContent?.trim() ?? ``,\n highlightKey: `vertical-list-search`,\n }),\n });\n\n #searchRegistry: CommandRegistry<unknown> | undefined;\n #unregisterSearchCommands: (() => void) | undefined;\n\n /** Command registry carrying this list's `list.search.*` commands. */\n get registry(): CommandRegistry<unknown> | undefined {\n return this.#searchRegistry;\n }\n\n #effectiveSearchModes(): ListSearchMode[] {\n const raw = this.searchModes?.trim();\n if (raw === undefined)\n return [...LIST_SEARCH_MODES];\n const requested = new Set(raw.split(/\\s+/));\n return LIST_SEARCH_MODES.filter(m => requested.has(m));\n }\n\n #syncSearchCommands(): void {\n if (!this.#searchRegistry)\n return;\n this.search.setModes(this.#effectiveSearchModes());\n this.#unregisterSearchCommands?.();\n this.#unregisterSearchCommands = registerListSearchCommands(this.#searchRegistry, this.search);\n }\n\n override connectedCallback(): void {\n super.connectedCallback();\n if (!this.hasAttribute(`tabindex`))\n this.tabIndex = 0;\n this.addEventListener(`pointerover`, this._onPointerOver);\n this.addEventListener(`pointerleave`, this._onPointerLeave);\n this.addEventListener(`keydown`, this._onKeyDown);\n this.addEventListener(`click`, this._onClick);\n this.addEventListener(`dblclick`, this._onDblClick);\n // Mark whatever's already here as \"known\" so the first populate doesn't\n // animate; anything added later collapses in.\n for (const el of this.querySelectorAll(ITEM_SELECTOR))\n this.#animatedItems.add(el);\n this.#itemObserver ??= new MutationObserver(records => this.#onItemsMutated(records));\n this.#itemObserver.observe(this, { childList: true });\n this.#searchRegistry ??= createCommandRegistry();\n this.#syncSearchCommands();\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n this.removeEventListener(`pointerover`, this._onPointerOver);\n this.removeEventListener(`pointerleave`, this._onPointerLeave);\n this.removeEventListener(`keydown`, this._onKeyDown);\n this.removeEventListener(`click`, this._onClick);\n this.removeEventListener(`dblclick`, this._onDblClick);\n this.#itemObserver?.disconnect();\n this.#unregisterSearchCommands?.();\n this.#unregisterSearchCommands = undefined;\n this.#searchRegistry = undefined;\n if (this.#postDragSuppressTimer) {\n clearTimeout(this.#postDragSuppressTimer);\n this.#postDragSuppressTimer = 0;\n }\n }\n\n /**\n * Animate in items appended to the light DOM (however they got there —\n * {@link addItem}, direct `appendChild`, a framework binding). A bulk populate\n * (roughly everything at once) is left alone, as are the wrapper `<li>`s the\n * checked-mode plumbing shuffles around.\n */\n #onItemsMutated(records: MutationRecord[]): void {\n if (!this.isConnected)\n return;\n const fresh: HTMLElement[] = [];\n for (const record of records) {\n for (const node of record.addedNodes) {\n if (!(node instanceof HTMLElement) || node.parentElement !== this)\n continue;\n if (!node.matches(ITEM_SELECTOR))\n continue;\n if (this.#animatedItems.has(node) || this.#customWrappers.has(node as HTMLLIElement) || this.#wrappedElements.has(node))\n continue;\n this.#animatedItems.add(node);\n fresh.push(node);\n }\n }\n if (fresh.length === 0)\n return;\n const total = this.querySelectorAll(ITEM_SELECTOR).length;\n if (fresh.length >= total)\n return; // first populate / wholesale replace — don't animate\n for (const el of fresh)\n animateListItemIn(el);\n }\n\n override updated(changed: PropertyValues): void {\n super.updated(changed);\n if (changed.has(`searchModes`)) {\n this.#syncSearchCommands();\n }\n if (changed.has(`interactionMode`)) {\n this.selection.clearAnchor();\n const previousSelected = this.selection.selected;\n // Save wrapper→inner map before _syncCheckboxes() clears it on unwrap\n const leavingChecked = (changed.get(`interactionMode`) as ListInteractionMode) === `checked`;\n const savedWrappers = leavingChecked ? new Map(this.#customWrappers) : null;\n\n this._syncCheckboxes();\n\n // Remap the selection across the wrap/unwrap element-reference boundary\n if (previousSelected.size > 0) {\n if (savedWrappers && savedWrappers.size > 0) {\n // Leaving checked: wrapper refs → inner element refs\n this.selection.remap(el => savedWrappers.get(el as HTMLLIElement) ?? el);\n } else if (this.#customWrappers.size > 0) {\n // Entering checked: inner element refs → wrapper refs\n const innerToWrapper = new Map([...this.#customWrappers].map(([w, i]) => [i, w]));\n this.selection.remap(el => innerToWrapper.get(el) ?? el);\n }\n }\n\n this._syncItemStates();\n } else if (changed.has(`checkboxRenderer`) && this.interactionMode === `checked`) {\n this._syncCheckboxes();\n }\n if (changed.has(`itemsDraggable`) || changed.has(`interactionMode`)) {\n this._syncNativeDraggable();\n this.#nativeDrag.refreshDraggable();\n }\n }\n\n // --- Public API ---\n\n /** The most recently selected item, or `undefined` if nothing is selected. */\n get selectedItem(): Element | undefined {\n return [...this.selection.selected].at(-1);\n }\n\n /** The full current selection set (read-only). */\n get selectedItems(): ReadonlySet<Element> {\n return this.selection.selected;\n }\n\n addItem(item: Element | string, options?: AddItemOptions): void {\n let el: Element;\n if (typeof item === `string`) {\n const li = document.createElement(`li`);\n li.textContent = item;\n el = li;\n } else {\n el = item;\n }\n if (options?.notChecked)\n el.setAttribute(`not-checked`, ``);\n this.appendChild(el);\n if (this.interactionMode === `checked`)\n this._syncCheckboxes();\n }\n\n /**\n * Collapse + fade `item` out, then detach it. The rows below slide up to\n * close the gap. Resolves once the element has actually been removed.\n * Under `prefers-reduced-motion: reduce` it's removed immediately.\n */\n removeItem(item: Element): Promise<void> {\n this.selection.deselect(item);\n const done = animateListItemOut(item as HTMLElement).then(() => {\n item.remove();\n if (this.interactionMode === `checked`)\n this._syncCheckboxes();\n });\n return done;\n }\n\n /** {@link removeItem} for several items at once. */\n removeItems(items: Iterable<Element>): Promise<void> {\n return Promise.all([...items].map(item => this.removeItem(item))).then(() => undefined);\n }\n\n clearItems(): void {\n this.#customWrappers.clear();\n this.replaceChildren();\n this.selection.reset();\n this._syncItemStates();\n }\n\n /**\n * Replace the entire item list in one call.\n * Equivalent to `clearItems()` followed by `addItem()` for each element.\n * Preserves `interactionMode` checkbox wrapping.\n */\n setItems(items: readonly Element[]): void {\n this.clearItems();\n for (const item of items) {\n this.appendChild(item);\n }\n if (this.interactionMode === `checked`)\n this._syncCheckboxes();\n }\n\n /**\n * Dispatch a command to this list — used by `ixfx-grouped-item-lister` to\n * forward commands to all sub-components. Routes through the command registry\n * (so `list.search.*` and any other registered commands work).\n */\n dispatch(commandId: CommandId, args?: Record<string, unknown>): void {\n this.#searchRegistry?.invoke(commandId, args);\n }\n\n select(item: Element): void {\n this.selection.replace(item);\n }\n\n selectMany(items: Iterable<Element>): void {\n this.selection.selectMany(items);\n }\n\n deselect(item: Element): void {\n this.selection.deselect(item);\n }\n\n clearSelection(): void {\n this.selection.clear();\n }\n\n selectAll(): void {\n if (this.selection.canSelectAll())\n this.selection.selectAll();\n }\n\n // --- Key-based selection & cursor ---\n\n /** The selection as stable `data-key` / `data-value` strings. */\n get selectedKeys(): ReadonlySet<string> {\n return this.selection.selectedKeys;\n }\n\n /** The key of the item under the cursor, or `undefined`. */\n get tickledKey(): string | undefined {\n const id = this.tickled.tickledId;\n return id ? keyForElement(id) : undefined;\n }\n\n /** Replace the selection with the items carrying these keys (unknown keys ignored). */\n selectKeys(keys: Iterable<string>): void {\n this.selection.selectKeys(keys, key => this.#itemForKey(key));\n }\n\n /** Add items by key to the selection (multi-select modes only). */\n addSelectedKeys(keys: Iterable<string>): void {\n this.selection.addKeys(keys, key => this.#itemForKey(key));\n }\n\n /** Remove items by key from the selection. */\n removeSelectedKeys(keys: Iterable<string>): void {\n this.selection.removeKeys(keys, key => this.#itemForKey(key));\n }\n\n /** Move the cursor to the item with this key and scroll it into view. */\n focusKey(key: string): void {\n const el = this.#itemForKey(key);\n if (!el)\n return;\n this.tickled.focus(el);\n el.scrollIntoView({ block: `nearest`, inline: `nearest` });\n }\n\n #itemForKey(key: string): Element | undefined {\n for (const el of this.querySelectorAll(ITEM_SELECTOR)) {\n if (this.#wrappedElements.has(el))\n continue;\n if (keyForElement(el) === key)\n return el;\n }\n return undefined;\n }\n\n * selectedFilter(kind: `selected` | `checked` | `either`): Generator<Element> {\n const wanted = (el: Element) => {\n switch (kind) {\n case `selected`: return el.hasAttribute(`data-selected`);\n case `checked`: return el.hasAttribute(`data-checked`);\n case `either`: return el.hasAttribute(`data-selected`) || el.hasAttribute(`data-checked`);\n }\n };\n\n for (const el of this.querySelectorAll(ITEM_SELECTOR)) {\n if (this.#wrappedElements.has(el))\n continue;\n if ((el as HTMLElement).hidden)\n continue;\n if (wanted(el))\n yield el;\n }\n }\n\n // --- Helpers ---\n\n private _getVisibleItems(): Element[] {\n return Array.from(this.querySelectorAll(ITEM_SELECTOR)).filter((el) => {\n if (this.#wrappedElements.has(el))\n return false;\n if ((el as HTMLElement).hidden)\n return false;\n if (this.filterPredicate && !this.filterPredicate(el))\n return false;\n return true;\n });\n }\n\n // Whether an item participates in checked-mode selection. Items carrying the\n // `not-checked` attribute are skipped; outside `checked` mode it has no effect.\n // For wrapped custom elements the flag lives on the inner element, so resolve\n // through the wrapper map.\n private _isCheckable(item: Element): boolean {\n if (this.interactionMode !== `checked`)\n return true;\n const real = this.#customWrappers.get(item as HTMLLIElement) ?? item;\n return !real.hasAttribute(`not-checked`);\n }\n\n private _syncItemStates(): void {\n for (const el of this.querySelectorAll(ITEM_SELECTOR)) {\n if (this.#wrappedElements.has(el))\n continue;\n const item = this.#customWrappers.get(el as HTMLLIElement) ?? el;\n el.toggleAttribute(`data-tickled`, this.tickled.isTickled(el));\n el.toggleAttribute(`data-selected`, this.selection.selected.has(el));\n if (this.interactionMode === `checked`) {\n el.toggleAttribute(`data-checked`, !item.hasAttribute(`not-checked`) && this.selection.selected.has(el));\n } else {\n el.removeAttribute(`data-checked`);\n }\n }\n this.dataset.inputMode = this.tickled.inputMode;\n this._syncCheckboxes();\n }\n\n private _syncCheckboxes(): void {\n const inCheckedMode = this.interactionMode === `checked`;\n const wantsCustom = typeof this.checkboxRenderer === `function`;\n\n if (inCheckedMode) {\n for (const inner of Array.from(this.children)) {\n if (!inner.hasAttribute(`data-list-item`))\n continue;\n if (inner.parentElement !== this)\n continue; // idempotency: already wrapped\n const wrapper = document.createElement(`li`);\n wrapper.className = `ixfx-custom-row`;\n const key = inner.getAttribute(`data-key`);\n const label = inner.getAttribute(`data-search-label`);\n if (key !== null)\n wrapper.setAttribute(`data-key`, key);\n if (label !== null)\n wrapper.setAttribute(`data-search-label`, label);\n inner.removeAttribute(`data-list-item`);\n this.insertBefore(wrapper, inner);\n wrapper.appendChild(inner);\n this.#customWrappers.set(wrapper, inner);\n this.#wrappedElements.add(inner);\n }\n } else {\n for (const [wrapper, inner] of this.#customWrappers) {\n if (wrapper.parentElement !== this)\n continue;\n inner.setAttribute(`data-list-item`, ``);\n this.insertBefore(inner, wrapper);\n wrapper.remove();\n }\n this.#customWrappers.clear();\n this.#wrappedElements.clear();\n }\n\n for (const el of this.querySelectorAll<HTMLElement>(`li`)) {\n const existing = el.querySelector<HTMLElement>(`.ixfx-list-checkbox`);\n if (!inCheckedMode) {\n if (existing)\n existing.remove();\n continue;\n }\n // Rows marked `not-checked` get no checkbox column and can't be checked\n const itemEl = this.#customWrappers.get(el as HTMLLIElement) ?? el;\n if (itemEl.hasAttribute(`not-checked`)) {\n if (existing)\n existing.remove();\n continue;\n }\n\n // In checked mode: if the existing checkbox is the wrong variant, drop it.\n const existingIsCustom = existing?.classList.contains(`ixfx-list-checkbox-custom`) ?? false;\n if (existing && existingIsCustom !== wantsCustom)\n existing.remove();\n\n let cb: HTMLElement | null = el.querySelector<HTMLElement>(`.ixfx-list-checkbox`);\n if (!cb) {\n if (wantsCustom) {\n const opts = this.checkboxRenderer as CustomCheckboxOptions;\n const margin = opts.margin ?? `0`;\n const width = opts.width ?? `24px`;\n cb = this._createCustomCheckbox(el, margin, width);\n } else {\n cb = this._createNativeCheckboxCell(el);\n }\n el.prepend(cb);\n }\n\n const checked = this.selection.selected.has(el);\n if (wantsCustom) {\n cb.setAttribute(`aria-checked`, String(checked));\n cb.toggleAttribute(`data-checked`, checked);\n if (this.checkboxRenderer) {\n const renderer = typeof this.checkboxRenderer === `function`\n ? this.checkboxRenderer\n : (this.checkboxRenderer as CustomCheckboxOptions).renderer;\n render(renderer(checked), cb);\n }\n } else {\n const input = cb.querySelector<HTMLInputElement>(`input.ixfx-list-checkbox`);\n if (input)\n input.checked = checked;\n }\n }\n }\n\n /**\n * Native checkbox cell: a span that stretches to the row's full cross-axis\n * (so the entire column height is a click target) with a real `<input>`\n * centred inside. The span owns the click; the input is purely visual and\n * has `pointer-events:none` so a single click produces one toggle.\n */\n private _createNativeCheckboxCell(el: HTMLElement): HTMLSpanElement {\n const cell = document.createElement(`span`);\n cell.className = `ixfx-list-checkbox ixfx-list-checkbox-native`;\n cell.style.cssText = `display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;align-self:stretch;width:18px;margin:0 6px 0 0;cursor:pointer;pointer-events:auto`;\n cell.tabIndex = -1;\n cell.setAttribute(`role`, `checkbox`);\n cell.setAttribute(`part`, `checkbox`);\n cell.addEventListener(`click`, (e) => {\n e.stopPropagation();\n this.selection.toggle(el);\n });\n const input = document.createElement(`input`);\n input.type = `checkbox`;\n input.className = `ixfx-list-checkbox`;\n input.style.cssText = `pointer-events:none;cursor:pointer;margin:0`;\n cell.appendChild(input);\n return cell;\n }\n\n /**\n * Custom checkbox cell: same shape as the native cell (a stretching span)\n * so behaviour is identical. The renderer's content goes inside.\n */\n private _createCustomCheckbox(el: HTMLElement, margin: string, width: string): HTMLSpanElement {\n const span = document.createElement(`span`);\n span.className = `ixfx-list-checkbox ixfx-list-checkbox-custom`;\n span.style.cssText = `display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;align-self:stretch;width:${width};margin:${margin};cursor:pointer;pointer-events:auto`;\n span.tabIndex = -1;\n span.setAttribute(`role`, `checkbox`);\n span.setAttribute(`part`, `checkbox`);\n span.addEventListener(`click`, (e) => {\n e.stopPropagation();\n this.selection.toggle(el);\n });\n return span;\n }\n\n private _findListItem(path: EventTarget[]): Element | undefined {\n for (const target of path) {\n if (!(target instanceof Element))\n continue;\n if (target === this)\n break;\n if (target.parentElement === this && target.matches(ITEM_SELECTOR))\n return target;\n }\n return undefined;\n }\n\n private _dispatch<T>(name: keyof ListElementEventMap, detail: T): void {\n this.dispatchEvent(new CustomEvent<T>(name as string, {\n detail,\n bubbles: true,\n composed: true,\n }));\n }\n\n private _dispatchCancelable<T>(name: string, detail: T): boolean {\n return this.dispatchEvent(new CustomEvent<T>(name, {\n detail,\n bubbles: true,\n composed: true,\n cancelable: true,\n }));\n }\n\n // --- Event handlers ---\n\n private _onPointerOver = (e: PointerEvent): void => {\n const item = this._findListItem(e.composedPath());\n if (item)\n this.tickled.handlePointerEnter(item);\n };\n\n private _onPointerLeave = (e: PointerEvent): void => {\n this.tickled.handlePointerLeave(e);\n };\n\n private _onKeyDown = (e: KeyboardEvent): void => {\n if (this.search.isOpen)\n return;\n if ((e.ctrlKey || e.metaKey) && e.key === `a`) {\n if (this.selection.canSelectAll()) {\n e.preventDefault();\n this.selection.selectAll();\n }\n return;\n }\n\n switch (e.key) {\n case `ArrowDown`: {\n e.preventDefault();\n this.tickled.handleKeyboardNavigation(`next`);\n // vscode: Shift+Arrow extends range from anchor\n if (e.shiftKey && this.interactionMode === `vscode`) {\n const cur = this.tickled.tickledId;\n if (cur)\n this.selection.rangeTo(cur);\n }\n break;\n }\n case `ArrowUp`: {\n e.preventDefault();\n this.tickled.handleKeyboardNavigation(`previous`);\n if (e.shiftKey && this.interactionMode === `vscode`) {\n const cur = this.tickled.tickledId;\n if (cur)\n this.selection.rangeTo(cur);\n }\n break;\n }\n case `Enter`: {\n const id = this.tickled.tickledId;\n if (id) {\n e.preventDefault();\n this._dispatch<ListItemClickDetail>(`list-item-click`, { item: id });\n this._activateItem(id);\n }\n break;\n }\n case ` `: {\n const id = this.tickled.tickledId;\n if (id) {\n e.preventDefault();\n this.selection.handleSpace(id);\n }\n break;\n }\n case `Escape`:\n this.selection.clear();\n break;\n }\n };\n\n private _onClick = (e: MouseEvent): void => {\n if (this.#dragSelect.isDragging || this.#nativeDrag.isDragging || this.#postDragSuppressTimer !== 0)\n return;\n const item = this._findListItem(e.composedPath());\n if (!item)\n return;\n this._dispatch<ListItemClickDetail>(`list-item-click`, { item });\n this.selection.handleClick(item, e);\n };\n\n private _onDblClick = (e: MouseEvent): void => {\n const item = this._findListItem(e.composedPath());\n if (item)\n this._activateItem(item);\n };\n\n private _activateItem(item: Element): void {\n // manual mode: never auto-select; just fire the event\n if (this.interactionMode !== `manual`) {\n this.selection.replace(item);\n }\n this._dispatch<ListActivateDetail>(`list-activate`, { item });\n }\n\n // --- Styles ---\n\n static override styles = [themeFallbacks, listSearchStyles, css`\n :host {\n display: block;\n position: relative;\n background: var(--list-bg, var(--surface-3));\n color: var(--list-text, var(--surface-3-text));\n border-radius: var(--list-radius, var(--radius-s));\n border: 1px solid var(--list-border, var(--border));\n width: 100%;\n height: 100%;\n overflow: hidden;\n box-sizing: border-box;\n outline: none;\n --ixfx-list-item-padding: var(--ixfx-list-item-padding, var(--list-item-padding, 4px var(--space-m, 8px)));\n }\n\n :host(:focus-visible) {\n outline: 2px solid var(--accent);\n outline-offset: 2px;\n }\n\n .container {\n position: relative;\n width: 100%;\n height: 100%;\n }\n\n ul {\n list-style: none;\n margin: 0;\n padding: 0;\n width: 100%;\n height: 100%;\n overflow-x: hidden;\n overflow-y: auto;\n box-sizing: border-box;\n /* Scrollbar theming: end users override these CSS variables to control\n the native scrollbar (scrollbar-width, scrollbar-gutter, scrollbar-color). */\n scrollbar-width: var(--list-scrollbar-width, auto);\n scrollbar-gutter: var(--list-scrollbar-gutter, auto);\n scrollbar-color: var(--list-scrollbar-color, auto);\n }\n\n ::slotted([hidden]) {\n display: none !important;\n }\n\n ::slotted(li) {\n display: flex;\n align-items: center;\n padding: var(--ixfx-list-item-padding);\n /* Inset each row from the scroll container's edges (margin, not padding:\n padding is inside the border box, so the tickle/selection outlines\n would still sit flush against the edge and be clipped). */\n margin: 0 var(--list-gutter, 4px);\n min-height: var(--list-item-height, 28px);\n cursor: pointer;\n user-select: none;\n transition: background var(--transition, 0.1s ease-out),\n outline-color var(--transition, 0.1s ease-out);\n box-sizing: border-box;\n outline: 1px solid transparent;\n }\n\n /* Custom shadow-DOM components handle their own internal layout/padding */\n ::slotted([data-list-item]) {\n display: block;\n margin: 0 var(--list-gutter, 4px);\n cursor: pointer;\n user-select: none;\n transition: background var(--transition, 0.1s ease-out),\n outline-color var(--transition, 0.1s ease-out);\n outline: 1px solid transparent;\n }\n\n /* In checked mode, only the checkbox column is clickable; the row body\n gets a default cursor so it doesn't suggest otherwise. */\n :host([interaction-mode=\"checked\"]) ::slotted(li),\n :host([interaction-mode=\"checked\"]) ::slotted([data-list-item]) {\n cursor: default;\n }\n\n /* Wrapper inserted around custom [data-list-item] elements in checked mode.\n Provides the flex layout so the injected checkbox can stretch to the\n row's full height via align-self:stretch. */\n .ixfx-custom-row {\n display: flex;\n align-items: center;\n min-height: var(--list-item-height, 28px);\n box-sizing: border-box;\n cursor: default;\n user-select: none;\n }\n\n ::slotted([data-tickled]) {\n background: var(--item-bg-tickled);\n outline: var(--item-border-tickled);\n }\n\n ::slotted([data-selected]) {\n background: var(--item-bg-selected);\n color: var(--item-text-selected);\n outline: 1px solid transparent;\n }\n\n ::slotted([data-selected][data-tickled]) {\n background: var(--item-bg-selected-tickled);\n outline: 2px dashed var(--surface-3);\n outline-offset: -2px;\n }\n\n /* Drag-select (marquee) rectangle + covered-row preview.\n Style hooks: part=\"marquee\", --vertical-list-marquee-border,\n --vertical-list-marquee-fill. */\n .marquee {\n position: absolute;\n pointer-events: none;\n z-index: 5;\n border: 1px solid var(--vertical-list-marquee-border, var(--accent));\n background: var(--vertical-list-marquee-fill, color-mix(in srgb, var(--accent) 15%, transparent));\n }\n\n ::slotted([data-marquee]) {\n background: var(--item-bg-selected);\n color: var(--item-text-selected);\n }\n\n ::slotted([data-drag-origin]) {\n opacity: 0.6;\n }\n\n /* checked mode: checkbox (injected into li) communicates selection — suppress bg highlight */\n :host([interaction-mode=\"checked\"]) ::slotted([data-selected]) {\n background: transparent;\n color: inherit;\n outline: 1px solid transparent;\n }\n\n :host([interaction-mode=\"checked\"]) ::slotted([data-selected][data-tickled]) {\n background: var(--item-bg-tickled);\n color: inherit;\n outline: var(--item-border-tickled);\n }\n `];\n\n // --- Render ---\n\n override render() {\n return html`\n <div class=\"container\">\n <ul part=\"list\">\n <slot></slot>\n </ul>\n ${this.#marqueeRect\n ? html`<div class=\"marquee\" part=\"marquee\" style=${this.#marqueeStyle(this.#marqueeRect)}></div>`\n : ``}\n ${this.search.renderField()}\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'ixfx-vertical-list': VerticalListElement;\n }\n\n interface HTMLElementEventMap extends VerticalListElementEventMap {}\n}\n\nexport interface VerticalListElementEventMap extends ListElementEventMap {\n 'list-native-drag-start': ListNativeDragStartEvent<Element>;\n}\n"],"mappings":"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"}
@@ -13,6 +13,7 @@ This directory contains documentation for each component and some architectural
13
13
  - [crumbs](./crumbs.md)
14
14
  - [data-display](./data-display.md)
15
15
  - [data-grid](./data-grid.md)
16
+ - [data-table](./data-table.md)
16
17
  - [detail-list](./detail-list.md)
17
18
  - [dock](./dock.md)
18
19
  - [editable-label](./editable-label.md)
@@ -116,6 +116,36 @@ selection actually changes.
116
116
 
117
117
  ---
118
118
 
119
+ ## Native drag-out
120
+
121
+ Set `items-draggable` to make selectable rows draggable out of the grid. A
122
+ dragged row is one logical item and its preview renders identically to the
123
+ other list components. On `dragstart` the grid fires the cancelable,
124
+ `composed`, bubbling `list-native-drag-start` event with
125
+ `{ key, index, item, selectedKeys, selected, preview }`:
126
+
127
+ - `selected` is the full selection as display-ordered `ListDragEntry[]`
128
+ (`{ key, index, item, element }`, where `item` is the row value and
129
+ `element` the `.row` div); `selectedKeys` mirrors the keys. The origin rule
130
+ applies: dragging an unselected row replaces the selection with it first.
131
+ - `preview` is the shared built-in drag preview: `preview.element` for
132
+ `DataTransfer.setDragImage()` (a row of cards for ≤5 rows, a compact
133
+ `N items` list with `+N more` for 6+; the row caption is the first visible
134
+ column's formatted value), plus a pre-painted `preview.canvas` for native
135
+ hosts.
136
+ - Assign `dragPreviewRenderer` for a custom synchronous preview returning
137
+ `{ element, hotspot?, canvas?, dispose? }`.
138
+ - `preventDefault()` on the event cancels the browser drag, but the sync
139
+ listener still sees `preview` (incl. `canvas`) — what lets an Electron host
140
+ rasterize it during the synchronous turn. Column-header reorder (pointer
141
+ drag) is a different mechanism and is unaffected.
142
+
143
+ The built-in preview is theme-aware (`--surface-4`, `--surface-4-text`,
144
+ `--border-subtle`, `--radius-m`, `--accent`, `font`) and exposes
145
+ `part="drag-preview"`.
146
+
147
+ ---
148
+
119
149
  ## Sorting
120
150
 
121
151
  Click header to cycle `asc → desc → none`. Or set:
@@ -0,0 +1,104 @@
1
+ # `ixfx-data-table`
2
+
3
+ A key/value data table with responsive layout, hover details, and keyboard/tickle navigation.
4
+
5
+ ---
6
+
7
+ ## Quick start
8
+
9
+ ```html
10
+ <ixfx-data-table id="table"></ixfx-data-table>
11
+
12
+ <script type="module">
13
+ import '@ixfx/components/data-table';
14
+
15
+ const table = document.querySelector('#table');
16
+ table.rows = [
17
+ { label: `Name`, value: `Ada` },
18
+ { label: `Age`, value: 30 },
19
+ { label: `Active`, value: true, detail: `Account was active since 2020` },
20
+ ];
21
+ </script>
22
+ ```
23
+
24
+ Import via bundle: `import '@ixfx/components';` or per-component `import '@ixfx/components/data-table';`.
25
+
26
+ ---
27
+
28
+ ## Rows
29
+
30
+ ```ts
31
+ type DataTableRow = {
32
+ label: string;
33
+ value: string | number | boolean | Node | TemplateResult;
34
+ fullWidth?: boolean; // spans entire row width
35
+ detail?: string | TemplateResult; // hover/click reveals this
36
+ id?: string;
37
+ };
38
+ ```
39
+
40
+ ---
41
+
42
+ ## Layout
43
+
44
+ | `layout` | Description |
45
+ |---|---|
46
+ | `auto` (default) | Responsive — widens to 2–3 pair columns on wide containers, stacks on narrow |
47
+ | `rows` | Always stacked (key above value) |
48
+ | `columns` | Always two-column grid regardless of width |
49
+
50
+ Switch to stacked at a custom breakpoint:
51
+
52
+ ```html
53
+ <ixfx-data-table narrow-breakpoint="620"></ixfx-data-table>
54
+ ```
55
+
56
+ ---
57
+
58
+ ## Selection & tickle
59
+
60
+ Clicking a row selects it and reveals its detail panel. `selectedIndex` tracks the selection:
61
+
62
+ ```ts
63
+ table.selectedIndex = 0; // select first row
64
+ table.addEventListener(`data-table-row-click`, (e) => {
65
+ console.log(e.detail.row, e.detail.index, e.detail.selected);
66
+ });
67
+ ```
68
+
69
+ Hover reveals the detail panel after `tickle-delay` ms (default 400). Clicking reveals immediately.
70
+
71
+ ---
72
+
73
+ ## CSS variables
74
+
75
+ | Variable | Default | Description |
76
+ |---|---|---|
77
+ | `--data-table-name-width` | `max-content` | Column width for labels |
78
+ | `--data-table-value-width` | `minmax(0, 1fr)` | Column width for values |
79
+ | `--data-table-cell-align` | `baseline` | Cell vertical alignment |
80
+ | `--data-table-row-padding` | `0.35em` | Row padding |
81
+ | `--data-table-name-align` | `left` | Label text alignment |
82
+
83
+ ---
84
+
85
+ ## Methods
86
+
87
+ | Method | Signature | Description |
88
+ |---|---|---|
89
+ | `rows` | `readonly DataTableRow[]` | Row data |
90
+
91
+ | Property | Type | Description |
92
+ |---|---|---|
93
+ | `selectedIndex` | `number \| undefined` | Currently selected row index |
94
+ | `layout` | `'auto' \| 'rows' \| 'columns'` | Layout mode |
95
+ | `narrowBreakpoint` | `number` | Breakpoint for stacking (px) |
96
+ | `tickleDelay` | `number` | Hover detail delay (ms) |
97
+
98
+ ---
99
+
100
+ ## Events
101
+
102
+ | Event | Detail | Description |
103
+ |---|---|---|
104
+ | `data-table-row-click` | `{ row, index, selected }` | Fired on row click |