apex-design-cli 1.0.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/dist/index.d.ts +1 -0
- package/dist/index.js +747 -0
- package/package.json +57 -0
- package/registry/components/accordion.json +26 -0
- package/registry/components/alert.json +25 -0
- package/registry/components/avatar.json +26 -0
- package/registry/components/badge.json +24 -0
- package/registry/components/breadcrumb.json +28 -0
- package/registry/components/button.json +25 -0
- package/registry/components/card.json +28 -0
- package/registry/components/checkbox.json +23 -0
- package/registry/components/command.json +31 -0
- package/registry/components/dialog.json +32 -0
- package/registry/components/divider.json +22 -0
- package/registry/components/dropdown-menu.json +36 -0
- package/registry/components/empty-state.json +21 -0
- package/registry/components/error-message.json +20 -0
- package/registry/components/field-group.json +20 -0
- package/registry/components/helper-text.json +20 -0
- package/registry/components/input.json +21 -0
- package/registry/components/label.json +20 -0
- package/registry/components/progress.json +22 -0
- package/registry/components/radio.json +23 -0
- package/registry/components/select.json +32 -0
- package/registry/components/spinner.json +20 -0
- package/registry/components/switch.json +22 -0
- package/registry/components/table.json +27 -0
- package/registry/components/tabs.json +25 -0
- package/registry/components/textarea.json +21 -0
- package/registry/components/theme-toggler.json +24 -0
- package/registry/components/toast.json +31 -0
- package/registry/components/tooltip.json +26 -0
- package/registry/components/use-theme.json +19 -0
- package/registry/components/utils.json +21 -0
- package/registry/registry.json +35 -0
- package/registry/source/accordion.tsx +55 -0
- package/registry/source/alert.tsx +102 -0
- package/registry/source/avatar.tsx +137 -0
- package/registry/source/badge.tsx +38 -0
- package/registry/source/breadcrumb.tsx +109 -0
- package/registry/source/button.tsx +58 -0
- package/registry/source/card.tsx +108 -0
- package/registry/source/checkbox.tsx +170 -0
- package/registry/source/command.tsx +195 -0
- package/registry/source/dialog.tsx +133 -0
- package/registry/source/divider.tsx +84 -0
- package/registry/source/dropdown-menu.tsx +209 -0
- package/registry/source/empty-state.tsx +88 -0
- package/registry/source/error-message.tsx +49 -0
- package/registry/source/field-group.tsx +53 -0
- package/registry/source/helper-text.tsx +40 -0
- package/registry/source/input.tsx +219 -0
- package/registry/source/label.tsx +60 -0
- package/registry/source/progress.tsx +84 -0
- package/registry/source/radio.tsx +161 -0
- package/registry/source/select.tsx +278 -0
- package/registry/source/spinner.tsx +84 -0
- package/registry/source/switch.tsx +104 -0
- package/registry/source/table.tsx +116 -0
- package/registry/source/tabs.tsx +55 -0
- package/registry/source/textarea.tsx +129 -0
- package/registry/source/theme-toggler.tsx +94 -0
- package/registry/source/toast.tsx +166 -0
- package/registry/source/tooltip.tsx +55 -0
- package/registry/source/use-theme.tsx +102 -0
- package/registry/source/utils.ts +13 -0
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import * as SelectPrimitive from '@radix-ui/react-select';
|
|
3
|
+
import { Check, ChevronDown, ChevronUp } from 'lucide-react';
|
|
4
|
+
import { cn } from '../../lib/utils';
|
|
5
|
+
|
|
6
|
+
// ===================
|
|
7
|
+
// SELECT ROOT
|
|
8
|
+
// ===================
|
|
9
|
+
|
|
10
|
+
const Select = SelectPrimitive.Root;
|
|
11
|
+
const SelectGroup = SelectPrimitive.Group;
|
|
12
|
+
const SelectValue = SelectPrimitive.Value;
|
|
13
|
+
|
|
14
|
+
// ===================
|
|
15
|
+
// SELECT TRIGGER (Button que abre el dropdown)
|
|
16
|
+
// ===================
|
|
17
|
+
|
|
18
|
+
interface SelectTriggerProps extends React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger> {
|
|
19
|
+
size?: 'sm' | 'md' | 'lg';
|
|
20
|
+
error?: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const SelectTrigger = React.forwardRef<
|
|
24
|
+
React.ElementRef<typeof SelectPrimitive.Trigger>,
|
|
25
|
+
SelectTriggerProps
|
|
26
|
+
>(({ className, children, size = 'md', error = false, ...props }, ref) => {
|
|
27
|
+
const sizeClasses = {
|
|
28
|
+
sm: 'h-8 px-3 text-sm',
|
|
29
|
+
md: 'h-10 px-3 text-base',
|
|
30
|
+
lg: 'h-12 px-4 text-lg',
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<SelectPrimitive.Trigger
|
|
35
|
+
ref={ref}
|
|
36
|
+
className={cn(
|
|
37
|
+
'flex w-full items-center justify-between rounded-md border bg-semantic-control-bg',
|
|
38
|
+
'transition-colors',
|
|
39
|
+
'focus:outline-none focus:ring-2 focus:ring-offset-0',
|
|
40
|
+
'disabled:cursor-not-allowed disabled:bg-semantic-bg-disabled disabled:text-semantic-fg-disabled',
|
|
41
|
+
'placeholder:text-semantic-control-placeholder',
|
|
42
|
+
'[&>span]:line-clamp-1',
|
|
43
|
+
sizeClasses[size],
|
|
44
|
+
error
|
|
45
|
+
? 'border-semantic-control-border-error text-semantic-control-fg focus:ring-semantic-control-border-error focus:border-semantic-control-border-error'
|
|
46
|
+
: 'border-semantic-control-border text-semantic-control-fg focus:ring-semantic-focus focus:border-semantic-border-focus',
|
|
47
|
+
className
|
|
48
|
+
)}
|
|
49
|
+
{...props}
|
|
50
|
+
>
|
|
51
|
+
{children}
|
|
52
|
+
<SelectPrimitive.Icon asChild>
|
|
53
|
+
<ChevronDown className="h-4 w-4 text-semantic-control-icon" aria-hidden="true" />
|
|
54
|
+
</SelectPrimitive.Icon>
|
|
55
|
+
</SelectPrimitive.Trigger>
|
|
56
|
+
);
|
|
57
|
+
});
|
|
58
|
+
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
|
|
59
|
+
|
|
60
|
+
// ===================
|
|
61
|
+
// SELECT SCROLL BUTTONS
|
|
62
|
+
// ===================
|
|
63
|
+
|
|
64
|
+
const SelectScrollUpButton = React.forwardRef<
|
|
65
|
+
React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,
|
|
66
|
+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>
|
|
67
|
+
>(({ className, ...props }, ref) => (
|
|
68
|
+
<SelectPrimitive.ScrollUpButton
|
|
69
|
+
ref={ref}
|
|
70
|
+
className={cn('flex cursor-default items-center justify-center py-1', className)}
|
|
71
|
+
aria-hidden="true"
|
|
72
|
+
{...props}
|
|
73
|
+
>
|
|
74
|
+
<ChevronUp className="h-4 w-4" />
|
|
75
|
+
</SelectPrimitive.ScrollUpButton>
|
|
76
|
+
));
|
|
77
|
+
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
|
|
78
|
+
|
|
79
|
+
const SelectScrollDownButton = React.forwardRef<
|
|
80
|
+
React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,
|
|
81
|
+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>
|
|
82
|
+
>(({ className, ...props }, ref) => (
|
|
83
|
+
<SelectPrimitive.ScrollDownButton
|
|
84
|
+
ref={ref}
|
|
85
|
+
className={cn('flex cursor-default items-center justify-center py-1', className)}
|
|
86
|
+
aria-hidden="true"
|
|
87
|
+
{...props}
|
|
88
|
+
>
|
|
89
|
+
<ChevronDown className="h-4 w-4" />
|
|
90
|
+
</SelectPrimitive.ScrollDownButton>
|
|
91
|
+
));
|
|
92
|
+
SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName;
|
|
93
|
+
|
|
94
|
+
// ===================
|
|
95
|
+
// SELECT CONTENT (Dropdown panel)
|
|
96
|
+
// ===================
|
|
97
|
+
|
|
98
|
+
const SelectContent = React.forwardRef<
|
|
99
|
+
React.ElementRef<typeof SelectPrimitive.Content>,
|
|
100
|
+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
|
|
101
|
+
>(({ className, children, position = 'popper', ...props }, ref) => (
|
|
102
|
+
<SelectPrimitive.Portal>
|
|
103
|
+
<SelectPrimitive.Content
|
|
104
|
+
ref={ref}
|
|
105
|
+
className={cn(
|
|
106
|
+
'relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border border-semantic-border-default bg-semantic-bg-elevated text-semantic-fg-primary shadow-md',
|
|
107
|
+
'data-[state=open]:animate-in data-[state=closed]:animate-out',
|
|
108
|
+
'data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
|
|
109
|
+
'data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95',
|
|
110
|
+
'data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2',
|
|
111
|
+
'data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
|
112
|
+
position === 'popper' &&
|
|
113
|
+
'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',
|
|
114
|
+
className
|
|
115
|
+
)}
|
|
116
|
+
position={position}
|
|
117
|
+
{...props}
|
|
118
|
+
>
|
|
119
|
+
<SelectScrollUpButton />
|
|
120
|
+
<SelectPrimitive.Viewport
|
|
121
|
+
className={cn(
|
|
122
|
+
'p-1',
|
|
123
|
+
position === 'popper' &&
|
|
124
|
+
'h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]'
|
|
125
|
+
)}
|
|
126
|
+
>
|
|
127
|
+
{children}
|
|
128
|
+
</SelectPrimitive.Viewport>
|
|
129
|
+
<SelectScrollDownButton />
|
|
130
|
+
</SelectPrimitive.Content>
|
|
131
|
+
</SelectPrimitive.Portal>
|
|
132
|
+
));
|
|
133
|
+
SelectContent.displayName = SelectPrimitive.Content.displayName;
|
|
134
|
+
|
|
135
|
+
// ===================
|
|
136
|
+
// SELECT LABEL (Heading dentro del dropdown)
|
|
137
|
+
// ===================
|
|
138
|
+
|
|
139
|
+
const SelectLabel = React.forwardRef<
|
|
140
|
+
React.ElementRef<typeof SelectPrimitive.Label>,
|
|
141
|
+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
|
|
142
|
+
>(({ className, ...props }, ref) => (
|
|
143
|
+
<SelectPrimitive.Label
|
|
144
|
+
ref={ref}
|
|
145
|
+
className={cn('py-1.5 pl-8 pr-2 text-sm font-semibold text-semantic-fg-primary', className)}
|
|
146
|
+
{...props}
|
|
147
|
+
/>
|
|
148
|
+
));
|
|
149
|
+
SelectLabel.displayName = SelectPrimitive.Label.displayName;
|
|
150
|
+
|
|
151
|
+
// ===================
|
|
152
|
+
// SELECT ITEM (Cada opción)
|
|
153
|
+
// ===================
|
|
154
|
+
|
|
155
|
+
const SelectItem = React.forwardRef<
|
|
156
|
+
React.ElementRef<typeof SelectPrimitive.Item>,
|
|
157
|
+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
|
|
158
|
+
>(({ className, children, ...props }, ref) => (
|
|
159
|
+
<SelectPrimitive.Item
|
|
160
|
+
ref={ref}
|
|
161
|
+
className={cn(
|
|
162
|
+
'relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none',
|
|
163
|
+
'focus:bg-semantic-bg-hover focus:text-semantic-fg-primary',
|
|
164
|
+
'data-[disabled]:pointer-events-none data-[disabled]:text-semantic-fg-disabled',
|
|
165
|
+
className
|
|
166
|
+
)}
|
|
167
|
+
{...props}
|
|
168
|
+
>
|
|
169
|
+
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center" aria-hidden="true">
|
|
170
|
+
<SelectPrimitive.ItemIndicator>
|
|
171
|
+
<Check className="h-4 w-4" />
|
|
172
|
+
</SelectPrimitive.ItemIndicator>
|
|
173
|
+
</span>
|
|
174
|
+
|
|
175
|
+
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
|
|
176
|
+
</SelectPrimitive.Item>
|
|
177
|
+
));
|
|
178
|
+
SelectItem.displayName = SelectPrimitive.Item.displayName;
|
|
179
|
+
|
|
180
|
+
// ===================
|
|
181
|
+
// SELECT SEPARATOR
|
|
182
|
+
// ===================
|
|
183
|
+
|
|
184
|
+
const SelectSeparator = React.forwardRef<
|
|
185
|
+
React.ElementRef<typeof SelectPrimitive.Separator>,
|
|
186
|
+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
|
|
187
|
+
>(({ className, ...props }, ref) => (
|
|
188
|
+
<SelectPrimitive.Separator
|
|
189
|
+
ref={ref}
|
|
190
|
+
className={cn('-mx-1 my-1 h-px bg-semantic-border-default', className)}
|
|
191
|
+
{...props}
|
|
192
|
+
/>
|
|
193
|
+
));
|
|
194
|
+
SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
|
|
195
|
+
|
|
196
|
+
// ===================
|
|
197
|
+
// SELECT WRAPPER (Componente conveniente con label y helper text)
|
|
198
|
+
// ===================
|
|
199
|
+
|
|
200
|
+
export interface SelectWrapperProps {
|
|
201
|
+
/** Label text */
|
|
202
|
+
label?: string;
|
|
203
|
+
/** Helper text shown below select */
|
|
204
|
+
helperText?: string;
|
|
205
|
+
/** Error message shown when error={true} */
|
|
206
|
+
errorMessage?: string;
|
|
207
|
+
/** Error state */
|
|
208
|
+
error?: boolean;
|
|
209
|
+
/** Mark as required */
|
|
210
|
+
required?: boolean;
|
|
211
|
+
/** Select size */
|
|
212
|
+
size?: 'sm' | 'md' | 'lg';
|
|
213
|
+
/** Children (must be Select components) */
|
|
214
|
+
children: React.ReactNode;
|
|
215
|
+
/** Container class name */
|
|
216
|
+
className?: string;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const SelectWrapper: React.FC<SelectWrapperProps> = ({
|
|
220
|
+
label,
|
|
221
|
+
helperText,
|
|
222
|
+
errorMessage,
|
|
223
|
+
error = false,
|
|
224
|
+
required = false,
|
|
225
|
+
size: _size = 'md',
|
|
226
|
+
children,
|
|
227
|
+
className,
|
|
228
|
+
}) => {
|
|
229
|
+
const selectId = React.useId();
|
|
230
|
+
const helperTextId = `${selectId}-helper`;
|
|
231
|
+
const errorMessageId = `${selectId}-error`;
|
|
232
|
+
|
|
233
|
+
return (
|
|
234
|
+
<div className={cn('w-full', className)}>
|
|
235
|
+
{label && (
|
|
236
|
+
<label
|
|
237
|
+
htmlFor={selectId}
|
|
238
|
+
className="mb-1.5 block text-sm font-medium text-semantic-fg-secondary"
|
|
239
|
+
>
|
|
240
|
+
{label}
|
|
241
|
+
{required && <span className="ml-1 text-semantic-fg-error">*</span>}
|
|
242
|
+
</label>
|
|
243
|
+
)}
|
|
244
|
+
|
|
245
|
+
{children}
|
|
246
|
+
|
|
247
|
+
{!error && helperText && (
|
|
248
|
+
<p id={helperTextId} className="mt-1.5 text-sm text-semantic-fg-secondary">
|
|
249
|
+
{helperText}
|
|
250
|
+
</p>
|
|
251
|
+
)}
|
|
252
|
+
|
|
253
|
+
{error && errorMessage && (
|
|
254
|
+
<p id={errorMessageId} className="mt-1.5 text-sm text-semantic-fg-error">
|
|
255
|
+
{errorMessage}
|
|
256
|
+
</p>
|
|
257
|
+
)}
|
|
258
|
+
</div>
|
|
259
|
+
);
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
// ===================
|
|
263
|
+
// EXPORTS
|
|
264
|
+
// ===================
|
|
265
|
+
|
|
266
|
+
export {
|
|
267
|
+
Select,
|
|
268
|
+
SelectGroup,
|
|
269
|
+
SelectValue,
|
|
270
|
+
SelectTrigger,
|
|
271
|
+
SelectContent,
|
|
272
|
+
SelectLabel,
|
|
273
|
+
SelectItem,
|
|
274
|
+
SelectSeparator,
|
|
275
|
+
SelectScrollUpButton,
|
|
276
|
+
SelectScrollDownButton,
|
|
277
|
+
SelectWrapper,
|
|
278
|
+
};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { cva, type VariantProps } from 'class-variance-authority';
|
|
3
|
+
import { cn } from '../../lib/utils';
|
|
4
|
+
import { Loader2 } from 'lucide-react';
|
|
5
|
+
|
|
6
|
+
const spinnerVariants = cva('animate-spin', {
|
|
7
|
+
variants: {
|
|
8
|
+
size: {
|
|
9
|
+
sm: 'h-4 w-4',
|
|
10
|
+
md: 'h-6 w-6',
|
|
11
|
+
lg: 'h-8 w-8',
|
|
12
|
+
xl: 'h-12 w-12',
|
|
13
|
+
},
|
|
14
|
+
variant: {
|
|
15
|
+
default: 'text-semantic-fg-primary',
|
|
16
|
+
primary: 'text-semantic-action-primary',
|
|
17
|
+
secondary: 'text-semantic-fg-secondary',
|
|
18
|
+
light: 'text-semantic-fg-inverse',
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
defaultVariants: {
|
|
22
|
+
size: 'md',
|
|
23
|
+
variant: 'default',
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
export interface SpinnerProps
|
|
28
|
+
extends Omit<React.HTMLAttributes<HTMLDivElement>, 'children'>,
|
|
29
|
+
VariantProps<typeof spinnerVariants> {
|
|
30
|
+
label?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const Spinner = React.forwardRef<HTMLDivElement, SpinnerProps>(
|
|
34
|
+
({ className, size, variant, label, ...props }, ref) => {
|
|
35
|
+
const labelColorClass = variant === 'light'
|
|
36
|
+
? 'text-semantic-fg-inverse'
|
|
37
|
+
: 'text-semantic-fg-secondary';
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<div
|
|
41
|
+
ref={ref}
|
|
42
|
+
role="status"
|
|
43
|
+
aria-label={label || 'Loading'}
|
|
44
|
+
className={cn('inline-flex items-center gap-2', className)}
|
|
45
|
+
{...props}
|
|
46
|
+
>
|
|
47
|
+
<Loader2 className={cn(spinnerVariants({ size, variant }))} />
|
|
48
|
+
{label && (
|
|
49
|
+
<span className={cn('text-sm', labelColorClass)}>{label}</span>
|
|
50
|
+
)}
|
|
51
|
+
<span className="sr-only">{label || 'Loading'}</span>
|
|
52
|
+
</div>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
);
|
|
56
|
+
Spinner.displayName = 'Spinner';
|
|
57
|
+
|
|
58
|
+
export interface LoadingOverlayProps extends SpinnerProps {
|
|
59
|
+
show?: boolean;
|
|
60
|
+
transparent?: boolean;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const LoadingOverlay = React.forwardRef<HTMLDivElement, LoadingOverlayProps>(
|
|
64
|
+
({ show = true, transparent = false, label = 'Loading...', ...spinnerProps }, ref) => {
|
|
65
|
+
if (!show) return null;
|
|
66
|
+
|
|
67
|
+
return (
|
|
68
|
+
<div
|
|
69
|
+
ref={ref}
|
|
70
|
+
className={cn(
|
|
71
|
+
'fixed inset-0 z-50 flex items-center justify-center',
|
|
72
|
+
transparent
|
|
73
|
+
? 'bg-transparent'
|
|
74
|
+
: 'bg-semantic-bg-loading-overlay backdrop-blur-sm'
|
|
75
|
+
)}
|
|
76
|
+
>
|
|
77
|
+
<Spinner label={label} {...spinnerProps} />
|
|
78
|
+
</div>
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
);
|
|
82
|
+
LoadingOverlay.displayName = 'LoadingOverlay';
|
|
83
|
+
|
|
84
|
+
export { Spinner, LoadingOverlay, spinnerVariants };
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import * as SwitchPrimitive from '@radix-ui/react-switch';
|
|
3
|
+
import { cn } from '../../lib/utils';
|
|
4
|
+
|
|
5
|
+
const SwitchRoot = React.forwardRef<
|
|
6
|
+
React.ElementRef<typeof SwitchPrimitive.Root>,
|
|
7
|
+
React.ComponentPropsWithoutRef<typeof SwitchPrimitive.Root> & {
|
|
8
|
+
size?: 'sm' | 'md' | 'lg';
|
|
9
|
+
}
|
|
10
|
+
>(({ className, size = 'md', ...props }, ref) => {
|
|
11
|
+
const sizeClasses = {
|
|
12
|
+
sm: 'h-5 w-9',
|
|
13
|
+
md: 'h-6 w-11',
|
|
14
|
+
lg: 'h-7 w-14',
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const thumbSizeClasses = {
|
|
18
|
+
sm: 'h-4 w-4 data-[state=checked]:translate-x-4',
|
|
19
|
+
md: 'h-5 w-5 data-[state=checked]:translate-x-5',
|
|
20
|
+
lg: 'h-6 w-6 data-[state=checked]:translate-x-7',
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<SwitchPrimitive.Root
|
|
25
|
+
className={cn(
|
|
26
|
+
'peer inline-flex shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent',
|
|
27
|
+
'transition-colors',
|
|
28
|
+
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-semantic-focus focus-visible:ring-offset-2',
|
|
29
|
+
'disabled:cursor-not-allowed disabled:bg-semantic-bg-disabled',
|
|
30
|
+
'data-[state=checked]:bg-semantic-control-bg-checked data-[state=unchecked]:bg-semantic-control-unchecked',
|
|
31
|
+
sizeClasses[size],
|
|
32
|
+
className
|
|
33
|
+
)}
|
|
34
|
+
{...props}
|
|
35
|
+
ref={ref}
|
|
36
|
+
>
|
|
37
|
+
<SwitchPrimitive.Thumb
|
|
38
|
+
className={cn(
|
|
39
|
+
'pointer-events-none block rounded-full bg-white shadow-lg ring-0 transition-transform',
|
|
40
|
+
'data-[state=unchecked]:translate-x-0',
|
|
41
|
+
thumbSizeClasses[size]
|
|
42
|
+
)}
|
|
43
|
+
/>
|
|
44
|
+
</SwitchPrimitive.Root>
|
|
45
|
+
);
|
|
46
|
+
});
|
|
47
|
+
SwitchRoot.displayName = SwitchPrimitive.Root.displayName;
|
|
48
|
+
|
|
49
|
+
export interface SwitchProps
|
|
50
|
+
extends React.ComponentPropsWithoutRef<typeof SwitchPrimitive.Root> {
|
|
51
|
+
label?: string;
|
|
52
|
+
description?: string;
|
|
53
|
+
size?: 'sm' | 'md' | 'lg';
|
|
54
|
+
labelPosition?: 'left' | 'right';
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const Switch = React.forwardRef<React.ElementRef<typeof SwitchPrimitive.Root>, SwitchProps>(
|
|
58
|
+
({ className, label, description, size = 'md', labelPosition = 'right', id, ...props }, ref) => {
|
|
59
|
+
const switchId = id || React.useId();
|
|
60
|
+
|
|
61
|
+
const switchElement = <SwitchRoot ref={ref} id={switchId} size={size} {...props} />;
|
|
62
|
+
|
|
63
|
+
if (!label) {
|
|
64
|
+
return switchElement;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return (
|
|
68
|
+
<div className={cn('flex items-start gap-3', className)}>
|
|
69
|
+
{labelPosition === 'left' && (
|
|
70
|
+
<div className="flex-1">
|
|
71
|
+
<label
|
|
72
|
+
htmlFor={switchId}
|
|
73
|
+
className="cursor-pointer select-none text-sm font-medium leading-none text-semantic-fg-primary peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
|
74
|
+
>
|
|
75
|
+
{label}
|
|
76
|
+
</label>
|
|
77
|
+
{description && (
|
|
78
|
+
<p className="mt-1 text-sm text-semantic-fg-secondary">{description}</p>
|
|
79
|
+
)}
|
|
80
|
+
</div>
|
|
81
|
+
)}
|
|
82
|
+
|
|
83
|
+
{switchElement}
|
|
84
|
+
|
|
85
|
+
{labelPosition === 'right' && (
|
|
86
|
+
<div className="flex-1">
|
|
87
|
+
<label
|
|
88
|
+
htmlFor={switchId}
|
|
89
|
+
className="cursor-pointer select-none text-sm font-medium leading-none text-semantic-fg-primary peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
|
90
|
+
>
|
|
91
|
+
{label}
|
|
92
|
+
</label>
|
|
93
|
+
{description && (
|
|
94
|
+
<p className="mt-1 text-sm text-semantic-fg-secondary">{description}</p>
|
|
95
|
+
)}
|
|
96
|
+
</div>
|
|
97
|
+
)}
|
|
98
|
+
</div>
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
);
|
|
102
|
+
Switch.displayName = 'Switch';
|
|
103
|
+
|
|
104
|
+
export { Switch, SwitchRoot };
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { cn } from '../../lib/utils';
|
|
3
|
+
|
|
4
|
+
const Table = React.forwardRef<
|
|
5
|
+
HTMLTableElement,
|
|
6
|
+
React.HTMLAttributes<HTMLTableElement>
|
|
7
|
+
>(({ className, ...props }, ref) => (
|
|
8
|
+
<div className="relative w-full overflow-auto">
|
|
9
|
+
<table
|
|
10
|
+
ref={ref}
|
|
11
|
+
className={cn('w-full caption-bottom text-sm', className)}
|
|
12
|
+
{...props}
|
|
13
|
+
/>
|
|
14
|
+
</div>
|
|
15
|
+
));
|
|
16
|
+
Table.displayName = 'Table';
|
|
17
|
+
|
|
18
|
+
const TableHeader = React.forwardRef<
|
|
19
|
+
HTMLTableSectionElement,
|
|
20
|
+
React.HTMLAttributes<HTMLTableSectionElement>
|
|
21
|
+
>(({ className, ...props }, ref) => (
|
|
22
|
+
<thead ref={ref} className={cn('[&_tr]:border-b', className)} {...props} />
|
|
23
|
+
));
|
|
24
|
+
TableHeader.displayName = 'TableHeader';
|
|
25
|
+
|
|
26
|
+
const TableBody = React.forwardRef<
|
|
27
|
+
HTMLTableSectionElement,
|
|
28
|
+
React.HTMLAttributes<HTMLTableSectionElement>
|
|
29
|
+
>(({ className, ...props }, ref) => (
|
|
30
|
+
<tbody
|
|
31
|
+
ref={ref}
|
|
32
|
+
className={cn('[&_tr:last-child]:border-0', className)}
|
|
33
|
+
{...props}
|
|
34
|
+
/>
|
|
35
|
+
));
|
|
36
|
+
TableBody.displayName = 'TableBody';
|
|
37
|
+
|
|
38
|
+
const TableFooter = React.forwardRef<
|
|
39
|
+
HTMLTableSectionElement,
|
|
40
|
+
React.HTMLAttributes<HTMLTableSectionElement>
|
|
41
|
+
>(({ className, ...props }, ref) => (
|
|
42
|
+
<tfoot
|
|
43
|
+
ref={ref}
|
|
44
|
+
className={cn(
|
|
45
|
+
'border-t bg-semantic-bg-sunken font-medium [&>tr]:last:border-b-0',
|
|
46
|
+
className
|
|
47
|
+
)}
|
|
48
|
+
{...props}
|
|
49
|
+
/>
|
|
50
|
+
));
|
|
51
|
+
TableFooter.displayName = 'TableFooter';
|
|
52
|
+
|
|
53
|
+
const TableRow = React.forwardRef<
|
|
54
|
+
HTMLTableRowElement,
|
|
55
|
+
React.HTMLAttributes<HTMLTableRowElement>
|
|
56
|
+
>(({ className, ...props }, ref) => (
|
|
57
|
+
<tr
|
|
58
|
+
ref={ref}
|
|
59
|
+
className={cn(
|
|
60
|
+
'border-b border-semantic-border-default transition-colors hover:bg-semantic-bg-hover data-[state=selected]:bg-semantic-bg-selected',
|
|
61
|
+
className
|
|
62
|
+
)}
|
|
63
|
+
{...props}
|
|
64
|
+
/>
|
|
65
|
+
));
|
|
66
|
+
TableRow.displayName = 'TableRow';
|
|
67
|
+
|
|
68
|
+
const TableHead = React.forwardRef<
|
|
69
|
+
HTMLTableCellElement,
|
|
70
|
+
React.ThHTMLAttributes<HTMLTableCellElement>
|
|
71
|
+
>(({ className, ...props }, ref) => (
|
|
72
|
+
<th
|
|
73
|
+
ref={ref}
|
|
74
|
+
className={cn(
|
|
75
|
+
'h-12 px-4 text-left align-middle font-medium text-semantic-fg-secondary [&:has([role=checkbox])]:pr-0',
|
|
76
|
+
className
|
|
77
|
+
)}
|
|
78
|
+
{...props}
|
|
79
|
+
/>
|
|
80
|
+
));
|
|
81
|
+
TableHead.displayName = 'TableHead';
|
|
82
|
+
|
|
83
|
+
const TableCell = React.forwardRef<
|
|
84
|
+
HTMLTableCellElement,
|
|
85
|
+
React.TdHTMLAttributes<HTMLTableCellElement>
|
|
86
|
+
>(({ className, ...props }, ref) => (
|
|
87
|
+
<td
|
|
88
|
+
ref={ref}
|
|
89
|
+
className={cn('p-4 align-middle [&:has([role=checkbox])]:pr-0', className)}
|
|
90
|
+
{...props}
|
|
91
|
+
/>
|
|
92
|
+
));
|
|
93
|
+
TableCell.displayName = 'TableCell';
|
|
94
|
+
|
|
95
|
+
const TableCaption = React.forwardRef<
|
|
96
|
+
HTMLTableCaptionElement,
|
|
97
|
+
React.HTMLAttributes<HTMLTableCaptionElement>
|
|
98
|
+
>(({ className, ...props }, ref) => (
|
|
99
|
+
<caption
|
|
100
|
+
ref={ref}
|
|
101
|
+
className={cn('mt-4 text-sm text-semantic-fg-secondary', className)}
|
|
102
|
+
{...props}
|
|
103
|
+
/>
|
|
104
|
+
));
|
|
105
|
+
TableCaption.displayName = 'TableCaption';
|
|
106
|
+
|
|
107
|
+
export {
|
|
108
|
+
Table,
|
|
109
|
+
TableHeader,
|
|
110
|
+
TableBody,
|
|
111
|
+
TableFooter,
|
|
112
|
+
TableHead,
|
|
113
|
+
TableRow,
|
|
114
|
+
TableCell,
|
|
115
|
+
TableCaption,
|
|
116
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import * as TabsPrimitive from '@radix-ui/react-tabs';
|
|
3
|
+
import { cn } from '../../lib/utils';
|
|
4
|
+
|
|
5
|
+
const Tabs = TabsPrimitive.Root;
|
|
6
|
+
|
|
7
|
+
const TabsList = React.forwardRef<
|
|
8
|
+
React.ElementRef<typeof TabsPrimitive.List>,
|
|
9
|
+
React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>
|
|
10
|
+
>(({ className, ...props }, ref) => (
|
|
11
|
+
<TabsPrimitive.List
|
|
12
|
+
ref={ref}
|
|
13
|
+
className={cn(
|
|
14
|
+
'inline-flex h-10 items-center justify-center rounded-md bg-semantic-bg-sunken p-1 text-semantic-fg-muted',
|
|
15
|
+
className
|
|
16
|
+
)}
|
|
17
|
+
{...props}
|
|
18
|
+
/>
|
|
19
|
+
));
|
|
20
|
+
TabsList.displayName = TabsPrimitive.List.displayName;
|
|
21
|
+
|
|
22
|
+
const TabsTrigger = React.forwardRef<
|
|
23
|
+
React.ElementRef<typeof TabsPrimitive.Trigger>,
|
|
24
|
+
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>
|
|
25
|
+
>(({ className, ...props }, ref) => (
|
|
26
|
+
<TabsPrimitive.Trigger
|
|
27
|
+
ref={ref}
|
|
28
|
+
className={cn(
|
|
29
|
+
'inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-semantic-offset transition-all',
|
|
30
|
+
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-semantic-focus focus-visible:ring-offset-2',
|
|
31
|
+
'disabled:pointer-events-none disabled:opacity-50',
|
|
32
|
+
'data-[state=active]:bg-semantic-bg-elevated data-[state=active]:text-semantic-fg-primary data-[state=active]:shadow-sm',
|
|
33
|
+
className
|
|
34
|
+
)}
|
|
35
|
+
{...props}
|
|
36
|
+
/>
|
|
37
|
+
));
|
|
38
|
+
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName;
|
|
39
|
+
|
|
40
|
+
const TabsContent = React.forwardRef<
|
|
41
|
+
React.ElementRef<typeof TabsPrimitive.Content>,
|
|
42
|
+
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>
|
|
43
|
+
>(({ className, ...props }, ref) => (
|
|
44
|
+
<TabsPrimitive.Content
|
|
45
|
+
ref={ref}
|
|
46
|
+
className={cn(
|
|
47
|
+
'mt-2 ring-offset-semantic-offset focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-semantic-focus focus-visible:ring-offset-2',
|
|
48
|
+
className
|
|
49
|
+
)}
|
|
50
|
+
{...props}
|
|
51
|
+
/>
|
|
52
|
+
));
|
|
53
|
+
TabsContent.displayName = TabsPrimitive.Content.displayName;
|
|
54
|
+
|
|
55
|
+
export { Tabs, TabsList, TabsTrigger, TabsContent };
|