@ixfx/components 0.5.15 → 0.5.16

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 (60) hide show
  1. package/bundle/index.d.ts +17 -5
  2. package/bundle/index.d.ts.map +1 -1
  3. package/bundle/index.js +459 -127
  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/crumbs.d.ts +16 -4
  11. package/dist/crumbs.d.ts.map +1 -1
  12. package/dist/crumbs.js +405 -125
  13. package/dist/crumbs.js.map +1 -1
  14. package/dist/data-display.js +1 -1
  15. package/dist/editable-label.js +1 -1
  16. package/dist/{fallbacks-pNMOMx38.js → fallbacks-DPgPEqo8.js} +51 -1
  17. package/dist/{fallbacks-pNMOMx38.js.map → fallbacks-DPgPEqo8.js.map} +1 -1
  18. package/dist/{icon-B22XVlCt.js → icon-DG9Lm3LT.js} +2 -2
  19. package/dist/{icon-B22XVlCt.js.map → icon-DG9Lm3LT.js.map} +1 -1
  20. package/dist/icons.js +1 -1
  21. package/dist/index.d.ts +1 -1
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +13 -12
  24. package/dist/index.js.map +1 -1
  25. package/dist/labelled-radial-input.js +2 -2
  26. package/dist/{menu-nuOIUD9K.js → menu-B8gE2MSo.js} +4 -4
  27. package/dist/{menu-nuOIUD9K.js.map → menu-B8gE2MSo.js.map} +1 -1
  28. package/dist/{menu-item-BBTZnjAR.js → menu-item-BYRvfyXK.js} +5 -4
  29. package/dist/menu-item-BYRvfyXK.js.map +1 -0
  30. package/dist/menu-item-DSyf9f_L.d.ts.map +1 -1
  31. package/dist/menu.js +2 -2
  32. package/dist/miller.js +1 -1
  33. package/dist/narrowed-text.js +1 -1
  34. package/dist/panel.js +2 -2
  35. package/dist/polar-pad.js +1 -1
  36. package/dist/{radial-input-DxCrt_NE.js → radial-input-CVxGH-Cp.js} +2 -2
  37. package/dist/{radial-input-DxCrt_NE.js.map → radial-input-CVxGH-Cp.js.map} +1 -1
  38. package/dist/radial-input.js +1 -1
  39. package/dist/range-input.js +1 -1
  40. package/dist/range.js +1 -1
  41. package/dist/selecthorizontal.js +1 -1
  42. package/dist/{slider-input-DFpG23bS.js → slider-input-CzmSVgm7.js} +2 -2
  43. package/dist/{slider-input-DFpG23bS.js.map → slider-input-CzmSVgm7.js.map} +1 -1
  44. package/dist/slider-input.js +1 -1
  45. package/dist/split-layout.js +1 -1
  46. package/dist/swipe.js +1 -1
  47. package/dist/{tab-list-DhkVhRQc.js → tab-list-6W5CgXix.js} +2 -2
  48. package/dist/{tab-list-DhkVhRQc.js.map → tab-list-6W5CgXix.js.map} +1 -1
  49. package/dist/{tab-list-BohmbvqF.d.ts → tab-list-ih-0I0NE.d.ts} +2 -2
  50. package/dist/{tab-list-BohmbvqF.d.ts.map → tab-list-ih-0I0NE.d.ts.map} +1 -1
  51. package/dist/tabs.d.ts +1 -1
  52. package/dist/tabs.js +1 -1
  53. package/dist/{tree-CL-txefQ.js → tree-BtiiDX1a.js} +2 -2
  54. package/dist/{tree-CL-txefQ.js.map → tree-BtiiDX1a.js.map} +1 -1
  55. package/dist/tree.js +1 -1
  56. package/dist/xy-pad.js +1 -1
  57. package/docs-user/crumbs.md +24 -9
  58. package/docs-user/tabs.md +19 -2
  59. package/package.json +1 -1
  60. package/dist/menu-item-BBTZnjAR.js.map +0 -1
package/bundle/index.js CHANGED
@@ -1119,6 +1119,56 @@ const themeFallbacks = i$3`
1119
1119
  inherits: true;
1120
1120
  initial-value: 0.1;
1121
1121
  }
1122
+ @property --crumb-item-bg {
1123
+ syntax: '<color>';
1124
+ inherits: true;
1125
+ initial-value: transparent;
1126
+ }
1127
+ @property --crumb-item-bg-tickled {
1128
+ syntax: '<color>';
1129
+ inherits: true;
1130
+ initial-value: var(--surface-5);
1131
+ }
1132
+ @property --crumb-item-text-tickled {
1133
+ syntax: '<color>';
1134
+ inherits: true;
1135
+ initial-value: inherit;
1136
+ }
1137
+ @property --crumb-item-border {
1138
+ syntax: '*';
1139
+ inherits: true;
1140
+ initial-value: 1px solid transparent;
1141
+ }
1142
+ @property --crumb-item-border-tickled {
1143
+ syntax: '<color>';
1144
+ inherits: true;
1145
+ initial-value: var(--accent);
1146
+ }
1147
+ @property --crumb-item-padding {
1148
+ syntax: '*';
1149
+ inherits: true;
1150
+ initial-value: var(--space-xs);
1151
+ }
1152
+ @property --crumb-item-border-radius {
1153
+ syntax: '<length>';
1154
+ inherits: true;
1155
+ initial-value: var(--radius-m);
1156
+ }
1157
+ @property --crumb-item-gap {
1158
+ syntax: '<length>';
1159
+ inherits: true;
1160
+ initial-value: var(--space-xs);
1161
+ }
1162
+ @property --crumb-separator {
1163
+ syntax: '<color>';
1164
+ inherits: true;
1165
+ initial-value: var(--surface-muted-text);
1166
+ }
1167
+ @property --crumb-truncate-min-width {
1168
+ syntax: '<length>';
1169
+ inherits: true;
1170
+ initial-value: 5ch;
1171
+ }
1122
1172
  `;
