@ixfx/components 0.4.4 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/bundle/index.d.ts +136 -8
  2. package/bundle/index.d.ts.map +1 -1
  3. package/bundle/index.js +569 -23
  4. package/bundle/index.js.map +1 -1
  5. package/bundle/style.css +1 -1
  6. package/dist/ac-text.d.ts +1 -1
  7. package/dist/{button-DxYee1ZH.js → button-CTE8crYn.js} +3 -1
  8. package/dist/button-CTE8crYn.js.map +1 -0
  9. package/dist/button.d.ts +1 -1
  10. package/dist/button.d.ts.map +1 -1
  11. package/dist/button.js +1 -1
  12. package/dist/{colour-picker-BacNRabo.js → colour-picker-C3Ag4uh-.js} +2 -2
  13. package/dist/{colour-picker-BacNRabo.js.map → colour-picker-C3Ag4uh-.js.map} +1 -1
  14. package/dist/colour-picker.d.ts +1 -1
  15. package/dist/colour-picker.js +1 -1
  16. package/dist/crumbs.d.ts +1 -1
  17. package/dist/{defaults-C3b9OWJD.js → defaults-DcE8RQJc.js} +19 -1
  18. package/dist/defaults-DcE8RQJc.js.map +1 -0
  19. package/dist/{icon-DpA9x2Ve.d.ts → icon-BUwP1CZ1.d.ts} +2 -2
  20. package/dist/icon-BUwP1CZ1.d.ts.map +1 -0
  21. package/dist/icons.d.ts +2 -2
  22. package/dist/icons.js +1 -1
  23. package/dist/{index-DfEkTGvT.d.ts → index-B1UKcA7l.d.ts} +3 -3
  24. package/dist/{index-DfEkTGvT.d.ts.map → index-B1UKcA7l.d.ts.map} +1 -1
  25. package/dist/{index-D_ftz_IT.d.ts → index-BKAlRgbq.d.ts} +2 -2
  26. package/dist/{index-D_ftz_IT.d.ts.map → index-BKAlRgbq.d.ts.map} +1 -1
  27. package/dist/{index-CoqtMSd9.d.ts → index-BUB5SICW.d.ts} +2 -2
  28. package/dist/{index-CoqtMSd9.d.ts.map → index-BUB5SICW.d.ts.map} +1 -1
  29. package/dist/{index-BtGFYfaK.d.ts → index-RqMh1i-O.d.ts} +2 -2
  30. package/dist/{index-BtGFYfaK.d.ts.map → index-RqMh1i-O.d.ts.map} +1 -1
  31. package/dist/index.d.ts +140 -12
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +548 -27
  34. package/dist/index.js.map +1 -1
  35. package/dist/{labelled-input-base-DiTme1JO.d.ts → labelled-input-base-Dp_9KP0G.d.ts} +2 -2
  36. package/dist/{labelled-input-base-DiTme1JO.d.ts.map → labelled-input-base-Dp_9KP0G.d.ts.map} +1 -1
  37. package/dist/labelled-radial-input.d.ts +3 -3
  38. package/dist/labelled-radial-input.d.ts.map +1 -1
  39. package/dist/labelled-range-input.d.ts +1 -1
  40. package/dist/{menu-item-DSyf9f_L.d.ts → menu-item-CQ4Qg731.d.ts} +2 -2
  41. package/dist/{menu-item-DSyf9f_L.d.ts.map → menu-item-CQ4Qg731.d.ts.map} +1 -1
  42. package/dist/menu.d.ts +2 -2
  43. package/dist/panel.d.ts +1 -1
  44. package/dist/polar-pad.d.ts +1 -1
  45. package/dist/{radial-input-DXMN55Nz.d.ts → radial-input-BhZeyyP-.d.ts} +2 -2
  46. package/dist/{radial-input-DXMN55Nz.d.ts.map → radial-input-BhZeyyP-.d.ts.map} +1 -1
  47. package/dist/radial-input.d.ts +1 -1
  48. package/dist/range-input.d.ts +1 -1
  49. package/dist/range.d.ts +1 -1
  50. package/dist/{slider-input-XppH3vcy.d.ts → slider-input-TidGhvXt.d.ts} +2 -2
  51. package/dist/{slider-input-XppH3vcy.d.ts.map → slider-input-TidGhvXt.d.ts.map} +1 -1
  52. package/dist/slider-input.d.ts +1 -1
  53. package/dist/style.css +1 -1
  54. package/dist/swipe.d.ts +1 -1
  55. package/dist/{tab-list-DzknpdSo.js → tab-list-DhkVhRQc.js} +7 -2
  56. package/dist/{tab-list-DzknpdSo.js.map → tab-list-DhkVhRQc.js.map} +1 -1
  57. package/dist/{tab-list-BohmbvqF.d.ts → tab-list-VtWT-_FG.d.ts} +2 -2
  58. package/dist/{tab-list-BohmbvqF.d.ts.map → tab-list-VtWT-_FG.d.ts.map} +1 -1
  59. package/dist/tabs.d.ts +1 -1
  60. package/dist/tabs.js +1 -1
  61. package/dist/wheel-nudge-BlSCe_hD.js.map +1 -1
  62. package/dist/{wheel-nudge-DGrNzWuE.d.ts → wheel-nudge-DgMTRYlh.d.ts} +3 -3
  63. package/dist/wheel-nudge-DgMTRYlh.d.ts.map +1 -0
  64. package/dist/xy-pad.d.ts +1 -1
  65. package/docs-user/README.md +2 -0
  66. package/docs-user/button.md +50 -0
  67. package/docs-user/narrowed-text.md +33 -1
  68. package/docs-user/notifier.md +131 -0
  69. package/docs-user/plots.md +61 -1
  70. package/docs-user/slider-input.md +673 -0
  71. package/docs-user/tabs.md +52 -14
  72. package/docs-user/user-catalog.md +69 -4
  73. package/docs-user/vertical-list.md +102 -7
  74. package/package.json +3 -1
  75. package/dist/button-DxYee1ZH.js.map +0 -1
  76. package/dist/defaults-C3b9OWJD.js.map +0 -1
  77. package/dist/icon-DpA9x2Ve.d.ts.map +0 -1
  78. package/dist/wheel-nudge-DGrNzWuE.d.ts.map +0 -1
