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