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.
Files changed (66) hide show
  1. package/dist/index.d.ts +1 -0
  2. package/dist/index.js +747 -0
  3. package/package.json +57 -0
  4. package/registry/components/accordion.json +26 -0
  5. package/registry/components/alert.json +25 -0
  6. package/registry/components/avatar.json +26 -0
  7. package/registry/components/badge.json +24 -0
  8. package/registry/components/breadcrumb.json +28 -0
  9. package/registry/components/button.json +25 -0
  10. package/registry/components/card.json +28 -0
  11. package/registry/components/checkbox.json +23 -0
  12. package/registry/components/command.json +31 -0
  13. package/registry/components/dialog.json +32 -0
  14. package/registry/components/divider.json +22 -0
  15. package/registry/components/dropdown-menu.json +36 -0
  16. package/registry/components/empty-state.json +21 -0
  17. package/registry/components/error-message.json +20 -0
  18. package/registry/components/field-group.json +20 -0
  19. package/registry/components/helper-text.json +20 -0
  20. package/registry/components/input.json +21 -0
  21. package/registry/components/label.json +20 -0
  22. package/registry/components/progress.json +22 -0
  23. package/registry/components/radio.json +23 -0
  24. package/registry/components/select.json +32 -0
  25. package/registry/components/spinner.json +20 -0
  26. package/registry/components/switch.json +22 -0
  27. package/registry/components/table.json +27 -0
  28. package/registry/components/tabs.json +25 -0
  29. package/registry/components/textarea.json +21 -0
  30. package/registry/components/theme-toggler.json +24 -0
  31. package/registry/components/toast.json +31 -0
  32. package/registry/components/tooltip.json +26 -0
  33. package/registry/components/use-theme.json +19 -0
  34. package/registry/components/utils.json +21 -0
  35. package/registry/registry.json +35 -0
  36. package/registry/source/accordion.tsx +55 -0
  37. package/registry/source/alert.tsx +102 -0
  38. package/registry/source/avatar.tsx +137 -0
  39. package/registry/source/badge.tsx +38 -0
  40. package/registry/source/breadcrumb.tsx +109 -0
  41. package/registry/source/button.tsx +58 -0
  42. package/registry/source/card.tsx +108 -0
  43. package/registry/source/checkbox.tsx +170 -0
  44. package/registry/source/command.tsx +195 -0
  45. package/registry/source/dialog.tsx +133 -0
  46. package/registry/source/divider.tsx +84 -0
  47. package/registry/source/dropdown-menu.tsx +209 -0
  48. package/registry/source/empty-state.tsx +88 -0
  49. package/registry/source/error-message.tsx +49 -0
  50. package/registry/source/field-group.tsx +53 -0
  51. package/registry/source/helper-text.tsx +40 -0
  52. package/registry/source/input.tsx +219 -0
  53. package/registry/source/label.tsx +60 -0
  54. package/registry/source/progress.tsx +84 -0
  55. package/registry/source/radio.tsx +161 -0
  56. package/registry/source/select.tsx +278 -0
  57. package/registry/source/spinner.tsx +84 -0
  58. package/registry/source/switch.tsx +104 -0
  59. package/registry/source/table.tsx +116 -0
  60. package/registry/source/tabs.tsx +55 -0
  61. package/registry/source/textarea.tsx +129 -0
  62. package/registry/source/theme-toggler.tsx +94 -0
  63. package/registry/source/toast.tsx +166 -0
  64. package/registry/source/tooltip.tsx +55 -0
  65. package/registry/source/use-theme.tsx +102 -0
  66. package/registry/source/utils.ts +13 -0