package/bundle/index.js CHANGED
@@ -2858,6 +2858,21 @@ const ICON_CHECK = `<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="tr
2858
2858
  */
2859
2859
  const ICON_CLOSE = `<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z"/></svg>`;
2860
2860
  /**
2861
+ * Error / warning icon. Useful for error notifications.
2862
+ * fill="currentColor" so it inherits CSS color.
2863
+ */
2864
+ const ICON_ERROR = `<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z"/></svg>`;
2865
+ /**
2866
+ * Info icon. Useful for info notifications.
2867
+ * fill="currentColor" so it inherits CSS color.
2868
+ */
2869
+ const ICON_INFO = `<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"/></svg>`;
2870
+ /**
2871
+ * Warning icon. Useful for warning notifications.
2872
+ * fill="currentColor" so it inherits CSS color.
2873
+ */
2874
+ const ICON_WARNING = `<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z"/></svg>`;
2875
+ /**
2861
2876
  * Chevron pointing right. Used for split-button switcher toggle.
2862
2877
  * fill="currentColor" so it inherits CSS color.
2863
2878
  */
@@ -2868,6 +2883,9 @@ function registerDefaults() {
2868
2883
  registerIcon(`caret-right`, ICON_CARET_RIGHT);
2869
2884
  registerIcon(`check`, ICON_CHECK);
2870
2885
  registerIcon(`close`, ICON_CLOSE);
2886
+ registerIcon(`error`, ICON_ERROR);
2887
+ registerIcon(`info`, ICON_INFO);
2888
+ registerIcon(`warning`, ICON_WARNING);
2871
2889
  }
2872
2890
  //#endregion
2873
2891
  //#region src/icons/index.ts
@@ -2989,6 +3007,7 @@ const buttonStyles = i$3`
2989
3007
  background: var(--surface-4, #ccc);
2990
3008
  color: var(--surface-3-text, #000);
2991
3009
  border: var(--border, 1px solid #999);
3010
+ outline: var(--ixfx-button-outline, none);
2992
3011
  border-radius: var(--radius-xl, 8px);
2993
3012
  corner-shape: superellipse(1);
2994
3013
  padding: var(--space-m, 8px) var(--space-l, 12px);
@@ -3003,6 +3022,7 @@ const buttonStyles = i$3`
3003
3022
  overflow: hidden;
3004
3023
  white-space: nowrap;
3005
3024
  box-shadow:
3025
+ var(--ixfx-button-shade, none),
3006
3026
  inset 1px 1px 1px 1px color-mix(in oklch, var(--surface-4) 30%, transparent), inset -1px -1px 1px 1px color-mix(in oklch, var(--surface-2) 20%, transparent),
3007
3027
  2px 2px 1px 0px color-mix(in oklch, var(--surface-2) 30%, transparent);
3008
3028
  transition: background var(--transition, 0.2s), border-color var(--transition, 0.2s);
@@ -3242,9 +3262,11 @@ let RadioGroup = class RadioGroup extends i {
3242
3262
  min-width: min-content;
3243
3263
  box-sizing: border-box;
3244
3264
  /* Default to looking like a cohesive control group */
3245
- background: var(--surface-3, transparent);
3265
+ background: var(--surface-4, transparent);
3266
+ background: oklch(from var(--surface-4) l calc(c*0.2) h / 0.4);
3267
+
3246
3268
  border: var(--border, none);
3247
- border-radius: var(--radius-m, 4px);
3269
+ border-radius: var(--radius-xl, 8px);
3248
3270
  }
3249
3271
 
