@ixfx/components 0.5.15 → 0.5.17

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 (62) hide show
  1. package/bundle/index.d.ts +17 -4
  2. package/bundle/index.d.ts.map +1 -1
  3. package/bundle/index.js +482 -103
  4. package/bundle/index.js.map +1 -1
  5. package/dist/ac-text.js +2 -2
  6. package/dist/checkbox.js +1 -1
  7. package/dist/{colour-picker-DGso9ZOp.js → colour-picker-DbypcEp6.js} +2 -2
  8. package/dist/{colour-picker-DGso9ZOp.js.map → colour-picker-DbypcEp6.js.map} +1 -1
  9. package/dist/colour-picker.js +1 -1
  10. package/dist/crumb-navigation-DFqTzElw.js +1149 -0
  11. package/dist/crumb-navigation-DFqTzElw.js.map +1 -0
  12. package/dist/crumbs.d.ts +16 -3
  13. package/dist/crumbs.d.ts.map +1 -1
  14. package/dist/crumbs.js +1 -821
  15. package/dist/data-display.js +1 -1
  16. package/dist/editable-label.js +1 -1
  17. package/dist/{fallbacks-pNMOMx38.js → fallbacks-DPgPEqo8.js} +51 -1
  18. package/dist/{fallbacks-pNMOMx38.js.map → fallbacks-DPgPEqo8.js.map} +1 -1
  19. package/dist/{icon-B22XVlCt.js → icon-DG9Lm3LT.js} +2 -2
  20. package/dist/{icon-B22XVlCt.js.map → icon-DG9Lm3LT.js.map} +1 -1
  21. package/dist/icons.js +1 -1
  22. package/dist/index.d.ts +1 -1
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +16 -14
  25. package/dist/index.js.map +1 -1
  26. package/dist/labelled-radial-input.js +2 -2
  27. package/dist/{menu-nuOIUD9K.js → menu-B8gE2MSo.js} +4 -4
  28. package/dist/{menu-nuOIUD9K.js.map → menu-B8gE2MSo.js.map} +1 -1
  29. package/dist/{menu-item-BBTZnjAR.js → menu-item-BYRvfyXK.js} +5 -4
  30. package/dist/menu-item-BYRvfyXK.js.map +1 -0
  31. package/dist/menu-item-DSyf9f_L.d.ts.map +1 -1
  32. package/dist/menu.js +2 -2
  33. package/dist/miller.js +1 -1
  34. package/dist/narrowed-text.js +1 -1
  35. package/dist/panel.js +2 -2
  36. package/dist/polar-pad.js +1 -1
  37. package/dist/{radial-input-DxCrt_NE.js → radial-input-CVxGH-Cp.js} +2 -2
  38. package/dist/{radial-input-DxCrt_NE.js.map → radial-input-CVxGH-Cp.js.map} +1 -1
  39. package/dist/radial-input.js +1 -1
  40. package/dist/range-input.js +1 -1
  41. package/dist/range.js +1 -1
  42. package/dist/selecthorizontal.js +1 -1
  43. package/dist/{slider-input-DFpG23bS.js → slider-input-CzmSVgm7.js} +2 -2
  44. package/dist/{slider-input-DFpG23bS.js.map → slider-input-CzmSVgm7.js.map} +1 -1
  45. package/dist/slider-input.js +1 -1
  46. package/dist/split-layout.js +1 -1
  47. package/dist/swipe.js +1 -1
  48. package/dist/{tab-list-DhkVhRQc.js → tab-list-6W5CgXix.js} +2 -2
  49. package/dist/{tab-list-DhkVhRQc.js.map → tab-list-6W5CgXix.js.map} +1 -1
  50. package/dist/{tab-list-BohmbvqF.d.ts → tab-list-ih-0I0NE.d.ts} +2 -2
  51. package/dist/{tab-list-BohmbvqF.d.ts.map → tab-list-ih-0I0NE.d.ts.map} +1 -1
  52. package/dist/tabs.d.ts +1 -1
  53. package/dist/tabs.js +1 -1
  54. package/dist/{tree-CL-txefQ.js → tree-BtiiDX1a.js} +2 -2
  55. package/dist/{tree-CL-txefQ.js.map → tree-BtiiDX1a.js.map} +1 -1
  56. package/dist/tree.js +1 -1
  57. package/dist/xy-pad.js +1 -1
  58. package/docs-user/crumbs.md +38 -9
  59. package/docs-user/tabs.md +19 -2
  60. package/package.json +1 -1
  61. package/dist/crumbs.js.map +0 -1
  62. package/dist/menu-item-BBTZnjAR.js.map +0 -1