@@ -0,0 +1,219 @@
1
+ import React from 'react';
2
+
3
+ export interface InputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'size'> {
4
+ /**
5
+ * Visual style variant of the input
6
+ */
7
+ variant?: 'default' | 'error';
8
+
9
+ /**
10
+ * Size of the input field
11
+ */
12
+ size?: 'sm' | 'md' | 'lg';
13
+
14
+ /**
15
+ * Label text displayed above the input
16
+ */
17
+ label?: string;
18
+
19
+ /**
20
+ * Helper text displayed below the input
21
+ */
22
+ helperText?: string;
23
+
24
+ /**
25
+ * Error message displayed below the input
26
+ */
27
+ errorMessage?: string;
28
+
29
+ /**
30
+ * Icon to display on the left side of the input
31
+ */
32
+ leftIcon?: React.ReactNode;
33
+
34
+ /**
35
+ * Icon to display on the right side of the input
36
+ */
37
+ rightIcon?: React.ReactNode;
38
+
39
+ /**
40
+ * Whether the field is required
41
+ */
42
+ required?: boolean;
43
+ }
44
+
45
+ const Input = React.forwardRef<HTMLInputElement, InputProps>(
46
+ (
47
+ {
48
+ variant = 'default',
49
+ size = 'md',
50
+ label,
51
+ helperText,
52
+ errorMessage,
53
+ leftIcon,
54
+ rightIcon,
55
+ required,
56
+ disabled,
57
+ className = '',
58
+ id,
59
+ ...props
60
+ },
61
+ ref
62
+ ) => {
63
+ const generatedId = React.useId();
64
+ const inputId = id || generatedId;
65
+ // Determine if we should show error state
66
+ const hasError = variant === 'error' || !!errorMessage;
67
+
68
+ // Base styles for the input container
69
+ const containerStyles = 'flex flex-col gap-1.5';
70
+
71
+ // Label styles
72
+ const labelStyles = 'text-sm font-medium text-semantic-fg-secondary';
73
+
74
+ // Input wrapper styles (for icon support)
75
+ const wrapperStyles = 'relative flex items-center';
76
+
77
+ // Base input styles
78
+ const baseInputStyles = `
79
+ w-full
80
+ rounded-md
81
+ border
82
+ bg-semantic-control-bg
83
+ text-semantic-control-fg
84
+ placeholder:text-semantic-control-placeholder
85
+ transition-colors
86
+ focus:outline-none
87
+ focus:ring-2
88
+ focus:ring-offset-0
89
+ disabled:cursor-not-allowed
90
+ disabled:bg-semantic-bg-disabled
91
+ disabled:text-semantic-fg-disabled
92
+ `.trim().replace(/\s+/g, ' ');
93
+
94
+ // Variant styles
95
+ const variantStyles = {
96
+ default: `
97
+ border-semantic-control-border
98
+ hover:border-semantic-control-border-hover
99
+ focus:border-semantic-border-focus
100
+ focus:ring-semantic-focus
101
+ `.trim().replace(/\s+/g, ' '),
102
+ error: `
103
+ border-semantic-control-border-error
104
+ hover:border-semantic-control-border-error
105
+ focus:border-semantic-control-border-error
106
+ focus:ring-semantic-control-border-error
107
+ `.trim().replace(/\s+/g, ' '),
108
+ };
109
+
110
+ // Size styles
111
+ const sizeStyles = {
112
+ sm: 'h-8 px-3 text-sm',
113
+ md: 'h-10 px-3 text-sm',
114
+ lg: 'h-12 px-4 text-base',
115
+ };
116
+
117
+ // Adjust padding for icons
118
+ const iconPaddingStyles = {
119
+ left: {
120
+ sm: 'pl-9',
121
+ md: 'pl-10',
122
+ lg: 'pl-12',
123
+ },
124
+ right: {
125
+ sm: 'pr-9',
126
+ md: 'pr-10',
127
+ lg: 'pr-12',
128
+ },
129
+ };
130
+
131
+ // Icon container styles
132
+ const iconContainerStyles = {
133
+ base: 'absolute flex items-center justify-center text-semantic-control-icon',
134
+ left: {
135
+ sm: 'left-2.5 w-4 h-4',
136
+ md: 'left-3 w-4 h-4',
137
+ lg: 'left-3.5 w-5 h-5',
138
+ },
139
+ right: {
140
+ sm: 'right-2.5 w-4 h-4',
141
+ md: 'right-3 w-4 h-4',
142
+ lg: 'right-3.5 w-5 h-5',
143
+ },
144
+ };
145
+
146
+ // Combine all input classes
147
+ const inputClassName = [
148
+ baseInputStyles,
149
+ variantStyles[hasError ? 'error' : 'default'],
150
+ sizeStyles[size],
151
+ leftIcon && iconPaddingStyles.left[size],
152
+ rightIcon && iconPaddingStyles.right[size],
153
+ className,
154
+ ].filter(Boolean).join(' ');
155
+
156
+ // Helper/Error text styles
157
+ const messageStyles = hasError
158
+ ? 'text-sm text-semantic-fg-error'
159
+ : 'text-sm text-semantic-fg-secondary';
160
+
161
+ return (
162
+ <div className={containerStyles}>
163
+ {/* Label */}
164
+ {label && (
165
+ <label htmlFor={inputId} className={labelStyles}>
166
+ {label}
167
+ {required && <span className="text-semantic-fg-error ml-1">*</span>}
168
+ </label>
169
+ )}
170
+
171
+ {/* Input wrapper with icons */}
172
+ <div className={wrapperStyles}>
173
+ {/* Left icon */}
174
+ {leftIcon && (
175
+ <div
176
+ className={`${iconContainerStyles.base} ${iconContainerStyles.left[size]}`}
177
+ aria-hidden="true"
178
+ >
179
+ {leftIcon}
180
+ </div>
181
+ )}
182
+
183
+ {/* Input field */}
184
+ <input
185
+ ref={ref}
186
+ id={inputId}
187
+ className={inputClassName}
188
+ disabled={disabled}
189
+ aria-invalid={hasError}
190
+ aria-required={required}
191
+ {...props}
192
+ />
193
+
194
+ {/* Right icon */}
195
+ {rightIcon && (
196
+ <div
197
+ className={`${iconContainerStyles.base} ${iconContainerStyles.right[size]}`}
198
+ aria-hidden="true"
199
+ >
200
+ {rightIcon}
201
+ </div>
202
+ )}
203
+ </div>
204
+
205
+ {/* Helper text or error message */}
206
+ {(errorMessage || helperText) && (
207
+ <p className={messageStyles}>
208
+ {errorMessage || helperText}
209
+ </p>
210
+ )}
211
+ </div>
212
+ );
213
+ }
214
+ );
215
+
216
+ Input.displayName = 'Input';
217
+
218
+ export { Input };
219
+ export default Input;
@@ -0,0 +1,60 @@
1
+ import * as React from 'react';
2
+ import { cva, type VariantProps } from 'class-variance-authority';
3
+ import { cn } from '../../lib/utils';
4
+
5
+ const labelVariants = cva(
6
+ 'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70',
7
+ {
8
+ variants: {
9
+ variant: {
10
+ default: 'text-semantic-fg-primary',
11
+ muted: 'text-semantic-fg-muted',
12
+ },
13
+ size: {
14
+ sm: 'text-xs',
15
+ md: 'text-sm',
16
+ lg: 'text-base',
17
+ },
18
+ },
19
+ defaultVariants: {
20
+ variant: 'default',
21
+ size: 'md',
22
+ },
23
+ }
24
+ );
25
+
26
+ export interface LabelProps
27
+ extends React.LabelHTMLAttributes<HTMLLabelElement>,
28
+ VariantProps<typeof labelVariants> {
29
+ /** Whether the field is required */
30
+ required?: boolean;
31
+ /** Whether the field is disabled */
32
+ disabled?: boolean;
33
+ }
34
+
35
+ const Label = React.forwardRef<HTMLLabelElement, LabelProps>(
36
+ ({ className, variant, size, required, disabled, children, ...props }, ref) => {
37
+ return (
38
+ <label
39
+ ref={ref}
40
+ className={cn(
41
+ labelVariants({ variant, size }),
42
+ disabled && 'cursor-not-allowed opacity-70',
43
+ className
44
+ )}
45
+ {...props}
46
+ >
47
+ {children}
48
+ {required && (
49
+ <span className="ml-1 text-semantic-fg-error" aria-label="required">
50
+ *
51
+ </span>
52
+ )}
53
+ </label>
54
+ );
55
+ }
56
+ );
57
+
58
+ Label.displayName = 'Label';
59
+
60
+ export { Label, labelVariants };
@@ -0,0 +1,84 @@
1
+ import * as React from 'react';
2
+ import * as ProgressPrimitive from '@radix-ui/react-progress';
3
+ import { cva, type VariantProps } from 'class-variance-authority';
4
+ import { cn } from '../../lib/utils';
5
+
6
+ const progressVariants = cva('relative h-4 w-full overflow-hidden rounded-full', {
7
+ variants: {
8
+ variant: {
9
+ default: 'bg-semantic-progress-track',
10
+ success: 'bg-semantic-progress-success-track',
11
+ warning: 'bg-semantic-progress-warning-track',
12
+ error: 'bg-semantic-progress-error-track',
13
+ },
14
+ },
15
+ defaultVariants: {
16
+ variant: 'default',
17
+ },
18
+ });
19
+
20
+ const progressIndicatorVariants = cva(
21
+ 'h-full w-full flex-1 transition-all duration-300 ease-in-out',
22
+ {
23
+ variants: {
24
+ variant: {
25
+ default: 'bg-semantic-progress-fill',
26
+ success: 'bg-semantic-progress-success-fill',
27
+ warning: 'bg-semantic-progress-warning-fill',
28
+ error: 'bg-semantic-progress-error-fill',
29
+ },
30
+ },
31
+ defaultVariants: {
32
+ variant: 'default',
33
+ },
34
+ }
35
+ );
36
+
37
+ export interface ProgressProps
38
+ extends React.ComponentPropsWithoutRef<typeof ProgressPrimitive.Root>,
39
+ VariantProps<typeof progressVariants> {
40
+ showLabel?: boolean;
41
+ label?: string;
42
+ }
43
+
44
+ const Progress = React.forwardRef<
45
+ React.ElementRef<typeof ProgressPrimitive.Root>,
46
+ ProgressProps
47
+ >(({ className, value = 0, variant, showLabel, label, ...props }, ref) => {
48
+ const progressValue = Math.min(Math.max(value || 0, 0), 100);
49
+
50
+ return (
51
+ <div className="w-full space-y-2" aria-live="polite">
52
+ {(showLabel || label) && (
53
+ <div className="flex items-center justify-between text-sm">
54
+ <span className="text-semantic-fg-secondary">
55
+ {label || 'Progress'}
56
+ </span>
57
+ {showLabel && (
58
+ <span className="font-medium text-semantic-fg-primary">
59
+ {progressValue}%
60
+ </span>
61
+ )}
62
+ </div>
63
+ )}
64
+ <ProgressPrimitive.Root
65
+ ref={ref}
66
+ className={cn(progressVariants({ variant }), className)}
67
+ value={progressValue}
68
+ aria-label={label || 'Progress'}
69
+ aria-valuemin={0}
70
+ aria-valuemax={100}
71
+ aria-valuenow={progressValue}
72
+ {...props}
73
+ >
74
+ <ProgressPrimitive.Indicator
75
+ className={cn(progressIndicatorVariants({ variant }))}
76
+ style={{ transform: `translateX(-${100 - progressValue}%)` }}
77
+ />
78
+ </ProgressPrimitive.Root>
79
+ </div>
80
+ );
81
+ });
82
+ Progress.displayName = ProgressPrimitive.Root.displayName;
83
+
84
+ export { Progress, progressVariants };
@@ -0,0 +1,161 @@
1
+ import * as React from 'react';
2
+ import * as RadioGroupPrimitive from '@radix-ui/react-radio-group';
3
+ import { Circle } from 'lucide-react';
4
+ import { cn } from '../../lib/utils';
5
+
6
+ const RadioGroup = React.forwardRef<
7
+ React.ElementRef<typeof RadioGroupPrimitive.Root>,
8
+ React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Root>
9
+ >(({ className, ...props }, ref) => {
10
+ return <RadioGroupPrimitive.Root className={cn('grid gap-2', className)} {...props} ref={ref} />;
11
+ });
12
+ RadioGroup.displayName = RadioGroupPrimitive.Root.displayName;
13
+
14
+ const RadioGroupItem = React.forwardRef<
15
+ React.ElementRef<typeof RadioGroupPrimitive.Item>,
16
+ React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Item> & {
17
+ size?: 'sm' | 'md' | 'lg';
18
+ }
19
+ >(({ className, size = 'md', ...props }, ref) => {
20
+ const sizeClasses = {
21
+ sm: 'h-4 w-4',
22
+ md: 'h-5 w-5',
23
+ lg: 'h-6 w-6',
24
+ };
25
+
26
+ const iconSizes = {
27
+ sm: 8,
28
+ md: 10,
29
+ lg: 12,
30
+ };
31
+
32
+ return (
33
+ <RadioGroupPrimitive.Item
34
+ ref={ref}
35
+ className={cn(
36
+ 'aspect-square rounded-full border-2 border-semantic-control-border text-semantic-control-bg-checked ring-offset-semantic-offset',
37
+ 'transition-colors',
38
+ 'focus:outline-none focus-visible:ring-2 focus-visible:ring-semantic-focus focus-visible:ring-offset-2',
39
+ 'disabled:cursor-not-allowed disabled:opacity-50',
40
+ 'data-[state=checked]:border-semantic-control-bg-checked data-[state=checked]:bg-semantic-control-bg',
41
+ sizeClasses[size],
42
+ className
43
+ )}
44
+ {...props}
45
+ >
46
+ <RadioGroupPrimitive.Indicator className="flex items-center justify-center">
47
+ <Circle className="fill-current text-current" size={iconSizes[size]} />
48
+ </RadioGroupPrimitive.Indicator>
49
+ </RadioGroupPrimitive.Item>
50
+ );
51
+ });
52
+ RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName;
53
+
54
+ export interface RadioOption {
55
+ value: string;
56
+ label: string;
57
+ description?: string;
58
+ disabled?: boolean;
59
+ }
60
+
61
+ export interface RadioGroupWrapperProps
62
+ extends Omit<React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Root>, 'children'> {
63
+ label?: string;
64
+ helperText?: string;
65
+ errorMessage?: string;
66
+ error?: boolean;
67
+ options: RadioOption[];
68
+ size?: 'sm' | 'md' | 'lg';
69
+ orientation?: 'vertical' | 'horizontal';
70
+ }
71
+
72
+ const RadioGroupWrapper = React.forwardRef<
73
+ React.ElementRef<typeof RadioGroupPrimitive.Root>,
74
+ RadioGroupWrapperProps
75
+ >(
76
+ (
77
+ {
78
+ label,
79
+ helperText,
80
+ errorMessage,
81
+ error = false,
82
+ options,
83
+ size = 'md',
84
+ orientation = 'vertical',
85
+ className,
86
+ ...props
87
+ },
88
+ ref
89
+ ) => {
90
+ const groupId = React.useId();
91
+ const helperTextId = `${groupId}-helper`;
92
+ const errorMessageId = `${groupId}-error`;
93
+
94
+ return (
95
+ <div className="w-full">
96
+ {label && (
97
+ <label className="mb-2 block text-sm font-medium text-semantic-fg-secondary">
98
+ {label}
99
+ </label>
100
+ )}
101
+
102
+ <RadioGroup
103
+ ref={ref}
104
+ className={cn(
105
+ orientation === 'horizontal' ? 'flex flex-wrap gap-4' : 'grid gap-3',
106
+ className
107
+ )}
108
+ aria-invalid={error ? 'true' : 'false'}
109
+ aria-describedby={
110
+ error && errorMessage ? errorMessageId : helperText ? helperTextId : undefined
111
+ }
112
+ {...props}
113
+ >
114
+ {options.map((option) => (
115
+ <div key={option.value} className="flex items-start gap-2">
116
+ <RadioGroupItem id={option.value} value={option.value} size={size} disabled={option.disabled} />
117
+ <div className="flex flex-col">
118
+ <label
119
+ htmlFor={option.value}
120
+ className={cn(
121
+ 'cursor-pointer select-none font-medium leading-none',
122
+ size === 'sm' ? 'text-sm' : size === 'lg' ? 'text-lg' : 'text-base',
123
+ option.disabled && 'cursor-not-allowed opacity-70',
124
+ 'text-semantic-fg-primary'
125
+ )}
126
+ >
127
+ {option.label}
128
+ </label>
129
+ {option.description && (
130
+ <p
131
+ className={cn(
132
+ 'text-semantic-fg-secondary',
133
+ size === 'sm' ? 'text-xs mt-0.5' : 'text-sm mt-1'
134
+ )}
135
+ >
136
+ {option.description}
137
+ </p>
138
+ )}
139
+ </div>
140
+ </div>
141
+ ))}
142
+ </RadioGroup>
143
+
144
+ {!error && helperText && (
145
+ <p id={helperTextId} className="mt-2 text-sm text-semantic-fg-secondary">
146
+ {helperText}
147
+ </p>
148
+ )}
149
+
150
+ {error && errorMessage && (
151
+ <p id={errorMessageId} className="mt-2 text-sm text-semantic-fg-error">
152
+ {errorMessage}
153
+ </p>
154
+ )}
155
+ </div>
156
+ );
157
+ }
158
+ );
159
+ RadioGroupWrapper.displayName = 'RadioGroupWrapper';
160
+
161
+ export { RadioGroup, RadioGroupItem, RadioGroupWrapper };