@antadesign/anta 0.3.31 → 0.3.33
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/avatar-core.d.ts +7 -2
- package/dist/box-types.d.ts +12 -1
- package/dist/bundle.css +1 -1
- package/dist/bundle.js +148 -73
- package/dist/components/Box.d.ts +18 -6
- package/dist/components/Box.js +4 -0
- package/dist/components/Expander.d.ts +17 -9
- package/dist/components/Expander.js +14 -1
- package/dist/components/Input.d.ts +9 -3
- package/dist/components/Input.js +2 -0
- package/dist/components/Slider.d.ts +22 -10
- package/dist/components/Slider.js +9 -5
- package/dist/elements/a-box.css +1 -1
- package/dist/elements/a-box.d.ts +2 -0
- package/dist/elements/a-box.js +74 -32
- package/dist/elements/a-button.js +2 -4
- package/dist/elements/a-dialog.js +2 -0
- package/dist/elements/a-expander.css +1 -1
- package/dist/elements/a-expander.js +118 -39
- package/dist/elements/a-input-time.js +1 -2
- package/dist/elements/a-input.css +1 -1
- package/dist/elements/a-input.js +11 -8
- package/dist/elements/a-menu.d.ts +1 -1
- package/dist/elements/a-menu.js +57 -7
- package/dist/elements/a-slider.d.ts +7 -5
- package/dist/elements/a-slider.js +256 -59
- package/dist/general_types.d.ts +27 -10
- package/dist/index.d.ts +2 -2
- package/dist/theme-antune.css +1 -1
- package/docs/changelog.md +59 -0
- package/docs/components/box.md +81 -24
- package/docs/components/button.md +9 -6
- package/docs/components/expander.md +88 -47
- package/docs/components/input.md +29 -9
- package/docs/components/panel.md +3 -2
- package/docs/components/select-faceted.md +40 -26
- package/docs/components/slider.md +139 -4
- package/docs/packages/plot.md +392 -38
- package/package.json +4 -2
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { HTMLElementBase } from '../anta_helpers';
|
|
2
2
|
import './a-slider.css';
|
|
3
|
+
type SliderValue = number | [number, number];
|
|
3
4
|
/**
|
|
4
|
-
* `<a-slider>` is a form-associated single-value slider. Its rail uses relative
|
|
5
|
+
* `<a-slider>` is a form-associated single-value or range slider. Its rail uses relative
|
|
5
6
|
* dragging by default: pressing any point captures the pointer, while only the
|
|
6
7
|
* subsequent horizontal movement changes the value. Set `track-click="jump"`
|
|
7
8
|
* for the conventional position-based track click.
|
|
@@ -11,10 +12,10 @@ export declare class ASliderElement extends HTMLElementBase {
|
|
|
11
12
|
static formAssociated: boolean;
|
|
12
13
|
static observedAttributes: string[];
|
|
13
14
|
private internals?;
|
|
14
|
-
get value():
|
|
15
|
-
set value(value:
|
|
16
|
-
get defaultValue():
|
|
17
|
-
set defaultValue(value:
|
|
15
|
+
get value(): SliderValue;
|
|
16
|
+
set value(value: SliderValue | string);
|
|
17
|
+
get defaultValue(): SliderValue;
|
|
18
|
+
set defaultValue(value: SliderValue | string);
|
|
18
19
|
constructor();
|
|
19
20
|
connectedCallback(): void;
|
|
20
21
|
attributeChangedCallback(name: string): void;
|
|
@@ -23,3 +24,4 @@ export declare class ASliderElement extends HTMLElementBase {
|
|
|
23
24
|
formDisabledCallback(): void;
|
|
24
25
|
}
|
|
25
26
|
export declare function register_a_slider(): void;
|
|
27
|
+
export {};
|
|
@@ -4,6 +4,16 @@ const precisionOf = (value) => {
|
|
|
4
4
|
const exponent = value.toString().match(/(?:\.(\d+))?(?:e([+-]?\d+))?$/i);
|
|
5
5
|
return Math.max(0, (exponent?.[1]?.length ?? 0) - Number(exponent?.[2] ?? 0));
|
|
6
6
|
};
|
|
7
|
+
const parseSliderValue = (source, fallback) => {
|
|
8
|
+
if (source === null || !source.trim()) return fallback;
|
|
9
|
+
const parts = source.trim().split(/\s+/);
|
|
10
|
+
if (parts.length !== 1 && parts.length !== 2) return fallback;
|
|
11
|
+
const numbers = parts.map(Number);
|
|
12
|
+
if (!numbers.every(Number.isFinite)) return fallback;
|
|
13
|
+
return numbers.length === 2 ? [numbers[0], numbers[1]] : numbers[0];
|
|
14
|
+
};
|
|
15
|
+
const serializeSliderValue = (value) => Array.isArray(value) ? value.join(" ") : String(value);
|
|
16
|
+
const sameSliderValue = (left, right) => Array.isArray(left) && Array.isArray(right) ? left[0] === right[0] && left[1] === right[1] : !Array.isArray(left) && !Array.isArray(right) && left === right;
|
|
7
17
|
class ASliderElement extends HTMLElementBase {
|
|
8
18
|
static formAssociated = true;
|
|
9
19
|
static observedAttributes = [
|
|
@@ -16,13 +26,19 @@ class ASliderElement extends HTMLElementBase {
|
|
|
16
26
|
"track-click",
|
|
17
27
|
"value-display",
|
|
18
28
|
"value-prefix",
|
|
19
|
-
"value-suffix"
|
|
29
|
+
"value-suffix",
|
|
30
|
+
"inverted",
|
|
31
|
+
"name",
|
|
32
|
+
"aria-label",
|
|
33
|
+
"aria-labelledby"
|
|
20
34
|
];
|
|
21
35
|
internals;
|
|
22
36
|
#control;
|
|
23
37
|
#railArea;
|
|
24
38
|
#fill;
|
|
39
|
+
#fillEnd;
|
|
25
40
|
#thumb;
|
|
41
|
+
#thumbEnd;
|
|
26
42
|
#header;
|
|
27
43
|
#label;
|
|
28
44
|
#inlineValue;
|
|
@@ -32,25 +48,31 @@ class ASliderElement extends HTMLElementBase {
|
|
|
32
48
|
#endValuePrefix;
|
|
33
49
|
#endValueSuffix;
|
|
34
50
|
#thumbValue;
|
|
51
|
+
#thumbValueEnd;
|
|
35
52
|
#thumbValuePrefix;
|
|
36
53
|
#thumbValueSuffix;
|
|
54
|
+
#thumbValueEndPrefix;
|
|
55
|
+
#thumbValueEndSuffix;
|
|
37
56
|
#markers;
|
|
38
57
|
#extras;
|
|
39
58
|
#value = 0;
|
|
59
|
+
#physicalValues = [0, 0];
|
|
60
|
+
#orderReversed = false;
|
|
61
|
+
#lastActiveThumb = 0;
|
|
40
62
|
#seeded = false;
|
|
41
63
|
#dirty = false;
|
|
42
64
|
#drag;
|
|
43
65
|
get value() {
|
|
44
|
-
return this.#value;
|
|
66
|
+
return Array.isArray(this.#value) ? [...this.#value] : this.#value;
|
|
45
67
|
}
|
|
46
68
|
set value(value) {
|
|
47
|
-
this.setAttribute("value", String(value));
|
|
69
|
+
this.setAttribute("value", Array.isArray(value) ? value.join(" ") : String(value));
|
|
48
70
|
}
|
|
49
71
|
get defaultValue() {
|
|
50
|
-
return
|
|
72
|
+
return parseSliderValue(this.getAttribute("defaultvalue"), this.#fallbackValue());
|
|
51
73
|
}
|
|
52
74
|
set defaultValue(value) {
|
|
53
|
-
this.setAttribute("defaultvalue", String(value));
|
|
75
|
+
this.setAttribute("defaultvalue", Array.isArray(value) ? value.join(" ") : String(value));
|
|
54
76
|
}
|
|
55
77
|
constructor() {
|
|
56
78
|
super();
|
|
@@ -113,7 +135,7 @@ class ASliderElement extends HTMLElementBase {
|
|
|
113
135
|
}
|
|
114
136
|
:host([track-click="jump"]) .control { cursor: pointer; }
|
|
115
137
|
.control[data-dragging] { cursor: grabbing; }
|
|
116
|
-
.control[data-dragging] .thumb { background: var(--slider-thumb-active); }
|
|
138
|
+
.control[data-dragging] .thumb[data-active] { background: var(--slider-thumb-active); }
|
|
117
139
|
:host(:focus-visible) .control:not([data-pointer-focus]) {
|
|
118
140
|
outline: 1px solid var(--focus-ring);
|
|
119
141
|
outline-offset: 0;
|
|
@@ -137,9 +159,9 @@ class ASliderElement extends HTMLElementBase {
|
|
|
137
159
|
background: var(--slider-track);
|
|
138
160
|
}
|
|
139
161
|
.fill {
|
|
140
|
-
inset-inline-start: calc(var(--
|
|
162
|
+
inset-inline-start: calc(var(--_fill-start, 0%) + var(--_fill-start-edge, 0px));
|
|
141
163
|
inset-inline-end: auto;
|
|
142
|
-
inline-size: calc(var(--
|
|
164
|
+
inline-size: calc(var(--_fill-end, 0%) - var(--_fill-start, 0%) + var(--_fill-end-edge, 0px) - var(--_fill-start-edge, 0px));
|
|
143
165
|
background: var(--slider-fill);
|
|
144
166
|
}
|
|
145
167
|
.thumb {
|
|
@@ -155,6 +177,12 @@ class ASliderElement extends HTMLElementBase {
|
|
|
155
177
|
transition: background-color 75ms ease-out, border-color 75ms ease-out;
|
|
156
178
|
transform: translate(-50%, -50%);
|
|
157
179
|
}
|
|
180
|
+
.thumb[data-last-active] { z-index: 1; }
|
|
181
|
+
.thumb:focus-visible {
|
|
182
|
+
outline: 1px solid var(--focus-ring);
|
|
183
|
+
outline-offset: 2px;
|
|
184
|
+
z-index: 2;
|
|
185
|
+
}
|
|
158
186
|
.thumb-value {
|
|
159
187
|
display: none;
|
|
160
188
|
position: absolute;
|
|
@@ -176,6 +204,7 @@ class ASliderElement extends HTMLElementBase {
|
|
|
176
204
|
));
|
|
177
205
|
}
|
|
178
206
|
:host([value-display="thumb"]) .thumb-value { display: block; }
|
|
207
|
+
:host([value-display="thumb"]) .thumb-value[hidden] { display: none !important; }
|
|
179
208
|
slot[name="markers"] {
|
|
180
209
|
display: block;
|
|
181
210
|
position: relative;
|
|
@@ -204,6 +233,7 @@ class ASliderElement extends HTMLElementBase {
|
|
|
204
233
|
.fill { background: Highlight; }
|
|
205
234
|
.thumb { background: Canvas; border-color: CanvasText; }
|
|
206
235
|
:host(:focus-visible) .control { outline: 2px solid Highlight; }
|
|
236
|
+
.thumb:focus-visible { outline: 2px solid Highlight; }
|
|
207
237
|
}
|
|
208
238
|
@media (prefers-reduced-motion: reduce) {
|
|
209
239
|
.rail,
|
|
@@ -248,9 +278,15 @@ class ASliderElement extends HTMLElementBase {
|
|
|
248
278
|
this.#fill = document.createElement("div");
|
|
249
279
|
this.#fill.className = "fill";
|
|
250
280
|
this.#fill.part.add("fill");
|
|
281
|
+
this.#fillEnd = document.createElement("div");
|
|
282
|
+
this.#fillEnd.className = "fill";
|
|
283
|
+
this.#fillEnd.part.add("fill", "fill-end");
|
|
251
284
|
this.#thumb = document.createElement("div");
|
|
252
285
|
this.#thumb.className = "thumb";
|
|
253
|
-
this.#thumb.part.add("thumb");
|
|
286
|
+
this.#thumb.part.add("thumb", "thumb-1");
|
|
287
|
+
this.#thumbEnd = document.createElement("div");
|
|
288
|
+
this.#thumbEnd.className = "thumb";
|
|
289
|
+
this.#thumbEnd.part.add("thumb", "thumb-2");
|
|
254
290
|
this.#thumbValue = document.createElement("span");
|
|
255
291
|
this.#thumbValue.className = "thumb-value";
|
|
256
292
|
this.#thumbValue.part.add("thumb-value");
|
|
@@ -259,7 +295,15 @@ class ASliderElement extends HTMLElementBase {
|
|
|
259
295
|
this.#thumbValueSuffix = document.createElement("span");
|
|
260
296
|
this.#thumbValueSuffix.className = "value-affix";
|
|
261
297
|
this.#thumbValue.append(this.#thumbValuePrefix, this.#thumbValueSuffix);
|
|
262
|
-
this.#
|
|
298
|
+
this.#thumbValueEnd = document.createElement("span");
|
|
299
|
+
this.#thumbValueEnd.className = "thumb-value";
|
|
300
|
+
this.#thumbValueEnd.part.add("thumb-value", "thumb-value-2");
|
|
301
|
+
this.#thumbValueEndPrefix = document.createElement("span");
|
|
302
|
+
this.#thumbValueEndPrefix.className = "value-affix";
|
|
303
|
+
this.#thumbValueEndSuffix = document.createElement("span");
|
|
304
|
+
this.#thumbValueEndSuffix.className = "value-affix";
|
|
305
|
+
this.#thumbValueEnd.append(this.#thumbValueEndPrefix, this.#thumbValueEndSuffix);
|
|
306
|
+
this.#railArea.append(rail, this.#fill, this.#fillEnd, this.#thumb, this.#thumbEnd, this.#thumbValue, this.#thumbValueEnd);
|
|
263
307
|
this.#control.append(this.#railArea);
|
|
264
308
|
this.#markers = document.createElement("slot");
|
|
265
309
|
this.#markers.name = "markers";
|
|
@@ -267,7 +311,17 @@ class ASliderElement extends HTMLElementBase {
|
|
|
267
311
|
this.#extras = document.createElement("slot");
|
|
268
312
|
this.#extras.part.add("extras");
|
|
269
313
|
for (const slot of [this.#label, this.#markers, this.#extras]) {
|
|
270
|
-
slot.addEventListener("slotchange", () =>
|
|
314
|
+
slot.addEventListener("slotchange", () => {
|
|
315
|
+
this.#syncSlotVisibility();
|
|
316
|
+
if (slot === this.#label) this.#paintAccessibility();
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
for (const [index, thumb] of [this.#thumb, this.#thumbEnd].entries()) {
|
|
320
|
+
thumb.addEventListener("focus", () => {
|
|
321
|
+
if (!Array.isArray(this.#value)) return;
|
|
322
|
+
this.#lastActiveThumb = index;
|
|
323
|
+
this.#paint();
|
|
324
|
+
});
|
|
271
325
|
}
|
|
272
326
|
this.#control.addEventListener("pointerdown", (event) => this.#beginDrag(event));
|
|
273
327
|
this.#control.addEventListener("pointermove", (event) => this.#moveDrag(event));
|
|
@@ -280,7 +334,7 @@ class ASliderElement extends HTMLElementBase {
|
|
|
280
334
|
}
|
|
281
335
|
connectedCallback() {
|
|
282
336
|
if (!this.#seeded) {
|
|
283
|
-
this.#
|
|
337
|
+
this.#applyValue(this.#initialValue());
|
|
284
338
|
this.#seeded = true;
|
|
285
339
|
}
|
|
286
340
|
this.#paint();
|
|
@@ -290,29 +344,29 @@ class ASliderElement extends HTMLElementBase {
|
|
|
290
344
|
if (!this.#seeded) return;
|
|
291
345
|
if (name === "value") {
|
|
292
346
|
if (this.hasAttribute("value")) {
|
|
293
|
-
this.#
|
|
347
|
+
this.#applyValue(parseSliderValue(this.getAttribute("value"), this.#fallbackValue()));
|
|
294
348
|
this.#dirty = false;
|
|
295
349
|
} else if (!this.#dirty) {
|
|
296
|
-
this.#
|
|
350
|
+
this.#applyValue(this.#initialValue());
|
|
297
351
|
}
|
|
298
352
|
} else if (name === "defaultvalue" && !this.hasAttribute("value") && !this.#dirty) {
|
|
299
|
-
this.#
|
|
353
|
+
this.#applyValue(this.#initialValue());
|
|
300
354
|
} else if (name === "min" || name === "max" || name === "step") {
|
|
301
|
-
this.#
|
|
355
|
+
this.#applyValue(this.#value);
|
|
302
356
|
}
|
|
303
357
|
this.#paint();
|
|
304
358
|
if (name === "value-display") this.#syncSlotVisibility();
|
|
305
359
|
}
|
|
306
360
|
formResetCallback() {
|
|
307
361
|
const previous = this.#value;
|
|
308
|
-
this.#
|
|
362
|
+
this.#applyValue(this.defaultValue);
|
|
309
363
|
this.#dirty = false;
|
|
310
364
|
this.#paint();
|
|
311
|
-
if (this.#value
|
|
365
|
+
if (!sameSliderValue(this.#value, previous)) this.#emitInputAndChange();
|
|
312
366
|
}
|
|
313
367
|
formStateRestoreCallback(state) {
|
|
314
368
|
if (typeof state !== "string") return;
|
|
315
|
-
this.#
|
|
369
|
+
this.#applyValue(parseSliderValue(state, this.#fallbackValue()));
|
|
316
370
|
this.#dirty = true;
|
|
317
371
|
this.#paint();
|
|
318
372
|
}
|
|
@@ -332,9 +386,13 @@ class ASliderElement extends HTMLElementBase {
|
|
|
332
386
|
get #isDisabled() {
|
|
333
387
|
return this.hasAttribute("disabled") || this.matches(":disabled");
|
|
334
388
|
}
|
|
389
|
+
#fallbackValue() {
|
|
390
|
+
const range = Array.isArray(this.#value) || Array.isArray(parseSliderValue(this.getAttribute("value"), this.#min));
|
|
391
|
+
return range ? [this.#min, this.#max] : this.#min;
|
|
392
|
+
}
|
|
335
393
|
#initialValue() {
|
|
336
394
|
const source = this.getAttribute("value") ?? this.getAttribute("defaultvalue");
|
|
337
|
-
return
|
|
395
|
+
return parseSliderValue(source, this.#fallbackValue());
|
|
338
396
|
}
|
|
339
397
|
#normalize(value) {
|
|
340
398
|
const min = this.#min;
|
|
@@ -344,38 +402,145 @@ class ASliderElement extends HTMLElementBase {
|
|
|
344
402
|
const precision = Math.min(12, Math.max(precisionOf(min), precisionOf(max), precisionOf(step)));
|
|
345
403
|
return Number(Math.min(max, Math.max(min, stepped)).toFixed(precision));
|
|
346
404
|
}
|
|
347
|
-
#
|
|
405
|
+
#normalizeValue(value) {
|
|
406
|
+
if (!Array.isArray(value)) return this.#normalize(value);
|
|
407
|
+
const low = this.#normalize(value[0]);
|
|
408
|
+
const high = this.#normalize(value[1]);
|
|
409
|
+
return low <= high ? [low, high] : [high, low];
|
|
410
|
+
}
|
|
411
|
+
#applyValue(value) {
|
|
412
|
+
const next = this.#normalizeValue(value);
|
|
413
|
+
const wasRange = Array.isArray(this.#value);
|
|
414
|
+
this.#value = next;
|
|
415
|
+
if (Array.isArray(next)) {
|
|
416
|
+
if (!wasRange) this.#orderReversed = false;
|
|
417
|
+
this.#physicalValues = this.#orderReversed ? [next[1], next[0]] : [next[0], next[1]];
|
|
418
|
+
} else {
|
|
419
|
+
this.#physicalValues = [next, next];
|
|
420
|
+
this.#orderReversed = false;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
#percent(value) {
|
|
348
424
|
const range = this.#max - this.#min;
|
|
349
425
|
return range > 0 ? (value - this.#min) / range * 100 : 0;
|
|
350
426
|
}
|
|
351
427
|
#paint() {
|
|
352
|
-
const
|
|
353
|
-
|
|
354
|
-
const
|
|
355
|
-
const
|
|
428
|
+
const currentValue = this.#value;
|
|
429
|
+
const range = Array.isArray(currentValue);
|
|
430
|
+
const low = range ? currentValue[0] : this.#min;
|
|
431
|
+
const high = range ? currentValue[1] : currentValue;
|
|
432
|
+
const lowPercent = `${this.#percent(low)}%`;
|
|
433
|
+
const highPercent = `${this.#percent(high)}%`;
|
|
434
|
+
const [first, second] = range ? this.#physicalValues : [high, high];
|
|
435
|
+
this.#thumb.style.setProperty("--_percent", `${this.#percent(first)}%`);
|
|
436
|
+
this.#thumbEnd.style.setProperty("--_percent", `${this.#percent(second)}%`);
|
|
437
|
+
this.#thumb.toggleAttribute("data-last-active", this.#lastActiveThumb === 0);
|
|
438
|
+
this.#thumbEnd.toggleAttribute("data-last-active", this.#lastActiveThumb === 1);
|
|
439
|
+
this.#thumbValue.style.setProperty("--_percent", `${this.#percent(first)}%`);
|
|
440
|
+
this.#thumbValueEnd.style.setProperty("--_percent", `${this.#percent(second)}%`);
|
|
441
|
+
this.#thumbEnd.hidden = !range;
|
|
442
|
+
const coincident = range && first === second;
|
|
443
|
+
this.#thumbValue.hidden = coincident && this.#lastActiveThumb !== 0;
|
|
444
|
+
this.#thumbValueEnd.hidden = !range || coincident && this.#lastActiveThumb !== 1;
|
|
445
|
+
const edgeStart = "calc(var(--slider-track-inset) - var(--anta-slider-thumb-size) / 2)";
|
|
446
|
+
const edgeEnd = "calc(var(--anta-slider-thumb-size) / 2 - var(--slider-track-inset))";
|
|
447
|
+
const setFill = (fill, start, end, startEdge = "0px", endEdge = "0px") => {
|
|
448
|
+
fill.style.setProperty("--_fill-start", start);
|
|
449
|
+
fill.style.setProperty("--_fill-end", end);
|
|
450
|
+
fill.style.setProperty("--_fill-start-edge", startEdge);
|
|
451
|
+
fill.style.setProperty("--_fill-end-edge", endEdge);
|
|
452
|
+
};
|
|
453
|
+
if (this.hasAttribute("inverted")) {
|
|
454
|
+
this.#fill.hidden = range ? low === this.#min : high === this.#max;
|
|
455
|
+
setFill(this.#fill, range ? "0%" : highPercent, range ? lowPercent : "100%", range ? edgeStart : high === this.#min ? edgeStart : "0px", range ? "0px" : edgeEnd);
|
|
456
|
+
this.#fillEnd.hidden = !range || high === this.#max;
|
|
457
|
+
if (range) setFill(this.#fillEnd, highPercent, "100%", "0px", edgeEnd);
|
|
458
|
+
} else {
|
|
459
|
+
this.#fill.hidden = range ? low === high : high === this.#min;
|
|
460
|
+
setFill(this.#fill, range ? lowPercent : "0%", highPercent, range && low !== this.#min ? "0px" : edgeStart, range && high === this.#max ? edgeEnd : "0px");
|
|
461
|
+
this.#fillEnd.hidden = true;
|
|
462
|
+
}
|
|
463
|
+
this.#renderValue(this.#inlineValue, this.#inlineValuePrefix, this.#inlineValueSuffix, range ? low : high, range ? high : void 0);
|
|
464
|
+
this.#renderValue(this.#endValue, this.#endValuePrefix, this.#endValueSuffix, range ? low : high, range ? high : void 0);
|
|
465
|
+
this.#renderValue(this.#thumbValue, this.#thumbValuePrefix, this.#thumbValueSuffix, first);
|
|
466
|
+
if (range) this.#renderValue(this.#thumbValueEnd, this.#thumbValueEndPrefix, this.#thumbValueEndSuffix, second);
|
|
467
|
+
this.#paintAccessibility();
|
|
468
|
+
this.#paintFormValue();
|
|
469
|
+
}
|
|
470
|
+
#renderValue(element, prefixElement, suffixElement, first, second) {
|
|
471
|
+
const prefix = this.getAttribute("value-prefix") ?? "";
|
|
472
|
+
const suffix = this.getAttribute("value-suffix") ?? "";
|
|
473
|
+
element.textContent = String(first);
|
|
474
|
+
prefixElement.textContent = prefix;
|
|
475
|
+
suffixElement.textContent = suffix;
|
|
476
|
+
element.prepend(prefixElement);
|
|
477
|
+
element.append(suffixElement);
|
|
478
|
+
if (second === void 0) return;
|
|
479
|
+
const secondPrefix = prefixElement.cloneNode(true);
|
|
480
|
+
const secondSuffix = suffixElement.cloneNode(true);
|
|
481
|
+
element.append("\u2013", secondPrefix, String(second), secondSuffix);
|
|
482
|
+
}
|
|
483
|
+
#formatValue(value) {
|
|
356
484
|
const prefix = this.getAttribute("value-prefix") ?? "";
|
|
357
485
|
const suffix = this.getAttribute("value-suffix") ?? "";
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
486
|
+
return `${prefix}${value}${suffix}`;
|
|
487
|
+
}
|
|
488
|
+
#paintAccessibility() {
|
|
489
|
+
const range = Array.isArray(this.#value);
|
|
490
|
+
const slottedLabel = this.#label.assignedNodes({ flatten: true }).map((node) => node.textContent ?? "").join("").trim();
|
|
491
|
+
const root = this.getRootNode();
|
|
492
|
+
const referencedLabel = (this.getAttribute("aria-labelledby") ?? "").split(/\s+/).map((id) => root.getElementById(id)?.textContent?.trim() ?? "").filter(Boolean).join(" ");
|
|
493
|
+
const base = (this.getAttribute("aria-label") ?? referencedLabel ?? slottedLabel).trim() || slottedLabel;
|
|
494
|
+
const internals = this.internals;
|
|
495
|
+
if (internals) {
|
|
496
|
+
internals.role = range ? "group" : "slider";
|
|
497
|
+
internals.ariaLabel = !this.hasAttribute("aria-label") && !this.hasAttribute("aria-labelledby") ? slottedLabel || null : null;
|
|
498
|
+
internals.ariaValueMin = range ? null : String(this.#min);
|
|
499
|
+
internals.ariaValueMax = range ? null : String(this.#max);
|
|
500
|
+
internals.ariaValueNow = range ? null : String(this.#value);
|
|
501
|
+
internals.ariaValueText = range ? null : this.#formatValue(this.#value);
|
|
502
|
+
}
|
|
503
|
+
for (const [index, thumb] of [this.#thumb, this.#thumbEnd].entries()) {
|
|
504
|
+
if (!range) {
|
|
505
|
+
thumb.removeAttribute("role");
|
|
506
|
+
thumb.tabIndex = -1;
|
|
507
|
+
thumb.removeAttribute("aria-label");
|
|
508
|
+
thumb.removeAttribute("aria-valuemin");
|
|
509
|
+
thumb.removeAttribute("aria-valuemax");
|
|
510
|
+
thumb.removeAttribute("aria-valuenow");
|
|
511
|
+
thumb.removeAttribute("aria-valuetext");
|
|
512
|
+
thumb.removeAttribute("aria-disabled");
|
|
513
|
+
continue;
|
|
514
|
+
}
|
|
515
|
+
const endpoint = index === 0 !== this.#orderReversed ? "minimum" : "maximum";
|
|
516
|
+
const value = this.#physicalValues[index];
|
|
517
|
+
thumb.role = "slider";
|
|
518
|
+
thumb.tabIndex = this.#isDisabled ? -1 : 0;
|
|
519
|
+
thumb.ariaLabel = base ? `${base} ${endpoint}` : `${endpoint[0].toUpperCase()}${endpoint.slice(1)} value`;
|
|
520
|
+
thumb.ariaValueMin = String(this.#min);
|
|
521
|
+
thumb.ariaValueMax = String(this.#max);
|
|
522
|
+
thumb.ariaValueNow = String(value);
|
|
523
|
+
thumb.ariaValueText = this.#formatValue(value);
|
|
524
|
+
thumb.ariaDisabled = this.#isDisabled ? "true" : null;
|
|
368
525
|
}
|
|
526
|
+
}
|
|
527
|
+
#paintFormValue() {
|
|
369
528
|
const internals = this.internals;
|
|
370
529
|
if (!internals) return;
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
530
|
+
const state = serializeSliderValue(this.#value);
|
|
531
|
+
if (this.#isDisabled) {
|
|
532
|
+
internals.setFormValue(null, state);
|
|
533
|
+
} else if (Array.isArray(this.#value)) {
|
|
534
|
+
const formValue = new FormData();
|
|
535
|
+
const name = this.getAttribute("name");
|
|
536
|
+
if (name) {
|
|
537
|
+
formValue.append(name, String(this.#value[0]));
|
|
538
|
+
formValue.append(name, String(this.#value[1]));
|
|
539
|
+
}
|
|
540
|
+
internals.setFormValue(formValue, state);
|
|
541
|
+
} else {
|
|
542
|
+
internals.setFormValue(state, state);
|
|
543
|
+
}
|
|
379
544
|
}
|
|
380
545
|
#syncSlotVisibility() {
|
|
381
546
|
const assigned = (slot) => slot.assignedNodes({ flatten: true }).length > 0;
|
|
@@ -383,12 +548,25 @@ class ASliderElement extends HTMLElementBase {
|
|
|
383
548
|
this.#markers.hidden = !assigned(this.#markers);
|
|
384
549
|
this.#extras.hidden = !assigned(this.#extras);
|
|
385
550
|
}
|
|
386
|
-
#setValue(value) {
|
|
551
|
+
#setValue(value, thumb = 0) {
|
|
387
552
|
const next = this.#normalize(value);
|
|
388
|
-
if (
|
|
389
|
-
|
|
553
|
+
if (Array.isArray(this.#value)) {
|
|
554
|
+
const previous = this.#value;
|
|
555
|
+
this.#physicalValues[thumb] = next;
|
|
556
|
+
if (this.#physicalValues[0] !== this.#physicalValues[1]) {
|
|
557
|
+
this.#orderReversed = this.#physicalValues[0] > this.#physicalValues[1];
|
|
558
|
+
}
|
|
559
|
+
this.#value = this.#orderReversed ? [this.#physicalValues[1], this.#physicalValues[0]] : [this.#physicalValues[0], this.#physicalValues[1]];
|
|
560
|
+
this.#lastActiveThumb = thumb;
|
|
561
|
+
this.#paint();
|
|
562
|
+
if (sameSliderValue(previous, this.#value)) return false;
|
|
563
|
+
} else {
|
|
564
|
+
if (next === this.#value) return false;
|
|
565
|
+
this.#value = next;
|
|
566
|
+
this.#physicalValues = [next, next];
|
|
567
|
+
this.#paint();
|
|
568
|
+
}
|
|
390
569
|
this.#dirty = true;
|
|
391
|
-
this.#paint();
|
|
392
570
|
this.dispatchEvent(new Event("input", { bubbles: true, composed: true }));
|
|
393
571
|
return true;
|
|
394
572
|
}
|
|
@@ -398,19 +576,33 @@ class ASliderElement extends HTMLElementBase {
|
|
|
398
576
|
}
|
|
399
577
|
#valueAt(clientX) {
|
|
400
578
|
const rect = this.#railArea.getBoundingClientRect();
|
|
401
|
-
if (rect.width <= 0) return this.#value;
|
|
579
|
+
if (rect.width <= 0) return Array.isArray(this.#value) ? this.#physicalValues[this.#lastActiveThumb] : this.#value;
|
|
402
580
|
return this.#min + (clientX - rect.left) / rect.width * (this.#max - this.#min);
|
|
403
581
|
}
|
|
582
|
+
#thumbAt(clientX) {
|
|
583
|
+
if (!Array.isArray(this.#value)) return 0;
|
|
584
|
+
const candidate = this.#valueAt(clientX);
|
|
585
|
+
const firstDistance = Math.abs(candidate - this.#physicalValues[0]);
|
|
586
|
+
const secondDistance = Math.abs(candidate - this.#physicalValues[1]);
|
|
587
|
+
return firstDistance === secondDistance ? this.#lastActiveThumb : firstDistance < secondDistance ? 0 : 1;
|
|
588
|
+
}
|
|
404
589
|
#beginDrag(event) {
|
|
405
590
|
if (this.#isDisabled || event.button !== 0) return;
|
|
406
591
|
const rect = this.#railArea.getBoundingClientRect();
|
|
407
592
|
if (rect.width <= 0) return;
|
|
408
|
-
this.#
|
|
593
|
+
const thumb = event.composedPath()[0] === this.#thumb ? 0 : event.composedPath()[0] === this.#thumbEnd ? 1 : this.#thumbAt(event.clientX);
|
|
594
|
+
const startValue = Array.isArray(this.#value) ? this.#physicalValues[thumb] : this.#value;
|
|
595
|
+
this.#drag = { pointerId: event.pointerId, startX: event.clientX, startValue, startPublicValue: this.value, thumb };
|
|
409
596
|
this.#control.setPointerCapture(event.pointerId);
|
|
410
597
|
this.#control.dataset.dragging = "";
|
|
411
598
|
this.#control.dataset.pointerFocus = "";
|
|
412
|
-
this
|
|
413
|
-
|
|
599
|
+
this.#lastActiveThumb = thumb;
|
|
600
|
+
this.#thumb.toggleAttribute("data-last-active", thumb === 0);
|
|
601
|
+
this.#thumbEnd.toggleAttribute("data-last-active", thumb === 1);
|
|
602
|
+
const focusTarget = Array.isArray(this.#value) ? [this.#thumb, this.#thumbEnd][thumb] : this;
|
|
603
|
+
focusTarget.focus({ preventScroll: true });
|
|
604
|
+
[this.#thumb, this.#thumbEnd][thumb].dataset.active = "";
|
|
605
|
+
if (this.getAttribute("track-click") === "jump") this.#setValue(this.#valueAt(event.clientX), thumb);
|
|
414
606
|
event.preventDefault();
|
|
415
607
|
}
|
|
416
608
|
#moveDrag(event) {
|
|
@@ -421,7 +613,7 @@ class ASliderElement extends HTMLElementBase {
|
|
|
421
613
|
return;
|
|
422
614
|
}
|
|
423
615
|
const next = this.getAttribute("track-click") === "jump" ? this.#valueAt(event.clientX) : drag.startValue + (event.clientX - drag.startX) / this.#railArea.getBoundingClientRect().width * (this.#max - this.#min);
|
|
424
|
-
this.#setValue(next);
|
|
616
|
+
this.#setValue(next, drag.thumb);
|
|
425
617
|
event.preventDefault();
|
|
426
618
|
}
|
|
427
619
|
#finishDrag(event) {
|
|
@@ -429,28 +621,33 @@ class ASliderElement extends HTMLElementBase {
|
|
|
429
621
|
if (!drag || drag.pointerId !== event.pointerId) return;
|
|
430
622
|
this.#drag = void 0;
|
|
431
623
|
delete this.#control.dataset.dragging;
|
|
624
|
+
delete [this.#thumb, this.#thumbEnd][drag.thumb].dataset.active;
|
|
432
625
|
if (this.#control.hasPointerCapture(event.pointerId)) this.#control.releasePointerCapture(event.pointerId);
|
|
433
|
-
if (this.#value
|
|
626
|
+
if (!sameSliderValue(this.#value, drag.startPublicValue)) this.dispatchEvent(new Event("change", { bubbles: true, composed: true }));
|
|
434
627
|
}
|
|
435
628
|
#handleKeydown(event) {
|
|
436
|
-
|
|
629
|
+
const source = event.composedPath()[0];
|
|
630
|
+
const range = Array.isArray(this.#value);
|
|
631
|
+
const thumb = source === this.#thumb ? 0 : source === this.#thumbEnd ? 1 : void 0;
|
|
632
|
+
if ((range ? thumb === void 0 : source !== this) || this.#isDisabled || isModifiedNavigationKey(event)) return;
|
|
437
633
|
delete this.#control.dataset.pointerFocus;
|
|
438
634
|
const page = Math.max(this.#step, (this.#max - this.#min) / 10);
|
|
635
|
+
const current = range ? this.#physicalValues[thumb ?? 0] : this.#value;
|
|
439
636
|
let next;
|
|
440
637
|
switch (event.key) {
|
|
441
638
|
case "ArrowRight":
|
|
442
639
|
case "ArrowUp":
|
|
443
|
-
next =
|
|
640
|
+
next = current + this.#step;
|
|
444
641
|
break;
|
|
445
642
|
case "ArrowLeft":
|
|
446
643
|
case "ArrowDown":
|
|
447
|
-
next =
|
|
644
|
+
next = current - this.#step;
|
|
448
645
|
break;
|
|
449
646
|
case "PageUp":
|
|
450
|
-
next =
|
|
647
|
+
next = current + page;
|
|
451
648
|
break;
|
|
452
649
|
case "PageDown":
|
|
453
|
-
next =
|
|
650
|
+
next = current - page;
|
|
454
651
|
break;
|
|
455
652
|
case "Home":
|
|
456
653
|
next = this.#min;
|
|
@@ -462,7 +659,7 @@ class ASliderElement extends HTMLElementBase {
|
|
|
462
659
|
return;
|
|
463
660
|
}
|
|
464
661
|
event.preventDefault();
|
|
465
|
-
if (this.#setValue(next)) this.dispatchEvent(new Event("change", { bubbles: true, composed: true }));
|
|
662
|
+
if (this.#setValue(next, range ? thumb ?? 0 : 0)) this.dispatchEvent(new Event("change", { bubbles: true, composed: true }));
|
|
466
663
|
}
|
|
467
664
|
}
|
|
468
665
|
function register_a_slider() {
|
package/dist/general_types.d.ts
CHANGED
|
@@ -334,6 +334,10 @@ export interface ABoxAttributes extends BaseAttributes {
|
|
|
334
334
|
* typed `attr()` read it from `--box-gap` in the host's inline style
|
|
335
335
|
* instead, which is what the JSX wrapper always sets. */
|
|
336
336
|
gap?: boolean | '' | number | string;
|
|
337
|
+
/** Adds matching descendant rects to each measurement snapshot. With
|
|
338
|
+
* `observe="scroll"`, use `throttle` when many descendants match: rects are
|
|
339
|
+
* read for each emitted event, and CSS state updates follow the interval. */
|
|
340
|
+
'include-rects-for'?: string;
|
|
337
341
|
/** CSS padding shorthand (`padding="8px 16px"`). Without typed `attr()`
|
|
338
342
|
* support, set `--box-padding` as well. The JSX wrapper supplies it. */
|
|
339
343
|
padding?: boolean | '' | number | string;
|
|
@@ -348,8 +352,12 @@ export interface ABoxAttributes extends BaseAttributes {
|
|
|
348
352
|
* attribute means `all`. Omission runs no observers unless `fade` is set;
|
|
349
353
|
* a native event listener alone does not enable observation. */
|
|
350
354
|
observe?: string;
|
|
351
|
-
/**
|
|
352
|
-
*
|
|
355
|
+
/** Keeps measurement observation active outside the viewport when `observe`
|
|
356
|
+
* or `fade` selects measurement. */
|
|
357
|
+
'observe-offscreen'?: boolean | '';
|
|
358
|
+
/** Minimum interval between measurement reads, CSS state updates, and
|
|
359
|
+
* events in milliseconds. The initial read is immediate. Omit or pass `0`
|
|
360
|
+
* for frame-based updates. Invalid or negative values use `0`. */
|
|
353
361
|
throttle?: number | string;
|
|
354
362
|
/** Depth of that mask, as a length value (`fade-size="2rem"`). Engines
|
|
355
363
|
* without typed `attr()` read it from `--box-fade-size` in the host's
|
|
@@ -423,6 +431,11 @@ export interface ATagAttributes extends BaseAttributes {
|
|
|
423
431
|
* use `Expander` from `@antadesign/anta`.
|
|
424
432
|
*/
|
|
425
433
|
export interface AExpanderAttributes extends BaseAttributes {
|
|
434
|
+
/** Built-in disclosure mark. Custom visuals use `slot="indicator"`.
|
|
435
|
+
* Defaults to `chevron`. */
|
|
436
|
+
indicator?: 'chevron' | 'triangle' | 'plus';
|
|
437
|
+
/** Logical side for the disclosure mark. Defaults to `start`. */
|
|
438
|
+
'indicator-placement'?: 'start' | 'end';
|
|
426
439
|
/** Controlled open state (`'open'` / `'closed'`). Present → controlled: the
|
|
427
440
|
* attribute is the source of truth, clicks only dispatch the cancelable
|
|
428
441
|
* `statechange` event, and the consumer answers by updating it. Absent →
|
|
@@ -434,10 +447,9 @@ export interface AExpanderAttributes extends BaseAttributes {
|
|
|
434
447
|
/** Surface emphasis. `secondary` (default) is a subtle fill; `primary`
|
|
435
448
|
* is a stronger raised fill; `tertiary` is transparent. */
|
|
436
449
|
priority?: 'primary' | 'secondary' | 'tertiary';
|
|
437
|
-
/**
|
|
438
|
-
*
|
|
439
|
-
*
|
|
440
|
-
* has to bound the chevron. Presence-based. */
|
|
450
|
+
/** Align the title and body with surrounding content on a tertiary
|
|
451
|
+
* expander. The start indicator hangs in the gutter; an end indicator
|
|
452
|
+
* stays inside its edge. Presence-based. */
|
|
441
453
|
outdent?: boolean | '';
|
|
442
454
|
/** Disables the header: not clickable or focusable, hover affordance
|
|
443
455
|
* off, text dimmed. The open state freezes as-is. Presence-based. */
|
|
@@ -693,9 +705,12 @@ export interface AInputAttributes extends BaseAttributes {
|
|
|
693
705
|
* wrapper) the glyph. Only `critical` carries validity weight (`aria-invalid`
|
|
694
706
|
* + `:state(invalid)`); the others are advisory. Omit for the neutral field. */
|
|
695
707
|
status?: 'neutral' | 'brand' | 'info' | 'success' | 'warning' | 'critical';
|
|
696
|
-
/** Custom accent color (any literal CSS color) — tints the
|
|
697
|
-
*
|
|
708
|
+
/** Custom accent color (any literal CSS color) — tints the border when shown
|
|
709
|
+
* via an oklch derivation. `status` overrides it for validation. */
|
|
698
710
|
tone?: string;
|
|
711
|
+
/** Field emphasis. Secondary removes the fill; tertiary also makes the
|
|
712
|
+
* resting border transparent. Status borders remain visible. */
|
|
713
|
+
priority?: 'primary' | 'secondary' | 'tertiary';
|
|
699
714
|
/** Disabled state. Presence-based. */
|
|
700
715
|
disabled?: boolean | '';
|
|
701
716
|
/** Read-only state. Presence-based. */
|
|
@@ -750,9 +765,9 @@ export interface AInputAttributes extends BaseAttributes {
|
|
|
750
765
|
/** Attributes for the `<a-slider>` custom element. For the typed JSX wrapper,
|
|
751
766
|
* display formatting, and marker layout use `Slider` from `@antadesign/anta`. */
|
|
752
767
|
export interface ASliderAttributes extends BaseAttributes {
|
|
753
|
-
/** Controlled
|
|
768
|
+
/** Controlled value. Range attributes use two space-separated numbers. */
|
|
754
769
|
value?: number | string;
|
|
755
|
-
/** Initial uncontrolled value and form-reset baseline. */
|
|
770
|
+
/** Initial uncontrolled value and form-reset baseline. Range attributes use two space-separated numbers. */
|
|
756
771
|
defaultvalue?: number | string;
|
|
757
772
|
/** Lowest permitted value. Defaults to 0. */
|
|
758
773
|
min?: number | string;
|
|
@@ -780,6 +795,8 @@ export interface ASliderAttributes extends BaseAttributes {
|
|
|
780
795
|
round?: boolean | number | string;
|
|
781
796
|
/** `jump` moves to a pressed rail position. Omit for relative dragging. */
|
|
782
797
|
'track-click'?: 'jump';
|
|
798
|
+
/** Fill the rail outside the selected value or range. Presence-based. */
|
|
799
|
+
inverted?: boolean | '';
|
|
783
800
|
/** `inline`, `thumb`, or `none`; omit for a value at the label row's end. */
|
|
784
801
|
'value-display'?: 'inline' | 'thumb' | 'none';
|
|
785
802
|
/** Text inserted before the live numeric value. */
|