@ixfx/components 0.5.19 → 0.6.0

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 (198) hide show
  1. package/bundle/index.d.ts +462 -210
  2. package/bundle/index.d.ts.map +1 -1
  3. package/bundle/index.js +8027 -6493
  4. package/bundle/index.js.map +1 -1
  5. package/dist/ac-text.js +3 -3
  6. package/dist/ac-token-DVbmHmky.d.ts +107 -0
  7. package/dist/ac-token-DVbmHmky.d.ts.map +1 -0
  8. package/dist/ac-token.d.ts +2 -0
  9. package/dist/ac-token.js +646 -0
  10. package/dist/ac-token.js.map +1 -0
  11. package/dist/{button-CphdEaZd.js → button-Bn0BuLpO.js} +2 -3
  12. package/dist/{button-CphdEaZd.js.map → button-Bn0BuLpO.js.map} +1 -1
  13. package/dist/button.d.ts +1 -1
  14. package/dist/button.d.ts.map +1 -1
  15. package/dist/button.js +1 -1
  16. package/dist/checkbox.js +1 -1
  17. package/dist/{colour-picker-37NyVenk.js → colour-picker-0stHB90J.js} +18 -8
  18. package/dist/colour-picker-0stHB90J.js.map +1 -0
  19. package/dist/colour-picker.d.ts +1 -1
  20. package/dist/colour-picker.js +1 -1
  21. package/dist/crumbs.d.ts +2 -2
  22. package/dist/crumbs.js +1152 -1
  23. package/dist/crumbs.js.map +1 -0
  24. package/dist/data-controller-DOlmqNKb.js +171 -0
  25. package/dist/data-controller-DOlmqNKb.js.map +1 -0
  26. package/dist/data-display.js +1 -1
  27. package/dist/data-grid-BeAkORz2.d.ts +215 -0
  28. package/dist/data-grid-BeAkORz2.d.ts.map +1 -0
  29. package/dist/data-grid.d.ts +2 -0
  30. package/dist/data-grid.js +1225 -0
  31. package/dist/data-grid.js.map +1 -0
  32. package/dist/data-provider-BAn_-oR1.d.ts +61 -0
  33. package/dist/data-provider-BAn_-oR1.d.ts.map +1 -0
  34. package/dist/{defaults-BkWCPJFL.js → defaults-BCiDhZbF.js} +20 -3
  35. package/dist/defaults-BCiDhZbF.js.map +1 -0
  36. package/dist/drag-ghost-DtSDvjri.js +85 -0
  37. package/dist/drag-ghost-DtSDvjri.js.map +1 -0
  38. package/dist/editable-label.js +1 -1
  39. package/dist/{incr-search-2pz21xtR.js → element-search-Y8SRfPGM.js} +2 -133
  40. package/dist/element-search-Y8SRfPGM.js.map +1 -0
  41. package/dist/fallbacks-DEE1nCTt.js +406 -0
  42. package/dist/fallbacks-DEE1nCTt.js.map +1 -0
  43. package/dist/form-CsXJX15p.js +64 -0
  44. package/dist/form-CsXJX15p.js.map +1 -0
  45. package/dist/form.d.ts +15 -0
  46. package/dist/form.d.ts.map +1 -0
  47. package/dist/form.js +2 -0
  48. package/dist/grouped-item-lister-CNLMgdIr.d.ts +121 -0
  49. package/dist/grouped-item-lister-CNLMgdIr.d.ts.map +1 -0
  50. package/dist/grouped-item-lister.d.ts +2 -0
  51. package/dist/grouped-item-lister.js +177 -0
  52. package/dist/grouped-item-lister.js.map +1 -0
  53. package/dist/{icon-DG9Lm3LT.js → icons-Bm2ByQ3s.js} +6 -3
  54. package/dist/icons-Bm2ByQ3s.js.map +1 -0
  55. package/dist/icons.js +2 -8
  56. package/dist/incr-search-vCzuao-2.js +137 -0
  57. package/dist/incr-search-vCzuao-2.js.map +1 -0
  58. package/dist/incr-search.d.ts +1 -1
  59. package/dist/incr-search.js +2 -1
  60. package/dist/{index-BKAlRgbq.d.ts → index-BOUaDIW-.d.ts} +4 -1
  61. package/dist/index-BOUaDIW-.d.ts.map +1 -0
  62. package/dist/{index-D9u_Q7-b.d.ts → index-Cu5i8zZY.d.ts} +3 -3
  63. package/dist/{index-D9u_Q7-b.d.ts.map → index-Cu5i8zZY.d.ts.map} +1 -1
  64. package/dist/{index-DkhBGHgF.d.ts → index-ovq_GTdl.d.ts} +2 -2
  65. package/dist/{index-DkhBGHgF.d.ts.map → index-ovq_GTdl.d.ts.map} +1 -1
  66. package/dist/index.d.ts +54 -572
  67. package/dist/index.d.ts.map +1 -1
  68. package/dist/index.js +1898 -4253
  69. package/dist/index.js.map +1 -1
  70. package/dist/labelled-radial-input.d.ts +1 -1
  71. package/dist/labelled-radial-input.js +1 -2
  72. package/dist/labelled-radial-input.js.map +1 -1
  73. package/dist/led.d.ts +0 -1
  74. package/dist/led.d.ts.map +1 -1
  75. package/dist/led.js +2 -4
  76. package/dist/led.js.map +1 -1
  77. package/dist/menu-DIfU70AA.js +1609 -0
  78. package/dist/menu-DIfU70AA.js.map +1 -0
  79. package/dist/menu-item-BG5w7fi4.d.ts.map +1 -1
  80. package/dist/menu.d.ts +1 -1
  81. package/dist/menu.js +1 -2
  82. package/dist/miller.d.ts +3 -3
  83. package/dist/miller.js +3 -3
  84. package/dist/narrowed-text.d.ts.map +1 -1
  85. package/dist/narrowed-text.js +4 -4
  86. package/dist/narrowed-text.js.map +1 -1
  87. package/dist/notification-pill-DyZydXoZ.d.ts +68 -0
  88. package/dist/notification-pill-DyZydXoZ.d.ts.map +1 -0
  89. package/dist/notifier.d.ts +2 -0
  90. package/dist/notifier.js +146 -0
  91. package/dist/notifier.js.map +1 -0
  92. package/dist/panel.js +1 -2
  93. package/dist/panel.js.map +1 -1
  94. package/dist/plots.js +1 -1
  95. package/dist/polar-pad.d.ts +1 -1
  96. package/dist/polar-pad.js +1 -1
  97. package/dist/{radial-input-BhZeyyP-.d.ts → radial-input-DXMN55Nz.d.ts} +2 -2
  98. package/dist/{radial-input-BhZeyyP-.d.ts.map → radial-input-DXMN55Nz.d.ts.map} +1 -1
  99. package/dist/{radial-input-CVxGH-Cp.js → radial-input-Dk8Wydp5.js} +4 -4
  100. package/dist/radial-input-Dk8Wydp5.js.map +1 -0
  101. package/dist/radial-input.d.ts +1 -1
  102. package/dist/radial-input.js +1 -1
  103. package/dist/range-input.d.ts +1 -1
  104. package/dist/range-input.js +1 -1
  105. package/dist/range.d.ts +1 -1
  106. package/dist/range.js +1 -1
  107. package/dist/scroll-fade-CAs1EabA.js +70 -0
  108. package/dist/scroll-fade-CAs1EabA.js.map +1 -0
  109. package/dist/select-horiz.d.ts +50 -0
  110. package/dist/select-horiz.d.ts.map +1 -0
  111. package/dist/select-horiz.js +263 -0
  112. package/dist/select-horiz.js.map +1 -0
  113. package/dist/{slider-input-CzmSVgm7.js → slider-input-CT4gcF-o.js} +2 -2
  114. package/dist/{slider-input-CzmSVgm7.js.map → slider-input-CT4gcF-o.js.map} +1 -1
  115. package/dist/{slider-input-TidGhvXt.d.ts → slider-input-XppH3vcy.d.ts} +2 -2
  116. package/dist/{slider-input-TidGhvXt.d.ts.map → slider-input-XppH3vcy.d.ts.map} +1 -1
  117. package/dist/slider-input.d.ts +1 -1
  118. package/dist/slider-input.js +1 -1
  119. package/dist/snackbar.d.ts +58 -0
  120. package/dist/snackbar.d.ts.map +1 -0
  121. package/dist/snackbar.js +265 -0
  122. package/dist/snackbar.js.map +1 -0
  123. package/dist/split-layout.js +1 -1
  124. package/dist/swipe.d.ts +1 -1
  125. package/dist/swipe.js +1 -1
  126. package/dist/tabs.js +533 -1
  127. package/dist/tabs.js.map +1 -0
  128. package/dist/{tickled-controller-BF2Ea0DE.d.ts → tickled-controller-BtevrRLV.d.ts} +8 -6
  129. package/dist/tickled-controller-BtevrRLV.d.ts.map +1 -0
  130. package/dist/{tickled-styles-fNDdqf6l.js → tickled-controller-DkAU-0qL.js} +19 -105
  131. package/dist/tickled-controller-DkAU-0qL.js.map +1 -0
  132. package/dist/tickled-styles-BrAHEj1q.js +89 -0
  133. package/dist/tickled-styles-BrAHEj1q.js.map +1 -0
  134. package/dist/transitory-label.d.ts +30 -0
  135. package/dist/transitory-label.d.ts.map +1 -0
  136. package/dist/transitory-label.js +249 -0
  137. package/dist/transitory-label.js.map +1 -0
  138. package/dist/{tree-BtiiDX1a.js → tree-BO6BXI2b.js} +4 -4
  139. package/dist/{tree-BtiiDX1a.js.map → tree-BO6BXI2b.js.map} +1 -1
  140. package/dist/{tree-component-DQ9vzdy5.d.ts → tree-component-BvV4muxx.d.ts} +2 -2
  141. package/dist/{tree-component-DQ9vzdy5.d.ts.map → tree-component-BvV4muxx.d.ts.map} +1 -1
  142. package/dist/tree.d.ts +3 -3
  143. package/dist/tree.js +1 -1
  144. package/dist/{types-DO3gt5vg.d.ts → types-Bc23SI94.d.ts} +3 -61
  145. package/dist/types-Bc23SI94.d.ts.map +1 -0
  146. package/dist/types-DlVNJez_.d.ts +41 -0
  147. package/dist/types-DlVNJez_.d.ts.map +1 -0
  148. package/dist/vertical-list.d.ts +122 -0
  149. package/dist/vertical-list.d.ts.map +1 -0
  150. package/dist/vertical-list.js +737 -0
  151. package/dist/vertical-list.js.map +1 -0
  152. package/dist/wheel-nudge-BlSCe_hD.js.map +1 -1
  153. package/dist/{wheel-nudge-DgMTRYlh.d.ts → wheel-nudge-DGrNzWuE.d.ts} +3 -3
  154. package/dist/wheel-nudge-DGrNzWuE.d.ts.map +1 -0
  155. package/dist/{xy-axis-CxHC8z5E.js → xy-axis-B6yMjckW.js} +3 -109
  156. package/dist/xy-axis-B6yMjckW.js.map +1 -0
  157. package/dist/xy-pad.d.ts +1 -1
  158. package/dist/xy-pad.js +1 -1
  159. package/docs-user/README.md +3 -2
  160. package/docs-user/ac-token.md +7 -1
  161. package/docs-user/colour-picker.md +8 -4
  162. package/docs-user/data-grid.md +248 -0
  163. package/docs-user/index.json +217 -0
  164. package/docs-user/llms.txt +57 -0
  165. package/docs-user/menu.md +11 -8
  166. package/docs-user/rating.md +102 -0
  167. package/llms.txt +57 -0
  168. package/package.json +21 -11
  169. package/dist/colour-picker-37NyVenk.js.map +0 -1
  170. package/dist/crumb-navigation-BrB0r14u.js +0 -1220
  171. package/dist/crumb-navigation-BrB0r14u.js.map +0 -1
  172. package/dist/defaults-BkWCPJFL.js.map +0 -1
  173. package/dist/fallbacks-DPgPEqo8.js +0 -218
  174. package/dist/fallbacks-DPgPEqo8.js.map +0 -1
  175. package/dist/icon-DG9Lm3LT.js.map +0 -1
  176. package/dist/icons.js.map +0 -1
  177. package/dist/incr-search-2pz21xtR.js.map +0 -1
  178. package/dist/index-BKAlRgbq.d.ts.map +0 -1
  179. package/dist/interaction-D6efrBLo.js +0 -1
  180. package/dist/menu-CdwSe_XN.js +0 -825
  181. package/dist/menu-CdwSe_XN.js.map +0 -1
  182. package/dist/menu-item-CrmUwIoJ.js +0 -782
  183. package/dist/menu-item-CrmUwIoJ.js.map +0 -1
  184. package/dist/radial-input-CVxGH-Cp.js.map +0 -1
  185. package/dist/registry-CkW7q09Q.js +0 -21
  186. package/dist/registry-CkW7q09Q.js.map +0 -1
  187. package/dist/selecthorizontal.d.ts +0 -15
  188. package/dist/selecthorizontal.d.ts.map +0 -1
  189. package/dist/selecthorizontal.js +0 -43
  190. package/dist/selecthorizontal.js.map +0 -1
  191. package/dist/tab-list-mAoD6L0e.js +0 -615
  192. package/dist/tab-list-mAoD6L0e.js.map +0 -1
  193. package/dist/tickled-controller-BF2Ea0DE.d.ts.map +0 -1
  194. package/dist/tickled-styles-fNDdqf6l.js.map +0 -1
  195. package/dist/types-DO3gt5vg.d.ts.map +0 -1
  196. package/dist/wheel-nudge-DgMTRYlh.d.ts.map +0 -1
  197. package/dist/xy-axis-CxHC8z5E.js.map +0 -1
  198. package/docs-user/selecthorizontal.md +0 -32
