@vuer-ai/vuer-uikit 0.0.12 → 0.0.13

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.
Files changed (45) hide show
  1. package/package.json +3 -2
  2. package/src/highlight-cursor/cursor-context.tsx +23 -0
  3. package/src/highlight-cursor/cursor-provider.tsx +264 -0
  4. package/src/highlight-cursor/enhanced-components.tsx +16 -0
  5. package/src/highlight-cursor/index.ts +21 -0
  6. package/src/highlight-cursor/tabs-cursor-context.tsx +121 -0
  7. package/src/highlight-cursor/types.ts +40 -0
  8. package/src/highlight-cursor/with-cursor.tsx +144 -0
  9. package/src/index.css +5 -0
  10. package/src/index.ts +5 -0
  11. package/src/styles/theme.css +80 -0
  12. package/src/styles/toast.css +64 -0
  13. package/src/styles/variables.css +194 -0
  14. package/src/ui/avatar.tsx +92 -0
  15. package/src/ui/badge.tsx +68 -0
  16. package/src/ui/button.tsx +100 -0
  17. package/src/ui/card.tsx +88 -0
  18. package/src/ui/checkbox.tsx +76 -0
  19. package/src/ui/collapsible.tsx +36 -0
  20. package/src/ui/dropdown.tsx +375 -0
  21. package/src/ui/index.ts +31 -0
  22. package/src/ui/input-numbers.tsx +201 -0
  23. package/src/ui/input.tsx +141 -0
  24. package/src/ui/layout.tsx +41 -0
  25. package/src/ui/modal.tsx +198 -0
  26. package/src/ui/popover.tsx +59 -0
  27. package/src/ui/radio-group.tsx +56 -0
  28. package/src/ui/select.tsx +292 -0
  29. package/src/ui/sheet.tsx +131 -0
  30. package/src/ui/slider.tsx +189 -0
  31. package/src/ui/switch.tsx +43 -0
  32. package/src/ui/tabs.tsx +128 -0
  33. package/src/ui/textarea.tsx +52 -0
  34. package/src/ui/theme-context.tsx +80 -0
  35. package/src/ui/timeline.tsx +717 -0
  36. package/src/ui/toast.tsx +27 -0
  37. package/src/ui/toggle-group.tsx +82 -0
  38. package/src/ui/toggle.tsx +88 -0
  39. package/src/ui/tooltip.tsx +132 -0
  40. package/src/ui/tree-view-v2.tsx +300 -0
  41. package/src/ui/tree-view.tsx +550 -0
  42. package/src/ui/version-badge.tsx +65 -0
  43. package/src/utils/cn.ts +21 -0
  44. package/src/utils/index.ts +2 -0
  45. package/src/utils/use-local-storage.ts +59 -0