1123
1173
  //#endregion
1124
1174
  //#region src/styles/popup.ts
@@ -3985,6 +4035,7 @@ let MenuTrigger = class MenuTrigger extends i {
3985
4035
  :host {
3986
4036
  display: inline-block;
3987
4037
  position: relative;
4038
+ --ixfx-button-padding: var(--ixfx-button-padding, var(--space-s) var(--space-m));
3988
4039
  }
3989
4040
 
3990
4041
  .trigger-area {
@@ -3998,7 +4049,7 @@ let MenuTrigger = class MenuTrigger extends i {
3998
4049
  border: none;
3999
4050
  color: var(--surface-3-text);
4000
4051
  cursor: pointer;
4001
- padding: var(--space-s) var(--space-m);
4052
+ padding: var(--ixfx-button-padding);
4002
4053
  font-family: var(--font-family, inherit);
4003
4054
  font-size: var(--text-m);
4004
4055
  font-weight: 600;
@@ -6974,10 +7025,14 @@ let CrumbNavigationElement = class CrumbNavigationElement extends i {
6974
7025
  this.exclusivity = `none`;
6975
7026
  this.selectionFilter = `leaf`;
6976
7027
  this._openIdx = -1;
6977
- this._isFocused = false;
6978
7028
  this._menuOpen = false;
6979
7029
  this._focusOnNarrowedText = false;
6980
7030
  this.inputMode = ``;
7031
+ this.noTruncate = false;
7032
+ this.showSuggestions = false;
7033
+ this.leadingCaret = false;
7034
+ this.caretEmbedded = true;
7035
+ this.hideRoot = false;
6981
7036
  this.#previousSelectionKey = ``;
6982
7037
  this.#menuOpenedByKeyboard = false;
6983
7038
  this.#currentOpenTrigger = null;
@@ -6987,21 +7042,15 @@ let CrumbNavigationElement = class CrumbNavigationElement extends i {
6987
7042
  if (index !== void 0) this.#onTickled(index);
6988
7043
  }
6989
7044
  });
6990
- this.#onFocus = () => {
6991
- this._isFocused = true;
6992
- };
6993
- this.#onBlur = () => {
6994
- this._isFocused = false;
6995
- };
6996
7045
  this.#onHostClick = () => {
6997
- this.shadowRoot?.querySelector(`.host-container`)?.focus();
7046
+ this.focus();
6998
7047
  };
6999
7048
  this.#onMenuTriggerClose = () => {
7000
7049
  document.removeEventListener(`keydown`, this.#onMenuKeyNav, { capture: true });
7001
7050
  this.#currentOpenTrigger = null;
7002
7051
  this.#menuOpenedByKeyboard = false;
7003
7052
  requestAnimationFrame(() => {
7004
- this.shadowRoot?.querySelector(`.host-container`)?.focus();
7053
+ this.focus();
7005
7054
  });
7006
7055
  };
7007
7056
  this.#onMenuKeyNav = (e) => {
@@ -7024,7 +7073,8 @@ let CrumbNavigationElement = class CrumbNavigationElement extends i {
7024
7073
  }
7025
7074
  }
7026
7075
  };
7027
- this.#onKeyDown = (e) => {
7076
+ this.#onKeyDown = (event) => {
7077
+ const e = event;
7028
7078
  const r = this.root;
7029
7079
  const sel = this.selectedNode;
7030
7080
  if (!r || !sel) return;
@@ -7083,6 +7133,29 @@ let CrumbNavigationElement = class CrumbNavigationElement extends i {
7083
7133
  this._focusOnNarrowedText = false;
7084
7134
  this.#shiftFocusToPath();
7085
7135
  };
7136
+ this.#onMouseOut = (e) => {
7137
+ this.tickled.handlePointerLeave(e);
7138
+ };
7139
+ this.#isWheelScrolling = false;
7140
+ this.#onWheel = (e) => {
7141
+ const wheelEvent = e;
7142
+ if (wheelEvent.deltaX !== 0 || wheelEvent.deltaY !== 0) {
7143
+ e.preventDefault();
7144
+ if (!this.#isWheelScrolling) {
7145
+ this.#isWheelScrolling = true;
7146
+ this.tickled.clearTickled();
7147
+ }
7148
+ this.scrollLeft += wheelEvent.deltaX + wheelEvent.deltaY;
7149
+ clearTimeout(this.#wheelScrollTimeout);
7150
+ this.#wheelScrollTimeout = setTimeout(() => {
7151
+ this.#isWheelScrolling = false;
7152
+ }, 150);
7153
+ }
7154
+ };
7155
+ this.#onScroll = () => {
7156
+ this.#updateFade();
7157
+ };
7158
+ this.#currentLeadingSiblings = void 0;
7086
7159
  }
