radtools 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.
Files changed (133) hide show
  1. package/README.md +108 -0
  2. package/bin/radtools.js +5 -0
  3. package/dist/cli/index.js +427 -0
  4. package/package.json +55 -0
  5. package/templates/api-routes/assets/optimize/route.ts +94 -0
  6. package/templates/api-routes/assets/route.ts +159 -0
  7. package/templates/api-routes/components/create-folder/route.ts +55 -0
  8. package/templates/api-routes/components/route.ts +156 -0
  9. package/templates/api-routes/fonts/route.ts +96 -0
  10. package/templates/api-routes/fonts/upload/route.ts +79 -0
  11. package/templates/api-routes/read-css/route.ts +29 -0
  12. package/templates/api-routes/write-css/route.ts +423 -0
  13. package/templates/components/Rad_os/AppWindow.tsx +423 -0
  14. package/templates/components/Rad_os/MobileAppModal.tsx +76 -0
  15. package/templates/components/Rad_os/WindowTitleBar.tsx +290 -0
  16. package/templates/components/icons/Icon.tsx +224 -0
  17. package/templates/components/icons/README.md +85 -0
  18. package/templates/components/icons/index.ts +20 -0
  19. package/templates/components/icons.tsx +164 -0
  20. package/templates/components/ui/Accordion.tsx +268 -0
  21. package/templates/components/ui/Alert.tsx +111 -0
  22. package/templates/components/ui/Badge.tsx +87 -0
  23. package/templates/components/ui/Breadcrumbs.tsx +88 -0
  24. package/templates/components/ui/Button.tsx +249 -0
  25. package/templates/components/ui/Card.tsx +137 -0
  26. package/templates/components/ui/Checkbox.tsx +137 -0
  27. package/templates/components/ui/ContextMenu.tsx +220 -0
  28. package/templates/components/ui/Dialog.tsx +264 -0
  29. package/templates/components/ui/Divider.tsx +70 -0
  30. package/templates/components/ui/DropdownMenu.tsx +301 -0
  31. package/templates/components/ui/HelpPanel.tsx +119 -0
  32. package/templates/components/ui/Input.tsx +176 -0
  33. package/templates/components/ui/Popover.tsx +211 -0
  34. package/templates/components/ui/Progress.tsx +158 -0
  35. package/templates/components/ui/Select.tsx +134 -0
  36. package/templates/components/ui/Sheet.tsx +316 -0
  37. package/templates/components/ui/Slider.tsx +223 -0
  38. package/templates/components/ui/Switch.tsx +155 -0
  39. package/templates/components/ui/Tabs.tsx +253 -0
  40. package/templates/components/ui/Toast.tsx +192 -0
  41. package/templates/components/ui/Tooltip.tsx +129 -0
  42. package/templates/components/ui/hooks/useModalBehavior.ts +66 -0
  43. package/templates/components/ui/index.ts +84 -0
  44. package/templates/devtools/DevToolsPanel.tsx +261 -0
  45. package/templates/devtools/DevToolsProvider.tsx +43 -0
  46. package/templates/devtools/components/BreakpointIndicator.tsx +49 -0
  47. package/templates/devtools/components/ColorPicker.tsx +33 -0
  48. package/templates/devtools/components/ComponentsSecondaryNav.tsx +44 -0
  49. package/templates/devtools/components/ContextualFooter.tsx +56 -0
  50. package/templates/devtools/components/DraggablePanel.tsx +43 -0
  51. package/templates/devtools/components/PrimaryNavigationFooter.tsx +254 -0
  52. package/templates/devtools/components/SearchableColorDropdown.tsx +253 -0
  53. package/templates/devtools/components/SecondaryNavigation.tsx +36 -0
  54. package/templates/devtools/components/TokenDropdown.tsx +47 -0
  55. package/templates/devtools/components/TypographyFooter.tsx +145 -0
  56. package/templates/devtools/hooks/useMockState.ts +16 -0
  57. package/templates/devtools/index.ts +17 -0
  58. package/templates/devtools/lib/componentScanner.ts +78 -0
  59. package/templates/devtools/lib/cssParser.ts +465 -0
  60. package/templates/devtools/lib/searchIndexes.ts +45 -0
  61. package/templates/devtools/lib/selectorGenerator.ts +86 -0
  62. package/templates/devtools/store/index.ts +66 -0
  63. package/templates/devtools/store/slices/assetsSlice.ts +106 -0
  64. package/templates/devtools/store/slices/componentsSlice.ts +59 -0
  65. package/templates/devtools/store/slices/mockStatesSlice.ts +77 -0
  66. package/templates/devtools/store/slices/panelSlice.ts +17 -0
  67. package/templates/devtools/store/slices/typographySlice.ts +538 -0
  68. package/templates/devtools/store/slices/variablesSlice.ts +167 -0
  69. package/templates/devtools/tabs/AssetsTab/AssetGrid.tsx +76 -0
  70. package/templates/devtools/tabs/AssetsTab/FolderTree.tsx +53 -0
  71. package/templates/devtools/tabs/AssetsTab/UploadDropzone.tsx +76 -0
  72. package/templates/devtools/tabs/AssetsTab/index.tsx +182 -0
  73. package/templates/devtools/tabs/ComponentsTab/AddTabButton.tsx +63 -0
  74. package/templates/devtools/tabs/ComponentsTab/ComponentList.tsx +153 -0
  75. package/templates/devtools/tabs/ComponentsTab/DesignSystemTab.tsx +1515 -0
  76. package/templates/devtools/tabs/ComponentsTab/DynamicFolderTab.tsx +113 -0
  77. package/templates/devtools/tabs/ComponentsTab/PropDisplay.tsx +55 -0
  78. package/templates/devtools/tabs/ComponentsTab/index.tsx +167 -0
  79. package/templates/devtools/tabs/ComponentsTab/previews/.gitkeep +4 -0
  80. package/templates/devtools/tabs/ComponentsTab/previews/Rad_os.tsx +262 -0
  81. package/templates/devtools/tabs/ComponentsTab/tabConfig.ts +53 -0
  82. package/templates/devtools/tabs/MockStatesTab/index.tsx +29 -0
  83. package/templates/devtools/tabs/TypographyTab/FontManager.tsx +421 -0
  84. package/templates/devtools/tabs/TypographyTab/TypographyStylesDisplay.tsx +290 -0
  85. package/templates/devtools/tabs/TypographyTab/index.tsx +98 -0
  86. package/templates/devtools/tabs/VariablesTab/BaseColorEditor.tsx +267 -0
  87. package/templates/devtools/tabs/VariablesTab/BorderRadiusEditor.tsx +37 -0
  88. package/templates/devtools/tabs/VariablesTab/ColorModeSelector.tsx +235 -0
  89. package/templates/devtools/tabs/VariablesTab/index.tsx +100 -0
  90. package/templates/devtools/types/index.ts +99 -0
  91. package/templates/globals.css +574 -0
  92. package/templates/hooks/index.ts +1 -0
  93. package/templates/hooks/useWindowManager.ts +212 -0
  94. package/templates/public/assets/icons/avatar.svg +18 -0
  95. package/templates/public/assets/icons/checkmark-filled.svg +14 -0
  96. package/templates/public/assets/icons/checkmark.svg +14 -0
  97. package/templates/public/assets/icons/chevron-down.svg +14 -0
  98. package/templates/public/assets/icons/close.svg +14 -0
  99. package/templates/public/assets/icons/copy.svg +14 -0
  100. package/templates/public/assets/icons/download.svg +14 -0
  101. package/templates/public/assets/icons/expand.svg +31 -0
  102. package/templates/public/assets/icons/file-blank.svg +17 -0
  103. package/templates/public/assets/icons/file-image.svg +19 -0
  104. package/templates/public/assets/icons/file-written.svg +17 -0
  105. package/templates/public/assets/icons/folder-closed.svg +17 -0
  106. package/templates/public/assets/icons/folder-open.svg +17 -0
  107. package/templates/public/assets/icons/hamburger.svg +18 -0
  108. package/templates/public/assets/icons/home-outline.svg +28 -0
  109. package/templates/public/assets/icons/home.svg +30 -0
  110. package/templates/public/assets/icons/hourglass.svg +25 -0
  111. package/templates/public/assets/icons/information-circle.svg +14 -0
  112. package/templates/public/assets/icons/information.svg +17 -0
  113. package/templates/public/assets/icons/lightning.svg +14 -0
  114. package/templates/public/assets/icons/locked.svg +17 -0
  115. package/templates/public/assets/icons/not-allowed.svg +14 -0
  116. package/templates/public/assets/icons/plus.svg +5 -0
  117. package/templates/public/assets/icons/power-thin.svg +17 -0
  118. package/templates/public/assets/icons/power.svg +17 -0
  119. package/templates/public/assets/icons/question-block.svg +14 -0
  120. package/templates/public/assets/icons/question.svg +17 -0
  121. package/templates/public/assets/icons/refresh-block.svg +14 -0
  122. package/templates/public/assets/icons/refresh.svg +17 -0
  123. package/templates/public/assets/icons/save.svg +14 -0
  124. package/templates/public/assets/icons/search.svg +25 -0
  125. package/templates/public/assets/icons/settings.svg +14 -0
  126. package/templates/public/assets/icons/trash-full.svg +21 -0
  127. package/templates/public/assets/icons/trash-open.svg +23 -0
  128. package/templates/public/assets/icons/trash.svg +18 -0
  129. package/templates/public/assets/icons/unlocked.svg +17 -0
  130. package/templates/public/assets/icons/waring-triangle-filled.svg +17 -0
  131. package/templates/public/assets/icons/warning-triangle-filled-2.svg +30 -0
  132. package/templates/public/assets/icons/warning-triangle-lines.svg +29 -0
  133. package/templates/public/assets/icons/wrench.svg +17 -0