@@ -0,0 +1,292 @@
1
+ import * as SelectPrimitive from "@radix-ui/react-select";
2
+ import { cva, type VariantProps } from "class-variance-authority";
3
+ import { ChevronDownIcon, ChevronUpIcon } from "lucide-react";
4
+ import type { ComponentProps, ReactNode } from "react";
5
+
6
+ import { cn } from "../utils";
7
+
8
+ function Select({ ...props }: ComponentProps<typeof SelectPrimitive.Root>) {
9
+ return <SelectPrimitive.Root data-slot="select" {...props} />;
10
+ }
11
+
12
+ function SelectGroup({ className, ...props }: ComponentProps<typeof SelectPrimitive.Group>) {
13
+ return (
14
+ <SelectPrimitive.Group
15
+ data-slot="select-group"
16
+ className={cn("space-y-xs", className)}
17
+ {...props}
18
+ />
19
+ );
20
+ }
21
+
22
+ function SelectValue({ ...props }: ComponentProps<typeof SelectPrimitive.Value>) {
23
+ return <SelectPrimitive.Value data-slot="select-value" {...props} />;
24
+ }
25
+
26
+ const selectTriggerVariants = cva(
27
+ [
28
+ "flex",
29
+ "items-center",
30
+ "justify-between",
31
+ "gap-2",
32
+ "rounded-uk-sm",
33
+ "bg-bg-secondary",
34
+ "whitespace-nowrap",
35
+ "shadow-xs",
36
+ "transition-[color,box-shadow]",
37
+ "outline-none",
38
+ "*:data-[slot=select-value]:line-clamp-1",
39
+ "*:data-[slot=select-value]:flex",
40
+ "*:data-[slot=select-value]:items-center",
41
+ "*:data-[slot=select-value]:gap-2",
42
+ "[&_svg]:shrink-0",
43
+ "[&_svg]:pointer-events-none",
44
+ "[&_svg:not([class*='text-'])]:text-muted-foreground",
45
+ ],
46
+ {
47
+ variants: {
48
+ state: {
49
+ default: ["hover:bg-bg-tertiary", "has-[input:focus]:bg-bg-quaternary"],
50
+ disabled: ["disabled:cursor-not-allowed", "bg-bg-tertiary", "text-text-tertiary"],
51
+ error: ["hover:bg-danger-primary/10", "bg-danger-primary/20"],
52
+ },
53
+ size: {
54
+ sm: "px-sm py-xs text-uk-sm h-5.5 [&_svg:not([class*='size-'])]:size-lg",
55
+ base: "px-md py-sm text-uk-md h-7 [&_svg:not([class*='size-'])]:size-[14px]",
56
+ lg: "px-lg py-sm text-uk-lg h-8 [&_svg:not([class*='size-'])]:size-xl",
57
+ },
58
+ },
59
+ defaultVariants: {
60
+ state: "default",
61
+ size: "base",
62
+ },
63
+ },
64
+ );
65
+
66
+ interface SelectTriggerProps
67
+ extends ComponentProps<typeof SelectPrimitive.Trigger>,
68
+ VariantProps<typeof selectTriggerVariants> {}
69
+
70
+ function SelectTrigger({
71
+ className,
72
+ size,
73
+ children,
74
+ disabled,
75
+ state,
76
+ ...props
77
+ }: SelectTriggerProps) {
78
+ return (
79
+ <SelectPrimitive.Trigger
80
+ data-slot="select-trigger"
81
+ data-size={size}
82
+ className={cn(
83
+ selectTriggerVariants({ size, className, state: disabled ? "disabled" : state }),
84
+ )}
85
+ disabled={disabled}
86
+ {...props}
87
+ >
88
+ {children}
89
+ <SelectPrimitive.Icon asChild>
90
+ <ChevronDownIcon className="opacity-50" />
91
+ </SelectPrimitive.Icon>
92
+ </SelectPrimitive.Trigger>
93
+ );
94
+ }
95
+
96
+ function SelectContent({
97
+ className,
98
+ children,
99
+ position = "item-aligned",
100
+ ...props
101
+ }: ComponentProps<typeof SelectPrimitive.Content>) {
102
+ return (
103
+ <SelectPrimitive.Portal>
104
+ <SelectPrimitive.Content
105
+ data-slot="select-content"
106
+ className={cn(
107
+ [
108
+ "bg-bg-primary",
109
+ "data-[state=open]:animate-in",
110
+ "data-[state=closed]:animate-out",
111
+ "data-[state=closed]:fade-out-0",
112
+ "data-[state=open]:fade-in-0",
113
+ "data-[state=closed]:zoom-out-95",
114
+ "data-[state=open]:zoom-in-95",
115
+ "data-[side=bottom]:slide-in-from-top-2",
116
+ "data-[side=left]:slide-in-from-right-2",
117
+ "data-[side=right]:slide-in-from-left-2",
118
+ "data-[side=top]:slide-in-from-bottom-2",
119
+ "relative",
120
+ "z-50",
121
+ "max-h-(--radix-select-content-available-height)",
122
+ "min-w-[8rem]",
123
+ "origin-(--radix-select-content-transform-origin)",
124
+ "overflow-x-hidden",
125
+ "overflow-y-auto",
126
+ "rounded-uk-md",
127
+ "shadow-md",
128
+ "shadow-[0_8px_20px_0_rgba(0,0,0,.80)]",
129
+ "liquid:liquid-bg",
130
+ ],
131
+ {
132
+ "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1":
133
+ position === "popper",
134
+ },
135
+ className,
136
+ )}
137
+ position={position}
138
+ {...props}
139
+ >
140
+ <SelectScrollUpButton />
141
+ <SelectPrimitive.Viewport
142
+ className={cn("p-md", {
143
+ "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1":
144
+ position === "popper",
145
+ })}
146
+ >
147
+ {children}
148
+ </SelectPrimitive.Viewport>
149
+ <SelectScrollDownButton />
150
+ </SelectPrimitive.Content>
151
+ </SelectPrimitive.Portal>
152
+ );
153
+ }
154
+
155
+ function SelectLabel({ className, ...props }: ComponentProps<typeof SelectPrimitive.Label>) {
156
+ return (
157
+ <SelectPrimitive.Label
158
+ data-slot="select-label"
159
+ className={cn("text-text-secondary text-uk-xs px-2 py-1.5", className)}
160
+ {...props}
161
+ />
162
+ );
163
+ }
164
+
165
+ interface SelectItemProps extends Omit<ComponentProps<typeof SelectPrimitive.Item>, "prefix"> {
166
+ prefix?: ReactNode;
167
+ suffix?: ReactNode;
168
+ }
169
+
170
+ function SelectItem({ className, children, prefix, suffix, disabled, ...props }: SelectItemProps) {
171
+ return (
172
+ <SelectPrimitive.Item
173
+ data-slot="select-item"
174
+ disabled={disabled}
175
+ className={cn(
176
+ [
177
+ "group/select-item",
178
+ "w-full",
179
+ "flex",
180
+ "gap-md",
181
+ "items-center",
182
+ "px-md",
183
+ "py-xs",
184
+ "relative",
185
+ "cursor-default",
186
+ "rounded-uk-xs",
187
+ "text-uk-xs",
188
+ "outline-hidden",
189
+ "select-none",
190
+ "focus:bg-bg-secondary",
191
+ "data-[state=checked]:bg-brand-primary",
192
+ "data-[disabled]:pointer-events-none",
193
+ "data-[disabled]:text-bg-quaternary",
194
+ "[&_svg]:pointer-events-none",
195
+ "[&_svg]:shrink-0",
196
+ "[&_svg:not([class*='size-'])]:size-4",
197
+ "[&_svg:not([class*='text-'])]:text-muted-foreground",
198
+ "*:[span]:last:flex",
199
+ "*:[span]:last:items-center",
200
+ "*:[span]:last:gap-2",
201
+ ],
202
+ className,
203
+ )}
204
+ {...props}
205
+ >
206
+ <p
207
+ data-disabled={disabled}
208
+ className={cn([
209
+ "text-text-tertiary",
210
+ "flex",
211
+ "shrink-0",
212
+ "gap-0.5",
213
+ "data-[disabled=true]:text-bg-quaternary",
214
+ "group-data-[state=checked]/select-item:text-text-withbg",
215
+ ])}
216
+ >
217
+ {prefix}
218
+ </p>
219
+ <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
220
+ <p
221
+ data-disabled={disabled}
222
+ className={cn(
223
+ "text-text-tertiary",
224
+ "ml-auto",
225
+ "flex",
226
+ "shrink-0",
227
+ "gap-0.5",
228
+ "data-[disabled=true]:text-bg-quaternary",
229
+ "group-data-[state=checked]/select-item:text-text-withbg",
230
+ )}
231
+ >
232
+ {suffix}
233
+ </p>
234
+ </SelectPrimitive.Item>
235
+ );
236
+ }
237
+
238
+ function SelectSeparator({
239
+ className,
240
+ ...props
241
+ }: ComponentProps<typeof SelectPrimitive.Separator>) {
242
+ return (
243
+ <SelectPrimitive.Separator
244
+ data-slot="select-separator"
245
+ className={cn("bg-line-primary my-lg pointer-events-none -mx-1 h-px", className)}
246
+ {...props}
247
+ />
248
+ );
249
+ }
250
+
251
+ function SelectScrollUpButton({
252
+ className,
253
+ ...props
254
+ }: ComponentProps<typeof SelectPrimitive.ScrollUpButton>) {
255
+ return (
256
+ <SelectPrimitive.ScrollUpButton
257
+ data-slot="select-scroll-up-button"
258
+ className={cn("flex cursor-default items-center justify-center py-1", className)}
259
+ {...props}
260
+ >
261
+ <ChevronUpIcon className="size-4" />
262
+ </SelectPrimitive.ScrollUpButton>
263
+ );
264
+ }
265
+
266
+ function SelectScrollDownButton({
267
+ className,
268
+ ...props
269
+ }: ComponentProps<typeof SelectPrimitive.ScrollDownButton>) {
270
+ return (
271
+ <SelectPrimitive.ScrollDownButton
272
+ data-slot="select-scroll-down-button"
273
+ className={cn("flex cursor-default items-center justify-center py-1", className)}
274
+ {...props}
275
+ >
276
+ <ChevronDownIcon className="size-4" />
277
+ </SelectPrimitive.ScrollDownButton>
278
+ );
279
+ }
280
+
281
+ export {
282
+ Select,
283
+ SelectContent,
284
+ SelectGroup,
285
+ SelectItem,
286
+ SelectLabel,
287
+ SelectScrollDownButton,
288
+ SelectScrollUpButton,
289
+ SelectSeparator,
290
+ SelectTrigger,
291
+ SelectValue,
292
+ };
@@ -0,0 +1,131 @@
1
+ import * as SheetPrimitive from "@radix-ui/react-dialog";
2
+ import { XIcon } from "lucide-react";
3
+ import { ComponentProps } from "react";
4
+
5
+ import { cn } from "../utils/cn";
6
+
7
+ function Sheet({ ...props }: ComponentProps<typeof SheetPrimitive.Root>) {
8
+ return <SheetPrimitive.Root data-slot="sheet" {...props} />;
9
+ }
10
+
11
+ function SheetTrigger({ ...props }: ComponentProps<typeof SheetPrimitive.Trigger>) {
12
+ return <SheetPrimitive.Trigger data-slot="sheet-trigger" {...props} />;
13
+ }
14
+
15
+ function SheetClose({ ...props }: ComponentProps<typeof SheetPrimitive.Close>) {
16
+ return (
17
+ <SheetPrimitive.Close
18
+ data-slot="sheet-close"
19
+ className={cn("focus:ring-0 focus:outline-none")}
20
+ {...props}
21
+ />
22
+ );
23
+ }
24
+
25
+ function SheetPortal({ ...props }: ComponentProps<typeof SheetPrimitive.Portal>) {
26
+ return <SheetPrimitive.Portal data-slot="sheet-portal" {...props} />;
27
+ }
28
+
29
+ function SheetOverlay({ className, ...props }: ComponentProps<typeof SheetPrimitive.Overlay>) {
30
+ return (
31
+ <SheetPrimitive.Overlay
32
+ data-slot="sheet-overlay"
33
+ className={cn(
34
+ "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 bg-bg-primary/50 fixed inset-0 z-50",
35
+ className,
36
+ )}
37
+ {...props}
38
+ />
39
+ );
40
+ }
41
+
42
+ function SheetContent({
43
+ className,
44
+ children,
45
+ side = "right",
46
+ ...props
47
+ }: ComponentProps<typeof SheetPrimitive.Content> & {
48
+ side?: "top" | "right" | "bottom" | "left";
49
+ }) {
50
+ return (
51
+ <SheetPortal>
52
+ <SheetOverlay />
53
+ <SheetPrimitive.Content
54
+ data-slot="sheet-content"
55
+ className={cn(
56
+ "liquid:liquid-bg bg-bg-primary data-[state=open]:animate-in data-[state=closed]:animate-out p-xl fixed z-50 flex flex-col gap-4 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
57
+ side === "right" &&
58
+ "data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right inset-y-0 right-0 h-full w-[340px]",
59
+ side === "left" &&
60
+ "data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left inset-y-0 left-0 h-full w-[340px]",
61
+ side === "top" &&
62
+ "data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top inset-x-0 top-0 h-auto",
63
+ side === "bottom" &&
64
+ "data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom inset-x-0 bottom-0 h-auto",
65
+ className,
66
+ )}
67
+ {...props}
68
+ >
69
+ {children}
70
+ <SheetPrimitive.Close className="text-icon-primary hover:text-icon-highlight absolute top-4 right-4 disabled:pointer-events-none">
71
+ <XIcon className="size-4" />
72
+ <span className="sr-only">Close</span>
73
+ </SheetPrimitive.Close>
74
+ </SheetPrimitive.Content>
75
+ </SheetPortal>
76
+ );
77
+ }
78
+
79
+ function SheetHeader({ className, ...props }: ComponentProps<"div">) {
80
+ return (
81
+ <div
82
+ data-slot="sheet-header"
83
+ className={cn("gap-md pb-xl flex flex-col", className)}
84
+ {...props}
85
+ />
86
+ );
87
+ }
88
+
89
+ function SheetFooter({ className, ...props }: ComponentProps<"div">) {
90
+ return (
91
+ <div
92
+ data-slot="sheet-footer"
93
+ className={cn("gap-lg pt-xl mt-auto flex flex-col", className)}
94
+ {...props}
95
+ />
96
+ );
97
+ }
98
+
99
+ function SheetTitle({ className, ...props }: ComponentProps<typeof SheetPrimitive.Title>) {
100
+ return (
101
+ <SheetPrimitive.Title
102
+ data-slot="sheet-title"
103
+ className={cn("text-text-highlight text-uk-xl leading-uk-xl font-medium", className)}
104
+ {...props}
105
+ />
106
+ );
107
+ }
108
+
109
+ function SheetDescription({
110
+ className,
111
+ ...props
112
+ }: ComponentProps<typeof SheetPrimitive.Description>) {
113
+ return (
114
+ <SheetPrimitive.Description
115
+ data-slot="sheet-description"
116
+ className={cn("text-text-secondary text-uk-md leading-uk-md", className)}
117
+ {...props}
118
+ />
119
+ );
120
+ }
121
+
122
+ export {
123
+ Sheet,
124
+ SheetTrigger,
125
+ SheetClose,
126
+ SheetContent,
127
+ SheetHeader,
128
+ SheetFooter,
129
+ SheetTitle,
130
+ SheetDescription,
131
+ };
@@ -0,0 +1,189 @@
1
+ import * as SliderPrimitive from "@radix-ui/react-slider";
2
+ import { ComponentProps, ComponentRef, forwardRef, useMemo, useState } from "react";
3
+
4
+ import { cn } from "../utils";
5
+
6
+ interface SliderProps extends ComponentProps<typeof SliderPrimitive.Root> {
7
+ showStep?: boolean;
8
+ }
9
+
10
+ const Slider = forwardRef<ComponentRef<typeof SliderPrimitive.Root>, SliderProps>(function Slider(
11
+ {
12
+ className,
13
+ defaultValue,
14
+ value,
15
+ onValueChange,
16
+ showStep = false,
17
+ step = 1,
18
+ min = 0,
19
+ max = 100,
20
+ ...props
21
+ },
22
+ ref,
23
+ ) {
24
+ const [currentValue, setCurrentValue] = useState<number[]>(value ?? defaultValue ?? [0]);
25
+
26
+ const _values = useMemo(
27
+ () => (Array.isArray(value) ? value : Array.isArray(defaultValue) ? defaultValue : [min, max]),
28
+ [value, defaultValue, min, max],
29
+ );
30
+
31
+ return (
32
+ <SliderPrimitive.Root
33
+ data-slot="slider"
34
+ value={value}
35
+ defaultValue={defaultValue}
36
+ onValueChange={(v) => {
37
+ onValueChange?.(v);
38
+ setCurrentValue(v);
39
+ }}
40
+ ref={ref}
41
+ min={min}
42
+ max={max}
43
+ step={step}
44
+ className={cn(
45
+ "relative",
46
+ "flex",
47
+ "w-full",
48
+ "touch-none",
49
+ "select-none",
50
+ "items-center",
51
+ "data-[orientation=vertical]:h-full",
52
+ "data-[orientation=vertical]:min-h-44",
53
+ "data-[orientation=vertical]:w-auto",
54
+ "data-[orientation=vertical]:flex-col",
55
+ className,
56
+ )}
57
+ {...props}
58
+ >
59
+ <SliderPrimitive.Track
60
+ data-slot="slider-track"
61
+ className={cn(
62
+ "peer/slider-track",
63
+ "bg-line-primary",
64
+ "relative",
65
+ "grow",
66
+ "overflow-hidden",
67
+ "rounded-full",
68
+ "data-[disabled]:bg-bg-secondary",
69
+ "data-[orientation=vertical]:w-0.5",
70
+ "data-[orientation=vertical]:h-full",
71
+ "data-[orientation=horizontal]:h-0.5",
72
+ "data-[orientation=horizontal]:w-full",
73
+ )}
74
+ >
75
+ <SliderPrimitive.Range
76
+ data-slot="slider-range"
77
+ className={cn(
78
+ "bg-brand-primary",
79
+ "absolute",
80
+ "data-[disabled]:bg-bg-secondary",
81
+ "data-[orientation=vertical]:w-full",
82
+ "data-[orientation=horizontal]:h-full",
83
+ )}
84
+ />
85
+ </SliderPrimitive.Track>
86
+
87
+ {showStep && <SliderSteps currentValue={currentValue} max={max} min={min} step={step} />}
88
+
89
+ {Array.from({ length: _values.length }, (_, index) => (
90
+ <SliderPrimitive.Thumb
91
+ data-slot="slider-thumb"
92
+ key={index}
93
+ className={cn(
94
+ "ring-brand-primary/50",
95
+ "bg-brand-primary",
96
+ "block",
97
+ "h-4",
98
+ "w-[0.6rem]",
99
+ "shrink-0",
100
+ "rounded-[3px]",
101
+ "transition-[color,box-shadow]",
102
+ "hover:ring-4",
103
+ "hover:cursor-ew-resize",
104
+ "focus-visible:ring-4",
105
+ "focus-visible:outline-hidden",
106
+ "data-[disabled]:bg-bg-tertiary",
107
+ "data-[disabled]:pointer-events-none",
108
+ )}
109
+ />
110
+ ))}
111
+ </SliderPrimitive.Root>
112
+ );
113
+ });
114
+
115
+ interface SliderStepsProps {
116
+ min: number;
117
+ max: number;
118
+ step: number;
119
+ currentValue: number[];
120
+ }
121
+
122
+ function SliderSteps({ step, min, max, currentValue }: SliderStepsProps) {
123
+ const steps = useMemo(() => {
124
+ const value = max - min;
125
+ const stepCount = Math.ceil(value / step);
126
+ return Array.from({ length: stepCount + 1 }, (_, i) => {
127
+ return { position: Math.min((100 / value) * (i * step), 100), v: i * step + min };
128
+ });
129
+ }, [max, min, step]);
130
+
131
+ const [minRange, maxRange] = useMemo(() => {
132
+ if (currentValue.length > 1) {
133
+ return [Math.min(...currentValue), Math.max(...currentValue)];
134
+ } else {
135
+ return [0, ...currentValue];
136
+ }
137
+ }, [currentValue]);
138
+
139
+ return steps.map((step, i) => {
140
+ /*
141
+ * 9.6 is the width of the thumb in pixels
142
+ * 1 is the direction of the thumb (1 for right, -1 for left)
143
+ * */
144
+ const thumbInBoundsOffset = getThumbInBoundsOffset(9.6, step.position, 1);
145
+ return (
146
+ <span
147
+ key={i}
148
+ data-slot="slider-step"
149
+ style={{ left: `calc(${step.position}% + ${thumbInBoundsOffset}px)` }}
150
+ className={cn(
151
+ "absolute",
152
+ "top-1/2",
153
+ "translate-y-[-50%]",
154
+ "translate-x-[-50%]",
155
+ "bg-brand-primary",
156
+ "block",
157
+ "h-[5pt]",
158
+ "w-[1pt]",
159
+ "shrink-0",
160
+ "rounded-[1px]",
161
+ "peer-has-data-[disabled]/slider-track:bg-bg-tertiary",
162
+ { "bg-bg-tertiary": step.v < minRange || step.v > maxRange },
163
+ )}
164
+ />
165
+ );
166
+ });
167
+ }
168
+
169
+ /**
170
+ * Offsets the thumb centre point while sliding to ensure it remains
171
+ * within the bounds of the slider when reaching the edges
172
+ */
173
+ function getThumbInBoundsOffset(width: number, left: number, direction: number) {
174
+ const halfWidth = width / 2;
175
+ const halfPercent = 50;
176
+ const offset = linearScale([0, halfPercent], [0, halfWidth]);
177
+ return (halfWidth - offset(left) * direction) * direction;
178
+ }
179
+
180
+ function linearScale(input: [number, number], output: [number, number]) {
181
+ return (value: number) => {
182
+ if (input[0] === input[1] || output[0] === output[1]) return output[0];
183
+ const ratio = (output[1] - output[0]) / (input[1] - input[0]);
184
+ return output[0] + ratio * (value - input[0]);
185
+ };
186
+ }
187
+
188
+ export { Slider };
189
+ export type { SliderProps };
@@ -0,0 +1,43 @@
1
+ import * as SwitchPrimitive from "@radix-ui/react-switch";
2
+ import { type ComponentProps } from "react";
3
+
4
+ import { cn } from "../utils";
5
+
6
+ function Switch({ className, ...props }: ComponentProps<typeof SwitchPrimitive.Root>) {
7
+ return (
8
+ <SwitchPrimitive.Root
9
+ data-slot="switch"
10
+ className={cn(
11
+ "group",
12
+ "w-[32px]",
13
+ "h-[16px]",
14
+ "rounded-[24px]",
15
+ "bg-bg-tertiary",
16
+ "data-[state=checked]:bg-brand-primary",
17
+ "disabled:bg-bg-secondary",
18
+ "disabled:cursor-not-allowed",
19
+ "disabled:data-[state=checked]:bg-bg-secondary",
20
+ "disabled:data-[state=unchecked]:bg-bg-secondary",
21
+ className,
22
+ )}
23
+ {...props}
24
+ >
25
+ <SwitchPrimitive.Thumb
26
+ data-slot="switch-thumb"
27
+ className={cn(
28
+ "block",
29
+ "size-[12px]",
30
+ "rounded-full",
31
+ "bg-icon-withbg",
32
+ "group-disabled:bg-bg-primary",
33
+ "transition-transform",
34
+ "duration-200",
35
+ "translate-x-[2px]",
36
+ "data-[state=checked]:translate-x-[18px]",
37
+ )}
38
+ />
39
+ </SwitchPrimitive.Root>
40
+ );
41
+ }
42
+
43
+ export { Switch };