@ixfx/components 0.2.6 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. package/bundle/index.d.ts +213 -117
  2. package/bundle/index.d.ts.map +1 -1
  3. package/bundle/index.js +1438 -686
  4. package/bundle/index.js.map +1 -1
  5. package/dist/ac-text.js +3 -3
  6. package/dist/ac-text.js.map +1 -1
  7. package/dist/{button-B9ztaKyl.js → button-DMt5XWUK.js} +4 -4
  8. package/dist/{button-B9ztaKyl.js.map → button-DMt5XWUK.js.map} +1 -1
  9. package/dist/button.js +1 -1
  10. package/dist/checkbox.d.ts +2 -3
  11. package/dist/checkbox.d.ts.map +1 -1
  12. package/dist/checkbox.js +1 -1
  13. package/dist/checkbox.js.map +1 -1
  14. package/dist/{colour-C3MQIjFJ-BJV-QC-3.js → colour-C3MQIjFJ-BLXg8W3U.js} +2 -2
  15. package/dist/{colour-C3MQIjFJ-BJV-QC-3.js.map → colour-C3MQIjFJ-BLXg8W3U.js.map} +1 -1
  16. package/dist/{colour-picker-BNqDz4wK.js → colour-picker-K6zu82lm.js} +5 -5
  17. package/dist/{colour-picker-BNqDz4wK.js.map → colour-picker-K6zu82lm.js.map} +1 -1
  18. package/dist/colour-picker.js +1 -1
  19. package/dist/crumbs.d.ts +2 -3
  20. package/dist/crumbs.d.ts.map +1 -1
  21. package/dist/crumbs.js +10 -10
  22. package/dist/crumbs.js.map +1 -1
  23. package/dist/data-display.js +3 -3
  24. package/dist/{decorate-O4m1U4l3.js → decorate-D7rC1gLP.js} +10 -8
  25. package/dist/decorate-D7rC1gLP.js.map +1 -0
  26. package/dist/{defaults-FEZaGiYZ.js → defaults-C7elhuyJ.js} +2 -2
  27. package/dist/{defaults-FEZaGiYZ.js.map → defaults-C7elhuyJ.js.map} +1 -1
  28. package/dist/{dist-By5URG0Y.js → dist-BX0OVIm7.js} +2 -2
  29. package/dist/{dist-By5URG0Y.js.map → dist-BX0OVIm7.js.map} +1 -1
  30. package/dist/{dist-D7kJgnSE.js → dist-TAGYwaju.js} +1 -1
  31. package/dist/{dist-D7kJgnSE.js.map → dist-TAGYwaju.js.map} +1 -1
  32. package/dist/editable-label.d.ts +6 -1
  33. package/dist/editable-label.d.ts.map +1 -1
  34. package/dist/editable-label.js +49 -2
  35. package/dist/editable-label.js.map +1 -1
  36. package/dist/{hex-editor-gH3mteMr.js → hex-editor-CsN_ySkH.js} +85 -85
  37. package/dist/hex-editor-CsN_ySkH.js.map +1 -0
  38. package/dist/{hex-editor-2_s0Ua5t.d.ts → hex-editor-DlfoA65y.d.ts} +5 -5
  39. package/dist/{hex-editor-2_s0Ua5t.d.ts.map → hex-editor-DlfoA65y.d.ts.map} +1 -1
  40. package/dist/hex.d.ts +1 -1
  41. package/dist/hex.js +1 -1
  42. package/dist/{highlight-CAjwQ4GE.js → highlight-TPbSHruI.js} +3 -3
  43. package/dist/highlight-TPbSHruI.js.map +1 -0
  44. package/dist/{icon-CYACf6o8.js → icon-9NWTXtE1.js} +4 -5
  45. package/dist/icon-9NWTXtE1.js.map +1 -0
  46. package/dist/icon-CeTDJhC7.d.ts.map +1 -1
  47. package/dist/icons.js +3 -3
  48. package/dist/{incr-search-BZawvrfl.js → incr-search-DSKKsk6w.js} +12 -10
  49. package/dist/incr-search-DSKKsk6w.js.map +1 -0
  50. package/dist/incr-search.d.ts +1 -1
  51. package/dist/incr-search.js +2 -2
  52. package/dist/{index-CwGkb9s1.d.ts → index--hrxPBkQ.d.ts} +69 -69
  53. package/dist/index--hrxPBkQ.d.ts.map +1 -0
  54. package/dist/{index-kY5gxbkI.d.ts → index-DSlPeWLK.d.ts} +15 -15
  55. package/dist/{index-kY5gxbkI.d.ts.map → index-DSlPeWLK.d.ts.map} +1 -1
  56. package/dist/{index-V2cJorlA.d.ts → index-hXFn14tM.d.ts} +4 -4
  57. package/dist/index-hXFn14tM.d.ts.map +1 -0
  58. package/dist/index.d.ts +109 -13
  59. package/dist/index.d.ts.map +1 -1
  60. package/dist/index.js +748 -52
  61. package/dist/index.js.map +1 -1
  62. package/dist/keyboard-De_vOHLg.js.map +1 -1
  63. package/dist/{labelled-input-base-BvgH7ldm.js → labelled-input-base-CqPL5rKm.js} +2 -2
  64. package/dist/labelled-input-base-CqPL5rKm.js.map +1 -0
  65. package/dist/labelled-radial-input.js +3 -3
  66. package/dist/labelled-radial-input.js.map +1 -1
  67. package/dist/labelled-range-input.js +2 -2
  68. package/dist/led.js +1 -1
  69. package/dist/{menu-C3eANB5M.js → menu-DJJuCm-5.js} +48 -48
  70. package/dist/menu-DJJuCm-5.js.map +1 -0
  71. package/dist/{menu-item-6IU5weHi.js → menu-item-CKRfP0pN.js} +10 -10
  72. package/dist/menu-item-CKRfP0pN.js.map +1 -0
  73. package/dist/menu.d.ts +1 -1
  74. package/dist/menu.js +2 -2
  75. package/dist/miller.d.ts +5 -7
  76. package/dist/miller.d.ts.map +1 -1
  77. package/dist/miller.js +14 -14
  78. package/dist/miller.js.map +1 -1
  79. package/dist/narrowed-text.d.ts.map +1 -1
  80. package/dist/narrowed-text.js +4 -4
  81. package/dist/narrowed-text.js.map +1 -1
  82. package/dist/panel.js +2 -2
  83. package/dist/panel.js.map +1 -1
  84. package/dist/plots.js +1 -1
  85. package/dist/polar-pad.d.ts.map +1 -1
  86. package/dist/polar-pad.js +1 -1
  87. package/dist/polar-pad.js.map +1 -1
  88. package/dist/{popup-Ddx9LCzY.js → popup-ImtS9KnS.js} +1 -1
  89. package/dist/{popup-Ddx9LCzY.js.map → popup-ImtS9KnS.js.map} +1 -1
  90. package/dist/{prominence-CreDBdCs.js → prominence--ic9uWBl.js} +1 -1
  91. package/dist/{prominence-CreDBdCs.js.map → prominence--ic9uWBl.js.map} +1 -1
  92. package/dist/{radial-input-Dbc6fgCK.js → radial-input-DZA6WaFu.js} +14 -14
  93. package/dist/radial-input-DZA6WaFu.js.map +1 -0
  94. package/dist/radial-input.js +1 -1
  95. package/dist/range-input.js +1 -1
  96. package/dist/range.js +2 -2
  97. package/dist/{registry-CZEB1YI9.js → registry-Q2gYQCHM.js} +1 -1
  98. package/dist/{registry-CZEB1YI9.js.map → registry-Q2gYQCHM.js.map} +1 -1
  99. package/dist/selecthorizontal.js +1 -1
  100. package/dist/snap-container.js +1 -1
  101. package/dist/split-layout.js +4 -4
  102. package/dist/split-layout.js.map +1 -1
  103. package/dist/swipe.d.ts +1 -2
  104. package/dist/swipe.d.ts.map +1 -1
  105. package/dist/swipe.js +2 -2
  106. package/dist/swipe.js.map +1 -1
  107. package/dist/tab-list-BnqISNvO.d.ts.map +1 -1
  108. package/dist/tabs.js +2 -2
  109. package/dist/tabs.js.map +1 -1
  110. package/dist/{timeline-B62Sz6J_.js → timeline-Cv75Th2U.js} +120 -116
  111. package/dist/timeline-Cv75Th2U.js.map +1 -0
  112. package/dist/timeline.d.ts +1 -1
  113. package/dist/timeline.js +1 -1
  114. package/dist/tooltip-CwGfb4lp.d.ts.map +1 -1
  115. package/dist/tree-CvvKbO-f.js +1330 -0
  116. package/dist/tree-CvvKbO-f.js.map +1 -0
  117. package/dist/{tree-component-CQpoPo4s.d.ts → tree-component-D8Bg48tt.d.ts} +12 -12
  118. package/dist/{tree-component-CQpoPo4s.d.ts.map → tree-component-D8Bg48tt.d.ts.map} +1 -1
  119. package/dist/tree.d.ts +1 -1
  120. package/dist/tree.js +1 -1143
  121. package/dist/xy-axis-D9uwpGTT.d.ts.map +1 -1
  122. package/dist/{xy-axis-C7fNxDwY.js → xy-axis-DL0tfJDr.js} +10 -10
  123. package/dist/{xy-axis-C7fNxDwY.js.map → xy-axis-DL0tfJDr.js.map} +1 -1
  124. package/dist/xy-pad.js +2 -2
  125. package/package.json +3 -3
  126. package/dist/decorate-O4m1U4l3.js.map +0 -1
  127. package/dist/hex-editor-gH3mteMr.js.map +0 -1
  128. package/dist/highlight-CAjwQ4GE.js.map +0 -1
  129. package/dist/icon-CYACf6o8.js.map +0 -1
  130. package/dist/incr-search-BZawvrfl.js.map +0 -1
  131. package/dist/index-CwGkb9s1.d.ts.map +0 -1
  132. package/dist/index-V2cJorlA.d.ts.map +0 -1
  133. package/dist/labelled-input-base-BvgH7ldm.js.map +0 -1
  134. package/dist/menu-C3eANB5M.js.map +0 -1
  135. package/dist/menu-item-6IU5weHi.js.map +0 -1
  136. package/dist/radial-input-Dbc6fgCK.js.map +0 -1
  137. package/dist/timeline-B62Sz6J_.js.map +0 -1
  138. package/dist/tree-data-model-DGvRVOFY.js +0 -190
  139. package/dist/tree-data-model-DGvRVOFY.js.map +0 -1
  140. package/dist/tree.js.map +0 -1
  141. /package/dist/{interaction-CQDQJf2Q.js → interaction-DG7X1rhI.js} +0 -0