3250
3272
  * {
@@ -3274,6 +3296,7 @@ let RadioGroup = class RadioGroup extends i {
3274
3296
  gap: var(--item-spacing, 0);
3275
3297
  overflow: hidden;
3276
3298
  border-radius: inherit;
3299
+ justify-content: center;
3277
3300
  }
3278
3301
 
3279
3302
  .radio-group.horizontal {
@@ -3315,13 +3338,13 @@ let RadioGroup = class RadioGroup extends i {
3315
3338
 
3316
3339
  .radio-item:hover:not([disabled]) {
3317
3340
  opacity: 0.9;
3318
- background: var(--surface-4, rgba(255,255,255,0.05));
3341
+ background: color-mix(in oklch, var(--surface-4) 80%, transparent);
3319
3342
  }
3320
3343
 
3321
3344
  .radio-item.selected {
3322
3345
  font-weight: bold;
3323
3346
  color: var(--color-fg-accent, var(--surface-2-text));
3324
- background: var(--surface-6, rgba(255,255,255,0.1));
3347
+ background: var(--surface-5, rgba(255,255,255,0.1));
3325
3348
  }
3326
3349
 
3327
3350
  .radio-item[disabled] {
@@ -11648,7 +11671,7 @@ let VerticalListElement = class VerticalListElement extends i {
11648
11671
  this._selectedItems = remapped;
11649
11672
  }
11650
11673
  this._syncItemStates();
11651
- }
11674
+ } else if (changed.has(`checkboxRenderer`) && this.interactionMode === `checked`) this._syncCheckboxes();
11652
11675
  }
11653
11676
  /** The most recently selected item, or `undefined` if nothing is selected. */
11654
11677
  get selectedItem() {
@@ -11844,6 +11867,7 @@ let VerticalListElement = class VerticalListElement extends i {
11844
11867
  }
11845
11868
  _syncCheckboxes() {
11846
11869
  const inCheckedMode = this.interactionMode === `checked`;
11870
+ const wantsCustom = typeof this.checkboxRenderer === `function`;
11847
11871
  if (inCheckedMode) for (const inner of Array.from(this.children)) {
11848
11872
  if (!inner.hasAttribute(`data-list-item`)) continue;
11849
11873
  if (inner.parentElement !== this) continue;
@@ -11868,24 +11892,78 @@ let VerticalListElement = class VerticalListElement extends i {
11868
11892
  this.#customWrappers.clear();
11869
11893
  }
11870
11894
  for (const el of this.querySelectorAll(`li`)) {
11871
- let cb = el.querySelector(`input.ixfx-list-checkbox`);
11872
- if (inCheckedMode) {
11873
- if (!cb) {
11874
- cb = document.createElement(`input`);
11875
- cb.type = `checkbox`;
11876
- cb.className = `ixfx-list-checkbox`;
11877
- cb.style.cssText = `flex-shrink:0;margin:0 6px 0 0;cursor:pointer;accent-color:var(--accent,#60a5fa);pointer-events:auto`;
11878
- cb.tabIndex = -1;
11879
- cb.addEventListener(`click`, (e) => {
11880
- e.stopPropagation();
11881
- this._toggleItem(el);
11882
- });
11883
- el.prepend(cb);
11895
+ const existing = el.querySelector(`.ixfx-list-checkbox`);
11896
+ if (!inCheckedMode) {
11897
+ if (existing) existing.remove();
11898
+ continue;
11899
+ }
11900
+ const existingIsCustom = existing?.classList.contains(`ixfx-list-checkbox-custom`) ?? false;
11901
+ if (existing && existingIsCustom !== wantsCustom) existing.remove();
11902
+ let cb = el.querySelector(`.ixfx-list-checkbox`);
11903
+ if (!cb) {
11904
+ if (wantsCustom) {
11905
+ const opts = this.checkboxRenderer;
11906
+ const margin = opts.margin ?? `0`;
11907
+ const width = opts.width ?? `24px`;
11908
+ cb = this._createCustomCheckbox(el, margin, width);
11909
+ } else cb = this._createNativeCheckboxCell(el);
11910
+ el.prepend(cb);
11911
+ }
11912
+ const checked = this._selectedItems.has(el);
11913
+ if (wantsCustom) {
11914
+ cb.setAttribute(`aria-checked`, String(checked));
11915
+ cb.toggleAttribute(`data-checked`, checked);
11916
+ if (this.checkboxRenderer) {
11917
+ const renderer = typeof this.checkboxRenderer === `function` ? this.checkboxRenderer : this.checkboxRenderer.renderer;
11918
+ D(renderer(checked), cb);
11884
11919
  }
11885
- cb.checked = this._selectedItems.has(el);
11886
- } else if (cb) cb.remove();
11920
+ } else {
11921
+ const input = cb.querySelector(`input.ixfx-list-checkbox`);
11922
+ if (input) input.checked = checked;
11923
+ }
11887
11924
  }
11888
11925
  }
11926
+ /**
11927
+ * Native checkbox cell: a span that stretches to the row's full cross-axis
11928
+ * (so the entire column height is a click target) with a real `<input>`
11929
+ * centred inside. The span owns the click; the input is purely visual and
11930
+ * has `pointer-events:none` so a single click produces one toggle.
11931
+ */
11932
+ _createNativeCheckboxCell(el) {
11933
+ const cell = document.createElement(`span`);
11934
+ cell.className = `ixfx-list-checkbox ixfx-list-checkbox-native`;
11935
+ cell.style.cssText = `display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;align-self:stretch;width:18px;margin:0 6px 0 0;cursor:pointer;pointer-events:auto`;
11936
+ cell.tabIndex = -1;
11937
+ cell.setAttribute(`role`, `checkbox`);
11938
+ cell.setAttribute(`part`, `checkbox`);
11939
+ cell.addEventListener(`click`, (e) => {
11940
+ e.stopPropagation();
11941
+ this._toggleItem(el);
11942
+ });
11943
+ const input = document.createElement(`input`);
11944
+ input.type = `checkbox`;
11945
+ input.className = `ixfx-list-checkbox`;
11946
+ input.style.cssText = `pointer-events:none;cursor:pointer;margin:0`;
11947
+ cell.appendChild(input);
11948
+ return cell;
11949
+ }
11950
+ /**
11951
+ * Custom checkbox cell: same shape as the native cell (a stretching span)
11952
+ * so behaviour is identical. The renderer's content goes inside.
11953
+ */
11954
+ _createCustomCheckbox(el, margin, width) {
11955
+ const span = document.createElement(`span`);
11956
+ span.className = `ixfx-list-checkbox ixfx-list-checkbox-custom`;
11957
+ span.style.cssText = `display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;align-self:stretch;width:${width};margin:${margin};cursor:pointer;pointer-events:auto`;
11958
+ span.tabIndex = -1;
11959
+ span.setAttribute(`role`, `checkbox`);
11960
+ span.setAttribute(`part`, `checkbox`);
11961
+ span.addEventListener(`click`, (e) => {
11962
+ e.stopPropagation();
11963
+ this._toggleItem(el);
11964
+ });
11965
+ return span;
11966
+ }
11889
11967
  _findListItem(path) {
11890
11968
  for (const target of path) {
11891
11969
  if (!(target instanceof Element)) continue;
@@ -11947,8 +12025,14 @@ let VerticalListElement = class VerticalListElement extends i {
11947
12025
  padding: 0;
11948
12026
  width: 100%;
11949
12027
  height: 100%;
12028
+ overflow-x: hidden;
11950
12029
  overflow-y: auto;
11951
12030
  box-sizing: border-box;
12031
+ /* Scrollbar theming: end users override these CSS variables to control
12032
+ the native scrollbar (scrollbar-width, scrollbar-gutter, scrollbar-color). */
12033
+ scrollbar-width: var(--list-scrollbar-width, auto);
12034
+ scrollbar-gutter: var(--list-scrollbar-gutter, auto);
12035
+ scrollbar-color: var(--list-scrollbar-color, auto);
11952
12036
  }
11953
12037
 
11954
12038
  ::slotted([hidden]) {
@@ -11978,6 +12062,25 @@ let VerticalListElement = class VerticalListElement extends i {
11978
12062
  outline: 1px solid transparent;
11979
12063
  }
11980
12064
 
12065
+ /* In checked mode, only the checkbox column is clickable; the row body
12066
+ gets a default cursor so it doesn't suggest otherwise. */
12067
+ :host([interaction-mode="checked"]) ::slotted(li),
12068
+ :host([interaction-mode="checked"]) ::slotted([data-list-item]) {
12069
+ cursor: default;
12070
+ }
12071
+
12072
+ /* Wrapper inserted around custom [data-list-item] elements in checked mode.
12073
+ Provides the flex layout so the injected checkbox can stretch to the
12074
+ row's full height via align-self:stretch. */
12075
+ .ixfx-custom-row {
12076
+ display: flex;
12077
+ align-items: center;
12078
+ min-height: var(--list-item-height, 28px);
12079
+ box-sizing: border-box;
12080
+ cursor: default;
12081
+ user-select: none;
12082
+ }
12083
+
11981
12084
  ::slotted([data-tickled]) {
11982
12085
  background: var(--item-bg-tickled);
11983
12086
  outline: var(--item-border-tickled);
@@ -12085,6 +12188,7 @@ __decorate([property({
12085
12188
  attribute: `interaction-mode`
12086
12189
  })], VerticalListElement.prototype, "interactionMode", void 0);
12087
12190
  __decorate([property({ attribute: false })], VerticalListElement.prototype, "filterPredicate", void 0);
12191
+ __decorate([property({ attribute: false })], VerticalListElement.prototype, "checkboxRenderer", void 0);
12088
12192
  __decorate([state()], VerticalListElement.prototype, "_selectedItems", void 0);
12089
12193
  __decorate([state()], VerticalListElement.prototype, "_searchVisible", void 0);
12090
12194
  __decorate([state()], VerticalListElement.prototype, "_searchQuery", void 0);
@@ -16643,6 +16747,441 @@ var MillerTreeController = class {
16643
16747
  }
16644
16748
  };
16645
16749
  //#endregion
16750
+ //#region src/notifier/notification-pill.ts
16751
+ let NotificationPillElement = class NotificationPillElement extends i {
16752
+ constructor(..._args) {
16753
+ super(..._args);
16754
+ this.notificationType = `info`;
16755
+ this.message = ``;
16756
+ this.dismissing = false;
16757
+ this.notificationId = ``;
16758
+ }
16759
+ #handleClick() {
16760
+ this.#emitTap();
16761
+ }
16762
+ #handleKeyDown(e) {
16763
+ if (e.key === `Enter` || e.key === ` `) {
16764
+ e.preventDefault();
16765
+ this.#emitTap();
16766
+ }
16767
+ }
16768
+ #emitTap() {
16769
+ this.dispatchEvent(new CustomEvent(`pill-tap`, {
16770
+ detail: { id: this.notificationId },
16771
+ bubbles: true,
16772
+ composed: true
16773
+ }));
16774
+ }
16775
+ #renderIcon() {
16776
+ if (!this.icon) return A;
16777
+ if (this.icon.startsWith(`<svg`)) return b`<span class="pill-icon">${unsafeHTML(this.icon)}</span>`;
16778
+ if (!getIcon(this.icon)) return A;
16779
+ return b`<ixfx-icon class="pill-icon" name="${this.icon}"></ixfx-icon>`;
16780
+ }
16781
+ render() {
16782
+ return b`
16783
+ <button
16784
+ type="button"
16785
+ class="pill"
16786
+ notification-type="${this.notificationType}"
16787
+ @click=${this.#handleClick}
16788
+ @keydown=${this.#handleKeyDown}
16789
+ aria-label="${this.message}"
16790
+ >
16791
+ ${this.#renderIcon()}
16792
+ <span class="pill-message">${this.message}</span>
16793
+ </button>
16794
+ `;
16795
+ }
16796
+ static {
16797
+ this.styles = [themeFallbacks, i$3`
16798
+ :host {
16799
+ display: block;
16800
+ }
16801
+
16802
+ .pill {
16803
+ appearance: none;
16804
+ cursor: pointer;
16805
+ font-family: var(--font-family, inherit);
16806
+ font-size: var(--text-m, 12px);
16807
+ padding: var(--space-s, 4px) var(--space-m, 8px);
16808
+ border-radius: var(--radius-l, 6px);
16809
+ border: 1px solid var(--surface-6, #666);
16810
+ display: inline-flex;
16811
+ align-items: center;
16812
+ gap: var(--space-s, 4px);
16813
+ max-width: 300px;
16814
+ box-shadow: var(--shadow-m);
16815
+ transition: opacity 0.2s ease-out, transform 0.2s ease-out;
16816
+ pointer-events: auto;
16817
+ }
16818
+
16819
+ :host([dismissing]) .pill {
16820
+ opacity: 0;
16821
+ transform: translateX(20px);
16822
+ }
16823
+
16824
+ .pill[notification-type="info"] {
16825
+ background: var(--surface-5, #555);
16826
+ color: var(--surface-5-text, #fff);
16827
+ }
16828
+
16829
+ .pill[notification-type="warning"] {
16830
+ background: color-mix(in oklch, oklch(65% 0.18 70) 30%, var(--surface-5));
16831
+ border-color: oklch(65% 0.18 70);
16832
+ color: var(--surface-5-text, #fff);
16833
+ }
16834
+
16835
+ .pill[notification-type="error"] {
16836
+ background: var(--error, #c00);
16837
+ border-color: var(--error, #c00);
16838
+ color: var(--error-text, #fff);
16839
+ }
16840
+
16841
+ .pill-icon {
16842
+ display: inline-flex;
16843
+ align-items: center;
16844
+ flex-shrink: 0;
16845
+ }
16846
+
16847
+ .pill-icon ixfx-icon {
16848
+ font-size: var(--text-m, 12px);
16849
+ }
16850
+
16851
+ .pill-message {
16852
+ white-space: nowrap;
16853
+ overflow: hidden;
16854
+ text-overflow: ellipsis;
16855
+ }
16856
+
16857
+ .pill:focus-visible {
16858
+ outline: 2px solid var(--accent, #4a9);
16859
+ outline-offset: 2px;
16860
+ }
16861
+
16862
+ @media (prefers-reduced-motion: reduce) {
16863
+ .pill {
16864
+ transition: opacity 0.1s ease-out;
16865
+ transform: none !important;
16866
+ }
16867
+ }
16868
+ `];
16869
+ }
16870
+ };
16871
+ __decorate([property({
16872
+ type: String,
16873
+ attribute: `notification-type`
16874
+ })], NotificationPillElement.prototype, "notificationType", void 0);
16875
+ __decorate([property({ type: String })], NotificationPillElement.prototype, "message", void 0);
16876
+ __decorate([property({ type: String })], NotificationPillElement.prototype, "icon", void 0);
16877
+ __decorate([property({
16878
+ type: Boolean,
16879
+ reflect: true
16880
+ })], NotificationPillElement.prototype, "dismissing", void 0);
16881
+ __decorate([property({
16882
+ type: String,
16883
+ attribute: `notification-id`
16884
+ })], NotificationPillElement.prototype, "notificationId", void 0);
16885
+ NotificationPillElement = __decorate([safeCustomElement(`ixfx-notification-pill`)], NotificationPillElement);
16886
+ //#endregion
16887
+ //#region src/notifier/pill-renderer.ts
16888
+ const ROW_SPACING = 8;
16889
+ const DEFAULT_ROW_HEIGHT = 40;
16890
+ const STACK_OFFSET_STEP = 4;
16891
+ const STACK_OPACITY_BASE = .7;
16892
+ const STACK_OPACITY_DECAY = .15;
16893
+ const STACK_OPACITY_MIN = .2;
16894
+ function edgesOf(position) {
16895
+ return {
16896
+ vertical: position.startsWith(`top`) ? `top` : `bottom`,
16897
+ horizontal: position.endsWith(`-left`) ? `left` : position.endsWith(`-right`) ? `right` : `middle`
16898
+ };
16899
+ }
16900
+ function anchorStyles(position, safeArea) {
16901
+ const { vertical, horizontal } = edgesOf(position);
16902
+ const vMargin = `calc(var(--space-l, 12px) + ${(vertical === `top` ? safeArea.top : safeArea.bottom) ?? 0}px)`;
16903
+ let css = `position: fixed; `;
16904
+ css += vertical === `top` ? `top: ${vMargin}; bottom: auto; ` : `bottom: ${vMargin}; top: auto; `;
16905
+ if (horizontal === `left`) {
16906
+ const hMargin = `calc(var(--space-l, 12px) + ${safeArea.left ?? 0}px)`;
16907
+ css += `left: ${hMargin}; right: auto;`;
16908
+ } else if (horizontal === `right`) {
16909
+ const hMargin = `calc(var(--space-l, 12px) + ${safeArea.right ?? 0}px)`;
16910
+ css += `right: ${hMargin}; left: auto;`;
16911
+ } else css += `left: 50%; right: auto;`;
16912
+ return css;
16913
+ }
16914
+ /** Entry-animation starting transform: pills slide in from the edge they're anchored to. */
16915
+ function entryTransform(edges) {
16916
+ const dy = edges.vertical === `top` ? -20 : 20;
16917
+ if (edges.horizontal === `middle`) return `translateX(-50%) translate(0, ${dy}px)`;
16918
+ return `translate(${edges.horizontal === `left` ? -20 : 20}px, ${dy}px)`;
16919
+ }
16920
+ function restingTransform(edges, dx, dy) {
16921
+ if (edges.horizontal === `middle`) return `translateX(-50%) translate(${dx}px, ${dy}px)`;
16922
+ return `translate(${dx}px, ${dy}px)`;
16923
+ }
16924
+ function createPillRenderer(options = {}) {
16925
+ const position = options.position ?? `top-right`;
16926
+ const queueDisplay = options.queueDisplay ?? `list`;
16927
+ const safeArea = options.safeArea ?? {};
16928
+ const edges = edgesOf(position);
16929
+ let container = null;
16930
+ let hooks = null;
16931
+ const pills = /* @__PURE__ */ new Map();
16932
+ let rowHeight = DEFAULT_ROW_HEIGHT;
16933
+ let measured = false;
16934
+ function ensureContainer() {
16935
+ if (container) return container;
16936
+ container = document.createElement(`div`);
16937
+ container.className = `ixfx-notification-container`;
16938
+ container.setAttribute(`role`, `region`);
16939
+ container.setAttribute(`aria-label`, `Notifications`);
16940
+ document.body.appendChild(container);
16941
+ return container;
16942
+ }
16943
+ function updateAttributes(el, notification) {
16944
+ el.setAttribute(`notification-type`, notification.type);
16945
+ el.setAttribute(`message`, notification.message);
16946
+ if (notification.icon) el.setAttribute(`icon`, notification.icon);
16947
+ else el.removeAttribute(`icon`);
16948
+ }
16949
+ function createPillElement(notification) {
16950
+ const el = document.createElement(`ixfx-notification-pill`);
16951
+ el.setAttribute(`notification-id`, notification.id);
16952
+ el.style.cssText = anchorStyles(position, safeArea);
16953
+ updateAttributes(el, notification);
16954
+ el.style.opacity = `0`;
16955
+ el.style.transform = entryTransform(edges);
16956
+ el.addEventListener(`pill-tap`, (e) => {
16957
+ const customEvent = e;
16958
+ hooks?.onTap(customEvent.detail.id);
16959
+ });
16960
+ return el;
16961
+ }
16962
+ function measureRowHeight(el) {
16963
+ if (measured) return;
16964
+ requestAnimationFrame(() => {
16965
+ const height = el.getBoundingClientRect().height;
16966
+ if (height > 0) {
16967
+ rowHeight = height;
16968
+ measured = true;
16969
+ }
16970
+ });
16971
+ }
16972
+ /** Positions every currently-visible pill for its rank in `order` (index 0 = front/oldest). */
16973
+ function applyLayout(order) {
16974
+ const total = order.length;
16975
+ order.forEach((notification, index) => {
16976
+ const entry = pills.get(notification.id);
16977
+ if (!entry) return;
16978
+ const el = entry.element;
16979
+ let dx = 0;
16980
+ let dy = 0;
16981
+ let opacity = 1;
16982
+ let pointerEvents = `auto`;
16983
+ let filter = `none`;
16984
+ if (queueDisplay === `list`) {
16985
+ const step = index * (rowHeight + ROW_SPACING);
16986
+ dy = edges.vertical === `top` ? step : -step;
16987
+ } else {
16988
+ const step = index * STACK_OFFSET_STEP;
16989
+ dy = edges.vertical === `top` ? step : -step;
16990
+ dx = edges.horizontal === `left` ? step : edges.horizontal === `right` ? -step : 0;
16991
+ if (index > 0) {
16992
+ const hintIndex = index - 1;
16993
+ opacity = Math.max(STACK_OPACITY_MIN, STACK_OPACITY_BASE - hintIndex * STACK_OPACITY_DECAY);
16994
+ pointerEvents = `none`;
16995
+ filter = `saturate(0.6)`;
16996
+ }
16997
+ }
16998
+ el.style.transition = `opacity 0.2s ease-out, transform 0.2s ease-out`;
16999
+ el.style.opacity = `${opacity}`;
17000
+ el.style.zIndex = `${total - index}`;
17001
+ el.style.pointerEvents = pointerEvents;
17002
+ el.style.filter = filter;
17003
+ el.style.transform = restingTransform(edges, dx, dy);
17004
+ });
17005
+ }
17006
+ function dismiss(id) {
17007
+ const entry = pills.get(id);
17008
+ if (!entry) return;
17009
+ const el = entry.element;
17010
+ el.setAttribute(`dismissing`, `true`);
17011
+ const cleanup = () => {
17012
+ el.removeEventListener(`transitionend`, cleanup);
17013
+ el.remove();
17014
+ pills.delete(id);
17015
+ if (container && pills.size === 0) {
17016
+ container.remove();
17017
+ container = null;
17018
+ measured = false;
17019
+ rowHeight = DEFAULT_ROW_HEIGHT;
17020
+ }
17021
+ };
17022
+ el.addEventListener(`transitionend`, cleanup);
17023
+ setTimeout(cleanup, 300);
17024
+ }
17025
+ function setNotifications(notifications, _queueSize) {
17026
+ const cont = ensureContainer();
17027
+ const idsToKeep = new Set(notifications.map((n) => n.id));
17028
+ for (const notification of notifications) {
17029
+ const existing = pills.get(notification.id);
17030
+ if (existing) {
17031
+ existing.notification = notification;
17032
+ updateAttributes(existing.element, notification);
17033
+ continue;
17034
+ }
17035
+ const el = createPillElement(notification);
17036
+ pills.set(notification.id, {
17037
+ element: el,
17038
+ notification
17039
+ });
17040
+ cont.appendChild(el);
17041
+ measureRowHeight(el);
17042
+ }
17043
+ for (const [id] of pills) if (!idsToKeep.has(id)) dismiss(id);
17044
+ requestAnimationFrame(() => applyLayout(notifications));
17045
+ }
17046
+ return {
17047
+ attach(newHooks) {
17048
+ hooks = newHooks;
17049
+ },
17050
+ detach() {
17051
+ for (const [id] of pills) dismiss(id);
17052
+ if (container) {
17053
+ container.remove();
17054
+ container = null;
17055
+ }
17056
+ },
17057
+ setNotifications
17058
+ };
17059
+ }
17060
+ //#endregion
17061
+ //#region src/notifier/manager.ts
17062
+ const options = {
17063
+ renderer: void 0,
17064
+ timeout: 4e3,
17065
+ timeoutError: 8e3,
17066
+ queue: true,
17067
+ queueSize: 3,
17068
+ tapBehaviour: `dismiss`,
17069
+ tapHandlerDefault: void 0
17070
+ };
17071
+ /** Rank-ordered, index 0 = oldest/first (next to be evicted or timed out). Length never exceeds queueSize (or 1 when queuing is disabled). */
17072
+ const visible = [];
17073
+ let renderer = null;
17074
+ let defaultRenderer = null;
17075
+ function getDefaultRenderer() {
17076
+ defaultRenderer ??= createPillRenderer({ position: `top-right` });
17077
+ return defaultRenderer;
17078
+ }
17079
+ function getRenderer() {
17080
+ return renderer ?? getDefaultRenderer();
17081
+ }
17082
+ function syncRenderer() {
17083
+ getRenderer().setNotifications([...visible], options.queueSize);
17084
+ }
17085
+ function clearTimer(managed) {
17086
+ if (managed.timer !== null) {
17087
+ clearTimeout(managed.timer);
17088
+ managed.timer = null;
17089
+ }
17090
+ }
17091
+ /**
17092
+ * A message shown alone takes the full timeout. A message that joined a
17093
+ * non-empty queue at arrival time is permanently "wasQueued" and gets
17094
+ * 1/queueSize of the timeout once it reaches the front. The timer for a
17095
+ * given message is only ever started once - when it first becomes front.
17096
+ */
17097
+ function startTimer(managed) {
17098
+ const base = managed.type === `error` ? options.timeoutError : options.timeout;
17099
+ const duration = managed.wasQueued ? base / options.queueSize : base;
17100
+ managed.timer = window.setTimeout(() => dismiss(managed.id), duration);
17101
+ }
17102
+ function ensureFrontTimer() {
17103
+ const front = visible[0];
17104
+ if (front && front.timer === null) startTimer(front);
17105
+ }
17106
+ const NotificationManager = {
17107
+ configure(opts) {
17108
+ if (opts.renderer !== void 0) {
17109
+ renderer?.detach?.();
17110
+ renderer = opts.renderer;
17111
+ renderer.attach({ onTap(id) {
17112
+ const notification = visible.find((n) => n.id === id);
17113
+ if (!notification) return;
17114
+ if (notification.onTap) notification.onTap(id);
17115
+ else options.tapHandlerDefault?.(notification);
17116
+ if (options.tapBehaviour === `dismiss`) dismiss(id);
17117
+ } });
17118
+ syncRenderer();
17119
+ }
17120
+ if (opts.timeout !== void 0) options.timeout = opts.timeout;
17121
+ if (opts.timeoutError !== void 0) options.timeoutError = opts.timeoutError;
17122
+ if (opts.queue !== void 0) options.queue = opts.queue;
17123
+ if (opts.queueSize !== void 0) {
17124
+ options.queueSize = Math.max(1, opts.queueSize);
17125
+ while (visible.length > options.queueSize) clearTimer(visible.shift());
17126
+ ensureFrontTimer();
17127
+ syncRenderer();
17128
+ }
17129
+ if (opts.tapBehaviour !== void 0) options.tapBehaviour = opts.tapBehaviour;
17130
+ if (opts.tapHandlerDefault !== void 0) options.tapHandlerDefault = opts.tapHandlerDefault;
17131
+ },
17132
+ notify(content) {
17133
+ const id = crypto.randomUUID();
17134
+ const now = Date.now();
17135
+ const icon = content.icon ?? (content.type === `error` ? `error` : void 0);
17136
+ const managed = {
17137
+ ...content,
17138
+ icon,
17139
+ id,
17140
+ createdAt: now,
17141
+ updatedAt: now,
17142
+ timer: null,
17143
+ wasQueued: false
17144
+ };
17145
+ if (options.queue) {
17146
+ if (visible.length >= options.queueSize) clearTimer(visible.shift());
17147
+ managed.wasQueued = visible.length > 0;
17148
+ visible.push(managed);
17149
+ } else {
17150
+ while (visible.length > 0) clearTimer(visible.pop());
17151
+ visible.push(managed);
17152
+ }
17153
+ ensureFrontTimer();
17154
+ syncRenderer();
17155
+ return id;
17156
+ },
17157
+ update(id, content) {
17158
+ const managed = visible.find((n) => n.id === id);
17159
+ if (!managed) return;
17160
+ Object.assign(managed, content);
17161
+ managed.updatedAt = Date.now();
17162
+ if (visible[0] === managed) {
17163
+ clearTimer(managed);
17164
+ startTimer(managed);
17165
+ }
17166
+ syncRenderer();
17167
+ },
17168
+ dismiss(id) {
17169
+ dismiss(id);
17170
+ },
17171
+ dismissAll() {
17172
+ for (const m of visible) clearTimer(m);
17173
+ visible.length = 0;
17174
+ syncRenderer();
17175
+ }
17176
+ };
17177
+ function dismiss(id) {
17178
+ const idx = visible.findIndex((m) => m.id === id);
17179
+ if (idx === -1) return;
17180
+ clearTimer(visible.splice(idx, 1)[0]);
17181
+ ensureFrontTimer();
17182
+ syncRenderer();
17183
+ }
17184
+ //#endregion
16646
17185
  //#region src/panel/panel-group.ts
16647
17186
  let PanelGroupElement = class PanelGroupElement extends i {
16648
17187
  constructor(..._args) {
@@ -23145,7 +23684,7 @@ let TabListItemElement = class TabListItemElement extends i {
23145
23684
  prominenceStyles,
23146
23685
  i$3`
23147
23686
  :host {
23148
- --selected-colour: var(--accent);
23687
+ --selected-colour: var(--surface-2-text);
23149
23688
  --selected-border-width: 1px;
23150
23689
  --padding: var(--space-m);
23151
23690
  display: inline-flex;
@@ -23171,6 +23710,8 @@ let TabListItemElement = class TabListItemElement extends i {
23171
23710
  border-bottom-color: var(--indicator-color-bottom, transparent);
23172
23711
  border-left-color: var(--indicator-color-left, transparent);
23173
23712
  border-right-color: var(--indicator-color-right, transparent);
23713
+ background: var(--surface-5);
23714
+ font-weight: bold;
23174
23715
  }
23175
23716
 
23176
23717
  .label {
@@ -23747,9 +24288,14 @@ let TabListElement = class TabListElement extends i {
23747
24288
  :host {
23748
24289
  display: inline-block;
23749
24290
  --activated-colour: var(--accent);
23750
- --colour: var(--surface-4);
24291
+ --colour: var(--surface-3-text);
23751
24292
  --scroll-max-height: 200px;
23752
24293
  --allow-overflow: hidden;
24294
+ color: var(--colour);
24295
+ border: var(--border, none);
24296
+ border-color: var(--surface-5);
24297
+ border-radius: var(--radius-l, 8px) var(--radius-l, 8px) 0 0;
24298
+
23753
24299
  }
23754
24300
  :host(.scrollable) {
23755
24301
  max-height: var(--scroll-max-height);
@@ -26523,6 +27069,6 @@ function init() {
26523
27069
  console.log(`Init`);
26524
27070
  }
26525
27071
  //#endregion
26526
- export { AcTextElement, AcTokenElement, ButtonElement, Checkbox, ColourPicker, ColourPickerPopup, CrumbNavigationElement, CrumbPathController, CrumbPathElement, DataController, DataDisplayComponent, EditableLabel, EditableNumber, GroupedItemListerElement, ICON_CARET_RIGHT, ICON_CHECK, ICON_CHEVRON_DOWN, ICON_CLOSE, IncrSearchTreeController as IncrSearchMillerController, IncrSearchTreeController, IxfxHexEditorElement, IxfxIconElement, IxfxTimelineElement, LabelledRadialInput, LabelledRangeInput, LedElement, menu_exports as Menu, MillerBaseElement, MillerListElement, MillerTreeController, NarrowedTextElement, PanelElement, PanelGroupElement, PlotDataSeries, PlotHistogram, PlotMultiAxis, PlotSingleAxis, PlotXyAxis, PolarPad, RadialInputElement, RadioGroup, RangeElement, RangeInput, RangeMultiElement, SelectHorizontalElement, SliderInputElement, SnapContainer, SplitButton, SplitLayoutElement, SwipeElement, TabListElement, TabListItemElement, TabPanelElement, TabPanelsElement, TimelineController, TimelineTrackTabElement, TooltipElement, TransitoryLabelElement, TreeBaseElement, TreeController, TreeDataModel, TreeListElement, VerticalListElement, XyPad, applyAgeModification, applyHighlights, attachTimelineInteractions, buildHighlightTargets, clearHighlights, collectDataResult, computeColour, createAcCompletionProvider, createElementIncrSearch, createFuzzySearch, createIncrSearch, createTreeListAdapter, createVerticalListAdapter, getIcon, hasIcon, iconBus, init, monitorIndeterminate, parseColourScaleAttr, readBaseColour, registerIcon, resolveCssVars, resolveGrid, svgToDataUri, timelineStyles };
27072
+ export { AcTextElement, AcTokenElement, ButtonElement, Checkbox, ColourPicker, ColourPickerPopup, CrumbNavigationElement, CrumbPathController, CrumbPathElement, DataController, DataDisplayComponent, EditableLabel, EditableNumber, GroupedItemListerElement, ICON_CARET_RIGHT, ICON_CHECK, ICON_CHEVRON_DOWN, ICON_CLOSE, IncrSearchTreeController as IncrSearchMillerController, IncrSearchTreeController, IxfxHexEditorElement, IxfxIconElement, IxfxTimelineElement, LabelledRadialInput, LabelledRangeInput, LedElement, menu_exports as Menu, MillerBaseElement, MillerListElement, MillerTreeController, NarrowedTextElement, NotificationManager, NotificationPillElement, PanelElement, PanelGroupElement, PlotDataSeries, PlotHistogram, PlotMultiAxis, PlotSingleAxis, PlotXyAxis, PolarPad, RadialInputElement, RadioGroup, RangeElement, RangeInput, RangeMultiElement, SelectHorizontalElement, SliderInputElement, SnapContainer, SplitButton, SplitLayoutElement, SwipeElement, TabListElement, TabListItemElement, TabPanelElement, TabPanelsElement, TimelineController, TimelineTrackTabElement, TooltipElement, TransitoryLabelElement, TreeBaseElement, TreeController, TreeDataModel, TreeListElement, VerticalListElement, XyPad, applyAgeModification, applyHighlights, attachTimelineInteractions, buildHighlightTargets, clearHighlights, collectDataResult, computeColour, createAcCompletionProvider, createElementIncrSearch, createFuzzySearch, createIncrSearch, createPillRenderer, createTreeListAdapter, createVerticalListAdapter, getIcon, hasIcon, iconBus, init, monitorIndeterminate, parseColourScaleAttr, readBaseColour, registerIcon, resolveCssVars, resolveGrid, svgToDataUri, timelineStyles };
26527
27073
 
26528
27074
  //# sourceMappingURL=index.js.map