@xsynaptic/sonic-ui 0.1.0 → 0.2.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 +8 -1
- package/dist/default/define/dial.js +7 -0
- package/dist/default/define/key.js +7 -0
- package/dist/default/define/meter.js +7 -0
- package/dist/default/define/segmented.js +7 -0
- package/dist/default/define/slider.js +7 -0
- package/dist/default/elements/define-once.js +7 -0
- package/dist/{elements → default/elements}/dial.js +15 -3
- package/dist/default/elements/form-element.js +56 -0
- package/dist/default/elements/key.js +144 -0
- package/dist/{elements → default/elements}/meter.js +27 -16
- package/dist/default/elements/range-element.js +410 -0
- package/dist/{elements → default/elements}/range-entry.js +5 -4
- package/dist/default/elements/segmented.js +173 -0
- package/dist/{elements → default/elements}/slider.js +23 -8
- package/dist/default/elements/sonic-element.js +153 -0
- package/dist/{index.js → default/index.js} +2 -1
- package/dist/default/lib/copy-node.js +30 -0
- package/dist/default/lib/percent.js +12 -0
- package/dist/default/lib/read-px-property.js +8 -0
- package/dist/{lib → default/lib}/render.js +9 -2
- package/dist/default/lib/taper.js +32 -0
- package/dist/default/lib/write-attribute.js +11 -0
- package/dist/dev/define/dial.js.map +1 -0
- package/dist/dev/define/key.js.map +1 -0
- package/dist/dev/define/meter.js.map +1 -0
- package/dist/dev/define/segmented.js.map +1 -0
- package/dist/dev/define/slider.js.map +1 -0
- package/dist/dev/define.js +5 -0
- package/dist/dev/elements/define-once.js.map +1 -0
- package/dist/{elements → dev/elements}/dial.d.ts +4 -0
- package/dist/dev/elements/dial.js +70 -0
- package/dist/dev/elements/dial.js.map +1 -0
- package/dist/dev/elements/form-element.d.ts +22 -0
- package/dist/dev/elements/form-element.js +57 -0
- package/dist/dev/elements/form-element.js.map +1 -0
- package/dist/dev/elements/key.d.ts +30 -0
- package/dist/{elements → dev/elements}/key.js +71 -25
- package/dist/dev/elements/key.js.map +1 -0
- package/dist/{elements → dev/elements}/meter.d.ts +6 -0
- package/dist/dev/elements/meter.js +131 -0
- package/dist/dev/elements/meter.js.map +1 -0
- package/dist/{elements → dev/elements}/range-element.d.ts +25 -5
- package/dist/{elements → dev/elements}/range-element.js +116 -53
- package/dist/dev/elements/range-element.js.map +1 -0
- package/dist/dev/elements/range-entry.js +75 -0
- package/dist/dev/elements/range-entry.js.map +1 -0
- package/dist/{elements → dev/elements}/segmented.d.ts +5 -2
- package/dist/dev/elements/segmented.js +174 -0
- package/dist/dev/elements/segmented.js.map +1 -0
- package/dist/{elements → dev/elements}/slider.d.ts +6 -0
- package/dist/dev/elements/slider.js +86 -0
- package/dist/dev/elements/slider.js.map +1 -0
- package/dist/dev/elements/sonic-element.d.ts +33 -0
- package/dist/dev/elements/sonic-element.js +169 -0
- package/dist/dev/elements/sonic-element.js.map +1 -0
- package/dist/{index.d.ts → dev/index.d.ts} +2 -1
- package/dist/dev/index.js +8 -0
- package/dist/dev/lib/copy-node.js +31 -0
- package/dist/dev/lib/copy-node.js.map +1 -0
- package/dist/dev/lib/percent.d.ts +5 -0
- package/dist/dev/lib/percent.js +13 -0
- package/dist/dev/lib/percent.js.map +1 -0
- package/dist/dev/lib/read-px-property.js.map +1 -0
- package/dist/dev/lib/render.js +27 -0
- package/dist/dev/lib/render.js.map +1 -0
- package/dist/dev/lib/taper.js.map +1 -0
- package/dist/dev/lib/write-attribute.js.map +1 -0
- package/dist/skins/ivory.css +3 -3
- package/dist/skins/slate.css +4 -4
- package/dist/styles/controls.css +0 -1
- package/dist/styles/dial.css +33 -13
- package/dist/styles/key.css +30 -10
- package/dist/styles/material.css +58 -10
- package/dist/styles/meter.css +25 -5
- package/dist/styles/screen.css +7 -1
- package/dist/styles/segmented.css +24 -7
- package/dist/styles/slider.css +17 -4
- package/package.json +13 -12
- package/dist/define/dial.js.map +0 -1
- package/dist/define/key.js.map +0 -1
- package/dist/define/meter.js.map +0 -1
- package/dist/define/segmented.js.map +0 -1
- package/dist/define/slider.js.map +0 -1
- package/dist/elements/define-once.js.map +0 -1
- package/dist/elements/dial.js.map +0 -1
- package/dist/elements/key.d.ts +0 -17
- package/dist/elements/key.js.map +0 -1
- package/dist/elements/meter.js.map +0 -1
- package/dist/elements/range-element.js.map +0 -1
- package/dist/elements/range-entry.js.map +0 -1
- package/dist/elements/segmented.js +0 -104
- package/dist/elements/segmented.js.map +0 -1
- package/dist/elements/slider.js.map +0 -1
- package/dist/elements/sonic-element.d.ts +0 -20
- package/dist/elements/sonic-element.js +0 -94
- package/dist/elements/sonic-element.js.map +0 -1
- package/dist/lib/read-px-property.js.map +0 -1
- package/dist/lib/render.js.map +0 -1
- package/dist/lib/taper.js.map +0 -1
- package/dist/lib/write-attribute.js.map +0 -1
- /package/dist/{define.js → default/define.js} +0 -0
- /package/dist/{define → dev/define}/dial.d.ts +0 -0
- /package/dist/{define → dev/define}/dial.js +0 -0
- /package/dist/{define → dev/define}/key.d.ts +0 -0
- /package/dist/{define → dev/define}/key.js +0 -0
- /package/dist/{define → dev/define}/meter.d.ts +0 -0
- /package/dist/{define → dev/define}/meter.js +0 -0
- /package/dist/{define → dev/define}/segmented.d.ts +0 -0
- /package/dist/{define → dev/define}/segmented.js +0 -0
- /package/dist/{define → dev/define}/slider.d.ts +0 -0
- /package/dist/{define → dev/define}/slider.js +0 -0
- /package/dist/{define.d.ts → dev/define.d.ts} +0 -0
- /package/dist/{elements → dev/elements}/define-once.js +0 -0
- /package/dist/{lib → dev/lib}/read-px-property.js +0 -0
- /package/dist/{lib → dev/lib}/taper.js +0 -0
- /package/dist/{lib → dev/lib}/write-attribute.js +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { RangeEntry } from "./range-entry.js";
|
|
2
|
-
import { writeAttribute } from "../lib/write-attribute.js";
|
|
3
|
-
import { SonicElement } from "./sonic-element.js";
|
|
4
1
|
import { requireChild } from "../lib/render.js";
|
|
2
|
+
import { writeAttribute } from "../lib/write-attribute.js";
|
|
3
|
+
import { SonicFormElement } from "./form-element.js";
|
|
4
|
+
import { RangeEntry } from "./range-entry.js";
|
|
5
5
|
import { clampUnit, linearTaper, logTaper, skewTaper } from "../lib/taper.js";
|
|
6
6
|
|
|
7
7
|
//#region src/elements/range-element.ts
|
|
@@ -11,7 +11,7 @@ const pageSteps = 10;
|
|
|
11
11
|
const revealMs = 250;
|
|
12
12
|
const revealPx = 4;
|
|
13
13
|
const canPopover = "togglePopover" in HTMLElement.prototype;
|
|
14
|
-
let
|
|
14
|
+
let instanceCount = 0;
|
|
15
15
|
const resetKeys = /* @__PURE__ */ new Set(["Backspace", "Delete"]);
|
|
16
16
|
const keySteps = /* @__PURE__ */ new Map([
|
|
17
17
|
["ArrowDown", -1],
|
|
@@ -31,9 +31,9 @@ const sliderAttributes = [
|
|
|
31
31
|
"role",
|
|
32
32
|
"tabindex"
|
|
33
33
|
];
|
|
34
|
-
var SonicRangeElement = class extends
|
|
34
|
+
var SonicRangeElement = class extends SonicFormElement {
|
|
35
35
|
static observedAttributes = [
|
|
36
|
-
...
|
|
36
|
+
...SonicFormElement.observedAttributes,
|
|
37
37
|
"max",
|
|
38
38
|
"midpoint",
|
|
39
39
|
"min",
|
|
@@ -44,6 +44,24 @@ var SonicRangeElement = class extends SonicElement {
|
|
|
44
44
|
"taper",
|
|
45
45
|
"value"
|
|
46
46
|
];
|
|
47
|
+
get default() {
|
|
48
|
+
return this.optionalNumberAttribute("default");
|
|
49
|
+
}
|
|
50
|
+
set default(value) {
|
|
51
|
+
this.reflect("default", value);
|
|
52
|
+
}
|
|
53
|
+
get dimmed() {
|
|
54
|
+
return this.hasAttribute("dimmed");
|
|
55
|
+
}
|
|
56
|
+
set dimmed(isDimmed) {
|
|
57
|
+
this.reflect("dimmed", isDimmed);
|
|
58
|
+
}
|
|
59
|
+
get doublePress() {
|
|
60
|
+
return this.getAttribute("double-press") === "reset" ? "reset" : "type";
|
|
61
|
+
}
|
|
62
|
+
set doublePress(gesture) {
|
|
63
|
+
this.reflect("double-press", gesture);
|
|
64
|
+
}
|
|
47
65
|
get formatValue() {
|
|
48
66
|
return this.#formatValue;
|
|
49
67
|
}
|
|
@@ -51,12 +69,60 @@ var SonicRangeElement = class extends SonicElement {
|
|
|
51
69
|
this.#formatValue = format;
|
|
52
70
|
this.render();
|
|
53
71
|
}
|
|
72
|
+
get max() {
|
|
73
|
+
return this.numberAttribute("max", 100);
|
|
74
|
+
}
|
|
75
|
+
set max(value) {
|
|
76
|
+
this.reflect("max", value);
|
|
77
|
+
}
|
|
78
|
+
get midpoint() {
|
|
79
|
+
return this.optionalNumberAttribute("midpoint");
|
|
80
|
+
}
|
|
81
|
+
set midpoint(value) {
|
|
82
|
+
this.reflect("midpoint", value);
|
|
83
|
+
}
|
|
84
|
+
get min() {
|
|
85
|
+
return this.numberAttribute("min", 0);
|
|
86
|
+
}
|
|
87
|
+
set min(value) {
|
|
88
|
+
this.reflect("min", value);
|
|
89
|
+
}
|
|
90
|
+
get notched() {
|
|
91
|
+
return this.hasAttribute("notched");
|
|
92
|
+
}
|
|
93
|
+
set notched(isNotched) {
|
|
94
|
+
this.reflect("notched", isNotched);
|
|
95
|
+
}
|
|
96
|
+
get origin() {
|
|
97
|
+
return this.optionalNumberAttribute("origin");
|
|
98
|
+
}
|
|
99
|
+
set origin(value) {
|
|
100
|
+
this.reflect("origin", value);
|
|
101
|
+
}
|
|
54
102
|
get parseValue() {
|
|
55
103
|
return this.#parseValue;
|
|
56
104
|
}
|
|
57
105
|
set parseValue(parse) {
|
|
58
106
|
this.#parseValue = parse;
|
|
59
107
|
}
|
|
108
|
+
get readout() {
|
|
109
|
+
return this.hasAttribute("readout");
|
|
110
|
+
}
|
|
111
|
+
set readout(isShown) {
|
|
112
|
+
this.reflect("readout", isShown);
|
|
113
|
+
}
|
|
114
|
+
get step() {
|
|
115
|
+
return this.numberAttribute("step", 1);
|
|
116
|
+
}
|
|
117
|
+
set step(value) {
|
|
118
|
+
this.reflect("step", value);
|
|
119
|
+
}
|
|
120
|
+
get taper() {
|
|
121
|
+
return this.getAttribute("taper") === "log" ? "log" : "linear";
|
|
122
|
+
}
|
|
123
|
+
set taper(curve) {
|
|
124
|
+
this.reflect("taper", curve);
|
|
125
|
+
}
|
|
60
126
|
get value() {
|
|
61
127
|
return this.#value;
|
|
62
128
|
}
|
|
@@ -64,10 +130,10 @@ var SonicRangeElement = class extends SonicElement {
|
|
|
64
130
|
if (this.#drag) return;
|
|
65
131
|
this.#write(next);
|
|
66
132
|
}
|
|
67
|
-
#anchor = `--sonic-readout-${String(anchorCount += 1)}`;
|
|
68
133
|
#drag;
|
|
69
134
|
#entry;
|
|
70
135
|
#formatValue;
|
|
136
|
+
#instance = String(instanceCount += 1);
|
|
71
137
|
#isDirty = false;
|
|
72
138
|
#keyRevealTimer;
|
|
73
139
|
#parseValue;
|
|
@@ -78,23 +144,22 @@ var SonicRangeElement = class extends SonicElement {
|
|
|
78
144
|
if (this.#drag) return;
|
|
79
145
|
this.#isDirty = false;
|
|
80
146
|
}
|
|
81
|
-
if (name === "disabled" && this.
|
|
147
|
+
if (name === "disabled" && this.isDisabled()) {
|
|
82
148
|
this.#entry?.close(false);
|
|
83
149
|
this.#endDrag();
|
|
84
150
|
}
|
|
85
|
-
this.#value = this.#clamp(this.#isDirty ? this.#value : this.numberAttribute("value", this.min
|
|
151
|
+
this.#value = this.#clamp(this.#isDirty ? this.#value : this.numberAttribute("value", this.min));
|
|
86
152
|
this.render();
|
|
87
153
|
}
|
|
88
154
|
connectedCallback() {
|
|
89
|
-
this.
|
|
90
|
-
this.upgradeProperty("parseValue");
|
|
91
|
-
this.upgradeProperty("value");
|
|
155
|
+
this.upgradeProperties("default", "dimmed", "doublePress", "max", "midpoint", "min", "notched", "origin", "readout", "step", "taper", "formatValue", "parseValue", "value");
|
|
92
156
|
super.connectedCallback();
|
|
93
157
|
}
|
|
94
158
|
bindGestures(control, signal, grab) {
|
|
95
159
|
const entry = this.#bindEntry(control, signal);
|
|
96
160
|
control.addEventListener("pointerdown", (event) => {
|
|
97
|
-
if (event.button !== 0 || this.
|
|
161
|
+
if (event.button !== 0 || this.isDisabled() || entry.isOpen) return;
|
|
162
|
+
if (event.pointerType === "touch") event.preventDefault();
|
|
98
163
|
control.focus();
|
|
99
164
|
if (event.metaKey || event.ctrlKey) {
|
|
100
165
|
this.#reset();
|
|
@@ -144,7 +209,7 @@ var SonicRangeElement = class extends SonicElement {
|
|
|
144
209
|
if (event.pointerId !== this.#drag?.pointerId) return;
|
|
145
210
|
this.#endDrag();
|
|
146
211
|
if (!entry.press(event)) return;
|
|
147
|
-
if (this.
|
|
212
|
+
if (this.doublePress === "reset") this.#reset();
|
|
148
213
|
else entry.open();
|
|
149
214
|
}, { signal });
|
|
150
215
|
this.#bindKeys(control, entry, signal);
|
|
@@ -154,10 +219,10 @@ var SonicRangeElement = class extends SonicElement {
|
|
|
154
219
|
}, { once: true });
|
|
155
220
|
}
|
|
156
221
|
clampRange(next) {
|
|
157
|
-
return Math.min(this
|
|
222
|
+
return Math.min(this.#max(), Math.max(this.min, next));
|
|
158
223
|
}
|
|
159
224
|
fraction(value) {
|
|
160
|
-
return (this.#taper() ?? linearTaper(this.min
|
|
225
|
+
return (this.#taper() ?? linearTaper(this.min, this.#max())).position(value);
|
|
161
226
|
}
|
|
162
227
|
input(next) {
|
|
163
228
|
const previous = this.#value;
|
|
@@ -169,24 +234,19 @@ var SonicRangeElement = class extends SonicElement {
|
|
|
169
234
|
}));
|
|
170
235
|
return true;
|
|
171
236
|
}
|
|
172
|
-
max() {
|
|
173
|
-
return Math.max(this.min(), this.numberAttribute("max", 100));
|
|
174
|
-
}
|
|
175
|
-
min() {
|
|
176
|
-
return this.numberAttribute("min", 0);
|
|
177
|
-
}
|
|
178
237
|
originFraction() {
|
|
179
|
-
return this.fraction(this.
|
|
238
|
+
return this.fraction(this.origin ?? this.min);
|
|
180
239
|
}
|
|
181
240
|
positions() {
|
|
182
|
-
const positions = Math.round(this.range() / this
|
|
183
|
-
if (!this.
|
|
241
|
+
const positions = Math.round(this.range() / this.step) + 1;
|
|
242
|
+
if (!this.notched || !Number.isFinite(positions) || positions < 2) return;
|
|
184
243
|
return positions;
|
|
185
244
|
}
|
|
186
245
|
range() {
|
|
187
|
-
return this
|
|
246
|
+
return this.#max() - this.min;
|
|
188
247
|
}
|
|
189
248
|
renderAria(control, orientation) {
|
|
249
|
+
this.writeFormValue(String(this.#value), String(this.#value));
|
|
190
250
|
if (this.#entry?.isOpen) {
|
|
191
251
|
for (const name of sliderAttributes) control.removeAttribute(name);
|
|
192
252
|
this.forwardNaming(control, false);
|
|
@@ -194,8 +254,8 @@ var SonicRangeElement = class extends SonicElement {
|
|
|
194
254
|
return;
|
|
195
255
|
}
|
|
196
256
|
control.setAttribute("role", "slider");
|
|
197
|
-
control.setAttribute("aria-valuemin", String(this.min
|
|
198
|
-
control.setAttribute("aria-valuemax", String(this
|
|
257
|
+
control.setAttribute("aria-valuemin", String(this.min));
|
|
258
|
+
control.setAttribute("aria-valuemax", String(this.#max()));
|
|
199
259
|
control.setAttribute("aria-valuenow", String(this.#value));
|
|
200
260
|
if (orientation) control.setAttribute("aria-orientation", orientation);
|
|
201
261
|
writeAttribute(control, "aria-valuetext", this.#formatValue?.(this.#value));
|
|
@@ -203,18 +263,23 @@ var SonicRangeElement = class extends SonicElement {
|
|
|
203
263
|
this.#renderDisabled(control);
|
|
204
264
|
this.#renderReadout();
|
|
205
265
|
}
|
|
266
|
+
restoreState(state) {
|
|
267
|
+
this.value = Number(state);
|
|
268
|
+
}
|
|
206
269
|
valueAt(fraction) {
|
|
207
|
-
return (this.#taper() ?? linearTaper(this.min
|
|
270
|
+
return (this.#taper() ?? linearTaper(this.min, this.#max())).value(fraction);
|
|
208
271
|
}
|
|
209
272
|
#bindEntry(control, signal) {
|
|
210
273
|
const input = requireChild(control, "input", HTMLInputElement);
|
|
211
274
|
const bubble = requireChild(control, "[popover]", HTMLElement);
|
|
275
|
+
const anchor = `--sonic-readout-${this.#instance}`;
|
|
276
|
+
input.id = `sonic-entry-${this.#instance}`;
|
|
212
277
|
this.#readout = {
|
|
213
278
|
bubble,
|
|
214
279
|
text: requireChild(bubble, "span", HTMLElement)
|
|
215
280
|
};
|
|
216
|
-
control.style.setProperty("anchor-name",
|
|
217
|
-
bubble.style.setProperty("position-anchor",
|
|
281
|
+
control.style.setProperty("anchor-name", anchor);
|
|
282
|
+
bubble.style.setProperty("position-anchor", anchor);
|
|
218
283
|
const entry = new RangeEntry(input, {
|
|
219
284
|
commit: (text) => {
|
|
220
285
|
this.#commit((this.#parseValue ?? Number.parseFloat)(text));
|
|
@@ -232,7 +297,7 @@ var SonicRangeElement = class extends SonicElement {
|
|
|
232
297
|
}
|
|
233
298
|
#bindKeys(control, entry, signal) {
|
|
234
299
|
control.addEventListener("keydown", (event) => {
|
|
235
|
-
if (this.
|
|
300
|
+
if (this.isDisabled() || event.defaultPrevented || event.target !== control) return;
|
|
236
301
|
if (event.key === "Enter") {
|
|
237
302
|
event.preventDefault();
|
|
238
303
|
entry.open();
|
|
@@ -249,8 +314,8 @@ var SonicRangeElement = class extends SonicElement {
|
|
|
249
314
|
}, { signal });
|
|
250
315
|
}
|
|
251
316
|
#clamp(next) {
|
|
252
|
-
const min = this.min
|
|
253
|
-
const step = this
|
|
317
|
+
const min = this.min;
|
|
318
|
+
const step = this.step;
|
|
254
319
|
const stepped = step > 0 ? min + Math.round((next - min) / step) * step : next;
|
|
255
320
|
return this.clampRange(Number(stepped.toPrecision(12)));
|
|
256
321
|
}
|
|
@@ -273,22 +338,25 @@ var SonicRangeElement = class extends SonicElement {
|
|
|
273
338
|
this.#renderReadout();
|
|
274
339
|
}
|
|
275
340
|
#isValueShown() {
|
|
276
|
-
return (this.#drag?.isRevealed === true || this.#keyRevealTimer !== void 0) && this.
|
|
341
|
+
return (this.#drag?.isRevealed === true || this.#keyRevealTimer !== void 0) && this.readout;
|
|
277
342
|
}
|
|
278
343
|
#keyTarget(key) {
|
|
279
|
-
if (key === "Home") return this.min
|
|
280
|
-
if (key === "End") return this
|
|
281
|
-
if (resetKeys.has(key)) return this
|
|
344
|
+
if (key === "Home") return this.min;
|
|
345
|
+
if (key === "End") return this.#max();
|
|
346
|
+
if (resetKeys.has(key)) return this.default;
|
|
282
347
|
const steps = keySteps.get(key);
|
|
283
348
|
if (steps === void 0) return void 0;
|
|
284
|
-
const step = this
|
|
349
|
+
const step = this.step > 0 ? this.step : this.range() / 100;
|
|
285
350
|
const taper = this.#taper();
|
|
286
351
|
if (!taper) return this.#value + steps * step;
|
|
287
352
|
const next = this.#clamp(taper.value(taper.position(this.#value) + steps / 100));
|
|
288
353
|
return next === this.#value ? this.#value + Math.sign(steps) * step : next;
|
|
289
354
|
}
|
|
355
|
+
#max() {
|
|
356
|
+
return Math.max(this.min, this.max);
|
|
357
|
+
}
|
|
290
358
|
#renderDisabled(control) {
|
|
291
|
-
if (this.
|
|
359
|
+
if (this.isDisabled()) {
|
|
292
360
|
control.setAttribute("aria-disabled", "true");
|
|
293
361
|
control.removeAttribute("tabindex");
|
|
294
362
|
return;
|
|
@@ -305,11 +373,7 @@ var SonicRangeElement = class extends SonicElement {
|
|
|
305
373
|
readout.bubble.togglePopover(isEditing || this.#isValueShown());
|
|
306
374
|
}
|
|
307
375
|
#reset() {
|
|
308
|
-
this.#commit(this.
|
|
309
|
-
}
|
|
310
|
-
#resetValue() {
|
|
311
|
-
const value = this.numberAttribute("default", NaN);
|
|
312
|
-
return Number.isFinite(value) ? value : void 0;
|
|
376
|
+
this.#commit(this.default ?? NaN);
|
|
313
377
|
}
|
|
314
378
|
#reveal(drag) {
|
|
315
379
|
if (drag.isRevealed || this.#drag !== drag) return;
|
|
@@ -323,14 +387,11 @@ var SonicRangeElement = class extends SonicElement {
|
|
|
323
387
|
}, keyRevealMs);
|
|
324
388
|
this.#renderReadout();
|
|
325
389
|
}
|
|
326
|
-
#step() {
|
|
327
|
-
return this.numberAttribute("step", 1);
|
|
328
|
-
}
|
|
329
390
|
#taper() {
|
|
330
|
-
if (this.
|
|
331
|
-
const min = this.min
|
|
332
|
-
const max = this
|
|
333
|
-
return (this.
|
|
391
|
+
if (this.notched) return void 0;
|
|
392
|
+
const min = this.min;
|
|
393
|
+
const max = this.#max();
|
|
394
|
+
return (this.taper === "log" ? logTaper(min, max) : void 0) ?? skewTaper(min, max, this.midpoint ?? NaN);
|
|
334
395
|
}
|
|
335
396
|
#valueText() {
|
|
336
397
|
return this.#formatValue?.(this.#value) ?? String(this.#value);
|
|
@@ -338,7 +399,9 @@ var SonicRangeElement = class extends SonicElement {
|
|
|
338
399
|
#write(next) {
|
|
339
400
|
if (!Number.isFinite(next)) return;
|
|
340
401
|
this.#isDirty = true;
|
|
341
|
-
|
|
402
|
+
const clamped = this.#clamp(next);
|
|
403
|
+
if (clamped === this.#value) return;
|
|
404
|
+
this.#value = clamped;
|
|
342
405
|
this.render();
|
|
343
406
|
}
|
|
344
407
|
};
|
|
@@ -0,0 +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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"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
//#region src/elements/range-entry.ts
|
|
2
|
+
const doublePressMs = 500;
|
|
3
|
+
const doublePressPx = 4;
|
|
4
|
+
function isDoublePress(previous, next) {
|
|
5
|
+
if (!previous) return false;
|
|
6
|
+
return next.time - previous.time < doublePressMs && Math.hypot(next.x - previous.x, next.y - previous.y) < doublePressPx;
|
|
7
|
+
}
|
|
8
|
+
var RangeEntry = class {
|
|
9
|
+
get isOpen() {
|
|
10
|
+
return this.#isOpen;
|
|
11
|
+
}
|
|
12
|
+
#input;
|
|
13
|
+
#isOpen = false;
|
|
14
|
+
#lastPress;
|
|
15
|
+
#openedText = "";
|
|
16
|
+
#options;
|
|
17
|
+
constructor(input, options) {
|
|
18
|
+
this.#input = input;
|
|
19
|
+
this.#options = options;
|
|
20
|
+
}
|
|
21
|
+
bind(control, signal) {
|
|
22
|
+
const input = this.#input;
|
|
23
|
+
input.addEventListener("keydown", (event) => {
|
|
24
|
+
if (event.isComposing || event.key !== "Enter" && event.key !== "Escape") return;
|
|
25
|
+
event.preventDefault();
|
|
26
|
+
this.close(event.key === "Enter");
|
|
27
|
+
control.focus();
|
|
28
|
+
}, { signal });
|
|
29
|
+
input.addEventListener("blur", () => {
|
|
30
|
+
this.close(true);
|
|
31
|
+
}, { signal });
|
|
32
|
+
for (const type of ["change", "input"]) input.addEventListener(type, (event) => {
|
|
33
|
+
event.stopPropagation();
|
|
34
|
+
}, { signal });
|
|
35
|
+
signal.addEventListener("abort", () => {
|
|
36
|
+
this.close(false);
|
|
37
|
+
}, { once: true });
|
|
38
|
+
}
|
|
39
|
+
close(isCommitting) {
|
|
40
|
+
if (!this.#isOpen) return;
|
|
41
|
+
this.#isOpen = false;
|
|
42
|
+
this.#input.hidden = true;
|
|
43
|
+
this.#options.toggle(false);
|
|
44
|
+
if (isCommitting && this.#input.value !== this.#openedText) this.#options.commit(this.#input.value);
|
|
45
|
+
}
|
|
46
|
+
forgetPress() {
|
|
47
|
+
this.#lastPress = void 0;
|
|
48
|
+
}
|
|
49
|
+
open() {
|
|
50
|
+
const input = this.#input;
|
|
51
|
+
this.#isOpen = true;
|
|
52
|
+
this.#openedText = this.#options.text();
|
|
53
|
+
input.value = this.#openedText;
|
|
54
|
+
input.hidden = false;
|
|
55
|
+
this.#options.toggle(true);
|
|
56
|
+
input.focus();
|
|
57
|
+
input.select();
|
|
58
|
+
}
|
|
59
|
+
press(event) {
|
|
60
|
+
const press = {
|
|
61
|
+
time: event.timeStamp,
|
|
62
|
+
x: event.clientX,
|
|
63
|
+
y: event.clientY
|
|
64
|
+
};
|
|
65
|
+
const previous = this.#lastPress;
|
|
66
|
+
this.#lastPress = press;
|
|
67
|
+
if (!isDoublePress(previous, press)) return false;
|
|
68
|
+
this.#lastPress = void 0;
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
//#endregion
|
|
74
|
+
export { RangeEntry };
|
|
75
|
+
//# sourceMappingURL=range-entry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"range-entry.js","names":[],"sources":["../../../src/elements/range-entry.ts"],"sourcesContent":["interface Press {\n\ttime: number;\n\tx: number;\n\ty: number;\n}\n\ninterface RangeEntryOptions {\n\tcommit: (text: string) => void;\n\ttext: () => string;\n\ttoggle: (isOpen: boolean) => void;\n}\n\n// Not `dblclick`, which iOS may not deliver under `touch-action: none`\nconst doublePressMs = 500;\nconst doublePressPx = 4;\n\nfunction isDoublePress(previous: Press | undefined, next: Press): boolean {\n\tif (!previous) return false;\n\n\treturn (\n\t\tnext.time - previous.time < doublePressMs &&\n\t\tMath.hypot(next.x - previous.x, next.y - previous.y) < doublePressPx\n\t);\n}\n\nexport class RangeEntry {\n\tget isOpen(): boolean {\n\t\treturn this.#isOpen;\n\t}\n\n\treadonly #input: HTMLInputElement;\n\n\t#isOpen = false;\n\n\t#lastPress: Press | undefined;\n\n\t#openedText = '';\n\n\treadonly #options: RangeEntryOptions;\n\n\tconstructor(input: HTMLInputElement, options: RangeEntryOptions) {\n\t\tthis.#input = input;\n\t\tthis.#options = options;\n\t}\n\n\tbind(control: HTMLElement, signal: AbortSignal): void {\n\t\tconst input = this.#input;\n\n\t\tinput.addEventListener(\n\t\t\t'keydown',\n\t\t\t(event) => {\n\t\t\t\tif (event.isComposing || (event.key !== 'Enter' && event.key !== 'Escape')) return;\n\n\t\t\t\tevent.preventDefault();\n\t\t\t\tthis.close(event.key === 'Enter');\n\t\t\t\tcontrol.focus();\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t\tinput.addEventListener(\n\t\t\t'blur',\n\t\t\t() => {\n\t\t\t\tthis.close(true);\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\n\t\t// The field's own events would reach the host's listeners as if the value had moved\n\t\tfor (const type of ['change', 'input']) {\n\t\t\tinput.addEventListener(\n\t\t\t\ttype,\n\t\t\t\t(event) => {\n\t\t\t\t\tevent.stopPropagation();\n\t\t\t\t},\n\t\t\t\t{ signal },\n\t\t\t);\n\t\t}\n\t\tsignal.addEventListener(\n\t\t\t'abort',\n\t\t\t() => {\n\t\t\t\tthis.close(false);\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\t}\n\n\t// Closes first, so the committed value renders with the control's role back in place\n\tclose(isCommitting: boolean): void {\n\t\tif (!this.#isOpen) return;\n\n\t\tthis.#isOpen = false;\n\t\tthis.#input.hidden = true;\n\t\tthis.#options.toggle(false);\n\t\t// A format that rounds would otherwise write its rounding back\n\t\tif (isCommitting && this.#input.value !== this.#openedText)\n\t\t\tthis.#options.commit(this.#input.value);\n\t}\n\n\tforgetPress(): void {\n\t\tthis.#lastPress = undefined;\n\t}\n\n\topen(): void {\n\t\tconst input = this.#input;\n\n\t\tthis.#isOpen = true;\n\t\tthis.#openedText = this.#options.text();\n\t\tinput.value = this.#openedText;\n\t\tinput.hidden = false;\n\t\tthis.#options.toggle(true);\n\t\tinput.focus();\n\t\tinput.select();\n\t}\n\n\tpress(event: PointerEvent): boolean {\n\t\tconst press = { time: event.timeStamp, x: event.clientX, y: event.clientY };\n\t\tconst previous = this.#lastPress;\n\n\t\tthis.#lastPress = press;\n\t\tif (!isDoublePress(previous, press)) return false;\n\n\t\tthis.#lastPress = undefined;\n\n\t\treturn true;\n\t}\n}\n"],"mappings":";AAaA,MAAM,gBAAgB;AACtB,MAAM,gBAAgB;AAEtB,SAAS,cAAc,UAA6B,MAAsB;CACzE,IAAI,CAAC,UAAU,OAAO;CAEtB,OACC,KAAK,OAAO,SAAS,OAAO,iBAC5B,KAAK,MAAM,KAAK,IAAI,SAAS,GAAG,KAAK,IAAI,SAAS,CAAC,IAAI;AAEzD;AAEA,IAAa,aAAb,MAAwB;CACvB,IAAI,SAAkB;EACrB,OAAO,KAAK;CACb;CAEA,AAAS;CAET,UAAU;CAEV;CAEA,cAAc;CAEd,AAAS;CAET,YAAY,OAAyB,SAA4B;EAChE,KAAK,SAAS;EACd,KAAK,WAAW;CACjB;CAEA,KAAK,SAAsB,QAA2B;EACrD,MAAM,QAAQ,KAAK;EAEnB,MAAM,iBACL,YACC,UAAU;GACV,IAAI,MAAM,eAAgB,MAAM,QAAQ,WAAW,MAAM,QAAQ,UAAW;GAE5E,MAAM,eAAe;GACrB,KAAK,MAAM,MAAM,QAAQ,OAAO;GAChC,QAAQ,MAAM;EACf,GACA,EAAE,OAAO,CACV;EACA,MAAM,iBACL,cACM;GACL,KAAK,MAAM,IAAI;EAChB,GACA,EAAE,OAAO,CACV;EAGA,KAAK,MAAM,QAAQ,CAAC,UAAU,OAAO,GACpC,MAAM,iBACL,OACC,UAAU;GACV,MAAM,gBAAgB;EACvB,GACA,EAAE,OAAO,CACV;EAED,OAAO,iBACN,eACM;GACL,KAAK,MAAM,KAAK;EACjB,GACA,EAAE,MAAM,KAAK,CACd;CACD;CAGA,MAAM,cAA6B;EAClC,IAAI,CAAC,KAAK,SAAS;EAEnB,KAAK,UAAU;EACf,KAAK,OAAO,SAAS;EACrB,KAAK,SAAS,OAAO,KAAK;EAE1B,IAAI,gBAAgB,KAAK,OAAO,UAAU,KAAK,aAC9C,KAAK,SAAS,OAAO,KAAK,OAAO,KAAK;CACxC;CAEA,cAAoB;EACnB,KAAK,aAAa;CACnB;CAEA,OAAa;EACZ,MAAM,QAAQ,KAAK;EAEnB,KAAK,UAAU;EACf,KAAK,cAAc,KAAK,SAAS,KAAK;EACtC,MAAM,QAAQ,KAAK;EACnB,MAAM,SAAS;EACf,KAAK,SAAS,OAAO,IAAI;EACzB,MAAM,MAAM;EACZ,MAAM,OAAO;CACd;CAEA,MAAM,OAA8B;EACnC,MAAM,QAAQ;GAAE,MAAM,MAAM;GAAW,GAAG,MAAM;GAAS,GAAG,MAAM;EAAQ;EAC1E,MAAM,WAAW,KAAK;EAEtB,KAAK,aAAa;EAClB,IAAI,CAAC,cAAc,UAAU,KAAK,GAAG,OAAO;EAE5C,KAAK,aAAa;EAElB,OAAO;CACR;AACD"}
|
|
@@ -1,17 +1,20 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { SonicFormElement } from "./form-element.js";
|
|
2
2
|
//#region src/elements/segmented.d.ts
|
|
3
3
|
declare global {
|
|
4
4
|
interface HTMLElementTagNameMap {
|
|
5
5
|
'sonic-segmented': SonicSegmented;
|
|
6
6
|
}
|
|
7
7
|
}
|
|
8
|
-
export declare class SonicSegmented extends
|
|
8
|
+
export declare class SonicSegmented extends SonicFormElement {
|
|
9
9
|
#private;
|
|
10
10
|
static readonly observedAttributes: string[];
|
|
11
11
|
get value(): string;
|
|
12
12
|
set value(next: string);
|
|
13
13
|
attributeChangedCallback(name: string): void;
|
|
14
14
|
protected connect(signal: AbortSignal): void;
|
|
15
|
+
protected focusTarget(): HTMLElement | undefined;
|
|
16
|
+
protected render(): void;
|
|
17
|
+
protected restoreState(state: string): void;
|
|
15
18
|
}
|
|
16
19
|
//#endregion
|
|
17
20
|
//# sourceMappingURL=segmented.d.ts.map
|