package/dist/crumbs.js CHANGED
@@ -1,3 +1,1154 @@
1
- import { t as CrumbNavigationElement } from "./crumb-navigation-BrB0r14u.js";
1
+ import { t as TickledController } from "./tickled-controller-DkAU-0qL.js";
2
+ import { n as tickledStyles, t as tickledItemStyles } from "./tickled-styles-BrAHEj1q.js";
3
+ import { t as themeFallbacks } from "./fallbacks-DEE1nCTt.js";
4
+ import { n as safeCustomElement, t as __decorate } from "./decorate-VRK8rslU.js";
5
+ import { t as collectDataResult } from "./data-provider-35TyWclX.js";
6
+ import { t as scrollFadeStyles } from "./scroll-fade-CAs1EabA.js";
2
7
  import "./narrowed-text.js";
8
+ import { LitElement, css, html, nothing } from "lit";
9
+ import { property, state } from "lit/decorators.js";
10
+ //#region src/crumbs/crumb-navigation.ts
11
+ let CrumbNavigationElement = class CrumbNavigationElement extends LitElement {
12
+ constructor(..._args) {
13
+ super(..._args);
14
+ this._selectedNodes = /* @__PURE__ */ new Set();
15
+ this.exclusivity = `none`;
16
+ this.selectionFilter = `leaf`;
17
+ this._openIdx = -1;
18
+ this._isFocused = false;
19
+ this._menuOpen = false;
20
+ this._focusOnNarrowedText = false;
21
+ this.inputMode = ``;
22
+ this.noTruncate = false;
23
+ this.showSuggestions = false;
24
+ this.leadingCaret = false;
25
+ this.caretEmbedded = true;
26
+ this.hideRoot = false;
27
+ this.padEnding = false;
28
+ this.withUp = false;
29
+ this.#previousSelectionKey = ``;
30
+ this.#menuOpenedByKeyboard = false;
31
+ this.#currentOpenTrigger = null;
32
+ this.tickled = new TickledController(this, {
33
+ getItems: () => this.#getTickledItems(),
34
+ onTickle: (index) => {
35
+ if (index !== void 0) this.#onTickled(index);
36
+ }
37
+ });
38
+ this.#onFocus = () => {
39
+ this._isFocused = true;
40
+ };
41
+ this.#onBlur = () => {
42
+ this._isFocused = false;
43
+ };
44
+ this.#onHostClick = () => {
45
+ this.#getHostContainer()?.focus();
46
+ };
47
+ this.#onMenuTriggerClose = () => {
48
+ document.removeEventListener(`keydown`, this.#onMenuKeyNav, { capture: true });
49
+ this.#currentOpenTrigger = null;
50
+ this.#menuOpenedByKeyboard = false;
51
+ requestAnimationFrame(() => {
52
+ this.#getHostContainer()?.focus();
53
+ });
54
+ };
55
+ this.#onMenuKeyNav = (e) => {
56
+ if (!this.#currentOpenTrigger?.querySelector(`ixfx-menu-container`) || !this.#currentOpenTrigger?.isOpen) return;
57
+ switch (e.key) {
58
+ case `ArrowLeft`:
59
+ case `ArrowRight`: {
60
+ e.preventDefault();
61
+ e.stopPropagation();
62
+ this.#currentOpenTrigger.closeMenu();
63
+ const r = this.root;
64
+ const sel = this.selectedNode;
65
+ if (!r || !sel) break;
66
+ const totalItems = this.#getTickledItems().length;
67
+ const currentTickled = this.tickled.tickledId ?? -1;
68
+ if (e.key === `ArrowLeft`) {
69
+ if (currentTickled > 0) this.tickled.handleKeyboardNavigation(`previous`);
70
+ } else if (currentTickled < totalItems - 1) this.tickled.handleKeyboardNavigation(`next`);
71
+ break;
72
+ }
73
+ }
74
+ };
75
+ this.#onKeyDown = (e) => {
76
+ const r = this.root;
77
+ const sel = this.selectedNode;
78
+ if (!r || !sel) return;
79
+ const totalItems = this.#getTickledItems().length;
80
+ const currentTickled = this.tickled.tickledId ?? -1;
81
+ switch (e.key) {
82
+ case `ArrowRight`:
83
+ e.preventDefault();
84
+ if (currentTickled < totalItems - 1) this.tickled.handleKeyboardNavigation(`next`);
85
+ else if (currentTickled === totalItems - 1 && this.#canFocusSuggestions()) this.#shiftFocusToNarrowedText();
86
+ break;
87
+ case `ArrowLeft`:
88
+ e.preventDefault();
89
+ if (currentTickled > 0) this.tickled.handleKeyboardNavigation(`previous`);
90
+ break;
91
+ case `ArrowDown`:
92
+ case `ArrowUp`:
93
+ if (this.tickled.tickledId !== void 0) {
94
+ e.preventDefault();
95
+ if (this.#currentOpenTrigger?.isOpen) {
96
+ const popover = this.#currentOpenTrigger.querySelector(`ixfx-menu-container`)?.shadowRoot?.querySelector(`.popover`);
97
+ if (popover) popover.dispatchEvent(new KeyboardEvent(`keydown`, {
98
+ key: e.key,
99
+ bubbles: true
100
+ }));
101
+ } else this.#handleOpenCaretMenu();
102
+ }
103
+ break;
104
+ case `Enter`:
105
+ case ` `:
106
+ e.preventDefault();
107
+ this.#activateTickled();
108
+ break;
109
+ case `Escape`:
110
+ e.preventDefault();
111
+ this._menuOpen = false;
112
+ this.tickled.clearTickled();
113
+ this.blur();
114
+ break;
115
+ case `Tab`:
116
+ if (e.shiftKey) break;
117
+ if (currentTickled === totalItems - 1 && this.#canFocusSuggestions()) {
118
+ e.preventDefault();
119
+ this.#shiftFocusToNarrowedText();
120
+ }
121
+ }
122
+ };
123
+ this.#onNarrowedTextBlur = () => {
124
+ this._focusOnNarrowedText = false;
125
+ this.#shiftFocusToPath();
126
+ };
127
+ this.#onNarrowedTextShiftTab = () => {
128
+ this._focusOnNarrowedText = false;
129
+ this.#shiftFocusToPath();
130
+ };
131
+ this.#isWheelScrolling = false;
132
+ this.#onWheel = (e) => {
133
+ const wheelEvent = e;
134
+ if (wheelEvent.deltaX !== 0 || wheelEvent.deltaY !== 0) {
135
+ e.preventDefault();
136
+ if (!this.#isWheelScrolling) {
137
+ this.#isWheelScrolling = true;
138
+ this.tickled.clearTickled();
139
+ }
140
+ const hostContainer = this.#getHostContainer();
141
+ if (hostContainer) hostContainer.scrollLeft += wheelEvent.deltaX + wheelEvent.deltaY;
142
+ clearTimeout(this.#wheelScrollTimeout);
143
+ this.#wheelScrollTimeout = setTimeout(() => {
144
+ this.#isWheelScrolling = false;
145
+ }, 150);
146
+ }
147
+ };
148
+ this.#currentLeadingSiblings = void 0;
149
+ }
150
+ get selectedNodes() {
151
+ return this._selectedNodes;
152
+ }
153
+ #pendingNode;
154
+ #pendingController;
155
+ #modelUnsubscribe;
156
+ #previousSelectionKey;
157
+ #menuOpenedByKeyboard;
158
+ #currentOpenTrigger;
159
+ #getTickledItems() {
160
+ const r = this.root;
161
+ const sel = this.selectedNode;
162
+ if (!r || !sel) return [];
163
+ const pathNodes = this.#findPath(r, sel) ?? [r];
164
+ const items = [];
165
+ if (this.caretEmbedded) {
166
+ pathNodes.forEach((_node, i) => {
167
+ items.push({
168
+ id: i,
169
+ enabled: true
170
+ });
171
+ });
172
+ if (this.#canFocusSuggestions()) items.push({
173
+ id: pathNodes.length,
174
+ enabled: true
175
+ });
176
+ } else {
177
+ let segmentId = 0;
178
+ pathNodes.forEach((node, i) => {
179
+ const isLast = i === pathNodes.length - 1;
180
+ const hasChildren = node.item.isLeaf !== true && (node.children === void 0 || node.children.length > 0);
181
+ const showLeadingCaret = this.leadingCaret && (i > 0 || this.hideRoot) && hasChildren;
182
+ const showTrailingCaret = !this.leadingCaret && hasChildren || this.leadingCaret && isLast && hasChildren;
183
+ if (showLeadingCaret) items.push({
184
+ id: segmentId++,
185
+ enabled: true
186
+ });
187
+ items.push({
188
+ id: segmentId++,
189
+ enabled: true
190
+ });
191
+ if (showTrailingCaret) items.push({
192
+ id: segmentId++,
193
+ enabled: true
194
+ });
195
+ });
196
+ }
197
+ return items;
198
+ }
199
+ #onTickled(index) {
200
+ if (this.tickled.inputMode === `keyboard`) this.#scrollTickledIntoView(index);
201
+ }
202
+ #scrollTickledIntoView(index) {
203
+ const r = this.root;
204
+ const sel = this.selectedNode;
205
+ if (!r || !sel) return;
206
+ const pathNodes = this.#findPath(r, sel) ?? [r];
207
+ let el = null;
208
+ if (this.caretEmbedded && this.#canFocusSuggestions() && index === pathNodes.length) el = this.shadowRoot?.querySelector(`ixfx-narrowed-text`) ?? null;
209
+ else el = this.shadowRoot?.querySelectorAll(`.crumb-segment`)[index] ?? null;
210
+ el?.scrollIntoView({
211
+ inline: `nearest`,
212
+ block: `nearest`,
213
+ behavior: `smooth`
214
+ });
215
+ }
216
+ #hasChildOptions() {
217
+ const s = this.selectedNode;
218
+ if (s === void 0) return false;
219
+ const isPending = this.#pendingNode === s;
220
+ return (s.children ?? []).length > 0 || isPending;
221
+ }
222
+ /** True when the suggestions (narrowed-text) sub-part is renderable and focusable. */
223
+ #canFocusSuggestions() {
224
+ return this.showSuggestions && this.#hasChildOptions();
225
+ }
226
+ #onFocus;
227
+ #onBlur;
228
+ #getHostContainer() {
229
+ return this.shadowRoot?.querySelector(`.host-container`) ?? null;
230
+ }
231
+ #onHostClick;
232
+ #handleOpenCaretMenu() {
233
+ const currentTickled = this.tickled.tickledId;
234
+ if (currentTickled === void 0) return;
235
+ const r = this.root;
236
+ const sel = this.selectedNode;
237
+ if (!r || !sel) return;
238
+ const pathNodes = this.#findPath(r, sel) ?? [r];
239
+ if (this.caretEmbedded && this.#canFocusSuggestions() && currentTickled === pathNodes.length) {
240
+ this.#shiftFocusToNarrowedText();
241
+ return;
242
+ }
243
+ const trigger = (this.shadowRoot?.querySelectorAll(`.crumb-segment`))?.[currentTickled]?.querySelector(`.caret ixfx-menu-trigger`);
244
+ if (trigger) {
245
+ this.#menuOpenedByKeyboard = true;
246
+ this.#currentOpenTrigger = trigger;
247
+ document.addEventListener(`keydown`, this.#onMenuKeyNav, { capture: true });
248
+ trigger.openMenu();
249
+ this.#propagateInputModeToMenu();
250
+ }
251
+ }
252
+ #propagateInputModeToMenu() {
253
+ const inputMode = this.tickled.inputMode;
254
+ requestAnimationFrame(() => {
255
+ const container = this.#currentOpenTrigger?.querySelector(`ixfx-menu-container`);
256
+ if (container) container.setAttribute(`data-input-mode`, inputMode);
257
+ });
258
+ }
259
+ #onMenuTriggerClose;
260
+ #onMenuKeyNav;
261
+ #onKeyDown;
262
+ /** Activates the currently tickled item, as if it had been clicked. */
263
+ #activateTickled() {
264
+ const currentTickled = this.tickled.tickledId;
265
+ if (currentTickled === void 0) return;
266
+ const r = this.root;
267
+ const sel = this.selectedNode;
268
+ if (!r || !sel) return;
269
+ const pathNodes = this.#findPath(r, sel) ?? [r];
270
+ if (this.caretEmbedded && this.#canFocusSuggestions() && currentTickled === pathNodes.length) {
271
+ this.#shiftFocusToNarrowedText();
272
+ return;
273
+ }
274
+ (this.shadowRoot?.querySelector(`.crumb-segment[data-segment-id="${currentTickled}"]`))?.click();
275
+ }
276
+ #shiftFocusToNarrowedText() {
277
+ const narrowedText = this.shadowRoot?.querySelector(`ixfx-narrowed-text`);
278
+ if (narrowedText) {
279
+ this._menuOpen = false;
280
+ this._focusOnNarrowedText = true;
281
+ narrowedText.focus();
282
+ }
283
+ }
284
+ #shiftFocusToPath() {
285
+ this._focusOnNarrowedText = false;
286
+ const hostContainer = this.#getHostContainer();
287
+ if (hostContainer) hostContainer.focus();
288
+ }
289
+ #onNarrowedTextBlur;
290
+ #onNarrowedTextShiftTab;
291
+ select(node) {
292
+ this.#setSelected(node);
293
+ }
294
+ deselect(_node) {}
295
+ clearSelection() {
296
+ if (this.root) this.#setSelected(this.root);
297
+ }
298
+ /** Not implemented on crumb-navigation. */
299
+ selectAll() {}
300
+ /** Not implemented on crumb-navigation. */
301
+ deselectAll() {}
302
+ getNodes() {
303
+ if (!this.root) return [];
304
+ const result = [];
305
+ const visit = (node, depth) => {
306
+ result.push({
307
+ node,
308
+ depth
309
+ });
310
+ if (node.children) for (const child of node.children) visit(child, depth + 1);
311
+ };
312
+ for (const child of this.root.children ?? []) visit(child, 0);
313
+ return result;
314
+ }
315
+ updated(changedProps) {
316
+ if (changedProps.has(`model`)) {
317
+ this.#modelUnsubscribe?.();
318
+ this.#modelUnsubscribe = void 0;
319
+ const model = this.model;
320
+ if (model) {
321
+ this.root = model.root;
322
+ this.#modelUnsubscribe = model._subscribe((event) => {
323
+ if (event.type === `root`) {
324
+ this.root = event.root;
325
+ this.requestUpdate();
326
+ }
327
+ });
328
+ }
329
+ }
330
+ this.#runLayoutUpdate(changedProps);
331
+ }
332
+ connectedCallback() {
333
+ super.connectedCallback();
334
+ this.addEventListener(`click`, this.#onHostClick);
335
+ this.addEventListener(`wheel`, this.#onWheel, { passive: false });
336
+ }
337
+ disconnectedCallback() {
338
+ super.disconnectedCallback();
339
+ this.removeEventListener(`click`, this.#onHostClick);
340
+ this.removeEventListener(`wheel`, this.#onWheel);
341
+ this.#modelUnsubscribe?.();
342
+ this.#modelUnsubscribe = void 0;
343
+ document.removeEventListener(`keydown`, this.#onMenuKeyNav, { capture: true });
344
+ this.#currentOpenTrigger = null;
345
+ }
346
+ firstUpdated() {
347
+ const hostContainer = this.#getHostContainer();
348
+ if (hostContainer) hostContainer.scrollLeft = hostContainer.scrollWidth - hostContainer.clientWidth;
349
+ }
350
+ #isWheelScrolling;
351
+ #onWheel;
352
+ #wheelScrollTimeout;
353
+ #runLayoutUpdate(_changedProperties) {
354
+ if (!this.shadowRoot) return;
355
+ const hsiElement = this.shadowRoot.querySelector(`.horiz-select-items`);
356
+ if (!hsiElement) return;
357
+ const ulElement = hsiElement.querySelector(`ul`);
358
+ if (!ulElement) return;
359
+ const menuTrigger = this.shadowRoot.querySelector(`ixfx-menu-trigger`);
360
+ if (!menuTrigger) return;
361
+ if (ulElement.getBoundingClientRect().width > hsiElement.getBoundingClientRect().width) menuTrigger.style.display = `inline-flex`;
362
+ else menuTrigger.style.display = `none`;
363
+ for (const li of ulElement.querySelectorAll(`li`)) if ((li.getAttribute(`data-key`) ?? ``) === this.#previousSelectionKey) {
364
+ this.#scrollToValue(li);
365
+ break;
366
+ }
367
+ }
368
+ #scrollToValue(li) {
369
+ let l = li.offsetLeft;
370
+ const ul = li.parentElement;
371
+ if (!ul) return;
372
+ l -= ul.offsetLeft;
373
+ const horizSelectItems = ul.parentElement;
374
+ if (!horizSelectItems) return;
375
+ horizSelectItems.scroll({
376
+ left: l,
377
+ top: 0,
378
+ behavior: `smooth`
379
+ });
380
+ const horizSelect = horizSelectItems.parentElement;
381
+ if (!horizSelect) return;
382
+ horizSelect.style.minWidth = `${li.getBoundingClientRect().width}px`;
383
+ }
384
+ #abortPending() {
385
+ if (this.#pendingController) {
386
+ this.#pendingController.abort();
387
+ this.#pendingController = void 0;
388
+ this.#pendingNode = void 0;
389
+ }
390
+ }
391
+ #findPath(node, target, acc = []) {
392
+ const path = [...acc, node];
393
+ if (node === target) return path;
394
+ if (!node.children) return void 0;
395
+ for (const child of node.children) {
396
+ const result = this.#findPath(child, target, path);
397
+ if (result) return result;
398
+ }
399
+ }
400
+ #scrollSegmentIntoView(depth) {
401
+ if (!this.shadowRoot) return;
402
+ this.shadowRoot.querySelectorAll(`.crumb-segment`)[depth]?.scrollIntoView({
403
+ block: `nearest`,
404
+ inline: `start`
405
+ });
406
+ }
407
+ #updateTrailingWidth() {
408
+ if (!this.padEnding || !this.shadowRoot) return;
409
+ const hostContainer = this.shadowRoot.querySelector(`.host-container`);
410
+ const trailing = this.shadowRoot.querySelector(`.trailing`);
411
+ if (!hostContainer || !trailing) return;
412
+ const segments = this.shadowRoot.querySelectorAll(`.crumb-segment`);
413
+ const lastSegment = segments[segments.length - 1];
414
+ if (!lastSegment) return;
415
+ const containerWidth = hostContainer.getBoundingClientRect().width;
416
+ const itemWidth = lastSegment.getBoundingClientRect().width;
417
+ if (itemWidth < containerWidth) {
418
+ trailing.style.width = `${containerWidth - itemWidth - 5}px`;
419
+ requestAnimationFrame(() => {
420
+ if (hostContainer.scrollWidth <= hostContainer.clientWidth) hostContainer.style.setProperty(`--scroll-fade-x-start`, `0px`, `important`);
421
+ else hostContainer.style.removeProperty(`--scroll-fade-x-start`);
422
+ });
423
+ } else trailing.style.width = ``;
424
+ }
425
+ #setSelected(node) {
426
+ const previous = this.selectedNode;
427
+ if (previous) this.#previousSelectionKey = previous.item?.key ?? ``;
428
+ else this.#previousSelectionKey = ``;
429
+ const previousSet = previous ? /* @__PURE__ */ new Set([previous]) : /* @__PURE__ */ new Set();
430
+ this.selectedNode = node;
431
+ this._selectedNodes = /* @__PURE__ */ new Set([node]);
432
+ if (this.root) {
433
+ const path = this.#findPath(this.root, node);
434
+ if (path) {
435
+ const depth = this.hideRoot && path.length > 1 ? path.length - 2 : path.length - 1;
436
+ requestAnimationFrame(() => {
437
+ this.#updateTrailingWidth();
438
+ const hostContainer = this.#getHostContainer();
439
+ if (hostContainer) hostContainer.scrollLeft = 0;
440
+ this.#scrollSegmentIntoView(depth);
441
+ });
442
+ }
443
+ }
444
+ this.dispatchEvent(new CustomEvent(`select`, {
445
+ detail: {
446
+ selected: /* @__PURE__ */ new Set([node]),
447
+ previous: previousSet
448
+ },
449
+ bubbles: true,
450
+ composed: true
451
+ }));
452
+ }
453
+ #onLabelClick(node, depth) {
454
+ this._openIdx = -1;
455
+ this.#setSelected(node);
456
+ this.#scrollSegmentIntoView(depth);
457
+ this.dispatchEvent(new CustomEvent(`item-click`, {
458
+ detail: {
459
+ node,
460
+ depth
461
+ },
462
+ bubbles: true,
463
+ composed: true
464
+ }));
465
+ }
466
+ #onCaretClick(node, depth, e) {
467
+ e.preventDefault();
468
+ e.stopPropagation();
469
+ if (this._openIdx === depth) {
470
+ this._openIdx = -1;
471
+ return;
472
+ }
473
+ this._openIdx = depth;
474
+ if (node.children === void 0 && this.#pendingNode !== node) {
475
+ this.#abortPending();
476
+ this.#pendingNode = node;
477
+ const controller = new AbortController();
478
+ this.#pendingController = controller;
479
+ if (this.loadChildren) this.#invokeLoadChildren(node, depth, controller).catch(() => {});
480
+ }
481
+ this.requestUpdate();
482
+ }
483
+ async #invokeLoadChildren(node, depth, controller) {
484
+ try {
485
+ const children = [...await collectDataResult(this.loadChildren({
486
+ node,
487
+ depth
488
+ }, controller.signal), controller.signal)];
489
+ if (controller.signal.aborted) return;
490
+ node.children = children;
491
+ this.#pendingNode = void 0;
492
+ this.#pendingController = void 0;
493
+ this.dispatchEvent(new CustomEvent(`expand`, {
494
+ detail: {
495
+ node,
496
+ depth
497
+ },
498
+ bubbles: true,
499
+ composed: true
500
+ }));
501
+ this.requestUpdate();
502
+ } catch {
503
+ if (controller.signal.aborted) return;
504
+ node.children = [];
505
+ this.#pendingNode = void 0;
506
+ this.#pendingController = void 0;
507
+ this.requestUpdate();
508
+ }
509
+ }
510
+ #onSegmentMenuCommand(e, node, depth) {
511
+ const key = e.detail.command;
512
+ if (this.leadingCaret) {
513
+ const child = node.children?.find((c) => c.item.key === key);
514
+ if (child) {
515
+ this.#onChildClick(child, depth + 1);
516
+ return;
517
+ }
518
+ if (this.#currentLeadingSiblings) {
519
+ const sibling = this.#currentLeadingSiblings.find((c) => c.item.key === key);
520
+ if (sibling) this.#onChildClick(sibling, depth);
521
+ this.#currentLeadingSiblings = void 0;
522
+ return;
523
+ }
524
+ }
525
+ const child = node.children?.find((c) => c.item.key === key);
526
+ if (child) this.#onChildClick(child, depth + 1);
527
+ }
528
+ #currentLeadingSiblings;
529
+ #getSiblingMenuItems(pathNodes, index) {
530
+ if (index === 0) {
531
+ if (!this.hideRoot || !this.root) return [];
532
+ const siblings = [...this.root.children ?? []].sort((a, b) => (a.item.label ?? ``).localeCompare(b.item.label ?? ``));
533
+ this.#currentLeadingSiblings = siblings;
534
+ return siblings.map((sibling) => html`
535
+ <ixfx-menu-item
536
+ label="${sibling.item.label}"
537
+ command="${sibling.item.key}"
538
+ icon-name="${sibling.item.icon ?? nothing}"
539
+ @click=${() => {
540
+ this.#onChildClick(sibling, index);
541
+ }}
542
+ ></ixfx-menu-item>
543
+ `);
544
+ }
545
+ const siblings = [...pathNodes[index - 1].children ?? []].sort((a, b) => (a.item.label ?? ``).localeCompare(b.item.label ?? ``));
546
+ this.#currentLeadingSiblings = siblings;
547
+ return siblings.map((sibling) => html`
548
+ <ixfx-menu-item
549
+ label="${sibling.item.label}"
550
+ command="${sibling.item.key}"
551
+ icon-name="${sibling.item.icon ?? nothing}"
552
+ @click=${() => {
553
+ this.#onChildClick(sibling, index);
554
+ }}
555
+ ></ixfx-menu-item>
556
+ `);
557
+ }
558
+ #onChildClick(node, depth) {
559
+ this._openIdx = -1;
560
+ this.#setSelected(node);
561
+ this.dispatchEvent(new CustomEvent(`item-click`, {
562
+ detail: {
563
+ node,
564
+ depth
565
+ },
566
+ bubbles: true,
567
+ composed: true
568
+ }));
569
+ }
570
+ #onUpClick() {
571
+ const r = this.root;
572
+ const sel = this.selectedNode;
573
+ if (!r || !sel) return;
574
+ const path = this.#findPath(r, sel);
575
+ if (!path || path.length <= 1) return;
576
+ const parent = path[path.length - 2];
577
+ this.#setSelected(parent);
578
+ }
579
+ /** Navigate to a node by key path. Returns false if any key is not found in already-loaded children. */
580
+ navigateTo(keys) {
581
+ if (!this.root?.children) return false;
582
+ let current = this.root;
583
+ for (const key of keys) {
584
+ const found = current?.children?.find((n) => n.item.key === key);
585
+ if (!found) return false;
586
+ current = found;
587
+ }
588
+ if (current && current !== this.root) this.#setSelected(current);
589
+ return true;
590
+ }
591
+ /** Navigate to the first node matching the predicate. Returns false if not found. */
592
+ navigateToNode(predicate) {
593
+ if (!this.root) return false;
594
+ const keyPath = this.#findNodePath(this.root.children ?? [], predicate);
595
+ if (!keyPath) return false;
596
+ return this.navigateTo(keyPath);
597
+ }
598
+ #findNodePath(nodes, predicate, prefix = []) {
599
+ for (const node of nodes) {
600
+ const path = [...prefix, node.item.key];
601
+ if (predicate(node)) return path;
602
+ if (node.children?.length) {
603
+ const result = this.#findNodePath(node.children, predicate, path);
604
+ if (result) return result;
605
+ }
606
+ }
607
+ }
608
+ #onMenuCommand(e) {
609
+ const node = this.selectedNode;
610
+ if (!node?.children) return;
611
+ const key = e.detail.command;
612
+ const child = node.children.find((c) => c.item.key === key);
613
+ if (child) {
614
+ const depth = this.root ? this.#findPath(this.root, node)?.length ?? 1 : 1;
615
+ this.#onChildClick(child, depth);
616
+ }
617
+ }
618
+ render() {
619
+ const r = this.root;
620
+ const sel = this.selectedNode;
621
+ if (!r || !sel) return html``;
622
+ this.inputMode = this.tickled.inputMode;
623
+ let pathNodes = this.#findPath(r, sel) ?? [r];
624
+ if (this.hideRoot && pathNodes.length > 1) pathNodes = pathNodes.slice(1);
625
+ this.#hasChildOptions();
626
+ const isAtRoot = (() => {
627
+ return (this.#findPath(r, sel) ?? [r]).length <= 1;
628
+ })();
629
+ const parts = [];
630
+ let segmentId = 0;
631
+ pathNodes.forEach((node, i) => {
632
+ const isLast = i === pathNodes.length - 1;
633
+ const isPending = this.#pendingNode === node;
634
+ const isOpen = this._openIdx === i;
635
+ const hasChildren = node.item.isLeaf !== true && (node.children === void 0 || node.children.length > 0);
636
+ const isTickled = this.tickled.isTickled(i);
637
+ const showLeadingCaret = this.leadingCaret && (i > 0 || this.hideRoot) && hasChildren;
638
+ const showTrailingCaret = !this.leadingCaret && hasChildren || this.leadingCaret && isLast && hasChildren;
639
+ const siblingMenuItems = showLeadingCaret ? this.#getSiblingMenuItems(pathNodes, i) : [];
640
+ const childMenuItems = (node.children ?? []).map((c) => html`
641
+ <ixfx-menu-item
642
+ label="${c.item.label}"
643
+ command="${c.item.key}"
644
+ icon-name="${c.item.icon ?? nothing}"
645
+ ></ixfx-menu-item>
646
+ `);
647
+ if (this.caretEmbedded) {
648
+ const canTruncate = !this.noTruncate && !isLast && !isTickled;
649
+ parts.push(html`
650
+ <div class="item crumb-segment ${isTickled ? `tickled` : ``} ${canTruncate ? `truncatable` : ``} ${this.leadingCaret ? `leading-caret` : ``} ${isLast ? `last-label` : ``}" data-segment-id="${i}" data-path-index="${i}"
651
+ @click=${(e) => {
652
+ if (e.target.closest(`.caret`)) return;
653
+ this.#onLabelClick(node, i);
654
+ }}
655
+ >
656
+ ${showLeadingCaret ? html`
657
+ <span class="caret ${isOpen ? `open` : ``} ${isPending ? `pending` : ``}">
658
+ ${isPending ? html`<span class="loading-spinner"></span>` : html`
659
+ <ixfx-menu-trigger mode="trigger" label="›" .tabStop=${false} @menu-command=${(e) => this.#onSegmentMenuCommand(e, node, i)}>
660
+ <ixfx-menu-container slot="menu">
661
+ ${siblingMenuItems}
662
+ </ixfx-menu-container>
663
+ </ixfx-menu-trigger>
664
+ `}
665
+ </span>
666
+ ` : nothing}
667
+ <span class="label">${node.item.label}</span>
668
+ ${showTrailingCaret ? html`
669
+ <span class="caret ${isOpen ? `open` : ``} ${isPending ? `pending` : ``}">
670
+ ${isPending ? html`<span class="loading-spinner"></span>` : html`
671
+ <ixfx-menu-trigger mode="trigger" label="›" .tabStop=${false} placement="right-start" @menu-command=${(e) => this.#onSegmentMenuCommand(e, node, i)}>
672
+ <ixfx-menu-container slot="menu">
673
+ ${childMenuItems}
674
+ </ixfx-menu-container>
675
+ </ixfx-menu-trigger>
676
+ `}
677
+ </span>
678
+ ` : nothing}
679
+ </div>
680
+ `);
681
+ } else {
682
+ const leadingCaretTickled = showLeadingCaret ? this.tickled.isTickled(segmentId) : false;
683
+ if (showLeadingCaret) {
684
+ parts.push(html`
685
+ <div class="item crumb-segment ${leadingCaretTickled ? `tickled` : ``}" data-segment-id="${segmentId}" data-path-index="${i}"
686
+ @click=${(e) => {
687
+ const trigger = e.target.closest(`.crumb-segment`)?.querySelector(`ixfx-menu-trigger`);
688
+ if (trigger) trigger.openMenu();
689
+ }}
690
+ >
691
+ <span class="caret ${isOpen ? `open` : ``} ${isPending ? `pending` : ``}">
692
+ ${isPending ? html`<span class="loading-spinner"></span>` : html`
693
+ <ixfx-menu-trigger mode="trigger" label="›" .tabStop=${false} placement="right-start" @menu-command=${(e) => this.#onSegmentMenuCommand(e, node, i)}>
694
+ <ixfx-menu-container slot="menu">
695
+ ${siblingMenuItems}
696
+ </ixfx-menu-container>
697
+ </ixfx-menu-trigger>
698
+ `}
699
+ </span>
700
+ </div>
701
+ `);
702
+ segmentId++;
703
+ }
704
+ const labelTickled = this.tickled.isTickled(segmentId);
705
+ const canTruncate = !this.noTruncate && !isLast && !labelTickled;
706
+ parts.push(html`
707
+ <div class="item crumb-segment ${labelTickled ? `tickled` : ``} ${canTruncate ? `truncatable` : ``} ${isLast ? `last-label` : ``}" data-segment-id="${segmentId}" data-path-index="${i}"
708
+ @click=${(e) => {
709
+ if (e.target.closest(`.caret`)) return;
710
+ this.#onLabelClick(node, i);
711
+ }}
712
+ >
713
+ <span class="label">${node.item.label}</span>
714
+ </div>
715
+ `);
716
+ segmentId++;
717
+ if (showTrailingCaret) {
718
+ const trailingCaretTickled = this.tickled.isTickled(segmentId);
719
+ parts.push(html`
720
+ <div class="item crumb-segment ${trailingCaretTickled ? `tickled` : ``}" data-segment-id="${segmentId}" data-path-index="${i}"
721
+ @click=${(e) => {
722
+ const trigger = e.target.closest(`.crumb-segment`)?.querySelector(`ixfx-menu-trigger`);
723
+ if (trigger) trigger.openMenu();
724
+ }}
725
+ >
726
+ <span class="caret ${isOpen ? `open` : ``} ${isPending ? `pending` : ``}">
727
+ ${isPending ? html`<span class="loading-spinner"></span>` : html`
728
+ <ixfx-menu-trigger mode="trigger" label="›" .tabStop=${false} @menu-command=${(e) => this.#onSegmentMenuCommand(e, node, i)}>
729
+ <ixfx-menu-container slot="menu">
730
+ ${childMenuItems}
731
+ </ixfx-menu-container>
732
+ </ixfx-menu-trigger>
733
+ `}
734
+ </span>
735
+ </div>
736
+ `);
737
+ segmentId++;
738
+ }
739
+ }
740
+ });
741
+ return html`
742
+ <div class="crumb-wrapper">
743
+ ${this.withUp ? html`
744
+ <button
745
+ class="up-button"
746
+ ?disabled=${isAtRoot}
747
+ aria-label="Go up one level"
748
+ @click=${this.#onUpClick}
749
+ >
750
+ <ixfx-icon name="folder-up"></ixfx-icon>
751
+ </button>
752
+ ` : nothing}
753
+ <div
754
+ class="host-container scroll-fade-x"
755
+ tabindex="0"
756
+ @focus=${this.#onFocus}
757
+ @blur=${this.#onBlur}
758
+ @keydown=${this.#onKeyDown}
759
+ @click=${this.#onHostClick}
760
+ @menu-trigger-close=${this.#onMenuTriggerClose}
761
+ @mouseout=${(e) => this.tickled.handlePointerLeave(e)}
762
+ >
763
+ <div class="parts"
764
+ @mouseover=${(e) => {
765
+ if (this.#isWheelScrolling) return;
766
+ const target = e.target;
767
+ if (!(target instanceof HTMLElement)) return;
768
+ const segment = target.closest(`.crumb-segment`);
769
+ if (!segment) return;
770
+ const segmentId = parseInt(segment.dataset.segmentId ?? ``, 10);
771
+ if (Number.isNaN(segmentId)) return;
772
+ const pathIndex = parseInt(segment.dataset.pathIndex ?? ``, 10);
773
+ const node = pathNodes[pathIndex];
774
+ if (!node) return;
775
+ this.tickled.handlePointerEnter(segmentId);
776
+ this.dispatchEvent(new CustomEvent(`tickle`, {
777
+ detail: {
778
+ node,
779
+ depth: pathIndex
780
+ },
781
+ bubbles: true,
782
+ composed: true
783
+ }));
784
+ }}
785
+ >${parts}${this.padEnding ? html`<div class="trailing"></div>` : nothing}</div>
786
+ ${this.showSuggestions ? this.#renderChildOptions() : nothing}
787
+ </div>
788
+ </div>
789
+ `;
790
+ }
791
+ #renderChildOptions() {
792
+ const s = this.selectedNode;
793
+ if (s === void 0) return nothing;
794
+ const isPending = this.#pendingNode === s;
795
+ const children = s.children ?? [];
796
+ if (children.length === 0 && !isPending) return nothing;
797
+ if (isPending) return html`
798
+ <div class="horiz-select">
799
+ <div class="horiz-select-items">
800
+ <span class="loading-spinner"></span>
801
+ </div>
802
+ </div>
803
+ `;
804
+ return html`
805
+ <ixfx-narrowed-text
806
+ class="horiz-select"
807
+ selection-mode="single"
808
+ orientation="horizontal"
809
+ nowrap
810
+ width=${this.#getChildOptionsWidth()}
811
+ @change=${this.#onNarrowedTextChange}
812
+ @narrowed-text-blur=${this.#onNarrowedTextBlur}
813
+ @narrowed-text-shift-tab=${this.#onNarrowedTextShiftTab}
814
+ >
815
+ ${children.map((c) => html`
816
+ <span data-value="${c.item.key}" data-label="${c.item.label}"></span>
817
+ `)}
818
+ </ixfx-narrowed-text>
819
+ `;
820
+ }
821
+ #onNarrowedTextChange(e) {
822
+ e.stopPropagation();
823
+ const detail = e.detail;
824
+ const node = this.selectedNode;
825
+ if (!node?.children) return;
826
+ const child = node.children.find((c) => c.item.key === detail.item.value);
827
+ if (child) {
828
+ const depth = this.root ? this.#findPath(this.root, node)?.length ?? 1 : 1;
829
+ this.#onChildClick(child, depth);
830
+ }
831
+ }
832
+ #getChildOptionsWidth() {
833
+ return `var(--child-options-width, 50ch)`;
834
+ }
835
+ static {
836
+ this.styles = [
837
+ themeFallbacks,
838
+ tickledStyles,
839
+ tickledItemStyles,
840
+ scrollFadeStyles(`x`),
841
+ css`
842
+ :host {
843
+ --crumb-item-bg: var(--item-bg, transparent);
844
+ --crumb-item-bg-tickled: var(--item-bg-tickled);
845
+ --crumb-item-text-tickled: var(--item-text-tickled);
846
+ --crumb-item-border: var(--item-border);
847
+ --crumb-item-border-tickled: var(--item-border-tickled);
848
+ --crumb-item-padding: var(--space-s, 2px);
849
+ --crumb-item-border-radius: var(--item-border-radius);
850
+ --crumb-item-gap: var(--item-gap, var(--space-xs));
851
+ --crumb-separator: var(--surface-muted-text);
852
+ --crumb-truncate-min-width: 10ch;
853
+ display: flex;
854
+ width: 100%;
855
+ }
856
+
857
+ /* Outline goes on :host, not .host-container: .host-container carries the
858
+ * scroll-fade mask-image, and CSS masks clip everything painted outside
859
+ * the masked box - including an outline, which paints outside the border edge. */
860
+ :host(:focus-within) {
861
+ outline: 2px solid var(--accent);
862
+ outline-offset: 2px;
863
+ }
864
+
865
+ .crumb-wrapper {
866
+ display: flex;
867
+ align-items: center;
868
+ gap: var(--crumb-item-gap);
869
+ width: 100%;
870
+ }
871
+
872
+ .up-button {
873
+ display: inline-flex;
874
+ align-items: center;
875
+ justify-content: center;
876
+ flex-shrink: 0;
877
+ background: var(--crumb-item-bg);
878
+ border: var(--crumb-item-border);
879
+ border-radius: var(--crumb-item-border-radius);
880
+ padding: var(--crumb-item-padding);
881
+ cursor: pointer;
882
+ color: inherit;
883
+ }
884
+
885
+ .up-button:disabled {
886
+ opacity: 0.5;
887
+ cursor: not-allowed;
888
+ }
889
+
890
+ .up-button:hover:not(:disabled) {
891
+ background: var(--crumb-item-bg-tickled);
892
+ border-color: var(--crumb-item-border-tickled);
893
+ }
894
+
895
+ .up-button ixfx-icon {
896
+ width: 1em;
897
+ height: 1em;
898
+ pointer-events: none;
899
+ }
900
+
901
+ /* Edge fade (shows/hides based on scroll position) comes from the
902
+ * shared .scroll-fade-x class - see ../styles/scroll-fade.ts */
903
+ .host-container {
904
+ display: flex;
905
+ flex: 1;
906
+ min-width: 0;
907
+ outline: none;
908
+ overflow: scroll;
909
+ scroll-snap-type: x mandatory;
910
+ scrollbar-width: none;
911
+ overscroll-behavior: none;
912
+ }
913
+
914
+ .host-container::-webkit-scrollbar {
915
+ display: none;
916
+ }
917
+
918
+ .parts {
919
+ display: flex;
920
+ gap: var(--space-s);
921
+ }
922
+
923
+ .trailing {
924
+ flex-shrink: 0;
925
+ }
926
+
927
+ .crumb-segment {
928
+ display: flex;
929
+ align-items: center;
930
+ flex-shrink: 0;
931
+ background: var(--crumb-item-bg);
932
+ border: var(--crumb-item-border);
933
+ border-radius: var(--crumb-item-border-radius);
934
+ padding:0;
935
+ }
936
+
937
+ .crumb-segment.leading-caret {
938
+ flex-direction: row;
939
+ }
940
+
941
+ .crumb-segment.tickled {
942
+ background: var(--crumb-item-bg-tickled);
943
+ border-color: var(--crumb-item-border-tickled);
944
+ color: var(--crumb-item-text-tickled);
945
+ flex-shrink: 0;
946
+ min-width: auto;
947
+ }
948
+
949
+ .crumb-segment.truncatable {
950
+ flex-shrink: 1;
951
+ min-width: min(var(--crumb-truncate-min-width), auto);
952
+ overflow: hidden;
953
+ }
954
+
955
+ .crumb-segment.truncatable .label {
956
+ overflow: hidden;
957
+ text-overflow: ellipsis;
958
+ white-space: nowrap;
959
+ min-width: 0;
960
+ }
961
+
962
+ .label {
963
+ padding: var(--crumb-item-padding);
964
+ cursor: pointer;
965
+ white-space: nowrap;
966
+ color: inherit;
967
+ }
968
+
969
+ .crumb-segment.last-label .label {
970
+ color: var(--crumb-last-label-color, inherit);
971
+ font-style: var(--crumb-last-label-font-style, inherit);
972
+ font-weight: var(--crumb-last-label-font-weight, inherit);
973
+ text-decoration: var(--crumb-last-label-text-decoration, inherit);
974
+ text-underline-offset: var(--crumb-last-label-text-underline-offset, auto);
975
+ }
976
+
977
+ .crumb-segment.tickled .label {
978
+ color: var(--crumb-item-text-tickled);
979
+ overflow: visible;
980
+ text-overflow: clip;
981
+ }
982
+
983
+ .crumb-segment ixfx-menu-trigger {
984
+ --ixfx-button-padding: var(--space-s, 2px);
985
+ }
986
+
987
+ .caret {
988
+ position: relative;
989
+ cursor: pointer;
990
+ color: var(--crumb-separator);
991
+ display: inline-flex;
992
+ align-items: center;
993
+ justify-content: center;
994
+ transition: all 0.2s ease-in-out;
995
+ }
996
+
997
+ .caret.open {
998
+ background: color-mix(in srgb, var(--surface-5) 40%, transparent);
999
+ }
1000
+
1001
+ .caret.pending {
1002
+ display: inline-flex;
1003
+ }
1004
+
1005
+ .caret > ixfx-menu-trigger {
1006
+ display: inline-flex;
1007
+ margin: 0;
1008
+ --_trigger-hover-bg: transparent;
1009
+ --_trigger-hover-opacity: 0.6;
1010
+ }
1011
+
1012
+ .caret > ixfx-menu-trigger::part(base) {
1013
+ background: none;
1014
+ border: none;
1015
+ color: inherit;
1016
+ cursor: pointer;
1017
+ font-size: inherit;
1018
+ line-height: 1;
1019
+ }
1020
+
1021
+ .caret > ixfx-menu-trigger::part(base):hover {
1022
+ background: none;
1023
+ opacity: 0.6;
1024
+ }
1025
+
1026
+ .separator {
1027
+ margin-left: var(--crumb-item-gap);
1028
+ margin-right: var(--crumb-item-gap);
1029
+ color: var(--crumb-separator);
1030
+ }
1031
+
1032
+ .separator-trigger {
1033
+ display: inline-flex;
1034
+ margin-left: var(--crumb-item-gap);
1035
+ margin-right: var(--crumb-item-gap);
1036
+ --_trigger-hover-bg: transparent;
1037
+ --_trigger-hover-opacity: 0.6;
1038
+ }
1039
+
1040
+ .separator-trigger::part(base) {
1041
+ background: none;
1042
+ border: none;
1043
+ color: var(--crumb-separator);
1044
+ padding: 0;
1045
+ cursor: pointer;
1046
+ font-size: inherit;
1047
+ line-height: 1;
1048
+ }
1049
+
1050
+ .separator-trigger::part(base):hover {
1051
+ background: none;
1052
+ opacity: 0.6;
1053
+ }
1054
+
1055
+ .separator-trigger.tickled::part(base) {
1056
+ }
1057
+
1058
+ ixfx-menu-trigger {
1059
+ display: none;
1060
+ }
1061
+
1062
+ .horiz-select > ixfx-menu-trigger {
1063
+ display: inline-flex;
1064
+ }
1065
+
1066
+ .horiz-select {
1067
+ display: flex;
1068
+ min-width: 5ch;
1069
+ }
1070
+
1071
+ .loading-spinner {
1072
+ display: inline-block;
1073
+ width: 12px;
1074
+ height: 12px;
1075
+ border: 2px solid var(--surface-5);
1076
+ border-top-color: var(--accent);
1077
+ border-radius: 50%;
1078
+ animation: crumb-spin 0.7s linear infinite;
1079
+ }
1080
+
1081
+ @keyframes crumb-spin {
1082
+ to { transform: rotate(360deg); }
1083
+ }
1084
+
1085
+ ::slotted(*), option {
1086
+ user-select: none;
1087
+ scroll-snap-align: start;
1088
+ }
1089
+ `
1090
+ ];
1091
+ }
1092
+ };
1093
+ __decorate([property({ type: Object })], CrumbNavigationElement.prototype, "root", void 0);
1094
+ __decorate([property({ attribute: false })], CrumbNavigationElement.prototype, "model", void 0);
1095
+ __decorate([property({ type: Object })], CrumbNavigationElement.prototype, "selectedNode", void 0);
1096
+ __decorate([state()], CrumbNavigationElement.prototype, "_selectedNodes", void 0);
1097
+ __decorate([property({ attribute: false })], CrumbNavigationElement.prototype, "loadChildren", void 0);
1098
+ __decorate([property({ attribute: false })], CrumbNavigationElement.prototype, "filterPredicate", void 0);
1099
+ __decorate([property({
1100
+ reflect: true,
1101
+ attribute: `exclusivity`
1102
+ })], CrumbNavigationElement.prototype, "exclusivity", void 0);
1103
+ __decorate([property({
1104
+ reflect: true,
1105
+ attribute: `selection-filter`
1106
+ })], CrumbNavigationElement.prototype, "selectionFilter", void 0);
1107
+ __decorate([state()], CrumbNavigationElement.prototype, "_openIdx", void 0);
1108
+ __decorate([state()], CrumbNavigationElement.prototype, "_isFocused", void 0);
1109
+ __decorate([state()], CrumbNavigationElement.prototype, "_menuOpen", void 0);
1110
+ __decorate([state()], CrumbNavigationElement.prototype, "_focusOnNarrowedText", void 0);
1111
+ __decorate([property({
1112
+ reflect: true,
1113
+ attribute: `input-mode`
1114
+ })], CrumbNavigationElement.prototype, "inputMode", void 0);
1115
+ __decorate([property({
1116
+ type: Boolean,
1117
+ reflect: true,
1118
+ attribute: `no-truncate`
1119
+ })], CrumbNavigationElement.prototype, "noTruncate", void 0);
1120
+ __decorate([property({
1121
+ type: Boolean,
1122
+ reflect: true,
1123
+ attribute: `show-suggestions`
1124
+ })], CrumbNavigationElement.prototype, "showSuggestions", void 0);
1125
+ __decorate([property({
1126
+ type: Boolean,
1127
+ reflect: true,
1128
+ attribute: `leading-caret`
1129
+ })], CrumbNavigationElement.prototype, "leadingCaret", void 0);
1130
+ __decorate([property({
1131
+ type: Boolean,
1132
+ reflect: true,
1133
+ attribute: `caret-embedded`
1134
+ })], CrumbNavigationElement.prototype, "caretEmbedded", void 0);
1135
+ __decorate([property({
1136
+ type: Boolean,
1137
+ reflect: true,
1138
+ attribute: `hide-root`
1139
+ })], CrumbNavigationElement.prototype, "hideRoot", void 0);
1140
+ __decorate([property({
1141
+ type: Boolean,
1142
+ reflect: true,
1143
+ attribute: `pad-ending`
1144
+ })], CrumbNavigationElement.prototype, "padEnding", void 0);
1145
+ __decorate([property({
1146
+ type: Boolean,
1147
+ reflect: true,
1148
+ attribute: `with-up`
1149
+ })], CrumbNavigationElement.prototype, "withUp", void 0);
1150
+ CrumbNavigationElement = __decorate([safeCustomElement(`ixfx-crumb-navigation`)], CrumbNavigationElement);
1151
+ //#endregion
3
1152
  export { CrumbNavigationElement };
1153
+
1154
+ //# sourceMappingURL=crumbs.js.map