7087
7160
  get selectedNodes() {
7088
7161
  return this._selectedNodes;
@@ -7099,26 +7172,41 @@ let CrumbNavigationElement = class CrumbNavigationElement extends i {
7099
7172
  if (!r || !sel) return [];
7100
7173
  const pathNodes = this.#findPath(r, sel) ?? [r];
7101
7174
  const items = [];
7102
- pathNodes.forEach((_node, i) => {
7103
- items.push({
7104
- id: i,
7175
+ if (this.caretEmbedded) {
7176
+ pathNodes.forEach((_node, i) => {
7177
+ items.push({
7178
+ id: i,
7179
+ enabled: true
7180
+ });
7181
+ });
7182
+ if (this.#hasChildOptions()) items.push({
7183
+ id: pathNodes.length,
7105
7184
  enabled: true
7106
7185
  });
7107
- });
7108
- if (this.#hasChildOptions()) items.push({
7109
- id: pathNodes.length,
7110
- enabled: true
7111
- });
7186
+ } else {
7187
+ let segmentId = 0;
7188
+ pathNodes.forEach((node, i) => {
7189
+ const isLast = i === pathNodes.length - 1;
7190
+ const hasChildren = node.item.isLeaf !== true && (node.children === void 0 || node.children.length > 0);
7191
+ const showLeadingCaret = this.leadingCaret && (i > 0 || this.hideRoot) && hasChildren;
7192
+ const showTrailingCaret = !this.leadingCaret && hasChildren || this.leadingCaret && isLast && hasChildren;
7193
+ if (showLeadingCaret) items.push({
7194
+ id: segmentId++,
7195
+ enabled: true
7196
+ });
7197
+ items.push({
7198
+ id: segmentId++,
7199
+ enabled: true
7200
+ });
7201
+ if (showTrailingCaret) items.push({
7202
+ id: segmentId++,
7203
+ enabled: true
7204
+ });
7205
+ });
7206
+ }
7112
7207
  return items;
7113
7208
  }
7114
7209
  #onTickled(index) {
7115
- const r = this.root;
7116
- const sel = this.selectedNode;
7117
- if (!r || !sel) return;
7118
- const pathNodes = this.#findPath(r, sel) ?? [r];
7119
- const hasChildOptions = this.#hasChildOptions();
7120
- if (index < pathNodes.length) this._openIdx = index;
7121
- else if (hasChildOptions && index === pathNodes.length) this._openIdx = pathNodes.length - 1;
7122
7210
  if (this.tickled.inputMode === `keyboard`) this.#scrollTickledIntoView(index);
7123
7211
  }
7124
7212
  #scrollTickledIntoView(index) {
@@ -7141,8 +7229,6 @@ let CrumbNavigationElement = class CrumbNavigationElement extends i {
7141
7229
  const isPending = this.#pendingNode === s;
7142
7230
  return (s.children ?? []).length > 0 || isPending;
7143
7231
  }
7144
- #onFocus;
7145
- #onBlur;
7146
7232
  #onHostClick;
7147
7233
  #handleOpenCaretMenu() {
7148
7234
  const currentTickled = this.tickled.tickledId;
@@ -7222,8 +7308,7 @@ let CrumbNavigationElement = class CrumbNavigationElement extends i {
7222
7308
  }
7223
7309
  #shiftFocusToPath() {
7224
7310
  this._focusOnNarrowedText = false;
7225
- const hostContainer = this.shadowRoot?.querySelector(`.host-container`);
7226
- if (hostContainer) hostContainer.focus();
7311
+ this.focus();
7227
7312
  }
7228
7313
  #onNarrowedTextBlur;
7229
7314
  #onNarrowedTextShiftTab;
@@ -7270,16 +7355,42 @@ let CrumbNavigationElement = class CrumbNavigationElement extends i {
7270
7355
  }
