bits-ui 1.0.0-next.75 → 1.0.0-next.76
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/bits/accordion/accordion.svelte.js +1 -1
- package/dist/bits/dialog/dialog.svelte.d.ts +0 -4
- package/dist/bits/dialog/dialog.svelte.js +0 -26
- package/dist/bits/popover/components/popover-content-static.svelte +10 -25
- package/dist/bits/popover/components/popover-content.svelte +9 -27
- package/dist/bits/popover/popover.svelte.d.ts +8 -1
- package/dist/bits/popover/popover.svelte.js +34 -0
- package/dist/bits/slider/components/slider.svelte +12 -3
- package/dist/bits/slider/slider.svelte.d.ts +110 -27
- package/dist/bits/slider/slider.svelte.js +248 -35
- package/dist/bits/slider/types.d.ts +52 -18
- package/package.json +1 -1
|
@@ -63,12 +63,10 @@ declare class DialogCloseState {
|
|
|
63
63
|
#private;
|
|
64
64
|
constructor(props: DialogCloseStateProps, root: DialogRootState);
|
|
65
65
|
onclick(e: BitsMouseEvent): void;
|
|
66
|
-
onpointerdown(e: BitsPointerEvent): void;
|
|
67
66
|
onkeydown(e: BitsKeyboardEvent): void;
|
|
68
67
|
props: {
|
|
69
68
|
readonly "data-state": "open" | "closed";
|
|
70
69
|
readonly id: string;
|
|
71
|
-
readonly onpointerdown: (e: BitsPointerEvent) => void;
|
|
72
70
|
readonly onclick: (e: BitsMouseEvent) => void;
|
|
73
71
|
readonly onkeydown: (e: BitsKeyboardEvent) => void;
|
|
74
72
|
};
|
|
@@ -146,12 +144,10 @@ declare class AlertDialogCancelState {
|
|
|
146
144
|
#private;
|
|
147
145
|
constructor(props: AlertDialogCancelStateProps, root: DialogRootState);
|
|
148
146
|
onclick(e: BitsMouseEvent): void;
|
|
149
|
-
onpointerdown(e: BitsPointerEvent): void;
|
|
150
147
|
onkeydown(e: BitsKeyboardEvent): void;
|
|
151
148
|
props: {
|
|
152
149
|
readonly "data-state": "open" | "closed";
|
|
153
150
|
readonly id: string;
|
|
154
|
-
readonly onpointerdown: (e: BitsPointerEvent) => void;
|
|
155
151
|
readonly onclick: (e: BitsMouseEvent) => void;
|
|
156
152
|
readonly onkeydown: (e: BitsKeyboardEvent) => void;
|
|
157
153
|
};
|
|
@@ -119,7 +119,6 @@ class DialogCloseState {
|
|
|
119
119
|
this.#variant = props.variant;
|
|
120
120
|
this.#disabled = props.disabled;
|
|
121
121
|
this.onclick = this.onclick.bind(this);
|
|
122
|
-
this.onpointerdown = this.onpointerdown.bind(this);
|
|
123
122
|
this.onkeydown = this.onkeydown.bind(this);
|
|
124
123
|
useRefById({
|
|
125
124
|
id: this.#id,
|
|
@@ -134,16 +133,6 @@ class DialogCloseState {
|
|
|
134
133
|
return;
|
|
135
134
|
this.#root.handleClose();
|
|
136
135
|
}
|
|
137
|
-
onpointerdown(e) {
|
|
138
|
-
if (this.#disabled.current)
|
|
139
|
-
return;
|
|
140
|
-
if (e.button > 0)
|
|
141
|
-
return;
|
|
142
|
-
// by default, it will attempt to focus this trigger on pointerdown
|
|
143
|
-
// since this also closes the dialog and restores focus we want to prevent that behavior
|
|
144
|
-
e.preventDefault();
|
|
145
|
-
this.#root.handleClose();
|
|
146
|
-
}
|
|
147
136
|
onkeydown(e) {
|
|
148
137
|
if (this.#disabled.current)
|
|
149
138
|
return;
|
|
@@ -155,7 +144,6 @@ class DialogCloseState {
|
|
|
155
144
|
props = $derived.by(() => ({
|
|
156
145
|
id: this.#id.current,
|
|
157
146
|
[this.#attr]: "",
|
|
158
|
-
onpointerdown: this.onpointerdown,
|
|
159
147
|
onclick: this.onclick,
|
|
160
148
|
onkeydown: this.onkeydown,
|
|
161
149
|
...this.#root.sharedProps,
|
|
@@ -299,7 +287,6 @@ class AlertDialogCancelState {
|
|
|
299
287
|
this.#root = root;
|
|
300
288
|
this.#disabled = props.disabled;
|
|
301
289
|
this.onclick = this.onclick.bind(this);
|
|
302
|
-
this.onpointerdown = this.onpointerdown.bind(this);
|
|
303
290
|
this.onkeydown = this.onkeydown.bind(this);
|
|
304
291
|
useRefById({
|
|
305
292
|
id: this.#id,
|
|
@@ -317,18 +304,6 @@ class AlertDialogCancelState {
|
|
|
317
304
|
return;
|
|
318
305
|
this.#root.handleClose();
|
|
319
306
|
}
|
|
320
|
-
onpointerdown(e) {
|
|
321
|
-
if (this.#disabled.current)
|
|
322
|
-
return;
|
|
323
|
-
if (e.pointerType === "touch")
|
|
324
|
-
return e.preventDefault();
|
|
325
|
-
if (e.button > 0)
|
|
326
|
-
return;
|
|
327
|
-
// by default, it will attempt to focus this trigger on pointerdown
|
|
328
|
-
// since this also opens the dialog we want to prevent that behavior
|
|
329
|
-
e.preventDefault();
|
|
330
|
-
this.#root.handleClose();
|
|
331
|
-
}
|
|
332
307
|
onkeydown(e) {
|
|
333
308
|
if (this.#disabled.current)
|
|
334
309
|
return;
|
|
@@ -340,7 +315,6 @@ class AlertDialogCancelState {
|
|
|
340
315
|
props = $derived.by(() => ({
|
|
341
316
|
id: this.#id.current,
|
|
342
317
|
[this.#root.attrs.cancel]: "",
|
|
343
|
-
onpointerdown: this.onpointerdown,
|
|
344
318
|
onclick: this.onclick,
|
|
345
319
|
onkeydown: this.onkeydown,
|
|
346
320
|
...this.#root.sharedProps,
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
onInteractOutside = noop,
|
|
20
20
|
trapFocus = true,
|
|
21
21
|
preventScroll = false,
|
|
22
|
+
|
|
22
23
|
...restProps
|
|
23
24
|
}: PopoverContentStaticProps = $props();
|
|
24
25
|
|
|
@@ -28,28 +29,12 @@
|
|
|
28
29
|
() => ref,
|
|
29
30
|
(v) => (ref = v)
|
|
30
31
|
),
|
|
32
|
+
onInteractOutside: box.with(() => onInteractOutside),
|
|
33
|
+
onEscapeKeydown: box.with(() => onEscapeKeydown),
|
|
34
|
+
onCloseAutoFocus: box.with(() => onCloseAutoFocus),
|
|
31
35
|
});
|
|
32
36
|
|
|
33
37
|
const mergedProps = $derived(mergeProps(restProps, contentState.props));
|
|
34
|
-
|
|
35
|
-
function handleInteractOutside(e: PointerEvent) {
|
|
36
|
-
onInteractOutside(e);
|
|
37
|
-
if (e.defaultPrevented) return;
|
|
38
|
-
contentState.root.handleClose();
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function handleEscapeKeydown(e: KeyboardEvent) {
|
|
42
|
-
onEscapeKeydown(e);
|
|
43
|
-
if (e.defaultPrevented) return;
|
|
44
|
-
contentState.root.handleClose();
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function handleCloseAutoFocus(e: Event) {
|
|
48
|
-
onCloseAutoFocus(e);
|
|
49
|
-
if (e.defaultPrevented) return;
|
|
50
|
-
e.preventDefault();
|
|
51
|
-
contentState.root.triggerNode?.focus();
|
|
52
|
-
}
|
|
53
38
|
</script>
|
|
54
39
|
|
|
55
40
|
{#if forceMount}
|
|
@@ -58,9 +43,9 @@
|
|
|
58
43
|
isStatic
|
|
59
44
|
enabled={contentState.root.open.current}
|
|
60
45
|
{id}
|
|
61
|
-
onInteractOutside={handleInteractOutside}
|
|
62
|
-
onEscapeKeydown={handleEscapeKeydown}
|
|
63
|
-
onCloseAutoFocus={handleCloseAutoFocus}
|
|
46
|
+
onInteractOutside={contentState.handleInteractOutside}
|
|
47
|
+
onEscapeKeydown={contentState.handleEscapeKeydown}
|
|
48
|
+
onCloseAutoFocus={contentState.handleCloseAutoFocus}
|
|
64
49
|
{trapFocus}
|
|
65
50
|
{preventScroll}
|
|
66
51
|
loop
|
|
@@ -85,9 +70,9 @@
|
|
|
85
70
|
isStatic
|
|
86
71
|
present={contentState.root.open.current}
|
|
87
72
|
{id}
|
|
88
|
-
onInteractOutside={handleInteractOutside}
|
|
89
|
-
onEscapeKeydown={handleEscapeKeydown}
|
|
90
|
-
onCloseAutoFocus={handleCloseAutoFocus}
|
|
73
|
+
onInteractOutside={contentState.handleInteractOutside}
|
|
74
|
+
onEscapeKeydown={contentState.handleEscapeKeydown}
|
|
75
|
+
onCloseAutoFocus={contentState.handleCloseAutoFocus}
|
|
91
76
|
{trapFocus}
|
|
92
77
|
{preventScroll}
|
|
93
78
|
loop
|
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
import { useId } from "../../../internal/use-id.js";
|
|
8
8
|
import { getFloatingContentCSSVars } from "../../../internal/floating-svelte/floating-utils.svelte.js";
|
|
9
9
|
import PopperLayerForceMount from "../../utilities/popper-layer/popper-layer-force-mount.svelte";
|
|
10
|
-
import { isHTMLElement } from "../../../internal/is.js";
|
|
11
10
|
|
|
12
11
|
let {
|
|
13
12
|
child,
|
|
@@ -29,29 +28,12 @@
|
|
|
29
28
|
() => ref,
|
|
30
29
|
(v) => (ref = v)
|
|
31
30
|
),
|
|
31
|
+
onInteractOutside: box.with(() => onInteractOutside),
|
|
32
|
+
onEscapeKeydown: box.with(() => onEscapeKeydown),
|
|
33
|
+
onCloseAutoFocus: box.with(() => onCloseAutoFocus),
|
|
32
34
|
});
|
|
33
35
|
|
|
34
36
|
const mergedProps = $derived(mergeProps(restProps, contentState.props));
|
|
35
|
-
|
|
36
|
-
function handleInteractOutside(e: PointerEvent) {
|
|
37
|
-
onInteractOutside(e);
|
|
38
|
-
if (e.defaultPrevented) return;
|
|
39
|
-
if (isHTMLElement(e.target) && e.target.closest("[data-popover-trigger")) return;
|
|
40
|
-
contentState.root.handleClose();
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function handleEscapeKeydown(e: KeyboardEvent) {
|
|
44
|
-
onEscapeKeydown(e);
|
|
45
|
-
if (e.defaultPrevented) return;
|
|
46
|
-
contentState.root.handleClose();
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function handleCloseAutoFocus(e: Event) {
|
|
50
|
-
onCloseAutoFocus(e);
|
|
51
|
-
if (e.defaultPrevented) return;
|
|
52
|
-
e.preventDefault();
|
|
53
|
-
contentState.root.triggerNode?.focus();
|
|
54
|
-
}
|
|
55
37
|
</script>
|
|
56
38
|
|
|
57
39
|
{#if forceMount}
|
|
@@ -59,9 +41,9 @@
|
|
|
59
41
|
{...mergedProps}
|
|
60
42
|
enabled={contentState.root.open.current}
|
|
61
43
|
{id}
|
|
62
|
-
onInteractOutside={handleInteractOutside}
|
|
63
|
-
onEscapeKeydown={handleEscapeKeydown}
|
|
64
|
-
onCloseAutoFocus={handleCloseAutoFocus}
|
|
44
|
+
onInteractOutside={contentState.handleInteractOutside}
|
|
45
|
+
onEscapeKeydown={contentState.handleEscapeKeydown}
|
|
46
|
+
onCloseAutoFocus={contentState.handleCloseAutoFocus}
|
|
65
47
|
{trapFocus}
|
|
66
48
|
{preventScroll}
|
|
67
49
|
loop
|
|
@@ -87,9 +69,9 @@
|
|
|
87
69
|
{...mergedProps}
|
|
88
70
|
present={contentState.root.open.current}
|
|
89
71
|
{id}
|
|
90
|
-
onInteractOutside={handleInteractOutside}
|
|
91
|
-
onEscapeKeydown={handleEscapeKeydown}
|
|
92
|
-
onCloseAutoFocus={handleCloseAutoFocus}
|
|
72
|
+
onInteractOutside={contentState.handleInteractOutside}
|
|
73
|
+
onEscapeKeydown={contentState.handleEscapeKeydown}
|
|
74
|
+
onCloseAutoFocus={contentState.handleCloseAutoFocus}
|
|
93
75
|
{trapFocus}
|
|
94
76
|
{preventScroll}
|
|
95
77
|
loop
|
|
@@ -34,11 +34,18 @@ declare class PopoverTriggerState {
|
|
|
34
34
|
readonly onclick: (e: BitsMouseEvent) => void;
|
|
35
35
|
};
|
|
36
36
|
}
|
|
37
|
-
type PopoverContentStateProps = WithRefProps
|
|
37
|
+
type PopoverContentStateProps = WithRefProps & ReadableBoxedValues<{
|
|
38
|
+
onInteractOutside: (e: PointerEvent) => void;
|
|
39
|
+
onEscapeKeydown: (e: KeyboardEvent) => void;
|
|
40
|
+
onCloseAutoFocus: (e: Event) => void;
|
|
41
|
+
}>;
|
|
38
42
|
declare class PopoverContentState {
|
|
39
43
|
#private;
|
|
40
44
|
root: PopoverRootState;
|
|
41
45
|
constructor(props: PopoverContentStateProps, root: PopoverRootState);
|
|
46
|
+
handleInteractOutside(e: PointerEvent): void;
|
|
47
|
+
handleEscapeKeydown(e: KeyboardEvent): void;
|
|
48
|
+
handleCloseAutoFocus(e: Event): void;
|
|
42
49
|
snippetProps: {
|
|
43
50
|
open: boolean;
|
|
44
51
|
};
|
|
@@ -2,6 +2,7 @@ import { useRefById } from "svelte-toolbelt";
|
|
|
2
2
|
import { Context } from "runed";
|
|
3
3
|
import { kbd } from "../../internal/kbd.js";
|
|
4
4
|
import { getAriaExpanded, getDataOpenClosed } from "../../internal/attrs.js";
|
|
5
|
+
import { isElement } from "../../internal/is.js";
|
|
5
6
|
class PopoverRootState {
|
|
6
7
|
open;
|
|
7
8
|
contentNode = $state(null);
|
|
@@ -87,10 +88,16 @@ class PopoverContentState {
|
|
|
87
88
|
#id;
|
|
88
89
|
#ref;
|
|
89
90
|
root;
|
|
91
|
+
#onInteractOutside;
|
|
92
|
+
#onEscapeKeydown;
|
|
93
|
+
#onCloseAutoFocus;
|
|
90
94
|
constructor(props, root) {
|
|
91
95
|
this.#id = props.id;
|
|
92
96
|
this.root = root;
|
|
93
97
|
this.#ref = props.ref;
|
|
98
|
+
this.#onEscapeKeydown = props.onEscapeKeydown;
|
|
99
|
+
this.#onInteractOutside = props.onInteractOutside;
|
|
100
|
+
this.#onCloseAutoFocus = props.onCloseAutoFocus;
|
|
94
101
|
useRefById({
|
|
95
102
|
id: this.#id,
|
|
96
103
|
ref: this.#ref,
|
|
@@ -99,6 +106,33 @@ class PopoverContentState {
|
|
|
99
106
|
this.root.contentNode = node;
|
|
100
107
|
},
|
|
101
108
|
});
|
|
109
|
+
this.handleInteractOutside = this.handleInteractOutside.bind(this);
|
|
110
|
+
this.handleEscapeKeydown = this.handleEscapeKeydown.bind(this);
|
|
111
|
+
this.handleCloseAutoFocus = this.handleCloseAutoFocus.bind(this);
|
|
112
|
+
}
|
|
113
|
+
handleInteractOutside(e) {
|
|
114
|
+
this.#onInteractOutside.current(e);
|
|
115
|
+
if (e.defaultPrevented)
|
|
116
|
+
return;
|
|
117
|
+
if (!isElement(e.target))
|
|
118
|
+
return;
|
|
119
|
+
const closestTrigger = e.target.closest(`[data-popover-trigger]`);
|
|
120
|
+
if (closestTrigger === this.root.triggerNode)
|
|
121
|
+
return;
|
|
122
|
+
this.root.handleClose();
|
|
123
|
+
}
|
|
124
|
+
handleEscapeKeydown(e) {
|
|
125
|
+
this.#onEscapeKeydown.current(e);
|
|
126
|
+
if (e.defaultPrevented)
|
|
127
|
+
return;
|
|
128
|
+
this.root.handleClose();
|
|
129
|
+
}
|
|
130
|
+
handleCloseAutoFocus(e) {
|
|
131
|
+
this.#onCloseAutoFocus.current(e);
|
|
132
|
+
if (e.defaultPrevented)
|
|
133
|
+
return;
|
|
134
|
+
e.preventDefault();
|
|
135
|
+
this.root.triggerNode?.focus();
|
|
102
136
|
}
|
|
103
137
|
snippetProps = $derived.by(() => ({ open: this.root.open.current }));
|
|
104
138
|
props = $derived.by(() => ({
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { box, mergeProps } from "svelte-toolbelt";
|
|
2
|
+
import { box, mergeProps, type WritableBox } from "svelte-toolbelt";
|
|
3
3
|
import type { SliderRootProps } from "../types.js";
|
|
4
4
|
import { useSliderRoot } from "../slider.svelte.js";
|
|
5
5
|
import { useId } from "../../../internal/use-id.js";
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
child,
|
|
11
11
|
id = useId(),
|
|
12
12
|
ref = $bindable(null),
|
|
13
|
-
value = $bindable(
|
|
13
|
+
value = $bindable(),
|
|
14
|
+
type,
|
|
14
15
|
onValueChange = noop,
|
|
15
16
|
onValueCommit = noop,
|
|
16
17
|
disabled = false,
|
|
@@ -24,6 +25,10 @@
|
|
|
24
25
|
...restProps
|
|
25
26
|
}: SliderRootProps = $props();
|
|
26
27
|
|
|
28
|
+
if (value === undefined) {
|
|
29
|
+
value = type === "single" ? 0 : [];
|
|
30
|
+
}
|
|
31
|
+
|
|
27
32
|
const rootState = useSliderRoot({
|
|
28
33
|
id: box.with(() => id),
|
|
29
34
|
ref: box.with(
|
|
@@ -34,13 +39,16 @@
|
|
|
34
39
|
() => value,
|
|
35
40
|
(v) => {
|
|
36
41
|
if (controlledValue) {
|
|
42
|
+
// @ts-expect-error - we know
|
|
37
43
|
onValueChange(v);
|
|
38
44
|
} else {
|
|
39
45
|
value = v;
|
|
46
|
+
// @ts-expect-error - we know
|
|
40
47
|
onValueChange(v);
|
|
41
48
|
}
|
|
42
49
|
}
|
|
43
|
-
)
|
|
50
|
+
) as WritableBox<number> | WritableBox<number[]>,
|
|
51
|
+
// @ts-expect-error - we know
|
|
44
52
|
onValueCommit: box.with(() => onValueCommit),
|
|
45
53
|
disabled: box.with(() => disabled),
|
|
46
54
|
min: box.with(() => min),
|
|
@@ -49,6 +57,7 @@
|
|
|
49
57
|
dir: box.with(() => dir),
|
|
50
58
|
autoSort: box.with(() => autoSort),
|
|
51
59
|
orientation: box.with(() => orientation),
|
|
60
|
+
type,
|
|
52
61
|
});
|
|
53
62
|
|
|
54
63
|
const mergedProps = $derived(mergeProps(restProps, rootState.props));
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { type Box, type ReadableBox } from "svelte-toolbelt";
|
|
1
2
|
import type { ReadableBoxedValues, WritableBoxedValues } from "../../internal/box.svelte.js";
|
|
2
3
|
import type { BitsKeyboardEvent, OnChangeFn, WithRefProps } from "../../internal/types.js";
|
|
3
4
|
import type { Direction, Orientation } from "../../shared/index.js";
|
|
4
|
-
type
|
|
5
|
+
type SliderBaseRootStateProps = WithRefProps<ReadableBoxedValues<{
|
|
5
6
|
disabled: boolean;
|
|
6
7
|
orientation: Orientation;
|
|
7
8
|
min: number;
|
|
@@ -9,31 +10,95 @@ type SliderRootStateProps = WithRefProps<ReadableBoxedValues<{
|
|
|
9
10
|
step: number;
|
|
10
11
|
dir: Direction;
|
|
11
12
|
autoSort: boolean;
|
|
13
|
+
}>>;
|
|
14
|
+
declare class SliderBaseRootState {
|
|
15
|
+
#private;
|
|
16
|
+
id: SliderBaseRootStateProps["id"];
|
|
17
|
+
ref: SliderBaseRootStateProps["ref"];
|
|
18
|
+
disabled: SliderBaseRootStateProps["disabled"];
|
|
19
|
+
orientation: SliderBaseRootStateProps["orientation"];
|
|
20
|
+
min: SliderBaseRootStateProps["min"];
|
|
21
|
+
max: SliderBaseRootStateProps["max"];
|
|
22
|
+
step: SliderBaseRootStateProps["step"];
|
|
23
|
+
dir: SliderBaseRootStateProps["dir"];
|
|
24
|
+
autoSort: SliderBaseRootStateProps["autoSort"];
|
|
25
|
+
isActive: boolean;
|
|
26
|
+
direction: "rl" | "lr" | "tb" | "bt";
|
|
27
|
+
constructor(props: SliderBaseRootStateProps);
|
|
28
|
+
getAllThumbs: () => HTMLElement[];
|
|
29
|
+
props: {
|
|
30
|
+
readonly id: string;
|
|
31
|
+
readonly "data-orientation": "horizontal" | "vertical";
|
|
32
|
+
readonly "data-disabled": "" | undefined;
|
|
33
|
+
readonly style: {
|
|
34
|
+
readonly touchAction: string | undefined;
|
|
35
|
+
};
|
|
36
|
+
readonly "data-slider-root": "";
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
type SliderSingleRootStateProps = SliderBaseRootStateProps & ReadableBoxedValues<{
|
|
40
|
+
onValueCommit: OnChangeFn<number>;
|
|
41
|
+
}> & WritableBoxedValues<{
|
|
42
|
+
value: number;
|
|
43
|
+
}>;
|
|
44
|
+
declare class SliderSingleRootState extends SliderBaseRootState {
|
|
45
|
+
value: SliderSingleRootStateProps["value"];
|
|
46
|
+
onValueCommit: SliderSingleRootStateProps["onValueCommit"];
|
|
47
|
+
isMulti: false;
|
|
48
|
+
constructor(props: SliderSingleRootStateProps);
|
|
49
|
+
applyPosition({ clientXY, start, end }: {
|
|
50
|
+
clientXY: number;
|
|
51
|
+
start: number;
|
|
52
|
+
end: number;
|
|
53
|
+
}): void;
|
|
54
|
+
updateValue: (newValue: number) => void;
|
|
55
|
+
handlePointerMove: (e: PointerEvent) => void;
|
|
56
|
+
handlePointerDown: (e: PointerEvent) => void;
|
|
57
|
+
handlePointerUp: () => void;
|
|
58
|
+
getPositionFromValue: (thumbValue: number) => number;
|
|
59
|
+
thumbsPropsArr: {
|
|
60
|
+
readonly role: "slider";
|
|
61
|
+
readonly "aria-valuemin": number;
|
|
62
|
+
readonly "aria-valuemax": number;
|
|
63
|
+
readonly "aria-valuenow": number;
|
|
64
|
+
readonly "aria-disabled": "true" | "false";
|
|
65
|
+
readonly "aria-orientation": "horizontal" | "vertical";
|
|
66
|
+
readonly "data-value": number;
|
|
67
|
+
readonly tabindex: 0 | -1;
|
|
68
|
+
readonly style: import("../../shared/index.js").StyleProperties;
|
|
69
|
+
readonly "data-slider-thumb": "";
|
|
70
|
+
}[];
|
|
71
|
+
thumbsRenderArr: number[];
|
|
72
|
+
ticksPropsArr: {
|
|
73
|
+
readonly "data-disabled": "" | undefined;
|
|
74
|
+
readonly "data-orientation": "horizontal" | "vertical";
|
|
75
|
+
readonly "data-bounded": "" | undefined;
|
|
76
|
+
readonly "data-value": number;
|
|
77
|
+
readonly style: import("../../shared/index.js").StyleProperties;
|
|
78
|
+
readonly "data-slider-tick": "";
|
|
79
|
+
}[];
|
|
80
|
+
ticksRenderArr: number[];
|
|
81
|
+
snippetProps: {
|
|
82
|
+
readonly ticks: number[];
|
|
83
|
+
readonly thumbs: number[];
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
type SliderMultiRootStateProps = SliderBaseRootStateProps & ReadableBoxedValues<{
|
|
12
87
|
onValueCommit: OnChangeFn<number[]>;
|
|
13
88
|
}> & WritableBoxedValues<{
|
|
14
89
|
value: number[];
|
|
15
|
-
}
|
|
16
|
-
declare class
|
|
90
|
+
}>;
|
|
91
|
+
declare class SliderMultiRootState extends SliderBaseRootState {
|
|
17
92
|
#private;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
value: SliderRootStateProps["value"];
|
|
21
|
-
disabled: SliderRootStateProps["disabled"];
|
|
22
|
-
orientation: SliderRootStateProps["orientation"];
|
|
23
|
-
min: SliderRootStateProps["min"];
|
|
24
|
-
max: SliderRootStateProps["max"];
|
|
25
|
-
step: SliderRootStateProps["step"];
|
|
26
|
-
dir: SliderRootStateProps["dir"];
|
|
27
|
-
autoSort: SliderRootStateProps["autoSort"];
|
|
93
|
+
value: SliderMultiRootStateProps["value"];
|
|
94
|
+
isMulti: true;
|
|
28
95
|
activeThumb: {
|
|
29
96
|
node: HTMLElement;
|
|
30
97
|
idx: number;
|
|
31
98
|
} | null;
|
|
32
|
-
isActive: boolean;
|
|
33
99
|
currentThumbIdx: number;
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
constructor(props: SliderRootStateProps);
|
|
100
|
+
onValueCommit: SliderMultiRootStateProps["onValueCommit"];
|
|
101
|
+
constructor(props: SliderMultiRootStateProps);
|
|
37
102
|
applyPosition({ clientXY, activeThumbIdx, start, end, }: {
|
|
38
103
|
clientXY: number;
|
|
39
104
|
activeThumbIdx: number;
|
|
@@ -72,15 +137,6 @@ declare class SliderRootState {
|
|
|
72
137
|
readonly ticks: number[];
|
|
73
138
|
readonly thumbs: number[];
|
|
74
139
|
};
|
|
75
|
-
props: {
|
|
76
|
-
readonly id: string;
|
|
77
|
-
readonly "data-orientation": "horizontal" | "vertical";
|
|
78
|
-
readonly "data-disabled": "" | undefined;
|
|
79
|
-
readonly style: {
|
|
80
|
-
readonly touchAction: string | undefined;
|
|
81
|
-
};
|
|
82
|
-
readonly "data-slider-root": "";
|
|
83
|
-
};
|
|
84
140
|
}
|
|
85
141
|
type SliderRangeStateProps = WithRefProps;
|
|
86
142
|
declare class SliderRangeState {
|
|
@@ -1756,6 +1812,19 @@ declare class SliderThumbState {
|
|
|
1756
1812
|
constructor(props: SliderThumbStateProps, root: SliderRootState);
|
|
1757
1813
|
onkeydown(e: BitsKeyboardEvent): void;
|
|
1758
1814
|
props: {
|
|
1815
|
+
readonly id: string;
|
|
1816
|
+
readonly onkeydown: (e: BitsKeyboardEvent) => void;
|
|
1817
|
+
readonly role: "slider";
|
|
1818
|
+
readonly "aria-valuemin": number;
|
|
1819
|
+
readonly "aria-valuemax": number;
|
|
1820
|
+
readonly "aria-valuenow": number;
|
|
1821
|
+
readonly "aria-disabled": "true" | "false";
|
|
1822
|
+
readonly "aria-orientation": "horizontal" | "vertical";
|
|
1823
|
+
readonly "data-value": number;
|
|
1824
|
+
readonly tabindex: 0 | -1;
|
|
1825
|
+
readonly style: import("../../shared/index.js").StyleProperties;
|
|
1826
|
+
readonly "data-slider-thumb": "";
|
|
1827
|
+
} | {
|
|
1759
1828
|
readonly id: string;
|
|
1760
1829
|
readonly onkeydown: (e: BitsKeyboardEvent) => void;
|
|
1761
1830
|
readonly role: "slider";
|
|
@@ -1784,9 +1853,23 @@ declare class SliderTickState {
|
|
|
1784
1853
|
readonly "data-value": number;
|
|
1785
1854
|
readonly style: import("../../shared/index.js").StyleProperties;
|
|
1786
1855
|
readonly "data-slider-tick": "";
|
|
1856
|
+
} | {
|
|
1857
|
+
readonly id: string;
|
|
1858
|
+
readonly "data-disabled": "" | undefined;
|
|
1859
|
+
readonly "data-orientation": "horizontal" | "vertical";
|
|
1860
|
+
readonly "data-bounded": "" | undefined;
|
|
1861
|
+
readonly "data-value": number;
|
|
1862
|
+
readonly style: import("../../shared/index.js").StyleProperties;
|
|
1863
|
+
readonly "data-slider-tick": "";
|
|
1787
1864
|
};
|
|
1788
1865
|
}
|
|
1789
|
-
|
|
1866
|
+
type SliderRootState = SliderSingleRootState | SliderMultiRootState;
|
|
1867
|
+
type InitSliderRootStateProps = {
|
|
1868
|
+
type: "single" | "multiple";
|
|
1869
|
+
value: Box<number> | Box<number[]>;
|
|
1870
|
+
onValueCommit: ReadableBox<OnChangeFn<number>> | ReadableBox<OnChangeFn<number[]>>;
|
|
1871
|
+
} & Omit<SliderBaseRootStateProps, "type">;
|
|
1872
|
+
export declare function useSliderRoot(props: InitSliderRootStateProps): SliderRootState;
|
|
1790
1873
|
export declare function useSliderRange(props: SliderRangeStateProps): SliderRangeState;
|
|
1791
1874
|
export declare function useSliderThumb(props: SliderThumbStateProps): SliderThumbState;
|
|
1792
1875
|
export declare function useSliderTick(props: SliderTickStateProps): SliderTickState;
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
* Abdelrahman (https://github.com/abdel-17)
|
|
4
4
|
*/
|
|
5
5
|
import { untrack } from "svelte";
|
|
6
|
-
import { executeCallbacks, useRefById } from "svelte-toolbelt";
|
|
6
|
+
import { executeCallbacks, onMountEffect, useRefById, } from "svelte-toolbelt";
|
|
7
7
|
import { on } from "svelte/events";
|
|
8
|
-
import { Context } from "runed";
|
|
8
|
+
import { Context, watch } from "runed";
|
|
9
9
|
import { getRangeStyles, getThumbStyles, getTickStyles } from "./helpers.js";
|
|
10
10
|
import { getAriaDisabled, getAriaOrientation, getDataDisabled, getDataOrientation, } from "../../internal/attrs.js";
|
|
11
11
|
import { kbd } from "../../internal/kbd.js";
|
|
@@ -16,10 +16,9 @@ const SLIDER_ROOT_ATTR = "data-slider-root";
|
|
|
16
16
|
const SLIDER_THUMB_ATTR = "data-slider-thumb";
|
|
17
17
|
const SLIDER_RANGE_ATTR = "data-slider-range";
|
|
18
18
|
const SLIDER_TICK_ATTR = "data-slider-tick";
|
|
19
|
-
class
|
|
19
|
+
class SliderBaseRootState {
|
|
20
20
|
id;
|
|
21
21
|
ref;
|
|
22
|
-
value;
|
|
23
22
|
disabled;
|
|
24
23
|
orientation;
|
|
25
24
|
min;
|
|
@@ -27,9 +26,7 @@ class SliderRootState {
|
|
|
27
26
|
step;
|
|
28
27
|
dir;
|
|
29
28
|
autoSort;
|
|
30
|
-
activeThumb = $state(null);
|
|
31
29
|
isActive = $state(false);
|
|
32
|
-
currentThumbIdx = $state(0);
|
|
33
30
|
direction = $derived.by(() => {
|
|
34
31
|
if (this.orientation.current === "horizontal") {
|
|
35
32
|
return this.dir.current === "rtl" ? "rl" : "lr";
|
|
@@ -38,7 +35,6 @@ class SliderRootState {
|
|
|
38
35
|
return this.dir.current === "rtl" ? "tb" : "bt";
|
|
39
36
|
}
|
|
40
37
|
});
|
|
41
|
-
onValueCommit;
|
|
42
38
|
constructor(props) {
|
|
43
39
|
this.id = props.id;
|
|
44
40
|
this.ref = props.ref;
|
|
@@ -49,20 +45,234 @@ class SliderRootState {
|
|
|
49
45
|
this.step = props.step;
|
|
50
46
|
this.dir = props.dir;
|
|
51
47
|
this.autoSort = props.autoSort;
|
|
52
|
-
this.value = props.value;
|
|
53
|
-
this.onValueCommit = props.onValueCommit;
|
|
54
48
|
useRefById({
|
|
55
49
|
id: this.id,
|
|
56
50
|
ref: this.ref,
|
|
57
51
|
});
|
|
58
|
-
|
|
52
|
+
}
|
|
53
|
+
#touchAction = $derived.by(() => {
|
|
54
|
+
if (this.disabled.current)
|
|
55
|
+
return undefined;
|
|
56
|
+
return this.orientation.current === "horizontal" ? "pan-y" : "pan-x";
|
|
57
|
+
});
|
|
58
|
+
getAllThumbs = () => {
|
|
59
|
+
const node = this.ref.current;
|
|
60
|
+
if (!node)
|
|
61
|
+
return [];
|
|
62
|
+
return Array.from(node.querySelectorAll(`[${SLIDER_THUMB_ATTR}]`));
|
|
63
|
+
};
|
|
64
|
+
props = $derived.by(() => ({
|
|
65
|
+
id: this.id.current,
|
|
66
|
+
"data-orientation": getDataOrientation(this.orientation.current),
|
|
67
|
+
"data-disabled": getDataDisabled(this.disabled.current),
|
|
68
|
+
style: {
|
|
69
|
+
touchAction: this.#touchAction,
|
|
70
|
+
},
|
|
71
|
+
[SLIDER_ROOT_ATTR]: "",
|
|
72
|
+
}));
|
|
73
|
+
}
|
|
74
|
+
class SliderSingleRootState extends SliderBaseRootState {
|
|
75
|
+
value;
|
|
76
|
+
onValueCommit;
|
|
77
|
+
isMulti = false;
|
|
78
|
+
constructor(props) {
|
|
79
|
+
super(props);
|
|
80
|
+
this.value = props.value;
|
|
81
|
+
this.onValueCommit = props.onValueCommit;
|
|
82
|
+
onMountEffect(() => {
|
|
59
83
|
return executeCallbacks(on(document, "pointerdown", this.handlePointerDown), on(document, "pointerup", this.handlePointerUp), on(document, "pointermove", this.handlePointerMove), on(document, "pointerleave", this.handlePointerUp));
|
|
60
84
|
});
|
|
61
|
-
|
|
85
|
+
watch([
|
|
86
|
+
() => this.step.current,
|
|
87
|
+
() => this.min.current,
|
|
88
|
+
() => this.max.current,
|
|
89
|
+
() => this.value.current,
|
|
90
|
+
], ([step, min, max, value]) => {
|
|
91
|
+
const isValidValue = (v) => {
|
|
92
|
+
const snappedValue = snapValueToStep(v, min, max, step);
|
|
93
|
+
return snappedValue === v;
|
|
94
|
+
};
|
|
95
|
+
const gcv = (v) => {
|
|
96
|
+
return snapValueToStep(v, min, max, step);
|
|
97
|
+
};
|
|
98
|
+
if (!isValidValue(value)) {
|
|
99
|
+
this.value.current = gcv(value);
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
applyPosition({ clientXY, start, end }) {
|
|
104
|
+
const min = this.min.current;
|
|
105
|
+
const max = this.max.current;
|
|
106
|
+
const percent = (clientXY - start) / (end - start);
|
|
107
|
+
const val = percent * (max - min) + min;
|
|
108
|
+
if (val < min) {
|
|
109
|
+
this.updateValue(min);
|
|
110
|
+
}
|
|
111
|
+
else if (val > max) {
|
|
112
|
+
this.updateValue(max);
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
62
115
|
const step = this.step.current;
|
|
63
|
-
const
|
|
64
|
-
const
|
|
65
|
-
const
|
|
116
|
+
const currStep = Math.floor((val - min) / step);
|
|
117
|
+
const midpointOfCurrStep = min + currStep * step + step / 2;
|
|
118
|
+
const midpointOfNextStep = min + (currStep + 1) * step + step / 2;
|
|
119
|
+
const newValue = val >= midpointOfCurrStep && val < midpointOfNextStep
|
|
120
|
+
? (currStep + 1) * step + min
|
|
121
|
+
: currStep * step + min;
|
|
122
|
+
if (newValue <= max) {
|
|
123
|
+
this.updateValue(newValue);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
updateValue = (newValue) => {
|
|
128
|
+
this.value.current = snapValueToStep(newValue, this.min.current, this.max.current, this.step.current);
|
|
129
|
+
};
|
|
130
|
+
handlePointerMove = (e) => {
|
|
131
|
+
if (!this.isActive || this.disabled.current)
|
|
132
|
+
return;
|
|
133
|
+
e.preventDefault();
|
|
134
|
+
e.stopPropagation();
|
|
135
|
+
const sliderNode = this.ref.current;
|
|
136
|
+
const activeThumb = this.getAllThumbs()[0];
|
|
137
|
+
if (!sliderNode || !activeThumb)
|
|
138
|
+
return;
|
|
139
|
+
activeThumb.focus();
|
|
140
|
+
const { left, right, top, bottom } = sliderNode.getBoundingClientRect();
|
|
141
|
+
if (this.direction === "lr") {
|
|
142
|
+
this.applyPosition({
|
|
143
|
+
clientXY: e.clientX,
|
|
144
|
+
start: left,
|
|
145
|
+
end: right,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
else if (this.direction === "rl") {
|
|
149
|
+
this.applyPosition({
|
|
150
|
+
clientXY: e.clientX,
|
|
151
|
+
start: right,
|
|
152
|
+
end: left,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
else if (this.direction === "bt") {
|
|
156
|
+
this.applyPosition({
|
|
157
|
+
clientXY: e.clientY,
|
|
158
|
+
start: bottom,
|
|
159
|
+
end: top,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
else if (this.direction === "tb") {
|
|
163
|
+
this.applyPosition({
|
|
164
|
+
clientXY: e.clientY,
|
|
165
|
+
start: top,
|
|
166
|
+
end: bottom,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
handlePointerDown = (e) => {
|
|
171
|
+
if (e.button !== 0 || this.disabled.current)
|
|
172
|
+
return;
|
|
173
|
+
const sliderNode = this.ref.current;
|
|
174
|
+
const closestThumb = this.getAllThumbs()[0];
|
|
175
|
+
if (!closestThumb || !sliderNode)
|
|
176
|
+
return;
|
|
177
|
+
const target = e.target;
|
|
178
|
+
if (!isElementOrSVGElement(target) || !sliderNode.contains(target))
|
|
179
|
+
return;
|
|
180
|
+
e.preventDefault();
|
|
181
|
+
closestThumb.focus();
|
|
182
|
+
this.isActive = true;
|
|
183
|
+
this.handlePointerMove(e);
|
|
184
|
+
};
|
|
185
|
+
handlePointerUp = () => {
|
|
186
|
+
if (this.disabled.current)
|
|
187
|
+
return;
|
|
188
|
+
if (this.isActive) {
|
|
189
|
+
this.onValueCommit.current(untrack(() => this.value.current));
|
|
190
|
+
}
|
|
191
|
+
this.isActive = false;
|
|
192
|
+
};
|
|
193
|
+
getPositionFromValue = (thumbValue) => {
|
|
194
|
+
const min = this.min.current;
|
|
195
|
+
const max = this.max.current;
|
|
196
|
+
return ((thumbValue - min) / (max - min)) * 100;
|
|
197
|
+
};
|
|
198
|
+
thumbsPropsArr = $derived.by(() => {
|
|
199
|
+
const currValue = this.value.current;
|
|
200
|
+
return Array.from({ length: 1 }, () => {
|
|
201
|
+
const thumbValue = currValue;
|
|
202
|
+
const thumbPosition = this.getPositionFromValue(thumbValue ?? 0);
|
|
203
|
+
const style = getThumbStyles(this.direction, thumbPosition);
|
|
204
|
+
return {
|
|
205
|
+
role: "slider",
|
|
206
|
+
"aria-valuemin": this.min.current,
|
|
207
|
+
"aria-valuemax": this.max.current,
|
|
208
|
+
"aria-valuenow": thumbValue,
|
|
209
|
+
"aria-disabled": getAriaDisabled(this.disabled.current),
|
|
210
|
+
"aria-orientation": getAriaOrientation(this.orientation.current),
|
|
211
|
+
"data-value": thumbValue,
|
|
212
|
+
tabindex: this.disabled.current ? -1 : 0,
|
|
213
|
+
style,
|
|
214
|
+
[SLIDER_THUMB_ATTR]: "",
|
|
215
|
+
};
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
thumbsRenderArr = $derived.by(() => {
|
|
219
|
+
return this.thumbsPropsArr.map((_, i) => i);
|
|
220
|
+
});
|
|
221
|
+
ticksPropsArr = $derived.by(() => {
|
|
222
|
+
const max = this.max.current;
|
|
223
|
+
const min = this.min.current;
|
|
224
|
+
const step = this.step.current;
|
|
225
|
+
const difference = max - min;
|
|
226
|
+
let count = Math.ceil(difference / step);
|
|
227
|
+
if (difference % step == 0) {
|
|
228
|
+
count++;
|
|
229
|
+
}
|
|
230
|
+
const currValue = this.value.current;
|
|
231
|
+
return Array.from({ length: count }, (_, i) => {
|
|
232
|
+
const tickPosition = i * (step / difference) * 100;
|
|
233
|
+
const isFirst = i === 0;
|
|
234
|
+
const isLast = i === count - 1;
|
|
235
|
+
const offsetPercentage = isFirst ? 0 : isLast ? -100 : -50;
|
|
236
|
+
const style = getTickStyles(this.direction, tickPosition, offsetPercentage);
|
|
237
|
+
const tickValue = min + i * step;
|
|
238
|
+
const bounded = tickValue <= currValue;
|
|
239
|
+
return {
|
|
240
|
+
"data-disabled": getDataDisabled(this.disabled.current),
|
|
241
|
+
"data-orientation": getDataOrientation(this.orientation.current),
|
|
242
|
+
"data-bounded": bounded ? "" : undefined,
|
|
243
|
+
"data-value": tickValue,
|
|
244
|
+
style,
|
|
245
|
+
[SLIDER_TICK_ATTR]: "",
|
|
246
|
+
};
|
|
247
|
+
});
|
|
248
|
+
});
|
|
249
|
+
ticksRenderArr = $derived.by(() => {
|
|
250
|
+
return this.ticksPropsArr.map((_, i) => i);
|
|
251
|
+
});
|
|
252
|
+
snippetProps = $derived.by(() => ({
|
|
253
|
+
ticks: this.ticksRenderArr,
|
|
254
|
+
thumbs: this.thumbsRenderArr,
|
|
255
|
+
}));
|
|
256
|
+
}
|
|
257
|
+
class SliderMultiRootState extends SliderBaseRootState {
|
|
258
|
+
value;
|
|
259
|
+
isMulti = true;
|
|
260
|
+
activeThumb = $state(null);
|
|
261
|
+
currentThumbIdx = $state(0);
|
|
262
|
+
onValueCommit;
|
|
263
|
+
constructor(props) {
|
|
264
|
+
super(props);
|
|
265
|
+
this.value = props.value;
|
|
266
|
+
this.onValueCommit = props.onValueCommit;
|
|
267
|
+
onMountEffect(() => {
|
|
268
|
+
return executeCallbacks(on(document, "pointerdown", this.handlePointerDown), on(document, "pointerup", this.handlePointerUp), on(document, "pointermove", this.handlePointerMove), on(document, "pointerleave", this.handlePointerUp));
|
|
269
|
+
});
|
|
270
|
+
watch([
|
|
271
|
+
() => this.step.current,
|
|
272
|
+
() => this.min.current,
|
|
273
|
+
() => this.max.current,
|
|
274
|
+
() => this.value.current,
|
|
275
|
+
], ([step, min, max, value]) => {
|
|
66
276
|
const isValidValue = (v) => {
|
|
67
277
|
const snappedValue = snapValueToStep(v, min, max, step);
|
|
68
278
|
return snappedValue === v;
|
|
@@ -306,20 +516,6 @@ class SliderRootState {
|
|
|
306
516
|
ticks: this.ticksRenderArr,
|
|
307
517
|
thumbs: this.thumbsRenderArr,
|
|
308
518
|
}));
|
|
309
|
-
#touchAction = $derived.by(() => {
|
|
310
|
-
if (this.disabled.current)
|
|
311
|
-
return undefined;
|
|
312
|
-
return this.orientation.current === "horizontal" ? "pan-y" : "pan-x";
|
|
313
|
-
});
|
|
314
|
-
props = $derived.by(() => ({
|
|
315
|
-
id: this.id.current,
|
|
316
|
-
"data-orientation": getDataOrientation(this.orientation.current),
|
|
317
|
-
"data-disabled": getDataDisabled(this.disabled.current),
|
|
318
|
-
style: {
|
|
319
|
-
touchAction: this.#touchAction,
|
|
320
|
-
},
|
|
321
|
-
[SLIDER_ROOT_ATTR]: "",
|
|
322
|
-
}));
|
|
323
519
|
}
|
|
324
520
|
const VALID_SLIDER_KEYS = [
|
|
325
521
|
kbd.ARROW_LEFT,
|
|
@@ -343,9 +539,14 @@ class SliderRangeState {
|
|
|
343
539
|
});
|
|
344
540
|
}
|
|
345
541
|
rangeStyles = $derived.by(() => {
|
|
346
|
-
const
|
|
347
|
-
|
|
348
|
-
|
|
542
|
+
const min = Array.isArray(this.#root.value.current)
|
|
543
|
+
? this.#root.value.current.length > 1
|
|
544
|
+
? this.#root.getPositionFromValue(Math.min(...this.#root.value.current) ?? 0)
|
|
545
|
+
: 0
|
|
546
|
+
: 0;
|
|
547
|
+
const max = Array.isArray(this.#root.value.current)
|
|
548
|
+
? 100 - this.#root.getPositionFromValue(Math.max(...this.#root.value.current) ?? 0)
|
|
549
|
+
: 100 - this.#root.getPositionFromValue(this.#root.value.current);
|
|
349
550
|
return {
|
|
350
551
|
position: "absolute",
|
|
351
552
|
...getRangeStyles(this.#root.direction, min, max),
|
|
@@ -377,7 +578,12 @@ class SliderThumbState {
|
|
|
377
578
|
this.onkeydown = this.onkeydown.bind(this);
|
|
378
579
|
}
|
|
379
580
|
#updateValue(newValue) {
|
|
380
|
-
this.#root.
|
|
581
|
+
if (this.#root.isMulti) {
|
|
582
|
+
this.#root.updateValue(newValue, this.#index.current);
|
|
583
|
+
}
|
|
584
|
+
else {
|
|
585
|
+
this.#root.updateValue(newValue);
|
|
586
|
+
}
|
|
381
587
|
}
|
|
382
588
|
onkeydown(e) {
|
|
383
589
|
if (this.#isDisabled)
|
|
@@ -389,14 +595,16 @@ class SliderThumbState {
|
|
|
389
595
|
if (!thumbs.length)
|
|
390
596
|
return;
|
|
391
597
|
const idx = thumbs.indexOf(currNode);
|
|
392
|
-
this.#root.
|
|
598
|
+
if (this.#root.isMulti) {
|
|
599
|
+
this.#root.currentThumbIdx = idx;
|
|
600
|
+
}
|
|
393
601
|
if (!VALID_SLIDER_KEYS.includes(e.key))
|
|
394
602
|
return;
|
|
395
603
|
e.preventDefault();
|
|
396
604
|
const min = this.#root.min.current;
|
|
397
605
|
const max = this.#root.max.current;
|
|
398
606
|
const value = this.#root.value.current;
|
|
399
|
-
const thumbValue = value[idx];
|
|
607
|
+
const thumbValue = Array.isArray(value) ? value[idx] : value;
|
|
400
608
|
const orientation = this.#root.orientation.current;
|
|
401
609
|
const direction = this.#root.direction;
|
|
402
610
|
const step = this.#root.step.current;
|
|
@@ -460,6 +668,7 @@ class SliderThumbState {
|
|
|
460
668
|
}
|
|
461
669
|
break;
|
|
462
670
|
}
|
|
671
|
+
// @ts-expect-error - this is fine
|
|
463
672
|
this.#root.onValueCommit.current(this.#root.value.current);
|
|
464
673
|
}
|
|
465
674
|
props = $derived.by(() => ({
|
|
@@ -490,7 +699,11 @@ class SliderTickState {
|
|
|
490
699
|
}
|
|
491
700
|
const SliderRootContext = new Context("Slider.Root");
|
|
492
701
|
export function useSliderRoot(props) {
|
|
493
|
-
|
|
702
|
+
const { type, ...rest } = props;
|
|
703
|
+
const rootState = type === "single"
|
|
704
|
+
? new SliderSingleRootState(rest)
|
|
705
|
+
: new SliderMultiRootState(rest);
|
|
706
|
+
return SliderRootContext.set(rootState);
|
|
494
707
|
}
|
|
495
708
|
export function useSliderRange(props) {
|
|
496
709
|
return new SliderRangeState(props, SliderRootContext.get());
|
|
@@ -5,22 +5,7 @@ export type SliderRootSnippetProps = {
|
|
|
5
5
|
ticks: number[];
|
|
6
6
|
thumbs: number[];
|
|
7
7
|
};
|
|
8
|
-
export type
|
|
9
|
-
/**
|
|
10
|
-
* The value of the slider.
|
|
11
|
-
* @bindable
|
|
12
|
-
*/
|
|
13
|
-
value?: number[];
|
|
14
|
-
/**
|
|
15
|
-
* A callback function called when the value changes.
|
|
16
|
-
*/
|
|
17
|
-
onValueChange?: OnChangeFn<number[]>;
|
|
18
|
-
/**
|
|
19
|
-
* A callback function called when the user stops dragging the thumb,
|
|
20
|
-
* which is useful for knowing when the user has finished interacting with the
|
|
21
|
-
* slider and _commits_ the value.
|
|
22
|
-
*/
|
|
23
|
-
onValueCommit?: OnChangeFn<number[]>;
|
|
8
|
+
export type BaseSliderRootPropsWithoutHTML = {
|
|
24
9
|
/**
|
|
25
10
|
* Whether to automatically sort the values in the array when moving thumbs past
|
|
26
11
|
* one another.
|
|
@@ -43,7 +28,7 @@ export type SliderRootPropsWithoutHTML = WithChild<{
|
|
|
43
28
|
/**
|
|
44
29
|
* The amount to increment the value by when the user presses the arrow keys.
|
|
45
30
|
*
|
|
46
|
-
* @
|
|
31
|
+
* @defaultValue 1
|
|
47
32
|
*/
|
|
48
33
|
step?: number;
|
|
49
34
|
/**
|
|
@@ -77,7 +62,56 @@ export type SliderRootPropsWithoutHTML = WithChild<{
|
|
|
77
62
|
* @defaultValue false
|
|
78
63
|
*/
|
|
79
64
|
controlledValue?: boolean;
|
|
80
|
-
}
|
|
65
|
+
};
|
|
66
|
+
export type SliderSingleRootPropsWithoutHTML = BaseSliderRootPropsWithoutHTML & {
|
|
67
|
+
/**
|
|
68
|
+
* The type of slider. If set to `'multiple'`, the slider will
|
|
69
|
+
* allow multiple ticks and the `value` will be an array of numbers.
|
|
70
|
+
*
|
|
71
|
+
* @required
|
|
72
|
+
*/
|
|
73
|
+
type: "single";
|
|
74
|
+
/**
|
|
75
|
+
* The value of the slider.
|
|
76
|
+
* @bindable
|
|
77
|
+
*/
|
|
78
|
+
value?: number;
|
|
79
|
+
/**
|
|
80
|
+
* A callback function called when the value changes.
|
|
81
|
+
*/
|
|
82
|
+
onValueChange?: OnChangeFn<number>;
|
|
83
|
+
/**
|
|
84
|
+
* A callback function called when the user stops dragging the
|
|
85
|
+
* thumb and the value is committed.
|
|
86
|
+
*/
|
|
87
|
+
onValueCommit?: OnChangeFn<number>;
|
|
88
|
+
};
|
|
89
|
+
export type SliderMultiRootPropsWithoutHTML = BaseSliderRootPropsWithoutHTML & {
|
|
90
|
+
/**
|
|
91
|
+
* The type of slider. If set to `'multiple'`, the slider will
|
|
92
|
+
* allow multiple ticks and the `value` will be an array of numbers.
|
|
93
|
+
*
|
|
94
|
+
* @required
|
|
95
|
+
*/
|
|
96
|
+
type: "multiple";
|
|
97
|
+
/**
|
|
98
|
+
* The value of the slider.
|
|
99
|
+
* @bindable
|
|
100
|
+
*/
|
|
101
|
+
value?: number[];
|
|
102
|
+
/**
|
|
103
|
+
* A callback function called when the value changes.
|
|
104
|
+
*/
|
|
105
|
+
onValueChange?: OnChangeFn<number[]>;
|
|
106
|
+
/**
|
|
107
|
+
* A callback function called when the user stops dragging the
|
|
108
|
+
* thumb and the value is committed.
|
|
109
|
+
*/
|
|
110
|
+
onValueCommit?: OnChangeFn<number[]>;
|
|
111
|
+
};
|
|
112
|
+
export type SliderRootPropsWithoutHTML = WithChild<SliderSingleRootPropsWithoutHTML, SliderRootSnippetProps> | WithChild<SliderMultiRootPropsWithoutHTML, SliderRootSnippetProps>;
|
|
113
|
+
export type SliderSingleRootProps = SliderSingleRootPropsWithoutHTML & Without<BitsPrimitiveSpanAttributes, WithChild<SliderSingleRootPropsWithoutHTML, SliderRootSnippetProps>>;
|
|
114
|
+
export type SliderMultipleRootProps = SliderMultiRootPropsWithoutHTML & Without<BitsPrimitiveSpanAttributes, WithChild<SliderMultiRootPropsWithoutHTML, SliderRootSnippetProps>>;
|
|
81
115
|
export type SliderRootProps = SliderRootPropsWithoutHTML & Without<BitsPrimitiveSpanAttributes, SliderRootPropsWithoutHTML>;
|
|
82
116
|
export type SliderRangePropsWithoutHTML = WithChild;
|
|
83
117
|
export type SliderRangeProps = SliderRangePropsWithoutHTML & Without<BitsPrimitiveSpanAttributes, SliderRangePropsWithoutHTML>;
|