@ixfx/components 0.7.4 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/bundle/index.d.ts +787 -190
  2. package/bundle/index.d.ts.map +1 -1
  3. package/bundle/index.js +2770 -592
  4. package/bundle/index.js.map +1 -1
  5. package/dist/ac-token.js +1 -1
  6. package/dist/crumbs.d.ts +5 -1
  7. package/dist/crumbs.d.ts.map +1 -1
  8. package/dist/crumbs.js +10 -0
  9. package/dist/crumbs.js.map +1 -1
  10. package/dist/{data-grid-BddUjAti.d.ts → data-grid-D-z7bx5-.d.ts} +26 -1
  11. package/dist/data-grid-D-z7bx5-.d.ts.map +1 -0
  12. package/dist/data-grid.d.ts +1 -1
  13. package/dist/data-grid.js +88 -7
  14. package/dist/data-grid.js.map +1 -1
  15. package/dist/{detail-list-Br0WAnry.d.ts → detail-list-_ZJHYEBr.d.ts} +20 -12
  16. package/dist/detail-list-_ZJHYEBr.d.ts.map +1 -0
  17. package/dist/detail-list.d.ts +2 -2
  18. package/dist/detail-list.js +51 -135
  19. package/dist/detail-list.js.map +1 -1
  20. package/dist/grid-list-nRUQOcEL.js +1537 -0
  21. package/dist/grid-list-nRUQOcEL.js.map +1 -0
  22. package/dist/{grid-list-B_orPsot.d.ts → grid-list-u33vmKzb.d.ts} +128 -10
  23. package/dist/grid-list-u33vmKzb.d.ts.map +1 -0
  24. package/dist/grid-list.d.ts +1 -1
  25. package/dist/grid-list.js +1 -932
  26. package/dist/incr-search-C05WjbZh.js +999 -0
  27. package/dist/incr-search-C05WjbZh.js.map +1 -0
  28. package/dist/incr-search.d.ts +2 -2
  29. package/dist/incr-search.js +2 -3
  30. package/dist/index-h6gxtfHH.d.ts +397 -0
  31. package/dist/index-h6gxtfHH.d.ts.map +1 -0
  32. package/dist/index.d.ts +62 -13
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +343 -17
  35. package/dist/index.js.map +1 -1
  36. package/dist/led.d.ts +33 -1
  37. package/dist/led.d.ts.map +1 -1
  38. package/dist/led.js +157 -13
  39. package/dist/led.js.map +1 -1
  40. package/dist/{list-selection-controller-CUo-Hhcm.js → list-selection-controller-DTFI40li.js} +2 -76
  41. package/dist/list-selection-controller-DTFI40li.js.map +1 -0
  42. package/dist/miller.d.ts +81 -3
  43. package/dist/miller.d.ts.map +1 -1
  44. package/dist/miller.js +327 -68
  45. package/dist/miller.js.map +1 -1
  46. package/dist/narrowed-text.js +1 -2
  47. package/dist/narrowed-text.js.map +1 -1
  48. package/dist/panel.d.ts +3 -1
  49. package/dist/panel.d.ts.map +1 -1
  50. package/dist/panel.js +31 -1
  51. package/dist/panel.js.map +1 -1
  52. package/dist/plots.d.ts +2 -2
  53. package/dist/plots.js +1 -1
  54. package/dist/{titlebar-Bj5pfdw_.d.ts → titlebar-YQ-jVQoS.d.ts} +2 -2
  55. package/dist/{titlebar-Bj5pfdw_.d.ts.map → titlebar-YQ-jVQoS.d.ts.map} +1 -1
  56. package/dist/titlebar.d.ts +1 -1
  57. package/dist/titlebar.js +83 -1
  58. package/dist/titlebar.js.map +1 -1
  59. package/dist/{tree-B74zdI60.js → tree-Chptss7I.js} +77 -15
  60. package/dist/tree-Chptss7I.js.map +1 -0
  61. package/dist/{tree-component-BvV4muxx.d.ts → tree-component-F6qZ0Gdv.d.ts} +9 -1
  62. package/dist/{tree-component-BvV4muxx.d.ts.map → tree-component-F6qZ0Gdv.d.ts.map} +1 -1
  63. package/dist/tree.d.ts +22 -1
  64. package/dist/tree.d.ts.map +1 -1
  65. package/dist/tree.js +1 -1
  66. package/dist/{vertical-list-CQSEVeOT.d.ts → vertical-list-CFuFDygi.d.ts} +17 -14
  67. package/dist/vertical-list-CFuFDygi.d.ts.map +1 -0
  68. package/dist/vertical-list.d.ts +1 -1
  69. package/dist/vertical-list.js +59 -127
  70. package/dist/vertical-list.js.map +1 -1
  71. package/dist/{xy-axis-5KhVTNoX.d.ts → xy-axis-BfP9hKLj.d.ts} +25 -2
  72. package/dist/{xy-axis-5KhVTNoX.d.ts.map → xy-axis-BfP9hKLj.d.ts.map} +1 -1
  73. package/dist/{xy-axis-VrX8dmvS.js → xy-axis-DzM1brBZ.js} +141 -8
  74. package/dist/xy-axis-DzM1brBZ.js.map +1 -0
  75. package/docs-user/data-grid.md +19 -0
  76. package/docs-user/detail-list.md +1 -1
  77. package/docs-user/grid-list.md +120 -11
  78. package/docs-user/incr-search.md +12 -2
  79. package/docs-user/index.json +3 -3
  80. package/docs-user/led.md +37 -7
  81. package/docs-user/llms.txt +1 -1
  82. package/docs-user/panel.md +68 -13
  83. package/docs-user/titlebar.md +3 -4
  84. package/docs-user/tree.md +22 -1
  85. package/docs-user/vertical-list.md +7 -1
  86. package/llms.txt +1 -1
  87. package/package.json +1 -1
  88. package/dist/data-grid-BddUjAti.d.ts.map +0 -1
  89. package/dist/detail-list-Br0WAnry.d.ts.map +0 -1
  90. package/dist/element-search-Y8SRfPGM.js +0 -185
  91. package/dist/element-search-Y8SRfPGM.js.map +0 -1
  92. package/dist/grid-list-B_orPsot.d.ts.map +0 -1
  93. package/dist/grid-list.js.map +0 -1
  94. package/dist/incr-search-vCzuao-2.js +0 -137
  95. package/dist/incr-search-vCzuao-2.js.map +0 -1
  96. package/dist/index-Cu5i8zZY.d.ts +0 -160
  97. package/dist/index-Cu5i8zZY.d.ts.map +0 -1
  98. package/dist/list-selection-controller-CUo-Hhcm.js.map +0 -1
  99. package/dist/tree-B74zdI60.js.map +0 -1
  100. package/dist/vertical-list-CQSEVeOT.d.ts.map +0 -1
  101. package/dist/xy-axis-VrX8dmvS.js.map +0 -1
package/dist/led.d.ts CHANGED
@@ -1,14 +1,46 @@
1
1
  import { LitElement } from "lit";
2
2
  //#region src/led/led.d.ts
3
+ type LedMode = `brightness` | `blink` | `pulse`;
3
4
  declare class LedElement extends LitElement {
4
5
  #private;
5
6
  value: number;
6
7
  lit: boolean;
7
8
  color: string;
9
+ glow: boolean;
10
+ /**
11
+ * Operating mode:
12
+ * - `brightness` (default): {@link value} sets the LED brightness directly
13
+ * - `blink`: LED blinks on/off every {@link rate} milliseconds (this mode
14
+ * is what the legacy `blinking` attribute now selects)
15
+ * - `pulse`: LED sits unlit until {@link pulse} is called, which fires an
16
+ * attack/release envelope up to {@link value} and back down
17
+ */
18
+ mode: LedMode;
19
+ /**
20
+ * Legacy alias for `mode="blink"`. Setting `blinking` switches {@link mode}
21
+ * to `blink`; clearing it returns to `brightness`.
22
+ */
8
23
  blinking: boolean;
9
24
  rate: number;
25
+ /** Pulse-mode attack time in milliseconds (0 → instant). */
26
+ attackTime: number;
27
+ /** Pulse-mode release/decay time in milliseconds (0 → instant). */
28
+ releaseTime: number;
29
+ /**
30
+ * Pulse-mode retrigger behaviour. When set, each {@link pulse} call restarts
31
+ * the envelope from 0. When unset (default), the attack ramps from wherever
32
+ * the envelope currently sits, so a retrigger mid-decay is continuous.
33
+ */
34
+ pulseRetrigger: boolean;
10
35
  willUpdate(changedProperties: Map<string, unknown>): void;
11
36
  disconnectedCallback(): void;
37
+ /**
38
+ * Pulse-mode trigger: lights the LED to {@link value} using an attack/release
39
+ * envelope, then decays back to unlit. Calling again restarts the envelope
40
+ * (from 0 if {@link pulseRetrigger} is set, otherwise from the current level).
41
+ * No-op unless {@link mode} is `pulse`.
42
+ */
43
+ pulse(): void;
12
44
  protected render(): import("lit").TemplateResult<1>;
13
45
  static styles: import("lit").CSSResult;
14
46
  }