package/dist/index.js CHANGED
@@ -1,33 +1,33 @@
1
- import { n as buildHighlightTargets, r as clearHighlights, t as applyHighlights } from "./highlight-CAjwQ4GE.js";
2
- import { r as TickledController } from "./tickled-styles-Bg3QbcrD.js";
3
- import { a as svgToDataUri, i as registerIcon, n as hasIcon, r as iconBus, t as getIcon } from "./registry-CZEB1YI9.js";
4
- import { n as safeCustomElement, t as __decorate } from "./decorate-O4m1U4l3.js";
5
- import { t as IxfxIconElement } from "./icon-CYACf6o8.js";
1
+ import { n as buildHighlightTargets, r as clearHighlights, t as applyHighlights } from "./highlight-TPbSHruI.js";
2
+ import { n as tickledStyles, r as TickledController } from "./tickled-styles-Bg3QbcrD.js";
3
+ import { n as safeCustomElement, t as __decorate } from "./decorate-D7rC1gLP.js";
4
+ import { a as svgToDataUri, i as registerIcon, n as hasIcon, r as iconBus, t as getIcon } from "./registry-Q2gYQCHM.js";
5
+ import { t as IxfxIconElement } from "./icon-9NWTXtE1.js";
6
6
  import { AcTextElement } from "./ac-text.js";
7
- import { i as ICON_CLOSE, n as ICON_CHECK, r as ICON_CHEVRON_DOWN, t as ICON_CARET_RIGHT } from "./defaults-FEZaGiYZ.js";
7
+ import { a as createFuzzySearch, i as createAcCompletionProvider, n as IncrSearchTreeController, r as createIncrSearch, t as createElementIncrSearch } from "./incr-search-DSKKsk6w.js";
8
+ import { i as ICON_CLOSE, n as ICON_CHECK, r as ICON_CHEVRON_DOWN, t as ICON_CARET_RIGHT } from "./defaults-C7elhuyJ.js";
8
9
  import "./icons.js";
9
- import { t as prominenceStyles } from "./prominence-CreDBdCs.js";
10
- import { n as buttonStyles, t as ButtonElement } from "./button-B9ztaKyl.js";
11
- import { t as popupSurfaceStyles } from "./popup-Ddx9LCzY.js";
12
- import "./menu-item-6IU5weHi.js";
10
+ import { t as prominenceStyles } from "./prominence--ic9uWBl.js";
11
+ import { n as buttonStyles, t as ButtonElement } from "./button-DMt5XWUK.js";
12
+ import { t as popupSurfaceStyles } from "./popup-ImtS9KnS.js";
13
+ import "./menu-item-CKRfP0pN.js";
13
14
  import { Checkbox } from "./checkbox.js";
14
- import { C as resultThrow, E as throwIfFailed, d as scaler, h as without, n as clamper, p as containsDuplicateInstances, y as nullUndefTest } from "./dist-D7kJgnSE.js";
15
- import { a as computeColour, c as last, i as applyAgeModification, l as QueueMutable, n as PlotSingleAxis, o as parseColourScaleAttr, r as PlotDataSeries, s as readBaseColour, t as PlotXyAxis, u as __exportAll } from "./xy-axis-C7fNxDwY.js";
16
- import { a as toStringAbbreviate, c as isEqualDefault, l as isEqualValueDefault, o as defaultComparer, r as isPrimitive, u as isEqualValueIgnoreOrder } from "./dist-By5URG0Y.js";
17
- import { n as ColourPicker, t as ColourPickerPopup } from "./colour-picker-BNqDz4wK.js";
15
+ import { C as resultThrow, E as throwIfFailed, d as scaler, h as without, n as clamper, p as containsDuplicateInstances, y as nullUndefTest } from "./dist-TAGYwaju.js";
16
+ import { a as computeColour, c as last, i as applyAgeModification, l as QueueMutable, n as PlotSingleAxis, o as parseColourScaleAttr, r as PlotDataSeries, s as readBaseColour, t as PlotXyAxis, u as __exportAll } from "./xy-axis-DL0tfJDr.js";
17
+ import { a as toStringAbbreviate, c as isEqualDefault, l as isEqualValueDefault, o as defaultComparer, r as isPrimitive, u as isEqualValueIgnoreOrder } from "./dist-BX0OVIm7.js";
18
+ import { n as ColourPicker, t as ColourPickerPopup } from "./colour-picker-K6zu82lm.js";
18
19
  import { LabelledRangeInput } from "./labelled-range-input.js";