package/dist/crumbs.js CHANGED
@@ -1,823 +1,3 @@
1
- import { n as tickledStyles, r as TickledController, t as tickledItemStyles } from "./tickled-styles-fNDdqf6l.js";
2
- import { t as themeFallbacks } from "./fallbacks-pNMOMx38.js";
3
- import { n as safeCustomElement, t as __decorate } from "./decorate-VRK8rslU.js";
4
- import "./interaction-D6efrBLo.js";
5
- import "./menu-item-BBTZnjAR.js";
6
- import { t as collectDataResult } from "./data-provider-35TyWclX.js";
1
+ import { t as CrumbNavigationElement } from "./crumb-navigation-DFqTzElw.js";
7
2
  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.#previousSelectionKey = ``;
23
- this.#menuOpenedByKeyboard = false;
24
- this.#currentOpenTrigger = null;
25
- this.tickled = new TickledController(this, {
26
- getItems: () => this.#getTickledItems(),
27
- onTickle: (index) => {
28
- if (index !== void 0) this.#onTickled(index);
29
- }
30
- });
31
- this.#onFocus = () => {
32
- this._isFocused = true;
33
- };
34
- this.#onBlur = () => {
35
- this._isFocused = false;
36
- };
37
- this.#onHostClick = () => {
38
- this.shadowRoot?.querySelector(`.host-container`)?.focus();
39
- };
40
- this.#onMenuTriggerClose = () => {
41
- document.removeEventListener(`keydown`, this.#onMenuKeyNav, { capture: true });
42
- this.#currentOpenTrigger = null;
43
- this.#menuOpenedByKeyboard = false;
44
- requestAnimationFrame(() => {
45
- this.shadowRoot?.querySelector(`.host-container`)?.focus();
46
- });
47
- };
48
- this.#onMenuKeyNav = (e) => {
49
- if (!this.#currentOpenTrigger?.querySelector(`ixfx-menu-container`) || !this.#currentOpenTrigger?.isOpen) return;
50
- switch (e.key) {
51
- case `ArrowLeft`:
52
- case `ArrowRight`: {
53
- e.preventDefault();
54
- e.stopPropagation();
55
- this.#currentOpenTrigger.closeMenu();
56
- const r = this.root;
57
- const sel = this.selectedNode;
58
- if (!r || !sel) break;
59
- const totalItems = (this.#findPath(r, sel) ?? [r]).length + (this.#hasChildOptions() ? 1 : 0);
60
- const currentTickled = this.tickled.tickledId ?? -1;
61
- if (e.key === `ArrowLeft`) {
62
- if (currentTickled > 0) this.tickled.handleKeyboardNavigation(`previous`);
63
- } else if (currentTickled < totalItems - 1) this.tickled.handleKeyboardNavigation(`next`);
64
- break;
65
- }
66
- }
67
- };
68
- this.#onKeyDown = (e) => {
69
- const r = this.root;
70
- const sel = this.selectedNode;
71
- if (!r || !sel) return;
72
- const totalItems = (this.#findPath(r, sel) ?? [r]).length + (this.#hasChildOptions() ? 1 : 0);
73
- const currentTickled = this.tickled.tickledId ?? -1;
74
- switch (e.key) {
75
- case `ArrowRight`:
76
- e.preventDefault();
77
- if (currentTickled < totalItems - 1) this.tickled.handleKeyboardNavigation(`next`);
78
- else if (currentTickled === totalItems - 1) this.#shiftFocusToNarrowedText();
79
- break;
80
- case `ArrowLeft`:
81
- e.preventDefault();
82
- if (currentTickled > 0) this.tickled.handleKeyboardNavigation(`previous`);
83
- break;
84
- case `ArrowDown`:
85
- case `ArrowUp`:
86
- if (this.tickled.tickledId !== void 0) {
87
- e.preventDefault();
88
- if (this.#currentOpenTrigger?.isOpen) {
89
- const popover = this.#currentOpenTrigger.querySelector(`ixfx-menu-container`)?.shadowRoot?.querySelector(`.popover`);
90
- if (popover) popover.dispatchEvent(new KeyboardEvent(`keydown`, {
91
- key: e.key,
92
- bubbles: true
93
- }));
94
- } else this.#handleOpenCaretMenu();
95
- }
96
- break;
97
- case `Enter`:
98
- e.preventDefault();
99
- this.#handleEnter();
100
- break;
101
- case `Escape`:
102
- e.preventDefault();
103
- this._menuOpen = false;
104
- this.tickled.clearTickled();
105
- this.blur();
106
- break;
107
- case `Tab`: if (e.shiftKey) {
108
- e.preventDefault();
109
- this.#shiftFocusToPath();
110
- } else if (currentTickled === totalItems - 1) {
111
- e.preventDefault();
112
- this._menuOpen = false;
113
- this._focusOnNarrowedText = true;
114
- const narrowedText = this.shadowRoot?.querySelector(`ixfx-narrowed-text`);
115
- if (narrowedText) narrowedText.focus();
116
- }
117
- }
118
- };
119
- this.#onNarrowedTextBlur = () => {
120
- this._focusOnNarrowedText = false;
121
- this.#shiftFocusToPath();
122
- };
123
- this.#onNarrowedTextShiftTab = () => {
124
- this._focusOnNarrowedText = false;
125
- this.#shiftFocusToPath();
126
- };
127
- }
128
- get selectedNodes() {
129
- return this._selectedNodes;
130
- }
131
- #pendingNode;
132
- #pendingController;
133
- #modelUnsubscribe;
134
- #previousSelectionKey;
135
- #menuOpenedByKeyboard;
136
- #currentOpenTrigger;
137
- #getTickledItems() {
138
- const r = this.root;
139
- const sel = this.selectedNode;
140
- if (!r || !sel) return [];
141
- const pathNodes = this.#findPath(r, sel) ?? [r];
142
- const items = [];
143
- pathNodes.forEach((_node, i) => {
144
- items.push({
145
- id: i,
146
- enabled: true
147
- });
148
- });
149
- if (this.#hasChildOptions()) items.push({
150
- id: pathNodes.length,
151
- enabled: true
152
- });
153
- return items;
154
- }
155
- #onTickled(index) {
156
- const r = this.root;
157
- const sel = this.selectedNode;
158
- if (!r || !sel) return;
159
- const pathNodes = this.#findPath(r, sel) ?? [r];
160
- const hasChildOptions = this.#hasChildOptions();
161
- if (index < pathNodes.length) this._openIdx = index;
162
- else if (hasChildOptions && index === pathNodes.length) this._openIdx = pathNodes.length - 1;
163
- if (this.tickled.inputMode === `keyboard`) this.#scrollTickledIntoView(index);
164
- }
165
- #scrollTickledIntoView(index) {
166
- const r = this.root;
167
- const sel = this.selectedNode;
168
- if (!r || !sel) return;
169
- const pathNodes = this.#findPath(r, sel) ?? [r];
170
- let el = null;
171
- if (index < pathNodes.length) el = this.shadowRoot?.querySelectorAll(`.crumb-segment`)[index] ?? null;
172
- else if (index === pathNodes.length) el = this.shadowRoot?.querySelector(`#child-options-trigger`) ?? null;
173
- el?.scrollIntoView({
174
- inline: `nearest`,
175
- block: `nearest`,
176
- behavior: `smooth`
177
- });
178
- }
179
- #hasChildOptions() {
180
- const s = this.selectedNode;
181
- if (s === void 0) return false;
182
- const isPending = this.#pendingNode === s;
183
- return (s.children ?? []).length > 0 || isPending;
184
- }
185
- #onFocus;
186
- #onBlur;
187
- #onHostClick;
188
- #handleOpenCaretMenu() {
189
- const currentTickled = this.tickled.tickledId;
190
- if (currentTickled === void 0) return;
191
- const r = this.root;
192
- const sel = this.selectedNode;
193
- if (!r || !sel) return;
194
- if (currentTickled < (this.#findPath(r, sel) ?? [r]).length - 1) {
195
- const trigger = (this.shadowRoot?.querySelectorAll(`.crumb-segment`))?.[currentTickled]?.querySelector(`.caret ixfx-menu-trigger`);
196
- if (trigger) {
197
- this.#menuOpenedByKeyboard = true;
198
- this.#currentOpenTrigger = trigger;
199
- document.addEventListener(`keydown`, this.#onMenuKeyNav, { capture: true });
200
- trigger.openMenu();
201
- this.#propagateInputModeToMenu();
202
- return;
203
- }
204
- }
205
- if (this.#hasChildOptions()) {
206
- const trigger = this.shadowRoot?.querySelector(`#child-options-trigger`);
207
- if (trigger) {
208
- this.#menuOpenedByKeyboard = true;
209
- this.#currentOpenTrigger = trigger;
210
- document.addEventListener(`keydown`, this.#onMenuKeyNav, { capture: true });
211
- trigger.openMenu();
212
- this.#propagateInputModeToMenu();
213
- }
214
- }
215
- }
216
- #propagateInputModeToMenu() {
217
- const inputMode = this.tickled.inputMode;
218
- requestAnimationFrame(() => {
219
- const container = this.#currentOpenTrigger?.querySelector(`ixfx-menu-container`);
220
- if (container) container.setAttribute(`data-input-mode`, inputMode);
221
- });
222
- }
223
- #onMenuTriggerClose;
224
- #onMenuKeyNav;
225
- #onKeyDown;
226
- #handleEnter() {
227
- const r = this.root;
228
- const sel = this.selectedNode;
229
- if (!r || !sel) return;
230
- const pathNodes = this.#findPath(r, sel) ?? [r];
231
- const hasChildOptions = this.#hasChildOptions();
232
- const currentTickled = this.tickled.tickledId ?? -1;
233
- if (currentTickled < pathNodes.length) {
234
- const node = pathNodes[currentTickled];
235
- this.#setSelected(node);
236
- this._menuOpen = false;
237
- this.dispatchEvent(new CustomEvent(`item-click`, {
238
- detail: {
239
- node,
240
- depth: currentTickled
241
- },
242
- bubbles: true,
243
- composed: true
244
- }));
245
- } else if (hasChildOptions && currentTickled === pathNodes.length) {
246
- this._menuOpen = true;
247
- this.#menuOpenedByKeyboard = true;
248
- const trigger = this.shadowRoot?.querySelector(`#child-options-trigger`);
249
- if (trigger) {
250
- this.#currentOpenTrigger = trigger;
251
- document.addEventListener(`keydown`, this.#onMenuKeyNav, { capture: true });
252
- trigger.openMenu();
253
- this.#propagateInputModeToMenu();
254
- }
255
- }
256
- }
257
- #shiftFocusToNarrowedText() {
258
- const narrowedText = this.shadowRoot?.querySelector(`ixfx-narrowed-text`);
259
- if (narrowedText) {
260
- this._focusOnNarrowedText = true;
261
- narrowedText.focus();
262
- }
263
- }
264
- #shiftFocusToPath() {
265
- this._focusOnNarrowedText = false;
266
- const hostContainer = this.shadowRoot?.querySelector(`.host-container`);
267
- if (hostContainer) hostContainer.focus();
268
- }
269
- #onNarrowedTextBlur;
270
- #onNarrowedTextShiftTab;
271
- select(node) {
272
- this.#setSelected(node);
273
- }
274
- deselect(_node) {}
275
- clearSelection() {
276
- if (this.root) this.#setSelected(this.root);
277
- }
278
- /** Not implemented on crumb-navigation. */
279
- selectAll() {}
280
- /** Not implemented on crumb-navigation. */
281
- deselectAll() {}
282
- getNodes() {
283
- if (!this.root) return [];
284
- const result = [];
285
- const visit = (node, depth) => {
286
- result.push({
287
- node,
288
- depth
289
- });
290
- if (node.children) for (const child of node.children) visit(child, depth + 1);
291
- };
292
- for (const child of this.root.children ?? []) visit(child, 0);
293
- return result;
294
- }
295
- updated(changedProps) {
296
- if (changedProps.has(`model`)) {
297
- this.#modelUnsubscribe?.();
298
- this.#modelUnsubscribe = void 0;
299
- const model = this.model;
300
- if (model) {
301
- this.root = model.root;
302
- this.#modelUnsubscribe = model._subscribe((event) => {
303
- if (event.type === `root`) {
304
- this.root = event.root;
305
- this.requestUpdate();
306
- }
307
- });
308
- }
309
- }
310
- this.#runLayoutUpdate(changedProps);
311
- }
312
- connectedCallback() {
313
- super.connectedCallback();
314
- this.addEventListener(`click`, this.#onHostClick);
315
- }
316
- disconnectedCallback() {
317
- super.disconnectedCallback();
318
- this.removeEventListener(`click`, this.#onHostClick);
319
- this.#modelUnsubscribe?.();
320
- this.#modelUnsubscribe = void 0;
321
- document.removeEventListener(`keydown`, this.#onMenuKeyNav, { capture: true });
322
- this.#currentOpenTrigger = null;
323
- }
324
- #runLayoutUpdate(_changedProperties) {
325
- if (!this.shadowRoot) return;
326
- const hsiElement = this.shadowRoot.querySelector(`.horiz-select-items`);
327
- if (!hsiElement) return;
328
- const ulElement = hsiElement.querySelector(`ul`);
329
- if (!ulElement) return;
330
- const menuTrigger = this.shadowRoot.querySelector(`ixfx-menu-trigger`);
331
- if (!menuTrigger) return;
332
- if (ulElement.getBoundingClientRect().width > hsiElement.getBoundingClientRect().width) menuTrigger.style.display = `inline-flex`;
333
- else menuTrigger.style.display = `none`;
334
- for (const li of ulElement.querySelectorAll(`li`)) if ((li.getAttribute(`data-key`) ?? ``) === this.#previousSelectionKey) {
335
- this.#scrollToValue(li);
336
- break;
337
- }
338
- }
339
- #scrollToValue(li) {
340
- let l = li.offsetLeft;
341
- const ul = li.parentElement;
342
- if (!ul) return;
343
- l -= ul.offsetLeft;
344
- const horizSelectItems = ul.parentElement;
345
- if (!horizSelectItems) return;
346
- horizSelectItems.scroll({
347
- left: l,
348
- top: 0,
349
- behavior: `smooth`
350
- });
351
- const horizSelect = horizSelectItems.parentElement;
352
- if (!horizSelect) return;
353
- horizSelect.style.minWidth = `${li.getBoundingClientRect().width}px`;
354
- }
355
- #abortPending() {
356
- if (this.#pendingController) {
357
- this.#pendingController.abort();
358
- this.#pendingController = void 0;
359
- this.#pendingNode = void 0;
360
- }
361
- }
362
- #findPath(node, target, acc = []) {
363
- const path = [...acc, node];
364
- if (node === target) return path;
365
- if (!node.children) return void 0;
366
- for (const child of node.children) {
367
- const result = this.#findPath(child, target, path);
368
- if (result) return result;
369
- }
370
- }
371
- #setSelected(node) {
372
- const previous = this.selectedNode;
373
- if (previous) this.#previousSelectionKey = previous.item?.key ?? ``;
374
- else this.#previousSelectionKey = ``;
375
- const previousSet = previous ? /* @__PURE__ */ new Set([previous]) : /* @__PURE__ */ new Set();
376
- this.selectedNode = node;
377
- this._selectedNodes = /* @__PURE__ */ new Set([node]);
378
- this.dispatchEvent(new CustomEvent(`select`, {
379
- detail: {
380
- selected: /* @__PURE__ */ new Set([node]),
381
- previous: previousSet
382
- },
383
- bubbles: true,
384
- composed: true
385
- }));
386
- }
387
- #onLabelClick(node, depth) {
388
- this._openIdx = -1;
389
- this.#setSelected(node);
390
- this.dispatchEvent(new CustomEvent(`item-click`, {
391
- detail: {
392
- node,
393
- depth
394
- },
395
- bubbles: true,
396
- composed: true
397
- }));
398
- }
399
- #onCaretClick(node, depth, e) {
400
- e.preventDefault();
401
- e.stopPropagation();
402
- if (this._openIdx === depth) {
403
- this._openIdx = -1;
404
- return;
405
- }
406
- this._openIdx = depth;
407
- if (node.children === void 0 && this.#pendingNode !== node) {
408
- this.#abortPending();
409
- this.#pendingNode = node;
410
- const controller = new AbortController();
411
- this.#pendingController = controller;
412
- if (this.loadChildren) this.#invokeLoadChildren(node, depth, controller).catch(() => {});
413
- }
414
- this.requestUpdate();
415
- }
416
- async #invokeLoadChildren(node, depth, controller) {
417
- try {
418
- const children = [...await collectDataResult(this.loadChildren({
419
- node,
420
- depth
421
- }, controller.signal), controller.signal)];
422
- if (controller.signal.aborted) return;
423
- node.children = children;
424
- this.#pendingNode = void 0;
425
- this.#pendingController = void 0;
426
- this.dispatchEvent(new CustomEvent(`expand`, {
427
- detail: {
428
- node,
429
- depth
430
- },
431
- bubbles: true,
432
- composed: true
433
- }));
434
- this.requestUpdate();
435
- } catch {
436
- if (controller.signal.aborted) return;
437
- node.children = [];
438
- this.#pendingNode = void 0;
439
- this.#pendingController = void 0;
440
- this.requestUpdate();
441
- }
442
- }
443
- #onSegmentMenuCommand(e, node, depth) {
444
- const key = e.detail.command;
445
- const child = node.children?.find((c) => c.item.key === key);
446
- if (child) this.#onChildClick(child, depth + 1);
447
- }
448
- #onChildClick(node, depth) {
449
- this._openIdx = -1;
450
- this.#setSelected(node);
451
- this.dispatchEvent(new CustomEvent(`item-click`, {
452
- detail: {
453
- node,
454
- depth
455
- },
456
- bubbles: true,
457
- composed: true
458
- }));
459
- }
460
- /** Navigate to a node by key path. Returns false if any key is not found in already-loaded children. */
461
- navigateTo(keys) {
462
- if (!this.root?.children) return false;
463
- let current = this.root;
464
- for (const key of keys) {
465
- const found = current?.children?.find((n) => n.item.key === key);
466
- if (!found) return false;
467
- current = found;
468
- }
469
- if (current && current !== this.root) this.#setSelected(current);
470
- return true;
471
- }
472
- /** Navigate to the first node matching the predicate. Returns false if not found. */
473
- navigateToNode(predicate) {
474
- if (!this.root) return false;
475
- const keyPath = this.#findNodePath(this.root.children ?? [], predicate);
476
- if (!keyPath) return false;
477
- return this.navigateTo(keyPath);
478
- }
479
- #findNodePath(nodes, predicate, prefix = []) {
480
- for (const node of nodes) {
481
- const path = [...prefix, node.item.key];
482
- if (predicate(node)) return path;
483
- if (node.children?.length) {
484
- const result = this.#findNodePath(node.children, predicate, path);
485
- if (result) return result;
486
- }
487
- }
488
- }
489
- #onMenuCommand(e) {
490
- const node = this.selectedNode;
491
- if (!node?.children) return;
492
- const key = e.detail.command;
493
- const child = node.children.find((c) => c.item.key === key);
494
- if (child) {
495
- const depth = this.root ? this.#findPath(this.root, node)?.length ?? 1 : 1;
496
- this.#onChildClick(child, depth);
497
- }
498
- }
499
- render() {
500
- const r = this.root;
501
- const sel = this.selectedNode;
502
- if (!r || !sel) return html``;
503
- this.inputMode = this.tickled.inputMode;
504
- const pathNodes = this.#findPath(r, sel) ?? [r];
505
- this.#hasChildOptions();
506
- const parts = pathNodes.map((node, i) => {
507
- const isLast = i === pathNodes.length - 1;
508
- const isPending = this.#pendingNode === node;
509
- const isOpen = this._openIdx === i;
510
- const hasChildren = node.item.isLeaf !== true && (node.children === void 0 || node.children.length > 0);
511
- const showCaret = !isLast && hasChildren;
512
- const isTickled = this.tickled.isTickled(i);
513
- const menuItems = (node.children ?? []).map((c) => html`
514
- <ixfx-menu-item
515
- label="${c.item.label}"
516
- command="${c.item.key}"
517
- icon-name="${c.item.icon ?? nothing}"
518
- ></ixfx-menu-item>
519
- `);
520
- return html`
521
- <div class="item crumb-segment ${isTickled ? `tickled` : ``}"
522
- @pointerenter=${() => {
523
- this.tickled.handlePointerEnter(i);
524
- this.dispatchEvent(new CustomEvent(`tickle`, {
525
- detail: {
526
- node,
527
- depth: i
528
- },
529
- bubbles: true,
530
- composed: true
531
- }));
532
- }}
533
- >
534
- <span class="label" @click=${() => this.#onLabelClick(node, i)}>${node.item.label}</span>
535
- ${showCaret ? html`
536
- <span class="caret ${isOpen ? `open` : ``} ${isPending ? `pending` : ``}">
537
- ${isPending ? html`<span class="loading-spinner"></span>` : html`
538
- <ixfx-menu-trigger mode="trigger" label="›" @menu-command=${(e) => this.#onSegmentMenuCommand(e, node, i)}>
539
- <ixfx-menu-container slot="menu">
540
- ${menuItems}
541
- </ixfx-menu-container>
542
- </ixfx-menu-trigger>
543
- `}
544
- </span>
545
- ` : nothing}
546
- </div>
547
- `;
548
- });
549
- const childOptions = this.#renderChildOptions();
550
- const isLastItemTickled = this.tickled.isTickled(pathNodes.length);
551
- return html`
552
- <div
553
- class="host-container"
554
- tabindex="0"
555
- @focus=${this.#onFocus}
556
- @blur=${this.#onBlur}
557
- @keydown=${this.#onKeyDown}
558
- @click=${this.#onHostClick}
559
- @menu-trigger-close=${this.#onMenuTriggerClose}
560
- @pointerleave=${(e) => this.tickled.handlePointerLeave(e)}
561
- >
562
- <div class="parts">${parts}<div class="trailing"></div></div>
563
- ${childOptions ? html`
564
- <ixfx-menu-trigger
565
- id="child-options-trigger"
566
- class="item separator-trigger ${isLastItemTickled ? `tickled` : ``}"
567
- mode="trigger"
568
- label="›"
569
- @menu-command=${this.#onMenuCommand}
570
- @pointerenter=${() => this.tickled.handlePointerEnter(pathNodes.length)}
571
- >
572
- <ixfx-menu-container slot="menu">
573
- ${(this.selectedNode?.children ?? []).map((c) => html`
574
- <ixfx-menu-item
575
- label="${c.item.label}"
576
- command="${c.item.key}"
577
- icon-name="${c.item.icon ?? nothing}"
578
- ></ixfx-menu-item>
579
- `)}
580
- </ixfx-menu-container>
581
- </ixfx-menu-trigger>
582
- ` : nothing}
583
- ${childOptions}
584
- </div>
585
- `;
586
- }
587
- #renderChildOptions() {
588
- const s = this.selectedNode;
589
- if (s === void 0) return nothing;
590
- const isPending = this.#pendingNode === s;
591
- const children = s.children ?? [];
592
- if (children.length === 0 && !isPending) return nothing;
593
- if (isPending) return html`
594
- <div class="horiz-select">
595
- <div class="horiz-select-items">
596
- <span class="loading-spinner"></span>
597
- </div>
598
- </div>
599
- `;
600
- return html`
601
- <ixfx-narrowed-text
602
- class="horiz-select"
603
- selection-mode="single"
604
- orientation="horizontal"
605
- nowrap
606
- width=${this.#getChildOptionsWidth()}
607
- @change=${this.#onNarrowedTextChange}
608
- @narrowed-text-blur=${this.#onNarrowedTextBlur}
609
- @narrowed-text-shift-tab=${this.#onNarrowedTextShiftTab}
610
- >
611
- ${children.map((c) => html`
612
- <span data-value="${c.item.key}" data-label="${c.item.label}"></span>
613
- `)}
614
- </ixfx-narrowed-text>
615
- `;
616
- }
617
- #onNarrowedTextChange(e) {
618
- e.stopPropagation();
619
- const detail = e.detail;
620
- const node = this.selectedNode;
621
- if (!node?.children) return;
622
- const child = node.children.find((c) => c.item.key === detail.item.value);
623
- if (child) {
624
- const depth = this.root ? this.#findPath(this.root, node)?.length ?? 1 : 1;
625
- this.#onChildClick(child, depth);
626
- }
627
- }
628
- #getChildOptionsWidth() {
629
- return `var(--child-options-width, 50ch)`;
630
- }
631
- static {
632
- this.styles = [
633
- themeFallbacks,
634
- tickledStyles,
635
- tickledItemStyles,
636
- css`
637
- :host {
638
- --text-highlight: var(--accent);
639
- --separator: var(--surface-muted-text);
640
- --padding-s: var(--space-xs);
641
- display: flex;
642
- overflow: scroll;
643
- width: 100%;
644
- scroll-snap-type: x mandatory;
645
- scrollbar-width: none;
646
-
647
- /* Breadcrumb tickled overrides — no layout shift: keep transparent border at rest */
648
- --item-padding: 0;
649
- }
650
-
651
- :host(.focused) {
652
- outline: none;
653
- }
654
-
655
- :host(:focus-within) .host-container {
656
- outline: 2px solid var(--accent);
657
- outline-offset: 2px;
658
- }
659
-
660
- .host-container {
661
- display: flex;
662
- width: 100%;
663
- outline: none;
664
- }
665
-
666
- .parts {
667
- display: flex;
668
- }
669
-
670
- .trailing {
671
- flex: 1;
672
- min-width: 1em;
673
- cursor: pointer;
674
- }
675
-
676
- .crumb-segment {
677
- display: flex;
678
- align-items: center;
679
- }
680
-
681
- .label {
682
- cursor: pointer;
683
- margin-right: var(--padding-s);
684
- white-space: nowrap;
685
- }
686
-
687
- .caret {
688
- position: relative;
689
- cursor: pointer;
690
- padding: var(--padding-m);
691
- margin-left: var(--padding-s);
692
- margin-right: var(--padding-s);
693
- color: var(--separator);
694
- display: inline-flex;
695
- align-items: center;
696
- justify-content: center;
697
- transition: all 0.2s ease-in-out;
698
- }
699
-
700
- .caret.open {
701
- background: color-mix(in srgb, var(--surface-5) 40%, transparent);
702
- }
703
-
704
- .caret.pending {
705
- display: inline-flex;
706
- }
707
-
708
- .caret > ixfx-menu-trigger {
709
- display: inline-flex;
710
- margin: 0;
711
- --_trigger-hover-bg: transparent;
712
- --_trigger-hover-opacity: 0.6;
713
- }
714
-
715
- .caret > ixfx-menu-trigger::part(base) {
716
- background: none;
717
- border: none;
718
- color: inherit;
719
- padding: 0;
720
- cursor: pointer;
721
- font-size: inherit;
722
- line-height: 1;
723
- }
724
-
725
- .caret > ixfx-menu-trigger::part(base):hover {
726
- background: none;
727
- opacity: 0.6;
728
- }
729
-
730
- .separator {
731
- margin-left: var(--padding-s);
732
- margin-right: var(--padding-s);
733
- color: var(--separator);
734
- }
735
-
736
- .separator-trigger {
737
- display: inline-flex;
738
- margin-left: var(--padding-s);
739
- margin-right: var(--padding-s);
740
- --_trigger-hover-bg: transparent;
741
- --_trigger-hover-opacity: 0.6;
742
- }
743
-
744
- .separator-trigger::part(base) {
745
- background: none;
746
- border: none;
747
- color: var(--separator);
748
- padding: 0;
749
- cursor: pointer;
750
- font-size: inherit;
751
- line-height: 1;
752
- }
753
-
754
- .separator-trigger::part(base):hover {
755
- background: none;
756
- opacity: 0.6;
757
- }
758
-
759
- .separator-trigger.tickled::part(base) {
760
- }
761
-
762
- ixfx-menu-trigger {
763
- display: none;
764
- }
765
-
766
- .horiz-select > ixfx-menu-trigger {
767
- display: inline-flex;
768
- }
769
-
770
- .horiz-select {
771
- display: flex;
772
- min-width: 5ch;
773
- }
774
-
775
- .loading-spinner {
776
- display: inline-block;
777
- width: 12px;
778
- height: 12px;
779
- border: 2px solid var(--surface-5);
780
- border-top-color: var(--accent);
781
- border-radius: 50%;
782
- animation: crumb-spin 0.7s linear infinite;
783
- }
784
-
785
- @keyframes crumb-spin {
786
- to { transform: rotate(360deg); }
787
- }
788
-
789
- ::slotted(*), option {
790
- user-select: none;
791
- scroll-snap-align: start;
792
- }
793
- `
794
- ];
795
- }
796
- };
797
- __decorate([property({ type: Object })], CrumbNavigationElement.prototype, "root", void 0);
798
- __decorate([property({ attribute: false })], CrumbNavigationElement.prototype, "model", void 0);
799
- __decorate([property({ type: Object })], CrumbNavigationElement.prototype, "selectedNode", void 0);
800
- __decorate([state()], CrumbNavigationElement.prototype, "_selectedNodes", void 0);
801
- __decorate([property({ attribute: false })], CrumbNavigationElement.prototype, "loadChildren", void 0);
802
- __decorate([property({ attribute: false })], CrumbNavigationElement.prototype, "filterPredicate", void 0);
803
- __decorate([property({
804
- reflect: true,
805
- attribute: `exclusivity`
806
- })], CrumbNavigationElement.prototype, "exclusivity", void 0);
807
- __decorate([property({
808
- reflect: true,
809
- attribute: `selection-filter`
810
- })], CrumbNavigationElement.prototype, "selectionFilter", void 0);
811
- __decorate([state()], CrumbNavigationElement.prototype, "_openIdx", void 0);
812
- __decorate([state()], CrumbNavigationElement.prototype, "_isFocused", void 0);
813
- __decorate([state()], CrumbNavigationElement.prototype, "_menuOpen", void 0);
814
- __decorate([state()], CrumbNavigationElement.prototype, "_focusOnNarrowedText", void 0);
815
- __decorate([property({
816
- reflect: true,
817
- attribute: `input-mode`
818
- })], CrumbNavigationElement.prototype, "inputMode", void 0);
819
- CrumbNavigationElement = __decorate([safeCustomElement(`ixfx-crumb-navigation`)], CrumbNavigationElement);
820
- //#endregion
821
3
  export { CrumbNavigationElement };
822
-
823
- //# sourceMappingURL=crumbs.js.map