@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.
- package/bundle/index.d.ts +787 -190
- package/bundle/index.d.ts.map +1 -1
- package/bundle/index.js +2770 -592
- package/bundle/index.js.map +1 -1
- package/dist/ac-token.js +1 -1
- package/dist/crumbs.d.ts +5 -1
- package/dist/crumbs.d.ts.map +1 -1
- package/dist/crumbs.js +10 -0
- package/dist/crumbs.js.map +1 -1
- package/dist/{data-grid-BddUjAti.d.ts → data-grid-D-z7bx5-.d.ts} +26 -1
- package/dist/data-grid-D-z7bx5-.d.ts.map +1 -0
- package/dist/data-grid.d.ts +1 -1
- package/dist/data-grid.js +88 -7
- package/dist/data-grid.js.map +1 -1
- package/dist/{detail-list-Br0WAnry.d.ts → detail-list-_ZJHYEBr.d.ts} +20 -12
- package/dist/detail-list-_ZJHYEBr.d.ts.map +1 -0
- package/dist/detail-list.d.ts +2 -2
- package/dist/detail-list.js +51 -135
- package/dist/detail-list.js.map +1 -1
- package/dist/grid-list-nRUQOcEL.js +1537 -0
- package/dist/grid-list-nRUQOcEL.js.map +1 -0
- package/dist/{grid-list-B_orPsot.d.ts → grid-list-u33vmKzb.d.ts} +128 -10
- package/dist/grid-list-u33vmKzb.d.ts.map +1 -0
- package/dist/grid-list.d.ts +1 -1
- package/dist/grid-list.js +1 -932
- package/dist/incr-search-C05WjbZh.js +999 -0
- package/dist/incr-search-C05WjbZh.js.map +1 -0
- package/dist/incr-search.d.ts +2 -2
- package/dist/incr-search.js +2 -3
- package/dist/index-h6gxtfHH.d.ts +397 -0
- package/dist/index-h6gxtfHH.d.ts.map +1 -0
- package/dist/index.d.ts +62 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +343 -17
- package/dist/index.js.map +1 -1
- package/dist/led.d.ts +33 -1
- package/dist/led.d.ts.map +1 -1
- package/dist/led.js +157 -13
- package/dist/led.js.map +1 -1
- package/dist/{list-selection-controller-CUo-Hhcm.js → list-selection-controller-DTFI40li.js} +2 -76
- package/dist/list-selection-controller-DTFI40li.js.map +1 -0
- package/dist/miller.d.ts +81 -3
- package/dist/miller.d.ts.map +1 -1
- package/dist/miller.js +327 -68
- package/dist/miller.js.map +1 -1
- package/dist/narrowed-text.js +1 -2
- package/dist/narrowed-text.js.map +1 -1
- package/dist/panel.d.ts +3 -1
- package/dist/panel.d.ts.map +1 -1
- package/dist/panel.js +31 -1
- package/dist/panel.js.map +1 -1
- package/dist/plots.d.ts +2 -2
- package/dist/plots.js +1 -1
- package/dist/{titlebar-Bj5pfdw_.d.ts → titlebar-YQ-jVQoS.d.ts} +2 -2
- package/dist/{titlebar-Bj5pfdw_.d.ts.map → titlebar-YQ-jVQoS.d.ts.map} +1 -1
- package/dist/titlebar.d.ts +1 -1
- package/dist/titlebar.js +83 -1
- package/dist/titlebar.js.map +1 -1
- package/dist/{tree-B74zdI60.js → tree-Chptss7I.js} +77 -15
- package/dist/tree-Chptss7I.js.map +1 -0
- package/dist/{tree-component-BvV4muxx.d.ts → tree-component-F6qZ0Gdv.d.ts} +9 -1
- package/dist/{tree-component-BvV4muxx.d.ts.map → tree-component-F6qZ0Gdv.d.ts.map} +1 -1
- package/dist/tree.d.ts +22 -1
- package/dist/tree.d.ts.map +1 -1
- package/dist/tree.js +1 -1
- package/dist/{vertical-list-CQSEVeOT.d.ts → vertical-list-CFuFDygi.d.ts} +17 -14
- package/dist/vertical-list-CFuFDygi.d.ts.map +1 -0
- package/dist/vertical-list.d.ts +1 -1
- package/dist/vertical-list.js +59 -127
- package/dist/vertical-list.js.map +1 -1
- package/dist/{xy-axis-5KhVTNoX.d.ts → xy-axis-BfP9hKLj.d.ts} +25 -2
- package/dist/{xy-axis-5KhVTNoX.d.ts.map → xy-axis-BfP9hKLj.d.ts.map} +1 -1
- package/dist/{xy-axis-VrX8dmvS.js → xy-axis-DzM1brBZ.js} +141 -8
- package/dist/xy-axis-DzM1brBZ.js.map +1 -0
- package/docs-user/data-grid.md +19 -0
- package/docs-user/detail-list.md +1 -1
- package/docs-user/grid-list.md +120 -11
- package/docs-user/incr-search.md +12 -2
- package/docs-user/index.json +3 -3
- package/docs-user/led.md +37 -7
- package/docs-user/llms.txt +1 -1
- package/docs-user/panel.md +68 -13
- package/docs-user/titlebar.md +3 -4
- package/docs-user/tree.md +22 -1
- package/docs-user/vertical-list.md +7 -1
- package/llms.txt +1 -1
- package/package.json +1 -1
- package/dist/data-grid-BddUjAti.d.ts.map +0 -1
- package/dist/detail-list-Br0WAnry.d.ts.map +0 -1
- package/dist/element-search-Y8SRfPGM.js +0 -185
- package/dist/element-search-Y8SRfPGM.js.map +0 -1
- package/dist/grid-list-B_orPsot.d.ts.map +0 -1
- package/dist/grid-list.js.map +0 -1
- package/dist/incr-search-vCzuao-2.js +0 -137
- package/dist/incr-search-vCzuao-2.js.map +0 -1
- package/dist/index-Cu5i8zZY.d.ts +0 -160
- package/dist/index-Cu5i8zZY.d.ts.map +0 -1
- package/dist/list-selection-controller-CUo-Hhcm.js.map +0 -1
- package/dist/tree-B74zdI60.js.map +0 -1
- package/dist/vertical-list-CQSEVeOT.d.ts.map +0 -1
- 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":";;
|
|
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(`
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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(--
|
|
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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"}
|
package/dist/{list-selection-controller-CUo-Hhcm.js → list-selection-controller-DTFI40li.js}
RENAMED
|
@@ -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 {
|
|
262
|
+
export { isMultiMode as n, ListSelectionController as t };
|
|
337
263
|
|
|
338
|
-
//# sourceMappingURL=list-selection-controller-
|
|
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-
|
|
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
|
package/dist/miller.d.ts.map
CHANGED
|
@@ -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":"
|
|
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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"}
|