19
- import { t as TreeDataModel } from "./tree-data-model-DGvRVOFY.js";
20
- import { a as createFuzzySearch, i as createAcCompletionProvider, n as IncrSearchTreeController, r as createIncrSearch, t as createElementIncrSearch } from "./incr-search-BZawvrfl.js";
20
+ import { i as TreeDataModel, n as TreeController, r as TreeBaseElement, t as TreeListElement } from "./tree-CvvKbO-f.js";
21
21
  import { NarrowedTextElement } from "./narrowed-text.js";
22
22
  import { CrumbNavigationElement } from "./crumbs.js";
23
23
  import { DataDisplayComponent } from "./data-display.js";
24
24
  import { EditableLabel } from "./editable-label.js";
25
25
  import { t as resolveStep } from "./keyboard-De_vOHLg.js";
26
- import { t as IxfxHexEditorElement } from "./hex-editor-gH3mteMr.js";
27
- import { t as RadialInputElement } from "./radial-input-Dbc6fgCK.js";
26
+ import { t as IxfxHexEditorElement } from "./hex-editor-CsN_ySkH.js";
27
+ import { t as RadialInputElement } from "./radial-input-DZA6WaFu.js";
28
28
  import { LabelledRadialInput } from "./labelled-radial-input.js";
29
29
  import { LedElement } from "./led.js";
30
- import { t as menu_exports } from "./menu-C3eANB5M.js";
30
+ import { t as menu_exports } from "./menu-DJJuCm-5.js";
31
31
  import { MillerBaseElement, MillerListElement, MillerTreeController } from "./miller.js";
32
32
  import { PanelElement } from "./panel.js";
33
33
  import { PolarPad } from "./polar-pad.js";
@@ -39,14 +39,633 @@ import { SnapContainer } from "./snap-container.js";
39
39
  import { SplitLayoutElement } from "./split-layout.js";
40
40
  import { SwipeElement } from "./swipe.js";
41
41
  import { TabListElement } from "./tabs.js";
42
- import { a as attachTimelineInteractions, i as timelineStyles, n as IxfxTimelineElement, o as resolveCssVars, r as TimelineTrackTabElement, s as resolveGrid, t as TimelineController } from "./timeline-B62Sz6J_.js";
43
- import { TreeBaseElement, TreeController, TreeListElement } from "./tree.js";
42
+ import { a as resolveCssVars, i as attachTimelineInteractions, n as TimelineTrackTabElement, o as resolveGrid, r as timelineStyles, s as TimelineController, t as IxfxTimelineElement } from "./timeline-Cv75Th2U.js";
44
43
  import { XyPad } from "./xy-pad.js";
45
44
  import { LitElement, css, html, nothing } from "lit";
46
45
  import { eventOptions, property, query, queryAssignedElements, state } from "lit/decorators.js";
47
46
  import { unsafeHTML } from "lit/directives/unsafe-html.js";
48
47
  import { keyed } from "lit/directives/keyed.js";
49
48
  import { classMap } from "lit/directives/class-map.js";