@@ -18,5 +50,5 @@ declare global {
18
50
  }
19
51
  }
20
52
  //#endregion
21
- export { LedElement };
53
+ export { LedElement, LedMode };
22
54
  //# sourceMappingURL=led.d.ts.map
package/dist/led.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"led.d.ts","names":[],"sources":["../src/led/led.ts"],"mappings":";;cAKa,mBAAmB;;EAE9B;EAGA;EAGA;EAGA;EAGA;EAIA,WAAW,mBAAmB;EAmB9B;YAwBU,wBAAM;SAIT,sBAAM;;QAqBP;YACI;IACR,YAAY"}
1
+ {"version":3,"file":"led.d.ts","names":[],"sources":["../src/led/led.ts"],"mappings":";;KAIY;cAGC,mBAAmB;;EAE9B;EAGA;EAGA;EAGA;;;;;;;;;EAWA,MAAM;;;;;EAON;EAGA;;EAIA;;EAIA;;;;;;EAQA;EAWA,WAAW,mBAAmB;EA+C9B;;;;;;;EAYA;YA6EU,wBAAM;SAIT,sBAAM;;QAwEP;YACI;IACR,YAAY"}
package/dist/led.js CHANGED
@@ -8,15 +8,62 @@ let LedElement = class LedElement extends LitElement {
8
8
  this.value = 0;
9
9
  this.lit = false;
10
10
  this.color = `oklch(50% 0.15 25)`;
11
+ this.glow = false;
12
+ this.mode = `brightness`;
11
13
  this.blinking = false;
12
14
  this.rate = 500;
15
+ this.attackTime = 40;
16
+ this.releaseTime = 400;
17
+ this.pulseRetrigger = false;
18
+ this.#appliedMode = `brightness`;
19
+ this.#pulseStart = 0;
20
+ this.#pulsePeak = 0;
21
+ this.#pulseFrom = 0;
22
+ this.#pulseLevel = 0;
23
+ this.#pulseTick = (now) => {
24
+ const attack = Math.max(0, this.attackTime);
25
+ const release = Math.max(0, this.releaseTime);
26
+ const from = this.#pulseFrom;
27
+ const peak = this.#pulsePeak;
28
+ const elapsed = now - this.#pulseStart;
29
+ let level;
30
+ if (attack > 0 && elapsed < attack) level = from + (peak - from) * (elapsed / attack);
31
+ else if (elapsed < attack + release) level = release > 0 ? peak * (1 - (elapsed - attack) / release) : 0;
32
+ else level = 0;
33
+ level = Math.max(0, Math.min(Math.max(from, peak), level));
34
+ this.#pulseLevel = level;
35
+ this.style.setProperty(`--value`, String(level));
36
+ if (elapsed < attack + release) this.#pulseRaf = requestAnimationFrame(this.#pulseTick);
37
+ else {
38
+ this.#pulseRaf = void 0;
39
+ this.#pulseLevel = 0;
40
+ }
41
+ };
13
42
  }
14
43
  #blinkInterval;
44
+ #appliedMode;
45
+ #pulseRaf;
46
+ #pulseStart;
47
+ #pulsePeak;
48
+ #pulseFrom;
49
+ #pulseLevel;
15
50
  willUpdate(changedProperties) {
16
- if (changedProperties.has(`lit`)) this.value = this.lit ? 1 : 0;
17
- if (changedProperties.has(`value`) || changedProperties.has(`lit`)) this.style.setProperty(`--value`, String(this.value));
18
- if (changedProperties.has(`blinking`) || changedProperties.has(`rate`)) this.#updateBlinking();
51
+ if (changedProperties.has(`blinking`)) {
52
+ if (this.blinking) this.mode = `blink`;
53
+ else if (this.mode === `blink`) this.mode = `brightness`;
54
+ }
55
+ if (this.mode !== this.#appliedMode) {
56
+ this.#appliedMode = this.mode;
57
+ this.#onModeChange();
58
+ }
59
+ if (changedProperties.has(`lit`)) {
60
+ if (this.lit) this.value = 1;
61
+ else if (changedProperties.get(`lit`) !== void 0) this.value = 0;
62
+ }
63
+ if (changedProperties.has(`color`)) this.style.setProperty(`--_color-attr`, this.color);
64
+ if (this.mode !== `pulse` && (changedProperties.has(`value`) || changedProperties.has(`lit`))) this.style.setProperty(`--value`, String(this.value));
19
65
  if (changedProperties.has(`rate`)) {
66
+ this.#updateBlinking();
20
67
  const transitionMs = this.rate < 300 ? this.rate * .5 : 150;
21
68
  this.style.setProperty(`--transition-duration`, `${transitionMs}ms`);
22
69
  }
@@ -24,18 +71,43 @@ let LedElement = class LedElement extends LitElement {
24
71
  disconnectedCallback() {
25
72
  super.disconnectedCallback();
26
73
  this.#stopBlinking();
74
+ this.#stopPulse();
27
75
  }
28
- #updateBlinking() {
29
- if (this.blinking && this.rate > 0) {
30
- this.#stopBlinking();
31
- this.#blinkInterval = window.setInterval(() => {
32
- this.value = this.value === 0 ? 1 : 0;
33
- }, this.rate);
34
- } else {
35
- this.#stopBlinking();
36
- this.value = 0;
76
+ /**
77
+ * Pulse-mode trigger: lights the LED to {@link value} using an attack/release
78
+ * envelope, then decays back to unlit. Calling again restarts the envelope
79
+ * (from 0 if {@link pulseRetrigger} is set, otherwise from the current level).
80
+ * No-op unless {@link mode} is `pulse`.
81
+ */
82
+ pulse() {
83
+ if (this.mode !== `pulse`) return;
84
+ this.#pulsePeak = this.value;
85
+ this.#pulseFrom = this.pulseRetrigger ? 0 : this.#pulseLevel;
86
+ this.#pulseStart = performance.now();
87
+ if (this.#pulseRaf === void 0) this.#pulseRaf = requestAnimationFrame(this.#pulseTick);
88
+ }
89
+ #onModeChange() {
90
+ this.#stopPulse();
91
+ this.#pulseLevel = 0;
92
+ this.blinking = this.mode === `blink`;
93
+ this.#updateBlinking();
94
+ this.style.setProperty(`--value`, this.mode === `pulse` ? `0` : String(this.value));
95
+ }
96
+ #pulseTick;
97
+ #stopPulse() {
98
+ if (this.#pulseRaf !== void 0) {
99
+ cancelAnimationFrame(this.#pulseRaf);
100
+ this.#pulseRaf = void 0;
37
101
  }
38
102
  }
103
+ #updateBlinking() {
104
+ const wasBlinking = this.#blinkInterval !== void 0;
105
+ this.#stopBlinking();
106
+ if (this.mode === `blink` && this.rate > 0) this.#blinkInterval = window.setInterval(() => {
107
+ this.value = this.value === 0 ? 1 : 0;
108
+ }, this.rate);
109
+ else if (wasBlinking || this.mode === `blink`) this.value = 0;
110
+ }
39
111
  #stopBlinking() {
