atlasui-lib 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +157 -0
- package/LICENSE +21 -0
- package/README.md +253 -0
- package/dist/cli/index.js +364 -0
- package/dist/index.d.mts +1027 -0
- package/dist/index.d.ts +1027 -0
- package/dist/index.js +3954 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +3733 -0
- package/dist/index.mjs.map +1 -0
- package/dist/provider.d.mts +15 -0
- package/dist/provider.d.ts +15 -0
- package/dist/provider.js +816 -0
- package/dist/provider.js.map +1 -0
- package/dist/provider.mjs +780 -0
- package/dist/provider.mjs.map +1 -0
- package/dist/tailwind.d.ts +25 -0
- package/dist/tailwind.js +129 -0
- package/package.json +138 -0
- package/src/cli/index.ts +301 -0
- package/src/cli/registry.ts +139 -0
- package/src/components/advanced-forms/index.tsx +567 -0
- package/src/components/basic/Button.tsx +135 -0
- package/src/components/basic/IconButton.tsx +69 -0
- package/src/components/basic/index.tsx +446 -0
- package/src/components/data-display/index.tsx +608 -0
- package/src/components/feedback/index.tsx +554 -0
- package/src/components/forms/index.tsx +476 -0
- package/src/components/layout/index.tsx +296 -0
- package/src/components/media/index.tsx +437 -0
- package/src/components/navigation/index.tsx +484 -0
- package/src/components/overlay/index.tsx +473 -0
- package/src/components/utility/index.tsx +411 -0
- package/src/hooks/index.ts +271 -0
- package/src/hooks/use-toast.tsx +74 -0
- package/src/index.ts +353 -0
- package/src/provider.tsx +54 -0
- package/src/styles/atlas.css +252 -0
- package/src/tailwind.ts +124 -0
- package/src/types/index.ts +95 -0
- package/src/utils/cn.ts +66 -0
|
@@ -0,0 +1,476 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
|
|
3
|
+
import * as RadioGroupPrimitive from "@radix-ui/react-radio-group";
|
|
4
|
+
import * as SwitchPrimitive from "@radix-ui/react-switch";
|
|
5
|
+
import * as SliderPrimitive from "@radix-ui/react-slider";
|
|
6
|
+
import * as SelectPrimitive from "@radix-ui/react-select";
|
|
7
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
8
|
+
import { cn } from "../../utils/cn";
|
|
9
|
+
|
|
10
|
+
// ─── Input ─────────────────────────────────────────────────────────────────
|
|
11
|
+
|
|
12
|
+
const inputVariants = cva(
|
|
13
|
+
[
|
|
14
|
+
"atlas-input flex w-full rounded-md border border-input bg-background px-3 py-2 text-sm",
|
|
15
|
+
"ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium",
|
|
16
|
+
"placeholder:text-muted-foreground",
|
|
17
|
+
"transition-shadow duration-150",
|
|
18
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
|
19
|
+
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
20
|
+
],
|
|
21
|
+
{
|
|
22
|
+
variants: {
|
|
23
|
+
size: {
|
|
24
|
+
sm: "h-8 px-2.5 text-xs",
|
|
25
|
+
md: "h-9 px-3 text-sm",
|
|
26
|
+
lg: "h-10 px-4 text-base",
|
|
27
|
+
},
|
|
28
|
+
invalid: {
|
|
29
|
+
true: "border-destructive focus-visible:ring-destructive",
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
defaultVariants: { size: "md" },
|
|
33
|
+
}
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
export interface InputProps
|
|
37
|
+
extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "size">,
|
|
38
|
+
VariantProps<typeof inputVariants> {
|
|
39
|
+
leftElement?: React.ReactNode;
|
|
40
|
+
rightElement?: React.ReactNode;
|
|
41
|
+
invalid?: boolean;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
45
|
+
({ className, type, size, invalid, leftElement, rightElement, ...props }, ref) => {
|
|
46
|
+
if (leftElement || rightElement) {
|
|
47
|
+
return (
|
|
48
|
+
<div className="relative flex items-center w-full">
|
|
49
|
+
{leftElement && (
|
|
50
|
+
<span className="absolute left-3 text-muted-foreground [&>svg]:h-4 [&>svg]:w-4 pointer-events-none">
|
|
51
|
+
{leftElement}
|
|
52
|
+
</span>
|
|
53
|
+
)}
|
|
54
|
+
<input
|
|
55
|
+
type={type}
|
|
56
|
+
className={cn(
|
|
57
|
+
inputVariants({ size, invalid: invalid ?? false }),
|
|
58
|
+
leftElement && "pl-9",
|
|
59
|
+
rightElement && "pr-9",
|
|
60
|
+
className
|
|
61
|
+
)}
|
|
62
|
+
ref={ref}
|
|
63
|
+
aria-invalid={invalid}
|
|
64
|
+
{...props}
|
|
65
|
+
/>
|
|
66
|
+
{rightElement && (
|
|
67
|
+
<span className="absolute right-3 text-muted-foreground [&>svg]:h-4 [&>svg]:w-4">
|
|
68
|
+
{rightElement}
|
|
69
|
+
</span>
|
|
70
|
+
)}
|
|
71
|
+
</div>
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return (
|
|
76
|
+
<input
|
|
77
|
+
type={type}
|
|
78
|
+
className={cn(inputVariants({ size, invalid: invalid ?? false }), className)}
|
|
79
|
+
ref={ref}
|
|
80
|
+
aria-invalid={invalid}
|
|
81
|
+
{...props}
|
|
82
|
+
/>
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
);
|
|
86
|
+
Input.displayName = "Input";
|
|
87
|
+
|
|
88
|
+
// ─── TextArea ─────────────────────────────────────────────────────────────
|
|
89
|
+
|
|
90
|
+
export interface TextAreaProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {
|
|
91
|
+
invalid?: boolean;
|
|
92
|
+
resize?: "none" | "both" | "horizontal" | "vertical";
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const TextArea = React.forwardRef<HTMLTextAreaElement, TextAreaProps>(
|
|
96
|
+
({ className, invalid, resize = "vertical", ...props }, ref) => (
|
|
97
|
+
<textarea
|
|
98
|
+
className={cn(
|
|
99
|
+
"atlas-textarea flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm",
|
|
100
|
+
"ring-offset-background placeholder:text-muted-foreground",
|
|
101
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
|
102
|
+
"disabled:cursor-not-allowed disabled:opacity-50 transition-shadow",
|
|
103
|
+
resize === "none" && "resize-none",
|
|
104
|
+
resize === "both" && "resize",
|
|
105
|
+
resize === "horizontal" && "resize-x",
|
|
106
|
+
resize === "vertical" && "resize-y",
|
|
107
|
+
invalid && "border-destructive focus-visible:ring-destructive",
|
|
108
|
+
className
|
|
109
|
+
)}
|
|
110
|
+
ref={ref}
|
|
111
|
+
aria-invalid={invalid}
|
|
112
|
+
{...props}
|
|
113
|
+
/>
|
|
114
|
+
)
|
|
115
|
+
);
|
|
116
|
+
TextArea.displayName = "TextArea";
|
|
117
|
+
|
|
118
|
+
// ─── Select ───────────────────────────────────────────────────────────────
|
|
119
|
+
|
|
120
|
+
const Select = SelectPrimitive.Root;
|
|
121
|
+
const SelectGroup = SelectPrimitive.Group;
|
|
122
|
+
const SelectValue = SelectPrimitive.Value;
|
|
123
|
+
|
|
124
|
+
const SelectTrigger = React.forwardRef<
|
|
125
|
+
React.ElementRef<typeof SelectPrimitive.Trigger>,
|
|
126
|
+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger> & { invalid?: boolean }
|
|
127
|
+
>(({ className, children, invalid, ...props }, ref) => (
|
|
128
|
+
<SelectPrimitive.Trigger
|
|
129
|
+
ref={ref}
|
|
130
|
+
className={cn(
|
|
131
|
+
"atlas-select-trigger flex h-9 w-full items-center justify-between rounded-md",
|
|
132
|
+
"border border-input bg-background px-3 py-2 text-sm",
|
|
133
|
+
"ring-offset-background placeholder:text-muted-foreground",
|
|
134
|
+
"focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
|
|
135
|
+
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
136
|
+
invalid && "border-destructive focus:ring-destructive",
|
|
137
|
+
"[&>span]:line-clamp-1",
|
|
138
|
+
className
|
|
139
|
+
)}
|
|
140
|
+
{...props}
|
|
141
|
+
>
|
|
142
|
+
{children}
|
|
143
|
+
<SelectPrimitive.Icon asChild>
|
|
144
|
+
<svg className="h-4 w-4 opacity-50 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
145
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
|
146
|
+
</svg>
|
|
147
|
+
</SelectPrimitive.Icon>
|
|
148
|
+
</SelectPrimitive.Trigger>
|
|
149
|
+
));
|
|
150
|
+
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
|
|
151
|
+
|
|
152
|
+
const SelectContent = React.forwardRef<
|
|
153
|
+
React.ElementRef<typeof SelectPrimitive.Content>,
|
|
154
|
+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
|
|
155
|
+
>(({ className, children, position = "popper", ...props }, ref) => (
|
|
156
|
+
<SelectPrimitive.Portal>
|
|
157
|
+
<SelectPrimitive.Content
|
|
158
|
+
ref={ref}
|
|
159
|
+
className={cn(
|
|
160
|
+
"atlas-select-content relative z-50 min-w-[8rem] overflow-hidden rounded-md",
|
|
161
|
+
"border border-border bg-popover text-popover-foreground shadow-md",
|
|
162
|
+
"animate-in fade-in-0 zoom-in-95",
|
|
163
|
+
position === "popper" && "translate-y-1",
|
|
164
|
+
className
|
|
165
|
+
)}
|
|
166
|
+
position={position}
|
|
167
|
+
{...props}
|
|
168
|
+
>
|
|
169
|
+
<SelectPrimitive.Viewport className={cn("p-1", position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]")}>
|
|
170
|
+
{children}
|
|
171
|
+
</SelectPrimitive.Viewport>
|
|
172
|
+
</SelectPrimitive.Content>
|
|
173
|
+
</SelectPrimitive.Portal>
|
|
174
|
+
));
|
|
175
|
+
SelectContent.displayName = SelectPrimitive.Content.displayName;
|
|
176
|
+
|
|
177
|
+
const SelectItem = React.forwardRef<
|
|
178
|
+
React.ElementRef<typeof SelectPrimitive.Item>,
|
|
179
|
+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
|
|
180
|
+
>(({ className, children, ...props }, ref) => (
|
|
181
|
+
<SelectPrimitive.Item
|
|
182
|
+
ref={ref}
|
|
183
|
+
className={cn(
|
|
184
|
+
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm",
|
|
185
|
+
"outline-none focus:bg-accent focus:text-accent-foreground",
|
|
186
|
+
"data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
187
|
+
className
|
|
188
|
+
)}
|
|
189
|
+
{...props}
|
|
190
|
+
>
|
|
191
|
+
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
|
192
|
+
<SelectPrimitive.ItemIndicator>
|
|
193
|
+
<svg className="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
194
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2.5} d="M5 13l4 4L19 7" />
|
|
195
|
+
</svg>
|
|
196
|
+
</SelectPrimitive.ItemIndicator>
|
|
197
|
+
</span>
|
|
198
|
+
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
|
|
199
|
+
</SelectPrimitive.Item>
|
|
200
|
+
));
|
|
201
|
+
SelectItem.displayName = SelectPrimitive.Item.displayName;
|
|
202
|
+
|
|
203
|
+
const SelectLabel = React.forwardRef<
|
|
204
|
+
React.ElementRef<typeof SelectPrimitive.Label>,
|
|
205
|
+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
|
|
206
|
+
>(({ className, ...props }, ref) => (
|
|
207
|
+
<SelectPrimitive.Label ref={ref} className={cn("py-1.5 pl-8 pr-2 text-xs font-semibold text-muted-foreground", className)} {...props} />
|
|
208
|
+
));
|
|
209
|
+
SelectLabel.displayName = SelectPrimitive.Label.displayName;
|
|
210
|
+
|
|
211
|
+
const SelectSeparator = React.forwardRef<
|
|
212
|
+
React.ElementRef<typeof SelectPrimitive.Separator>,
|
|
213
|
+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
|
|
214
|
+
>(({ className, ...props }, ref) => (
|
|
215
|
+
<SelectPrimitive.Separator ref={ref} className={cn("-mx-1 my-1 h-px bg-border", className)} {...props} />
|
|
216
|
+
));
|
|
217
|
+
SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
|
|
218
|
+
|
|
219
|
+
// ─── Checkbox ─────────────────────────────────────────────────────────────
|
|
220
|
+
|
|
221
|
+
export interface CheckboxProps extends React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root> {
|
|
222
|
+
label?: React.ReactNode;
|
|
223
|
+
description?: string;
|
|
224
|
+
invalid?: boolean;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const Checkbox = React.forwardRef<React.ElementRef<typeof CheckboxPrimitive.Root>, CheckboxProps>(
|
|
228
|
+
({ className, label, description, invalid, id, ...props }, ref) => {
|
|
229
|
+
const checkboxId = id ?? React.useId();
|
|
230
|
+
|
|
231
|
+
return (
|
|
232
|
+
<div className="atlas-checkbox flex items-start gap-2.5">
|
|
233
|
+
<CheckboxPrimitive.Root
|
|
234
|
+
ref={ref}
|
|
235
|
+
id={checkboxId}
|
|
236
|
+
className={cn(
|
|
237
|
+
"peer h-4 w-4 shrink-0 rounded border border-primary ring-offset-background",
|
|
238
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
|
239
|
+
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
240
|
+
"data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
|
|
241
|
+
invalid && "border-destructive",
|
|
242
|
+
className
|
|
243
|
+
)}
|
|
244
|
+
{...props}
|
|
245
|
+
>
|
|
246
|
+
<CheckboxPrimitive.Indicator className="flex items-center justify-center text-current">
|
|
247
|
+
<svg className="h-3 w-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
248
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={3} d="M5 13l4 4L19 7" />
|
|
249
|
+
</svg>
|
|
250
|
+
</CheckboxPrimitive.Indicator>
|
|
251
|
+
</CheckboxPrimitive.Root>
|
|
252
|
+
{(label || description) && (
|
|
253
|
+
<div className="grid gap-0.5">
|
|
254
|
+
{label && (
|
|
255
|
+
<label htmlFor={checkboxId} className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 cursor-pointer">
|
|
256
|
+
{label}
|
|
257
|
+
</label>
|
|
258
|
+
)}
|
|
259
|
+
{description && <p className="text-xs text-muted-foreground">{description}</p>}
|
|
260
|
+
</div>
|
|
261
|
+
)}
|
|
262
|
+
</div>
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
);
|
|
266
|
+
Checkbox.displayName = "Checkbox";
|
|
267
|
+
|
|
268
|
+
// ─── RadioGroup ───────────────────────────────────────────────────────────
|
|
269
|
+
|
|
270
|
+
export interface RadioOption {
|
|
271
|
+
value: string;
|
|
272
|
+
label: React.ReactNode;
|
|
273
|
+
description?: string;
|
|
274
|
+
disabled?: boolean;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
export interface RadioGroupProps extends React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Root> {
|
|
278
|
+
options?: RadioOption[];
|
|
279
|
+
orientation?: "horizontal" | "vertical";
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
const RadioGroup = React.forwardRef<React.ElementRef<typeof RadioGroupPrimitive.Root>, RadioGroupProps>(
|
|
283
|
+
({ className, options, orientation = "vertical", children, ...props }, ref) => (
|
|
284
|
+
<RadioGroupPrimitive.Root
|
|
285
|
+
ref={ref}
|
|
286
|
+
className={cn(
|
|
287
|
+
"atlas-radio-group",
|
|
288
|
+
orientation === "vertical" ? "flex flex-col gap-2" : "flex flex-row flex-wrap gap-4",
|
|
289
|
+
className
|
|
290
|
+
)}
|
|
291
|
+
{...props}
|
|
292
|
+
>
|
|
293
|
+
{options
|
|
294
|
+
? options.map((option) => (
|
|
295
|
+
<div key={option.value} className="flex items-start gap-2.5">
|
|
296
|
+
<RadioGroupPrimitive.Item
|
|
297
|
+
value={option.value}
|
|
298
|
+
disabled={option.disabled}
|
|
299
|
+
className={cn(
|
|
300
|
+
"mt-0.5 h-4 w-4 rounded-full border border-primary shrink-0",
|
|
301
|
+
"ring-offset-background transition-colors",
|
|
302
|
+
"focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
|
303
|
+
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
304
|
+
"data-[state=checked]:border-primary"
|
|
305
|
+
)}
|
|
306
|
+
>
|
|
307
|
+
<RadioGroupPrimitive.Indicator className="flex items-center justify-center">
|
|
308
|
+
<span className="h-2 w-2 rounded-full bg-primary" />
|
|
309
|
+
</RadioGroupPrimitive.Indicator>
|
|
310
|
+
</RadioGroupPrimitive.Item>
|
|
311
|
+
<div>
|
|
312
|
+
<label className="text-sm font-medium cursor-pointer">{option.label}</label>
|
|
313
|
+
{option.description && <p className="text-xs text-muted-foreground">{option.description}</p>}
|
|
314
|
+
</div>
|
|
315
|
+
</div>
|
|
316
|
+
))
|
|
317
|
+
: children}
|
|
318
|
+
</RadioGroupPrimitive.Root>
|
|
319
|
+
)
|
|
320
|
+
);
|
|
321
|
+
RadioGroup.displayName = "RadioGroup";
|
|
322
|
+
|
|
323
|
+
// ─── Switch ───────────────────────────────────────────────────────────────
|
|
324
|
+
|
|
325
|
+
export interface SwitchProps extends React.ComponentPropsWithoutRef<typeof SwitchPrimitive.Root> {
|
|
326
|
+
label?: React.ReactNode;
|
|
327
|
+
description?: string;
|
|
328
|
+
size?: "sm" | "md" | "lg";
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
const switchSizes = {
|
|
332
|
+
sm: { root: "h-4 w-7", thumb: "h-3 w-3 data-[state=checked]:translate-x-3" },
|
|
333
|
+
md: { root: "h-5 w-9", thumb: "h-4 w-4 data-[state=checked]:translate-x-4" },
|
|
334
|
+
lg: { root: "h-6 w-11", thumb: "h-5 w-5 data-[state=checked]:translate-x-5" },
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
const Switch = React.forwardRef<React.ElementRef<typeof SwitchPrimitive.Root>, SwitchProps>(
|
|
338
|
+
({ className, label, description, size = "md", id, ...props }, ref) => {
|
|
339
|
+
const switchId = id ?? React.useId();
|
|
340
|
+
const sz = switchSizes[size];
|
|
341
|
+
|
|
342
|
+
return (
|
|
343
|
+
<div className="atlas-switch flex items-center gap-2.5">
|
|
344
|
+
<SwitchPrimitive.Root
|
|
345
|
+
id={switchId}
|
|
346
|
+
ref={ref}
|
|
347
|
+
className={cn(
|
|
348
|
+
"peer inline-flex shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent",
|
|
349
|
+
"transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
|
350
|
+
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
351
|
+
"bg-input data-[state=checked]:bg-primary",
|
|
352
|
+
sz.root,
|
|
353
|
+
className
|
|
354
|
+
)}
|
|
355
|
+
{...props}
|
|
356
|
+
>
|
|
357
|
+
<SwitchPrimitive.Thumb
|
|
358
|
+
className={cn(
|
|
359
|
+
"pointer-events-none block rounded-full bg-background shadow-lg ring-0",
|
|
360
|
+
"transition-transform translate-x-0",
|
|
361
|
+
sz.thumb
|
|
362
|
+
)}
|
|
363
|
+
/>
|
|
364
|
+
</SwitchPrimitive.Root>
|
|
365
|
+
{(label || description) && (
|
|
366
|
+
<div>
|
|
367
|
+
{label && <label htmlFor={switchId} className="text-sm font-medium cursor-pointer">{label}</label>}
|
|
368
|
+
{description && <p className="text-xs text-muted-foreground">{description}</p>}
|
|
369
|
+
</div>
|
|
370
|
+
)}
|
|
371
|
+
</div>
|
|
372
|
+
);
|
|
373
|
+
}
|
|
374
|
+
);
|
|
375
|
+
Switch.displayName = "Switch";
|
|
376
|
+
|
|
377
|
+
// ─── Slider ───────────────────────────────────────────────────────────────
|
|
378
|
+
|
|
379
|
+
const Slider = React.forwardRef<
|
|
380
|
+
React.ElementRef<typeof SliderPrimitive.Root>,
|
|
381
|
+
React.ComponentPropsWithoutRef<typeof SliderPrimitive.Root>
|
|
382
|
+
>(({ className, ...props }, ref) => (
|
|
383
|
+
<SliderPrimitive.Root
|
|
384
|
+
ref={ref}
|
|
385
|
+
className={cn("atlas-slider relative flex w-full touch-none select-none items-center", className)}
|
|
386
|
+
{...props}
|
|
387
|
+
>
|
|
388
|
+
<SliderPrimitive.Track className="relative h-1.5 w-full grow overflow-hidden rounded-full bg-secondary">
|
|
389
|
+
<SliderPrimitive.Range className="absolute h-full bg-primary" />
|
|
390
|
+
</SliderPrimitive.Track>
|
|
391
|
+
{(Array.isArray(props.value) ? props.value : props.defaultValue ?? [0]).map((_, i) => (
|
|
392
|
+
<SliderPrimitive.Thumb
|
|
393
|
+
key={i}
|
|
394
|
+
className={cn(
|
|
395
|
+
"block h-4 w-4 rounded-full border-2 border-primary bg-background shadow",
|
|
396
|
+
"ring-offset-background transition-colors",
|
|
397
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
|
398
|
+
"disabled:pointer-events-none disabled:opacity-50"
|
|
399
|
+
)}
|
|
400
|
+
/>
|
|
401
|
+
))}
|
|
402
|
+
</SliderPrimitive.Root>
|
|
403
|
+
));
|
|
404
|
+
Slider.displayName = "Slider";
|
|
405
|
+
|
|
406
|
+
// ─── RangeSlider ──────────────────────────────────────────────────────────
|
|
407
|
+
|
|
408
|
+
export type RangeSliderProps = React.ComponentPropsWithoutRef<typeof SliderPrimitive.Root>;
|
|
409
|
+
|
|
410
|
+
const RangeSlider = React.forwardRef<React.ElementRef<typeof SliderPrimitive.Root>, RangeSliderProps>(
|
|
411
|
+
({ className, defaultValue = [20, 80], ...props }, ref) => (
|
|
412
|
+
<Slider ref={ref} defaultValue={defaultValue} className={cn("atlas-range-slider", className)} {...props} />
|
|
413
|
+
)
|
|
414
|
+
);
|
|
415
|
+
RangeSlider.displayName = "RangeSlider";
|
|
416
|
+
|
|
417
|
+
// ─── DatePicker ───────────────────────────────────────────────────────────
|
|
418
|
+
|
|
419
|
+
export interface DatePickerProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "type" | "size"> {
|
|
420
|
+
label?: string;
|
|
421
|
+
invalid?: boolean;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
const DatePicker = React.forwardRef<HTMLInputElement, DatePickerProps>(
|
|
425
|
+
({ className, label, invalid, id, ...props }, ref) => {
|
|
426
|
+
const inputId = id ?? React.useId();
|
|
427
|
+
return (
|
|
428
|
+
<div className="atlas-date-picker grid gap-1.5 w-full">
|
|
429
|
+
{label && <label htmlFor={inputId} className="text-sm font-medium">{label}</label>}
|
|
430
|
+
<Input
|
|
431
|
+
ref={ref}
|
|
432
|
+
id={inputId}
|
|
433
|
+
type="date"
|
|
434
|
+
invalid={invalid}
|
|
435
|
+
className={className}
|
|
436
|
+
{...props}
|
|
437
|
+
/>
|
|
438
|
+
</div>
|
|
439
|
+
);
|
|
440
|
+
}
|
|
441
|
+
);
|
|
442
|
+
DatePicker.displayName = "DatePicker";
|
|
443
|
+
|
|
444
|
+
// ─── TimePicker ───────────────────────────────────────────────────────────
|
|
445
|
+
|
|
446
|
+
export interface TimePickerProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "type" | "size"> {
|
|
447
|
+
label?: string;
|
|
448
|
+
invalid?: boolean;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
const TimePicker = React.forwardRef<HTMLInputElement, TimePickerProps>(
|
|
452
|
+
({ className, label, invalid, id, ...props }, ref) => {
|
|
453
|
+
const inputId = id ?? React.useId();
|
|
454
|
+
return (
|
|
455
|
+
<div className="atlas-time-picker grid gap-1.5 w-full">
|
|
456
|
+
{label && <label htmlFor={inputId} className="text-sm font-medium">{label}</label>}
|
|
457
|
+
<Input ref={ref} id={inputId} type="time" invalid={invalid} className={className} {...props} />
|
|
458
|
+
</div>
|
|
459
|
+
);
|
|
460
|
+
}
|
|
461
|
+
);
|
|
462
|
+
TimePicker.displayName = "TimePicker";
|
|
463
|
+
|
|
464
|
+
export {
|
|
465
|
+
Input, inputVariants,
|
|
466
|
+
TextArea,
|
|
467
|
+
Select, SelectGroup, SelectValue, SelectTrigger, SelectContent,
|
|
468
|
+
SelectItem, SelectLabel, SelectSeparator,
|
|
469
|
+
Checkbox,
|
|
470
|
+
RadioGroup,
|
|
471
|
+
Switch,
|
|
472
|
+
Slider,
|
|
473
|
+
RangeSlider,
|
|
474
|
+
DatePicker,
|
|
475
|
+
TimePicker,
|
|
476
|
+
};
|