49
+ //#region src/ac-token/ac-token.ts
50
+ const DEFAULT_TOKENIZER = (text) => text.split(/[\s,;]+/);
51
+ function makeLruCache(maxSize) {
52
+ const map = /* @__PURE__ */ new Map();
53
+ return {
54
+ get: (k) => map.get(k),
55
+ set: (k, v) => {
56
+ if (map.has(k)) map.delete(k);
57
+ map.set(k, v);
58
+ if (map.size > maxSize) map.delete(map.keys().next().value);
59
+ },
60
+ has: (k) => map.has(k),
61
+ clear: () => map.clear()
62
+ };
63
+ }
64
+ let AcTokenElement = class AcTokenElement extends LitElement {
65
+ #cache;
66
+ #debounceTimer;
67
+ #abortController;
68
+ #staticProvider;
69
+ constructor() {
70
+ super();
71
+ this.placeholder = ``;
72
+ this.cacheSize = 20;
73
+ this.debounceMs = 150;
74
+ this.strict = false;
75
+ this._tokens = [];
76
+ this._draft = ``;
77
+ this._suggestions = [];
78
+ this._isOpen = false;
79
+ this._isLoading = false;
80
+ this._tickled = new TickledController(this, { getItems: () => this._suggestions.map((_, i) => ({
81
+ id: i,
82
+ enabled: true
83
+ })) });
84
+ this.#cache = makeLruCache(20);
85
+ this.#onWrapperClick = (e) => {
86
+ if (!e.target.closest(`.token-chip`)) this.shadowRoot?.querySelector(`input.draft`)?.focus();
87
+ };
88
+ this.#onFocus = () => {
89
+ this._selectedTokenIndex = void 0;
90
+ this.#scheduleSuggestionFetch(this._draft);
91
+ };
92
+ this.#onBlur = () => {
93
+ setTimeout(() => {
94
+ this._isOpen = false;
95
+ this._tickled.clearTickled();
96
+ this._selectedTokenIndex = void 0;
97
+ }, 120);
98
+ };
99
+ this.#onInput = (e) => {
100
+ const value = e.target.value;
101
+ this._selectedTokenIndex = void 0;
102
+ const parts = (this.tokenizer ?? DEFAULT_TOKENIZER)(value);
103
+ if (parts.length > 1) {
104
+ for (let i = 0; i < parts.length - 1; i++) {
105
+ const label = parts[i].trim();
106
+ if (label) this.#addToken({ label });
107
+ }
108
+ this._draft = parts[parts.length - 1] ?? ``;
109
+ } else this._draft = value;
110
+ this.#scheduleSuggestionFetch(this._draft);
111
+ };
112
+ this.#onKeyDown = (e) => {
113
+ const input = e.target;
114
+ if ((e.metaKey || e.ctrlKey) && e.key === `a`) {
115
+ e.preventDefault();
116
+ const parts = [...this._tokens.map((t) => t.label), this._draft.trim()].filter(Boolean);
117
+ navigator.clipboard.writeText(parts.join(` `)).catch(() => {});
118
+ return;
119
+ }
120
+ if (this._selectedTokenIndex !== void 0 && e.key.length === 1 && !e.metaKey && !e.ctrlKey && !e.altKey) {
121
+ e.preventDefault();
122
+ this.#startEditingSelectedToken(e.key);
123
+ return;
124
+ }
125
+ this.#handleNavigationKey(e, input);
126
+ };
127
+ this.#onPaste = (e) => {
128
+ e.preventDefault();
129
+ const text = e.clipboardData?.getData(`text/plain`) ?? ``;
130
+ if (!text) return;
131
+ const parts = (this.tokenizer ?? DEFAULT_TOKENIZER)(this._draft + text).filter(Boolean);
132
+ if (parts.length === 0) return;
133
+ if (parts.length === 1) this._draft = parts[0];
134
+ else {
135
+ for (let i = 0; i < parts.length - 1; i++) {
136
+ const label = parts[i].trim();
137
+ if (label) this.#addToken({ label });
138
+ }
139
+ this._draft = parts[parts.length - 1] ?? ``;
140
+ }
141
+ this.updateComplete.then(() => {
142
+ this.shadowRoot?.querySelector(`input.draft`)?.setSelectionRange(this._draft.length, this._draft.length);
143
+ });
144
+ this.#scheduleSuggestionFetch(this._draft);
145
+ };
146
+ this.#onSuggestionPointerDown = (e, item) => {
147
+ e.preventDefault();
148
+ this.#selectSuggestion(item);
149
+ };
150
+ this.#onChipPointerDown = (e, index) => {
151
+ if (e.button !== 0) return;
152
+ e.preventDefault();
153
+ const el = e.currentTarget;
154
+ const startY = e.clientY;
155
+ let dragged = false;
156
+ el.setPointerCapture(e.pointerId);
157
+ const cleanup = () => {
158
+ el.removeEventListener(`pointermove`, onMove);
159
+ el.removeEventListener(`pointerup`, onUp);
160
+ };
161
+ const onMove = (me) => {
162
+ if (Math.abs(me.clientY - startY) > 28) {
163
+ dragged = true;
164
+ el.releasePointerCapture(me.pointerId);
165
+ cleanup();
166
+ this.#removeToken(index);
167
+ this.shadowRoot?.querySelector(`input.draft`)?.focus();
168
+ }
169
+ };
170
+ const onUp = () => {
171
+ cleanup();
172
+ if (!dragged) {
173
+ this._selectedTokenIndex = this._selectedTokenIndex === index ? void 0 : index;
174
+ this.shadowRoot?.querySelector(`input.draft`)?.focus();
175
+ }
176
+ };
177
+ el.addEventListener(`pointermove`, onMove);
178
+ el.addEventListener(`pointerup`, onUp);
179
+ };
180
+ this.#cache = makeLruCache(this.cacheSize);
181
+ }
182
+ get tokens() {
183
+ return this._tokens;
184
+ }
185
+ set tokens(value) {
186
+ this._tokens = [...value];
187
+ this.#emitChange();
188
+ }
189
+ /** All committed token labels joined with commas. */
190
+ get value() {
191
+ return this._tokens.map((t) => t.label).join(`, `);
192
+ }
193
+ connectedCallback() {
194
+ super.connectedCallback();
195
+ this.#buildStaticProvider();
196
+ }
197
+ disconnectedCallback() {
198
+ super.disconnectedCallback();
199
+ this.#cancelPending();
200
+ }
201
+ updated(changedProps) {
202
+ if (changedProps.has(`completionProvider`) || changedProps.has(`cacheSize`)) this.#cache = makeLruCache(this.cacheSize);
203
+ if (changedProps.has(`completionProvider`)) this.#buildStaticProvider();
204
+ }
205
+ #buildStaticProvider() {
206
+ const raw = this.getAttribute(`data-tokens`);
207
+ if (!raw || this.completionProvider) {
208
+ this.#staticProvider = void 0;
209
+ return;
210
+ }
211
+ const items = raw.trim().split(/[\s,;]+/).filter(Boolean).map((label) => ({ label }));
212
+ this.#staticProvider = createAcCompletionProvider(items);
213
+ }
214
+ #onWrapperClick;
215
+ #onFocus;
216
+ #onBlur;
217
+ #onInput;
218
+ #onKeyDown;
219
+ #handleNavigationKey(e, input) {
220
+ switch (e.key) {
221
+ case `ArrowLeft`:
222
+ if (input.selectionStart === 0 && input.selectionEnd === 0) {
223
+ e.preventDefault();
224
+ if (this._selectedTokenIndex === void 0) {
225
+ if (this._tokens.length > 0) this._selectedTokenIndex = this._tokens.length - 1;
226
+ } else if (this._selectedTokenIndex > 0) this._selectedTokenIndex--;
227
+ }
228
+ break;
229
+ case `ArrowRight`:
230
+ if (this._selectedTokenIndex !== void 0) {
231
+ e.preventDefault();
232
+ if (this._selectedTokenIndex < this._tokens.length - 1) this._selectedTokenIndex++;
233
+ else this._selectedTokenIndex = void 0;
234
+ }
235
+ break;
236
+ case `Backspace`:
237
+ if (input.selectionStart === 0 && input.selectionEnd === 0) {
238
+ e.preventDefault();
239
+ if (this._selectedTokenIndex !== void 0) this.#removeToken(this._selectedTokenIndex);
240
+ else if (this._tokens.length > 0) this.#removeToken(this._tokens.length - 1);
241
+ }
242
+ break;
243
+ case `Enter`: {
244
+ const tickledIdx = this._tickled.tickledId;
245
+ if (this._isOpen && tickledIdx !== void 0 && this._suggestions[tickledIdx]) {
246
+ e.preventDefault();
247
+ this.#selectSuggestion(this._suggestions[tickledIdx]);
248
+ } else if (!this.strict && this._draft.trim()) {
249
+ e.preventDefault();
250
+ this.#addToken({ label: this._draft.trim() });
251
+ this._draft = ``;
252
+ this.#closePopup();
253
+ }
254
+ break;
255
+ }
256
+ case `ArrowDown`:
257
+ if (!this._isOpen) return;
258
+ e.preventDefault();
259
+ this._tickled.handleKeyboardNavigation(`next`);
260
+ this.#scrollSuggestionIntoView();
261
+ break;
262
+ case `ArrowUp`:
263
+ if (!this._isOpen) return;
264
+ e.preventDefault();
265
+ this._tickled.handleKeyboardNavigation(`previous`);
266
+ this.#scrollSuggestionIntoView();
267
+ break;
268
+ case `Escape`:
269
+ e.preventDefault();
270
+ this.#closePopup();
271
+ this._selectedTokenIndex = void 0;
272
+ break;
273
+ }
274
+ }
275
+ #onPaste;
276
+ #onSuggestionPointerDown;
277
+ #onChipPointerDown;
278
+ #startEditingSelectedToken(key) {
279
+ const idx = this._selectedTokenIndex;
280
+ if (idx === void 0) return;
281
+ const token = this._tokens[idx];
282
+ if (!token) return;
283
+ const newDraft = token.label + key;
284
+ this.#removeToken(idx);
285
+ this._selectedTokenIndex = void 0;
286
+ this._draft = newDraft;
287
+ this.updateComplete.then(() => {
288
+ const inp = this.shadowRoot?.querySelector(`input.draft`);
289
+ if (!inp) return;
290
+ inp.setSelectionRange(inp.value.length, inp.value.length);
291
+ });
292
+ this.#scheduleSuggestionFetch(newDraft);
293
+ }
294
+ #addToken(item) {
295
+ const index = this._tokens.length;
296
+ this._tokens = [...this._tokens, item];
297
+ this.dispatchEvent(new CustomEvent(`ac-token-add`, {
298
+ detail: {
299
+ item,
300
+ index
301
+ },
302
+ bubbles: true,
303
+ composed: true
304
+ }));
305
+ this.#emitChange();
306
+ }
307
+ #removeToken(index) {
308
+ const item = this._tokens[index];
309
+ if (!item) return;
310
+ this._tokens = this._tokens.filter((_, i) => i !== index);
311
+ if (this._selectedTokenIndex !== void 0) {
312
+ if (this._selectedTokenIndex === index) this._selectedTokenIndex = void 0;
313
+ else if (this._selectedTokenIndex > index) this._selectedTokenIndex--;
314
+ }
315
+ this.dispatchEvent(new CustomEvent(`ac-token-remove`, {
316
+ detail: {
317
+ item,
318
+ index
319
+ },
320
+ bubbles: true,
321
+ composed: true
322
+ }));
323
+ this.#emitChange();
324
+ }
325
+ #selectSuggestion(item) {
326
+ this.#addToken(item);
327
+ this._draft = ``;
328
+ this.#closePopup();
329
+ }
330
+ #emitChange() {
331
+ this.dispatchEvent(new CustomEvent(`ac-token-change`, {
332
+ detail: { tokens: this._tokens },
333
+ bubbles: true,
334
+ composed: true
335
+ }));
336
+ }
337
+ #closePopup() {
338
+ this._isOpen = false;
339
+ this._tickled.clearTickled();
340
+ }
341
+ #cancelPending() {
342
+ if (this.#debounceTimer !== void 0) {
343
+ clearTimeout(this.#debounceTimer);
344
+ this.#debounceTimer = void 0;
345
+ }
346
+ this.#abortController?.abort();
347
+ this.#abortController = void 0;
348
+ this._isLoading = false;
349
+ }
350
+ #scheduleSuggestionFetch(query) {
351
+ const provider = this.completionProvider ?? this.#staticProvider;
352
+ if (!provider) {
353
+ this._isOpen = false;
354
+ return;
355
+ }
356
+ if (this.#debounceTimer !== void 0) clearTimeout(this.#debounceTimer);
357
+ this._isOpen = true;
358
+ this.#debounceTimer = setTimeout(() => {
359
+ this.#fetchSuggestions(query, provider);
360
+ }, this.debounceMs);
361
+ }
362
+ #fetchSuggestions(query, provider) {
363
+ const cached = this.#cache.get(query);
364
+ if (cached !== void 0) {
365
+ this._suggestions = cached;
366
+ this._tickled.clearTickled();
367
+ this._isLoading = false;
368
+ return;
369
+ }
370
+ this.#abortController?.abort();
371
+ this.#abortController = new AbortController();
372
+ const { signal } = this.#abortController;
373
+ this._isLoading = true;
374
+ Promise.resolve(provider(query, signal)).then((results) => {
375
+ if (signal.aborted) return;
376
+ this.#cache.set(query, results);
377
+ this._suggestions = results;
378
+ this._tickled.clearTickled();
379
+ this._isLoading = false;
380
+ }).catch((err) => {
381
+ if (signal.aborted) return;
382
+ console.error(`[ixfx-ac-token] completionProvider error:`, err);
383
+ this._suggestions = [];
384
+ this._isLoading = false;
385
+ });
386
+ }
387
+ #scrollSuggestionIntoView() {
388
+ this.updateComplete.then(() => {
389
+ this.shadowRoot?.querySelector(`.suggestion.tickled`)?.scrollIntoView({ block: `nearest` });
390
+ });
391
+ }
392
+ #renderChip(item, index) {
393
+ return html`
394
+ <span
395
+ class="token-chip ${this._selectedTokenIndex === index ? `selected` : ``}"
396
+ title=${item.subLabel ?? nothing}
397
+ @pointerdown=${(e) => this.#onChipPointerDown(e, index)}
398
+ >
399
+ ${item.icon ? html`<ixfx-icon class="chip-icon" name=${item.icon}></ixfx-icon>` : nothing}
400
+ <span class="chip-label">${item.label}</span>
401
+ </span>
402
+ `;
403
+ }
404
+ #renderSuggestion(item, index) {
405
+ const isTickled = this._tickled.isTickled(index);
406
+ return html`
407
+ <div
408
+ class="suggestion ${isTickled ? `tickled` : ``}"
409
+ role="option"
410
+ id="ac-token-sugg-${index}"
411
+ aria-selected=${isTickled}
412
+ @pointerenter=${() => this._tickled.handlePointerEnter(index)}
413
+ @pointerdown=${(e) => this.#onSuggestionPointerDown(e, item)}
414
+ >
415
+ ${item.icon ? html`<ixfx-icon class="sugg-icon" name=${item.icon}></ixfx-icon>` : nothing}
416
+ <span class="sugg-label">${item.label}</span>
417
+ ${item.subLabel ? html`<span class="sugg-sublabel">${item.subLabel}</span>` : nothing}
418
+ </div>
419
+ `;
420
+ }
421
+ #renderPopup() {
422
+ const showEmpty = !this._isLoading && this._suggestions.length === 0;
423
+ return html`
424
+ <div
425
+ class="popup"
426
+ role="listbox"
427
+ aria-label="Suggestions"
428
+ data-input-mode=${this._tickled.inputMode}
429
+ @pointerleave=${(e) => this._tickled.handlePointerLeave(e)}
430
+ >
431
+ ${showEmpty ? html`<div class="no-results">No matching results</div>` : this._suggestions.map((item, i) => this.#renderSuggestion(item, i))}
432
+ </div>
433
+ `;
434
+ }
435
+ render() {
436
+ const tickledIdx = this._tickled.tickledId ?? -1;
437
+ const activeDescendant = tickledIdx >= 0 ? `ac-token-sugg-${tickledIdx}` : void 0;
438
+ const showPlaceholder = this._tokens.length === 0 ? this.placeholder || nothing : nothing;
439
+ return html`
440
+ <div class="wrapper" @click=${this.#onWrapperClick}>
441
+ ${this._tokens.map((t, i) => this.#renderChip(t, i))}
442
+ <input
443
+ type="text"
444
+ class="draft"
445
+ .value=${this._draft}
446
+ placeholder=${showPlaceholder}
447
+ autocomplete="off"
448
+ spellcheck="false"
449
+ role="combobox"
450
+ aria-autocomplete="list"
451
+ aria-expanded=${this._isOpen}
452
+ aria-activedescendant=${activeDescendant ?? nothing}
453
+ @input=${this.#onInput}
454
+ @keydown=${this.#onKeyDown}
455
+ @focus=${this.#onFocus}
456
+ @blur=${this.#onBlur}
457
+ @paste=${this.#onPaste}
458
+ >
459
+ ${this._isLoading ? html`<span class="spinner" aria-hidden="true"></span>` : nothing}
460
+ </div>
461
+ ${this._isOpen ? this.#renderPopup() : nothing}
462
+ `;
463
+ }
464
+ static {
465
+ this.styles = [tickledStyles, css`
466
+ :host {
467
+ display: block;
468
+ position: relative;
469
+ font-family: var(--font-family, system-ui, sans-serif);
470
+ font-size: var(--text-m, 12px);
471
+ color: var(--surface-2-text);
472
+ }
473
+
474
+ /* ---- Wrapper (looks like a text input) ---- */
475
+
476
+ .wrapper {
477
+ display: flex;
478
+ flex-wrap: wrap;
479
+ align-items: center;
480
+ gap: var(--ac-token-chip-gap, var(--space-xs, 3px));
481
+ padding: var(--ac-token-input-padding, var(--space-xs, 3px) var(--space-m, 8px));
482
+ background: var(--ac-token-input-background, var(--surface-3, var(--surface-2)));
483
+ color: var(--surface-2-text);
484
+ border: var(--ac-token-input-border, var(--border));
485
+ border-radius: var(--ac-token-input-border-radius, var(--radius-m, 4px));
486
+ cursor: text;
487
+ min-height: 32px;
488
+ box-sizing: border-box;
489
+ transition: border-color var(--transition, 0.15s ease-out);
490
+ }
491
+
492
+ .wrapper:focus-within {
493
+ border-color: var(--accent);
494
+ }
495
+
496
+ /* ---- Token chips ---- */
497
+
498
+ .token-chip {
499
+ display: inline-flex;
500
+ align-items: center;
501
+ gap: var(--space-xs, 3px);
502
+ padding: 1px var(--space-m, 8px);
503
+ background: var(--ac-token-chip-background, var(--surface-4));
504
+ border: var(--ac-token-chip-border, var(--border));
505
+ border-radius: var(--ac-token-chip-border-radius, var(--radius-s, 3px));
506
+ user-select: none;
507
+ cursor: default;
508
+ font-size: var(--text-s, 11px);
509
+ white-space: nowrap;
510
+ touch-action: none;
511
+ transition:
512
+ background var(--transition, 0.1s ease-out),
513
+ border-color var(--transition, 0.1s ease-out),
514
+ color var(--transition, 0.1s ease-out);
515
+ }
516
+
517
+ .token-chip.selected {
518
+ background: var(--ac-token-chip-selected-background, var(--accent));
519
+ color: var(--ac-token-chip-selected-color, var(--accent-text, #fff));
520
+ border-color: var(--accent);
521
+ }
522
+
523
+ .chip-icon {
524
+ flex-shrink: 0;
525
+ width: 1em;
526
+ height: 1em;
527
+ }
528
+
529
+ /* ---- Draft input ---- */
530
+
531
+ input.draft {
532
+ flex: 1;
533
+ min-width: 60px;
534
+ border: none;
535
+ background: transparent;
536
+ color: inherit;
537
+ font: inherit;
538
+ outline: none;
539
+ caret-color: var(--accent);
540
+ padding: 1px 0;
541
+ anchor-name: --ac-token-draft;
542
+ }
543
+
544
+ input.draft::placeholder {
545
+ color: var(--surface-muted-text);
546
+ }
547
+
548
+ /* ---- Spinner ---- */
549
+
550
+ .spinner {
551
+ width: 12px;
552
+ height: 12px;
553
+ border: 2px solid var(--surface-5);
554
+ border-top-color: var(--accent);
555
+ border-radius: 50%;
556
+ animation: ac-token-spin 0.7s linear infinite;
557
+ flex-shrink: 0;
558
+ pointer-events: none;
559
+ margin-left: var(--space-xs, 3px);
560
+ }
561
+
562
+ @keyframes ac-token-spin {
563
+ to { transform: rotate(360deg); }
564
+ }
565
+
566
+ /* ---- Popup ---- */
567
+
568
+ .popup {
569
+ position: fixed;
570
+ position-anchor: --ac-token-draft;
571
+ top: anchor(bottom);
572
+ left: anchor(left);
573
+ margin-top: 2px;
574
+ position-try-fallbacks: --ac-token-popup-above;
575
+ z-index: 200;
576
+ background: var(--surface-3, var(--surface-2));
577
+ border: var(--border);
578
+ border-radius: var(--radius-m, 4px);
579
+ box-shadow: var(--shadow-l, 0 4px 24px rgba(0, 0, 0, 0.5));
580
+ padding: var(--space-xs, 3px) 0;
581
+ max-height: 260px;
582
+ min-width: 160px;
583
+ overflow-y: auto;
584
+ font-size: var(--ac-token-popup-font-size, var(--text-s, 11px));
585
+ }
586
+
587
+ @position-try --ac-token-popup-above {
588
+ top: auto;
589
+ bottom: anchor(top);
590
+ left: anchor(left);
591
+ margin-top: 0;
592
+ margin-bottom: 2px;
593
+ }
594
+
595
+ /* ---- Suggestion items ---- */
596
+
597
+ .suggestion {
598
+ display: flex;
599
+ align-items: center;
600
+ gap: var(--space-m, 8px);
601
+ padding: var(--space-xs, 3px) var(--space-l, 12px);
602
+ cursor: pointer;
603
+ border-radius: var(--radius-s, 3px);
604
+ margin: 0 var(--space-xs, 3px);
605
+ transition: background var(--transition, 0.1s ease-out);
606
+ user-select: none;
607
+ white-space: nowrap;
608
+ min-height: 28px;
609
+ }
610
+
611
+ .suggestion.tickled {
612
+ background: var(--_item-focused-bg, var(--accent));
613
+ color: var(--accent-text, #fff);
614
+ }
615
+
616
+ .suggestion.tickled .sugg-sublabel {
617
+ color: color-mix(in srgb, var(--accent-text, #fff) 70%, transparent);
618
+ }
619
+
620
+ .sugg-icon {
621
+ flex-shrink: 0;
622
+ width: 1em;
623
+ height: 1em;
624
+ }
625
+
626
+ .sugg-label {
627
+ flex: 1;
628
+ overflow: hidden;
629
+ text-overflow: ellipsis;
630
+ }
631
+
632
+ .sugg-sublabel {
633
+ flex-shrink: 0;
634
+ margin-left: auto;
635
+ color: var(--surface-muted-text);
636
+ font-size: var(--text-s, 11px);
637
+ }
638
+
639
+ /* ---- Empty state ---- */
640
+
641
+ .no-results {
642
+ padding: var(--space-m, 8px) var(--space-l, 12px);
643
+ color: var(--surface-muted-text);
644
+ font-style: italic;
645
+ }
646
+ `];
647
+ }
648
+ };
649
+ __decorate([property({ type: Object })], AcTokenElement.prototype, "completionProvider", void 0);
650
+ __decorate([property({ type: Object })], AcTokenElement.prototype, "tokenizer", void 0);
651
+ __decorate([property({ type: String })], AcTokenElement.prototype, "placeholder", void 0);
652
+ __decorate([property({
653
+ type: Number,
654
+ attribute: `cache-size`
655
+ })], AcTokenElement.prototype, "cacheSize", void 0);
656
+ __decorate([property({
657
+ type: Number,
658
+ attribute: `debounce-ms`
659
+ })], AcTokenElement.prototype, "debounceMs", void 0);
660
+ __decorate([property({ type: Boolean })], AcTokenElement.prototype, "strict", void 0);
661
+ __decorate([state()], AcTokenElement.prototype, "_tokens", void 0);
662
+ __decorate([state()], AcTokenElement.prototype, "_draft", void 0);
663
+ __decorate([state()], AcTokenElement.prototype, "_selectedTokenIndex", void 0);
664
+ __decorate([state()], AcTokenElement.prototype, "_suggestions", void 0);
665
+ __decorate([state()], AcTokenElement.prototype, "_isOpen", void 0);
666
+ __decorate([state()], AcTokenElement.prototype, "_isLoading", void 0);
667
+ AcTokenElement = __decorate([safeCustomElement(`ixfx-ac-token`)], AcTokenElement);
668
+ //#endregion
50
669
  //#region src/button/radio-group.ts
