@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/dist/index.js CHANGED
@@ -6,14 +6,14 @@ import { a as svgToDataUri, i as registerIcon, n as hasIcon, r as iconBus, t as
6
6
  import { t as IxfxIconElement } from "./icon-B22XVlCt.js";
7
7
  import { AcTextElement } from "./ac-text.js";
8
8
  import { a as createFuzzySearch, i as createAcCompletionProvider, n as IncrSearchTreeController, r as createIncrSearch, t as createElementIncrSearch } from "./incr-search-2pz21xtR.js";
9
- import { i as ICON_CLOSE, n as ICON_CHECK, r as ICON_CHEVRON_DOWN, t as ICON_CARET_RIGHT } from "./defaults-C3b9OWJD.js";
9
+ import { i as ICON_CLOSE, n as ICON_CHECK, r as ICON_CHEVRON_DOWN, t as ICON_CARET_RIGHT } from "./defaults-DcE8RQJc.js";
10
10
  import "./icons.js";
11
11
  import { t as prominenceStyles } from "./prominence-DlFShic3.js";
12
- import { n as buttonStyles, t as ButtonElement } from "./button-DxYee1ZH.js";
12
+ import { n as buttonStyles, t as ButtonElement } from "./button-CTE8crYn.js";
13
13
  import { t as popupSurfaceStyles } from "./popup-DiWMxsWj.js";
14
14
  import "./menu-item-NUKwJ4s5.js";
15
15
  import { Checkbox } from "./checkbox.js";
16
- import { n as ColourPicker, t as ColourPickerPopup } from "./colour-picker-BacNRabo.js";
16
+ import { n as ColourPicker, t as ColourPickerPopup } from "./colour-picker-C3Ag4uh-.js";
17
17
  import { LabelledRangeInput } from "./labelled-range-input.js";
18
18
  import { t as collectDataResult } from "./data-provider-35TyWclX.js";
19
19
  import { i as TreeDataModel, n as TreeListElement, r as TreeBaseElement, t as TreeController } from "./tree-BnvzhsAw.js";
@@ -40,10 +40,10 @@ import { t as SliderInputElement } from "./slider-input-DFpG23bS.js";
40
40
  import { SnapContainer } from "./snap-container.js";
41
41
  import { SplitLayoutElement } from "./split-layout.js";
42
42
  import { SwipeElement } from "./swipe.js";
43
- import { t as TabListElement } from "./tab-list-DzknpdSo.js";
43
+ import { t as TabListElement } from "./tab-list-DhkVhRQc.js";
44
44
  import { a as resolveCssVars, i as attachTimelineInteractions, n as TimelineTrackTabElement, o as resolveGrid, r as timelineStyles, s as TimelineController, t as IxfxTimelineElement } from "./timeline-B2fgDL4U.js";
45
45
  import { XyPad } from "./xy-pad.js";
46
- import { LitElement, css, html, nothing } from "lit";
46
+ import { LitElement, css, html, nothing, render } from "lit";
47
47
  import { eventOptions, property, query, queryAssignedElements, state } from "lit/decorators.js";
48
48
  import { unsafeHTML } from "lit/directives/unsafe-html.js";
49
49
  import { Trees } from "@ixfx/collections";
@@ -1001,9 +1001,11 @@ let RadioGroup = class RadioGroup extends LitElement {
1001
1001
  min-width: min-content;
1002
1002
  box-sizing: border-box;
1003
1003
  /* Default to looking like a cohesive control group */
1004
- background: var(--surface-3, transparent);
1004
+ background: var(--surface-4, transparent);
1005
+ background: oklch(from var(--surface-4) l calc(c*0.2) h / 0.4);
1006
+
1005
1007
  border: var(--border, none);
1006
- border-radius: var(--radius-m, 4px);
1008
+ border-radius: var(--radius-xl, 8px);
1007
1009
  }
1008
1010
 
1009
1011
  * {
@@ -1033,6 +1035,7 @@ let RadioGroup = class RadioGroup extends LitElement {
1033
1035
  gap: var(--item-spacing, 0);
1034
1036
  overflow: hidden;
1035
1037
  border-radius: inherit;
1038
+ justify-content: center;
1036
1039
  }
1037
1040
 
1038
1041
  .radio-group.horizontal {
@@ -1074,13 +1077,13 @@ let RadioGroup = class RadioGroup extends LitElement {
1074
1077
 
1075
1078
  .radio-item:hover:not([disabled]) {
1076
1079
  opacity: 0.9;
1077
- background: var(--surface-4, rgba(255,255,255,0.05));
1080
+ background: color-mix(in oklch, var(--surface-4) 80%, transparent);
1078
1081
  }
1079
1082
 
1080
1083
  .radio-item.selected {
1081
1084
  font-weight: bold;
1082
1085
  color: var(--color-fg-accent, var(--surface-2-text));
1083
- background: var(--surface-6, rgba(255,255,255,0.1));
1086
+ background: var(--surface-5, rgba(255,255,255,0.1));
1084
1087
  }
1085
1088
 
1086
1089
  .radio-item[disabled] {
@@ -3356,7 +3359,7 @@ let VerticalListElement = class VerticalListElement extends LitElement {
3356
3359
  this._selectedItems = remapped;
3357
3360
  }
3358
3361
  this._syncItemStates();
3359
- }
3362
+ } else if (changed.has(`checkboxRenderer`) && this.interactionMode === `checked`) this._syncCheckboxes();
3360
3363
  }
3361
3364
  /** The most recently selected item, or `undefined` if nothing is selected. */
3362
3365
  get selectedItem() {
@@ -3552,6 +3555,7 @@ let VerticalListElement = class VerticalListElement extends LitElement {
3552
3555
  }
3553
3556
  _syncCheckboxes() {
3554
3557
  const inCheckedMode = this.interactionMode === `checked`;
3558
+ const wantsCustom = typeof this.checkboxRenderer === `function`;
3555
3559
  if (inCheckedMode) for (const inner of Array.from(this.children)) {
3556
3560
  if (!inner.hasAttribute(`data-list-item`)) continue;
3557
3561
  if (inner.parentElement !== this) continue;
@@ -3576,24 +3580,78 @@ let VerticalListElement = class VerticalListElement extends LitElement {
3576
3580
  this.#customWrappers.clear();
3577
3581
  }
3578
3582
  for (const el of this.querySelectorAll(`li`)) {
3579
- let cb = el.querySelector(`input.ixfx-list-checkbox`);
3580
- if (inCheckedMode) {
3581
- if (!cb) {
3582
- cb = document.createElement(`input`);
3583
- cb.type = `checkbox`;
3584
- cb.className = `ixfx-list-checkbox`;
3585
- cb.style.cssText = `flex-shrink:0;margin:0 6px 0 0;cursor:pointer;accent-color:var(--accent,#60a5fa);pointer-events:auto`;
3586
- cb.tabIndex = -1;
3587
- cb.addEventListener(`click`, (e) => {
3588
- e.stopPropagation();
3589
- this._toggleItem(el);
3590
- });
3591
- el.prepend(cb);
3583
+ const existing = el.querySelector(`.ixfx-list-checkbox`);
3584
+ if (!inCheckedMode) {
3585
+ if (existing) existing.remove();
3586
+ continue;
3587
+ }
3588
+ const existingIsCustom = existing?.classList.contains(`ixfx-list-checkbox-custom`) ?? false;
3589
+ if (existing && existingIsCustom !== wantsCustom) existing.remove();
3590
+ let cb = el.querySelector(`.ixfx-list-checkbox`);
3591
+ if (!cb) {
3592
+ if (wantsCustom) {
3593
+ const opts = this.checkboxRenderer;
3594
+ const margin = opts.margin ?? `0`;
3595
+ const width = opts.width ?? `24px`;
3596
+ cb = this._createCustomCheckbox(el, margin, width);
3597
+ } else cb = this._createNativeCheckboxCell(el);
3598
+ el.prepend(cb);
3599
+ }
3600
+ const checked = this._selectedItems.has(el);
3601
+ if (wantsCustom) {
3602
+ cb.setAttribute(`aria-checked`, String(checked));
3603
+ cb.toggleAttribute(`data-checked`, checked);
3604
+ if (this.checkboxRenderer) {
3605
+ const renderer = typeof this.checkboxRenderer === `function` ? this.checkboxRenderer : this.checkboxRenderer.renderer;
3606
+ render(renderer(checked), cb);
3592
3607
  }
3593
- cb.checked = this._selectedItems.has(el);
3594
- } else if (cb) cb.remove();
3608
+ } else {
3609
+ const input = cb.querySelector(`input.ixfx-list-checkbox`);
3610
+ if (input) input.checked = checked;
3611
+ }
3595
3612
  }
3596
3613
  }
3614
+ /**
3615
+ * Native checkbox cell: a span that stretches to the row's full cross-axis
3616
+ * (so the entire column height is a click target) with a real `<input>`
3617
+ * centred inside. The span owns the click; the input is purely visual and
3618
+ * has `pointer-events:none` so a single click produces one toggle.
3619
+ */
3620
+ _createNativeCheckboxCell(el) {
3621
+ const cell = document.createElement(`span`);
3622
+ cell.className = `ixfx-list-checkbox ixfx-list-checkbox-native`;
3623
+ 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`;
3624
+ cell.tabIndex = -1;
3625
+ cell.setAttribute(`role`, `checkbox`);
3626
+ cell.setAttribute(`part`, `checkbox`);
3627
+ cell.addEventListener(`click`, (e) => {
3628
+ e.stopPropagation();
3629
+ this._toggleItem(el);
3630
+ });
3631
+ const input = document.createElement(`input`);
3632
+ input.type = `checkbox`;
3633
+ input.className = `ixfx-list-checkbox`;
3634
+ input.style.cssText = `pointer-events:none;cursor:pointer;margin:0`;
3635
+ cell.appendChild(input);
3636
+ return cell;
3637
+ }
3638
+ /**
3639
+ * Custom checkbox cell: same shape as the native cell (a stretching span)
3640
+ * so behaviour is identical. The renderer's content goes inside.
3641
+ */
3642
+ _createCustomCheckbox(el, margin, width) {
3643
+ const span = document.createElement(`span`);
3644
+ span.className = `ixfx-list-checkbox ixfx-list-checkbox-custom`;
3645
+ 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`;
3646
+ span.tabIndex = -1;
3647
+ span.setAttribute(`role`, `checkbox`);
3648
+ span.setAttribute(`part`, `checkbox`);
3649
+ span.addEventListener(`click`, (e) => {
3650
+ e.stopPropagation();
3651
+ this._toggleItem(el);
3652
+ });
3653
+ return span;
3654
+ }
3597
3655
  _findListItem(path) {
3598
3656
  for (const target of path) {
3599
3657
  if (!(target instanceof Element)) continue;
@@ -3655,8 +3713,14 @@ let VerticalListElement = class VerticalListElement extends LitElement {
3655
3713
  padding: 0;
3656
3714
  width: 100%;
3657
3715
  height: 100%;
3716
+ overflow-x: hidden;
3658
3717
  overflow-y: auto;
3659
3718
  box-sizing: border-box;
3719
+ /* Scrollbar theming: end users override these CSS variables to control
3720
+ the native scrollbar (scrollbar-width, scrollbar-gutter, scrollbar-color). */
3721
+ scrollbar-width: var(--list-scrollbar-width, auto);
3722
+ scrollbar-gutter: var(--list-scrollbar-gutter, auto);
3723
+ scrollbar-color: var(--list-scrollbar-color, auto);
3660
3724
  }
3661
3725
 
3662
3726
  ::slotted([hidden]) {
@@ -3686,6 +3750,25 @@ let VerticalListElement = class VerticalListElement extends LitElement {
3686
3750
  outline: 1px solid transparent;
3687
3751
  }
3688
3752
 
3753
+ /* In checked mode, only the checkbox column is clickable; the row body
3754
+ gets a default cursor so it doesn't suggest otherwise. */
3755
+ :host([interaction-mode="checked"]) ::slotted(li),
3756
+ :host([interaction-mode="checked"]) ::slotted([data-list-item]) {
3757
+ cursor: default;
3758
+ }
3759
+
3760
+ /* Wrapper inserted around custom [data-list-item] elements in checked mode.
3761
+ Provides the flex layout so the injected checkbox can stretch to the
3762
+ row's full height via align-self:stretch. */
3763
+ .ixfx-custom-row {
3764
+ display: flex;
3765
+ align-items: center;
3766
+ min-height: var(--list-item-height, 28px);
3767
+ box-sizing: border-box;
3768
+ cursor: default;
3769
+ user-select: none;
3770
+ }
3771
+
3689
3772
  ::slotted([data-tickled]) {
3690
3773
  background: var(--item-bg-tickled);
3691
3774
  outline: var(--item-border-tickled);
@@ -3793,6 +3876,7 @@ __decorate([property({
3793
3876
  attribute: `interaction-mode`
3794
3877
  })], VerticalListElement.prototype, "interactionMode", void 0);
3795
3878
  __decorate([property({ attribute: false })], VerticalListElement.prototype, "filterPredicate", void 0);
3879
+ __decorate([property({ attribute: false })], VerticalListElement.prototype, "checkboxRenderer", void 0);
3796
3880
  __decorate([state()], VerticalListElement.prototype, "_selectedItems", void 0);
3797
3881
  __decorate([state()], VerticalListElement.prototype, "_searchVisible", void 0);
3798
3882
  __decorate([state()], VerticalListElement.prototype, "_searchQuery", void 0);
@@ -3928,6 +4012,441 @@ function createVerticalListAdapter(renderItem, options) {
3928
4012
  return adapter;
3929
4013
  }
3930
4014
  //#endregion
4015
+ //#region src/notifier/notification-pill.ts
4016
+ let NotificationPillElement = class NotificationPillElement extends LitElement {
4017
+ constructor(..._args) {
4018
+ super(..._args);
4019
+ this.notificationType = `info`;
4020
+ this.message = ``;
4021
+ this.dismissing = false;
4022
+ this.notificationId = ``;
4023
+ }
4024
+ #handleClick() {
4025
+ this.#emitTap();
4026
+ }
4027
+ #handleKeyDown(e) {
4028
+ if (e.key === `Enter` || e.key === ` `) {
4029
+ e.preventDefault();
4030
+ this.#emitTap();
4031
+ }
4032
+ }
4033
+ #emitTap() {
4034
+ this.dispatchEvent(new CustomEvent(`pill-tap`, {
4035
+ detail: { id: this.notificationId },
4036
+ bubbles: true,
4037
+ composed: true
4038
+ }));
4039
+ }
4040
+ #renderIcon() {
4041
+ if (!this.icon) return nothing;
4042
+ if (this.icon.startsWith(`<svg`)) return html`<span class="pill-icon">${unsafeHTML(this.icon)}</span>`;
4043
+ if (!getIcon(this.icon)) return nothing;
4044
+ return html`<ixfx-icon class="pill-icon" name="${this.icon}"></ixfx-icon>`;
4045
+ }
4046
+ render() {
4047
+ return html`
4048
+ <button
4049
+ type="button"
4050
+ class="pill"
4051
+ notification-type="${this.notificationType}"
4052
+ @click=${this.#handleClick}
4053
+ @keydown=${this.#handleKeyDown}
4054
+ aria-label="${this.message}"
4055
+ >
4056
+ ${this.#renderIcon()}
4057
+ <span class="pill-message">${this.message}</span>
4058
+ </button>
4059
+ `;
4060
+ }
4061
+ static {
4062
+ this.styles = [themeFallbacks, css`
4063
+ :host {
4064
+ display: block;
4065
+ }
4066
+
4067
+ .pill {
4068
+ appearance: none;
4069
+ cursor: pointer;
4070
+ font-family: var(--font-family, inherit);
4071
+ font-size: var(--text-m, 12px);
4072
+ padding: var(--space-s, 4px) var(--space-m, 8px);
4073
+ border-radius: var(--radius-l, 6px);
4074
+ border: 1px solid var(--surface-6, #666);
4075
+ display: inline-flex;
4076
+ align-items: center;
4077
+ gap: var(--space-s, 4px);
4078
+ max-width: 300px;
4079
+ box-shadow: var(--shadow-m);
4080
+ transition: opacity 0.2s ease-out, transform 0.2s ease-out;
4081
+ pointer-events: auto;
4082
+ }
4083
+
4084
+ :host([dismissing]) .pill {
4085
+ opacity: 0;
4086
+ transform: translateX(20px);
4087
+ }
4088
+
4089
+ .pill[notification-type="info"] {
4090
+ background: var(--surface-5, #555);
4091
+ color: var(--surface-5-text, #fff);
4092
+ }
4093
+
4094
+ .pill[notification-type="warning"] {
4095
+ background: color-mix(in oklch, oklch(65% 0.18 70) 30%, var(--surface-5));
4096
+ border-color: oklch(65% 0.18 70);
4097
+ color: var(--surface-5-text, #fff);
4098
+ }
4099
+
4100
+ .pill[notification-type="error"] {
4101
+ background: var(--error, #c00);
4102
+ border-color: var(--error, #c00);
4103
+ color: var(--error-text, #fff);
4104
+ }
4105
+
4106
+ .pill-icon {
4107
+ display: inline-flex;
4108
+ align-items: center;
4109
+ flex-shrink: 0;
4110
+ }
4111
+
4112
+ .pill-icon ixfx-icon {
4113
+ font-size: var(--text-m, 12px);
4114
+ }
4115
+
4116
+ .pill-message {
4117
+ white-space: nowrap;
4118
+ overflow: hidden;
4119
+ text-overflow: ellipsis;
4120
+ }
4121
+
4122
+ .pill:focus-visible {
4123
+ outline: 2px solid var(--accent, #4a9);
4124
+ outline-offset: 2px;
4125
+ }
4126
+
4127
+ @media (prefers-reduced-motion: reduce) {
4128
+ .pill {
4129
+ transition: opacity 0.1s ease-out;
4130
+ transform: none !important;
4131
+ }
4132
+ }
4133
+ `];
4134
+ }
4135
+ };
4136
+ __decorate([property({
4137
+ type: String,
4138
+ attribute: `notification-type`
4139
+ })], NotificationPillElement.prototype, "notificationType", void 0);
4140
+ __decorate([property({ type: String })], NotificationPillElement.prototype, "message", void 0);
4141
+ __decorate([property({ type: String })], NotificationPillElement.prototype, "icon", void 0);
4142
+ __decorate([property({
4143
+ type: Boolean,
4144
+ reflect: true
4145
+ })], NotificationPillElement.prototype, "dismissing", void 0);
4146
+ __decorate([property({
4147
+ type: String,
4148
+ attribute: `notification-id`
4149
+ })], NotificationPillElement.prototype, "notificationId", void 0);
4150
+ NotificationPillElement = __decorate([safeCustomElement(`ixfx-notification-pill`)], NotificationPillElement);
4151
+ //#endregion
4152
+ //#region src/notifier/pill-renderer.ts
4153
+ const ROW_SPACING = 8;
4154
+ const DEFAULT_ROW_HEIGHT = 40;
4155
+ const STACK_OFFSET_STEP = 4;
4156
+ const STACK_OPACITY_BASE = .7;
4157
+ const STACK_OPACITY_DECAY = .15;
4158
+ const STACK_OPACITY_MIN = .2;
4159
+ function edgesOf(position) {
4160
+ return {
4161
+ vertical: position.startsWith(`top`) ? `top` : `bottom`,
4162
+ horizontal: position.endsWith(`-left`) ? `left` : position.endsWith(`-right`) ? `right` : `middle`
4163
+ };
4164
+ }
4165
+ function anchorStyles(position, safeArea) {
4166
+ const { vertical, horizontal } = edgesOf(position);
4167
+ const vMargin = `calc(var(--space-l, 12px) + ${(vertical === `top` ? safeArea.top : safeArea.bottom) ?? 0}px)`;
4168
+ let css = `position: fixed; `;
4169
+ css += vertical === `top` ? `top: ${vMargin}; bottom: auto; ` : `bottom: ${vMargin}; top: auto; `;
4170
+ if (horizontal === `left`) {
4171
+ const hMargin = `calc(var(--space-l, 12px) + ${safeArea.left ?? 0}px)`;
4172
+ css += `left: ${hMargin}; right: auto;`;
4173
+ } else if (horizontal === `right`) {
4174
+ const hMargin = `calc(var(--space-l, 12px) + ${safeArea.right ?? 0}px)`;
4175
+ css += `right: ${hMargin}; left: auto;`;
4176
+ } else css += `left: 50%; right: auto;`;
4177
+ return css;
4178
+ }
4179
+ /** Entry-animation starting transform: pills slide in from the edge they're anchored to. */
4180
+ function entryTransform(edges) {
4181
+ const dy = edges.vertical === `top` ? -20 : 20;
4182
+ if (edges.horizontal === `middle`) return `translateX(-50%) translate(0, ${dy}px)`;
4183
+ return `translate(${edges.horizontal === `left` ? -20 : 20}px, ${dy}px)`;
4184
+ }
4185
+ function restingTransform(edges, dx, dy) {
4186
+ if (edges.horizontal === `middle`) return `translateX(-50%) translate(${dx}px, ${dy}px)`;
4187
+ return `translate(${dx}px, ${dy}px)`;
4188
+ }
4189
+ function createPillRenderer(options = {}) {
4190
+ const position = options.position ?? `top-right`;
4191
+ const queueDisplay = options.queueDisplay ?? `list`;
4192
+ const safeArea = options.safeArea ?? {};
4193
+ const edges = edgesOf(position);
4194
+ let container = null;
4195
+ let hooks = null;
4196
+ const pills = /* @__PURE__ */ new Map();
4197
+ let rowHeight = DEFAULT_ROW_HEIGHT;
4198
+ let measured = false;
4199
+ function ensureContainer() {
4200
+ if (container) return container;
4201
+ container = document.createElement(`div`);
4202
+ container.className = `ixfx-notification-container`;
4203
+ container.setAttribute(`role`, `region`);
4204
+ container.setAttribute(`aria-label`, `Notifications`);
4205
+ document.body.appendChild(container);
4206
+ return container;
4207
+ }
4208
+ function updateAttributes(el, notification) {
4209
+ el.setAttribute(`notification-type`, notification.type);
4210
+ el.setAttribute(`message`, notification.message);
4211
+ if (notification.icon) el.setAttribute(`icon`, notification.icon);
4212
+ else el.removeAttribute(`icon`);
4213
+ }
4214
+ function createPillElement(notification) {
4215
+ const el = document.createElement(`ixfx-notification-pill`);
4216
+ el.setAttribute(`notification-id`, notification.id);
4217
+ el.style.cssText = anchorStyles(position, safeArea);
4218
+ updateAttributes(el, notification);
4219
+ el.style.opacity = `0`;
4220
+ el.style.transform = entryTransform(edges);
4221
+ el.addEventListener(`pill-tap`, (e) => {
4222
+ const customEvent = e;
4223
+ hooks?.onTap(customEvent.detail.id);
4224
+ });
4225
+ return el;
4226
+ }
4227
+ function measureRowHeight(el) {
4228
+ if (measured) return;
4229
+ requestAnimationFrame(() => {
4230
+ const height = el.getBoundingClientRect().height;
4231
+ if (height > 0) {
4232
+ rowHeight = height;
4233
+ measured = true;
4234
+ }
4235
+ });
4236
+ }
4237
+ /** Positions every currently-visible pill for its rank in `order` (index 0 = front/oldest). */
4238
+ function applyLayout(order) {
4239
+ const total = order.length;
4240
+ order.forEach((notification, index) => {
4241
+ const entry = pills.get(notification.id);
4242
+ if (!entry) return;
4243
+ const el = entry.element;
4244
+ let dx = 0;
4245
+ let dy = 0;
4246
+ let opacity = 1;
4247
+ let pointerEvents = `auto`;
4248
+ let filter = `none`;
4249
+ if (queueDisplay === `list`) {
4250
+ const step = index * (rowHeight + ROW_SPACING);
4251
+ dy = edges.vertical === `top` ? step : -step;
4252
+ } else {
4253
+ const step = index * STACK_OFFSET_STEP;
4254
+ dy = edges.vertical === `top` ? step : -step;
4255
+ dx = edges.horizontal === `left` ? step : edges.horizontal === `right` ? -step : 0;
4256
+ if (index > 0) {
4257
+ const hintIndex = index - 1;
4258
+ opacity = Math.max(STACK_OPACITY_MIN, STACK_OPACITY_BASE - hintIndex * STACK_OPACITY_DECAY);
4259
+ pointerEvents = `none`;
4260
+ filter = `saturate(0.6)`;
4261
+ }
4262
+ }
4263
+ el.style.transition = `opacity 0.2s ease-out, transform 0.2s ease-out`;
4264
+ el.style.opacity = `${opacity}`;
4265
+ el.style.zIndex = `${total - index}`;
4266
+ el.style.pointerEvents = pointerEvents;
4267
+ el.style.filter = filter;
4268
+ el.style.transform = restingTransform(edges, dx, dy);
4269
+ });
4270
+ }
4271
+ function dismiss(id) {
4272
+ const entry = pills.get(id);
4273
+ if (!entry) return;
4274
+ const el = entry.element;
4275
+ el.setAttribute(`dismissing`, `true`);
4276
+ const cleanup = () => {
4277
+ el.removeEventListener(`transitionend`, cleanup);
4278
+ el.remove();
4279
+ pills.delete(id);
4280
+ if (container && pills.size === 0) {
4281
+ container.remove();
4282
+ container = null;
4283
+ measured = false;
4284
+ rowHeight = DEFAULT_ROW_HEIGHT;
4285
+ }
4286
+ };
4287
+ el.addEventListener(`transitionend`, cleanup);
4288
+ setTimeout(cleanup, 300);
4289
+ }
4290
+ function setNotifications(notifications, _queueSize) {
4291
+ const cont = ensureContainer();
4292
+ const idsToKeep = new Set(notifications.map((n) => n.id));
4293
+ for (const notification of notifications) {
4294
+ const existing = pills.get(notification.id);
4295
+ if (existing) {
4296
+ existing.notification = notification;
4297
+ updateAttributes(existing.element, notification);
4298
+ continue;
4299
+ }
4300
+ const el = createPillElement(notification);
4301
+ pills.set(notification.id, {
4302
+ element: el,
4303
+ notification
4304
+ });
4305
+ cont.appendChild(el);
4306
+ measureRowHeight(el);
4307
+ }
4308
+ for (const [id] of pills) if (!idsToKeep.has(id)) dismiss(id);
4309
+ requestAnimationFrame(() => applyLayout(notifications));
4310
+ }
4311
+ return {
4312
+ attach(newHooks) {
4313
+ hooks = newHooks;
4314
+ },
4315
+ detach() {
4316
+ for (const [id] of pills) dismiss(id);
4317
+ if (container) {
4318
+ container.remove();
4319
+ container = null;
4320
+ }
4321
+ },
4322
+ setNotifications
4323
+ };
4324
+ }
4325
+ //#endregion
4326
+ //#region src/notifier/manager.ts
4327
+ const options = {
4328
+ renderer: void 0,
4329
+ timeout: 4e3,
4330
+ timeoutError: 8e3,
4331
+ queue: true,
4332
+ queueSize: 3,
4333
+ tapBehaviour: `dismiss`,
4334
+ tapHandlerDefault: void 0
4335
+ };
4336
+ /** Rank-ordered, index 0 = oldest/first (next to be evicted or timed out). Length never exceeds queueSize (or 1 when queuing is disabled). */
4337
+ const visible = [];
4338
+ let renderer = null;
4339
+ let defaultRenderer = null;
4340
+ function getDefaultRenderer() {
4341
+ defaultRenderer ??= createPillRenderer({ position: `top-right` });
4342
+ return defaultRenderer;
4343
+ }
4344
+ function getRenderer() {
4345
+ return renderer ?? getDefaultRenderer();
4346
+ }
4347
+ function syncRenderer() {
4348
+ getRenderer().setNotifications([...visible], options.queueSize);
4349
+ }
4350
+ function clearTimer(managed) {
4351
+ if (managed.timer !== null) {
4352
+ clearTimeout(managed.timer);
4353
+ managed.timer = null;
4354
+ }
4355
+ }
4356
+ /**
4357
+ * A message shown alone takes the full timeout. A message that joined a
4358
+ * non-empty queue at arrival time is permanently "wasQueued" and gets
4359
+ * 1/queueSize of the timeout once it reaches the front. The timer for a
4360
+ * given message is only ever started once - when it first becomes front.
4361
+ */
4362
+ function startTimer(managed) {
4363
+ const base = managed.type === `error` ? options.timeoutError : options.timeout;
4364
+ const duration = managed.wasQueued ? base / options.queueSize : base;
4365
+ managed.timer = window.setTimeout(() => dismiss(managed.id), duration);
4366
+ }
4367
+ function ensureFrontTimer() {
4368
+ const front = visible[0];
4369
+ if (front && front.timer === null) startTimer(front);
4370
+ }
4371
+ const NotificationManager = {
4372
+ configure(opts) {
4373
+ if (opts.renderer !== void 0) {
4374
+ renderer?.detach?.();
4375
+ renderer = opts.renderer;
4376
+ renderer.attach({ onTap(id) {
4377
+ const notification = visible.find((n) => n.id === id);
4378
+ if (!notification) return;
4379
+ if (notification.onTap) notification.onTap(id);
4380
+ else options.tapHandlerDefault?.(notification);
4381
+ if (options.tapBehaviour === `dismiss`) dismiss(id);
4382
+ } });
4383
+ syncRenderer();
4384
+ }
4385
+ if (opts.timeout !== void 0) options.timeout = opts.timeout;
4386
+ if (opts.timeoutError !== void 0) options.timeoutError = opts.timeoutError;
4387
+ if (opts.queue !== void 0) options.queue = opts.queue;
4388
+ if (opts.queueSize !== void 0) {
4389
+ options.queueSize = Math.max(1, opts.queueSize);
4390
+ while (visible.length > options.queueSize) clearTimer(visible.shift());
4391
+ ensureFrontTimer();
4392
+ syncRenderer();
4393
+ }
4394
+ if (opts.tapBehaviour !== void 0) options.tapBehaviour = opts.tapBehaviour;
4395
+ if (opts.tapHandlerDefault !== void 0) options.tapHandlerDefault = opts.tapHandlerDefault;
4396
+ },
4397
+ notify(content) {
4398
+ const id = crypto.randomUUID();
4399
+ const now = Date.now();
4400
+ const icon = content.icon ?? (content.type === `error` ? `error` : void 0);
4401
+ const managed = {
4402
+ ...content,
4403
+ icon,
4404
+ id,
4405
+ createdAt: now,
4406
+ updatedAt: now,
4407
+ timer: null,
4408
+ wasQueued: false
4409
+ };
4410
+ if (options.queue) {
4411
+ if (visible.length >= options.queueSize) clearTimer(visible.shift());
4412
+ managed.wasQueued = visible.length > 0;
4413
+ visible.push(managed);
4414
+ } else {
4415
+ while (visible.length > 0) clearTimer(visible.pop());
4416
+ visible.push(managed);
4417
+ }
4418
+ ensureFrontTimer();
4419
+ syncRenderer();
4420
+ return id;
4421
+ },
4422
+ update(id, content) {
4423
+ const managed = visible.find((n) => n.id === id);
4424
+ if (!managed) return;
4425
+ Object.assign(managed, content);
4426
+ managed.updatedAt = Date.now();
4427
+ if (visible[0] === managed) {
4428
+ clearTimer(managed);
4429
+ startTimer(managed);
4430
+ }
4431
+ syncRenderer();
4432
+ },
4433
+ dismiss(id) {
4434
+ dismiss(id);
4435
+ },
4436
+ dismissAll() {
4437
+ for (const m of visible) clearTimer(m);
4438
+ visible.length = 0;
4439
+ syncRenderer();
4440
+ }
4441
+ };
4442
+ function dismiss(id) {
4443
+ const idx = visible.findIndex((m) => m.id === id);
4444
+ if (idx === -1) return;
4445
+ clearTimer(visible.splice(idx, 1)[0]);
4446
+ ensureFrontTimer();
4447
+ syncRenderer();
4448
+ }
4449
+ //#endregion
3931
4450
  //#region src/panel/panel-group.ts
3932
4451
  let PanelGroupElement = class PanelGroupElement extends LitElement {
3933
4452
  constructor(..._args) {
@@ -4616,7 +5135,7 @@ let TabListItemElement = class TabListItemElement extends LitElement {
4616
5135
  prominenceStyles,
4617
5136
  css`
4618
5137
  :host {
4619
- --selected-colour: var(--accent);
5138
+ --selected-colour: var(--surface-2-text);
4620
5139
  --selected-border-width: 1px;
4621
5140
  --padding: var(--space-m);
4622
5141
  display: inline-flex;
@@ -4642,6 +5161,8 @@ let TabListItemElement = class TabListItemElement extends LitElement {
4642
5161
  border-bottom-color: var(--indicator-color-bottom, transparent);
4643
5162
  border-left-color: var(--indicator-color-left, transparent);
4644
5163
  border-right-color: var(--indicator-color-right, transparent);
5164
+ background: var(--surface-5);
5165
+ font-weight: bold;
4645
5166
  }
4646
5167
 
4647
5168
  .label {
@@ -5104,6 +5625,6 @@ function init() {
5104
5625
  console.log(`Init`);
5105
5626
  }
5106
5627
  //#endregion
5107
- 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 };
5628
+ 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 };
5108
5629
 
5109
5630
  //# sourceMappingURL=index.js.map