@varialkit/slider 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/docs.md ADDED
@@ -0,0 +1,132 @@
1
+ # Slider
2
+
3
+ Slider lets users pick a value from a continuous range with optional tooltip feedback.
4
+
5
+ ## Setup
6
+
7
+ ```tsx
8
+ import { Slider } from "@solara/slider";
9
+
10
+ export function VolumeSetting() {
11
+ const [value, setValue] = React.useState(35);
12
+
13
+ return (
14
+ <Slider
15
+ min={0}
16
+ max={100}
17
+ value={value}
18
+ onChange={(event) => setValue(Number(event.target.value))}
19
+ showTooltip
20
+ />
21
+ );
22
+ }
23
+ ```
24
+
25
+ ## Range Selection
26
+
27
+ Pass a tuple to enable range mode (or set `range` explicitly). The component will clamp values so the lower value never exceeds the upper value.
28
+
29
+ ```tsx
30
+ import { Slider } from "@solara/slider";
31
+
32
+ export function PriceRange() {
33
+ const [value, setValue] = React.useState<[number, number]>([20, 80]);
34
+
35
+ return (
36
+ <Slider
37
+ min={0}
38
+ max={100}
39
+ value={value}
40
+ onValueChange={(next) => Array.isArray(next) && setValue(next)}
41
+ showTooltip
42
+ />
43
+ );
44
+ }
45
+ ```
46
+
47
+ ## Vertical Orientation
48
+
49
+ Set `orientation="vertical"` and give the container a height.
50
+
51
+ ```tsx
52
+ import { Slider } from "@solara/slider";
53
+
54
+ export function TemperatureSetting() {
55
+ const [value, setValue] = React.useState(60);
56
+
57
+ return (
58
+ <div style={{ height: 200 }}>
59
+ <Slider
60
+ min={0}
61
+ max={100}
62
+ value={value}
63
+ onValueChange={(next) => typeof next === "number" && setValue(next)}
64
+ orientation="vertical"
65
+ showTooltip
66
+ />
67
+ </div>
68
+ );
69
+ }
70
+ ```
71
+
72
+ ## Visual Types
73
+
74
+ Slider supports two visual styles via the `type` prop:
75
+
76
+ - `knob` (default): circular thumb with a slimmer track.
77
+ - `thumb`: taller track with a slim, inset thumb bar that stays aligned with the fill. The thumb is intentionally inset from the fill edge and scaled to leave top/bottom breathing room.
78
+
79
+ These types only change styling. All slider logic (range handling, orientation, keyboard controls, tooltips) stays the same.
80
+
81
+ ```tsx
82
+ import { Slider } from "@solara/slider";
83
+
84
+ export function TypesExample() {
85
+ return (
86
+ <div style={{ display: "grid", gap: "1rem" }}>
87
+ <Slider defaultValue={40} showTooltip />
88
+ <Slider defaultValue={40} type="thumb" showTooltip />
89
+ </div>
90
+ );
91
+ }
92
+ ```
93
+
94
+ ## Props
95
+
96
+ - `value`: Controlled value. Use a number for single mode or `[min, max]` for range mode.
97
+ - `defaultValue`: Uncontrolled initial value. Use a number for single mode or `[min, max]` for range mode.
98
+ - `range`: Force range mode even if a tuple value is not provided.
99
+ - `orientation`: `horizontal` (default) or `vertical`.
100
+ - `type`: `knob` (default) or `thumb` for the visual style.
101
+ - `onChange`: Fires with a change event (kept for compatibility with the single slider).
102
+ - `onValueChange`: Fires with a number (single) or tuple (range).
103
+ - `showTooltip`: Show value tooltips on hover, focus, or drag.
104
+ - `size`: `small`, `medium`, `large`, `xlarge` (aliases: `sm`, `md`, `lg`, `xl`). Thumb sizes scale with track height, while the thumb width stays slim and consistent across sizes.
105
+ - `min`, `max`, `step`, `disabled`, plus standard input props (except `value`/`defaultValue` overrides above).
106
+
107
+ ## Usage
108
+
109
+ - Use `min`, `max`, and `step` to control the range.
110
+ - Provide `showTooltip` to reveal the current value on hover, focus, or drag.
111
+ - Sizes map to `small`, `medium`, `large`, and `xlarge` (aliases: `sm`, `md`, `lg`, `xl`).
112
+ - Range selection is enabled by passing `[min, max]` to `value`/`defaultValue` or by setting `range`.
113
+ - Use `onValueChange` to receive either a number (single) or tuple (range).
114
+ - Set `orientation="vertical"` and provide a height on the container to render vertically.
115
+ - Use `type="thumb"` for the taller track with the inset thumb bar.
116
+ - For `type="thumb"`, the thumb aligns to the fill end using the track's usable width (accounting for padding), so it remains visually consistent at high values.
117
+
118
+ ## Behavior Notes
119
+
120
+ - In range mode, clicking the track moves the closest thumb.
121
+ - During drag in range mode, only the active thumb shows its tooltip. On hover, both thumbs can show tooltips.
122
+ - Range values are clamped to `min`/`max`, stepped, and kept ordered so the lower value does not exceed the upper value.
123
+ - Vertical sliders increase from bottom (min) to top (max).
124
+
125
+ ## Tokens Used
126
+
127
+ The slider styles rely on `@solara/styles` tokens for spacing, typography, colors, elevation, and radius:
128
+
129
+ - `--space-*`, `--spacing-multiplier`
130
+ - `--font-body`, `--font-size-caption-scaled`, `--line-height-caption-scaled`
131
+ - `--color-surface-*`, `--color-text-*`, `--color-divider-*`, `--color-accent-primary`
132
+ - `--elevation-*`
package/examples.tsx ADDED
@@ -0,0 +1,309 @@
1
+ import React from "react";
2
+ import { Slider } from "./src/Slider";
3
+ import type { SliderProps } from "./src/Slider.types";
4
+
5
+ const SliderPlayground: React.FC<SliderProps> = (props) => {
6
+ const [value, setValue] = React.useState<number>(() => {
7
+ if (props.value !== undefined) return Number(props.value);
8
+ if (props.defaultValue !== undefined) return Number(props.defaultValue);
9
+ if (props.min !== undefined) return Number(props.min);
10
+ return 0;
11
+ });
12
+
13
+ React.useEffect(() => {
14
+ if (props.value === undefined) return;
15
+ setValue(Number(props.value));
16
+ }, [props.value]);
17
+
18
+ return (
19
+ <Slider
20
+ {...props}
21
+ value={value}
22
+ onChange={(event) => {
23
+ setValue(Number(event.target.value));
24
+ props.onChange?.(event);
25
+ }}
26
+ />
27
+ );
28
+ };
29
+
30
+ export const stories = {
31
+ playground: {
32
+ title: "Playground",
33
+ description: "Adjust the props to explore the Slider behavior.",
34
+ render: (props: SliderProps) => <SliderPlayground {...props} />,
35
+ controls: [
36
+ { name: "value", type: "number", min: 0, max: 100, step: 1 },
37
+ { name: "min", type: "number", min: 0, max: 100, step: 1 },
38
+ { name: "max", type: "number", min: 1, max: 100, step: 1 },
39
+ { name: "step", type: "number", min: 1, max: 20, step: 1 },
40
+ { name: "size", type: "select", options: ["small", "medium", "large", "xlarge"] },
41
+ { name: "type", type: "select", options: ["knob", "thumb"] },
42
+ { name: "showTooltip", type: "boolean", label: "Show Tooltip" },
43
+ { name: "disabled", type: "boolean", label: "Disabled" },
44
+ ],
45
+ initialProps: {
46
+ value: 35,
47
+ min: 0,
48
+ max: 100,
49
+ step: 5,
50
+ size: "medium",
51
+ type: "knob",
52
+ showTooltip: true,
53
+ disabled: false,
54
+ },
55
+ },
56
+ types: {
57
+ title: "Types",
58
+ description: "Knob (default) and thumb slider variants.",
59
+ showProps: false,
60
+ render: () => (
61
+ <div style={{ display: "grid", gap: "1.5rem" }}>
62
+ <div>
63
+ <p style={{ margin: "0 0 0.5rem", color: "var(--color-text-secondary)" }}>Knob</p>
64
+ <Slider defaultValue={40} showTooltip />
65
+ </div>
66
+ <div>
67
+ <p style={{ margin: "0 0 0.5rem", color: "var(--color-text-secondary)" }}>Thumb</p>
68
+ <Slider defaultValue={40} type="thumb" showTooltip />
69
+ </div>
70
+ </div>
71
+ ),
72
+ code: `import { Slider } from "@solara/slider";
73
+
74
+ export function Example() {
75
+ return (
76
+ <div style={{ display: "grid", gap: "1.5rem" }}>
77
+ <div>
78
+ <p style={{ margin: "0 0 0.5rem", color: "var(--color-text-secondary)" }}>Knob</p>
79
+ <Slider defaultValue={40} showTooltip />
80
+ </div>
81
+ <div>
82
+ <p style={{ margin: "0 0 0.5rem", color: "var(--color-text-secondary)" }}>Thumb</p>
83
+ <Slider defaultValue={40} type="thumb" showTooltip />
84
+ </div>
85
+ </div>
86
+ );
87
+ }
88
+ `,
89
+ },
90
+ sizes: {
91
+ title: "Sizes",
92
+ description: "Slider supports small, medium, large, and xlarge sizes for both knob and thumb types.",
93
+ showProps: false,
94
+ render: () => (
95
+ <div style={{ display: "grid", gap: "1.5rem" }}>
96
+ <div>
97
+ <p style={{ margin: "0 0 0.5rem", color: "var(--color-text-secondary)" }}>Small</p>
98
+ <div style={{ display: "grid", gap: "0.75rem" }}>
99
+ <Slider defaultValue={25} size="small" showTooltip />
100
+ <Slider defaultValue={25} size="small" type="thumb" showTooltip />
101
+ </div>
102
+ </div>
103
+ <div>
104
+ <p style={{ margin: "0 0 0.5rem", color: "var(--color-text-secondary)" }}>Medium</p>
105
+ <div style={{ display: "grid", gap: "0.75rem" }}>
106
+ <Slider defaultValue={50} size="medium" showTooltip />
107
+ <Slider defaultValue={50} size="medium" type="thumb" showTooltip />
108
+ </div>
109
+ </div>
110
+ <div>
111
+ <p style={{ margin: "0 0 0.5rem", color: "var(--color-text-secondary)" }}>Large</p>
112
+ <div style={{ display: "grid", gap: "0.75rem" }}>
113
+ <Slider defaultValue={75} size="large" showTooltip />
114
+ <Slider defaultValue={75} size="large" type="thumb" showTooltip />
115
+ </div>
116
+ </div>
117
+ <div>
118
+ <p style={{ margin: "0 0 0.5rem", color: "var(--color-text-secondary)" }}>X-Large</p>
119
+ <div style={{ display: "grid", gap: "0.75rem" }}>
120
+ <Slider defaultValue={85} size="xlarge" showTooltip />
121
+ <Slider defaultValue={85} size="xlarge" type="thumb" showTooltip />
122
+ </div>
123
+ </div>
124
+ </div>
125
+ ),
126
+ code: `import { Slider } from "@solara/slider";
127
+
128
+ export function Example() {
129
+ return (
130
+ <div style={{ display: "grid", gap: "1.5rem" }}>
131
+ <div>
132
+ <p style={{ margin: "0 0 0.5rem", color: "var(--color-text-secondary)" }}>Small</p>
133
+ <div style={{ display: "grid", gap: "0.75rem" }}>
134
+ <Slider defaultValue={25} size="small" showTooltip />
135
+ <Slider defaultValue={25} size="small" type="thumb" showTooltip />
136
+ </div>
137
+ </div>
138
+ <div>
139
+ <p style={{ margin: "0 0 0.5rem", color: "var(--color-text-secondary)" }}>Medium</p>
140
+ <div style={{ display: "grid", gap: "0.75rem" }}>
141
+ <Slider defaultValue={50} size="medium" showTooltip />
142
+ <Slider defaultValue={50} size="medium" type="thumb" showTooltip />
143
+ </div>
144
+ </div>
145
+ <div>
146
+ <p style={{ margin: "0 0 0.5rem", color: "var(--color-text-secondary)" }}>Large</p>
147
+ <div style={{ display: "grid", gap: "0.75rem" }}>
148
+ <Slider defaultValue={75} size="large" showTooltip />
149
+ <Slider defaultValue={75} size="large" type="thumb" showTooltip />
150
+ </div>
151
+ </div>
152
+ <div>
153
+ <p style={{ margin: "0 0 0.5rem", color: "var(--color-text-secondary)" }}>X-Large</p>
154
+ <div style={{ display: "grid", gap: "0.75rem" }}>
155
+ <Slider defaultValue={85} size="xlarge" showTooltip />
156
+ <Slider defaultValue={85} size="xlarge" type="thumb" showTooltip />
157
+ </div>
158
+ </div>
159
+ </div>
160
+ );
161
+ }
162
+ `,
163
+ },
164
+ states: {
165
+ title: "States",
166
+ description: "Disabled and standard states for the slider.",
167
+ showProps: false,
168
+ render: () => (
169
+ <div style={{ display: "grid", gap: "1.5rem" }}>
170
+ <div>
171
+ <p style={{ margin: "0 0 0.5rem", color: "var(--color-text-secondary)" }}>Default</p>
172
+ <Slider defaultValue={45} showTooltip />
173
+ </div>
174
+ <div>
175
+ <p style={{ margin: "0 0 0.5rem", color: "var(--color-text-secondary)" }}>Disabled</p>
176
+ <Slider defaultValue={60} disabled showTooltip />
177
+ </div>
178
+ </div>
179
+ ),
180
+ code: `import { Slider } from "@solara/slider";
181
+
182
+ export function Example() {
183
+ return (
184
+ <div style={{ display: "grid", gap: "1.5rem" }}>
185
+ <div>
186
+ <p style={{ margin: "0 0 0.5rem", color: "var(--color-text-secondary)" }}>Default</p>
187
+ <Slider defaultValue={45} showTooltip />
188
+ </div>
189
+ <div>
190
+ <p style={{ margin: "0 0 0.5rem", color: "var(--color-text-secondary)" }}>Disabled</p>
191
+ <Slider defaultValue={60} disabled showTooltip />
192
+ </div>
193
+ </div>
194
+ );
195
+ }
196
+ `,
197
+ },
198
+ orientation: {
199
+ title: "Orientation",
200
+ description: "Horizontal and vertical slider layouts.",
201
+ showProps: false,
202
+ render: () => {
203
+ const [rangeValue, setRangeValue] = React.useState<[number, number]>([20, 80]);
204
+ return (
205
+ <div style={{ display: "grid", gap: "1.5rem" }}>
206
+ <div>
207
+ <p style={{ margin: "0 0 0.5rem", color: "var(--color-text-secondary)" }}>Horizontal</p>
208
+ <Slider defaultValue={40} showTooltip />
209
+ </div>
210
+ <div>
211
+ <p style={{ margin: "0 0 0.5rem", color: "var(--color-text-secondary)" }}>Vertical</p>
212
+ <div style={{ display: "flex", gap: "2rem" }}>
213
+ <div style={{ height: 180 }}>
214
+ <Slider defaultValue={60} orientation="vertical" showTooltip />
215
+ </div>
216
+ <div style={{ height: 180 }}>
217
+ <Slider
218
+ min={0}
219
+ max={100}
220
+ value={rangeValue}
221
+ onValueChange={(next) => Array.isArray(next) && setRangeValue(next)}
222
+ orientation="vertical"
223
+ showTooltip
224
+ />
225
+ </div>
226
+ </div>
227
+ <div style={{ marginTop: "0.5rem", color: "var(--color-text-secondary)" }}>
228
+ Range: {rangeValue[0]} - {rangeValue[1]}
229
+ </div>
230
+ </div>
231
+ </div>
232
+ );
233
+ },
234
+ code: `import { Slider } from "@solara/slider";
235
+
236
+ export function Example() {
237
+ const [rangeValue, setRangeValue] = React.useState([20, 80]);
238
+
239
+ return (
240
+ <div style={{ display: "grid", gap: "1.5rem" }}>
241
+ <div>
242
+ <p style={{ margin: "0 0 0.5rem", color: "var(--color-text-secondary)" }}>Horizontal</p>
243
+ <Slider defaultValue={40} showTooltip />
244
+ </div>
245
+ <div>
246
+ <p style={{ margin: "0 0 0.5rem", color: "var(--color-text-secondary)" }}>Vertical</p>
247
+ <div style={{ display: "flex", gap: "2rem" }}>
248
+ <div style={{ height: 180 }}>
249
+ <Slider defaultValue={60} orientation="vertical" showTooltip />
250
+ </div>
251
+ <div style={{ height: 180 }}>
252
+ <Slider
253
+ min={0}
254
+ max={100}
255
+ value={rangeValue}
256
+ onValueChange={(next) => Array.isArray(next) && setRangeValue(next)}
257
+ orientation="vertical"
258
+ showTooltip
259
+ />
260
+ </div>
261
+ </div>
262
+ <div style={{ marginTop: "0.5rem", color: "var(--color-text-secondary)" }}>
263
+ Range: {rangeValue[0]} - {rangeValue[1]}
264
+ </div>
265
+ </div>
266
+ </div>
267
+ );
268
+ }
269
+ `,
270
+ },
271
+ range: {
272
+ title: "Range",
273
+ description: "Select a lower and upper bound with two thumbs.",
274
+ showProps: false,
275
+ render: () => {
276
+ const [value, setValue] = React.useState<[number, number]>([20, 80]);
277
+ return (
278
+ <div style={{ display: "grid", gap: "1rem" }}>
279
+ {/* Range mode is enabled by passing a tuple value. */}
280
+ <Slider min={0} max={100} value={value} onValueChange={(next) => Array.isArray(next) && setValue(next)} />
281
+ <div style={{ color: "var(--color-text-secondary)" }}>
282
+ Range: {value[0]} - {value[1]}
283
+ </div>
284
+ </div>
285
+ );
286
+ },
287
+ code: `import { Slider } from "@solara/slider";
288
+
289
+ export function Example() {
290
+ const [value, setValue] = React.useState([20, 80]);
291
+
292
+ return (
293
+ <div style={{ display: "grid", gap: "1rem" }}>
294
+ {/* Range mode is enabled by passing a tuple value. */}
295
+ <Slider
296
+ min={0}
297
+ max={100}
298
+ value={value}
299
+ onValueChange={(next) => Array.isArray(next) && setValue(next)}
300
+ />
301
+ <div style={{ color: "var(--color-text-secondary)" }}>
302
+ Range: {value[0]} - {value[1]}
303
+ </div>
304
+ </div>
305
+ );
306
+ }
307
+ `,
308
+ },
309
+ };
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "@varialkit/slider",
3
+ "version": "0.1.1",
4
+ "type": "module",
5
+ "main": "src/index.ts",
6
+ "types": "src/index.ts",
7
+ "exports": {
8
+ ".": "./src/index.ts",
9
+ "./examples": "./examples.tsx"
10
+ },
11
+ "files": [
12
+ "src",
13
+ "docs.md",
14
+ "examples.tsx"
15
+ ],
16
+ "peerDependencies": {
17
+ "react": "^19.0.0",
18
+ "react-dom": "^19.0.0",
19
+ "@varialkit/tooltip": "0.1.1"
20
+ },
21
+ "devDependencies": {
22
+ "@types/react": "19.0.10",
23
+ "react": "19.0.0",
24
+ "react-dom": "19.0.0",
25
+ "@types/react-dom": "19.0.4"
26
+ }
27
+ }