40
112
  if (this.#blinkInterval !== void 0) {
41
113
  clearInterval(this.#blinkInterval);
@@ -49,6 +121,8 @@ let LedElement = class LedElement extends LitElement {
49
121
  this.styles = css`
50
122
  :host {
51
123
  --_size: var(--size, 1ex);
124
+ /* Resolved colour: explicit --colour wins, then the \`color\` attribute, then a default. */
125
+ --_colour: var(--colour, var(--_color-attr, oklch(50% 0.15 25)));
52
126
  display: inline-block;
53
127
  min-width: 1ex;
54
128
  min-height: 1ex;
@@ -58,12 +132,61 @@ let LedElement = class LedElement extends LitElement {
58
132
  }
59
133
 
60
134
  div {
135
+ position: relative;
61
136
  width: 100%;
62
137
  height: 100%;
63
138
  border-radius: 50%;
64
- background: color-mix(in oklch, var(--colour, oklch(50% 0.15 25)), white calc((1 - var(--value, 0)) * 50%));
139
+ background: color-mix(in oklch, var(--_colour), white calc((1 - var(--value, 0)) * 50%));
65
140
  transition: background var(--transition-duration, 150ms) ease-out;
66
141
  }
142
+
143
+ /* Glow: a more realistic domed LED with a specular highlight and coloured bloom. */
144
+ :host([glow]) {
145
+ /* Body darkens toward black as the LED dims, so an unlit LED reads as a dark dome. */
146
+ --_body: color-mix(in oklch, var(--_colour), black calc((1 - var(--value, 0)) * 60%));
147
+ overflow: visible;
148
+ }
149
+
150
+ :host([glow]) div {
151
+ background:
152
+ radial-gradient(circle at 32% 28%,
153
+ color-mix(in oklch, var(--_body), white 55%) 0%,
154
+ var(--_body) 45%,
155
+ color-mix(in oklch, var(--_body), black 32%) 100%);
156
+ box-shadow:
157
+ /* inner rim shading for depth */
158
+ inset 0 calc(var(--_size) * -0.1) calc(var(--_size) * 0.18)
159
+ color-mix(in oklch, var(--_body), black 45%),
160
+ inset 0 calc(var(--_size) * 0.08) calc(var(--_size) * 0.14)
161
+ color-mix(in oklch, var(--_body), white 25%),
162
+ /* coloured bloom, scaled by value so it only glows when lit */
163
+ 0 0 calc(var(--_size) * 0.6 * var(--value, 0)) calc(var(--_size) * 0.12 * var(--value, 0))
164
+ var(--_colour),
165
+ 0 0 calc(var(--_size) * 1.7 * var(--value, 0)) calc(var(--_size) * 0.5 * var(--value, 0))
166
+ color-mix(in oklch, var(--_colour), transparent 45%);
167
+ transition:
168
+ background var(--transition-duration, 150ms) ease-out,
169
+ box-shadow var(--transition-duration, 150ms) ease-out;
170
+ }
171
+
172
+ /* Specular highlight blob near the top-left. */
173
+ :host([glow]) div::after {
174
+ content: "";
175
+ position: absolute;
176
+ inset: 0;
177
+ border-radius: 50%;
178
+ background: radial-gradient(ellipse 55% 45% at 30% 22%,
179
+ color-mix(in oklch, white, transparent 10%) 0%,
180
+ transparent 60%);
181
+ pointer-events: none;
182
+ }
183
+
184
+ /* Pulse mode animates --value frame-by-frame, so drop the CSS transition
185
+ that would otherwise smear the envelope. Placed last to win over the
186
+ glow rule for elements matching both. */
187
+ :host([mode="pulse"]) div {
188
+ transition: none;
189
+ }
67
190
  `;
68
191
  }
69
192
  };
@@ -74,6 +197,15 @@ __decorate([property({
74
197
  reflect: true
75
198
  })], LedElement.prototype, "lit", void 0);
76
199
  __decorate([property({ type: String })], LedElement.prototype, "color", void 0);
200
+ __decorate([property({
201
+ type: Boolean,
202
+ attribute: `glow`,
203
+ reflect: true
204
+ })], LedElement.prototype, "glow", void 0);
205
+ __decorate([property({
206
+ type: String,
207
+ reflect: true
208
+ })], LedElement.prototype, "mode", void 0);
77
209
  __decorate([property({
78
210
  type: Boolean,
79
211
  attribute: `blinking`,
@@ -83,6 +215,18 @@ __decorate([property({
83
215
  type: Number,
84
216
  attribute: `rate`
85
217
  })], LedElement.prototype, "rate", void 0);
218
+ __decorate([property({
219
+ type: Number,
220
+ attribute: `attack-time`
221
+ })], LedElement.prototype, "attackTime", void 0);
222
+ __decorate([property({
223
+ type: Number,
224
+ attribute: `release-time`
225
+ })], LedElement.prototype, "releaseTime", void 0);
226
+ __decorate([property({
227
+ type: Boolean,
228
+ attribute: `pulse-retrigger`
229
+ })], LedElement.prototype, "pulseRetrigger", void 0);
86
230
  LedElement = __decorate([safeCustomElement(`ixfx-led`)], LedElement);
87
231
  //#endregion
88
232
  export { LedElement };
package/dist/led.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"led.js","names":["#updateBlinking","#stopBlinking","#blinkInterval","customElement"],"sources":["../src/led/led.ts"],"sourcesContent":["import { css, html, LitElement } from \"lit\";\nimport { property } from \"lit/decorators.js\";\nimport { safeCustomElement as customElement } from \"../util/custom-elements.js\";\n\n@customElement(`ixfx-led`)\nexport class LedElement extends LitElement {\n @property({ type: Number })\n value = 0;\n\n @property({ type: Boolean, attribute: `lit`, reflect: true })\n lit = false;\n\n @property({ type: String })\n color = `oklch(50% 0.15 25)`;\n\n @property({ type: Boolean, attribute: `blinking`, reflect: true })\n blinking = false;\n\n @property({ type: Number, attribute: `rate` })\n rate = 500;\n\n #blinkInterval: number | undefined;\n\n willUpdate(changedProperties: Map<string, unknown>) {\n if (changedProperties.has(`lit`)) {\n this.value = this.lit ? 1 : 0;\n }\n\n if (changedProperties.has(`value`) || changedProperties.has(`lit`)) {\n this.style.setProperty(`--value`, String(this.value));\n }\n\n if (changedProperties.has(`blinking`) || changedProperties.has(`rate`)) {\n this.#updateBlinking();\n }\n\n if (changedProperties.has(`rate`)) {\n const transitionMs = this.rate < 300 ? this.rate * 0.5 : 150;\n this.style.setProperty(`--transition-duration`, `${transitionMs}ms`);\n }\n }\n\n disconnectedCallback() {\n super.disconnectedCallback();\n this.#stopBlinking();\n }\n\n #updateBlinking() {\n if (this.blinking && this.rate > 0) {\n this.#stopBlinking();\n this.#blinkInterval = window.setInterval(() => {\n this.value = this.value === 0 ? 1 : 0;\n }, this.rate);\n } else {\n this.#stopBlinking();\n this.value = 0;\n }\n }\n\n #stopBlinking() {\n if (this.#blinkInterval !== undefined) {\n clearInterval(this.#blinkInterval);\n this.#blinkInterval = undefined;\n }\n }\n\n protected render() {\n return html`<div part=\"circle\"></div>`;\n }\n\n static styles = css`\n :host {\n --_size: var(--size, 1ex);\n display: inline-block;\n min-width: 1ex;\n min-height: 1ex;\n width: var(--_size);\n height: var(--_size);\n vertical-align: middle;\n }\n\n div {\n width: 100%;\n height: 100%;\n border-radius: 50%;\n background: color-mix(in oklch, var(--colour, oklch(50% 0.15 25)), white calc((1 - var(--value, 0)) * 50%));\n transition: background var(--transition-duration, 150ms) ease-out;\n }\n `;\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"ixfx-led\": LedElement;\n }\n}\n"],"mappings":";;;;AAKO,IAAM,aAAN,MAAM,mBAAmB,WAAW;;;EAEjC,KAAA,QAAA;EAGF,KAAA,MAAA;EAGE,KAAA,QAAA;EAGG,KAAA,WAAA;EAGJ,KAAA,OAAA;;CAEP;CAEA,WAAW,mBAAyC;EAClD,IAAI,kBAAkB,IAAI,KAAK,GAC7B,KAAK,QAAQ,KAAK,MAAM,IAAI;EAG9B,IAAI,kBAAkB,IAAI,OAAO,KAAK,kBAAkB,IAAI,KAAK,GAC/D,KAAK,MAAM,YAAY,WAAW,OAAO,KAAK,KAAK,CAAC;EAGtD,IAAI,kBAAkB,IAAI,UAAU,KAAK,kBAAkB,IAAI,MAAM,GACnE,KAAKA,gBAAgB;EAGvB,IAAI,kBAAkB,IAAI,MAAM,GAAG;GACjC,MAAM,eAAe,KAAK,OAAO,MAAM,KAAK,OAAO,KAAM;GACzD,KAAK,MAAM,YAAY,yBAAyB,GAAG,aAAa,GAAG;EACrE;CACF;CAEA,uBAAuB;EACrB,MAAM,qBAAqB;EAC3B,KAAKC,cAAc;CACrB;CAEA,kBAAkB;EAChB,IAAI,KAAK,YAAY,KAAK,OAAO,GAAG;GAClC,KAAKA,cAAc;GACnB,KAAKC,iBAAiB,OAAO,kBAAkB;IAC7C,KAAK,QAAQ,KAAK,UAAU,IAAI,IAAI;GACtC,GAAG,KAAK,IAAI;EACd,OAAO;GACL,KAAKD,cAAc;GACnB,KAAK,QAAQ;EACf;CACF;CAEA,gBAAgB;EACd,IAAI,KAAKC,mBAAmB,KAAA,GAAW;GACrC,cAAc,KAAKA,cAAc;GACjC,KAAKA,iBAAiB,KAAA;EACxB;CACF;CAEA,SAAmB;EACjB,OAAO,IAAI;CACb;;EAEgB,KAAA,SAAA,GAAG;;;;;;;;;;;;;;;;;;;;AAmBrB;AAnFG,WAAA,CAAA,SAAS,EAAE,MAAM,OAAO,CAAC,CAAA,GAAA,WAAA,WAAA,SAAA,KAAA,CAAA;AAGzB,WAAA,CAAA,SAAS;CAAE,MAAM;CAAS,WAAW;CAAO,SAAS;AAAK,CAAC,CAAA,GAAA,WAAA,WAAA,OAAA,KAAA,CAAA;AAG3D,WAAA,CAAA,SAAS,EAAE,MAAM,OAAO,CAAC,CAAA,GAAA,WAAA,WAAA,SAAA,KAAA,CAAA;AAGzB,WAAA,CAAA,SAAS;CAAE,MAAM;CAAS,WAAW;CAAY,SAAS;AAAK,CAAC,CAAA,GAAA,WAAA,WAAA,YAAA,KAAA,CAAA;AAGhE,WAAA,CAAA,SAAS;CAAE,MAAM;CAAQ,WAAW;AAAO,CAAC,CAAA,GAAA,WAAA,WAAA,QAAA,KAAA,CAAA;AAd9CC,aAAAA,WAAAA,CAAAA,kBAAc,UAAU,CAAA,GAAA,UAAA"}
1
+ {"version":3,"file":"led.js","names":["#pulseFrom","#pulsePeak","#pulseStart","#pulseLevel","#pulseRaf","#pulseTick","#appliedMode","#onModeChange","#updateBlinking","#stopBlinking","#stopPulse","#blinkInterval","customElement"],"sources":["../src/led/led.ts"],"sourcesContent":["import { css, html, LitElement } from \"lit\";\nimport { property } from \"lit/decorators.js\";\nimport { safeCustomElement as customElement } from \"../util/custom-elements.js\";\n\nexport type LedMode = `brightness` | `blink` | `pulse`;\n\n@customElement(`ixfx-led`)\nexport class LedElement extends LitElement {\n @property({ type: Number })\n value = 0;\n\n @property({ type: Boolean, attribute: `lit`, reflect: true })\n lit = false;\n\n @property({ type: String })\n color = `oklch(50% 0.15 25)`;\n\n @property({ type: Boolean, attribute: `glow`, reflect: true })\n glow = false;\n\n /**\n * Operating mode:\n * - `brightness` (default): {@link value} sets the LED brightness directly\n * - `blink`: LED blinks on/off every {@link rate} milliseconds (this mode\n * is what the legacy `blinking` attribute now selects)\n * - `pulse`: LED sits unlit until {@link pulse} is called, which fires an\n * attack/release envelope up to {@link value} and back down\n */\n @property({ type: String, reflect: true })\n mode: LedMode = `brightness`;\n\n /**\n * Legacy alias for `mode=\"blink\"`. Setting `blinking` switches {@link mode}\n * to `blink`; clearing it returns to `brightness`.\n */\n @property({ type: Boolean, attribute: `blinking`, reflect: true })\n blinking = false;\n\n @property({ type: Number, attribute: `rate` })\n rate = 500;\n\n /** Pulse-mode attack time in milliseconds (0 → instant). */\n @property({ type: Number, attribute: `attack-time` })\n attackTime = 40;\n\n /** Pulse-mode release/decay time in milliseconds (0 → instant). */\n @property({ type: Number, attribute: `release-time` })\n releaseTime = 400;\n\n /**\n * Pulse-mode retrigger behaviour. When set, each {@link pulse} call restarts\n * the envelope from 0. When unset (default), the attack ramps from wherever\n * the envelope currently sits, so a retrigger mid-decay is continuous.\n */\n @property({ type: Boolean, attribute: `pulse-retrigger` })\n pulseRetrigger = false;\n\n #blinkInterval: number | undefined;\n #appliedMode: LedMode = `brightness`;\n\n #pulseRaf: number | undefined;\n #pulseStart = 0;\n #pulsePeak = 0;\n #pulseFrom = 0;\n #pulseLevel = 0;\n\n willUpdate(changedProperties: Map<string, unknown>) {\n // `blinking` is a compatibility alias for mode=\"blink\".\n if (changedProperties.has(`blinking`)) {\n if (this.blinking) {\n this.mode = `blink`;\n } else if (this.mode === `blink`) {\n this.mode = `brightness`;\n }\n }\n\n // Handle a mode change (including one triggered by `blinking`, above, in\n // this same update). `#appliedMode` is compared directly rather than via\n // `changedProperties` so a same-pass change is still caught.\n if (this.mode !== this.#appliedMode) {\n this.#appliedMode = this.mode;\n this.#onModeChange();\n }\n\n if (changedProperties.has(`lit`)) {\n // Map `lit` onto `value`, but don't let the initial (undefined -> false)\n // change clobber an explicit `value` attribute.\n if (this.lit) {\n this.value = 1;\n } else if (changedProperties.get(`lit`) !== undefined) {\n this.value = 0;\n }\n }\n\n if (changedProperties.has(`color`)) {\n this.style.setProperty(`--_color-attr`, this.color);\n }\n\n // In pulse mode `--value` is driven by the envelope, not by `value`.\n if (\n this.mode !== `pulse`\n && (changedProperties.has(`value`) || changedProperties.has(`lit`))\n ) {\n this.style.setProperty(`--value`, String(this.value));\n }\n\n if (changedProperties.has(`rate`)) {\n this.#updateBlinking();\n const transitionMs = this.rate < 300 ? this.rate * 0.5 : 150;\n this.style.setProperty(`--transition-duration`, `${transitionMs}ms`);\n }\n }\n\n disconnectedCallback() {\n super.disconnectedCallback();\n this.#stopBlinking();\n this.#stopPulse();\n }\n\n /**\n * Pulse-mode trigger: lights the LED to {@link value} using an attack/release\n * envelope, then decays back to unlit. Calling again restarts the envelope\n * (from 0 if {@link pulseRetrigger} is set, otherwise from the current level).\n * No-op unless {@link mode} is `pulse`.\n */\n pulse() {\n if (this.mode !== `pulse`)\n return;\n this.#pulsePeak = this.value;\n this.#pulseFrom = this.pulseRetrigger ? 0 : this.#pulseLevel;\n this.#pulseStart = performance.now();\n if (this.#pulseRaf === undefined) {\n this.#pulseRaf = requestAnimationFrame(this.#pulseTick);\n }\n }\n\n #onModeChange() {\n this.#stopPulse();\n this.#pulseLevel = 0;\n this.blinking = this.mode === `blink`;\n this.#updateBlinking();\n this.style.setProperty(\n `--value`,\n this.mode === `pulse` ? `0` : String(this.value),\n );\n }\n\n #pulseTick = (now: number) => {\n const attack = Math.max(0, this.attackTime);\n const release = Math.max(0, this.releaseTime);\n const from = this.#pulseFrom;\n const peak = this.#pulsePeak;\n const elapsed = now - this.#pulseStart;\n\n let level: number;\n if (attack > 0 && elapsed < attack) {\n // Ramp from the envelope's starting level up to the peak.\n level = from + (peak - from) * (elapsed / attack);\n } else if (elapsed < attack + release) {\n level = release > 0 ? peak * (1 - (elapsed - attack) / release) : 0;\n } else {\n level = 0;\n }\n level = Math.max(0, Math.min(Math.max(from, peak), level));\n this.#pulseLevel = level;\n this.style.setProperty(`--value`, String(level));\n\n if (elapsed < attack + release) {\n this.#pulseRaf = requestAnimationFrame(this.#pulseTick);\n } else {\n this.#pulseRaf = undefined;\n this.#pulseLevel = 0;\n }\n };\n\n #stopPulse() {\n if (this.#pulseRaf !== undefined) {\n cancelAnimationFrame(this.#pulseRaf);\n this.#pulseRaf = undefined;\n }\n }\n\n #updateBlinking() {\n const wasBlinking = this.#blinkInterval !== undefined;\n this.#stopBlinking();\n if (this.mode === `blink` && this.rate > 0) {\n this.#blinkInterval = window.setInterval(() => {\n this.value = this.value === 0 ? 1 : 0;\n }, this.rate);\n } else if (wasBlinking || this.mode === `blink`) {\n // Leaving blink mode (or blink with rate 0): settle to off.\n this.value = 0;\n }\n }\n\n #stopBlinking() {\n if (this.#blinkInterval !== undefined) {\n clearInterval(this.#blinkInterval);\n this.#blinkInterval = undefined;\n }\n }\n\n protected render() {\n return html`<div part=\"circle\"></div>`;\n }\n\n static styles = css`\n :host {\n --_size: var(--size, 1ex);\n /* Resolved colour: explicit --colour wins, then the \\`color\\` attribute, then a default. */\n --_colour: var(--colour, var(--_color-attr, oklch(50% 0.15 25)));\n display: inline-block;\n min-width: 1ex;\n min-height: 1ex;\n width: var(--_size);\n height: var(--_size);\n vertical-align: middle;\n }\n\n div {\n position: relative;\n width: 100%;\n height: 100%;\n border-radius: 50%;\n background: color-mix(in oklch, var(--_colour), white calc((1 - var(--value, 0)) * 50%));\n transition: background var(--transition-duration, 150ms) ease-out;\n }\n\n /* Glow: a more realistic domed LED with a specular highlight and coloured bloom. */\n :host([glow]) {\n /* Body darkens toward black as the LED dims, so an unlit LED reads as a dark dome. */\n --_body: color-mix(in oklch, var(--_colour), black calc((1 - var(--value, 0)) * 60%));\n overflow: visible;\n }\n\n :host([glow]) div {\n background:\n radial-gradient(circle at 32% 28%,\n color-mix(in oklch, var(--_body), white 55%) 0%,\n var(--_body) 45%,\n color-mix(in oklch, var(--_body), black 32%) 100%);\n box-shadow:\n /* inner rim shading for depth */\n inset 0 calc(var(--_size) * -0.1) calc(var(--_size) * 0.18)\n color-mix(in oklch, var(--_body), black 45%),\n inset 0 calc(var(--_size) * 0.08) calc(var(--_size) * 0.14)\n color-mix(in oklch, var(--_body), white 25%),\n /* coloured bloom, scaled by value so it only glows when lit */\n 0 0 calc(var(--_size) * 0.6 * var(--value, 0)) calc(var(--_size) * 0.12 * var(--value, 0))\n var(--_colour),\n 0 0 calc(var(--_size) * 1.7 * var(--value, 0)) calc(var(--_size) * 0.5 * var(--value, 0))\n color-mix(in oklch, var(--_colour), transparent 45%);\n transition:\n background var(--transition-duration, 150ms) ease-out,\n box-shadow var(--transition-duration, 150ms) ease-out;\n }\n\n /* Specular highlight blob near the top-left. */\n :host([glow]) div::after {\n content: \"\";\n position: absolute;\n inset: 0;\n border-radius: 50%;\n background: radial-gradient(ellipse 55% 45% at 30% 22%,\n color-mix(in oklch, white, transparent 10%) 0%,\n transparent 60%);\n pointer-events: none;\n }\n\n /* Pulse mode animates --value frame-by-frame, so drop the CSS transition\n that would otherwise smear the envelope. Placed last to win over the\n glow rule for elements matching both. */\n :host([mode=\"pulse\"]) div {\n transition: none;\n }\n `;\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"ixfx-led\": LedElement;\n }\n}\n"],"mappings":"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"}
@@ -1,77 +1,3 @@
1
- //#region src/interaction/keyed-list.ts
2
- /**
3
- * Helpers for key-based selection on the Element-flavoured list components
4
- * (`ixfx-vertical-list`, `ixfx-detail-list`, `ixfx-grid-list`).
5
- *
6
- * A "key" is a stable string the host puts on each item element — `data-key`
7
- * (preferred) or `data-value`. It survives the host rebuilding the item set
8
- * (re-fetch, re-sort, filter) where raw `Element` references do not.
9
- */
10
- /**
11
- * The stable key for an item element: `data-key`, then `data-value`, else
12
- * `undefined`.
13
- */
14
- function keyForElement(el) {
15
- return el.getAttribute(`data-key`) ?? el.getAttribute(`data-value`) ?? void 0;
16
- }
17
- /**
18
- * A lazily-built key → element index over a container. Rebuilt on first use
19
- * and after any child-list mutation under `root`; `invalidate()` forces a
20
- * rebuild on the next `get()`.
21
- *
22
- * ```ts
23
- * #keyIndex = new KeyedElementIndex(this);
24
- * // ...
25
- * const el = this.#keyIndex.get(`row-42`);
26
- * // on disconnect:
27
- * this.#keyIndex.dispose();
28
- * ```
29
- */
30
- var KeyedElementIndex = class {
31
- #root;
32
- #selector;
33
- #map = /* @__PURE__ */ new Map();
34
- #dirty = true;
35
- #observer;
36
- /**
37
- * @param root Container whose descendants are indexed.
38
- * @param itemSelector Which descendants count as items (default: any `[data-key]`).
39
- */
40
- constructor(root, itemSelector = `[data-key]`) {
41
- this.#root = root;
42
- this.#selector = itemSelector;
43
- this.#observer = new MutationObserver(() => {
44
- this.#dirty = true;
45
- });
46
- this.#observer.observe(root, {
47
- childList: true,
48
- subtree: true
49
- });
50
- }
51
- /** The element with this key, or `undefined`. First match wins on duplicates. */
52
- get(key) {
53
- if (this.#dirty) this.#rebuild();
54
- return this.#map.get(key);
55
- }
56
- /** Force a rebuild on the next `get()` — e.g. after a `data-key` attribute change. */
57
- invalidate() {
58
- this.#dirty = true;
59
- }
60
- /** Stop observing and drop the cache. */
61
- dispose() {
62
- this.#observer.disconnect();
63
- this.#map.clear();
64
- }
65
- #rebuild() {
66
- this.#map.clear();
67
- for (const el of this.#root.querySelectorAll(this.#selector)) {
68
- const key = keyForElement(el);
69
- if (key !== void 0 && !this.#map.has(key)) this.#map.set(key, el);
70
- }
71
- this.#dirty = false;
72
- }
73
- };
74
- //#endregion
75
1
  //#region src/interaction/list-selection-controller.ts
76
2
  /** Whether the interaction mode allows building a multi-item selection. */
77
3
  function isMultiMode(mode) {
@@ -333,6 +259,6 @@ var ListSelectionController = class {
333
259
  }
334
260
  };
335
261
  //#endregion
336
- export { keyForElement as i, isMultiMode as n, KeyedElementIndex as r, ListSelectionController as t };
262
+ export { isMultiMode as n, ListSelectionController as t };
337
263
 
338
- //# sourceMappingURL=list-selection-controller-CUo-Hhcm.js.map
264
+ //# sourceMappingURL=list-selection-controller-DTFI40li.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list-selection-controller-DTFI40li.js","names":["#config","#selected","#anchor","#apply","#effectiveMulti"],"sources":["../src/interaction/list-selection-controller.ts"],"sourcesContent":["import type { ListInteractionMode, ListSelectionMode } from './list-selection-types.js';\n\n/** Modifier state needed to route a click gesture per interaction mode. */\nexport type ListSelectionGesture = {\n readonly ctrlKey: boolean;\n readonly metaKey: boolean;\n readonly shiftKey: boolean;\n};\n\nexport type ListSelectionModes = {\n readonly selectionMode: ListSelectionMode;\n readonly interactionMode: ListInteractionMode;\n};\n\n/**\n * Configuration supplied by the host component.\n *\n * The controller owns selection *state* only — everything DOM- or\n * component-specific is expressed through these callbacks.\n */\nexport type ListSelectionControllerConfig<T> = {\n /** Visible items in navigation order — used for range selection and select-all. */\n readonly getOrderedItems: () => readonly T[];\n /** Whether an item may join the selection (e.g. reject `not-checked` rows). */\n readonly isSelectable: (item: T) => boolean;\n /** Current modes; read on every gesture so property changes apply immediately. */\n readonly getModes: () => ListSelectionModes;\n /**\n * Fired after every selection change. The host syncs DOM state\n * (`data-selected` / `data-checked`), dispatches `list-select`, and\n * requests an update.\n */\n readonly onChange: (selected: ReadonlySet<T>, previous: ReadonlySet<T>) => void;\n /**\n * Optional: derive a stable string key for an item. Enables `selectedKeys`\n * and `selectKeys()`. When omitted, `selectedKeys` is always empty.\n */\n readonly getKey?: (item: T) => string | undefined;\n};\n\n/** Whether the interaction mode allows building a multi-item selection. */\nexport function isMultiMode(mode: ListInteractionMode): boolean {\n return mode === `checked` || mode === `sticky` || mode === `vscode` || mode === `standard`;\n}\n\n/**\n * Generic selection state machine shared by list components.\n *\n * Owns the selected set plus the range-selection anchor, the selection\n * primitives (`replace` / `toggle` / `rangeTo` / `clear` / `selectAll` /\n * `selectMany` / `deselect`) and the per-interactionMode gesture routers\n * (`handleClick` / `handleSpace`).\n *\n * Identity is generic: both `ixfx-vertical-list` and `ixfx-detail-list` use\n * `ListSelectionController<Element>`.\n *\n * @example\n * ```ts\n * const selection = new ListSelectionController<Element>({\n * getOrderedItems: () => this.getVisibleItems(),\n * isSelectable: el => !el.hasAttribute(`not-checked`),\n * getModes: () => ({ selectionMode: this.selectionMode, interactionMode: this.interactionMode }),\n * onChange: (selected, previous) => {\n * this.syncItemStates();\n * this.dispatchEvent(new CustomEvent(`list-select`, { detail: { selected, previous } }));\n * },\n * });\n * ```\n */\nexport class ListSelectionController<T> {\n readonly #config: ListSelectionControllerConfig<T>;\n #selected: ReadonlySet<T> = new Set();\n #anchor: T | undefined;\n\n constructor(config: ListSelectionControllerConfig<T>) {\n this.#config = config;\n }\n\n /** The current selection set (read-only). */\n get selected(): ReadonlySet<T> {\n return this.#selected;\n }\n\n /**\n * The current selection as stable string keys (read-only). Empty unless a\n * `getKey` was supplied in config. Items whose `getKey` returns `undefined`\n * are omitted.\n */\n get selectedKeys(): ReadonlySet<string> {\n const getKey = this.#config.getKey;\n const out = new Set<string>();\n if (getKey) {\n for (const item of this.#selected) {\n const key = getKey(item);\n if (key !== undefined) {\n out.add(key);\n }\n }\n }\n return out;\n }\n\n /** Anchor for range (shift) selection — the last plainly-selected item. */\n get anchor(): T | undefined {\n return this.#anchor;\n }\n\n /** Whether \"select all\" (Cmd/Ctrl+A) is currently permitted. */\n canSelectAll(): boolean {\n return this.canSelectMultiple() && this.#config.getModes().selectionMode !== `none`;\n }\n\n /** Whether a multi-item selection can be built in the current modes. */\n canSelectMultiple(): boolean {\n const { selectionMode, interactionMode } = this.#config.getModes();\n return selectionMode === `multiple` || isMultiMode(interactionMode);\n }\n\n // --- Selection primitives ---\n\n /** Replace the entire selection with a single item; updates the anchor. */\n replace(item: T): void {\n if (this.#config.getModes().selectionMode === `none`)\n return;\n if (!this.#config.isSelectable(item))\n return;\n this.#anchor = item;\n this.#apply(new Set([item]));\n }\n\n /** Toggle a single item in/out of the selection without replacing it. Anchor unchanged. */\n toggle(item: T): void {\n const { selectionMode, interactionMode } = this.#config.getModes();\n if (!this.#config.isSelectable(item))\n return;\n // checked/vscode/sticky modes imply multiple selection capacity\n if (selectionMode === `none` && interactionMode !== `checked` && interactionMode !== `sticky`)\n return;\n\n const next = new Set(this.#selected);\n if (next.has(item)) {\n next.delete(item);\n } else {\n if (!this.canSelectMultiple())\n next.clear();\n next.add(item);\n }\n this.#apply(next);\n }\n\n /** Select the contiguous ordered range from the anchor to `item`. Anchor unchanged. */\n rangeTo(item: T): void {\n if (this.#config.getModes().selectionMode === `none`)\n return;\n if (this.#anchor === undefined) {\n this.replace(item);\n return;\n }\n const ordered = this.#config.getOrderedItems();\n const anchorIdx = ordered.indexOf(this.#anchor);\n const targetIdx = ordered.indexOf(item);\n if (anchorIdx === -1 || targetIdx === -1) {\n this.replace(item);\n return;\n }\n const start = Math.min(anchorIdx, targetIdx);\n const end = Math.max(anchorIdx, targetIdx);\n this.#apply(new Set(ordered.slice(start, end + 1).filter(t => this.#config.isSelectable(t))));\n }\n\n /** Clear the selection. No change event when the selection is already empty. */\n clear(): void {\n if (this.#selected.size === 0)\n return;\n this.#apply(new Set());\n }\n\n /** Select every ordered item that passes `isSelectable`. */\n selectAll(): void {\n const ordered = this.#config.getOrderedItems();\n if (ordered.length === 0)\n return;\n const next = new Set(ordered.filter(t => this.#config.isSelectable(t)));\n if (next.size === 0)\n return;\n this.#apply(next);\n }\n\n /**\n * Replace the selection with many items at once. Requires\n * `selectionMode=\"multiple\"`, throws otherwise. Updates the anchor.\n */\n selectMany(items: Iterable<T>): void {\n const { selectionMode } = this.#config.getModes();\n if (selectionMode !== `multiple`) {\n throw new Error(`selectMany requires selectionMode=\"multiple\" (current: \"${selectionMode}\")`);\n }\n const next = new Set([...items].filter(t => this.#config.isSelectable(t)));\n if (next.size === 0)\n return;\n this.#anchor = [...next].at(-1);\n this.#apply(next);\n }\n\n /** Remove a single item from the selection. */\n deselect(item: T): void {\n if (!this.#selected.has(item))\n return;\n const next = new Set(this.#selected);\n next.delete(item);\n this.#apply(next);\n }\n\n // --- Key-based selection ---\n\n /**\n * Whether the current modes permit a selection of more than one item.\n * Stricter than `canSelectMultiple()`: `standard` / `implicit` only count\n * as multi when `selectionMode=\"multiple\"`.\n */\n #effectiveMulti(): boolean {\n const { selectionMode, interactionMode } = this.#config.getModes();\n return selectionMode === `multiple`\n || interactionMode === `checked`\n || interactionMode === `sticky`\n || interactionMode === `vscode`;\n }\n\n /**\n * Replace the selection with the items resolved from `keys`. Keys that\n * `resolve` cannot map, or that resolve to a non-selectable item, are\n * ignored. `selectionMode=\"none\"` is a no-op. When multi-select is not\n * available, only the last resolvable key is kept. `onChange` fires once.\n */\n selectKeys(keys: Iterable<string>, resolve: (key: string) => T | undefined): void {\n if (this.#config.getModes().selectionMode === `none`)\n return;\n let next = new Set<T>();\n for (const key of new Set(keys)) {\n const item = resolve(key);\n if (item !== undefined && this.#config.isSelectable(item)) {\n next.add(item);\n }\n }\n if (!this.#effectiveMulti() && next.size > 1) {\n const last = [...next].at(-1);\n next = last === undefined ? new Set() : new Set([last]);\n }\n this.#anchor = [...next].at(-1);\n this.#apply(next);\n }\n\n /**\n * Add the items resolved from `keys` to the selection (multi-select only;\n * a no-op otherwise). `onChange` fires once if anything changed.\n */\n addKeys(keys: Iterable<string>, resolve: (key: string) => T | undefined): void {\n if (!this.#effectiveMulti())\n return;\n const next = new Set(this.#selected);\n let changed = false;\n for (const key of new Set(keys)) {\n const item = resolve(key);\n if (item !== undefined && !next.has(item) && this.#config.isSelectable(item)) {\n next.add(item);\n changed = true;\n }\n }\n if (changed) {\n this.#apply(next);\n }\n }\n\n /**\n * Remove the items resolved from `keys` from the selection. `onChange`\n * fires once if anything changed.\n */\n removeKeys(keys: Iterable<string>, resolve: (key: string) => T | undefined): void {\n if (this.#selected.size === 0)\n return;\n const next = new Set(this.#selected);\n let changed = false;\n for (const key of new Set(keys)) {\n const item = resolve(key);\n if (item !== undefined && next.delete(item)) {\n changed = true;\n }\n }\n if (changed) {\n this.#apply(next);\n }\n }\n\n // --- Gesture routers ---\n\n /** Route a click on `item` according to the current interaction mode. */\n handleClick(item: T, gesture: ListSelectionGesture): void {\n const { selectionMode, interactionMode: mode } = this.#config.getModes();\n if (mode === `manual`)\n return;\n if (selectionMode === `none` && mode !== `checked` && mode !== `sticky`)\n return;\n\n switch (mode) {\n case `implicit`:\n // Single-select only; modifier keys have no effect\n this.replace(item);\n break;\n\n case `standard`:\n case `vscode`:\n if (gesture.ctrlKey || gesture.metaKey) {\n // Ctrl/Cmd+click: toggle item; requires effective multi-select\n if (selectionMode === `multiple` || mode === `vscode`) {\n this.toggle(item);\n }\n } else if (gesture.shiftKey && (selectionMode === `multiple` || mode === `vscode`)) {\n // Shift+click: range-select from anchor\n this.rangeTo(item);\n } else {\n // Plain click: replace selection, update anchor\n this.replace(item);\n }\n break;\n\n case `checked`:\n // Body clicks do nothing — only the checkbox column toggles.\n break;\n\n case `sticky`:\n // Every tap/click toggles — multi-select without modifier keys (touchscreens).\n this.toggle(item);\n break;\n }\n }\n\n /** Route a Space keypress on `item` according to the current interaction mode. */\n handleSpace(item: T): void {\n const mode = this.#config.getModes().interactionMode;\n if (mode === `manual`)\n return;\n if (mode === `implicit`) {\n // Space selects (same as click) in implicit mode\n this.replace(item);\n } else {\n // standard / vscode / checked / sticky: Space toggles\n this.toggle(item);\n }\n }\n\n // --- Maintenance ---\n\n /** Drop the range-selection anchor (e.g. when the interaction mode changes). */\n clearAnchor(): void {\n this.#anchor = undefined;\n }\n\n /**\n * Re-key the selection across an identity boundary — e.g. when items are\n * wrapped/unwrapped and the selected Element references change. Silent:\n * no `onChange` is fired.\n */\n remap(fn: (item: T) => T): void {\n if (this.#selected.size === 0)\n return;\n this.#selected = new Set([...this.#selected].map(fn));\n }\n\n /**\n * Hard reset: empty the selection and clear the anchor without firing\n * `onChange` (e.g. when all items are removed).\n */\n reset(): void {\n this.#selected = new Set();\n this.#anchor = undefined;\n }\n\n /**\n * Replace the selection and anchor silently (no `onChange`). Used to\n * re-key the selection when items are rebuilt from data — e.g. matching\n * new elements to previously selected keys.\n */\n seed(items: Iterable<T>, anchor?: T): void {\n this.#selected = new Set(items);\n this.#anchor = anchor;\n }\n\n #apply(next: ReadonlySet<T>): void {\n const previous = this.#selected;\n this.#selected = next;\n this.#config.onChange(next, previous);\n }\n}\n"],"mappings":"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"}
package/dist/miller.d.ts CHANGED
@@ -1,7 +1,9 @@
1
1
  import { a as TreeItem, n as TreeExclusivity, o as TreeNode, r as TreeFilterPredicate, s as TreeSelectionFilter, t as LoadChildrenCallback } from "./types-Bc23SI94.js";
2
- import { a as TreeComponent, c as TreeExpandEvent, d as TreeSearchHost, f as TreeSelectDetail, g as TreeDataModel, h as TreeTickleEvent, i as TreeCollapseEvent, l as TreeItemClickDetail, m as TreeTickleDetail, n as TreeActivateEvent, o as TreeComponentEventMap, p as TreeSelectEvent, r as TreeCollapseDetail, s as TreeExpandDetail, t as TreeActivateDetail, u as TreeItemClickEvent } from "./tree-component-BvV4muxx.js";
2
+ import { a as TreeComponent, c as TreeExpandEvent, d as TreeSearchHost, f as TreeSelectDetail, g as TreeDataModel, h as TreeTickleEvent, i as TreeCollapseEvent, l as TreeItemClickDetail, m as TreeTickleDetail, n as TreeActivateEvent, o as TreeComponentEventMap, p as TreeSelectEvent, r as TreeCollapseDetail, s as TreeExpandDetail, t as TreeActivateDetail, u as TreeItemClickEvent } from "./tree-component-F6qZ0Gdv.js";
3
+ import { S as ListSearchController } from "./index-h6gxtfHH.js";
3
4
  import { n as TickledController } from "./tickled-controller-Bn3YdQsM.js";
4
5
  import { LitElement, PropertyValues, TemplateResult, nothing } from "lit";
6
+ import { CommandRegistry } from "@clinth/ui-commands";
5
7
  import { TraversableTree } from "@ixfx/collections";
6
8
  //#region src/miller/types.d.ts
7
9
  type MillerSelectionMode = `implicit` | `standard` | `manual` | `checked` | `vscode`;
@@ -28,6 +30,16 @@ type MillerItemDblClickDetail = {
28
30
  node: TreeNode;
29
31
  depth: number;
30
32
  };
33
+ /** Detail for the `rebase` event fired when the view root changes. */
34
+ type MillerRebaseDetail = {
35
+ /** The new view root, or `undefined` when the view was restored to the full tree. */
36
+ viewRoot: TreeNode | undefined;
37
+ /**
38
+ * Ancestry chain from the true root down to and including the new view root.
39
+ * Empty when the view shows the full tree.
40
+ */
41
+ path: Array<TreeNode>;
42
+ };
31
43
  /** Detail for the `item-rightclick` event. */
32
44
  type MillerItemRightClickDetail = {
33
45
  node: TreeNode;
@@ -45,6 +57,7 @@ type MillerHeaderClickEvent = CustomEvent<MillerHeaderClickDetail>;
45
57
  type MillerItemDblClickEvent = CustomEvent<MillerItemDblClickDetail>;
46
58
  type MillerItemRightClickEvent = CustomEvent<MillerItemRightClickDetail>;
47
59
  type MillerHeaderRightClickEvent = CustomEvent<MillerHeaderRightClickDetail>;
60
+ type MillerRebaseEvent = CustomEvent<MillerRebaseDetail>;
48
61
  /**
49
62
  * A snapshot of all display states for a single item in the miller columns.
50
63
  *
@@ -108,6 +121,29 @@ declare class MillerBaseElement extends LitElement implements TreeComponent, Tre
108
121
  root: TreeNode | undefined;
109
122
  model: TreeDataModel | undefined;
110
123
  headers: boolean;
124
+ /**
125
+ * When true, the last (rightmost) column grows to fill any remaining
126
+ * horizontal space in the host. All other columns size to their content
127
+ * (`max-content`), unless the user has manually resized them.
128
+ *
129
+ * When the user clicks into an item in the stretched column and a new
130
+ * column opens, the previously-last column automatically transitions back
131
+ * to its content-based width and the new last column stretches instead.
132
+ * Explicit user-resized widths (via the resize handle) always win over
133
+ * stretching.
134
+ */
135
+ stretchLastColumn: boolean;
136
+ /**
137
+ * Alternative node to use as the display root (Finder-style column-view
138
+ * rebase). When set, column 0 shows this node's children and all depths,
139
+ * headers, and key paths are relative to it. When `undefined` (default),
140
+ * the full `root` tree is shown.
141
+ *
142
+ * Prefer `rebaseTo()` / `rebaseUp()` over assigning this directly — they
143
+ * validate the target and report whether the view changed. Assigning a new
144
+ * `root` (or receiving one via `model`) silently clears the view root.
145
+ */
146
+ viewRoot: TreeNode | undefined;
111
147
  /**
112
148
  * Controls the column-level interaction pattern (how clicks map to column changes).
113
149
  * This is distinct from `selectionFilter` (which restricts which node kinds are selectable).
@@ -123,13 +159,27 @@ declare class MillerBaseElement extends LitElement implements TreeComponent, Tre
123
159
  */
124
160
  loadChildren: LoadChildrenCallback | undefined;
125
161
  filterPredicate: TreeFilterPredicate | undefined;
162
+ /**
163
+ * Space-separated subset of `navigate select filter` — limits which
164
+ * incremental-search modes are offered. Unset ⇒ all three; empty ⇒ search
165
+ * disabled. See `docs/infra-incremental.md`.
166
+ */
167
+ searchModes: string | undefined;
126
168
  protected _columns: TreeNode[][];
127
169
  protected _loadingDepth: number | undefined;
128
170
  protected _selectedByDepth: Map<number, TreeNode>;
129
171
  protected _selectedKeys: Set<string>;
130
172
  protected _selectedNodes: Set<TreeNode>;
173
+ /** Incremental search (navigate / select / filter). See `docs/infra-incremental.md`. */
174
+ readonly search: ListSearchController<{
175
+ node: TreeNode;
176
+ depth: number;
177
+ }>;
178
+ /** Command registry carrying this component's `list.search.*` commands. */
179
+ get registry(): CommandRegistry<unknown> | undefined;
131
180
  tickled: TickledController<string>;
132
181
  updated(changedProps: PropertyValues): void;
182
+ connectedCallback(): void;
133
183
  disconnectedCallback(): void;
134
184
  getConfig(): MillerConfig;
135
185
  setConfig(config: MillerConfig): void;
@@ -165,17 +215,41 @@ declare class MillerBaseElement extends LitElement implements TreeComponent, Tre
165
215
  */
166
216
  getSelected(): TreeNode[];
167
217
  /**
168
- * Programmatically navigate to a node by key path.
218
+ * Programmatically navigate to a node by key path, relative to the current
219
+ * view root (rebased or full tree).
169
220
  */
170
221
  navigateTo(keys: readonly string[]): boolean;
171
222
  /**
172
223
  * Programmatically navigate to the first node (breadth-first) matching a predicate.
173
224
  */
174
225
  navigateToNode(predicate: (node: TreeNode) => boolean): boolean;
226
+ /** Open the columns down to `key` and move the cursor to it, without changing the selection (search "navigate" mode). */
227
+ revealKey(key: string): boolean;
228
+ /** Replace the selection with `nodes` (search "select" mode). */
229
+ selectMany(nodes: Iterable<TreeNode>): void;
175
230
  getNodes(): ReadonlyArray<{
176
231
  node: TreeNode;
177
232
  depth: number;
178
233
  }>;
234
+ /**
235
+ * Rebase the view to show `target`'s children in column 0 (Finder column-view
236
+ * behavior on double-click), keeping the full tree in `root`.
237
+ *
238
+ * - Pass a `MillerNode` (used as-is) or a key (looked up in the full tree).
239
+ * - Pass `undefined` to restore the full-tree view.
240
+ * - Rebasing to the true root is equivalent to restoring the full view.
241
+ * - Leaves are refused (returns `false`).
242
+ *
243
+ * Selection is cleared; all depths, headers, and key paths are relative to
244
+ * the new view root. Fires a `rebase` event when the view actually changes.
245
+ * Returns `false` when nothing changed (unknown key, leaf, or same view).
246
+ */
247
+ rebaseTo(target: TreeNode | string | undefined): boolean;
248
+ /**
249
+ * Rebase one level up from the current view root (no-op at the full tree).
250
+ * Fires a `rebase` event when the view actually changes.
251
+ */
252
+ rebaseUp(): boolean;
179
253
  protected renderColumnContent(nodes: TreeNode[], depth: number): TemplateResult | typeof nothing;
180
254
  protected _handleItemClick(node: TreeNode, depth: number, event?: MouseEvent): void;
181
255
  /**
@@ -234,6 +308,7 @@ interface MillerBaseElementEventMap extends TreeComponentEventMap {
234
308
  'item-rightclick': CustomEvent<MillerItemRightClickDetail>;
235
309
  'header-click': CustomEvent<MillerHeaderClickDetail>;
236
310
  'header-rightclick': CustomEvent<MillerHeaderRightClickDetail>;
311
+ 'rebase': CustomEvent<MillerRebaseDetail>;
237
312
  }
238
313
  interface MillerBaseElement {
239
314
  addEventListener: (<K extends keyof MillerBaseElementEventMap>(type: K, listener: (this: MillerBaseElement, event: MillerBaseElementEventMap[K]) => void, options?: boolean | AddEventListenerOptions) => void) & ((type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions) => void);
@@ -319,10 +394,13 @@ declare class MillerTreeController<T> {
319
394
  * Any ancestor `MillerNode`s with unloaded children are populated
320
395
  * synchronously before calling `element.navigateTo()`.
321
396
  *
397
+ * Key paths are relative to the element's current view root: when the view
398
+ * is rebased, only matches at or below the view root can be reached.
399
+ *
322
400
  * Returns `false` if no matching node is found.
323
401
  */
324
402
  navigateToValue(predicate: (value: T) => boolean): boolean;
325
403
  }
326
404
  //#endregion
327
- export { type TreeActivateDetail as MillerActivateDetail, type TreeActivateEvent as MillerActivateEvent, MillerBaseElement, MillerBaseElementEventMap, type TreeCollapseDetail as MillerCollapseDetail, type TreeCollapseEvent as MillerCollapseEvent, MillerConfig, type TreeExpandDetail as MillerExpandDetail, type TreeExpandEvent as MillerExpandEvent, MillerHeaderClickDetail, MillerHeaderClickEvent, MillerHeaderRightClickDetail, MillerHeaderRightClickEvent, type TreeItem as MillerItem, type TreeItemClickDetail as MillerItemClickDetail, type TreeItemClickEvent as MillerItemClickEvent, MillerItemDblClickDetail, MillerItemDblClickEvent, MillerItemRightClickDetail, MillerItemRightClickEvent, MillerItemState, MillerListElement, type TreeNode as MillerNode, type TreeSelectDetail as MillerSelectDetail, type TreeSelectEvent as MillerSelectEvent, MillerSelectionMode, type TreeTickleDetail as MillerTickleDetail, type TreeTickleEvent as MillerTickleEvent, MillerTreeController, MillerTreeControllerOptions };
405
+ export { type TreeActivateDetail as MillerActivateDetail, type TreeActivateEvent as MillerActivateEvent, MillerBaseElement, MillerBaseElementEventMap, type TreeCollapseDetail as MillerCollapseDetail, type TreeCollapseEvent as MillerCollapseEvent, MillerConfig, type TreeExpandDetail as MillerExpandDetail, type TreeExpandEvent as MillerExpandEvent, MillerHeaderClickDetail, MillerHeaderClickEvent, MillerHeaderRightClickDetail, MillerHeaderRightClickEvent, type TreeItem as MillerItem, type TreeItemClickDetail as MillerItemClickDetail, type TreeItemClickEvent as MillerItemClickEvent, MillerItemDblClickDetail, MillerItemDblClickEvent, MillerItemRightClickDetail, MillerItemRightClickEvent, MillerItemState, MillerListElement, type TreeNode as MillerNode, MillerRebaseDetail, MillerRebaseEvent, type TreeSelectDetail as MillerSelectDetail, type TreeSelectEvent as MillerSelectEvent, MillerSelectionMode, type TreeTickleDetail as MillerTickleDetail, type TreeTickleEvent as MillerTickleEvent, MillerTreeController, MillerTreeControllerOptions };
328
406
  //# sourceMappingURL=miller.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"miller.d.ts","names":[],"sources":["../src/miller/types.ts","../src/miller/miller-base.ts","../src/miller/miller-list.ts","../src/miller/miller-tree-controller.ts"],"mappings":";;;;;;KAyBY;KAEA;EACV,cAAc;;;KAIJ;;EAEV;;EAEA;;;;;;;;EAQA,MAAM,MAZI;;;KAgBA;EACV,MADU;EAEV;;;KAIU;EACV,MADU;EAEV;EACA,YAAY;;;KAIF;EACV;EACA;EACA,MAAM,MAHI;EAIV,YAAY;;KAGF,yBAAyB,YAAY;KACrC,0BAA0B,YAAY;KACtC,4BAA4B,YAAY;KACxC,8BAA8B,YAAY;;;;;;;;;;KAW1C;EACV;EACA;EACA;EACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cCMW,0BAA0B,sBAAsB,eAAe;;EAE1E,MAAM;EAGN,OAAO;EAGP;;;;;;EAQA,iBAAiB;EAKjB,iBAAiB;EAGjB,aAAa;;;;;;EAQb,cAAc;EAGd,iBAAiB;YAGP,UAAU;YAIV;YAGA,kBAAgB,YAAA;YAGhB,eAAe;YAIf,gBAAc,IAAA;EA2BxB,SAAO;EA+BE,QAAQ,cAAc;EA8DtB;EAQT,aAAa;EAQb,UAAU,QAAQ;;MASd,gBAAgB;;MAKhB,aAAa,MAAM;;MAQnB,gBAAgB;MAIhB,aAAa,MAAM;;MAWnB,iBAAiB,YAAY;;EAOjC,OAAO,MAAM;;EAYb,SAAS,MAAM;;;;EAgBf;;EAYA;;EAKA;;;;;EAQA,YAAY,MAAM,UAAY;;;;;EAmB9B,eAAe;;;;EAcf,WAAW;;;;EAsDX,eAAe,YAAY,MAAM;EASjC,YAAY;IAAgB,MAAM;IAAY;;YAkCpC,oBAAoB,OAAO,YAAc,gBAAgB,wBAAwB;YAMjF,iBAAiB,MAAM,UAAY,eAAe,QAAQ;;;;YAO1D,qBAAqB,OAAO,YAAY,MAAM,UAAY;;;;YAQ1D,oBAAoB,MAAM,UAAY,eAAe,QAAQ;;;;YAW7D,sBAAsB,MAAM,UAAY,eAAe,OAAO;;;;YAW9D,wBAAwB,OAAO,YAAY;YAU3C,YAAY,MAAM,UAAY;YAY9B,QAAQ,MAAM,UAAY;;;;EAOpC,aAAa,aAAa,gBAAgB;;;;YA2nBhC,0BAA0B,wBAAwB;YAoBzC,UAAM;YAgCf,iBAAiB;YAQjB,WAAW,MAAM,UAAY;;;;;YAQ7B,wBAAwB,MAAM,UAAY;;;;YAY1C;SAIM,sBAAM;;QAiHhB;YACI;IACR,oBAAoB;;;UAIP,kCAAkC;EACjD,cAAc;EACd,UAAU;EACV,YAAY;EACZ,UAAU;EACV,UAAU;EACV,YAAY;EACZ,iBAAiB,YAAY;EAC7B,mBAAmB,YAAY;EAC/B,gBAAgB,YAAY;EAC5B,qBAAqB,YAAY;;UAGlB;EACf,oBAAoB,gBAAgB,2BAClC,MAAM,GACN,WAAW,MAAM,mBAAmB,OAAO,0BAA0B,aACrE,oBAAoB,sCAEpB,cACA,UAAU,oCACV,oBAAoB;EAEtB,uBAAuB,gBAAgB,2BACrC,MAAM,GACN,WAAW,MAAM,mBAAmB,OAAO,0BAA0B,aACrE,oBAAoB,mCAEpB,cACA,UAAU,oCACV,oBAAoB;;;;;;;;;;;;;;;;;;;;cCl3CX,0BAA0B;;YAClB,oBAAoB,OAAO,YAAc,gBAAgB;SA0D5D,sBAAM;;QAqFhB;YACI;IACR,oBAAoB;;;;;KCnMZ,4BAA4B;;;;;;;EAOtC,cAAc,OAAO,GAAG,MAAM,gBAAgB,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;cA2B1C,qBAAqB;;EAa9B,YAAA,MAAM,gBAAgB,IACtB,SAAS,mBACT,SAAS,4BAA4B;;;;;EAavC;;EAoBA;;;;;;;;;;EAeA,gBAAgB,YAAY,OAAO"}
1
+ {"version":3,"file":"miller.d.ts","names":[],"sources":["../src/miller/types.ts","../src/miller/miller-base.ts","../src/miller/miller-list.ts","../src/miller/miller-tree-controller.ts"],"mappings":"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"}