@xsynaptic/sonic-ui 0.2.0 → 0.4.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/README.md +2 -2
- package/dist/default/define/number.js +7 -0
- package/dist/default/define.js +1 -0
- package/dist/default/elements/dial.js +24 -10
- package/dist/default/elements/key.js +29 -4
- package/dist/default/elements/meter.js +63 -9
- package/dist/default/elements/number.js +46 -0
- package/dist/default/elements/range-element.js +172 -36
- package/dist/default/elements/slider.js +63 -3
- package/dist/default/index.js +2 -1
- package/dist/default/lib/detent.js +17 -0
- package/dist/default/lib/math.js +16 -0
- package/dist/default/lib/number-list.js +14 -0
- package/dist/default/lib/taper.js +17 -4
- package/dist/dev/define/number.d.ts +2 -0
- package/dist/dev/define/number.js +8 -0
- package/dist/dev/define/number.js.map +1 -0
- package/dist/dev/define.d.ts +3 -1
- package/dist/dev/define.js +1 -0
- package/dist/dev/elements/dial.d.ts +3 -0
- package/dist/dev/elements/dial.js +24 -10
- package/dist/dev/elements/dial.js.map +1 -1
- package/dist/dev/elements/key.d.ts +4 -0
- package/dist/dev/elements/key.js +29 -4
- package/dist/dev/elements/key.js.map +1 -1
- package/dist/dev/elements/meter.d.ts +6 -0
- package/dist/dev/elements/meter.js +63 -9
- package/dist/dev/elements/meter.js.map +1 -1
- package/dist/dev/elements/number.d.ts +16 -0
- package/dist/dev/elements/number.js +47 -0
- package/dist/dev/elements/number.js.map +1 -0
- package/dist/dev/elements/range-element.d.ts +12 -0
- package/dist/dev/elements/range-element.js +172 -36
- package/dist/dev/elements/range-element.js.map +1 -1
- package/dist/dev/elements/slider.d.ts +8 -0
- package/dist/dev/elements/slider.js +63 -3
- package/dist/dev/elements/slider.js.map +1 -1
- package/dist/dev/index.d.ts +2 -1
- package/dist/dev/index.js +2 -1
- package/dist/dev/lib/detent.js +18 -0
- package/dist/dev/lib/detent.js.map +1 -0
- package/dist/dev/lib/math.js +17 -0
- package/dist/dev/lib/math.js.map +1 -0
- package/dist/dev/lib/number-list.js +15 -0
- package/dist/dev/lib/number-list.js.map +1 -0
- package/dist/dev/lib/read-px-property.js.map +1 -1
- package/dist/dev/lib/taper.js +17 -4
- package/dist/dev/lib/taper.js.map +1 -1
- package/dist/styles/controls.css +2 -0
- package/dist/styles/dial.css +80 -6
- package/dist/styles/key.css +96 -11
- package/dist/styles/led.css +82 -0
- package/dist/styles/material.css +150 -20
- package/dist/styles/meter.css +120 -8
- package/dist/styles/number.css +81 -0
- package/dist/styles/screen.css +6 -39
- package/dist/styles/segmented.css +23 -7
- package/dist/styles/slider.css +143 -30
- package/package.json +4 -2
|
@@ -2,9 +2,15 @@ import { requireChild } from "../lib/render.js";
|
|
|
2
2
|
import { writeAttribute } from "../lib/write-attribute.js";
|
|
3
3
|
import { SonicFormElement } from "./form-element.js";
|
|
4
4
|
import { RangeEntry } from "./range-entry.js";
|
|
5
|
-
import {
|
|
5
|
+
import { copyNode } from "../lib/copy-node.js";
|
|
6
|
+
import { passDetent } from "../lib/detent.js";
|
|
7
|
+
import { clamp, clampUnit, trimFloat, wrapUnit } from "../lib/math.js";
|
|
8
|
+
import { nearestEntry, parseNumberList } from "../lib/number-list.js";
|
|
9
|
+
import { readPxProperty } from "../lib/read-px-property.js";
|
|
10
|
+
import { linearTaper, listTaper, logTaper, skewTaper } from "../lib/taper.js";
|
|
6
11
|
|
|
7
12
|
//#region src/elements/range-element.ts
|
|
13
|
+
const detentZonePx = 8;
|
|
8
14
|
const fineScale = .1;
|
|
9
15
|
const keyRevealMs = 1e3;
|
|
10
16
|
const pageSteps = 10;
|
|
@@ -13,6 +19,7 @@ const revealPx = 4;
|
|
|
13
19
|
const canPopover = "togglePopover" in HTMLElement.prototype;
|
|
14
20
|
let instanceCount = 0;
|
|
15
21
|
const resetKeys = /* @__PURE__ */ new Set(["Backspace", "Delete"]);
|
|
22
|
+
const endKeys = /* @__PURE__ */ new Set(["End", "Home"]);
|
|
16
23
|
const keySteps = /* @__PURE__ */ new Map([
|
|
17
24
|
["ArrowDown", -1],
|
|
18
25
|
["ArrowLeft", -1],
|
|
@@ -21,7 +28,7 @@ const keySteps = /* @__PURE__ */ new Map([
|
|
|
21
28
|
["PageDown", -10],
|
|
22
29
|
["PageUp", pageSteps]
|
|
23
30
|
]);
|
|
24
|
-
const
|
|
31
|
+
const roleAttributes = [
|
|
25
32
|
"aria-disabled",
|
|
26
33
|
"aria-orientation",
|
|
27
34
|
"aria-valuemax",
|
|
@@ -31,9 +38,22 @@ const sliderAttributes = [
|
|
|
31
38
|
"role",
|
|
32
39
|
"tabindex"
|
|
33
40
|
];
|
|
41
|
+
function scaleValue(mark) {
|
|
42
|
+
const text = mark.dataset.sonicValue?.trim();
|
|
43
|
+
return text ? Number(text) : NaN;
|
|
44
|
+
}
|
|
45
|
+
function scaleMark(original) {
|
|
46
|
+
const mark = copyNode(original);
|
|
47
|
+
if (!(mark instanceof HTMLElement || mark instanceof SVGElement)) return [];
|
|
48
|
+
if (!Number.isFinite(scaleValue(mark))) return [];
|
|
49
|
+
const isTick = mark.childElementCount === 0 && mark.textContent.trim() === "";
|
|
50
|
+
mark.classList.add(isTick ? "sonic-scale-tick" : "sonic-scale-label");
|
|
51
|
+
return [mark];
|
|
52
|
+
}
|
|
34
53
|
var SonicRangeElement = class extends SonicFormElement {
|
|
35
54
|
static observedAttributes = [
|
|
36
55
|
...SonicFormElement.observedAttributes,
|
|
56
|
+
"detent",
|
|
37
57
|
"max",
|
|
38
58
|
"midpoint",
|
|
39
59
|
"min",
|
|
@@ -42,7 +62,8 @@ var SonicRangeElement = class extends SonicFormElement {
|
|
|
42
62
|
"readout",
|
|
43
63
|
"step",
|
|
44
64
|
"taper",
|
|
45
|
-
"value"
|
|
65
|
+
"value",
|
|
66
|
+
"values"
|
|
46
67
|
];
|
|
47
68
|
get default() {
|
|
48
69
|
return this.optionalNumberAttribute("default");
|
|
@@ -50,6 +71,12 @@ var SonicRangeElement = class extends SonicFormElement {
|
|
|
50
71
|
set default(value) {
|
|
51
72
|
this.reflect("default", value);
|
|
52
73
|
}
|
|
74
|
+
get detent() {
|
|
75
|
+
return this.optionalNumberAttribute("detent");
|
|
76
|
+
}
|
|
77
|
+
set detent(value) {
|
|
78
|
+
this.reflect("detent", value);
|
|
79
|
+
}
|
|
53
80
|
get dimmed() {
|
|
54
81
|
return this.hasAttribute("dimmed");
|
|
55
82
|
}
|
|
@@ -130,7 +157,14 @@ var SonicRangeElement = class extends SonicFormElement {
|
|
|
130
157
|
if (this.#drag) return;
|
|
131
158
|
this.#write(next);
|
|
132
159
|
}
|
|
160
|
+
get values() {
|
|
161
|
+
return parseNumberList(this.getAttribute("values"));
|
|
162
|
+
}
|
|
163
|
+
set values(entries) {
|
|
164
|
+
this.reflect("values", entries?.join(" "));
|
|
165
|
+
}
|
|
133
166
|
#drag;
|
|
167
|
+
#entries;
|
|
134
168
|
#entry;
|
|
135
169
|
#formatValue;
|
|
136
170
|
#instance = String(instanceCount += 1);
|
|
@@ -138,8 +172,10 @@ var SonicRangeElement = class extends SonicFormElement {
|
|
|
138
172
|
#keyRevealTimer;
|
|
139
173
|
#parseValue;
|
|
140
174
|
#readout;
|
|
175
|
+
#scale;
|
|
141
176
|
#value = 0;
|
|
142
177
|
attributeChangedCallback(name) {
|
|
178
|
+
this.#entries = parseNumberList(this.getAttribute("values"));
|
|
143
179
|
if (name === "value") {
|
|
144
180
|
if (this.#drag) return;
|
|
145
181
|
this.#isDirty = false;
|
|
@@ -152,7 +188,7 @@ var SonicRangeElement = class extends SonicFormElement {
|
|
|
152
188
|
this.render();
|
|
153
189
|
}
|
|
154
190
|
connectedCallback() {
|
|
155
|
-
this.upgradeProperties("default", "dimmed", "doublePress", "max", "midpoint", "min", "notched", "origin", "readout", "step", "taper", "formatValue", "parseValue", "value");
|
|
191
|
+
this.upgradeProperties("default", "dimmed", "doublePress", "max", "midpoint", "min", "notched", "origin", "readout", "step", "taper", "values", "detent", "formatValue", "parseValue", "value");
|
|
156
192
|
super.connectedCallback();
|
|
157
193
|
}
|
|
158
194
|
bindGestures(control, signal, grab) {
|
|
@@ -170,6 +206,7 @@ var SonicRangeElement = class extends SonicFormElement {
|
|
|
170
206
|
if (!axis) return;
|
|
171
207
|
const startPosition = axis.position(event);
|
|
172
208
|
const drag = {
|
|
209
|
+
detent: this.#detentHold(control, axis.travelPx),
|
|
173
210
|
fromValue,
|
|
174
211
|
isRevealed: false,
|
|
175
212
|
lastPosition: startPosition,
|
|
@@ -192,10 +229,10 @@ var SonicRangeElement = class extends SonicFormElement {
|
|
|
192
229
|
if (event.pointerId !== drag?.pointerId) return;
|
|
193
230
|
const position = drag.position(event);
|
|
194
231
|
const scale = event.shiftKey ? fineScale : 1;
|
|
195
|
-
|
|
232
|
+
const next = this.#move(drag, (position - drag.lastPosition) / drag.travelPx * scale);
|
|
196
233
|
drag.lastPosition = position;
|
|
197
234
|
if (Math.abs(position - drag.startPosition) >= revealPx) this.#reveal(drag);
|
|
198
|
-
this.input(
|
|
235
|
+
this.input(next);
|
|
199
236
|
}, { signal });
|
|
200
237
|
const endDrag = (event) => {
|
|
201
238
|
if (event.pointerId === this.#drag?.pointerId) this.#endDrag();
|
|
@@ -208,7 +245,7 @@ var SonicRangeElement = class extends SonicFormElement {
|
|
|
208
245
|
control.addEventListener("pointerup", (event) => {
|
|
209
246
|
if (event.pointerId !== this.#drag?.pointerId) return;
|
|
210
247
|
this.#endDrag();
|
|
211
|
-
if (!entry.press(event)) return;
|
|
248
|
+
if (!entry.press(event) || this.springTarget() !== void 0) return;
|
|
212
249
|
if (this.doublePress === "reset") this.#reset();
|
|
213
250
|
else entry.open();
|
|
214
251
|
}, { signal });
|
|
@@ -218,8 +255,27 @@ var SonicRangeElement = class extends SonicFormElement {
|
|
|
218
255
|
this.#hideForKeys();
|
|
219
256
|
}, { once: true });
|
|
220
257
|
}
|
|
258
|
+
bindScale(control, scale, signal) {
|
|
259
|
+
this.#scale = scale;
|
|
260
|
+
this.mirrorChildren({
|
|
261
|
+
control,
|
|
262
|
+
copy: (originals) => {
|
|
263
|
+
scale.replaceChildren(...originals.flatMap((original) => scaleMark(original)));
|
|
264
|
+
this.renderScale();
|
|
265
|
+
},
|
|
266
|
+
isCopied: (child) => child instanceof Element && child.matches("[data-sonic-value]")
|
|
267
|
+
}, signal);
|
|
268
|
+
}
|
|
221
269
|
clampRange(next) {
|
|
222
|
-
|
|
270
|
+
const min = this.min;
|
|
271
|
+
if (!this.wraps()) return clamp(next, min, this.#max());
|
|
272
|
+
const range = this.range();
|
|
273
|
+
if (range <= 0) return min;
|
|
274
|
+
const offset = trimFloat(((next - min) % range + range) % range);
|
|
275
|
+
return offset >= range ? min : trimFloat(min + offset);
|
|
276
|
+
}
|
|
277
|
+
controlRole() {
|
|
278
|
+
return "slider";
|
|
223
279
|
}
|
|
224
280
|
fraction(value) {
|
|
225
281
|
return (this.#taper() ?? linearTaper(this.min, this.#max())).position(value);
|
|
@@ -234,11 +290,16 @@ var SonicRangeElement = class extends SonicFormElement {
|
|
|
234
290
|
}));
|
|
235
291
|
return true;
|
|
236
292
|
}
|
|
293
|
+
modulationFractions(amount) {
|
|
294
|
+
const value = this.fraction(this.#value);
|
|
295
|
+
const modulated = this.fraction(this.#value + amount);
|
|
296
|
+
return [Math.min(value, modulated), Math.max(value, modulated)];
|
|
297
|
+
}
|
|
237
298
|
originFraction() {
|
|
238
|
-
return this.fraction(this.
|
|
299
|
+
return this.fraction(this.restValue());
|
|
239
300
|
}
|
|
240
301
|
positions() {
|
|
241
|
-
const positions = Math.round(this.range() / this.step) + 1;
|
|
302
|
+
const positions = this.#entries?.length ?? Math.round(this.range() / this.step) + (this.wraps() ? 0 : 1);
|
|
242
303
|
if (!this.notched || !Number.isFinite(positions) || positions < 2) return;
|
|
243
304
|
return positions;
|
|
244
305
|
}
|
|
@@ -248,14 +309,15 @@ var SonicRangeElement = class extends SonicFormElement {
|
|
|
248
309
|
renderAria(control, orientation) {
|
|
249
310
|
this.writeFormValue(String(this.#value), String(this.#value));
|
|
250
311
|
if (this.#entry?.isOpen) {
|
|
251
|
-
for (const name of
|
|
312
|
+
for (const name of roleAttributes) control.removeAttribute(name);
|
|
252
313
|
this.forwardNaming(control, false);
|
|
253
314
|
this.#renderReadout();
|
|
254
315
|
return;
|
|
255
316
|
}
|
|
256
|
-
|
|
257
|
-
control.setAttribute("
|
|
258
|
-
control.setAttribute("aria-
|
|
317
|
+
const [low, high] = this.#bounds();
|
|
318
|
+
control.setAttribute("role", this.controlRole());
|
|
319
|
+
control.setAttribute("aria-valuemin", String(low));
|
|
320
|
+
control.setAttribute("aria-valuemax", String(high));
|
|
259
321
|
control.setAttribute("aria-valuenow", String(this.#value));
|
|
260
322
|
if (orientation) control.setAttribute("aria-orientation", orientation);
|
|
261
323
|
writeAttribute(control, "aria-valuetext", this.#formatValue?.(this.#value));
|
|
@@ -263,28 +325,48 @@ var SonicRangeElement = class extends SonicFormElement {
|
|
|
263
325
|
this.#renderDisabled(control);
|
|
264
326
|
this.#renderReadout();
|
|
265
327
|
}
|
|
328
|
+
renderScale() {
|
|
329
|
+
const marks = this.#scale?.children ?? [];
|
|
330
|
+
for (const mark of marks) {
|
|
331
|
+
if (!(mark instanceof HTMLElement || mark instanceof SVGElement)) continue;
|
|
332
|
+
const at = String(this.fraction(this.clampRange(scaleValue(mark))));
|
|
333
|
+
if (mark.style.getPropertyValue("--_sonic-scale-at") !== at) mark.style.setProperty("--_sonic-scale-at", at);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
266
336
|
restoreState(state) {
|
|
267
337
|
this.value = Number(state);
|
|
268
338
|
}
|
|
339
|
+
restValue() {
|
|
340
|
+
return this.origin ?? this.#bounds()[0];
|
|
341
|
+
}
|
|
342
|
+
springTarget() {}
|
|
269
343
|
valueAt(fraction) {
|
|
270
344
|
return (this.#taper() ?? linearTaper(this.min, this.#max())).value(fraction);
|
|
271
345
|
}
|
|
346
|
+
valueText() {
|
|
347
|
+
return this.#formatValue?.(this.#value) ?? String(this.#value);
|
|
348
|
+
}
|
|
349
|
+
wraps() {
|
|
350
|
+
return false;
|
|
351
|
+
}
|
|
272
352
|
#bindEntry(control, signal) {
|
|
273
353
|
const input = requireChild(control, "input", HTMLInputElement);
|
|
274
|
-
const bubble =
|
|
275
|
-
const anchor = `--sonic-readout-${this.#instance}`;
|
|
354
|
+
const bubble = control.querySelector("[popover]");
|
|
276
355
|
input.id = `sonic-entry-${this.#instance}`;
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
356
|
+
if (bubble) {
|
|
357
|
+
const anchor = `--sonic-readout-${this.#instance}`;
|
|
358
|
+
this.#readout = {
|
|
359
|
+
bubble,
|
|
360
|
+
text: requireChild(bubble, "span", HTMLElement)
|
|
361
|
+
};
|
|
362
|
+
control.style.setProperty("anchor-name", anchor);
|
|
363
|
+
bubble.style.setProperty("position-anchor", anchor);
|
|
364
|
+
}
|
|
283
365
|
const entry = new RangeEntry(input, {
|
|
284
366
|
commit: (text) => {
|
|
285
367
|
this.#commit((this.#parseValue ?? Number.parseFloat)(text));
|
|
286
368
|
},
|
|
287
|
-
text: () => this.#parseValue ? this
|
|
369
|
+
text: () => this.#parseValue ? this.valueText() : String(this.#value),
|
|
288
370
|
toggle: (isOpen) => {
|
|
289
371
|
if (isOpen) this.forwardNaming(input, true);
|
|
290
372
|
this.toggleState("editing", isOpen);
|
|
@@ -299,6 +381,7 @@ var SonicRangeElement = class extends SonicFormElement {
|
|
|
299
381
|
control.addEventListener("keydown", (event) => {
|
|
300
382
|
if (this.isDisabled() || event.defaultPrevented || event.target !== control) return;
|
|
301
383
|
if (event.key === "Enter") {
|
|
384
|
+
if (this.springTarget() !== void 0) return;
|
|
302
385
|
event.preventDefault();
|
|
303
386
|
entry.open();
|
|
304
387
|
return;
|
|
@@ -309,19 +392,45 @@ var SonicRangeElement = class extends SonicFormElement {
|
|
|
309
392
|
this.#commit(next);
|
|
310
393
|
this.#revealForKeys();
|
|
311
394
|
}, { signal });
|
|
395
|
+
control.addEventListener("keyup", (event) => {
|
|
396
|
+
if (keySteps.has(event.key) || endKeys.has(event.key)) this.#springBack();
|
|
397
|
+
}, { signal });
|
|
312
398
|
control.addEventListener("blur", () => {
|
|
313
399
|
this.#hideForKeys();
|
|
400
|
+
this.#springBack();
|
|
314
401
|
}, { signal });
|
|
315
402
|
}
|
|
403
|
+
#bounds() {
|
|
404
|
+
const entries = this.#entries;
|
|
405
|
+
return [entries?.[0] ?? this.min, entries?.at(-1) ?? this.#max()];
|
|
406
|
+
}
|
|
316
407
|
#clamp(next) {
|
|
408
|
+
const entries = this.#entries;
|
|
409
|
+
if (entries) return nearestEntry(entries, next);
|
|
317
410
|
const min = this.min;
|
|
318
411
|
const step = this.step;
|
|
319
412
|
const stepped = step > 0 ? min + Math.round((next - min) / step) * step : next;
|
|
320
|
-
return this.clampRange(
|
|
413
|
+
return this.clampRange(trimFloat(stepped));
|
|
321
414
|
}
|
|
322
415
|
#commit(next) {
|
|
323
416
|
if (this.input(next)) this.dispatchEvent(new Event("change", { bubbles: true }));
|
|
324
417
|
}
|
|
418
|
+
#detentHold(control, travelPx) {
|
|
419
|
+
const detent = this.detent;
|
|
420
|
+
if (detent === void 0) return void 0;
|
|
421
|
+
const value = this.#clamp(detent);
|
|
422
|
+
const zonePx = readPxProperty(getComputedStyle(control), "--_sonic-detent-zone", detentZonePx);
|
|
423
|
+
return {
|
|
424
|
+
place: this.fraction(value),
|
|
425
|
+
slack: value === this.#value ? 0 : void 0,
|
|
426
|
+
value,
|
|
427
|
+
zone: zonePx / travelPx
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
#end(step) {
|
|
431
|
+
if (!this.wraps()) return this.#bounds()[1];
|
|
432
|
+
return this.min + (Math.ceil(this.range() / step) - 1) * step;
|
|
433
|
+
}
|
|
325
434
|
#endDrag() {
|
|
326
435
|
const drag = this.#drag;
|
|
327
436
|
if (!drag) return;
|
|
@@ -330,6 +439,7 @@ var SonicRangeElement = class extends SonicFormElement {
|
|
|
330
439
|
this.toggleState("dragging", false);
|
|
331
440
|
this.#renderReadout();
|
|
332
441
|
if (this.#value !== drag.fromValue) this.dispatchEvent(new Event("change", { bubbles: true }));
|
|
442
|
+
this.#springBack();
|
|
333
443
|
}
|
|
334
444
|
#hideForKeys() {
|
|
335
445
|
clearTimeout(this.#keyRevealTimer);
|
|
@@ -341,20 +451,29 @@ var SonicRangeElement = class extends SonicFormElement {
|
|
|
341
451
|
return (this.#drag?.isRevealed === true || this.#keyRevealTimer !== void 0) && this.readout;
|
|
342
452
|
}
|
|
343
453
|
#keyTarget(key) {
|
|
344
|
-
if (key === "Home") return this
|
|
345
|
-
|
|
454
|
+
if (key === "Home") return this.#bounds()[0];
|
|
455
|
+
const step = this.step > 0 ? this.step : this.range() / 100;
|
|
456
|
+
if (key === "End") return this.#end(step);
|
|
346
457
|
if (resetKeys.has(key)) return this.default;
|
|
347
458
|
const steps = keySteps.get(key);
|
|
348
459
|
if (steps === void 0) return void 0;
|
|
349
|
-
|
|
350
|
-
const taper = this.#taper();
|
|
351
|
-
if (!taper) return this.#value + steps * step;
|
|
352
|
-
const next = this.#clamp(taper.value(taper.position(this.#value) + steps / 100));
|
|
353
|
-
return next === this.#value ? this.#value + Math.sign(steps) * step : next;
|
|
460
|
+
return this.#stopAtDetent(this.#stepBy(steps, step));
|
|
354
461
|
}
|
|
355
462
|
#max() {
|
|
356
463
|
return Math.max(this.min, this.max);
|
|
357
464
|
}
|
|
465
|
+
#move(drag, delta) {
|
|
466
|
+
const { detent } = drag;
|
|
467
|
+
const to = drag.rawFraction + delta;
|
|
468
|
+
const next = detent ? passDetent(detent, drag.rawFraction, to) : to;
|
|
469
|
+
if (detent && next === void 0) {
|
|
470
|
+
drag.rawFraction = detent.place;
|
|
471
|
+
return detent.value;
|
|
472
|
+
}
|
|
473
|
+
const place = next ?? to;
|
|
474
|
+
drag.rawFraction = this.wraps() ? wrapUnit(place) : clampUnit(place);
|
|
475
|
+
return this.valueAt(drag.rawFraction);
|
|
476
|
+
}
|
|
358
477
|
#renderDisabled(control) {
|
|
359
478
|
if (this.isDisabled()) {
|
|
360
479
|
control.setAttribute("aria-disabled", "true");
|
|
@@ -369,7 +488,7 @@ var SonicRangeElement = class extends SonicFormElement {
|
|
|
369
488
|
if (!readout || !canPopover || !this.isConnected) return;
|
|
370
489
|
const isEditing = this.#entry?.isOpen === true;
|
|
371
490
|
readout.text.hidden = isEditing;
|
|
372
|
-
if (!isEditing) readout.text.textContent = this
|
|
491
|
+
if (!isEditing) readout.text.textContent = this.valueText();
|
|
373
492
|
readout.bubble.togglePopover(isEditing || this.#isValueShown());
|
|
374
493
|
}
|
|
375
494
|
#reset() {
|
|
@@ -387,15 +506,32 @@ var SonicRangeElement = class extends SonicFormElement {
|
|
|
387
506
|
}, keyRevealMs);
|
|
388
507
|
this.#renderReadout();
|
|
389
508
|
}
|
|
509
|
+
#springBack() {
|
|
510
|
+
const target = this.springTarget();
|
|
511
|
+
if (target !== void 0) this.#commit(target);
|
|
512
|
+
}
|
|
513
|
+
#stepBy(steps, step) {
|
|
514
|
+
const entries = this.#entries;
|
|
515
|
+
if (entries) return entries[clamp(entries.indexOf(this.#value) + steps, 0, entries.length - 1)] ?? this.#value;
|
|
516
|
+
const taper = this.#taper();
|
|
517
|
+
if (!taper) return this.#value + steps * step;
|
|
518
|
+
const next = this.#clamp(taper.value(taper.position(this.#value) + steps / 100));
|
|
519
|
+
return next === this.#value ? this.#value + Math.sign(steps) * step : next;
|
|
520
|
+
}
|
|
521
|
+
#stopAtDetent(next) {
|
|
522
|
+
const detent = this.detent;
|
|
523
|
+
if (detent === void 0) return next;
|
|
524
|
+
const value = this.#clamp(detent);
|
|
525
|
+
return (this.#value - value) * (next - value) < 0 ? value : next;
|
|
526
|
+
}
|
|
390
527
|
#taper() {
|
|
391
|
-
|
|
528
|
+
const entries = this.#entries;
|
|
529
|
+
if (entries) return listTaper(entries);
|
|
530
|
+
if (this.notched || this.wraps()) return void 0;
|
|
392
531
|
const min = this.min;
|
|
393
532
|
const max = this.#max();
|
|
394
533
|
return (this.taper === "log" ? logTaper(min, max) : void 0) ?? skewTaper(min, max, this.midpoint ?? NaN);
|
|
395
534
|
}
|
|
396
|
-
#valueText() {
|
|
397
|
-
return this.#formatValue?.(this.#value) ?? String(this.#value);
|
|
398
|
-
}
|
|
399
535
|
#write(next) {
|
|
400
536
|
if (!Number.isFinite(next)) return;
|
|
401
537
|
this.#isDirty = true;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"range-element.js","names":[],"sources":["../../../src/elements/range-element.ts"],"sourcesContent":["import type { Taper } from '#lib/taper.ts';\n\nimport { SonicFormElement } from '#elements/form-element.ts';\nimport { RangeEntry } from '#elements/range-entry.ts';\nimport { requireChild } from '#lib/render.ts';\nimport { clampUnit, linearTaper, logTaper, skewTaper } from '#lib/taper.ts';\nimport { writeAttribute } from '#lib/write-attribute.ts';\n\n// `position` grows with the value\nexport interface RangeAxis {\n\tposition: (event: PointerEvent) => number;\n\ttravelPx: number;\n}\n\ninterface RangeDrag extends RangeAxis {\n\tfromValue: number;\n\tisRevealed: boolean;\n\tlastPosition: number;\n\tpointerId: number;\n\t// Unstepped, or a slow drag rounds back to where it started\n\trawFraction: number;\n\trevealTimer: ReturnType<typeof setTimeout> | undefined;\n\tstartPosition: number;\n}\n\nconst fineScale = 0.1;\nconst keyRevealMs = 1000;\nconst pageSteps = 10;\nconst revealMs = 250;\nconst revealPx = 4;\n\n// happy-dom has no popover API\nconst canPopover = 'togglePopover' in HTMLElement.prototype;\n\n// A shared anchor name resolves to the last one on the page\nlet instanceCount = 0;\n\n// A Mac keyboard's Delete sends Backspace\nconst resetKeys = new Set(['Backspace', 'Delete']);\n\nconst keySteps = new Map([\n\t['ArrowDown', -1],\n\t['ArrowLeft', -1],\n\t['ArrowRight', 1],\n\t['ArrowUp', 1],\n\t['PageDown', -pageSteps],\n\t['PageUp', pageSteps],\n]);\n\n// A slider's children are presentational, so these come off while the entry is open\nconst sliderAttributes = [\n\t'aria-disabled',\n\t'aria-orientation',\n\t'aria-valuemax',\n\t'aria-valuemin',\n\t'aria-valuenow',\n\t'aria-valuetext',\n\t'role',\n\t'tabindex',\n];\n\n// As on `<input type=\"range\">`, the property never writes the `value` attribute back\nexport abstract class SonicRangeElement extends SonicFormElement {\n\tstatic override readonly observedAttributes = [\n\t\t...SonicFormElement.observedAttributes,\n\t\t'max',\n\t\t'midpoint',\n\t\t'min',\n\t\t'notched',\n\t\t'origin',\n\t\t'readout',\n\t\t'step',\n\t\t'taper',\n\t\t'value',\n\t];\n\n\tget default(): number | undefined {\n\t\treturn this.optionalNumberAttribute('default');\n\t}\n\n\tset default(value: number | undefined) {\n\t\tthis.reflect('default', value);\n\t}\n\n\tget dimmed(): boolean {\n\t\treturn this.hasAttribute('dimmed');\n\t}\n\n\tset dimmed(isDimmed: boolean) {\n\t\tthis.reflect('dimmed', isDimmed);\n\t}\n\n\t// Named for the absent attribute, as `input.type` reads `text`\n\tget doublePress(): 'reset' | 'type' {\n\t\treturn this.getAttribute('double-press') === 'reset' ? 'reset' : 'type';\n\t}\n\n\tset doublePress(gesture: 'reset' | 'type' | undefined) {\n\t\tthis.reflect('double-press', gesture);\n\t}\n\n\tget formatValue(): ((value: number) => string) | undefined {\n\t\treturn this.#formatValue;\n\t}\n\n\tset formatValue(format: ((value: number) => string) | undefined) {\n\t\tthis.#formatValue = format;\n\t\tthis.render();\n\t}\n\n\t// Unclamped by `min`, so reading it back never pins the range\n\tget max(): number {\n\t\treturn this.numberAttribute('max', 100);\n\t}\n\n\tset max(value: number | undefined) {\n\t\tthis.reflect('max', value);\n\t}\n\n\tget midpoint(): number | undefined {\n\t\treturn this.optionalNumberAttribute('midpoint');\n\t}\n\n\tset midpoint(value: number | undefined) {\n\t\tthis.reflect('midpoint', value);\n\t}\n\n\tget min(): number {\n\t\treturn this.numberAttribute('min', 0);\n\t}\n\n\tset min(value: number | undefined) {\n\t\tthis.reflect('min', value);\n\t}\n\n\tget notched(): boolean {\n\t\treturn this.hasAttribute('notched');\n\t}\n\n\tset notched(isNotched: boolean) {\n\t\tthis.reflect('notched', isNotched);\n\t}\n\n\t// `undefined` while unset, so reading it back never pins it to `min`\n\tget origin(): number | undefined {\n\t\treturn this.optionalNumberAttribute('origin');\n\t}\n\n\tset origin(value: number | undefined) {\n\t\tthis.reflect('origin', value);\n\t}\n\n\tget parseValue(): ((text: string) => number) | undefined {\n\t\treturn this.#parseValue;\n\t}\n\n\tset parseValue(parse: ((text: string) => number) | undefined) {\n\t\tthis.#parseValue = parse;\n\t}\n\n\tget readout(): boolean {\n\t\treturn this.hasAttribute('readout');\n\t}\n\n\tset readout(isShown: boolean) {\n\t\tthis.reflect('readout', isShown);\n\t}\n\n\tget step(): number {\n\t\treturn this.numberAttribute('step', 1);\n\t}\n\n\tset step(value: number | undefined) {\n\t\tthis.reflect('step', value);\n\t}\n\n\tget taper(): 'linear' | 'log' {\n\t\treturn this.getAttribute('taper') === 'log' ? 'log' : 'linear';\n\t}\n\n\tset taper(curve: 'linear' | 'log' | undefined) {\n\t\tthis.reflect('taper', curve);\n\t}\n\n\tget value(): number {\n\t\treturn this.#value;\n\t}\n\n\t// A drag owns the value, so a timer's write can't pull the cap from under the pointer\n\tset value(next: number) {\n\t\tif (this.#drag) return;\n\n\t\tthis.#write(next);\n\t}\n\n\t#drag: RangeDrag | undefined;\n\n\t#entry: RangeEntry | undefined;\n\n\t#formatValue: ((value: number) => string) | undefined;\n\n\treadonly #instance = String((instanceCount += 1));\n\n\t// Re-read from the attribute until the property is set, since `max` may arrive after `value`\n\t#isDirty = false;\n\n\t#keyRevealTimer: ReturnType<typeof setTimeout> | undefined;\n\n\t#parseValue: ((text: string) => number) | undefined;\n\n\t#readout: undefined | { bubble: HTMLElement; text: HTMLElement };\n\n\t#value = 0;\n\n\tattributeChangedCallback(name: string): void {\n\t\tif (name === 'value') {\n\t\t\tif (this.#drag) return;\n\n\t\t\tthis.#isDirty = false;\n\t\t}\n\t\tif (name === 'disabled' && this.isDisabled()) {\n\t\t\tthis.#entry?.close(false);\n\t\t\tthis.#endDrag();\n\t\t}\n\n\t\tthis.#value = this.#clamp(\n\t\t\tthis.#isDirty ? this.#value : this.numberAttribute('value', this.min),\n\t\t);\n\t\tthis.render();\n\t}\n\n\t// The range before `value`, or a value set with its `max` before upgrade clamps to the old one\n\toverride connectedCallback(): void {\n\t\tthis.upgradeProperties(\n\t\t\t'default',\n\t\t\t'dimmed',\n\t\t\t'doublePress',\n\t\t\t'max',\n\t\t\t'midpoint',\n\t\t\t'min',\n\t\t\t'notched',\n\t\t\t'origin',\n\t\t\t'readout',\n\t\t\t'step',\n\t\t\t'taper',\n\t\t\t'formatValue',\n\t\t\t'parseValue',\n\t\t\t'value',\n\t\t);\n\t\tsuper.connectedCallback();\n\t}\n\n\tprotected bindGestures(\n\t\tcontrol: HTMLElement,\n\t\tsignal: AbortSignal,\n\t\tgrab: (event: PointerEvent) => RangeAxis | undefined,\n\t): void {\n\t\tconst entry = this.#bindEntry(control, signal);\n\n\t\tcontrol.addEventListener(\n\t\t\t'pointerdown',\n\t\t\t(event) => {\n\t\t\t\tif (event.button !== 0 || this.isDisabled() || entry.isOpen) return;\n\n\t\t\t\t// A click listener above makes WebKit send a tap's compatibility mousedown, which blurs the entry a double tap just opened\n\t\t\t\tif (event.pointerType === 'touch') event.preventDefault();\n\t\t\t\tcontrol.focus();\n\t\t\t\tif (event.metaKey || event.ctrlKey) {\n\t\t\t\t\tthis.#reset();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst fromValue = this.#value;\n\t\t\t\tconst axis = grab(event);\n\t\t\t\tif (!axis) return;\n\n\t\t\t\tconst startPosition = axis.position(event);\n\t\t\t\tconst drag: RangeDrag = {\n\t\t\t\t\tfromValue,\n\t\t\t\t\tisRevealed: false,\n\t\t\t\t\tlastPosition: startPosition,\n\t\t\t\t\tpointerId: event.pointerId,\n\t\t\t\t\tposition: axis.position,\n\t\t\t\t\trawFraction: this.fraction(this.#value),\n\t\t\t\t\trevealTimer: undefined,\n\t\t\t\t\tstartPosition,\n\t\t\t\t\ttravelPx: axis.travelPx,\n\t\t\t\t};\n\n\t\t\t\tdrag.revealTimer = setTimeout(() => {\n\t\t\t\t\tthis.#reveal(drag);\n\t\t\t\t}, revealMs);\n\t\t\t\tthis.#drag = drag;\n\t\t\t\tcontrol.setPointerCapture(event.pointerId);\n\t\t\t\tthis.toggleState('dragging', true);\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t\tcontrol.addEventListener(\n\t\t\t'pointermove',\n\t\t\t(event) => {\n\t\t\t\tconst drag = this.#drag;\n\t\t\t\tif (event.pointerId !== drag?.pointerId) return;\n\n\t\t\t\tconst position = drag.position(event);\n\t\t\t\tconst scale = event.shiftKey ? fineScale : 1;\n\n\t\t\t\t// Per move, so pressing Shift mid-drag changes pace without a jump\n\t\t\t\tdrag.rawFraction = clampUnit(\n\t\t\t\t\tdrag.rawFraction + ((position - drag.lastPosition) / drag.travelPx) * scale,\n\t\t\t\t);\n\t\t\t\tdrag.lastPosition = position;\n\t\t\t\tif (Math.abs(position - drag.startPosition) >= revealPx) this.#reveal(drag);\n\t\t\t\tthis.input(this.valueAt(drag.rawFraction));\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\n\t\tconst endDrag = (event: PointerEvent): void => {\n\t\t\tif (event.pointerId === this.#drag?.pointerId) this.#endDrag();\n\t\t};\n\n\t\tcontrol.addEventListener(\n\t\t\t'pointercancel',\n\t\t\t(event) => {\n\t\t\t\tendDrag(event);\n\t\t\t\tentry.forgetPress();\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t\t// Other code can release capture without a `pointerup`\n\t\tcontrol.addEventListener('lostpointercapture', endDrag, { signal });\n\t\tcontrol.addEventListener(\n\t\t\t'pointerup',\n\t\t\t(event) => {\n\t\t\t\tif (event.pointerId !== this.#drag?.pointerId) return;\n\n\t\t\t\tthis.#endDrag();\n\t\t\t\tif (!entry.press(event)) return;\n\n\t\t\t\tif (this.doublePress === 'reset') this.#reset();\n\t\t\t\telse entry.open();\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t\tthis.#bindKeys(control, entry, signal);\n\t\tsignal.addEventListener(\n\t\t\t'abort',\n\t\t\t() => {\n\t\t\t\tthis.#endDrag();\n\t\t\t\tthis.#hideForKeys();\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\t}\n\n\tprotected clampRange(next: number): number {\n\t\treturn Math.min(this.#max(), Math.max(this.min, next));\n\t}\n\n\tprotected fraction(value: number): number {\n\t\treturn (this.#taper() ?? linearTaper(this.min, this.#max())).position(value);\n\t}\n\n\tprotected input(next: number): boolean {\n\t\tconst previous = this.#value;\n\n\t\tthis.#write(next);\n\t\tif (this.#value === previous) return false;\n\n\t\tthis.dispatchEvent(new Event('input', { bubbles: true, composed: true }));\n\n\t\treturn true;\n\t}\n\n\tprotected originFraction(): number {\n\t\treturn this.fraction(this.origin ?? this.min);\n\t}\n\n\tprotected positions(): number | undefined {\n\t\tconst positions = Math.round(this.range() / this.step) + 1;\n\n\t\tif (!this.notched || !Number.isFinite(positions) || positions < 2) return;\n\n\t\treturn positions;\n\t}\n\n\tprotected range(): number {\n\t\treturn this.#max() - this.min;\n\t}\n\n\t// Per render rather than per write, so a range with no `value` attribute submits its minimum too\n\tprotected renderAria(control: HTMLElement, orientation?: 'horizontal' | 'vertical'): void {\n\t\tthis.writeFormValue(String(this.#value), String(this.#value));\n\t\tif (this.#entry?.isOpen) {\n\t\t\tfor (const name of sliderAttributes) control.removeAttribute(name);\n\t\t\tthis.forwardNaming(control, false);\n\t\t\tthis.#renderReadout();\n\t\t\treturn;\n\t\t}\n\n\t\tcontrol.setAttribute('role', 'slider');\n\t\tcontrol.setAttribute('aria-valuemin', String(this.min));\n\t\tcontrol.setAttribute('aria-valuemax', String(this.#max()));\n\t\tcontrol.setAttribute('aria-valuenow', String(this.#value));\n\t\tif (orientation) control.setAttribute('aria-orientation', orientation);\n\t\twriteAttribute(control, 'aria-valuetext', this.#formatValue?.(this.#value));\n\t\tthis.forwardNaming(control, true);\n\t\tthis.#renderDisabled(control);\n\t\tthis.#renderReadout();\n\t}\n\n\tprotected restoreState(state: string): void {\n\t\tthis.value = Number(state);\n\t}\n\n\tprotected valueAt(fraction: number): number {\n\t\treturn (this.#taper() ?? linearTaper(this.min, this.#max())).value(fraction);\n\t}\n\n\t#bindEntry(control: HTMLElement, signal: AbortSignal): RangeEntry {\n\t\tconst input = requireChild(control, 'input', HTMLInputElement);\n\t\tconst bubble = requireChild(control, '[popover]', HTMLElement);\n\n\t\tconst anchor = `--sonic-readout-${this.#instance}`;\n\n\t\t// Chrome's issues panel flags a form field with no id or name; a `name` would submit it\n\t\tinput.id = `sonic-entry-${this.#instance}`;\n\t\tthis.#readout = { bubble, text: requireChild(bubble, 'span', HTMLElement) };\n\t\tcontrol.style.setProperty('anchor-name', anchor);\n\t\tbubble.style.setProperty('position-anchor', anchor);\n\n\t\tconst entry = new RangeEntry(input, {\n\t\t\tcommit: (text) => {\n\t\t\t\t// eslint-disable-next-line unicorn/prefer-number-coercion -- `Number('')` is 0; an emptied field should leave the value alone\n\t\t\t\tthis.#commit((this.#parseValue ?? Number.parseFloat)(text));\n\t\t\t},\n\t\t\t// `Number.parseFloat` reads \"5 kHz\" as 5\n\t\t\ttext: () => (this.#parseValue ? this.#valueText() : String(this.#value)),\n\t\t\ttoggle: (isOpen) => {\n\t\t\t\tif (isOpen) this.forwardNaming(input, true);\n\t\t\t\tthis.toggleState('editing', isOpen);\n\t\t\t\tthis.render();\n\t\t\t},\n\t\t});\n\n\t\tthis.#entry = entry;\n\t\tentry.bind(control, signal);\n\n\t\treturn entry;\n\t}\n\n\t#bindKeys(control: HTMLElement, entry: RangeEntry, signal: AbortSignal): void {\n\t\tcontrol.addEventListener(\n\t\t\t'keydown',\n\t\t\t(event) => {\n\t\t\t\tif (this.isDisabled() || event.defaultPrevented || event.target !== control) return;\n\n\t\t\t\tif (event.key === 'Enter') {\n\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\tentry.open();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst next = this.#keyTarget(event.key);\n\t\t\t\tif (next === undefined) return;\n\n\t\t\t\tevent.preventDefault();\n\t\t\t\tthis.#commit(next);\n\t\t\t\tthis.#revealForKeys();\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t\tcontrol.addEventListener(\n\t\t\t'blur',\n\t\t\t() => {\n\t\t\t\tthis.#hideForKeys();\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t}\n\n\t#clamp(next: number): number {\n\t\tconst min = this.min;\n\t\tconst step = this.step;\n\t\tconst stepped = step > 0 ? min + Math.round((next - min) / step) * step : next;\n\n\t\t// A step like 0.01 leaves float residue\n\t\treturn this.clampRange(Number(stepped.toPrecision(12)));\n\t}\n\n\t#commit(next: number): void {\n\t\tif (this.input(next)) this.dispatchEvent(new Event('change', { bubbles: true }));\n\t}\n\n\t#endDrag(): void {\n\t\tconst drag = this.#drag;\n\t\tif (!drag) return;\n\n\t\tclearTimeout(drag.revealTimer);\n\t\tthis.#drag = undefined;\n\t\tthis.toggleState('dragging', false);\n\t\tthis.#renderReadout();\n\t\tif (this.#value !== drag.fromValue) this.dispatchEvent(new Event('change', { bubbles: true }));\n\t}\n\n\t#hideForKeys(): void {\n\t\tclearTimeout(this.#keyRevealTimer);\n\t\tif (this.#keyRevealTimer === undefined) return;\n\n\t\tthis.#keyRevealTimer = undefined;\n\t\tthis.#renderReadout();\n\t}\n\n\t#isValueShown(): boolean {\n\t\tconst isRevealed = this.#drag?.isRevealed === true || this.#keyRevealTimer !== undefined;\n\n\t\treturn isRevealed && this.readout;\n\t}\n\n\t#keyTarget(key: string): number | undefined {\n\t\tif (key === 'Home') return this.min;\n\t\tif (key === 'End') return this.#max();\n\t\tif (resetKeys.has(key)) return this.default;\n\n\t\tconst steps = keySteps.get(key);\n\t\tif (steps === undefined) return undefined;\n\n\t\tconst step = this.step > 0 ? this.step : this.range() / 100;\n\t\tconst taper = this.#taper();\n\t\tif (!taper) return this.#value + steps * step;\n\n\t\t// By travel, since steps at the wide end would take forever; at least one step, or the snap holds it in place\n\t\tconst next = this.#clamp(taper.value(taper.position(this.#value) + steps / 100));\n\n\t\treturn next === this.#value ? this.#value + Math.sign(steps) * step : next;\n\t}\n\n\t#max(): number {\n\t\treturn Math.max(this.min, this.max);\n\t}\n\n\t#renderDisabled(control: HTMLElement): void {\n\t\tif (this.isDisabled()) {\n\t\t\tcontrol.setAttribute('aria-disabled', 'true');\n\t\t\tcontrol.removeAttribute('tabindex');\n\t\t\treturn;\n\t\t}\n\n\t\tcontrol.removeAttribute('aria-disabled');\n\t\tcontrol.setAttribute('tabindex', '0');\n\t}\n\n\t#renderReadout(): void {\n\t\tconst readout = this.#readout;\n\t\tif (!readout || !canPopover || !this.isConnected) return;\n\n\t\tconst isEditing = this.#entry?.isOpen === true;\n\n\t\treadout.text.hidden = isEditing;\n\t\tif (!isEditing) readout.text.textContent = this.#valueText();\n\t\treadout.bubble.togglePopover(isEditing || this.#isValueShown());\n\t}\n\n\t#reset(): void {\n\t\tthis.#commit(this.default ?? NaN);\n\t}\n\n\t#reveal(drag: RangeDrag): void {\n\t\tif (drag.isRevealed || this.#drag !== drag) return;\n\n\t\tdrag.isRevealed = true;\n\t\tthis.#renderReadout();\n\t}\n\n\t#revealForKeys(): void {\n\t\tclearTimeout(this.#keyRevealTimer);\n\t\tthis.#keyRevealTimer = setTimeout(() => {\n\t\t\tthis.#hideForKeys();\n\t\t}, keyRevealMs);\n\t\tthis.#renderReadout();\n\t}\n\n\t// Notches are evenly spaced, so a notched control stays linear\n\t#taper(): Taper | undefined {\n\t\tif (this.notched) return undefined;\n\n\t\tconst min = this.min;\n\t\tconst max = this.#max();\n\t\tconst log = this.taper === 'log' ? logTaper(min, max) : undefined;\n\n\t\treturn log ?? skewTaper(min, max, this.midpoint ?? NaN);\n\t}\n\n\t#valueText(): string {\n\t\treturn this.#formatValue?.(this.#value) ?? String(this.#value);\n\t}\n\n\t#write(next: number): void {\n\t\tif (!Number.isFinite(next)) return;\n\n\t\tthis.#isDirty = true;\n\n\t\tconst clamped = this.#clamp(next);\n\t\tif (clamped === this.#value) return;\n\n\t\tthis.#value = clamped;\n\t\tthis.render();\n\t}\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"range-element.js","names":[],"sources":["../../../src/elements/range-element.ts"],"sourcesContent":["import type { DetentHold } from '#lib/detent.ts';\nimport type { Taper } from '#lib/taper.ts';\n\nimport { SonicFormElement } from '#elements/form-element.ts';\nimport { RangeEntry } from '#elements/range-entry.ts';\nimport { copyNode } from '#lib/copy-node.ts';\nimport { passDetent } from '#lib/detent.ts';\nimport { clamp, clampUnit, trimFloat, wrapUnit } from '#lib/math.ts';\nimport { nearestEntry, parseNumberList } from '#lib/number-list.ts';\nimport { readPxProperty } from '#lib/read-px-property.ts';\nimport { requireChild } from '#lib/render.ts';\nimport { linearTaper, listTaper, logTaper, skewTaper } from '#lib/taper.ts';\nimport { writeAttribute } from '#lib/write-attribute.ts';\n\n// `position` grows with the value\nexport interface RangeAxis {\n\tposition: (event: PointerEvent) => number;\n\ttravelPx: number;\n}\n\ninterface RangeDrag extends RangeAxis {\n\tdetent: DetentHold | undefined;\n\tfromValue: number;\n\tisRevealed: boolean;\n\tlastPosition: number;\n\tpointerId: number;\n\t// Unstepped, or a slow drag rounds back to where it started\n\trawFraction: number;\n\trevealTimer: ReturnType<typeof setTimeout> | undefined;\n\tstartPosition: number;\n}\n\nconst detentZonePx = 8;\nconst fineScale = 0.1;\nconst keyRevealMs = 1000;\nconst pageSteps = 10;\nconst revealMs = 250;\nconst revealPx = 4;\n\n// happy-dom has no popover API\nconst canPopover = 'togglePopover' in HTMLElement.prototype;\n\n// A shared anchor name resolves to the last one on the page\nlet instanceCount = 0;\n\n// A Mac keyboard's Delete sends Backspace\nconst resetKeys = new Set(['Backspace', 'Delete']);\n\nconst endKeys = new Set(['End', 'Home']);\n\nconst keySteps = new Map([\n\t['ArrowDown', -1],\n\t['ArrowLeft', -1],\n\t['ArrowRight', 1],\n\t['ArrowUp', 1],\n\t['PageDown', -pageSteps],\n\t['PageUp', pageSteps],\n]);\n\n// A slider's children are presentational, so these come off while the entry is open\nconst roleAttributes = [\n\t'aria-disabled',\n\t'aria-orientation',\n\t'aria-valuemax',\n\t'aria-valuemin',\n\t'aria-valuenow',\n\t'aria-valuetext',\n\t'role',\n\t'tabindex',\n];\n\ntype ScaleMark = HTMLElement | SVGElement;\n\n// Blank is not zero, and a mark with no value has no place\nfunction scaleValue(mark: ScaleMark): number {\n\tconst text = mark.dataset.sonicValue?.trim();\n\n\treturn text ? Number(text) : NaN;\n}\n\nfunction scaleMark(original: ChildNode): Array<ScaleMark> {\n\tconst mark = copyNode(original);\n\tif (!(mark instanceof HTMLElement || mark instanceof SVGElement)) return [];\n\tif (!Number.isFinite(scaleValue(mark))) return [];\n\n\tconst isTick = mark.childElementCount === 0 && mark.textContent.trim() === '';\n\n\tmark.classList.add(isTick ? 'sonic-scale-tick' : 'sonic-scale-label');\n\n\treturn [mark];\n}\n\n// As on `<input type=\"range\">`, the property never writes the `value` attribute back\nexport abstract class SonicRangeElement extends SonicFormElement {\n\tstatic override readonly observedAttributes = [\n\t\t...SonicFormElement.observedAttributes,\n\t\t'detent',\n\t\t'max',\n\t\t'midpoint',\n\t\t'min',\n\t\t'notched',\n\t\t'origin',\n\t\t'readout',\n\t\t'step',\n\t\t'taper',\n\t\t'value',\n\t\t'values',\n\t];\n\n\tget default(): number | undefined {\n\t\treturn this.optionalNumberAttribute('default');\n\t}\n\n\tset default(value: number | undefined) {\n\t\tthis.reflect('default', value);\n\t}\n\n\tget detent(): number | undefined {\n\t\treturn this.optionalNumberAttribute('detent');\n\t}\n\n\tset detent(value: number | undefined) {\n\t\tthis.reflect('detent', value);\n\t}\n\n\tget dimmed(): boolean {\n\t\treturn this.hasAttribute('dimmed');\n\t}\n\n\tset dimmed(isDimmed: boolean) {\n\t\tthis.reflect('dimmed', isDimmed);\n\t}\n\n\t// Named for the absent attribute, as `input.type` reads `text`\n\tget doublePress(): 'reset' | 'type' {\n\t\treturn this.getAttribute('double-press') === 'reset' ? 'reset' : 'type';\n\t}\n\n\tset doublePress(gesture: 'reset' | 'type' | undefined) {\n\t\tthis.reflect('double-press', gesture);\n\t}\n\n\tget formatValue(): ((value: number) => string) | undefined {\n\t\treturn this.#formatValue;\n\t}\n\n\tset formatValue(format: ((value: number) => string) | undefined) {\n\t\tthis.#formatValue = format;\n\t\tthis.render();\n\t}\n\n\t// Unclamped by `min`, so reading it back never pins the range\n\tget max(): number {\n\t\treturn this.numberAttribute('max', 100);\n\t}\n\n\tset max(value: number | undefined) {\n\t\tthis.reflect('max', value);\n\t}\n\n\tget midpoint(): number | undefined {\n\t\treturn this.optionalNumberAttribute('midpoint');\n\t}\n\n\tset midpoint(value: number | undefined) {\n\t\tthis.reflect('midpoint', value);\n\t}\n\n\tget min(): number {\n\t\treturn this.numberAttribute('min', 0);\n\t}\n\n\tset min(value: number | undefined) {\n\t\tthis.reflect('min', value);\n\t}\n\n\tget notched(): boolean {\n\t\treturn this.hasAttribute('notched');\n\t}\n\n\tset notched(isNotched: boolean) {\n\t\tthis.reflect('notched', isNotched);\n\t}\n\n\t// `undefined` while unset, so reading it back never pins it to `min`\n\tget origin(): number | undefined {\n\t\treturn this.optionalNumberAttribute('origin');\n\t}\n\n\tset origin(value: number | undefined) {\n\t\tthis.reflect('origin', value);\n\t}\n\n\tget parseValue(): ((text: string) => number) | undefined {\n\t\treturn this.#parseValue;\n\t}\n\n\tset parseValue(parse: ((text: string) => number) | undefined) {\n\t\tthis.#parseValue = parse;\n\t}\n\n\tget readout(): boolean {\n\t\treturn this.hasAttribute('readout');\n\t}\n\n\tset readout(isShown: boolean) {\n\t\tthis.reflect('readout', isShown);\n\t}\n\n\tget step(): number {\n\t\treturn this.numberAttribute('step', 1);\n\t}\n\n\tset step(value: number | undefined) {\n\t\tthis.reflect('step', value);\n\t}\n\n\tget taper(): 'linear' | 'log' {\n\t\treturn this.getAttribute('taper') === 'log' ? 'log' : 'linear';\n\t}\n\n\tset taper(curve: 'linear' | 'log' | undefined) {\n\t\tthis.reflect('taper', curve);\n\t}\n\n\tget value(): number {\n\t\treturn this.#value;\n\t}\n\n\t// A drag owns the value, so a timer's write can't pull the cap from under the pointer\n\tset value(next: number) {\n\t\tif (this.#drag) return;\n\n\t\tthis.#write(next);\n\t}\n\n\tget values(): Array<number> | undefined {\n\t\treturn parseNumberList(this.getAttribute('values'));\n\t}\n\n\tset values(entries: Array<number> | undefined) {\n\t\tthis.reflect('values', entries?.join(' '));\n\t}\n\n\t#drag: RangeDrag | undefined;\n\n\t#entries: Array<number> | undefined;\n\n\t#entry: RangeEntry | undefined;\n\n\t#formatValue: ((value: number) => string) | undefined;\n\n\treadonly #instance = String((instanceCount += 1));\n\n\t// Re-read from the attribute until the property is set, since `max` may arrive after `value`\n\t#isDirty = false;\n\n\t#keyRevealTimer: ReturnType<typeof setTimeout> | undefined;\n\n\t#parseValue: ((text: string) => number) | undefined;\n\n\t#readout: undefined | { bubble: HTMLElement; text: HTMLElement };\n\n\t#scale: HTMLElement | undefined;\n\n\t#value = 0;\n\n\tattributeChangedCallback(name: string): void {\n\t\tthis.#entries = parseNumberList(this.getAttribute('values'));\n\t\tif (name === 'value') {\n\t\t\tif (this.#drag) return;\n\n\t\t\tthis.#isDirty = false;\n\t\t}\n\t\tif (name === 'disabled' && this.isDisabled()) {\n\t\t\tthis.#entry?.close(false);\n\t\t\tthis.#endDrag();\n\t\t}\n\n\t\tthis.#value = this.#clamp(\n\t\t\tthis.#isDirty ? this.#value : this.numberAttribute('value', this.min),\n\t\t);\n\t\tthis.render();\n\t}\n\n\t// The range before `value`, or a value set with its `max` before upgrade clamps to the old one\n\toverride connectedCallback(): void {\n\t\tthis.upgradeProperties(\n\t\t\t'default',\n\t\t\t'dimmed',\n\t\t\t'doublePress',\n\t\t\t'max',\n\t\t\t'midpoint',\n\t\t\t'min',\n\t\t\t'notched',\n\t\t\t'origin',\n\t\t\t'readout',\n\t\t\t'step',\n\t\t\t'taper',\n\t\t\t'values',\n\t\t\t'detent',\n\t\t\t'formatValue',\n\t\t\t'parseValue',\n\t\t\t'value',\n\t\t);\n\t\tsuper.connectedCallback();\n\t}\n\n\tprotected bindGestures(\n\t\tcontrol: HTMLElement,\n\t\tsignal: AbortSignal,\n\t\tgrab: (event: PointerEvent) => RangeAxis | undefined,\n\t): void {\n\t\tconst entry = this.#bindEntry(control, signal);\n\n\t\tcontrol.addEventListener(\n\t\t\t'pointerdown',\n\t\t\t(event) => {\n\t\t\t\tif (event.button !== 0 || this.isDisabled() || entry.isOpen) return;\n\n\t\t\t\t// A click listener above makes WebKit send a tap's compatibility mousedown, which blurs the entry a double tap just opened\n\t\t\t\tif (event.pointerType === 'touch') event.preventDefault();\n\t\t\t\tcontrol.focus();\n\t\t\t\tif (event.metaKey || event.ctrlKey) {\n\t\t\t\t\tthis.#reset();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst fromValue = this.#value;\n\t\t\t\tconst axis = grab(event);\n\t\t\t\tif (!axis) return;\n\n\t\t\t\tconst startPosition = axis.position(event);\n\t\t\t\tconst drag: RangeDrag = {\n\t\t\t\t\tdetent: this.#detentHold(control, axis.travelPx),\n\t\t\t\t\tfromValue,\n\t\t\t\t\tisRevealed: false,\n\t\t\t\t\tlastPosition: startPosition,\n\t\t\t\t\tpointerId: event.pointerId,\n\t\t\t\t\tposition: axis.position,\n\t\t\t\t\trawFraction: this.fraction(this.#value),\n\t\t\t\t\trevealTimer: undefined,\n\t\t\t\t\tstartPosition,\n\t\t\t\t\ttravelPx: axis.travelPx,\n\t\t\t\t};\n\n\t\t\t\tdrag.revealTimer = setTimeout(() => {\n\t\t\t\t\tthis.#reveal(drag);\n\t\t\t\t}, revealMs);\n\t\t\t\tthis.#drag = drag;\n\t\t\t\tcontrol.setPointerCapture(event.pointerId);\n\t\t\t\tthis.toggleState('dragging', true);\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t\tcontrol.addEventListener(\n\t\t\t'pointermove',\n\t\t\t(event) => {\n\t\t\t\tconst drag = this.#drag;\n\t\t\t\tif (event.pointerId !== drag?.pointerId) return;\n\n\t\t\t\tconst position = drag.position(event);\n\t\t\t\tconst scale = event.shiftKey ? fineScale : 1;\n\n\t\t\t\t// Per move, so pressing Shift mid-drag changes pace without a jump\n\t\t\t\tconst next = this.#move(drag, ((position - drag.lastPosition) / drag.travelPx) * scale);\n\n\t\t\t\tdrag.lastPosition = position;\n\t\t\t\tif (Math.abs(position - drag.startPosition) >= revealPx) this.#reveal(drag);\n\t\t\t\tthis.input(next);\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\n\t\tconst endDrag = (event: PointerEvent): void => {\n\t\t\tif (event.pointerId === this.#drag?.pointerId) this.#endDrag();\n\t\t};\n\n\t\tcontrol.addEventListener(\n\t\t\t'pointercancel',\n\t\t\t(event) => {\n\t\t\t\tendDrag(event);\n\t\t\t\tentry.forgetPress();\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t\t// Other code can release capture without a `pointerup`\n\t\tcontrol.addEventListener('lostpointercapture', endDrag, { signal });\n\t\tcontrol.addEventListener(\n\t\t\t'pointerup',\n\t\t\t(event) => {\n\t\t\t\tif (event.pointerId !== this.#drag?.pointerId) return;\n\n\t\t\t\tthis.#endDrag();\n\t\t\t\tif (!entry.press(event) || this.springTarget() !== undefined) return;\n\n\t\t\t\tif (this.doublePress === 'reset') this.#reset();\n\t\t\t\telse entry.open();\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t\tthis.#bindKeys(control, entry, signal);\n\t\tsignal.addEventListener(\n\t\t\t'abort',\n\t\t\t() => {\n\t\t\t\tthis.#endDrag();\n\t\t\t\tthis.#hideForKeys();\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\t}\n\n\tprotected bindScale(control: HTMLElement, scale: HTMLElement, signal: AbortSignal): void {\n\t\tthis.#scale = scale;\n\t\tthis.mirrorChildren(\n\t\t\t{\n\t\t\t\tcontrol,\n\t\t\t\tcopy: (originals) => {\n\t\t\t\t\tscale.replaceChildren(...originals.flatMap((original) => scaleMark(original)));\n\t\t\t\t\tthis.renderScale();\n\t\t\t\t},\n\t\t\t\tisCopied: (child) => child instanceof Element && child.matches('[data-sonic-value]'),\n\t\t\t},\n\t\t\tsignal,\n\t\t);\n\t}\n\n\tprotected clampRange(next: number): number {\n\t\tconst min = this.min;\n\t\tif (!this.wraps()) return clamp(next, min, this.#max());\n\n\t\tconst range = this.range();\n\t\tif (range <= 0) return min;\n\n\t\tconst offset = trimFloat((((next - min) % range) + range) % range);\n\n\t\treturn offset >= range ? min : trimFloat(min + offset);\n\t}\n\n\tprotected controlRole(): 'slider' | 'spinbutton' {\n\t\treturn 'slider';\n\t}\n\n\tprotected fraction(value: number): number {\n\t\treturn (this.#taper() ?? linearTaper(this.min, this.#max())).position(value);\n\t}\n\n\tprotected input(next: number): boolean {\n\t\tconst previous = this.#value;\n\n\t\tthis.#write(next);\n\t\tif (this.#value === previous) return false;\n\n\t\tthis.dispatchEvent(new Event('input', { bubbles: true, composed: true }));\n\n\t\treturn true;\n\t}\n\n\tprotected modulationFractions(amount: number): [number, number] {\n\t\tconst value = this.fraction(this.#value);\n\t\tconst modulated = this.fraction(this.#value + amount);\n\n\t\treturn [Math.min(value, modulated), Math.max(value, modulated)];\n\t}\n\n\tprotected originFraction(): number {\n\t\treturn this.fraction(this.restValue());\n\t}\n\n\tprotected positions(): number | undefined {\n\t\tconst positions =\n\t\t\tthis.#entries?.length ?? Math.round(this.range() / this.step) + (this.wraps() ? 0 : 1);\n\n\t\tif (!this.notched || !Number.isFinite(positions) || positions < 2) return;\n\n\t\treturn positions;\n\t}\n\n\tprotected range(): number {\n\t\treturn this.#max() - this.min;\n\t}\n\n\t// Per render rather than per write, so a range with no `value` attribute submits its minimum too\n\tprotected renderAria(control: HTMLElement, orientation?: 'horizontal' | 'vertical'): void {\n\t\tthis.writeFormValue(String(this.#value), String(this.#value));\n\t\tif (this.#entry?.isOpen) {\n\t\t\tfor (const name of roleAttributes) control.removeAttribute(name);\n\t\t\tthis.forwardNaming(control, false);\n\t\t\tthis.#renderReadout();\n\t\t\treturn;\n\t\t}\n\n\t\tconst [low, high] = this.#bounds();\n\n\t\tcontrol.setAttribute('role', this.controlRole());\n\t\tcontrol.setAttribute('aria-valuemin', String(low));\n\t\tcontrol.setAttribute('aria-valuemax', String(high));\n\t\tcontrol.setAttribute('aria-valuenow', String(this.#value));\n\t\tif (orientation) control.setAttribute('aria-orientation', orientation);\n\t\twriteAttribute(control, 'aria-valuetext', this.#formatValue?.(this.#value));\n\t\tthis.forwardNaming(control, true);\n\t\tthis.#renderDisabled(control);\n\t\tthis.#renderReadout();\n\t}\n\n\t// Runs on every input, while the places move only with the range\n\tprotected renderScale(): void {\n\t\tconst marks = this.#scale?.children ?? [];\n\n\t\tfor (const mark of marks) {\n\t\t\tif (!(mark instanceof HTMLElement || mark instanceof SVGElement)) continue;\n\n\t\t\tconst at = String(this.fraction(this.clampRange(scaleValue(mark))));\n\n\t\t\tif (mark.style.getPropertyValue('--_sonic-scale-at') !== at) {\n\t\t\t\tmark.style.setProperty('--_sonic-scale-at', at);\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected restoreState(state: string): void {\n\t\tthis.value = Number(state);\n\t}\n\n\tprotected restValue(): number {\n\t\treturn this.origin ?? this.#bounds()[0];\n\t}\n\n\tprotected springTarget(): number | undefined {\n\t\treturn undefined;\n\t}\n\n\tprotected valueAt(fraction: number): number {\n\t\treturn (this.#taper() ?? linearTaper(this.min, this.#max())).value(fraction);\n\t}\n\n\tprotected valueText(): string {\n\t\treturn this.#formatValue?.(this.#value) ?? String(this.#value);\n\t}\n\n\tprotected wraps(): boolean {\n\t\treturn false;\n\t}\n\n\t#bindEntry(control: HTMLElement, signal: AbortSignal): RangeEntry {\n\t\tconst input = requireChild(control, 'input', HTMLInputElement);\n\t\tconst bubble = control.querySelector<HTMLElement>('[popover]');\n\n\t\t// Chrome's issues panel flags a form field with no id or name; a `name` would submit it\n\t\tinput.id = `sonic-entry-${this.#instance}`;\n\t\tif (bubble) {\n\t\t\tconst anchor = `--sonic-readout-${this.#instance}`;\n\n\t\t\tthis.#readout = { bubble, text: requireChild(bubble, 'span', HTMLElement) };\n\t\t\tcontrol.style.setProperty('anchor-name', anchor);\n\t\t\tbubble.style.setProperty('position-anchor', anchor);\n\t\t}\n\n\t\tconst entry = new RangeEntry(input, {\n\t\t\tcommit: (text) => {\n\t\t\t\t// eslint-disable-next-line unicorn/prefer-number-coercion -- `Number('')` is 0; an emptied field should leave the value alone\n\t\t\t\tthis.#commit((this.#parseValue ?? Number.parseFloat)(text));\n\t\t\t},\n\t\t\t// `Number.parseFloat` reads \"5 kHz\" as 5\n\t\t\ttext: () => (this.#parseValue ? this.valueText() : String(this.#value)),\n\t\t\ttoggle: (isOpen) => {\n\t\t\t\tif (isOpen) this.forwardNaming(input, true);\n\t\t\t\tthis.toggleState('editing', isOpen);\n\t\t\t\tthis.render();\n\t\t\t},\n\t\t});\n\n\t\tthis.#entry = entry;\n\t\tentry.bind(control, signal);\n\n\t\treturn entry;\n\t}\n\n\t#bindKeys(control: HTMLElement, entry: RangeEntry, signal: AbortSignal): void {\n\t\tcontrol.addEventListener(\n\t\t\t'keydown',\n\t\t\t(event) => {\n\t\t\t\tif (this.isDisabled() || event.defaultPrevented || event.target !== control) return;\n\n\t\t\t\tif (event.key === 'Enter') {\n\t\t\t\t\tif (this.springTarget() !== undefined) return;\n\n\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\tentry.open();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst next = this.#keyTarget(event.key);\n\t\t\t\tif (next === undefined) return;\n\n\t\t\t\tevent.preventDefault();\n\t\t\t\tthis.#commit(next);\n\t\t\t\tthis.#revealForKeys();\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t\tcontrol.addEventListener(\n\t\t\t'keyup',\n\t\t\t(event) => {\n\t\t\t\tif (keySteps.has(event.key) || endKeys.has(event.key)) this.#springBack();\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t\tcontrol.addEventListener(\n\t\t\t'blur',\n\t\t\t() => {\n\t\t\t\tthis.#hideForKeys();\n\t\t\t\tthis.#springBack();\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t}\n\n\t#bounds(): [number, number] {\n\t\tconst entries = this.#entries;\n\n\t\treturn [entries?.[0] ?? this.min, entries?.at(-1) ?? this.#max()];\n\t}\n\n\t#clamp(next: number): number {\n\t\tconst entries = this.#entries;\n\t\tif (entries) return nearestEntry(entries, next);\n\n\t\tconst min = this.min;\n\t\tconst step = this.step;\n\t\tconst stepped = step > 0 ? min + Math.round((next - min) / step) * step : next;\n\n\t\t// A step like 0.01 leaves float residue\n\t\treturn this.clampRange(trimFloat(stepped));\n\t}\n\n\t#commit(next: number): void {\n\t\tif (this.input(next)) this.dispatchEvent(new Event('change', { bubbles: true }));\n\t}\n\n\t#detentHold(control: HTMLElement, travelPx: number): DetentHold | undefined {\n\t\tconst detent = this.detent;\n\t\tif (detent === undefined) return undefined;\n\n\t\tconst value = this.#clamp(detent);\n\t\tconst zonePx = readPxProperty(getComputedStyle(control), '--_sonic-detent-zone', detentZonePx);\n\n\t\treturn {\n\t\t\tplace: this.fraction(value),\n\t\t\tslack: value === this.#value ? 0 : undefined,\n\t\t\tvalue,\n\t\t\tzone: zonePx / travelPx,\n\t\t};\n\t}\n\n\t#end(step: number): number {\n\t\tif (!this.wraps()) return this.#bounds()[1];\n\n\t\treturn this.min + (Math.ceil(this.range() / step) - 1) * step;\n\t}\n\n\t#endDrag(): void {\n\t\tconst drag = this.#drag;\n\t\tif (!drag) return;\n\n\t\tclearTimeout(drag.revealTimer);\n\t\tthis.#drag = undefined;\n\t\tthis.toggleState('dragging', false);\n\t\tthis.#renderReadout();\n\t\tif (this.#value !== drag.fromValue) this.dispatchEvent(new Event('change', { bubbles: true }));\n\t\tthis.#springBack();\n\t}\n\n\t#hideForKeys(): void {\n\t\tclearTimeout(this.#keyRevealTimer);\n\t\tif (this.#keyRevealTimer === undefined) return;\n\n\t\tthis.#keyRevealTimer = undefined;\n\t\tthis.#renderReadout();\n\t}\n\n\t#isValueShown(): boolean {\n\t\tconst isRevealed = this.#drag?.isRevealed === true || this.#keyRevealTimer !== undefined;\n\n\t\treturn isRevealed && this.readout;\n\t}\n\n\t#keyTarget(key: string): number | undefined {\n\t\tif (key === 'Home') return this.#bounds()[0];\n\t\tconst step = this.step > 0 ? this.step : this.range() / 100;\n\n\t\tif (key === 'End') return this.#end(step);\n\t\tif (resetKeys.has(key)) return this.default;\n\n\t\tconst steps = keySteps.get(key);\n\t\tif (steps === undefined) return undefined;\n\n\t\treturn this.#stopAtDetent(this.#stepBy(steps, step));\n\t}\n\n\t#max(): number {\n\t\treturn Math.max(this.min, this.max);\n\t}\n\n\t#move(drag: RangeDrag, delta: number): number {\n\t\tconst { detent } = drag;\n\t\tconst to = drag.rawFraction + delta;\n\t\tconst next = detent ? passDetent(detent, drag.rawFraction, to) : to;\n\n\t\tif (detent && next === undefined) {\n\t\t\tdrag.rawFraction = detent.place;\n\t\t\treturn detent.value;\n\t\t}\n\n\t\tconst place = next ?? to;\n\n\t\tdrag.rawFraction = this.wraps() ? wrapUnit(place) : clampUnit(place);\n\n\t\treturn this.valueAt(drag.rawFraction);\n\t}\n\n\t#renderDisabled(control: HTMLElement): void {\n\t\tif (this.isDisabled()) {\n\t\t\tcontrol.setAttribute('aria-disabled', 'true');\n\t\t\tcontrol.removeAttribute('tabindex');\n\t\t\treturn;\n\t\t}\n\n\t\tcontrol.removeAttribute('aria-disabled');\n\t\tcontrol.setAttribute('tabindex', '0');\n\t}\n\n\t#renderReadout(): void {\n\t\tconst readout = this.#readout;\n\t\tif (!readout || !canPopover || !this.isConnected) return;\n\n\t\tconst isEditing = this.#entry?.isOpen === true;\n\n\t\treadout.text.hidden = isEditing;\n\t\tif (!isEditing) readout.text.textContent = this.valueText();\n\t\treadout.bubble.togglePopover(isEditing || this.#isValueShown());\n\t}\n\n\t#reset(): void {\n\t\tthis.#commit(this.default ?? NaN);\n\t}\n\n\t#reveal(drag: RangeDrag): void {\n\t\tif (drag.isRevealed || this.#drag !== drag) return;\n\n\t\tdrag.isRevealed = true;\n\t\tthis.#renderReadout();\n\t}\n\n\t#revealForKeys(): void {\n\t\tclearTimeout(this.#keyRevealTimer);\n\t\tthis.#keyRevealTimer = setTimeout(() => {\n\t\t\tthis.#hideForKeys();\n\t\t}, keyRevealMs);\n\t\tthis.#renderReadout();\n\t}\n\n\t#springBack(): void {\n\t\tconst target = this.springTarget();\n\n\t\tif (target !== undefined) this.#commit(target);\n\t}\n\n\t#stepBy(steps: number, step: number): number {\n\t\tconst entries = this.#entries;\n\t\tif (entries) {\n\t\t\tconst index = clamp(entries.indexOf(this.#value) + steps, 0, entries.length - 1);\n\n\t\t\treturn entries[index] ?? this.#value;\n\t\t}\n\n\t\tconst taper = this.#taper();\n\t\tif (!taper) return this.#value + steps * step;\n\n\t\t// By travel, since steps at the wide end would take forever; at least one step, or the snap holds it in place\n\t\tconst next = this.#clamp(taper.value(taper.position(this.#value) + steps / 100));\n\n\t\treturn next === this.#value ? this.#value + Math.sign(steps) * step : next;\n\t}\n\n\t#stopAtDetent(next: number): number {\n\t\tconst detent = this.detent;\n\t\tif (detent === undefined) return next;\n\n\t\tconst value = this.#clamp(detent);\n\n\t\treturn (this.#value - value) * (next - value) < 0 ? value : next;\n\t}\n\n\t// Notches are evenly spaced, so a notched control stays linear\n\t#taper(): Taper | undefined {\n\t\tconst entries = this.#entries;\n\t\tif (entries) return listTaper(entries);\n\t\tif (this.notched || this.wraps()) return undefined;\n\n\t\tconst min = this.min;\n\t\tconst max = this.#max();\n\t\tconst log = this.taper === 'log' ? logTaper(min, max) : undefined;\n\n\t\treturn log ?? skewTaper(min, max, this.midpoint ?? NaN);\n\t}\n\n\t#write(next: number): void {\n\t\tif (!Number.isFinite(next)) return;\n\n\t\tthis.#isDirty = true;\n\n\t\tconst clamped = this.#clamp(next);\n\t\tif (clamped === this.#value) return;\n\n\t\tthis.#value = clamped;\n\t\tthis.render();\n\t}\n}\n"],"mappings":"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"}
|
|
@@ -5,17 +5,25 @@ declare global {
|
|
|
5
5
|
'sonic-slider': SonicSlider;
|
|
6
6
|
}
|
|
7
7
|
}
|
|
8
|
+
export type BufferedRanges = Iterable<readonly [number, number]> | TimeRanges;
|
|
8
9
|
export declare class SonicSlider extends SonicRangeElement {
|
|
9
10
|
#private;
|
|
10
11
|
static readonly observedAttributes: string[];
|
|
12
|
+
get buffered(): Array<[number, number]>;
|
|
13
|
+
set buffered(ranges: BufferedRanges | undefined);
|
|
11
14
|
get groovePress(): 'jump' | 'none';
|
|
12
15
|
set groovePress(gesture: 'jump' | 'none' | undefined);
|
|
16
|
+
get modulation(): number;
|
|
17
|
+
set modulation(amount: number | undefined);
|
|
13
18
|
get orientation(): 'horizontal' | 'vertical';
|
|
14
19
|
set orientation(direction: 'horizontal' | 'vertical' | undefined);
|
|
20
|
+
get spring(): boolean;
|
|
21
|
+
set spring(isSpring: boolean);
|
|
15
22
|
connectedCallback(): void;
|
|
16
23
|
protected connect(signal: AbortSignal): void;
|
|
17
24
|
protected focusTarget(): HTMLElement;
|
|
18
25
|
protected render(): void;
|
|
26
|
+
protected springTarget(): number | undefined;
|
|
19
27
|
}
|
|
20
28
|
//#endregion
|
|
21
29
|
//# sourceMappingURL=slider.d.ts.map
|