@@ -0,0 +1,220 @@
1
+ 'use client';
2
+
3
+ import React, { useState, useRef, useEffect, createContext, useContext } from 'react';
4
+ import { Icon } from '@/components/icons';
5
+
6
+ // ============================================================================
7
+ // Types
8
+ // ============================================================================
9
+
10
+ interface Position {
11
+ x: number;
12
+ y: number;
13
+ }
14
+
15
+ interface ContextMenuContextValue {
16
+ isOpen: boolean;
17
+ position: Position;
18
+ open: (position: Position) => void;
19
+ close: () => void;
20
+ }
21
+
22
+ interface ContextMenuProps {
23
+ /** Content that triggers context menu on right-click */
24
+ children: React.ReactNode;
25
+ /** Additional classes for trigger container */
26
+ className?: string;
27
+ }
28
+
29
+ interface ContextMenuContentProps {
30
+ /** Menu items */
31
+ children: React.ReactNode;
32
+ /** Additional classes */
33
+ className?: string;
34
+ }
35
+
36
+ interface ContextMenuItemProps {
37
+ /** Click handler */
38
+ onClick?: () => void;
39
+ /** Disabled state */
40
+ disabled?: boolean;
41
+ /** Destructive action (red text) */
42
+ destructive?: boolean;
43
+ /** Icon name (filename without .svg extension) */
44
+ iconName?: string;
45
+ /** Menu item content */
46
+ children: React.ReactNode;
47
+ /** Additional classes */
48
+ className?: string;
49
+ }
50
+
51
+ interface ContextMenuSeparatorProps {
52
+ className?: string;
53
+ }
54
+
55
+ // ============================================================================
56
+ // Context
57
+ // ============================================================================
58
+
59
+ const ContextMenuContext = createContext<ContextMenuContextValue | null>(null);
60
+
61
+ function useContextMenu() {
62
+ const context = useContext(ContextMenuContext);
63
+ if (!context) {
64
+ throw new Error('ContextMenu components must be used within a ContextMenu provider');
65
+ }
66
+ return context;
67
+ }
68
+
69
+ // ============================================================================
70
+ // Components
71
+ // ============================================================================
72
+
73
+ /**
74
+ * Context menu container - wraps content that should have right-click menu
75
+ */
76
+ export function ContextMenu({ children, className = '' }: ContextMenuProps) {
77
+ const [isOpen, setIsOpen] = useState(false);
78
+ const [position, setPosition] = useState<Position>({ x: 0, y: 0 });
79
+ const containerRef = useRef<HTMLDivElement>(null);
80
+
81
+ const open = (pos: Position) => {
82
+ setPosition(pos);
83
+ setIsOpen(true);
84
+ };
85
+
86
+ const close = () => {
87
+ setIsOpen(false);
88
+ };
89
+
90
+ // Handle right-click
91
+ const handleContextMenu = (e: React.MouseEvent) => {
92
+ e.preventDefault();
93
+ open({ x: e.clientX, y: e.clientY });
94
+ };
95
+
96
+ // Close on click outside
97
+ useEffect(() => {
98
+ const handleClickOutside = (e: MouseEvent) => {
99
+ if (containerRef.current && !containerRef.current.contains(e.target as Node)) {
100
+ close();
101
+ }
102
+ };
103
+
104
+ const handleEscape = (e: KeyboardEvent) => {
105
+ if (e.key === 'Escape') close();
106
+ };
107
+
108
+ if (isOpen) {
109
+ document.addEventListener('mousedown', handleClickOutside);
110
+ document.addEventListener('keydown', handleEscape);
111
+ }
112
+
113
+ return () => {
114
+ document.removeEventListener('mousedown', handleClickOutside);
115
+ document.removeEventListener('keydown', handleEscape);
116
+ };
117
+ }, [isOpen]);
118
+
119
+ return (
120
+ <ContextMenuContext.Provider value={{ isOpen, position, open, close }}>
121
+ <div
122
+ ref={containerRef}
123
+ onContextMenu={handleContextMenu}
124
+ className={className}
125
+ >
126
+ {children}
127
+ </div>
128
+ </ContextMenuContext.Provider>
129
+ );
130
+ }
131
+
132
+ /**
133
+ * Context menu dropdown content
134
+ */
135
+ export function ContextMenuContent({ children, className = '' }: ContextMenuContentProps) {
136
+ const { isOpen, position, close } = useContextMenu();
137
+
138
+ if (!isOpen) return null;
139
+
140
+ return (
141
+ <div
142
+ className={`
143
+ fixed z-[1000]
144
+ min-w-[160px]
145
+ bg-warm-cloud
146
+ border border-black
147
+ rounded-sm
148
+ shadow-[2px_2px_0_0_var(--color-black)]
149
+ py-1
150
+ ${className}
151
+ `}
152
+ style={{
153
+ left: position.x,
154
+ top: position.y,
155
+ }}
156
+ onClick={close}
157
+ >
158
+ {children}
159
+ </div>
160
+ );
161
+ }
162
+
163
+ /**
164
+ * Context menu item
165
+ */
166
+ export function ContextMenuItem({
167
+ onClick,
168
+ disabled = false,
169
+ destructive = false,
170
+ iconName,
171
+ children,
172
+ className = '',
173
+ }: ContextMenuItemProps) {
174
+ const { close } = useContextMenu();
175
+
176
+ const handleClick = () => {
177
+ if (!disabled && onClick) {
178
+ onClick();
179
+ close();
180
+ }
181
+ };
182
+
183
+ return (
184
+ <button
185
+ type="button"
186
+ onClick={handleClick}
187
+ disabled={disabled}
188
+ className={`
189
+ w-full flex items-center gap-2
190
+ px-3 py-1.5
191
+ font-mondwest text-base text-left
192
+ ${destructive ? 'text-error-red' : 'text-black'}
193
+ ${disabled ? 'opacity-50 cursor-not-allowed' : 'hover:bg-sun-yellow cursor-pointer'}
194
+ ${className}
195
+ `}
196
+ >
197
+ {iconName && (
198
+ <span className="w-4 h-4 flex items-center justify-center">
199
+ <Icon name={iconName} size={16} />
200
+ </span>
201
+ )}
202
+ <span>{children}</span>
203
+ </button>
204
+ );
205
+ }
206
+
207
+ /**
208
+ * Context menu separator line
209
+ */
210
+ export function ContextMenuSeparator({ className = '' }: ContextMenuSeparatorProps) {
211
+ return (
212
+ <div
213
+ className={`my-1 border-t ${className}`}
214
+ style={{ borderTopColor: 'var(--border-black-20)' }}
215
+ />
216
+ );
217
+ }
218
+
219
+ export default ContextMenu;
220
+
@@ -0,0 +1,264 @@
1
+ 'use client';
2
+
3
+ import React, { createContext, useContext, useState, useCallback, useEffect } from 'react';
4
+ import { createPortal } from 'react-dom';
5
+ import { useEscapeKey, useLockBodyScroll } from './hooks/useModalBehavior';
6
+
7
+ // ============================================================================
8
+ // Types
9
+ // ============================================================================
10
+
11
+ interface DialogContextValue {
12
+ open: boolean;
13
+ setOpen: (open: boolean) => void;
14
+ }
15
+
16
+ // ============================================================================
17
+ // Context
18
+ // ============================================================================
19
+
20
+ const DialogContext = createContext<DialogContextValue | null>(null);
21
+
22
+ function useDialogContext() {
23
+ const context = useContext(DialogContext);
24
+ if (!context) {
25
+ throw new Error('Dialog components must be used within a Dialog');
26
+ }
27
+ return context;
28
+ }
29
+
30
+ // ============================================================================
31
+ // Dialog Root
32
+ // ============================================================================
33
+
34
+ interface DialogProps {
35
+ /** Controlled open state */
36
+ open?: boolean;
37
+ /** Default open state for uncontrolled usage */
38
+ defaultOpen?: boolean;
39
+ /** Callback when open state changes */
40
+ onOpenChange?: (open: boolean) => void;
41
+ /** Children */
42
+ children: React.ReactNode;
43
+ }
44
+
45
+ export function Dialog({
46
+ open: controlledOpen,
47
+ defaultOpen = false,
48
+ onOpenChange,
49
+ children,
50
+ }: DialogProps) {
51
+ const [internalOpen, setInternalOpen] = useState(defaultOpen);
52
+ const isControlled = controlledOpen !== undefined;
53
+ const open = isControlled ? controlledOpen : internalOpen;
54
+
55
+ const setOpen = useCallback((newOpen: boolean) => {
56
+ if (!isControlled) {
57
+ setInternalOpen(newOpen);
58
+ }
59
+ onOpenChange?.(newOpen);
60
+ }, [isControlled, onOpenChange]);
61
+
62
+ return (
63
+ <DialogContext.Provider value={{ open, setOpen }}>
64
+ {children}
65
+ </DialogContext.Provider>
66
+ );
67
+ }
68
+
69
+ // ============================================================================
70
+ // Dialog Trigger
71
+ // ============================================================================
72
+
73
+ interface DialogTriggerProps {
74
+ /** Trigger element */
75
+ children: React.ReactElement;
76
+ /** Pass through as child instead of wrapping */
77
+ asChild?: boolean;
78
+ }
79
+
80
+ export function DialogTrigger({ children, asChild }: DialogTriggerProps) {
81
+ const { setOpen } = useDialogContext();
82
+
83
+ if (asChild && React.isValidElement(children)) {
84
+ return React.cloneElement(children as React.ReactElement<{ onClick?: () => void }>, {
85
+ onClick: () => setOpen(true),
86
+ });
87
+ }
88
+
89
+ return (
90
+ <button type="button" onClick={() => setOpen(true)}>
91
+ {children}
92
+ </button>
93
+ );
94
+ }
95
+
96
+ // ============================================================================
97
+ // Dialog Portal & Overlay
98
+ // ============================================================================
99
+
100
+ interface DialogContentProps {
101
+ /** Additional className */
102
+ className?: string;
103
+ /** Children */
104
+ children: React.ReactNode;
105
+ }
106
+
107
+ export function DialogContent({ className = '', children }: DialogContentProps) {
108
+ const { open, setOpen } = useDialogContext();
109
+ const [mounted, setMounted] = useState(false);
110
+
111
+ useEffect(() => {
112
+ setMounted(true);
113
+ }, []);
114
+
115
+ // Handle escape key
116
+ useEscapeKey(open, () => setOpen(false));
117
+
118
+ // Prevent body scroll when open
119
+ useLockBodyScroll(open);
120
+
121
+ if (!mounted || !open) return null;
122
+
123
+ return createPortal(
124
+ <div className="fixed inset-0 z-50 flex items-center justify-center">
125
+ {/* Overlay */}
126
+ <div
127
+ className="absolute inset-0 bg-black/50 animate-fadeIn"
128
+ onClick={() => setOpen(false)}
129
+ aria-hidden="true"
130
+ />
131
+
132
+ {/* Content */}
133
+ <div
134
+ role="dialog"
135
+ aria-modal="true"
136
+ className={`
137
+ relative z-10
138
+ w-full max-w-lg mx-4
139
+ bg-warm-cloud
140
+ border-2 border-black
141
+ rounded-sm
142
+ shadow-[4px_4px_0_0_var(--color-black)]
143
+ animate-scaleIn
144
+ ${className}
145
+ `.trim()}
146
+ >
147
+ {children}
148
+ </div>
149
+ </div>,
150
+ document.body
151
+ );
152
+ }
153
+
154
+ // ============================================================================
155
+ // Dialog Header, Title, Description
156
+ // ============================================================================
157
+
158
+ interface DialogHeaderProps {
159
+ /** Additional className */
160
+ className?: string;
161
+ /** Children */
162
+ children: React.ReactNode;
163
+ }
164
+
165
+ export function DialogHeader({ className = '', children }: DialogHeaderProps) {
166
+ return (
167
+ <div className={`px-6 pt-6 pb-4 border-b border-black/20 ${className}`.trim()}>
168
+ {children}
169
+ </div>
170
+ );
171
+ }
172
+
173
+ interface DialogTitleProps {
174
+ /** Additional className */
175
+ className?: string;
176
+ /** Children */
177
+ children: React.ReactNode;
178
+ }
179
+
180
+ export function DialogTitle({ className = '', children }: DialogTitleProps) {
181
+ return (
182
+ <h2 className={`font-joystix text-base uppercase text-black ${className}`.trim()}>
183
+ {children}
184
+ </h2>
185
+ );
186
+ }
187
+
188
+ interface DialogDescriptionProps {
189
+ /** Additional className */
190
+ className?: string;
191
+ /** Children */
192
+ children: React.ReactNode;
193
+ }
194
+
195
+ export function DialogDescription({ className = '', children }: DialogDescriptionProps) {
196
+ return (
197
+ <p className={`font-mondwest text-base text-black/70 mt-2 ${className}`.trim()}>
198
+ {children}
199
+ </p>
200
+ );
201
+ }
202
+
203
+ // ============================================================================
204
+ // Dialog Body & Footer
205
+ // ============================================================================
206
+
207
+ interface DialogBodyProps {
208
+ /** Additional className */
209
+ className?: string;
210
+ /** Children */
211
+ children: React.ReactNode;
212
+ }
213
+
214
+ export function DialogBody({ className = '', children }: DialogBodyProps) {
215
+ return (
216
+ <div className={`px-6 py-4 ${className}`.trim()}>
217
+ {children}
218
+ </div>
219
+ );
220
+ }
221
+
222
+ interface DialogFooterProps {
223
+ /** Additional className */
224
+ className?: string;
225
+ /** Children */
226
+ children: React.ReactNode;
227
+ }
228
+
229
+ export function DialogFooter({ className = '', children }: DialogFooterProps) {
230
+ return (
231
+ <div className={`px-6 pb-6 pt-4 border-t border-black/20 flex justify-end gap-2 ${className}`.trim()}>
232
+ {children}
233
+ </div>
234
+ );
235
+ }
236
+
237
+ // ============================================================================
238
+ // Dialog Close
239
+ // ============================================================================
240
+
241
+ interface DialogCloseProps {
242
+ /** Close button element */
243
+ children: React.ReactElement;
244
+ /** Pass through as child instead of wrapping */
245
+ asChild?: boolean;
246
+ }
247
+
248
+ export function DialogClose({ children, asChild }: DialogCloseProps) {
249
+ const { setOpen } = useDialogContext();
250
+
251
+ if (asChild && React.isValidElement(children)) {
252
+ return React.cloneElement(children as React.ReactElement<{ onClick?: () => void }>, {
253
+ onClick: () => setOpen(false),
254
+ });
255
+ }
256
+
257
+ return (
258
+ <button type="button" onClick={() => setOpen(false)}>
259
+ {children}
260
+ </button>
261
+ );
262
+ }
263
+
264
+ export default Dialog;
@@ -0,0 +1,70 @@
1
+ 'use client';
2
+
3
+ import React from 'react';
4
+
5
+ // ============================================================================
6
+ // Types
7
+ // ============================================================================
8
+
9
+ type DividerOrientation = 'horizontal' | 'vertical';
10
+ type DividerVariant = 'solid' | 'dashed' | 'decorated';
11
+
12
+ interface DividerProps {
13
+ /** Orientation */
14
+ orientation?: DividerOrientation;
15
+ /** Visual variant */
16
+ variant?: DividerVariant;
17
+ /** Additional classes */
18
+ className?: string;
19
+ }
20
+
21
+ // ============================================================================
22
+ // Component
23
+ // ============================================================================
24
+
25
+ /**
26
+ * Divider component for separating content
27
+ */
28
+ export function Divider({
29
+ orientation = 'horizontal',
30
+ variant = 'solid',
31
+ className = '',
32
+ }: DividerProps) {
33
+ // Decorated variant with diamond in center
34
+ if (variant === 'decorated') {
35
+ return (
36
+ <div className={`flex items-center gap-4 ${className}`}>
37
+ <div className="flex-1 h-[2px]" style={{ backgroundColor: 'var(--border-black-20)' }} />
38
+ <div className="w-2 h-2 bg-sun-yellow border border-black rotate-45" />
39
+ <div className="flex-1 h-[2px]" style={{ backgroundColor: 'var(--border-black-20)' }} />
40
+ </div>
41
+ );
42
+ }
43
+
44
+ // Horizontal divider
45
+ if (orientation === 'horizontal') {
46
+ const borderStyle = variant === 'dashed' ? 'border-dashed' : 'border-solid';
47
+ return (
48
+ <div
49
+ className={`w-full border-t ${borderStyle} ${className}`}
50
+ style={{ borderTopColor: 'var(--border-black-20)' }}
51
+ role="separator"
52
+ aria-orientation="horizontal"
53
+ />
54
+ );
55
+ }
56
+
57
+ // Vertical divider
58
+ const borderStyle = variant === 'dashed' ? 'border-dashed' : 'border-solid';
59
+ return (
60
+ <div
61
+ className={`h-full border-l ${borderStyle} ${className}`}
62
+ style={{ borderLeftColor: 'var(--border-black-20)' }}
63
+ role="separator"
64
+ aria-orientation="vertical"
65
+ />
66
+ );
67
+ }
68
+
69
+ export default Divider;
70
+