7271
7356
  connectedCallback() {
7272
7357
  super.connectedCallback();
7358
+ this.tabIndex = 0;
7273
7359
  this.addEventListener(`click`, this.#onHostClick);
7360
+ this.addEventListener(`wheel`, this.#onWheel, { passive: false });
7361
+ this.addEventListener(`scroll`, this.#onScroll);
7362
+ this.addEventListener(`keydown`, this.#onKeyDown);
7363
+ this.addEventListener(`menu-trigger-close`, this.#onMenuTriggerClose);
7364
+ this.addEventListener(`mouseout`, this.#onMouseOut);
7274
7365
  }
7275
7366
  disconnectedCallback() {
7276
7367
  super.disconnectedCallback();
7277
7368
  this.removeEventListener(`click`, this.#onHostClick);
7369
+ this.removeEventListener(`wheel`, this.#onWheel);
7370
+ this.removeEventListener(`scroll`, this.#onScroll);
7371
+ this.removeEventListener(`keydown`, this.#onKeyDown);
7372
+ this.removeEventListener(`menu-trigger-close`, this.#onMenuTriggerClose);
7373
+ this.removeEventListener(`mouseout`, this.#onMouseOut);
7278
7374
  this.#modelUnsubscribe?.();
7279
7375
  this.#modelUnsubscribe = void 0;
7280
7376
  document.removeEventListener(`keydown`, this.#onMenuKeyNav, { capture: true });
7281
7377
  this.#currentOpenTrigger = null;
7282
7378
  }
7379
+ firstUpdated() {
7380
+ this.scrollLeft = this.scrollWidth - this.clientWidth;
7381
+ this.#updateFade();
7382
+ }
7383
+ #onMouseOut;
7384
+ #isWheelScrolling;
7385
+ #onWheel;
7386
+ #wheelScrollTimeout;
7387
+ #onScroll;
7388
+ #updateFade() {
7389
+ const hasLeftFade = this.scrollLeft > 0;
7390
+ const hasRightFade = this.scrollLeft < this.scrollWidth - this.clientWidth - 1;
7391
+ this.classList.toggle(`has-left-fade`, hasLeftFade);
7392
+ this.classList.toggle(`has-right-fade`, hasRightFade);
7393
+ }
7283
7394
  #runLayoutUpdate(_changedProperties) {
7284
7395
  if (!this.shadowRoot) return;
7285
7396
  const hsiElement = this.shadowRoot.querySelector(`.horiz-select-items`);
@@ -7327,6 +7438,13 @@ let CrumbNavigationElement = class CrumbNavigationElement extends i {
7327
7438
  if (result) return result;
7328
7439
  }
7329
7440
  }
7441
+ #scrollSegmentIntoView(depth) {
7442
+ if (!this.shadowRoot) return;
7443
+ this.shadowRoot.querySelectorAll(`.crumb-segment`)[depth]?.scrollIntoView({
7444
+ block: `nearest`,
7445
+ inline: `start`
7446
+ });
7447
+ }
7330
7448
  #setSelected(node) {
7331
7449
  const previous = this.selectedNode;
7332
7450
  if (previous) this.#previousSelectionKey = previous.item?.key ?? ``;
@@ -7334,6 +7452,15 @@ let CrumbNavigationElement = class CrumbNavigationElement extends i {
7334
7452
  const previousSet = previous ? /* @__PURE__ */ new Set([previous]) : /* @__PURE__ */ new Set();
7335
7453
  this.selectedNode = node;
7336
7454
  this._selectedNodes = /* @__PURE__ */ new Set([node]);
7455
+ if (this.root) {
7456
+ const path = this.#findPath(this.root, node);
7457
+ if (path) {
7458
+ const depth = this.hideRoot && path.length > 1 ? path.length - 2 : path.length - 1;
7459
+ requestAnimationFrame(() => {
7460
+ this.#scrollSegmentIntoView(depth);
7461
+ });
7462
+ }
7463
+ }
7337
7464
  this.dispatchEvent(new CustomEvent(`select`, {
7338
7465
  detail: {
7339
7466
  selected: /* @__PURE__ */ new Set([node]),
@@ -7346,6 +7473,7 @@ let CrumbNavigationElement = class CrumbNavigationElement extends i {
7346
7473
  #onLabelClick(node, depth) {
7347
7474
  this._openIdx = -1;
7348
7475
  this.#setSelected(node);
7476
+ if (this.leadingCaret) this.#scrollSegmentIntoView(depth);
7349
7477
  this.dispatchEvent(new CustomEvent(`item-click`, {
7350
7478
  detail: {
7351
7479
  node,
@@ -7401,9 +7529,52 @@ let CrumbNavigationElement = class CrumbNavigationElement extends i {
7401
7529
  }
7402
7530
  #onSegmentMenuCommand(e, node, depth) {
7403
7531
  const key = e.detail.command;
7532
+ if (this.leadingCaret) {
7533
+ const child = node.children?.find((c) => c.item.key === key);
7534
+ if (child) {
7535
+ this.#onChildClick(child, depth + 1);
7536
+ return;
7537
+ }
7538
+ if (this.#currentLeadingSiblings) {
7539
+ const sibling = this.#currentLeadingSiblings.find((c) => c.item.key === key);
7540
+ if (sibling) this.#onChildClick(sibling, depth);
7541
+ this.#currentLeadingSiblings = void 0;
7542
+ return;
7543
+ }
7544
+ }
7404
7545
  const child = node.children?.find((c) => c.item.key === key);
7405
7546
  if (child) this.#onChildClick(child, depth + 1);
7406
7547
  }
7548
+ #currentLeadingSiblings;
7549
+ #getSiblingMenuItems(pathNodes, index) {
7550
+ if (index === 0) {
7551
+ if (!this.hideRoot || !this.root) return [];
7552
+ const siblings = [...this.root.children ?? []].sort((a, b) => (a.item.label ?? ``).localeCompare(b.item.label ?? ``));
7553
+ this.#currentLeadingSiblings = siblings;
7554
+ return siblings.map((sibling) => b`
7555
+ <ixfx-menu-item
7556
+ label="${sibling.item.label}"
7557
+ command="${sibling.item.key}"
7558
+ icon-name="${sibling.item.icon ?? A}"
7559
+ @click=${() => {
7560
+ this.#onChildClick(sibling, index);
7561
+ }}
7562
+ ></ixfx-menu-item>
7563
+ `);
7564
+ }
7565
+ const siblings = [...pathNodes[index - 1].children ?? []].sort((a, b) => (a.item.label ?? ``).localeCompare(b.item.label ?? ``));
7566
+ this.#currentLeadingSiblings = siblings;
7567
+ return siblings.map((sibling) => b`
7568
+ <ixfx-menu-item
7569
+ label="${sibling.item.label}"
7570
+ command="${sibling.item.key}"
7571
+ icon-name="${sibling.item.icon ?? A}"
7572
+ @click=${() => {
7573
+ this.#onChildClick(sibling, index);
7574
+ }}
7575
+ ></ixfx-menu-item>
7576
+ `);
7577
+ }
7407
7578
  #onChildClick(node, depth) {
7408
7579
  this._openIdx = -1;
7409
7580
  this.#setSelected(node);
@@ -7460,87 +7631,146 @@ let CrumbNavigationElement = class CrumbNavigationElement extends i {
7460
7631
  const sel = this.selectedNode;
7461
7632
  if (!r || !sel) return b``;
7462
7633
  this.inputMode = this.tickled.inputMode;
7463
- const pathNodes = this.#findPath(r, sel) ?? [r];
7634
+ let pathNodes = this.#findPath(r, sel) ?? [r];
7635
+ if (this.hideRoot && pathNodes.length > 1) pathNodes = pathNodes.slice(1);
7464
7636
  this.#hasChildOptions();
7465
- const parts = pathNodes.map((node, i) => {
7637
+ const parts = [];
7638
+ let segmentId = 0;
7639
+ pathNodes.forEach((node, i) => {
7466
7640
  const isLast = i === pathNodes.length - 1;
7467
7641
  const isPending = this.#pendingNode === node;
7468
7642
  const isOpen = this._openIdx === i;
7469
7643
  const hasChildren = node.item.isLeaf !== true && (node.children === void 0 || node.children.length > 0);
7470
- const showCaret = !isLast && hasChildren;
7471
7644
  const isTickled = this.tickled.isTickled(i);
7472
- const menuItems = (node.children ?? []).map((c) => b`
7473
- <ixfx-menu-item
7474
- label="${c.item.label}"
7645
+ const showLeadingCaret = this.leadingCaret && (i > 0 || this.hideRoot) && hasChildren;
7646
+ const showTrailingCaret = !this.leadingCaret && hasChildren || this.leadingCaret && isLast && hasChildren;
7647
+ const siblingMenuItems = showLeadingCaret ? this.#getSiblingMenuItems(pathNodes, i) : [];
7648
+ const childMenuItems = (node.children ?? []).map((c) => b`
7649
+ <ixfx-menu-item
7650
+ label="${c.item.label}"
7475
7651
  command="${c.item.key}"
7476
7652
  icon-name="${c.item.icon ?? A}"
7477
7653
  ></ixfx-menu-item>
7478
7654
  `);
7479
- return b`
7480
- <div class="item crumb-segment ${isTickled ? `tickled` : ``}"
7481
- @pointerenter=${() => {
7482
- this.tickled.handlePointerEnter(i);
7483
- this.dispatchEvent(new CustomEvent(`tickle`, {
7484
- detail: {
7485
- node,
7486
- depth: i
7487
- },
7488
- bubbles: true,
7489
- composed: true
7490
- }));
7491
- }}
7492
- >
7493
- <span class="label" @click=${() => this.#onLabelClick(node, i)}>${node.item.label}</span>
7494
- ${showCaret ? b`
7495
- <span class="caret ${isOpen ? `open` : ``} ${isPending ? `pending` : ``}">
7496
- ${isPending ? b`<span class="loading-spinner"></span>` : b`
7497
- <ixfx-menu-trigger mode="trigger" label="›" @menu-command=${(e) => this.#onSegmentMenuCommand(e, node, i)}>
7498
- <ixfx-menu-container slot="menu">
7499
- ${menuItems}
7500
- </ixfx-menu-container>
7501
- </ixfx-menu-trigger>
7502
- `}
7503
- </span>
7504
- ` : A}
7505
- </div>
7506
- `;
7655
+ if (this.caretEmbedded) {
7656
+ const canTruncate = !this.noTruncate && !isLast && !isTickled;
7657
+ parts.push(b`
7658
+ <div class="item crumb-segment ${isTickled ? `tickled` : ``} ${canTruncate ? `truncatable` : ``} ${this.leadingCaret ? `leading-caret` : ``}" data-segment-id="${i}" data-path-index="${i}"
7659
+ @click=${(e) => {
7660
+ if (e.target.closest(`.caret`)) return;
7661
+ this.#onLabelClick(node, i);
7662
+ }}
7663
+ >
7664
+ ${showLeadingCaret ? b`
7665
+ <span class="caret ${isOpen ? `open` : ``} ${isPending ? `pending` : ``}">
7666
+ ${isPending ? b`<span class="loading-spinner"></span>` : b`
7667
+ <ixfx-menu-trigger mode="trigger" label="›" @menu-command=${(e) => this.#onSegmentMenuCommand(e, node, i)}>
7668
+ <ixfx-menu-container slot="menu">
7669
+ ${siblingMenuItems}
7670
+ </ixfx-menu-container>
7671
+ </ixfx-menu-trigger>
7672
+ `}
7673
+ </span>
7674
+ ` : A}
7675
+ <span class="label">${node.item.label}</span>
7676
+ ${showTrailingCaret ? b`
7677
+ <span class="caret ${isOpen ? `open` : ``} ${isPending ? `pending` : ``}">
7678
+ ${isPending ? b`<span class="loading-spinner"></span>` : b`
7679
+ <ixfx-menu-trigger mode="trigger" label="›" placement="right-start" @menu-command=${(e) => this.#onSegmentMenuCommand(e, node, i)}>
7680
+ <ixfx-menu-container slot="menu">
7681
+ ${childMenuItems}
7682
+ </ixfx-menu-container>
7683
+ </ixfx-menu-trigger>
7684
+ `}
7685
+ </span>
7686
+ ` : A}
7687
+ </div>
7688
+ `);
7689
+ } else {
7690
+ const leadingCaretTickled = showLeadingCaret ? this.tickled.isTickled(segmentId) : false;
7691
+ if (showLeadingCaret) {
7692
+ parts.push(b`
7693
+ <div class="item crumb-segment ${leadingCaretTickled ? `tickled` : ``}" data-segment-id="${segmentId}" data-path-index="${i}"
7694
+ @click=${(e) => {
7695
+ const trigger = e.target.closest(`.crumb-segment`)?.querySelector(`ixfx-menu-trigger`);
7696
+ if (trigger) trigger.openMenu();
7697
+ }}
7698
+ >
7699
+ <span class="caret ${isOpen ? `open` : ``} ${isPending ? `pending` : ``}">
7700
+ ${isPending ? b`<span class="loading-spinner"></span>` : b`
7701
+ <ixfx-menu-trigger mode="trigger" label="›" placement="right-start" @menu-command=${(e) => this.#onSegmentMenuCommand(e, node, i)}>
7702
+ <ixfx-menu-container slot="menu">
7703
+ ${siblingMenuItems}
7704
+ </ixfx-menu-container>
7705
+ </ixfx-menu-trigger>
7706
+ `}
7707
+ </span>
7708
+ </div>
7709
+ `);
7710
+ segmentId++;
7711
+ }
7712
+ const labelTickled = this.tickled.isTickled(segmentId);
7713
+ const canTruncate = !this.noTruncate && !isLast && !labelTickled;
7714
+ parts.push(b`
7715
+ <div class="item crumb-segment ${labelTickled ? `tickled` : ``} ${canTruncate ? `truncatable` : ``}" data-segment-id="${segmentId}" data-path-index="${i}"
7716
+ @click=${(e) => {
7717
+ if (e.target.closest(`.caret`)) return;
7718
+ this.#onLabelClick(node, i);
7719
+ }}
7720
+ >
7721
+ <span class="label">${node.item.label}</span>
7722
+ </div>
7723
+ `);
7724
+ segmentId++;
7725
+ if (showTrailingCaret) {
7726
+ const trailingCaretTickled = this.tickled.isTickled(segmentId);
7727
+ parts.push(b`
7728
+ <div class="item crumb-segment ${trailingCaretTickled ? `tickled` : ``}" data-segment-id="${segmentId}" data-path-index="${i}"
7729
+ @click=${(e) => {
7730
+ const trigger = e.target.closest(`.crumb-segment`)?.querySelector(`ixfx-menu-trigger`);
7731
+ if (trigger) trigger.openMenu();
7732
+ }}
7733
+ >
7734
+ <span class="caret ${isOpen ? `open` : ``} ${isPending ? `pending` : ``}">
7735
+ ${isPending ? b`<span class="loading-spinner"></span>` : b`
7736
+ <ixfx-menu-trigger mode="trigger" label="›" @menu-command=${(e) => this.#onSegmentMenuCommand(e, node, i)}>
7737
+ <ixfx-menu-container slot="menu">
7738
+ ${childMenuItems}
7739
+ </ixfx-menu-container>
7740
+ </ixfx-menu-trigger>
7741
+ `}
7742
+ </span>
7743
+ </div>
7744
+ `);
7745
+ segmentId++;
7746
+ }
7747
+ }
7507
7748
  });
7508
- const childOptions = this.#renderChildOptions();
7509
- const isLastItemTickled = this.tickled.isTickled(pathNodes.length);
7510
7749
  return b`
7511
- <div
7512
- class="host-container"
7513
- tabindex="0"
7514
- @focus=${this.#onFocus}
7515
- @blur=${this.#onBlur}
7516
- @keydown=${this.#onKeyDown}
7517
- @click=${this.#onHostClick}
7518
- @menu-trigger-close=${this.#onMenuTriggerClose}
7519
- @pointerleave=${(e) => this.tickled.handlePointerLeave(e)}
7520
- >
7521
- <div class="parts">${parts}<div class="trailing"></div></div>
7522
- ${childOptions ? b`
7523
- <ixfx-menu-trigger
7524
- id="child-options-trigger"
7525
- class="item separator-trigger ${isLastItemTickled ? `tickled` : ``}"
7526
- mode="trigger"
7527
- label="›"
7528
- @menu-command=${this.#onMenuCommand}
7529
- @pointerenter=${() => this.tickled.handlePointerEnter(pathNodes.length)}
7530
- >
7531
- <ixfx-menu-container slot="menu">
7532
- ${(this.selectedNode?.children ?? []).map((c) => b`
7533
- <ixfx-menu-item
7534
- label="${c.item.label}"
7535
- command="${c.item.key}"
7536
- icon-name="${c.item.icon ?? A}"
7537
- ></ixfx-menu-item>
7538
- `)}
7539
- </ixfx-menu-container>
7540
- </ixfx-menu-trigger>
7541
- ` : A}
7542
- ${childOptions}
7543
- </div>
7750
+ <div class="parts"
7751
+ @mouseover=${(e) => {
7752
+ if (this.#isWheelScrolling) return;
7753
+ const target = e.target;
7754
+ if (!(target instanceof HTMLElement)) return;
7755
+ const segment = target.closest(`.crumb-segment`);
7756
+ if (!segment) return;
7757
+ const segmentId = parseInt(segment.dataset.segmentId ?? ``, 10);
7758
+ if (Number.isNaN(segmentId)) return;
7759
+ const pathIndex = parseInt(segment.dataset.pathIndex ?? ``, 10);
7760
+ const node = pathNodes[pathIndex];
7761
+ if (!node) return;
7762
+ this.tickled.handlePointerEnter(segmentId);
7763
+ this.dispatchEvent(new CustomEvent(`tickle`, {
7764
+ detail: {
7765
+ node,
7766
+ depth: pathIndex
7767
+ },
7768
+ bubbles: true,
7769
+ composed: true
7770
+ }));
7771
+ }}
7772
+ >${parts}<div class="trailing"></div></div>
7773
+ ${this.showSuggestions ? this.#renderChildOptions() : A}
7544
7774
  `;
7545
7775
  }
7546
7776
  #renderChildOptions() {
@@ -7594,36 +7824,66 @@ let CrumbNavigationElement = class CrumbNavigationElement extends i {
7594
7824
  tickledItemStyles,
7595
7825
  i$3`
7596
7826
  :host {
7597
- --text-highlight: var(--accent);
7598
- --separator: var(--surface-muted-text);
7599
- --padding-s: var(--space-xs);
7827
+ --crumb-item-bg: var(--item-bg, transparent);
7828
+ --crumb-item-bg-tickled: var(--item-bg-tickled);
7829
+ --crumb-item-text-tickled: var(--item-text-tickled);
7830
+ --crumb-item-border: var(--item-border);
7831
+ --crumb-item-border-tickled: var(--item-border-tickled);
7832
+ --crumb-item-padding: var(--space-xs);
7833
+ --crumb-item-border-radius: var(--item-border-radius);
7834
+ --crumb-item-gap: var(--space-xs);
7835
+ --crumb-separator: var(--surface-muted-text);
7836
+ --crumb-truncate-min-width: 10ch;
7837
+ --crumb-fade-width: 30px;
7838
+ --crumb-fade-color: var(--surface-3);
7839
+ position: relative;
7600
7840
  display: flex;
7601
7841
  overflow: scroll;
7602
7842
  width: 100%;
7603
7843
  scroll-snap-type: x mandatory;
7604
7844
  scrollbar-width: none;
7605
-
7606
- /* Breadcrumb tickled overrides — no layout shift: keep transparent border at rest */
7607
- --item-padding: 0;
7608
7845
  }
7609
7846
 
7610
- :host(.focused) {
7847
+ :host(:focus) {
7611
7848
  outline: none;
7612
7849
  }
7613
7850
 
7614
- :host(:focus-within) .host-container {
7851
+ :host(:focus) .parts {
7615
7852
  outline: 2px solid var(--accent);
7616
7853
  outline-offset: 2px;
7617
7854
  }
7618
7855
 
7619
- .host-container {
7620
- display: flex;
7621
- width: 100%;
7622
- outline: none;
7856
+ :host::before,
7857
+ :host::after {
7858
+ content: '';
7859
+ position: absolute;
7860
+ top: 0;
7861
+ bottom: 0;
7862
+ width: var(--crumb-fade-width);
7863
+ pointer-events: none;
7864
+ opacity: 0;
7865
+ transition: opacity 0.2s ease;
7866
+ z-index: 1;
7867
+ }
7868
+
7869
+ :host::before {
7870
+ left: 0;
7871
+ background: linear-gradient(to right, var(--crumb-fade-color), transparent);
7872
+ }
7873
+
7874
+ :host::after {
7875
+ right: 0;
7876
+ background: linear-gradient(to left, var(--crumb-fade-color), transparent);
7877
+ }
7878
+
7879
+ :host(.has-left-fade)::before,
7880
+ :host(.has-right-fade)::after {
7881
+ opacity: 1;
7623
7882
  }
7624
7883
 
7625
7884
  .parts {
7626
7885
  display: flex;
7886
+ gap: var(--space-s);
7627
7887
  }
7628
7888
 
7629
7889
  .trailing {
@@ -7635,21 +7895,68 @@ let CrumbNavigationElement = class CrumbNavigationElement extends i {
7635
7895
  .crumb-segment {
7636
7896
  display: flex;
7637
7897
  align-items: center;
7898
+ flex-shrink: 0;
7899
+ background: var(--crumb-item-bg);
7900
+ border: var(--crumb-item-border);
7901
+ border-radius: var(--crumb-item-border-radius);
7902
+ }
7903
+
7904
+ .crumb-segment.leading-caret {
7905
+ flex-direction: row;
7906
+ }
7907
+
7908
+ .crumb-segment.leading-caret-only {
7909
+ flex-direction: row;
7910
+ padding: var(--crumb-item-padding);
7911
+ }
7912
+
7913
+ .crumb-segment.trailing-caret-only {
7914
+ flex-direction: row;
7915
+ padding: var(--crumb-item-padding);
7916
+ }
7917
+
7918
+ .crumb-segment.tickled {
7919
+ background: var(--crumb-item-bg-tickled);
7920
+ border-color: var(--crumb-item-border-tickled);
7921
+ color: var(--crumb-item-text-tickled);
7922
+ flex-shrink: 0;
7923
+ min-width: auto;
7924
+ }
7925
+
7926
+ .crumb-segment.truncatable {
7927
+ flex-shrink: 1;
7928
+ min-width: min(var(--crumb-truncate-min-width), auto);
7929
+ overflow: hidden;
7930
+ }
7931
+
7932
+ .crumb-segment.truncatable .label {
7933
+ overflow: hidden;
7934
+ text-overflow: ellipsis;
7935
+ white-space: nowrap;
7936
+ min-width: 0;
7638
7937
  }
7639
7938
 
7640
7939
  .label {
7641
7940
  cursor: pointer;
7642
- margin-right: var(--padding-s);
7643
7941
  white-space: nowrap;
7942
+ color: inherit;
7943
+ }
7944
+
7945
+ .crumb-segment.tickled .label {
7946
+ color: var(--crumb-item-text-tickled);
7947
+ overflow: visible;
7948
+ text-overflow: clip;
7949
+ }
7950
+
7951
+ .crumb-segment ixfx-menu-trigger {
7952
+ --ixfx-button-padding: var(--space-xs) 8px;
7644
7953
  }
7645
7954
 
7646
7955
  .caret {
7647
7956
  position: relative;
7648
- cursor: pointer;
7649
- padding: var(--padding-m);
7650
- margin-left: var(--padding-s);
7651
- margin-right: var(--padding-s);
7652
- color: var(--separator);
7957
+ cursor: pointer;
7958
+
7959
+ color: var(--crumb-separator);
7653
7960
  display: inline-flex;
7654
7961
  align-items: center;
7655
7962
  justify-content: center;
@@ -7687,15 +7994,15 @@ let CrumbNavigationElement = class CrumbNavigationElement extends i {
7687
7994
  }
7688
7995
 
7689
7996
  .separator {
7690
- margin-left: var(--padding-s);
7691
- margin-right: var(--padding-s);
7692
- color: var(--separator);
7997
+ margin-left: var(--crumb-item-gap);
7998
+ margin-right: var(--crumb-item-gap);
7999
+ color: var(--crumb-separator);
7693
8000
  }
7694
8001
 
7695
8002
  .separator-trigger {
7696
8003
  display: inline-flex;
7697
- margin-left: var(--padding-s);
7698
- margin-right: var(--padding-s);
8004
+ margin-left: var(--crumb-item-gap);
8005
+ margin-right: var(--crumb-item-gap);
7699
8006
  --_trigger-hover-bg: transparent;
7700
8007
  --_trigger-hover-opacity: 0.6;
7701
8008
  }
@@ -7703,7 +8010,7 @@ let CrumbNavigationElement = class CrumbNavigationElement extends i {
7703
8010
  .separator-trigger::part(base) {
7704
8011
  background: none;
7705
8012
  border: none;
7706
- color: var(--separator);
8013
+ color: var(--crumb-separator);
7707
8014
  padding: 0;
7708
8015
  cursor: pointer;
7709
8016
  font-size: inherit;
@@ -7768,13 +8075,37 @@ __decorate([property({
7768
8075
  attribute: `selection-filter`
7769
8076
  })], CrumbNavigationElement.prototype, "selectionFilter", void 0);
7770
8077
  __decorate([state()], CrumbNavigationElement.prototype, "_openIdx", void 0);
7771
- __decorate([state()], CrumbNavigationElement.prototype, "_isFocused", void 0);
7772
8078
  __decorate([state()], CrumbNavigationElement.prototype, "_menuOpen", void 0);
7773
8079
  __decorate([state()], CrumbNavigationElement.prototype, "_focusOnNarrowedText", void 0);
7774
8080
  __decorate([property({
7775
8081
  reflect: true,
7776
8082
  attribute: `input-mode`
7777
8083
  })], CrumbNavigationElement.prototype, "inputMode", void 0);
8084
+ __decorate([property({
8085
+ type: Boolean,
8086
+ reflect: true,
8087
+ attribute: `no-truncate`
8088
+ })], CrumbNavigationElement.prototype, "noTruncate", void 0);
8089
+ __decorate([property({
8090
+ type: Boolean,
8091
+ reflect: true,
8092
+ attribute: `show-suggestions`
8093
+ })], CrumbNavigationElement.prototype, "showSuggestions", void 0);
8094
+ __decorate([property({
8095
+ type: Boolean,
8096
+ reflect: true,
8097
+ attribute: `leading-caret`
8098
+ })], CrumbNavigationElement.prototype, "leadingCaret", void 0);
8099
+ __decorate([property({
8100
+ type: Boolean,
8101
+ reflect: true,
8102
+ attribute: `caret-embedded`
8103
+ })], CrumbNavigationElement.prototype, "caretEmbedded", void 0);
8104
+ __decorate([property({
8105
+ type: Boolean,
8106
+ reflect: true,
8107
+ attribute: `hideroot`
8108
+ })], CrumbNavigationElement.prototype, "hideRoot", void 0);
7778
8109
  CrumbNavigationElement = __decorate([safeCustomElement(`ixfx-crumb-navigation`)], CrumbNavigationElement);
7779
8110
  //#endregion
7780
8111
  //#region src/crumbs/crumb-path-controller.ts
@@ -12350,6 +12681,7 @@ let VerticalListElement = class VerticalListElement extends i {
12350
12681
  overflow: hidden;
12351
12682
  box-sizing: border-box;
12352
12683
  outline: none;
12684
+ --ixfx-list-item-padding: var(--ixfx-list-item-padding, var(--list-item-padding, 4px var(--space-m, 8px)));
12353
12685
  }
12354
12686
 
12355
12687
  :host(:focus-visible) {
@@ -12386,7 +12718,7 @@ let VerticalListElement = class VerticalListElement extends i {
12386
12718
  ::slotted(li) {
12387
12719
  display: flex;
12388
12720
  align-items: center;
12389
- padding: var(--list-item-padding, 4px var(--space-m, 8px));
12721
+ padding: var(--ixfx-list-item-padding);
12390
12722
  min-height: var(--list-item-height, 28px);
12391
12723
  cursor: pointer;
12392
12724
  user-select: none;
@@ -24293,7 +24625,7 @@ let TabListItemElement = class TabListItemElement extends i {
24293
24625
  --selected-tab-border-width: var(--ixfx-selected-tab-border-width, 1px);
24294
24626
  --selected-tab-weight: var(--ixfx-selected-tab-weight, bold);
24295
24627
  --selected-background: var(--ixfx-tab-selected-background, var(--surface-5));
24296
- --padding: var(--space-m);
24628
+ --tab-padding: var(--ixfx-tab-padding, var(--space-m));
24297
24629
  display: inline-flex;
24298
24630
  min-width: 0;
24299
24631
  user-select: none;
@@ -24304,7 +24636,7 @@ let TabListItemElement = class TabListItemElement extends i {
24304
24636
  display: flex;
24305
24637
  align-items: center;
24306
24638
  gap: var(--space-s, 4px);
24307
- padding: var(--padding) calc(2 * var(--padding));
24639
+ padding: var(--tab-padding) calc(2 * var(--tab-padding));
24308
24640
  min-width: 0;
24309
24641
  width: 100%;
24310
24642
  font-weight: 500;