51
670
  let RadioGroup = class RadioGroup extends LitElement {
52
671
  constructor(..._args) {
@@ -5409,16 +6028,19 @@ let TabListItemElement = class TabListItemElement extends LitElement {
5409
6028
  <div>
5410
6029
  ${this.iconName ? html`<ixfx-icon name="${this.iconName}"></ixfx-icon>` : nothing}
5411
6030
  ${showLabel ? html`<span class="label"><slot></slot></span>` : nothing}
5412
- ${this.closeable ? html`
5413
- <button
5414
- class="close-btn"
5415
- type="button"
5416
- aria-label="Close tab"
5417
- @click=${this._onCloseClick}
5418
- >
5419
- <ixfx-icon name="close"></ixfx-icon>
5420
- </button>
5421
- ` : nothing}
6031
+ <div class="actions">
6032
+ <div class="toolbar"><slot name="toolbar"></slot></div>
6033
+ ${this.closeable ? html`
6034
+ <button
6035
+ class="close-btn"
6036
+ type="button"
6037
+ aria-label="Close tab"
6038
+ @click=${this._onCloseClick}
6039
+ >
6040
+ <ixfx-icon name="close"></ixfx-icon>
6041
+ </button>
6042
+ ` : nothing}
6043
+ </div>
5422
6044
  </div>
5423
6045
  `;
5424
6046
  }
@@ -5442,7 +6064,7 @@ let TabListItemElement = class TabListItemElement extends LitElement {
5442
6064
  box-sizing: border-box;
5443
6065
  }
5444
6066
 
5445
- div {
6067
+ :host > div {
5446
6068
  display: flex;
5447
6069
  align-items: center;
5448
6070
  gap: var(--space-s, 4px);
@@ -5473,6 +6095,28 @@ let TabListItemElement = class TabListItemElement extends LitElement {
5473
6095
  flex-shrink: 0;
5474
6096
  }
5475
6097
 
6098
+ .actions {
6099
+ display: flex;
6100
+ align-items: center;
6101
+ gap: var(--space-xs);
6102
+ margin-left: auto;
6103
+ flex-shrink: 0;
6104
+ }
6105
+
6106
+ .toolbar {
6107
+ display: none;
6108
+ align-items: center;
6109
+ gap: var(--space-xs);
6110
+ }
6111
+
6112
+ :host([selected]) .toolbar {
6113
+ display: flex;
6114
+ }
6115
+
6116
+ slot[name="toolbar"] {
6117
+ display: contents;
6118
+ }
6119
+
5476
6120
  .close-btn {
5477
6121
  display: inline-flex;
5478
6122
  align-items: center;
@@ -5480,7 +6124,6 @@ let TabListItemElement = class TabListItemElement extends LitElement {
5480
6124
  background: none;
5481
6125
  border: none;
5482
6126
  padding: var(--space-xs);
5483
- margin-left: auto;
5484
6127
  cursor: pointer;
5485
6128
  color: inherit;
5486
6129
  border-radius: var(--radius-s);
@@ -5562,14 +6205,17 @@ let TabPanelsElement = class TabPanelsElement extends LitElement {
5562
6205
  if (this.onSelectedPanel) this.onSelectedPanel(el.id, el);
5563
6206
  }
5564
6207
  syncWithList(el) {
6208
+ const apply = (t) => {
6209
+ if (!t) return;
6210
+ const forEl = t.getAttribute(`for`);
6211
+ if (forEl) this.selectPanel(forEl);
6212
+ };
5565
6213
  const t = el.getSelectedElement();
5566
6214
  if (!t) {
5567
- el.updateComplete.then(() => this.syncWithList(el));
6215
+ el.updateComplete.then(() => apply(el.getSelectedElement()));
5568
6216
  return;
5569
6217
  }
5570
- const forEl = t.getAttribute(`for`);
5571
- if (!forEl) return;
5572
- this.selectPanel(forEl);
6218
+ apply(t);
5573
6219
  }
5574
6220
  selectPanel(id) {
5575
6221
  const current = this.getSelectedElement();
@@ -5967,7 +6613,9 @@ TooltipElement = _TooltipElement = __decorate([safeCustomElement(`ixfx-tooltip`)
5967
6613
  //#endregion
5968
6614
  //#region src/vertical-list/vertical-list.ts
5969
6615
  const ITEM_SELECTOR = `li, [data-list-item]`;
5970
- const isMultiMode = (mode) => mode === `checked` || mode === `vscode` || mode === `standard`;
6616
+ function isMultiMode(mode) {
6617
+ return mode === `checked` || mode === `vscode` || mode === `standard`;
6618
+ }
5971
6619
  let VerticalListElement = class VerticalListElement extends LitElement {
5972
6620
  constructor(..._args) {
5973
6621
  super(..._args);
@@ -5976,6 +6624,7 @@ let VerticalListElement = class VerticalListElement extends LitElement {
5976
6624
  this._selectedItems = /* @__PURE__ */ new Set();
5977
6625
  this._searchVisible = false;
5978
6626
  this._searchQuery = ``;
6627
+ this.#customWrappers = /* @__PURE__ */ new Map();
5979
6628
  this.tickled = new TickledController(this, {
5980
6629
  getItems: () => this._getVisibleItems().map((el) => ({
5981
6630
  id: el,
@@ -6085,6 +6734,7 @@ let VerticalListElement = class VerticalListElement extends LitElement {
6085
6734
  }
6086
6735
  #search;
6087
6736
  #selectionAnchor;
6737
+ #customWrappers;
6088
6738
  connectedCallback() {
6089
6739
  super.connectedCallback();
6090
6740
  if (!this.hasAttribute(`tabindex`)) this.tabIndex = 0;
@@ -6117,9 +6767,29 @@ let VerticalListElement = class VerticalListElement extends LitElement {
6117
6767
  super.updated(changed);
6118
6768
  if (changed.has(`interactionMode`)) {
6119
6769
  this.#selectionAnchor = void 0;
6120
- this._clearSelection();
6770
+ const previousSelected = this._selectedItems;
6771
+ const savedWrappers = changed.get(`interactionMode`) === `checked` ? new Map(this.#customWrappers) : null;
6772
+ this._syncCheckboxes();
6773
+ if (previousSelected.size > 0) {
6774
+ let remapped;
6775
+ if (savedWrappers && savedWrappers.size > 0) remapped = new Set([...previousSelected].map((el) => savedWrappers.get(el) ?? el));
6776
+ else if (this.#customWrappers.size > 0) {
6777
+ const innerToWrapper = new Map([...this.#customWrappers].map(([w, i]) => [i, w]));
6778
+ remapped = new Set([...previousSelected].map((el) => innerToWrapper.get(el) ?? el));
6779
+ } else remapped = new Set(previousSelected);
6780
+ this._selectedItems = remapped;
6781
+ }
6782
+ this._syncItemStates();
6121
6783
  }
6122
6784
  }
6785
+ /** The most recently selected item, or `undefined` if nothing is selected. */
6786
+ get selectedItem() {
6787
+ return [...this._selectedItems].at(-1);
6788
+ }
6789
+ /** The full current selection set (read-only). */
6790
+ get selectedItems() {
6791
+ return this._selectedItems;
6792
+ }
6123
6793
  addItem(item) {
6124
6794
  if (typeof item === `string`) {
6125
6795
  const li = document.createElement(`li`);
@@ -6132,6 +6802,7 @@ let VerticalListElement = class VerticalListElement extends LitElement {
6132
6802
  item.remove();
6133
6803
  }
6134
6804
  clearItems() {
6805
+ this.#customWrappers.clear();
6135
6806
  this.replaceChildren();
6136
6807
  this.#selectionAnchor = void 0;
6137
6808
  this._selectedItems = /* @__PURE__ */ new Set();
@@ -6140,6 +6811,19 @@ let VerticalListElement = class VerticalListElement extends LitElement {
6140
6811
  select(item) {
6141
6812
  this._replaceSelection(item);
6142
6813
  }
6814
+ selectMany(items) {
6815
+ if (this.selectionMode !== `multiple`) throw new Error(`selectMany requires selectionMode="multiple" (current: "${this.selectionMode}")`);
6816
+ const previous = this._selectedItems;
6817
+ const next = new Set(items);
6818
+ if (next.size === 0) return;
6819
+ this._selectedItems = next;
6820
+ this.#selectionAnchor = [...next].at(-1);
6821
+ this._syncItemStates();
6822
+ this._dispatch(`list-select`, {
6823
+ selected: next,
6824
+ previous
6825
+ });
6826
+ }
6143
6827
  deselect(item) {
6144
6828
  if (!this._selectedItems.has(item)) return;
6145
6829
  const previous = this._selectedItems;
@@ -6271,11 +6955,34 @@ let VerticalListElement = class VerticalListElement extends LitElement {
6271
6955
  if (this.interactionMode === `checked`) el.toggleAttribute(`data-checked`, this._selectedItems.has(el));
6272
6956
  else el.removeAttribute(`data-checked`);
6273
6957
  }
6274
- this.dataset[`inputMode`] = this.tickled.inputMode;
6958
+ this.dataset.inputMode = this.tickled.inputMode;
6275
6959
  this._syncCheckboxes();
6276
6960
  }
6277
6961
  _syncCheckboxes() {
6278
6962
  const inCheckedMode = this.interactionMode === `checked`;
6963
+ if (inCheckedMode) for (const inner of Array.from(this.children)) {
6964
+ if (!inner.hasAttribute(`data-list-item`)) continue;
6965
+ if (inner.parentElement !== this) continue;
6966
+ const wrapper = document.createElement(`li`);
6967
+ wrapper.className = `ixfx-custom-row`;
6968
+ const key = inner.getAttribute(`data-key`);
6969
+ const label = inner.getAttribute(`data-search-label`);
6970
+ if (key !== null) wrapper.setAttribute(`data-key`, key);
6971
+ if (label !== null) wrapper.setAttribute(`data-search-label`, label);
6972
+ inner.removeAttribute(`data-list-item`);
6973
+ this.insertBefore(wrapper, inner);
6974
+ wrapper.appendChild(inner);
6975
+ this.#customWrappers.set(wrapper, inner);
6976
+ }
6977
+ else {
6978
+ for (const [wrapper, inner] of this.#customWrappers) {
6979
+ if (wrapper.parentElement !== this) continue;
6980
+ inner.setAttribute(`data-list-item`, ``);
6981
+ this.insertBefore(inner, wrapper);
6982
+ wrapper.remove();
6983
+ }
6984
+ this.#customWrappers.clear();
6985
+ }
6279
6986
  for (const el of this.querySelectorAll(`li`)) {
6280
6987
  let cb = el.querySelector(`input.ixfx-list-checkbox`);
6281
6988
  if (inCheckedMode) {
@@ -6417,17 +7124,6 @@ let VerticalListElement = class VerticalListElement extends LitElement {
6417
7124
  outline: 1px solid var(--accent, #60a5fa);
6418
7125
  }
6419
7126
 
6420
- /* checked mode: left-border indicator for custom shadow-DOM items (no checkbox injection possible) */
6421
- :host([interaction-mode="checked"]) ::slotted([data-list-item]) {
6422
- border-left: 3px solid transparent;
6423
- transition: background var(--transition, 0.1s ease-out),
6424
- border-color var(--transition, 0.1s ease-out);
6425
- }
6426
-
6427
- :host([interaction-mode="checked"]) ::slotted([data-list-item][data-checked]) {
6428
- border-left: 3px solid var(--accent, #60a5fa);
6429
- }
6430
-
6431
7127
  .search-overlay {
6432
7128
  position: absolute;
6433
7129
  top: 8px;
@@ -6516,6 +7212,6 @@ function init() {
6516
7212
  console.log(`Init`);
6517
7213
  }
6518
7214
  //#endregion
6519
- export { AcTextElement, ButtonElement, Checkbox, ColourPicker, ColourPickerPopup, CrumbNavigationElement, CrumbPathController, CrumbPathElement, DataDisplayComponent, EditableLabel, EditableNumber, ICON_CARET_RIGHT, ICON_CHECK, ICON_CHEVRON_DOWN, ICON_CLOSE, IncrSearchTreeController as IncrSearchMillerController, IncrSearchTreeController, IxfxHexEditorElement, IxfxIconElement, IxfxTimelineElement, LabelledRadialInput, LabelledRangeInput, LedElement, menu_exports as Menu, MillerBaseElement, MillerListElement, MillerTreeController, NarrowedTextElement, PanelElement, PanelGroupElement, PlotDataSeries, PlotSingleAxis, PlotXyAxis, PolarPad, RadialInputElement, RadioGroup, RangeElement, RangeInput, RangeMultiElement, SelectHorizontalElement, SnapContainer, SplitButton, SplitLayoutElement, SwipeElement, TabListElement, TabListItemElement, TabPanelElement, TabPanelsElement, TimelineController, TimelineTrackTabElement, TooltipElement, TransitoryLabelElement, TreeBaseElement, TreeController, TreeDataModel, TreeListElement, VerticalListElement, XyPad, applyAgeModification, applyHighlights, attachTimelineInteractions, buildHighlightTargets, clearHighlights, computeColour, createAcCompletionProvider, createElementIncrSearch, createFuzzySearch, createIncrSearch, getIcon, hasIcon, iconBus, init, monitorIndeterminate, parseColourScaleAttr, readBaseColour, registerIcon, resolveCssVars, resolveGrid, svgToDataUri, timelineStyles };
7215
+ export { AcTextElement, AcTokenElement, ButtonElement, Checkbox, ColourPicker, ColourPickerPopup, CrumbNavigationElement, CrumbPathController, CrumbPathElement, DataDisplayComponent, EditableLabel, EditableNumber, ICON_CARET_RIGHT, ICON_CHECK, ICON_CHEVRON_DOWN, ICON_CLOSE, IncrSearchTreeController as IncrSearchMillerController, IncrSearchTreeController, IxfxHexEditorElement, IxfxIconElement, IxfxTimelineElement, LabelledRadialInput, LabelledRangeInput, LedElement, menu_exports as Menu, MillerBaseElement, MillerListElement, MillerTreeController, NarrowedTextElement, PanelElement, PanelGroupElement, PlotDataSeries, PlotSingleAxis, PlotXyAxis, PolarPad, RadialInputElement, RadioGroup, RangeElement, RangeInput, RangeMultiElement, SelectHorizontalElement, SnapContainer, SplitButton, SplitLayoutElement, SwipeElement, TabListElement, TabListItemElement, TabPanelElement, TabPanelsElement, TimelineController, TimelineTrackTabElement, TooltipElement, TransitoryLabelElement, TreeBaseElement, TreeController, TreeDataModel, TreeListElement, VerticalListElement, XyPad, applyAgeModification, applyHighlights, attachTimelineInteractions, buildHighlightTargets, clearHighlights, computeColour, createAcCompletionProvider, createElementIncrSearch, createFuzzySearch, createIncrSearch, getIcon, hasIcon, iconBus, init, monitorIndeterminate, parseColourScaleAttr, readBaseColour, registerIcon, resolveCssVars, resolveGrid, svgToDataUri, timelineStyles };
6520
7216
 
6521
7217
  //# sourceMappingURL=index.js.map