@xsynaptic/sonic-ui 0.10.0 → 0.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/default/elements/value-element.js +5 -1
- package/dist/default/elements/value-gestures.js +27 -15
- package/dist/default/lib/hover-preview.js +4 -2
- package/dist/dev/elements/value-element.js +7 -1
- package/dist/dev/elements/value-element.js.map +1 -1
- package/dist/dev/elements/value-gestures.js +27 -15
- package/dist/dev/elements/value-gestures.js.map +1 -1
- package/dist/dev/lib/hover-preview.js +4 -2
- package/dist/dev/lib/hover-preview.js.map +1 -1
- package/package.json +1 -1
|
@@ -290,6 +290,9 @@ var SonicValueElement = class extends SonicFormElement {
|
|
|
290
290
|
value: () => this.#cell.value
|
|
291
291
|
}, control);
|
|
292
292
|
this.#gestures.bind(signal, grab);
|
|
293
|
+
control.addEventListener("pointerup", (event) => {
|
|
294
|
+
this.#hover?.at(event);
|
|
295
|
+
}, { signal });
|
|
293
296
|
}
|
|
294
297
|
bindHover(control, signal, { canShow = () => true, place, valueAt }) {
|
|
295
298
|
this.#hover = bindHoverPreview(control, {
|
|
@@ -393,7 +396,8 @@ var SonicValueElement = class extends SonicFormElement {
|
|
|
393
396
|
#entryText() {
|
|
394
397
|
const value = this.#cell.value;
|
|
395
398
|
if (this.#formatEntry) return this.#formatEntry(value);
|
|
396
|
-
|
|
399
|
+
if (!this.#parseValue) return String(value);
|
|
400
|
+
return this.valueText;
|
|
397
401
|
}
|
|
398
402
|
#isEditing() {
|
|
399
403
|
return this.#gestures?.isEditing() === true;
|
|
@@ -14,6 +14,7 @@ const detentZonePx = 8;
|
|
|
14
14
|
var ValueGestures = class {
|
|
15
15
|
#claim = new ReadoutClaim(() => {
|
|
16
16
|
this.#host.holdChanged();
|
|
17
|
+
this.#showRevealed();
|
|
17
18
|
});
|
|
18
19
|
#control;
|
|
19
20
|
#entry;
|
|
@@ -43,8 +44,12 @@ var ValueGestures = class {
|
|
|
43
44
|
return;
|
|
44
45
|
}
|
|
45
46
|
const fromValue = host.value();
|
|
47
|
+
host.toggleState("dragging", true);
|
|
46
48
|
const axis = grab(event);
|
|
47
|
-
if (!axis)
|
|
49
|
+
if (!axis) {
|
|
50
|
+
host.toggleState("dragging", false);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
48
53
|
const drag = {
|
|
49
54
|
...axis,
|
|
50
55
|
fromValue,
|
|
@@ -145,8 +150,9 @@ var ValueGestures = class {
|
|
|
145
150
|
const next = this.#keyTarget(event.key);
|
|
146
151
|
if (next === void 0) return;
|
|
147
152
|
event.preventDefault();
|
|
148
|
-
this.#keyTo(event, next);
|
|
149
153
|
this.#claim.reveal("keys");
|
|
154
|
+
this.#showRevealed();
|
|
155
|
+
this.#keyTo(event, next);
|
|
150
156
|
host.holdChanged();
|
|
151
157
|
}, { signal });
|
|
152
158
|
control.addEventListener("keyup", (event) => {
|
|
@@ -167,7 +173,18 @@ var ValueGestures = class {
|
|
|
167
173
|
if (host.input(landed)) host.dispatch("change");
|
|
168
174
|
}
|
|
169
175
|
#concealKeyReveal() {
|
|
170
|
-
if (this.#claim.conceal("keys"))
|
|
176
|
+
if (!this.#claim.conceal("keys")) return;
|
|
177
|
+
this.#host.holdChanged();
|
|
178
|
+
this.#showRevealed();
|
|
179
|
+
}
|
|
180
|
+
#crossCancelZone(drag, value) {
|
|
181
|
+
const host = this.#host;
|
|
182
|
+
const isOutside = !drag.isOutside;
|
|
183
|
+
drag.isOutside = isOutside;
|
|
184
|
+
host.toggleState("cancelling", isOutside);
|
|
185
|
+
if (isOutside) this.#renderDragReveal(drag);
|
|
186
|
+
host.input(isOutside ? drag.fromValue : value);
|
|
187
|
+
if (!isOutside) this.#renderDragReveal(drag);
|
|
171
188
|
}
|
|
172
189
|
#dragging() {
|
|
173
190
|
return this.#pointerDrag?.current();
|
|
@@ -185,13 +202,8 @@ var ValueGestures = class {
|
|
|
185
202
|
drag.state = this.#withinLimit(mapping, state);
|
|
186
203
|
if (value === void 0) return;
|
|
187
204
|
if (!wasEngaged) this.#reveal(drag);
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
drag.isOutside = isOutside;
|
|
191
|
-
host.toggleState("cancelling", isOutside);
|
|
192
|
-
}
|
|
193
|
-
host.input(isOutside ? drag.fromValue : value);
|
|
194
|
-
if (isCrossing) this.#renderDragReveal(drag);
|
|
205
|
+
if (isOutside === drag.isOutside) host.input(value);
|
|
206
|
+
else this.#crossCancelZone(drag, value);
|
|
195
207
|
}
|
|
196
208
|
#endKeyScrub() {
|
|
197
209
|
const host = this.#host;
|
|
@@ -221,16 +233,15 @@ var ValueGestures = class {
|
|
|
221
233
|
this.#claim.conceal("drag");
|
|
222
234
|
host.toggleState("cancelling", false);
|
|
223
235
|
host.holdChanged();
|
|
224
|
-
this.#showRevealed(
|
|
236
|
+
this.#showRevealed();
|
|
225
237
|
if (host.value() !== drag.fromValue) host.dispatch("change");
|
|
226
238
|
this.#springBack();
|
|
227
239
|
}
|
|
228
240
|
#renderDragReveal(drag) {
|
|
229
|
-
|
|
230
|
-
if (isRevealed) this.#claim.reveal("drag");
|
|
241
|
+
if (drag.isRevealed && !drag.isOutside) this.#claim.reveal("drag");
|
|
231
242
|
else this.#claim.conceal("drag");
|
|
232
243
|
this.#host.holdChanged();
|
|
233
|
-
this.#showRevealed(
|
|
244
|
+
this.#showRevealed();
|
|
234
245
|
}
|
|
235
246
|
#reset() {
|
|
236
247
|
this.#commit(this.#host.default() ?? NaN);
|
|
@@ -251,7 +262,8 @@ var ValueGestures = class {
|
|
|
251
262
|
}
|
|
252
263
|
host.input(next);
|
|
253
264
|
}
|
|
254
|
-
#showRevealed(
|
|
265
|
+
#showRevealed() {
|
|
266
|
+
const { isRevealed } = this.#claim;
|
|
255
267
|
if (isRevealed === this.#isRevealed) return;
|
|
256
268
|
this.#isRevealed = isRevealed;
|
|
257
269
|
this.#host.toggleState("revealed", isRevealed);
|
|
@@ -29,10 +29,11 @@ function bindHoverPreview(target, options, signal) {
|
|
|
29
29
|
options.show();
|
|
30
30
|
watchEscape(value !== void 0);
|
|
31
31
|
};
|
|
32
|
-
|
|
32
|
+
const at = (event) => {
|
|
33
33
|
if (event.pointerType === "touch" || event.buttons !== 0 || !options.canShow()) return;
|
|
34
34
|
hoverAt(options.valueAt(event));
|
|
35
|
-
}
|
|
35
|
+
};
|
|
36
|
+
target.addEventListener("pointermove", at, { signal });
|
|
36
37
|
for (const type of ["pointerdown", "pointerleave"]) target.addEventListener(type, () => {
|
|
37
38
|
hoverAt(void 0);
|
|
38
39
|
}, { signal });
|
|
@@ -40,6 +41,7 @@ function bindHoverPreview(target, options, signal) {
|
|
|
40
41
|
watchEscape(false);
|
|
41
42
|
}, { once: true });
|
|
42
43
|
return {
|
|
44
|
+
at,
|
|
43
45
|
clear: () => {
|
|
44
46
|
hold(void 0);
|
|
45
47
|
},
|
|
@@ -290,6 +290,9 @@ var SonicValueElement = class extends SonicFormElement {
|
|
|
290
290
|
value: () => this.#cell.value
|
|
291
291
|
}, control);
|
|
292
292
|
this.#gestures.bind(signal, grab);
|
|
293
|
+
control.addEventListener("pointerup", (event) => {
|
|
294
|
+
this.#hover?.at(event);
|
|
295
|
+
}, { signal });
|
|
293
296
|
}
|
|
294
297
|
bindHover(control, signal, { canShow = () => true, place, valueAt }) {
|
|
295
298
|
this.#hover = bindHoverPreview(control, {
|
|
@@ -393,7 +396,10 @@ var SonicValueElement = class extends SonicFormElement {
|
|
|
393
396
|
#entryText() {
|
|
394
397
|
const value = this.#cell.value;
|
|
395
398
|
if (this.#formatEntry) return this.#formatEntry(value);
|
|
396
|
-
|
|
399
|
+
if (!this.#parseValue) return String(value);
|
|
400
|
+
const text = this.valueText;
|
|
401
|
+
if (!Number.isFinite(this.#parseValue(text))) console.warn(`<${this.localName}> opens its entry with "${text}", which parseValue cannot read; set formatEntry`);
|
|
402
|
+
return text;
|
|
397
403
|
}
|
|
398
404
|
#isEditing() {
|
|
399
405
|
return this.#gestures?.isEditing() === true;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"value-element.js","names":[],"sources":["../../../src/elements/value-element.ts"],"sourcesContent":["import type { ValueAxis } from '#elements/value-gestures.ts';\nimport type { AskedValue } from '#lib/asked-value.ts';\nimport type { HoverPreview } from '#lib/hover-preview.ts';\nimport type { ValueMapping } from '#lib/value-mapping.ts';\n\nimport { SonicFormElement } from '#elements/form-element.ts';\nimport { Readout } from '#elements/readout.ts';\nimport { ValueGestures } from '#elements/value-gestures.ts';\nimport { moveTo, resnap, setAsked } from '#lib/asked-value.ts';\nimport { bindHoverPreview } from '#lib/hover-preview.ts';\nimport { clamp, toNumber, trimFloat } from '#lib/math.ts';\nimport { parseNumberList } from '#lib/number-list.ts';\nimport { readPxProperty } from '#lib/read-px-property.ts';\nimport { valueMapping } from '#lib/value-mapping.ts';\nimport { writeAttribute } from '#lib/write-attribute.ts';\n\ninterface HoverBinding {\n\tcanShow?: () => boolean;\n\tplace: () => void;\n\tvalueAt: (event: PointerEvent) => number | undefined;\n}\n\nconst fallbackTravelPx = 160;\n\nlet instanceCount = 0;\n\nconst proportionAttributes = new Set(['max', 'midpoint', 'min', 'notched', 'positions', 'taper']);\n\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 ValueLanding = (target: number, direction: -1 | 0 | 1) => number;\n\nexport interface ValueLink {\n\tinput: (next: number, isMover?: boolean) => boolean;\n\tisDisabled: () => boolean;\n\tisHeld: () => boolean;\n\tland: (resolve: undefined | ValueLanding) => void;\n\tlimit: (bounds: [number, number] | undefined) => void;\n\tmapping: () => ValueMapping;\n\tvalue: () => number;\n\twatch: (listener: () => void) => () => void;\n}\n\n// Filled from inside the class, so it reaches protected members and nothing lands on the element's public type\nlet link: (element: SonicValueElement) => ValueLink;\n\nexport function linkValue(element: SonicValueElement): ValueLink {\n\treturn link(element);\n}\n\nexport abstract class SonicValueElement 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'spoken-step',\n\t\t'step',\n\t\t'tabindex',\n\t\t'taper',\n\t\t'value',\n\t\t'positions',\n\t];\n\n\tstatic {\n\t\tlink = (element) => ({\n\t\t\tinput: (next, isMover = false) => (isMover || !element.#isHeld()) && element.input(next),\n\t\t\tisDisabled: () => element.isDisabled(),\n\t\t\tisHeld: () => element.#isHeld(),\n\t\t\tland: (resolve) => {\n\t\t\t\telement.#land = resolve;\n\t\t\t},\n\t\t\tlimit: (bounds) => {\n\t\t\t\telement.#limit = bounds;\n\t\t\t},\n\t\t\tmapping: () => element.mapping(),\n\t\t\tvalue: () => element.#cell.value,\n\t\t\twatch: (listener) => {\n\t\t\t\telement.#watchers.add(listener);\n\n\t\t\t\treturn () => {\n\t\t\t\t\telement.#watchers.delete(listener);\n\t\t\t\t};\n\t\t\t},\n\t\t});\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\tget doublePress(): 'entry' | 'none' | 'reset' {\n\t\tconst gesture = this.getAttribute('double-press');\n\n\t\treturn gesture === 'none' || gesture === 'reset' ? gesture : 'entry';\n\t}\n\n\tset doublePress(gesture: 'entry' | 'none' | 'reset' | undefined) {\n\t\tthis.reflect('double-press', gesture);\n\t}\n\n\tget formatEntry(): ((value: number) => string) | undefined {\n\t\treturn this.#formatEntry;\n\t}\n\n\tset formatEntry(format: ((value: number) => string) | undefined) {\n\t\tthis.#formatEntry = format;\n\t}\n\n\tget formatSpokenValue(): ((value: number) => string) | undefined {\n\t\treturn this.#formatSpokenValue;\n\t}\n\n\tset formatSpokenValue(format: ((value: number) => string) | undefined) {\n\t\tthis.#formatSpokenValue = format;\n\t\tthis.render();\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\tget hoverValue(): number | undefined {\n\t\treturn this.#hover?.value();\n\t}\n\n\tget keyStep(): number | undefined {\n\t\treturn this.optionalNumberAttribute('key-step');\n\t}\n\n\tset keyStep(value: number | undefined) {\n\t\tthis.reflect('key-step', value);\n\t}\n\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\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 pointerType(): string | undefined {\n\t\treturn this.#gestures?.pointerType();\n\t}\n\n\tget positions(): Array<number> | undefined {\n\t\treturn parseNumberList(this.getAttribute('positions'));\n\t}\n\n\tset positions(positions: Array<number> | undefined) {\n\t\tthis.reflect('positions', positions?.join(' '));\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 spokenStep(): number | undefined {\n\t\treturn this.optionalNumberAttribute('spoken-step');\n\t}\n\n\tset spokenStep(value: number | undefined) {\n\t\tthis.reflect('spoken-step', value);\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.#cell.value;\n\t}\n\n\tset value(next: null | number | undefined) {\n\t\t// A framework removes a prop by setting the property to `undefined`\n\t\tif (next === undefined || next === null) {\n\t\t\tthis.formResetCallback();\n\t\t\treturn;\n\t\t}\n\n\t\tconst value = toNumber(next);\n\n\t\tif (this.#isHeld()) {\n\t\t\tthis.heldWrite(value);\n\t\t\treturn;\n\t\t}\n\n\t\tif (setAsked(this.#cell, this.mapping(), value)) this.render();\n\t}\n\n\tget valueText(): string {\n\t\treturn this.#textFor(this.#cell.value);\n\t}\n\n\treadonly #cell: AskedValue = { asked: undefined, value: 0 };\n\n\t#formatEntry: ((value: number) => string) | undefined;\n\n\t#formatSpokenValue: ((value: number) => string) | undefined;\n\n\t#formatValue: ((value: number) => string) | undefined;\n\n\t#gestures: undefined | ValueGestures;\n\n\t#heldAt: number | undefined;\n\n\t#hover: HoverPreview | undefined;\n\n\treadonly #instance = String((instanceCount += 1));\n\n\t#land: undefined | ValueLanding;\n\n\t#limit: [number, number] | undefined;\n\n\t// Every attribute it reads is observed; a subclass adding an input to `isWrapping()` has to observe it\n\t#mapping: undefined | ValueMapping;\n\n\t#parseValue: ((text: string) => number) | undefined;\n\n\t#positions: Array<number> | undefined;\n\n\t#readout: undefined | { anchor: HTMLElement; bubble: Readout };\n\n\treadonly #watchers = new Set<() => void>();\n\n\tattributeChangedCallback(name: string): void {\n\t\tthis.#mapping = undefined;\n\t\tthis.#positions = parseNumberList(this.getAttribute('positions'));\n\t\tif (name === 'value') {\n\t\t\tif (this.#isHeld()) {\n\t\t\t\tthis.heldWrite(this.numberAttribute('value', this.min));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.#cell.asked = undefined;\n\t\t}\n\t\tif (name === 'disabled' && this.isDisabled()) this.#gestures?.end();\n\n\t\tresnap(this.#cell, this.mapping(), this.numberAttribute('value', this.min));\n\t\tif (proportionAttributes.has(name) && this.isBound()) this.proportionsChanged();\n\t\tthis.render();\n\t}\n\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'spokenStep',\n\t\t\t'step',\n\t\t\t'taper',\n\t\t\t'positions',\n\t\t\t'detent',\n\t\t\t'formatEntry',\n\t\t\t'formatValue',\n\t\t\t'formatSpokenValue',\n\t\t\t'keyStep',\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) => undefined | ValueAxis,\n\t): void {\n\t\tconst bubble = control.querySelector<HTMLElement>('[popover]');\n\n\t\tif (bubble) this.#readout = { anchor: control, bubble: new Readout(bubble) };\n\t\tthis.#gestures = new ValueGestures(\n\t\t\t{\n\t\t\t\tdefault: () => this.default,\n\t\t\t\tdetent: () => this.detent,\n\t\t\t\tdispatch: (type) => {\n\t\t\t\t\tthis.dispatchEvent(new Event(type, { bubbles: true }));\n\t\t\t\t},\n\t\t\t\tdoublePress: () => this.doublePress,\n\t\t\t\tentryId: `sonic-entry-${this.#instance}`,\n\t\t\t\tentryText: () => this.#entryText(),\n\t\t\t\tforwardNaming: (target, isNamed) => {\n\t\t\t\t\tthis.forwardNaming(target, isNamed);\n\t\t\t\t},\n\t\t\t\tholdChanged: () => {\n\t\t\t\t\tthis.#renderHold();\n\t\t\t\t},\n\t\t\t\tinput: (next) => this.input(next),\n\t\t\t\tisDisabled: () => this.isDisabled(),\n\t\t\t\tkeyStep: () => this.keyStep,\n\t\t\t\tland: (target, direction) => this.#land?.(target, direction) ?? target,\n\t\t\t\tlimit: () => this.#limit,\n\t\t\t\tmapping: () => this.mapping(),\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\tparse: (text) => (this.#parseValue ?? Number.parseFloat)(text),\n\t\t\t\trender: () => {\n\t\t\t\t\tthis.render();\n\t\t\t\t},\n\t\t\t\tscrubsKeyRepeat: () => this.scrubsKeyRepeat(),\n\t\t\t\tspringTarget: () => this.springTarget(),\n\t\t\t\ttapTarget: () => this.tapTarget(),\n\t\t\t\ttoggleState: (state, isOn) => {\n\t\t\t\t\tthis.toggleState(state, isOn);\n\t\t\t\t},\n\t\t\t\tvalue: () => this.#cell.value,\n\t\t\t},\n\t\t\tcontrol,\n\t\t);\n\t\tthis.#gestures.bind(signal, grab);\n\t}\n\n\tprotected bindHover(\n\t\tcontrol: HTMLElement,\n\t\tsignal: AbortSignal,\n\t\t{ canShow = () => true, place, valueAt }: HoverBinding,\n\t): void {\n\t\tthis.#hover = bindHoverPreview(\n\t\t\tcontrol,\n\t\t\t{\n\t\t\t\tcanShow,\n\t\t\t\tchanged: () => {\n\t\t\t\t\tthis.dispatchEvent(new Event('sonic-hover', { bubbles: true }));\n\t\t\t\t},\n\t\t\t\tdismiss: () => {\n\t\t\t\t\tthis.render();\n\t\t\t\t},\n\t\t\t\tisTaken: () => this.#isEditing() || this.isRevealed(),\n\t\t\t\tshow: () => {\n\t\t\t\t\tif (!this.readout) return;\n\n\t\t\t\t\tthis.renderReadout();\n\t\t\t\t\tplace();\n\t\t\t\t},\n\t\t\t\tvalueAt,\n\t\t\t},\n\t\t\tsignal,\n\t\t);\n\t}\n\n\tprotected controlOrientation(): 'horizontal' | 'vertical' | undefined {\n\t\treturn undefined;\n\t}\n\n\tprotected controlRole(): 'slider' | 'spinbutton' {\n\t\treturn 'slider';\n\t}\n\n\tprotected abstract draw(): void;\n\n\tprotected abstract override focusTarget(): HTMLElement;\n\n\tprotected heldFrom(): number | undefined {\n\t\treturn this.#gestures?.heldFrom();\n\t}\n\n\tprotected heldWrite(next: number): void {\n\t\tthis.#heldAt = next;\n\t}\n\n\tprotected holdChanged(): void {\n\t\t// Only a subclass draws a hold\n\t}\n\n\tprotected input(next: number): boolean {\n\t\tconst previous = this.#cell.value;\n\n\t\tconst target = this.#limit ? clamp(next, ...this.#limit) : next;\n\n\t\tif (moveTo(this.#cell, this.mapping(), target)) this.render();\n\t\tif (this.#cell.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 isRevealed(): boolean {\n\t\treturn this.#gestures?.isRevealed() === true;\n\t}\n\n\tprotected isWrapping(): boolean {\n\t\treturn false;\n\t}\n\n\tprotected mapping(): ValueMapping {\n\t\tif (this.#mapping) return this.#mapping;\n\n\t\tconst { detent, midpoint } = this;\n\n\t\tthis.#mapping = valueMapping({\n\t\t\t...(detent === undefined ? {} : { detent }),\n\t\t\t...(this.#positions ? { positions: this.#positions } : {}),\n\t\t\tmax: this.max,\n\t\t\t...(midpoint === undefined ? {} : { midpoint }),\n\t\t\tisNotched: this.notched,\n\t\t\tisWrapping: this.isWrapping(),\n\t\t\tmin: this.min,\n\t\t\tstep: this.step,\n\t\t\ttaper: this.taper,\n\t\t});\n\n\t\treturn this.#mapping;\n\t}\n\n\tprotected originValue(): number {\n\t\treturn this.origin ?? this.mapping().bounds[0];\n\t}\n\n\tprotected playback(): number {\n\t\tconst from = this.heldFrom();\n\n\t\treturn from === undefined ? this.#cell.value : (this.#heldAt ?? from);\n\t}\n\n\tprotected proportionsChanged(): void {\n\t\t// Runs when every proportion moves, never per input\n\t}\n\n\tprotected readoutValue(): number {\n\t\treturn this.#shown().value ?? this.#cell.value;\n\t}\n\n\tprotected render(): void {\n\t\tif (this.isBound()) this.#renderControl();\n\t\tfor (const listener of this.#watchers) listener();\n\t}\n\n\tprotected renderReadout(): void {\n\t\tconst readout = this.#readout;\n\t\tif (!readout) return;\n\n\t\tconst { isOpen, value } = this.#shown();\n\t\tconst text = value === undefined ? undefined : this.#textFor(value);\n\n\t\treadout.bubble.show({ anchor: readout.anchor, isOpen: isOpen && text !== '', text });\n\t}\n\n\tprotected restoreState(state: string): void {\n\t\tthis.value = Number(state);\n\t}\n\n\tprotected scrubsKeyRepeat(): boolean {\n\t\treturn false;\n\t}\n\n\tprotected springTarget(): number | undefined {\n\t\treturn undefined;\n\t}\n\n\tprotected tapTarget(): number | undefined {\n\t\treturn undefined;\n\t}\n\n\tprotected travelPx(control: HTMLElement, property: `--_sonic-${string}`): number {\n\t\treturn Math.max(1, readPxProperty(getComputedStyle(control), property, fallbackTravelPx));\n\t}\n\n\t#entryText(): string {\n\t\tconst value = this.#cell.value;\n\n\t\tif (this.#formatEntry) return this.#formatEntry(value);\n\n\t\t// `Number.parseFloat` reads \"5 kHz\" as 5\n\t\treturn this.#parseValue ? this.valueText : String(value);\n\t}\n\n\t#isEditing(): boolean {\n\t\treturn this.#gestures?.isEditing() === true;\n\t}\n\n\t#isHeld(): boolean {\n\t\treturn this.#gestures?.isHeld() === true;\n\t}\n\n\t#renderAria(control: HTMLElement, orientation?: 'horizontal' | 'vertical'): void {\n\t\tthis.writeFormValue(String(this.#cell.value), String(this.#cell.value));\n\t\tif (this.#isEditing()) {\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.mapping().bounds;\n\n\t\twriteAttribute(control, 'role', this.controlRole());\n\t\twriteAttribute(control, 'aria-valuemin', String(low));\n\t\twriteAttribute(control, 'aria-valuemax', String(high));\n\t\twriteAttribute(control, 'aria-valuenow', String(this.#spokenNow()));\n\t\tif (orientation) writeAttribute(control, 'aria-orientation', orientation);\n\t\twriteAttribute(\n\t\t\tcontrol,\n\t\t\t'aria-valuetext',\n\t\t\t(this.#formatSpokenValue ?? this.#formatValue)?.(this.#cell.value),\n\t\t);\n\t\tthis.forwardNaming(control, true);\n\t\tthis.#renderDisabled(control);\n\t\tthis.renderReadout();\n\t}\n\n\t#renderControl(): void {\n\t\t// A reveal or typed entry takes the readout from a hover\n\t\tif (this.#isEditing() || this.isRevealed()) this.#hover?.clear();\n\t\tthis.draw();\n\t\tthis.toggleState(\n\t\t\t'at-origin',\n\t\t\t!this.isWrapping() && this.#cell.value === this.mapping().snap(this.originValue()),\n\t\t);\n\t\tthis.#renderAria(this.focusTarget(), this.controlOrientation());\n\t}\n\n\t#renderDisabled(control: HTMLElement): void {\n\t\tconst isDisabled = this.isDisabled();\n\n\t\twriteAttribute(control, 'aria-disabled', isDisabled ? 'true' : undefined);\n\t\t// The host is `display: contents`, so its tab stop is the control's\n\t\tconst stop = this.getAttribute('tabindex') === '-1' ? '-1' : '0';\n\n\t\twriteAttribute(control, 'tabindex', isDisabled ? undefined : stop);\n\t}\n\n\t#renderHold(): void {\n\t\tif (this.heldFrom() === undefined) this.#heldAt = undefined;\n\t\tthis.renderReadout();\n\t\tthis.holdChanged();\n\t}\n\n\t#shown(): { isOpen: boolean; value: number | undefined } {\n\t\tif (this.#isEditing()) return { isOpen: true, value: undefined };\n\n\t\tconst isRevealed = this.isRevealed();\n\t\tconst preview = isRevealed ? undefined : this.#hover?.value();\n\n\t\treturn {\n\t\t\tisOpen: (isRevealed || preview !== undefined) && this.readout,\n\t\t\tvalue: preview ?? this.#cell.value,\n\t\t};\n\t}\n\n\t#spokenNow(): number {\n\t\tconst step = this.spokenStep;\n\n\t\treturn step !== undefined && step > 0\n\t\t\t? trimFloat(Math.round(this.#cell.value / step) * step)\n\t\t\t: this.#cell.value;\n\t}\n\n\t#textFor(value: number): string {\n\t\treturn this.#formatValue?.(value) ?? String(value);\n\t}\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"value-element.js","names":[],"sources":["../../../src/elements/value-element.ts"],"sourcesContent":["import type { ValueAxis } from '#elements/value-gestures.ts';\nimport type { AskedValue } from '#lib/asked-value.ts';\nimport type { HoverPreview } from '#lib/hover-preview.ts';\nimport type { ValueMapping } from '#lib/value-mapping.ts';\n\nimport { SonicFormElement } from '#elements/form-element.ts';\nimport { Readout } from '#elements/readout.ts';\nimport { ValueGestures } from '#elements/value-gestures.ts';\nimport { moveTo, resnap, setAsked } from '#lib/asked-value.ts';\nimport { bindHoverPreview } from '#lib/hover-preview.ts';\nimport { clamp, toNumber, trimFloat } from '#lib/math.ts';\nimport { parseNumberList } from '#lib/number-list.ts';\nimport { readPxProperty } from '#lib/read-px-property.ts';\nimport { valueMapping } from '#lib/value-mapping.ts';\nimport { writeAttribute } from '#lib/write-attribute.ts';\n\n// eslint-disable-next-line unicorn/consistent-boolean-name -- the name bundlers and other kits use\ndeclare const __DEV__: boolean;\n\ninterface HoverBinding {\n\tcanShow?: () => boolean;\n\tplace: () => void;\n\tvalueAt: (event: PointerEvent) => number | undefined;\n}\n\nconst fallbackTravelPx = 160;\n\nlet instanceCount = 0;\n\nconst proportionAttributes = new Set(['max', 'midpoint', 'min', 'notched', 'positions', 'taper']);\n\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 ValueLanding = (target: number, direction: -1 | 0 | 1) => number;\n\nexport interface ValueLink {\n\tinput: (next: number, isMover?: boolean) => boolean;\n\tisDisabled: () => boolean;\n\tisHeld: () => boolean;\n\tland: (resolve: undefined | ValueLanding) => void;\n\tlimit: (bounds: [number, number] | undefined) => void;\n\tmapping: () => ValueMapping;\n\tvalue: () => number;\n\twatch: (listener: () => void) => () => void;\n}\n\n// Filled from inside the class, so it reaches protected members and nothing lands on the element's public type\nlet link: (element: SonicValueElement) => ValueLink;\n\nexport function linkValue(element: SonicValueElement): ValueLink {\n\treturn link(element);\n}\n\nexport abstract class SonicValueElement 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'spoken-step',\n\t\t'step',\n\t\t'tabindex',\n\t\t'taper',\n\t\t'value',\n\t\t'positions',\n\t];\n\n\tstatic {\n\t\tlink = (element) => ({\n\t\t\tinput: (next, isMover = false) => (isMover || !element.#isHeld()) && element.input(next),\n\t\t\tisDisabled: () => element.isDisabled(),\n\t\t\tisHeld: () => element.#isHeld(),\n\t\t\tland: (resolve) => {\n\t\t\t\telement.#land = resolve;\n\t\t\t},\n\t\t\tlimit: (bounds) => {\n\t\t\t\telement.#limit = bounds;\n\t\t\t},\n\t\t\tmapping: () => element.mapping(),\n\t\t\tvalue: () => element.#cell.value,\n\t\t\twatch: (listener) => {\n\t\t\t\telement.#watchers.add(listener);\n\n\t\t\t\treturn () => {\n\t\t\t\t\telement.#watchers.delete(listener);\n\t\t\t\t};\n\t\t\t},\n\t\t});\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\tget doublePress(): 'entry' | 'none' | 'reset' {\n\t\tconst gesture = this.getAttribute('double-press');\n\n\t\treturn gesture === 'none' || gesture === 'reset' ? gesture : 'entry';\n\t}\n\n\tset doublePress(gesture: 'entry' | 'none' | 'reset' | undefined) {\n\t\tthis.reflect('double-press', gesture);\n\t}\n\n\tget formatEntry(): ((value: number) => string) | undefined {\n\t\treturn this.#formatEntry;\n\t}\n\n\tset formatEntry(format: ((value: number) => string) | undefined) {\n\t\tthis.#formatEntry = format;\n\t}\n\n\tget formatSpokenValue(): ((value: number) => string) | undefined {\n\t\treturn this.#formatSpokenValue;\n\t}\n\n\tset formatSpokenValue(format: ((value: number) => string) | undefined) {\n\t\tthis.#formatSpokenValue = format;\n\t\tthis.render();\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\tget hoverValue(): number | undefined {\n\t\treturn this.#hover?.value();\n\t}\n\n\tget keyStep(): number | undefined {\n\t\treturn this.optionalNumberAttribute('key-step');\n\t}\n\n\tset keyStep(value: number | undefined) {\n\t\tthis.reflect('key-step', value);\n\t}\n\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\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 pointerType(): string | undefined {\n\t\treturn this.#gestures?.pointerType();\n\t}\n\n\tget positions(): Array<number> | undefined {\n\t\treturn parseNumberList(this.getAttribute('positions'));\n\t}\n\n\tset positions(positions: Array<number> | undefined) {\n\t\tthis.reflect('positions', positions?.join(' '));\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 spokenStep(): number | undefined {\n\t\treturn this.optionalNumberAttribute('spoken-step');\n\t}\n\n\tset spokenStep(value: number | undefined) {\n\t\tthis.reflect('spoken-step', value);\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.#cell.value;\n\t}\n\n\tset value(next: null | number | undefined) {\n\t\t// A framework removes a prop by setting the property to `undefined`\n\t\tif (next === undefined || next === null) {\n\t\t\tthis.formResetCallback();\n\t\t\treturn;\n\t\t}\n\n\t\tconst value = toNumber(next);\n\n\t\tif (this.#isHeld()) {\n\t\t\tthis.heldWrite(value);\n\t\t\treturn;\n\t\t}\n\n\t\tif (setAsked(this.#cell, this.mapping(), value)) this.render();\n\t}\n\n\tget valueText(): string {\n\t\treturn this.#textFor(this.#cell.value);\n\t}\n\n\treadonly #cell: AskedValue = { asked: undefined, value: 0 };\n\n\t#formatEntry: ((value: number) => string) | undefined;\n\n\t#formatSpokenValue: ((value: number) => string) | undefined;\n\n\t#formatValue: ((value: number) => string) | undefined;\n\n\t#gestures: undefined | ValueGestures;\n\n\t#heldAt: number | undefined;\n\n\t#hover: HoverPreview | undefined;\n\n\treadonly #instance = String((instanceCount += 1));\n\n\t#land: undefined | ValueLanding;\n\n\t#limit: [number, number] | undefined;\n\n\t// Every attribute it reads is observed; a subclass adding an input to `isWrapping()` has to observe it\n\t#mapping: undefined | ValueMapping;\n\n\t#parseValue: ((text: string) => number) | undefined;\n\n\t#positions: Array<number> | undefined;\n\n\t#readout: undefined | { anchor: HTMLElement; bubble: Readout };\n\n\treadonly #watchers = new Set<() => void>();\n\n\tattributeChangedCallback(name: string): void {\n\t\tthis.#mapping = undefined;\n\t\tthis.#positions = parseNumberList(this.getAttribute('positions'));\n\t\tif (name === 'value') {\n\t\t\tif (this.#isHeld()) {\n\t\t\t\tthis.heldWrite(this.numberAttribute('value', this.min));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.#cell.asked = undefined;\n\t\t}\n\t\tif (name === 'disabled' && this.isDisabled()) this.#gestures?.end();\n\n\t\tresnap(this.#cell, this.mapping(), this.numberAttribute('value', this.min));\n\t\tif (proportionAttributes.has(name) && this.isBound()) this.proportionsChanged();\n\t\tthis.render();\n\t}\n\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'spokenStep',\n\t\t\t'step',\n\t\t\t'taper',\n\t\t\t'positions',\n\t\t\t'detent',\n\t\t\t'formatEntry',\n\t\t\t'formatValue',\n\t\t\t'formatSpokenValue',\n\t\t\t'keyStep',\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) => undefined | ValueAxis,\n\t): void {\n\t\tconst bubble = control.querySelector<HTMLElement>('[popover]');\n\n\t\tif (bubble) this.#readout = { anchor: control, bubble: new Readout(bubble) };\n\t\tthis.#gestures = new ValueGestures(\n\t\t\t{\n\t\t\t\tdefault: () => this.default,\n\t\t\t\tdetent: () => this.detent,\n\t\t\t\tdispatch: (type) => {\n\t\t\t\t\tthis.dispatchEvent(new Event(type, { bubbles: true }));\n\t\t\t\t},\n\t\t\t\tdoublePress: () => this.doublePress,\n\t\t\t\tentryId: `sonic-entry-${this.#instance}`,\n\t\t\t\tentryText: () => this.#entryText(),\n\t\t\t\tforwardNaming: (target, isNamed) => {\n\t\t\t\t\tthis.forwardNaming(target, isNamed);\n\t\t\t\t},\n\t\t\t\tholdChanged: () => {\n\t\t\t\t\tthis.#renderHold();\n\t\t\t\t},\n\t\t\t\tinput: (next) => this.input(next),\n\t\t\t\tisDisabled: () => this.isDisabled(),\n\t\t\t\tkeyStep: () => this.keyStep,\n\t\t\t\tland: (target, direction) => this.#land?.(target, direction) ?? target,\n\t\t\t\tlimit: () => this.#limit,\n\t\t\t\tmapping: () => this.mapping(),\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\tparse: (text) => (this.#parseValue ?? Number.parseFloat)(text),\n\t\t\t\trender: () => {\n\t\t\t\t\tthis.render();\n\t\t\t\t},\n\t\t\t\tscrubsKeyRepeat: () => this.scrubsKeyRepeat(),\n\t\t\t\tspringTarget: () => this.springTarget(),\n\t\t\t\ttapTarget: () => this.tapTarget(),\n\t\t\t\ttoggleState: (state, isOn) => {\n\t\t\t\t\tthis.toggleState(state, isOn);\n\t\t\t\t},\n\t\t\t\tvalue: () => this.#cell.value,\n\t\t\t},\n\t\t\tcontrol,\n\t\t);\n\t\tthis.#gestures.bind(signal, grab);\n\t\tcontrol.addEventListener(\n\t\t\t'pointerup',\n\t\t\t(event) => {\n\t\t\t\tthis.#hover?.at(event);\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t}\n\n\tprotected bindHover(\n\t\tcontrol: HTMLElement,\n\t\tsignal: AbortSignal,\n\t\t{ canShow = () => true, place, valueAt }: HoverBinding,\n\t): void {\n\t\tthis.#hover = bindHoverPreview(\n\t\t\tcontrol,\n\t\t\t{\n\t\t\t\tcanShow,\n\t\t\t\tchanged: () => {\n\t\t\t\t\tthis.dispatchEvent(new Event('sonic-hover', { bubbles: true }));\n\t\t\t\t},\n\t\t\t\tdismiss: () => {\n\t\t\t\t\tthis.render();\n\t\t\t\t},\n\t\t\t\tisTaken: () => this.#isEditing() || this.isRevealed(),\n\t\t\t\tshow: () => {\n\t\t\t\t\tif (!this.readout) return;\n\n\t\t\t\t\tthis.renderReadout();\n\t\t\t\t\tplace();\n\t\t\t\t},\n\t\t\t\tvalueAt,\n\t\t\t},\n\t\t\tsignal,\n\t\t);\n\t}\n\n\tprotected controlOrientation(): 'horizontal' | 'vertical' | undefined {\n\t\treturn undefined;\n\t}\n\n\tprotected controlRole(): 'slider' | 'spinbutton' {\n\t\treturn 'slider';\n\t}\n\n\tprotected abstract draw(): void;\n\n\tprotected abstract override focusTarget(): HTMLElement;\n\n\tprotected heldFrom(): number | undefined {\n\t\treturn this.#gestures?.heldFrom();\n\t}\n\n\tprotected heldWrite(next: number): void {\n\t\tthis.#heldAt = next;\n\t}\n\n\tprotected holdChanged(): void {\n\t\t// Only a subclass draws a hold\n\t}\n\n\tprotected input(next: number): boolean {\n\t\tconst previous = this.#cell.value;\n\n\t\tconst target = this.#limit ? clamp(next, ...this.#limit) : next;\n\n\t\tif (moveTo(this.#cell, this.mapping(), target)) this.render();\n\t\tif (this.#cell.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 isRevealed(): boolean {\n\t\treturn this.#gestures?.isRevealed() === true;\n\t}\n\n\tprotected isWrapping(): boolean {\n\t\treturn false;\n\t}\n\n\tprotected mapping(): ValueMapping {\n\t\tif (this.#mapping) return this.#mapping;\n\n\t\tconst { detent, midpoint } = this;\n\n\t\tthis.#mapping = valueMapping({\n\t\t\t...(detent === undefined ? {} : { detent }),\n\t\t\t...(this.#positions ? { positions: this.#positions } : {}),\n\t\t\tmax: this.max,\n\t\t\t...(midpoint === undefined ? {} : { midpoint }),\n\t\t\tisNotched: this.notched,\n\t\t\tisWrapping: this.isWrapping(),\n\t\t\tmin: this.min,\n\t\t\tstep: this.step,\n\t\t\ttaper: this.taper,\n\t\t});\n\n\t\treturn this.#mapping;\n\t}\n\n\tprotected originValue(): number {\n\t\treturn this.origin ?? this.mapping().bounds[0];\n\t}\n\n\tprotected playback(): number {\n\t\tconst from = this.heldFrom();\n\n\t\treturn from === undefined ? this.#cell.value : (this.#heldAt ?? from);\n\t}\n\n\tprotected proportionsChanged(): void {\n\t\t// Runs when every proportion moves, never per input\n\t}\n\n\tprotected readoutValue(): number {\n\t\treturn this.#shown().value ?? this.#cell.value;\n\t}\n\n\tprotected render(): void {\n\t\tif (this.isBound()) this.#renderControl();\n\t\tfor (const listener of this.#watchers) listener();\n\t}\n\n\tprotected renderReadout(): void {\n\t\tconst readout = this.#readout;\n\t\tif (!readout) return;\n\n\t\tconst { isOpen, value } = this.#shown();\n\t\tconst text = value === undefined ? undefined : this.#textFor(value);\n\n\t\treadout.bubble.show({ anchor: readout.anchor, isOpen: isOpen && text !== '', text });\n\t}\n\n\tprotected restoreState(state: string): void {\n\t\tthis.value = Number(state);\n\t}\n\n\tprotected scrubsKeyRepeat(): boolean {\n\t\treturn false;\n\t}\n\n\tprotected springTarget(): number | undefined {\n\t\treturn undefined;\n\t}\n\n\tprotected tapTarget(): number | undefined {\n\t\treturn undefined;\n\t}\n\n\tprotected travelPx(control: HTMLElement, property: `--_sonic-${string}`): number {\n\t\treturn Math.max(1, readPxProperty(getComputedStyle(control), property, fallbackTravelPx));\n\t}\n\n\t#entryText(): string {\n\t\tconst value = this.#cell.value;\n\n\t\tif (this.#formatEntry) return this.#formatEntry(value);\n\n\t\t// `Number.parseFloat` reads \"5 kHz\" as 5\n\t\tif (!this.#parseValue) return String(value);\n\n\t\tconst text = this.valueText;\n\n\t\tif (__DEV__ && !Number.isFinite(this.#parseValue(text))) {\n\t\t\tconsole.warn(\n\t\t\t\t`<${this.localName}> opens its entry with \"${text}\", which parseValue cannot read; set formatEntry`,\n\t\t\t);\n\t\t}\n\n\t\treturn text;\n\t}\n\n\t#isEditing(): boolean {\n\t\treturn this.#gestures?.isEditing() === true;\n\t}\n\n\t#isHeld(): boolean {\n\t\treturn this.#gestures?.isHeld() === true;\n\t}\n\n\t#renderAria(control: HTMLElement, orientation?: 'horizontal' | 'vertical'): void {\n\t\tthis.writeFormValue(String(this.#cell.value), String(this.#cell.value));\n\t\tif (this.#isEditing()) {\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.mapping().bounds;\n\n\t\twriteAttribute(control, 'role', this.controlRole());\n\t\twriteAttribute(control, 'aria-valuemin', String(low));\n\t\twriteAttribute(control, 'aria-valuemax', String(high));\n\t\twriteAttribute(control, 'aria-valuenow', String(this.#spokenNow()));\n\t\tif (orientation) writeAttribute(control, 'aria-orientation', orientation);\n\t\twriteAttribute(\n\t\t\tcontrol,\n\t\t\t'aria-valuetext',\n\t\t\t(this.#formatSpokenValue ?? this.#formatValue)?.(this.#cell.value),\n\t\t);\n\t\tthis.forwardNaming(control, true);\n\t\tthis.#renderDisabled(control);\n\t\tthis.renderReadout();\n\t}\n\n\t#renderControl(): void {\n\t\t// A reveal or typed entry takes the readout from a hover\n\t\tif (this.#isEditing() || this.isRevealed()) this.#hover?.clear();\n\t\tthis.draw();\n\t\tthis.toggleState(\n\t\t\t'at-origin',\n\t\t\t!this.isWrapping() && this.#cell.value === this.mapping().snap(this.originValue()),\n\t\t);\n\t\tthis.#renderAria(this.focusTarget(), this.controlOrientation());\n\t}\n\n\t#renderDisabled(control: HTMLElement): void {\n\t\tconst isDisabled = this.isDisabled();\n\n\t\twriteAttribute(control, 'aria-disabled', isDisabled ? 'true' : undefined);\n\t\t// The host is `display: contents`, so its tab stop is the control's\n\t\tconst stop = this.getAttribute('tabindex') === '-1' ? '-1' : '0';\n\n\t\twriteAttribute(control, 'tabindex', isDisabled ? undefined : stop);\n\t}\n\n\t#renderHold(): void {\n\t\tif (this.heldFrom() === undefined) this.#heldAt = undefined;\n\t\tthis.renderReadout();\n\t\tthis.holdChanged();\n\t}\n\n\t#shown(): { isOpen: boolean; value: number | undefined } {\n\t\tif (this.#isEditing()) return { isOpen: true, value: undefined };\n\n\t\tconst isRevealed = this.isRevealed();\n\t\tconst preview = isRevealed ? undefined : this.#hover?.value();\n\n\t\treturn {\n\t\t\tisOpen: (isRevealed || preview !== undefined) && this.readout,\n\t\t\tvalue: preview ?? this.#cell.value,\n\t\t};\n\t}\n\n\t#spokenNow(): number {\n\t\tconst step = this.spokenStep;\n\n\t\treturn step !== undefined && step > 0\n\t\t\t? trimFloat(Math.round(this.#cell.value / step) * step)\n\t\t\t: this.#cell.value;\n\t}\n\n\t#textFor(value: number): string {\n\t\treturn this.#formatValue?.(value) ?? String(value);\n\t}\n}\n"],"mappings":"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"}
|
|
@@ -14,6 +14,7 @@ const detentZonePx = 8;
|
|
|
14
14
|
var ValueGestures = class {
|
|
15
15
|
#claim = new ReadoutClaim(() => {
|
|
16
16
|
this.#host.holdChanged();
|
|
17
|
+
this.#showRevealed();
|
|
17
18
|
});
|
|
18
19
|
#control;
|
|
19
20
|
#entry;
|
|
@@ -43,8 +44,12 @@ var ValueGestures = class {
|
|
|
43
44
|
return;
|
|
44
45
|
}
|
|
45
46
|
const fromValue = host.value();
|
|
47
|
+
host.toggleState("dragging", true);
|
|
46
48
|
const axis = grab(event);
|
|
47
|
-
if (!axis)
|
|
49
|
+
if (!axis) {
|
|
50
|
+
host.toggleState("dragging", false);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
48
53
|
const drag = {
|
|
49
54
|
...axis,
|
|
50
55
|
fromValue,
|
|
@@ -145,8 +150,9 @@ var ValueGestures = class {
|
|
|
145
150
|
const next = this.#keyTarget(event.key);
|
|
146
151
|
if (next === void 0) return;
|
|
147
152
|
event.preventDefault();
|
|
148
|
-
this.#keyTo(event, next);
|
|
149
153
|
this.#claim.reveal("keys");
|
|
154
|
+
this.#showRevealed();
|
|
155
|
+
this.#keyTo(event, next);
|
|
150
156
|
host.holdChanged();
|
|
151
157
|
}, { signal });
|
|
152
158
|
control.addEventListener("keyup", (event) => {
|
|
@@ -167,7 +173,18 @@ var ValueGestures = class {
|
|
|
167
173
|
if (host.input(landed)) host.dispatch("change");
|
|
168
174
|
}
|
|
169
175
|
#concealKeyReveal() {
|
|
170
|
-
if (this.#claim.conceal("keys"))
|
|
176
|
+
if (!this.#claim.conceal("keys")) return;
|
|
177
|
+
this.#host.holdChanged();
|
|
178
|
+
this.#showRevealed();
|
|
179
|
+
}
|
|
180
|
+
#crossCancelZone(drag, value) {
|
|
181
|
+
const host = this.#host;
|
|
182
|
+
const isOutside = !drag.isOutside;
|
|
183
|
+
drag.isOutside = isOutside;
|
|
184
|
+
host.toggleState("cancelling", isOutside);
|
|
185
|
+
if (isOutside) this.#renderDragReveal(drag);
|
|
186
|
+
host.input(isOutside ? drag.fromValue : value);
|
|
187
|
+
if (!isOutside) this.#renderDragReveal(drag);
|
|
171
188
|
}
|
|
172
189
|
#dragging() {
|
|
173
190
|
return this.#pointerDrag?.current();
|
|
@@ -185,13 +202,8 @@ var ValueGestures = class {
|
|
|
185
202
|
drag.state = this.#withinLimit(mapping, state);
|
|
186
203
|
if (value === void 0) return;
|
|
187
204
|
if (!wasEngaged) this.#reveal(drag);
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
drag.isOutside = isOutside;
|
|
191
|
-
host.toggleState("cancelling", isOutside);
|
|
192
|
-
}
|
|
193
|
-
host.input(isOutside ? drag.fromValue : value);
|
|
194
|
-
if (isCrossing) this.#renderDragReveal(drag);
|
|
205
|
+
if (isOutside === drag.isOutside) host.input(value);
|
|
206
|
+
else this.#crossCancelZone(drag, value);
|
|
195
207
|
}
|
|
196
208
|
#endKeyScrub() {
|
|
197
209
|
const host = this.#host;
|
|
@@ -221,16 +233,15 @@ var ValueGestures = class {
|
|
|
221
233
|
this.#claim.conceal("drag");
|
|
222
234
|
host.toggleState("cancelling", false);
|
|
223
235
|
host.holdChanged();
|
|
224
|
-
this.#showRevealed(
|
|
236
|
+
this.#showRevealed();
|
|
225
237
|
if (host.value() !== drag.fromValue) host.dispatch("change");
|
|
226
238
|
this.#springBack();
|
|
227
239
|
}
|
|
228
240
|
#renderDragReveal(drag) {
|
|
229
|
-
|
|
230
|
-
if (isRevealed) this.#claim.reveal("drag");
|
|
241
|
+
if (drag.isRevealed && !drag.isOutside) this.#claim.reveal("drag");
|
|
231
242
|
else this.#claim.conceal("drag");
|
|
232
243
|
this.#host.holdChanged();
|
|
233
|
-
this.#showRevealed(
|
|
244
|
+
this.#showRevealed();
|
|
234
245
|
}
|
|
235
246
|
#reset() {
|
|
236
247
|
this.#commit(this.#host.default() ?? NaN);
|
|
@@ -251,7 +262,8 @@ var ValueGestures = class {
|
|
|
251
262
|
}
|
|
252
263
|
host.input(next);
|
|
253
264
|
}
|
|
254
|
-
#showRevealed(
|
|
265
|
+
#showRevealed() {
|
|
266
|
+
const { isRevealed } = this.#claim;
|
|
255
267
|
if (isRevealed === this.#isRevealed) return;
|
|
256
268
|
this.#isRevealed = isRevealed;
|
|
257
269
|
this.#host.toggleState("revealed", isRevealed);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"value-gestures.js","names":[],"sources":["../../../src/elements/value-gestures.ts"],"sourcesContent":["import type { DragState } from '#lib/drag-step.ts';\nimport type { PointerDrag } from '#lib/pointer-drag.ts';\nimport type { ValueMapping } from '#lib/value-mapping.ts';\n\nimport { ReadoutClaim, revealDelay } from '#elements/readout-claim.ts';\nimport { ValueEntry } from '#elements/value-entry.ts';\nimport { dragThresholdPx, startDrag, stepDrag } from '#lib/drag-step.ts';\nimport { focusByPointer } from '#lib/focus-by-pointer.ts';\nimport { clamp } from '#lib/math.ts';\nimport { isMenuPress, isResetPress } from '#lib/modifier-press.ts';\nimport { bindDrag } from '#lib/pointer-drag.ts';\nimport { readPxProperty } from '#lib/read-px-property.ts';\nimport { requireChild } from '#lib/render.ts';\nimport { resetKeys } from '#lib/value-mapping.ts';\n\nexport interface ValueAxis {\n\tfromProportion?: number;\n\tisKeptOnCancel?: boolean;\n\toutside?: (event: PointerEvent) => boolean;\n\tposition: (event: PointerEvent) => number;\n\ttravelPx: number;\n}\n\ninterface GestureHost {\n\tdefault: () => number | undefined;\n\tdetent: () => number | undefined;\n\tdispatch: (type: 'change' | 'sonic-reveal') => void;\n\tdoublePress: () => 'entry' | 'none' | 'reset';\n\tentryId: string;\n\tentryText: () => string;\n\tforwardNaming: (target: Element, isNamed: boolean) => void;\n\tholdChanged: () => void;\n\tinput: (next: number) => boolean;\n\tisDisabled: () => boolean;\n\tkeyStep: () => number | undefined;\n\tland: (target: number, direction: -1 | 0 | 1) => number;\n\tlimit: () => [number, number] | undefined;\n\tmapping: () => ValueMapping;\n\tparse: (text: string) => number;\n\trender: () => void;\n\tscrubsKeyRepeat: () => boolean;\n\tspringTarget: () => number | undefined;\n\ttapTarget: () => number | undefined;\n\ttoggleState: (state: string, isOn: boolean) => void;\n\tvalue: () => number;\n}\n\ninterface KeyScrub {\n\tfromValue: number;\n\tkey: string;\n}\n\ninterface ValueDrag extends ValueAxis {\n\tfromValue: number;\n\tisOutside: boolean;\n\tisRevealed: boolean;\n\tpointerType: string;\n\trevealTimer: ReturnType<typeof setTimeout> | undefined;\n\tstate: DragState;\n}\n\nconst detentZonePx = 8;\n\nexport class ValueGestures {\n\treadonly #claim = new ReadoutClaim(() => {\n\t\tthis.#host.holdChanged();\n\t});\n\n\treadonly #control: HTMLElement;\n\n\t#entry: undefined | ValueEntry;\n\n\treadonly #host: GestureHost;\n\n\t#isRevealed = false;\n\n\t#keyScrub: KeyScrub | undefined;\n\n\t#pointerDrag: PointerDrag<ValueDrag> | undefined;\n\n\tconstructor(host: GestureHost, control: HTMLElement) {\n\t\tthis.#host = host;\n\t\tthis.#control = control;\n\t}\n\n\tbind(signal: AbortSignal, grab: (event: PointerEvent) => undefined | ValueAxis): void {\n\t\tconst control = this.#control;\n\t\tconst host = this.#host;\n\t\tconst entry = this.#bindEntry(signal);\n\n\t\tthis.#pointerDrag = bindDrag<ValueDrag>(\n\t\t\tcontrol,\n\t\t\t{\n\t\t\t\tcancel: (drag) => {\n\t\t\t\t\tif (drag.isKeptOnCancel !== true) host.input(drag.fromValue);\n\t\t\t\t},\n\t\t\t\tgrab: (event) => {\n\t\t\t\t\tif (isMenuPress(event) || host.isDisabled() || entry.isOpen) return;\n\n\t\t\t\t\thost.toggleState('springing', false);\n\t\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\t\tif (event.pointerType === 'touch') event.preventDefault();\n\t\t\t\t\tfocusByPointer(control);\n\t\t\t\t\tif (isResetPress(event)) {\n\t\t\t\t\t\tthis.#reset();\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst fromValue = host.value();\n\t\t\t\t\tconst axis = grab(event);\n\t\t\t\t\tif (!axis) return;\n\n\t\t\t\t\tconst drag: ValueDrag = {\n\t\t\t\t\t\t...axis,\n\t\t\t\t\t\tfromValue,\n\t\t\t\t\t\tisOutside: false,\n\t\t\t\t\t\tisRevealed: false,\n\t\t\t\t\t\tpointerType: event.pointerType,\n\t\t\t\t\t\trevealTimer: undefined,\n\t\t\t\t\t\tstate: this.#startDrag(axis, event),\n\t\t\t\t\t};\n\n\t\t\t\t\tdrag.revealTimer = setTimeout(() => {\n\t\t\t\t\t\tthis.#reveal(drag);\n\t\t\t\t\t}, revealDelay(control));\n\n\t\t\t\t\treturn drag;\n\t\t\t\t},\n\t\t\t\tlift: (drag, event) => {\n\t\t\t\t\tconst tap = drag.state.isEngaged ? undefined : host.tapTarget();\n\n\t\t\t\t\tif (tap !== undefined) {\n\t\t\t\t\t\tentry.forgetPress();\n\t\t\t\t\t\tthis.#commit(tap);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (!entry.press(event) || host.springTarget() !== undefined) return;\n\n\t\t\t\t\tconst gesture = host.doublePress();\n\n\t\t\t\t\tif (gesture === 'reset') this.#reset();\n\t\t\t\t\telse if (gesture === 'entry') entry.open();\n\t\t\t\t},\n\t\t\t\tmove: (drag, event) => {\n\t\t\t\t\tthis.#dragTo(drag, event);\n\t\t\t\t},\n\t\t\t\trelease: (drag) => {\n\t\t\t\t\tthis.#releaseDrag(drag);\n\t\t\t\t},\n\t\t\t\ttoggle: (isDragging) => {\n\t\t\t\t\thost.toggleState('dragging', isDragging);\n\t\t\t\t},\n\t\t\t},\n\t\t\tsignal,\n\t\t);\n\t\tcontrol.addEventListener(\n\t\t\t'pointercancel',\n\t\t\t() => {\n\t\t\t\tentry.forgetPress();\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t\tthis.#bindKeys(entry, signal);\n\t\tsignal.addEventListener(\n\t\t\t'abort',\n\t\t\t() => {\n\t\t\t\tthis.#endKeyScrub();\n\t\t\t\tthis.#concealKeyReveal();\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\t}\n\n\tend(): void {\n\t\tthis.#entry?.close(false);\n\t\tthis.#pointerDrag?.end();\n\t\tthis.#endKeyScrub();\n\t}\n\n\theldFrom(): number | undefined {\n\t\treturn this.#dragging()?.fromValue ?? this.#keyScrub?.fromValue;\n\t}\n\n\tisEditing(): boolean {\n\t\treturn this.#entry?.isOpen === true;\n\t}\n\n\tisHeld(): boolean {\n\t\treturn this.#dragging() !== undefined || this.#keyScrub !== undefined;\n\t}\n\n\tisRevealed(): boolean {\n\t\treturn this.#claim.isRevealed;\n\t}\n\n\tpointerType(): string | undefined {\n\t\treturn this.#dragging()?.pointerType;\n\t}\n\n\t#bindEntry(signal: AbortSignal): ValueEntry {\n\t\tconst control = this.#control;\n\t\tconst host = this.#host;\n\t\tconst input = requireChild(control, 'input', HTMLInputElement);\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 = host.entryId;\n\n\t\tconst entry = new ValueEntry(input, {\n\t\t\tcommit: (text) => {\n\t\t\t\tthis.#commit(host.parse(text));\n\t\t\t},\n\t\t\ttext: host.entryText,\n\t\t\ttoggle: (isOpen) => {\n\t\t\t\tif (isOpen) host.forwardNaming(input, true);\n\t\t\t\thost.toggleState('editing', isOpen);\n\t\t\t\thost.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(entry: ValueEntry, signal: AbortSignal): void {\n\t\tconst control = this.#control;\n\t\tconst host = this.#host;\n\n\t\tcontrol.addEventListener(\n\t\t\t'keydown',\n\t\t\t(event) => {\n\t\t\t\tif (host.isDisabled() || event.defaultPrevented || event.target !== control) return;\n\n\t\t\t\thost.toggleState('springing', false);\n\t\t\t\tif (event.key === 'Enter') {\n\t\t\t\t\tif (host.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.#keyTo(event, next);\n\t\t\t\tthis.#claim.reveal('keys');\n\t\t\t\thost.holdChanged();\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\t// macOS sends no `keyup` for a key let go while Cmd is down\n\t\t\t\tconst isMeta = event.key === 'Meta';\n\n\t\t\t\tif (isMeta || event.key === this.#keyScrub?.key) this.#endKeyScrub();\n\t\t\t\tif (isMeta && this.#dragging()) return;\n\t\t\t\tif (isMeta || host.mapping().keyTarget(event.key, host.value()) !== undefined) {\n\t\t\t\t\tthis.#springBack();\n\t\t\t\t}\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.#endKeyScrub();\n\t\t\t\tthis.#concealKeyReveal();\n\t\t\t\tthis.#springBack();\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t}\n\n\t#commit(next: number, direction: -1 | 0 | 1 = 0): void {\n\t\tconst host = this.#host;\n\t\tconst landed = Number.isFinite(next) ? host.land(next, direction) : next;\n\n\t\tif (host.input(landed)) host.dispatch('change');\n\t}\n\n\t#concealKeyReveal(): void {\n\t\tif (this.#claim.conceal('keys')) this.#host.holdChanged();\n\t}\n\n\t#dragging(): undefined | ValueDrag {\n\t\treturn this.#pointerDrag?.current();\n\t}\n\n\t#dragTo(drag: ValueDrag, event: PointerEvent): void {\n\t\tconst host = this.#host;\n\t\tconst isOutside = drag.outside?.(event) === true;\n\t\tconst wasEngaged = drag.state.isEngaged;\n\t\tconst mapping = host.mapping();\n\t\tconst { state, value } = stepDrag(mapping, drag.state, {\n\t\t\tisFine: event.shiftKey,\n\t\t\tisOutside,\n\t\t\tposition: drag.position(event),\n\t\t});\n\n\t\tdrag.state = this.#withinLimit(mapping, state);\n\t\tif (value === undefined) return;\n\n\t\tif (!wasEngaged) this.#reveal(drag);\n\n\t\tconst isCrossing = isOutside !== drag.isOutside;\n\n\t\tif (isCrossing) {\n\t\t\tdrag.isOutside = isOutside;\n\t\t\thost.toggleState('cancelling', isOutside);\n\t\t}\n\t\thost.input(isOutside ? drag.fromValue : value);\n\t\tif (isCrossing) this.#renderDragReveal(drag);\n\t}\n\n\t#endKeyScrub(): void {\n\t\tconst host = this.#host;\n\t\tconst scrub = this.#keyScrub;\n\t\tif (!scrub) return;\n\n\t\tthis.#keyScrub = undefined;\n\t\thost.holdChanged();\n\t\tif (host.value() !== scrub.fromValue) host.dispatch('change');\n\t}\n\n\t#keyTarget(key: string): number | undefined {\n\t\tconst host = this.#host;\n\n\t\tif (resetKeys.has(key)) return host.default();\n\n\t\treturn host.mapping().keyTarget(key, host.value(), host.keyStep());\n\t}\n\n\t#keyTo(event: KeyboardEvent, next: number): void {\n\t\tconst host = this.#host;\n\n\t\tif (event.repeat && host.scrubsKeyRepeat()) {\n\t\t\tthis.#scrubKey(event.key, next);\n\t\t\treturn;\n\t\t}\n\n\t\tthis.#endKeyScrub();\n\t\tthis.#commit(next, next > host.value() ? 1 : -1);\n\t}\n\n\t#releaseDrag(drag: ValueDrag): void {\n\t\tconst host = this.#host;\n\n\t\tclearTimeout(drag.revealTimer);\n\t\tthis.#claim.conceal('drag');\n\t\thost.toggleState('cancelling', false);\n\t\thost.holdChanged();\n\t\tthis.#showRevealed(false);\n\t\tif (host.value() !== drag.fromValue) host.dispatch('change');\n\t\tthis.#springBack();\n\t}\n\n\t#renderDragReveal(drag: ValueDrag): void {\n\t\tconst isRevealed = drag.isRevealed && !drag.isOutside;\n\n\t\tif (isRevealed) this.#claim.reveal('drag');\n\t\telse this.#claim.conceal('drag');\n\t\tthis.#host.holdChanged();\n\t\tthis.#showRevealed(isRevealed);\n\t}\n\n\t#reset(): void {\n\t\tthis.#commit(this.#host.default() ?? NaN);\n\t}\n\n\t#reveal(drag: ValueDrag): void {\n\t\tif (drag.isRevealed || this.#dragging() !== drag) return;\n\n\t\tdrag.isRevealed = true;\n\t\tthis.#renderDragReveal(drag);\n\t}\n\n\t#scrubKey(key: string, next: number): void {\n\t\tconst host = this.#host;\n\n\t\tif (this.#keyScrub?.key !== key) {\n\t\t\tthis.#endKeyScrub();\n\t\t\tthis.#keyScrub = { fromValue: host.value(), key };\n\t\t}\n\t\thost.input(next);\n\t}\n\n\t#showRevealed(isRevealed: boolean): void {\n\t\tif (isRevealed === this.#isRevealed) return;\n\n\t\tthis.#isRevealed = isRevealed;\n\t\tthis.#host.toggleState('revealed', isRevealed);\n\t\tthis.#host.dispatch('sonic-reveal');\n\t}\n\n\t#springBack(): void {\n\t\tconst host = this.#host;\n\t\tconst target = host.springTarget();\n\t\tif (target === undefined) return;\n\n\t\t// Set before the commit so a `change` listener reading styles sees the glide\n\t\tif (host.mapping().snap(target) !== host.value()) host.toggleState('springing', true);\n\t\tthis.#commit(target);\n\t}\n\n\t#startDrag(axis: ValueAxis, event: PointerEvent): DragState {\n\t\tconst host = this.#host;\n\t\tconst mapping = host.mapping();\n\t\tconst detent = host.detent();\n\t\tconst value = host.value();\n\t\tconst start = {\n\t\t\tfrom: value,\n\t\t\tposition: axis.position(event),\n\t\t\tproportion: axis.fromProportion ?? mapping.proportionOf(value),\n\t\t\tthresholdPx: dragThresholdPx(event.pointerType),\n\t\t\ttravelPx: axis.travelPx,\n\t\t};\n\t\tif (detent === undefined) return startDrag(mapping, start);\n\n\t\tconst zonePx = readPxProperty(\n\t\t\tgetComputedStyle(this.#control),\n\t\t\t'--_sonic-detent-zone',\n\t\t\tdetentZonePx,\n\t\t);\n\n\t\treturn startDrag(mapping, {\n\t\t\t...start,\n\t\t\tdetent: { value: detent, zone: zonePx / axis.travelPx },\n\t\t});\n\t}\n\n\t#withinLimit(mapping: ValueMapping, state: DragState): DragState {\n\t\tconst limit = this.#host.limit();\n\t\tif (!limit || mapping.isWrapping) return state;\n\n\t\tconst [low, high] = limit;\n\n\t\treturn {\n\t\t\t...state,\n\t\t\trawProportion: clamp(\n\t\t\t\tstate.rawProportion,\n\t\t\t\tmapping.proportionOf(low),\n\t\t\t\tmapping.proportionOf(high),\n\t\t\t),\n\t\t};\n\t}\n}\n"],"mappings":";;;;;;;;;;;;AA6DA,MAAM,eAAe;AAErB,IAAa,gBAAb,MAA2B;CAC1B,AAAS,SAAS,IAAI,mBAAmB;EACxC,KAAK,MAAM,YAAY;CACxB,CAAC;CAED,AAAS;CAET;CAEA,AAAS;CAET,cAAc;CAEd;CAEA;CAEA,YAAY,MAAmB,SAAsB;EACpD,KAAK,QAAQ;EACb,KAAK,WAAW;CACjB;CAEA,KAAK,QAAqB,MAA4D;EACrF,MAAM,UAAU,KAAK;EACrB,MAAM,OAAO,KAAK;EAClB,MAAM,QAAQ,KAAK,WAAW,MAAM;EAEpC,KAAK,eAAe,SACnB,SACA;GACC,SAAS,SAAS;IACjB,IAAI,KAAK,mBAAmB,MAAM,KAAK,MAAM,KAAK,SAAS;GAC5D;GACA,OAAO,UAAU;IAChB,IAAI,YAAY,KAAK,KAAK,KAAK,WAAW,KAAK,MAAM,QAAQ;IAE7D,KAAK,YAAY,aAAa,KAAK;IAEnC,IAAI,MAAM,gBAAgB,SAAS,MAAM,eAAe;IACxD,eAAe,OAAO;IACtB,IAAI,aAAa,KAAK,GAAG;KACxB,KAAK,OAAO;KACZ;IACD;IAEA,MAAM,YAAY,KAAK,MAAM;IAC7B,MAAM,OAAO,KAAK,KAAK;IACvB,IAAI,CAAC,MAAM;IAEX,MAAM,OAAkB;KACvB,GAAG;KACH;KACA,WAAW;KACX,YAAY;KACZ,aAAa,MAAM;KACnB,aAAa;KACb,OAAO,KAAK,WAAW,MAAM,KAAK;IACnC;IAEA,KAAK,cAAc,iBAAiB;KACnC,KAAK,QAAQ,IAAI;IAClB,GAAG,YAAY,OAAO,CAAC;IAEvB,OAAO;GACR;GACA,OAAO,MAAM,UAAU;IACtB,MAAM,MAAM,KAAK,MAAM,YAAY,SAAY,KAAK,UAAU;IAE9D,IAAI,QAAQ,QAAW;KACtB,MAAM,YAAY;KAClB,KAAK,QAAQ,GAAG;KAChB;IACD;IACA,IAAI,CAAC,MAAM,MAAM,KAAK,KAAK,KAAK,aAAa,MAAM,QAAW;IAE9D,MAAM,UAAU,KAAK,YAAY;IAEjC,IAAI,YAAY,SAAS,KAAK,OAAO;SAChC,IAAI,YAAY,SAAS,MAAM,KAAK;GAC1C;GACA,OAAO,MAAM,UAAU;IACtB,KAAK,QAAQ,MAAM,KAAK;GACzB;GACA,UAAU,SAAS;IAClB,KAAK,aAAa,IAAI;GACvB;GACA,SAAS,eAAe;IACvB,KAAK,YAAY,YAAY,UAAU;GACxC;EACD,GACA,MACD;EACA,QAAQ,iBACP,uBACM;GACL,MAAM,YAAY;EACnB,GACA,EAAE,OAAO,CACV;EACA,KAAK,UAAU,OAAO,MAAM;EAC5B,OAAO,iBACN,eACM;GACL,KAAK,aAAa;GAClB,KAAK,kBAAkB;EACxB,GACA,EAAE,MAAM,KAAK,CACd;CACD;CAEA,MAAY;EACX,KAAK,QAAQ,MAAM,KAAK;EACxB,KAAK,cAAc,IAAI;EACvB,KAAK,aAAa;CACnB;CAEA,WAA+B;EAC9B,OAAO,KAAK,UAAU,CAAC,EAAE,aAAa,KAAK,WAAW;CACvD;CAEA,YAAqB;EACpB,OAAO,KAAK,QAAQ,WAAW;CAChC;CAEA,SAAkB;EACjB,OAAO,KAAK,UAAU,MAAM,UAAa,KAAK,cAAc;CAC7D;CAEA,aAAsB;EACrB,OAAO,KAAK,OAAO;CACpB;CAEA,cAAkC;EACjC,OAAO,KAAK,UAAU,CAAC,EAAE;CAC1B;CAEA,WAAW,QAAiC;EAC3C,MAAM,UAAU,KAAK;EACrB,MAAM,OAAO,KAAK;EAClB,MAAM,QAAQ,aAAa,SAAS,SAAS,gBAAgB;EAG7D,MAAM,KAAK,KAAK;EAEhB,MAAM,QAAQ,IAAI,WAAW,OAAO;GACnC,SAAS,SAAS;IACjB,KAAK,QAAQ,KAAK,MAAM,IAAI,CAAC;GAC9B;GACA,MAAM,KAAK;GACX,SAAS,WAAW;IACnB,IAAI,QAAQ,KAAK,cAAc,OAAO,IAAI;IAC1C,KAAK,YAAY,WAAW,MAAM;IAClC,KAAK,OAAO;GACb;EACD,CAAC;EAED,KAAK,SAAS;EACd,MAAM,KAAK,SAAS,MAAM;EAE1B,OAAO;CACR;CAEA,UAAU,OAAmB,QAA2B;EACvD,MAAM,UAAU,KAAK;EACrB,MAAM,OAAO,KAAK;EAElB,QAAQ,iBACP,YACC,UAAU;GACV,IAAI,KAAK,WAAW,KAAK,MAAM,oBAAoB,MAAM,WAAW,SAAS;GAE7E,KAAK,YAAY,aAAa,KAAK;GACnC,IAAI,MAAM,QAAQ,SAAS;IAC1B,IAAI,KAAK,aAAa,MAAM,QAAW;IAEvC,MAAM,eAAe;IACrB,MAAM,KAAK;IACX;GACD;GAEA,MAAM,OAAO,KAAK,WAAW,MAAM,GAAG;GACtC,IAAI,SAAS,QAAW;GAExB,MAAM,eAAe;GACrB,KAAK,OAAO,OAAO,IAAI;GACvB,KAAK,OAAO,OAAO,MAAM;GACzB,KAAK,YAAY;EAClB,GACA,EAAE,OAAO,CACV;EACA,QAAQ,iBACP,UACC,UAAU;GAEV,MAAM,SAAS,MAAM,QAAQ;GAE7B,IAAI,UAAU,MAAM,QAAQ,KAAK,WAAW,KAAK,KAAK,aAAa;GACnE,IAAI,UAAU,KAAK,UAAU,GAAG;GAChC,IAAI,UAAU,KAAK,QAAQ,CAAC,CAAC,UAAU,MAAM,KAAK,KAAK,MAAM,CAAC,MAAM,QACnE,KAAK,YAAY;EAEnB,GACA,EAAE,OAAO,CACV;EACA,QAAQ,iBACP,cACM;GACL,KAAK,aAAa;GAClB,KAAK,kBAAkB;GACvB,KAAK,YAAY;EAClB,GACA,EAAE,OAAO,CACV;CACD;CAEA,QAAQ,MAAc,YAAwB,GAAS;EACtD,MAAM,OAAO,KAAK;EAClB,MAAM,SAAS,OAAO,SAAS,IAAI,IAAI,KAAK,KAAK,MAAM,SAAS,IAAI;EAEpE,IAAI,KAAK,MAAM,MAAM,GAAG,KAAK,SAAS,QAAQ;CAC/C;CAEA,oBAA0B;EACzB,IAAI,KAAK,OAAO,QAAQ,MAAM,GAAG,KAAK,MAAM,YAAY;CACzD;CAEA,YAAmC;EAClC,OAAO,KAAK,cAAc,QAAQ;CACnC;CAEA,QAAQ,MAAiB,OAA2B;EACnD,MAAM,OAAO,KAAK;EAClB,MAAM,YAAY,KAAK,UAAU,KAAK,MAAM;EAC5C,MAAM,aAAa,KAAK,MAAM;EAC9B,MAAM,UAAU,KAAK,QAAQ;EAC7B,MAAM,EAAE,OAAO,UAAU,SAAS,SAAS,KAAK,OAAO;GACtD,QAAQ,MAAM;GACd;GACA,UAAU,KAAK,SAAS,KAAK;EAC9B,CAAC;EAED,KAAK,QAAQ,KAAK,aAAa,SAAS,KAAK;EAC7C,IAAI,UAAU,QAAW;EAEzB,IAAI,CAAC,YAAY,KAAK,QAAQ,IAAI;EAElC,MAAM,aAAa,cAAc,KAAK;EAEtC,IAAI,YAAY;GACf,KAAK,YAAY;GACjB,KAAK,YAAY,cAAc,SAAS;EACzC;EACA,KAAK,MAAM,YAAY,KAAK,YAAY,KAAK;EAC7C,IAAI,YAAY,KAAK,kBAAkB,IAAI;CAC5C;CAEA,eAAqB;EACpB,MAAM,OAAO,KAAK;EAClB,MAAM,QAAQ,KAAK;EACnB,IAAI,CAAC,OAAO;EAEZ,KAAK,YAAY;EACjB,KAAK,YAAY;EACjB,IAAI,KAAK,MAAM,MAAM,MAAM,WAAW,KAAK,SAAS,QAAQ;CAC7D;CAEA,WAAW,KAAiC;EAC3C,MAAM,OAAO,KAAK;EAElB,IAAI,UAAU,IAAI,GAAG,GAAG,OAAO,KAAK,QAAQ;EAE5C,OAAO,KAAK,QAAQ,CAAC,CAAC,UAAU,KAAK,KAAK,MAAM,GAAG,KAAK,QAAQ,CAAC;CAClE;CAEA,OAAO,OAAsB,MAAoB;EAChD,MAAM,OAAO,KAAK;EAElB,IAAI,MAAM,UAAU,KAAK,gBAAgB,GAAG;GAC3C,KAAK,UAAU,MAAM,KAAK,IAAI;GAC9B;EACD;EAEA,KAAK,aAAa;EAClB,KAAK,QAAQ,MAAM,OAAO,KAAK,MAAM,IAAI,IAAI,EAAE;CAChD;CAEA,aAAa,MAAuB;EACnC,MAAM,OAAO,KAAK;EAElB,aAAa,KAAK,WAAW;EAC7B,KAAK,OAAO,QAAQ,MAAM;EAC1B,KAAK,YAAY,cAAc,KAAK;EACpC,KAAK,YAAY;EACjB,KAAK,cAAc,KAAK;EACxB,IAAI,KAAK,MAAM,MAAM,KAAK,WAAW,KAAK,SAAS,QAAQ;EAC3D,KAAK,YAAY;CAClB;CAEA,kBAAkB,MAAuB;EACxC,MAAM,aAAa,KAAK,cAAc,CAAC,KAAK;EAE5C,IAAI,YAAY,KAAK,OAAO,OAAO,MAAM;OACpC,KAAK,OAAO,QAAQ,MAAM;EAC/B,KAAK,MAAM,YAAY;EACvB,KAAK,cAAc,UAAU;CAC9B;CAEA,SAAe;EACd,KAAK,QAAQ,KAAK,MAAM,QAAQ,KAAK,GAAG;CACzC;CAEA,QAAQ,MAAuB;EAC9B,IAAI,KAAK,cAAc,KAAK,UAAU,MAAM,MAAM;EAElD,KAAK,aAAa;EAClB,KAAK,kBAAkB,IAAI;CAC5B;CAEA,UAAU,KAAa,MAAoB;EAC1C,MAAM,OAAO,KAAK;EAElB,IAAI,KAAK,WAAW,QAAQ,KAAK;GAChC,KAAK,aAAa;GAClB,KAAK,YAAY;IAAE,WAAW,KAAK,MAAM;IAAG;GAAI;EACjD;EACA,KAAK,MAAM,IAAI;CAChB;CAEA,cAAc,YAA2B;EACxC,IAAI,eAAe,KAAK,aAAa;EAErC,KAAK,cAAc;EACnB,KAAK,MAAM,YAAY,YAAY,UAAU;EAC7C,KAAK,MAAM,SAAS,cAAc;CACnC;CAEA,cAAoB;EACnB,MAAM,OAAO,KAAK;EAClB,MAAM,SAAS,KAAK,aAAa;EACjC,IAAI,WAAW,QAAW;EAG1B,IAAI,KAAK,QAAQ,CAAC,CAAC,KAAK,MAAM,MAAM,KAAK,MAAM,GAAG,KAAK,YAAY,aAAa,IAAI;EACpF,KAAK,QAAQ,MAAM;CACpB;CAEA,WAAW,MAAiB,OAAgC;EAC3D,MAAM,OAAO,KAAK;EAClB,MAAM,UAAU,KAAK,QAAQ;EAC7B,MAAM,SAAS,KAAK,OAAO;EAC3B,MAAM,QAAQ,KAAK,MAAM;EACzB,MAAM,QAAQ;GACb,MAAM;GACN,UAAU,KAAK,SAAS,KAAK;GAC7B,YAAY,KAAK,kBAAkB,QAAQ,aAAa,KAAK;GAC7D,aAAa,gBAAgB,MAAM,WAAW;GAC9C,UAAU,KAAK;EAChB;EACA,IAAI,WAAW,QAAW,OAAO,UAAU,SAAS,KAAK;EAEzD,MAAM,SAAS,eACd,iBAAiB,KAAK,QAAQ,GAC9B,wBACA,YACD;EAEA,OAAO,UAAU,SAAS;GACzB,GAAG;GACH,QAAQ;IAAE,OAAO;IAAQ,MAAM,SAAS,KAAK;GAAS;EACvD,CAAC;CACF;CAEA,aAAa,SAAuB,OAA6B;EAChE,MAAM,QAAQ,KAAK,MAAM,MAAM;EAC/B,IAAI,CAAC,SAAS,QAAQ,YAAY,OAAO;EAEzC,MAAM,CAAC,KAAK,QAAQ;EAEpB,OAAO;GACN,GAAG;GACH,eAAe,MACd,MAAM,eACN,QAAQ,aAAa,GAAG,GACxB,QAAQ,aAAa,IAAI,CAC1B;EACD;CACD;AACD"}
|
|
1
|
+
{"version":3,"file":"value-gestures.js","names":[],"sources":["../../../src/elements/value-gestures.ts"],"sourcesContent":["import type { DragState } from '#lib/drag-step.ts';\nimport type { PointerDrag } from '#lib/pointer-drag.ts';\nimport type { ValueMapping } from '#lib/value-mapping.ts';\n\nimport { ReadoutClaim, revealDelay } from '#elements/readout-claim.ts';\nimport { ValueEntry } from '#elements/value-entry.ts';\nimport { dragThresholdPx, startDrag, stepDrag } from '#lib/drag-step.ts';\nimport { focusByPointer } from '#lib/focus-by-pointer.ts';\nimport { clamp } from '#lib/math.ts';\nimport { isMenuPress, isResetPress } from '#lib/modifier-press.ts';\nimport { bindDrag } from '#lib/pointer-drag.ts';\nimport { readPxProperty } from '#lib/read-px-property.ts';\nimport { requireChild } from '#lib/render.ts';\nimport { resetKeys } from '#lib/value-mapping.ts';\n\nexport interface ValueAxis {\n\tfromProportion?: number;\n\tisKeptOnCancel?: boolean;\n\toutside?: (event: PointerEvent) => boolean;\n\tposition: (event: PointerEvent) => number;\n\ttravelPx: number;\n}\n\ninterface GestureHost {\n\tdefault: () => number | undefined;\n\tdetent: () => number | undefined;\n\tdispatch: (type: 'change' | 'sonic-reveal') => void;\n\tdoublePress: () => 'entry' | 'none' | 'reset';\n\tentryId: string;\n\tentryText: () => string;\n\tforwardNaming: (target: Element, isNamed: boolean) => void;\n\tholdChanged: () => void;\n\tinput: (next: number) => boolean;\n\tisDisabled: () => boolean;\n\tkeyStep: () => number | undefined;\n\tland: (target: number, direction: -1 | 0 | 1) => number;\n\tlimit: () => [number, number] | undefined;\n\tmapping: () => ValueMapping;\n\tparse: (text: string) => number;\n\trender: () => void;\n\tscrubsKeyRepeat: () => boolean;\n\tspringTarget: () => number | undefined;\n\ttapTarget: () => number | undefined;\n\ttoggleState: (state: string, isOn: boolean) => void;\n\tvalue: () => number;\n}\n\ninterface KeyScrub {\n\tfromValue: number;\n\tkey: string;\n}\n\ninterface ValueDrag extends ValueAxis {\n\tfromValue: number;\n\tisOutside: boolean;\n\tisRevealed: boolean;\n\tpointerType: string;\n\trevealTimer: ReturnType<typeof setTimeout> | undefined;\n\tstate: DragState;\n}\n\nconst detentZonePx = 8;\n\nexport class ValueGestures {\n\treadonly #claim = new ReadoutClaim(() => {\n\t\tthis.#host.holdChanged();\n\t\tthis.#showRevealed();\n\t});\n\n\treadonly #control: HTMLElement;\n\n\t#entry: undefined | ValueEntry;\n\n\treadonly #host: GestureHost;\n\n\t#isRevealed = false;\n\n\t#keyScrub: KeyScrub | undefined;\n\n\t#pointerDrag: PointerDrag<ValueDrag> | undefined;\n\n\tconstructor(host: GestureHost, control: HTMLElement) {\n\t\tthis.#host = host;\n\t\tthis.#control = control;\n\t}\n\n\tbind(signal: AbortSignal, grab: (event: PointerEvent) => undefined | ValueAxis): void {\n\t\tconst control = this.#control;\n\t\tconst host = this.#host;\n\t\tconst entry = this.#bindEntry(signal);\n\n\t\tthis.#pointerDrag = bindDrag<ValueDrag>(\n\t\t\tcontrol,\n\t\t\t{\n\t\t\t\tcancel: (drag) => {\n\t\t\t\t\tif (drag.isKeptOnCancel !== true) host.input(drag.fromValue);\n\t\t\t\t},\n\t\t\t\tgrab: (event) => {\n\t\t\t\t\tif (isMenuPress(event) || host.isDisabled() || entry.isOpen) return;\n\n\t\t\t\t\thost.toggleState('springing', false);\n\t\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\t\tif (event.pointerType === 'touch') event.preventDefault();\n\t\t\t\t\tfocusByPointer(control);\n\t\t\t\t\tif (isResetPress(event)) {\n\t\t\t\t\t\tthis.#reset();\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst fromValue = host.value();\n\n\t\t\t\t\thost.toggleState('dragging', true);\n\n\t\t\t\t\tconst axis = grab(event);\n\t\t\t\t\tif (!axis) {\n\t\t\t\t\t\thost.toggleState('dragging', false);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst drag: ValueDrag = {\n\t\t\t\t\t\t...axis,\n\t\t\t\t\t\tfromValue,\n\t\t\t\t\t\tisOutside: false,\n\t\t\t\t\t\tisRevealed: false,\n\t\t\t\t\t\tpointerType: event.pointerType,\n\t\t\t\t\t\trevealTimer: undefined,\n\t\t\t\t\t\tstate: this.#startDrag(axis, event),\n\t\t\t\t\t};\n\n\t\t\t\t\tdrag.revealTimer = setTimeout(() => {\n\t\t\t\t\t\tthis.#reveal(drag);\n\t\t\t\t\t}, revealDelay(control));\n\n\t\t\t\t\treturn drag;\n\t\t\t\t},\n\t\t\t\tlift: (drag, event) => {\n\t\t\t\t\tconst tap = drag.state.isEngaged ? undefined : host.tapTarget();\n\n\t\t\t\t\tif (tap !== undefined) {\n\t\t\t\t\t\tentry.forgetPress();\n\t\t\t\t\t\tthis.#commit(tap);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (!entry.press(event) || host.springTarget() !== undefined) return;\n\n\t\t\t\t\tconst gesture = host.doublePress();\n\n\t\t\t\t\tif (gesture === 'reset') this.#reset();\n\t\t\t\t\telse if (gesture === 'entry') entry.open();\n\t\t\t\t},\n\t\t\t\tmove: (drag, event) => {\n\t\t\t\t\tthis.#dragTo(drag, event);\n\t\t\t\t},\n\t\t\t\trelease: (drag) => {\n\t\t\t\t\tthis.#releaseDrag(drag);\n\t\t\t\t},\n\t\t\t\ttoggle: (isDragging) => {\n\t\t\t\t\thost.toggleState('dragging', isDragging);\n\t\t\t\t},\n\t\t\t},\n\t\t\tsignal,\n\t\t);\n\t\tcontrol.addEventListener(\n\t\t\t'pointercancel',\n\t\t\t() => {\n\t\t\t\tentry.forgetPress();\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t\tthis.#bindKeys(entry, signal);\n\t\tsignal.addEventListener(\n\t\t\t'abort',\n\t\t\t() => {\n\t\t\t\tthis.#endKeyScrub();\n\t\t\t\tthis.#concealKeyReveal();\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\t}\n\n\tend(): void {\n\t\tthis.#entry?.close(false);\n\t\tthis.#pointerDrag?.end();\n\t\tthis.#endKeyScrub();\n\t}\n\n\theldFrom(): number | undefined {\n\t\treturn this.#dragging()?.fromValue ?? this.#keyScrub?.fromValue;\n\t}\n\n\tisEditing(): boolean {\n\t\treturn this.#entry?.isOpen === true;\n\t}\n\n\tisHeld(): boolean {\n\t\treturn this.#dragging() !== undefined || this.#keyScrub !== undefined;\n\t}\n\n\tisRevealed(): boolean {\n\t\treturn this.#claim.isRevealed;\n\t}\n\n\tpointerType(): string | undefined {\n\t\treturn this.#dragging()?.pointerType;\n\t}\n\n\t#bindEntry(signal: AbortSignal): ValueEntry {\n\t\tconst control = this.#control;\n\t\tconst host = this.#host;\n\t\tconst input = requireChild(control, 'input', HTMLInputElement);\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 = host.entryId;\n\n\t\tconst entry = new ValueEntry(input, {\n\t\t\tcommit: (text) => {\n\t\t\t\tthis.#commit(host.parse(text));\n\t\t\t},\n\t\t\ttext: host.entryText,\n\t\t\ttoggle: (isOpen) => {\n\t\t\t\tif (isOpen) host.forwardNaming(input, true);\n\t\t\t\thost.toggleState('editing', isOpen);\n\t\t\t\thost.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(entry: ValueEntry, signal: AbortSignal): void {\n\t\tconst control = this.#control;\n\t\tconst host = this.#host;\n\n\t\tcontrol.addEventListener(\n\t\t\t'keydown',\n\t\t\t(event) => {\n\t\t\t\tif (host.isDisabled() || event.defaultPrevented || event.target !== control) return;\n\n\t\t\t\thost.toggleState('springing', false);\n\t\t\t\tif (event.key === 'Enter') {\n\t\t\t\t\tif (host.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.#claim.reveal('keys');\n\t\t\t\tthis.#showRevealed();\n\t\t\t\tthis.#keyTo(event, next);\n\t\t\t\thost.holdChanged();\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\t// macOS sends no `keyup` for a key let go while Cmd is down\n\t\t\t\tconst isMeta = event.key === 'Meta';\n\n\t\t\t\tif (isMeta || event.key === this.#keyScrub?.key) this.#endKeyScrub();\n\t\t\t\tif (isMeta && this.#dragging()) return;\n\t\t\t\tif (isMeta || host.mapping().keyTarget(event.key, host.value()) !== undefined) {\n\t\t\t\t\tthis.#springBack();\n\t\t\t\t}\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.#endKeyScrub();\n\t\t\t\tthis.#concealKeyReveal();\n\t\t\t\tthis.#springBack();\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t}\n\n\t#commit(next: number, direction: -1 | 0 | 1 = 0): void {\n\t\tconst host = this.#host;\n\t\tconst landed = Number.isFinite(next) ? host.land(next, direction) : next;\n\n\t\tif (host.input(landed)) host.dispatch('change');\n\t}\n\n\t#concealKeyReveal(): void {\n\t\tif (!this.#claim.conceal('keys')) return;\n\n\t\tthis.#host.holdChanged();\n\t\tthis.#showRevealed();\n\t}\n\n\t#crossCancelZone(drag: ValueDrag, value: number): void {\n\t\tconst host = this.#host;\n\t\tconst isOutside = !drag.isOutside;\n\n\t\tdrag.isOutside = isOutside;\n\t\thost.toggleState('cancelling', isOutside);\n\t\tif (isOutside) this.#renderDragReveal(drag);\n\t\thost.input(isOutside ? drag.fromValue : value);\n\t\tif (!isOutside) this.#renderDragReveal(drag);\n\t}\n\n\t#dragging(): undefined | ValueDrag {\n\t\treturn this.#pointerDrag?.current();\n\t}\n\n\t#dragTo(drag: ValueDrag, event: PointerEvent): void {\n\t\tconst host = this.#host;\n\t\tconst isOutside = drag.outside?.(event) === true;\n\t\tconst wasEngaged = drag.state.isEngaged;\n\t\tconst mapping = host.mapping();\n\t\tconst { state, value } = stepDrag(mapping, drag.state, {\n\t\t\tisFine: event.shiftKey,\n\t\t\tisOutside,\n\t\t\tposition: drag.position(event),\n\t\t});\n\n\t\tdrag.state = this.#withinLimit(mapping, state);\n\t\tif (value === undefined) return;\n\n\t\tif (!wasEngaged) this.#reveal(drag);\n\n\t\tif (isOutside === drag.isOutside) host.input(value);\n\t\telse this.#crossCancelZone(drag, value);\n\t}\n\n\t#endKeyScrub(): void {\n\t\tconst host = this.#host;\n\t\tconst scrub = this.#keyScrub;\n\t\tif (!scrub) return;\n\n\t\tthis.#keyScrub = undefined;\n\t\thost.holdChanged();\n\t\tif (host.value() !== scrub.fromValue) host.dispatch('change');\n\t}\n\n\t#keyTarget(key: string): number | undefined {\n\t\tconst host = this.#host;\n\n\t\tif (resetKeys.has(key)) return host.default();\n\n\t\treturn host.mapping().keyTarget(key, host.value(), host.keyStep());\n\t}\n\n\t#keyTo(event: KeyboardEvent, next: number): void {\n\t\tconst host = this.#host;\n\n\t\tif (event.repeat && host.scrubsKeyRepeat()) {\n\t\t\tthis.#scrubKey(event.key, next);\n\t\t\treturn;\n\t\t}\n\n\t\tthis.#endKeyScrub();\n\t\tthis.#commit(next, next > host.value() ? 1 : -1);\n\t}\n\n\t#releaseDrag(drag: ValueDrag): void {\n\t\tconst host = this.#host;\n\n\t\tclearTimeout(drag.revealTimer);\n\t\tthis.#claim.conceal('drag');\n\t\thost.toggleState('cancelling', false);\n\t\thost.holdChanged();\n\t\tthis.#showRevealed();\n\t\tif (host.value() !== drag.fromValue) host.dispatch('change');\n\t\tthis.#springBack();\n\t}\n\n\t#renderDragReveal(drag: ValueDrag): void {\n\t\tconst isRevealed = drag.isRevealed && !drag.isOutside;\n\n\t\tif (isRevealed) this.#claim.reveal('drag');\n\t\telse this.#claim.conceal('drag');\n\t\tthis.#host.holdChanged();\n\t\tthis.#showRevealed();\n\t}\n\n\t#reset(): void {\n\t\tthis.#commit(this.#host.default() ?? NaN);\n\t}\n\n\t#reveal(drag: ValueDrag): void {\n\t\tif (drag.isRevealed || this.#dragging() !== drag) return;\n\n\t\tdrag.isRevealed = true;\n\t\tthis.#renderDragReveal(drag);\n\t}\n\n\t#scrubKey(key: string, next: number): void {\n\t\tconst host = this.#host;\n\n\t\tif (this.#keyScrub?.key !== key) {\n\t\t\tthis.#endKeyScrub();\n\t\t\tthis.#keyScrub = { fromValue: host.value(), key };\n\t\t}\n\t\thost.input(next);\n\t}\n\n\t#showRevealed(): void {\n\t\tconst { isRevealed } = this.#claim;\n\t\tif (isRevealed === this.#isRevealed) return;\n\n\t\tthis.#isRevealed = isRevealed;\n\t\tthis.#host.toggleState('revealed', isRevealed);\n\t\tthis.#host.dispatch('sonic-reveal');\n\t}\n\n\t#springBack(): void {\n\t\tconst host = this.#host;\n\t\tconst target = host.springTarget();\n\t\tif (target === undefined) return;\n\n\t\t// Set before the commit so a `change` listener reading styles sees the glide\n\t\tif (host.mapping().snap(target) !== host.value()) host.toggleState('springing', true);\n\t\tthis.#commit(target);\n\t}\n\n\t#startDrag(axis: ValueAxis, event: PointerEvent): DragState {\n\t\tconst host = this.#host;\n\t\tconst mapping = host.mapping();\n\t\tconst detent = host.detent();\n\t\tconst value = host.value();\n\t\tconst start = {\n\t\t\tfrom: value,\n\t\t\tposition: axis.position(event),\n\t\t\tproportion: axis.fromProportion ?? mapping.proportionOf(value),\n\t\t\tthresholdPx: dragThresholdPx(event.pointerType),\n\t\t\ttravelPx: axis.travelPx,\n\t\t};\n\t\tif (detent === undefined) return startDrag(mapping, start);\n\n\t\tconst zonePx = readPxProperty(\n\t\t\tgetComputedStyle(this.#control),\n\t\t\t'--_sonic-detent-zone',\n\t\t\tdetentZonePx,\n\t\t);\n\n\t\treturn startDrag(mapping, {\n\t\t\t...start,\n\t\t\tdetent: { value: detent, zone: zonePx / axis.travelPx },\n\t\t});\n\t}\n\n\t#withinLimit(mapping: ValueMapping, state: DragState): DragState {\n\t\tconst limit = this.#host.limit();\n\t\tif (!limit || mapping.isWrapping) return state;\n\n\t\tconst [low, high] = limit;\n\n\t\treturn {\n\t\t\t...state,\n\t\t\trawProportion: clamp(\n\t\t\t\tstate.rawProportion,\n\t\t\t\tmapping.proportionOf(low),\n\t\t\t\tmapping.proportionOf(high),\n\t\t\t),\n\t\t};\n\t}\n}\n"],"mappings":"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"}
|
|
@@ -29,10 +29,11 @@ function bindHoverPreview(target, options, signal) {
|
|
|
29
29
|
options.show();
|
|
30
30
|
watchEscape(value !== void 0);
|
|
31
31
|
};
|
|
32
|
-
|
|
32
|
+
const at = (event) => {
|
|
33
33
|
if (event.pointerType === "touch" || event.buttons !== 0 || !options.canShow()) return;
|
|
34
34
|
hoverAt(options.valueAt(event));
|
|
35
|
-
}
|
|
35
|
+
};
|
|
36
|
+
target.addEventListener("pointermove", at, { signal });
|
|
36
37
|
for (const type of ["pointerdown", "pointerleave"]) target.addEventListener(type, () => {
|
|
37
38
|
hoverAt(void 0);
|
|
38
39
|
}, { signal });
|
|
@@ -40,6 +41,7 @@ function bindHoverPreview(target, options, signal) {
|
|
|
40
41
|
watchEscape(false);
|
|
41
42
|
}, { once: true });
|
|
42
43
|
return {
|
|
44
|
+
at,
|
|
43
45
|
clear: () => {
|
|
44
46
|
hold(void 0);
|
|
45
47
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hover-preview.js","names":[],"sources":["../../../src/lib/hover-preview.ts"],"sourcesContent":["interface HoverOptions {\n\tcanShow: () => boolean;\n\tchanged: () => void;\n\tdismiss: () => void;\n\tisTaken: () => boolean;\n\tshow: () => void;\n\tvalueAt: (event: PointerEvent) => number | undefined;\n}\n\nexport interface HoverPreview {\n\tclear: () => void;\n\tvalue: () => number | undefined;\n}\n\nexport function bindHoverPreview(\n\ttarget: HTMLElement,\n\toptions: HoverOptions,\n\tsignal: AbortSignal,\n): HoverPreview {\n\tlet hover: number | undefined;\n\tlet isDismissed = false;\n\tlet escapeWatch: AbortController | undefined;\n\n\tconst hold = (value: number | undefined): void => {\n\t\tif (value === hover) return;\n\n\t\thover = value;\n\t\toptions.changed();\n\t};\n\n\t// A hover holds no focus, so the key is heard on the document (WCAG 1.4.13)\n\tconst watchEscape = (isHovered: boolean): void => {\n\t\tif (!isHovered) {\n\t\t\tescapeWatch?.abort();\n\t\t\tescapeWatch = undefined;\n\t\t\treturn;\n\t\t}\n\t\tif (escapeWatch) return;\n\n\t\tescapeWatch = new AbortController();\n\t\ttarget.ownerDocument.addEventListener(\n\t\t\t'keydown',\n\t\t\t(event) => {\n\t\t\t\tif (hover === undefined || event.key !== 'Escape') return;\n\n\t\t\t\t// Stays dismissed until the hover ends, or the next pointer move would bring it straight back\n\t\t\t\tisDismissed = true;\n\t\t\t\thold(undefined);\n\t\t\t\toptions.dismiss();\n\t\t\t},\n\t\t\t{ signal: escapeWatch.signal },\n\t\t);\n\t};\n\tconst hoverAt = (value: number | undefined): void => {\n\t\tif (value === undefined) isDismissed = false;\n\t\tif (!isDismissed && !options.isTaken()) hold(value);\n\t\toptions.show();\n\t\twatchEscape(value !== undefined);\n\t};\n\n\
|
|
1
|
+
{"version":3,"file":"hover-preview.js","names":[],"sources":["../../../src/lib/hover-preview.ts"],"sourcesContent":["interface HoverOptions {\n\tcanShow: () => boolean;\n\tchanged: () => void;\n\tdismiss: () => void;\n\tisTaken: () => boolean;\n\tshow: () => void;\n\tvalueAt: (event: PointerEvent) => number | undefined;\n}\n\nexport interface HoverPreview {\n\tat: (event: PointerEvent) => void;\n\tclear: () => void;\n\tvalue: () => number | undefined;\n}\n\nexport function bindHoverPreview(\n\ttarget: HTMLElement,\n\toptions: HoverOptions,\n\tsignal: AbortSignal,\n): HoverPreview {\n\tlet hover: number | undefined;\n\tlet isDismissed = false;\n\tlet escapeWatch: AbortController | undefined;\n\n\tconst hold = (value: number | undefined): void => {\n\t\tif (value === hover) return;\n\n\t\thover = value;\n\t\toptions.changed();\n\t};\n\n\t// A hover holds no focus, so the key is heard on the document (WCAG 1.4.13)\n\tconst watchEscape = (isHovered: boolean): void => {\n\t\tif (!isHovered) {\n\t\t\tescapeWatch?.abort();\n\t\t\tescapeWatch = undefined;\n\t\t\treturn;\n\t\t}\n\t\tif (escapeWatch) return;\n\n\t\tescapeWatch = new AbortController();\n\t\ttarget.ownerDocument.addEventListener(\n\t\t\t'keydown',\n\t\t\t(event) => {\n\t\t\t\tif (hover === undefined || event.key !== 'Escape') return;\n\n\t\t\t\t// Stays dismissed until the hover ends, or the next pointer move would bring it straight back\n\t\t\t\tisDismissed = true;\n\t\t\t\thold(undefined);\n\t\t\t\toptions.dismiss();\n\t\t\t},\n\t\t\t{ signal: escapeWatch.signal },\n\t\t);\n\t};\n\tconst hoverAt = (value: number | undefined): void => {\n\t\tif (value === undefined) isDismissed = false;\n\t\tif (!isDismissed && !options.isTaken()) hold(value);\n\t\toptions.show();\n\t\twatchEscape(value !== undefined);\n\t};\n\n\tconst at = (event: PointerEvent): void => {\n\t\tif (event.pointerType === 'touch' || event.buttons !== 0 || !options.canShow()) return;\n\n\t\thoverAt(options.valueAt(event));\n\t};\n\n\ttarget.addEventListener('pointermove', at, { signal });\n\tfor (const type of ['pointerdown', 'pointerleave']) {\n\t\ttarget.addEventListener(\n\t\t\ttype,\n\t\t\t() => {\n\t\t\t\thoverAt(undefined);\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t}\n\tsignal.addEventListener(\n\t\t'abort',\n\t\t() => {\n\t\t\twatchEscape(false);\n\t\t},\n\t\t{ once: true },\n\t);\n\n\treturn {\n\t\tat,\n\t\tclear: () => {\n\t\t\thold(undefined);\n\t\t},\n\t\tvalue: () => hover,\n\t};\n}\n"],"mappings":";AAeA,SAAgB,iBACf,QACA,SACA,QACe;CACf,IAAI;CACJ,IAAI,cAAc;CAClB,IAAI;CAEJ,MAAM,QAAQ,UAAoC;EACjD,IAAI,UAAU,OAAO;EAErB,QAAQ;EACR,QAAQ,QAAQ;CACjB;CAGA,MAAM,eAAe,cAA6B;EACjD,IAAI,CAAC,WAAW;GACf,aAAa,MAAM;GACnB,cAAc;GACd;EACD;EACA,IAAI,aAAa;EAEjB,cAAc,IAAI,gBAAgB;EAClC,OAAO,cAAc,iBACpB,YACC,UAAU;GACV,IAAI,UAAU,UAAa,MAAM,QAAQ,UAAU;GAGnD,cAAc;GACd,KAAK,MAAS;GACd,QAAQ,QAAQ;EACjB,GACA,EAAE,QAAQ,YAAY,OAAO,CAC9B;CACD;CACA,MAAM,WAAW,UAAoC;EACpD,IAAI,UAAU,QAAW,cAAc;EACvC,IAAI,CAAC,eAAe,CAAC,QAAQ,QAAQ,GAAG,KAAK,KAAK;EAClD,QAAQ,KAAK;EACb,YAAY,UAAU,MAAS;CAChC;CAEA,MAAM,MAAM,UAA8B;EACzC,IAAI,MAAM,gBAAgB,WAAW,MAAM,YAAY,KAAK,CAAC,QAAQ,QAAQ,GAAG;EAEhF,QAAQ,QAAQ,QAAQ,KAAK,CAAC;CAC/B;CAEA,OAAO,iBAAiB,eAAe,IAAI,EAAE,OAAO,CAAC;CACrD,KAAK,MAAM,QAAQ,CAAC,eAAe,cAAc,GAChD,OAAO,iBACN,YACM;EACL,QAAQ,MAAS;CAClB,GACA,EAAE,OAAO,CACV;CAED,OAAO,iBACN,eACM;EACL,YAAY,KAAK;CAClB,GACA,EAAE,MAAM,KAAK,CACd;CAEA,OAAO;EACN;EACA,aAAa;GACZ,KAAK,MAAS;EACf;EACA,aAAa;CACd;AACD"}
|