@opencosmos/ui 1.3.1
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/.claude/CLAUDE.md +239 -0
- package/README.md +161 -0
- package/dist/cli.mjs +151 -0
- package/dist/dates.d.mts +20 -0
- package/dist/dates.d.ts +20 -0
- package/dist/dates.js +240 -0
- package/dist/dates.js.map +1 -0
- package/dist/dates.mjs +203 -0
- package/dist/dates.mjs.map +1 -0
- package/dist/dnd.d.mts +126 -0
- package/dist/dnd.d.ts +126 -0
- package/dist/dnd.js +274 -0
- package/dist/dnd.js.map +1 -0
- package/dist/dnd.mjs +250 -0
- package/dist/dnd.mjs.map +1 -0
- package/dist/fontThemes-Dh8mtXES.d.mts +868 -0
- package/dist/fontThemes-Dh8mtXES.d.ts +868 -0
- package/dist/forms.d.mts +38 -0
- package/dist/forms.d.ts +38 -0
- package/dist/forms.js +198 -0
- package/dist/forms.js.map +1 -0
- package/dist/forms.mjs +159 -0
- package/dist/forms.mjs.map +1 -0
- package/dist/hooks-1b8WaQf1.d.mts +225 -0
- package/dist/hooks-CKW8vE9H.d.ts +225 -0
- package/dist/hooks.d.mts +3 -0
- package/dist/hooks.d.ts +3 -0
- package/dist/hooks.js +971 -0
- package/dist/hooks.js.map +1 -0
- package/dist/hooks.mjs +943 -0
- package/dist/hooks.mjs.map +1 -0
- package/dist/index-DscTIrZ2.d.mts +29 -0
- package/dist/index-DscTIrZ2.d.ts +29 -0
- package/dist/index.d.mts +3382 -0
- package/dist/index.d.ts +3382 -0
- package/dist/index.js +15146 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +14802 -0
- package/dist/index.mjs.map +1 -0
- package/dist/providers-CXPDMsl7.d.mts +30 -0
- package/dist/providers-Dn_Msjvz.d.ts +30 -0
- package/dist/providers.d.mts +3 -0
- package/dist/providers.d.ts +3 -0
- package/dist/providers.js +1885 -0
- package/dist/providers.js.map +1 -0
- package/dist/providers.mjs +1859 -0
- package/dist/providers.mjs.map +1 -0
- package/dist/tables.d.mts +10 -0
- package/dist/tables.d.ts +10 -0
- package/dist/tables.js +248 -0
- package/dist/tables.js.map +1 -0
- package/dist/tables.mjs +218 -0
- package/dist/tables.mjs.map +1 -0
- package/dist/tokens.d.mts +1065 -0
- package/dist/tokens.d.ts +1065 -0
- package/dist/tokens.js +2637 -0
- package/dist/tokens.js.map +1 -0
- package/dist/tokens.mjs +2555 -0
- package/dist/tokens.mjs.map +1 -0
- package/dist/utils-CIIM7dAC.d.ts +986 -0
- package/dist/utils-Cs04sxth.d.mts +986 -0
- package/dist/utils.d.mts +4 -0
- package/dist/utils.d.ts +4 -0
- package/dist/utils.js +874 -0
- package/dist/utils.js.map +1 -0
- package/dist/utils.mjs +806 -0
- package/dist/utils.mjs.map +1 -0
- package/dist/validation-Bj1ye-v_.d.mts +114 -0
- package/dist/validation-Bj1ye-v_.d.ts +114 -0
- package/dist/webgl.d.mts +104 -0
- package/dist/webgl.d.ts +104 -0
- package/dist/webgl.js +226 -0
- package/dist/webgl.js.map +1 -0
- package/dist/webgl.mjs +195 -0
- package/dist/webgl.mjs.map +1 -0
- package/package.json +267 -0
- package/src/cli.ts +206 -0
- package/src/component-registry.ts +183 -0
- package/src/components/actions/Button.test.tsx +61 -0
- package/src/components/actions/Button.tsx +70 -0
- package/src/components/actions/Link.tsx +78 -0
- package/src/components/actions/Magnetic.tsx +68 -0
- package/src/components/actions/Toggle.test.tsx +40 -0
- package/src/components/actions/Toggle.tsx +47 -0
- package/src/components/actions/ToggleGroup.tsx +70 -0
- package/src/components/actions/index.ts +5 -0
- package/src/components/backgrounds/FaultyTerminal.tsx +426 -0
- package/src/components/backgrounds/OrbBackground.tsx +424 -0
- package/src/components/backgrounds/WarpBackground.tsx +358 -0
- package/src/components/backgrounds/index.ts +3 -0
- package/src/components/blocks/Hero.tsx +142 -0
- package/src/components/blocks/social/OpenGraphCard.tsx +243 -0
- package/src/components/cursor/SplashCursor.tsx +1315 -0
- package/src/components/cursor/TargetCursor.tsx +187 -0
- package/src/components/cursor/index.ts +2 -0
- package/src/components/data-display/AspectImage.tsx +73 -0
- package/src/components/data-display/Avatar.test.tsx +35 -0
- package/src/components/data-display/Avatar.tsx +55 -0
- package/src/components/data-display/Badge.test.tsx +43 -0
- package/src/components/data-display/Badge.tsx +84 -0
- package/src/components/data-display/Brand.tsx +123 -0
- package/src/components/data-display/Calendar.tsx +70 -0
- package/src/components/data-display/Card.test.tsx +92 -0
- package/src/components/data-display/Card.tsx +115 -0
- package/src/components/data-display/Code.tsx +210 -0
- package/src/components/data-display/CollapsibleCodeBlock.tsx +238 -0
- package/src/components/data-display/DataTable.tsx +119 -0
- package/src/components/data-display/DescriptionList.tsx +41 -0
- package/src/components/data-display/GitHubIcon.tsx +44 -0
- package/src/components/data-display/Heading.test.tsx +36 -0
- package/src/components/data-display/Heading.tsx +83 -0
- package/src/components/data-display/StatCard.tsx +195 -0
- package/src/components/data-display/Table.tsx +133 -0
- package/src/components/data-display/Text.test.tsx +48 -0
- package/src/components/data-display/Text.tsx +144 -0
- package/src/components/data-display/Timeline.tsx +194 -0
- package/src/components/data-display/TreeView.tsx +226 -0
- package/src/components/data-display/Typewriter.tsx +119 -0
- package/src/components/data-display/VariableWeightText.tsx +130 -0
- package/src/components/data-display/index.ts +19 -0
- package/src/components/feedback/Alert.test.tsx +44 -0
- package/src/components/feedback/Alert.tsx +65 -0
- package/src/components/feedback/EmptyState.tsx +113 -0
- package/src/components/feedback/Progress.test.tsx +60 -0
- package/src/components/feedback/Progress.tsx +30 -0
- package/src/components/feedback/ProgressBar.tsx +158 -0
- package/src/components/feedback/Skeleton.test.tsx +39 -0
- package/src/components/feedback/Skeleton.tsx +45 -0
- package/src/components/feedback/Sonner.tsx +28 -0
- package/src/components/feedback/Spinner.test.tsx +33 -0
- package/src/components/feedback/Spinner.tsx +99 -0
- package/src/components/feedback/Stepper.tsx +307 -0
- package/src/components/feedback/Toast/Toast.tsx +243 -0
- package/src/components/feedback/Toast/index.ts +2 -0
- package/src/components/feedback/index.ts +9 -0
- package/src/components/forms/Checkbox.test.tsx +40 -0
- package/src/components/forms/Checkbox.tsx +31 -0
- package/src/components/forms/ColorPicker.tsx +118 -0
- package/src/components/forms/Combobox.tsx +96 -0
- package/src/components/forms/DragDrop.tsx +440 -0
- package/src/components/forms/FileUpload.tsx +252 -0
- package/src/components/forms/FilterButton.tsx +65 -0
- package/src/components/forms/Form.tsx +197 -0
- package/src/components/forms/Input.test.tsx +46 -0
- package/src/components/forms/Input.tsx +43 -0
- package/src/components/forms/InputOTP.tsx +81 -0
- package/src/components/forms/Label.test.tsx +20 -0
- package/src/components/forms/Label.tsx +25 -0
- package/src/components/forms/RadioGroup.tsx +51 -0
- package/src/components/forms/SearchBar.tsx +215 -0
- package/src/components/forms/Select.test.tsx +118 -0
- package/src/components/forms/Select.tsx +274 -0
- package/src/components/forms/Slider.tsx +29 -0
- package/src/components/forms/Switch.test.tsx +76 -0
- package/src/components/forms/Switch.tsx +30 -0
- package/src/components/forms/TextField.tsx +152 -0
- package/src/components/forms/Textarea.test.tsx +41 -0
- package/src/components/forms/Textarea.tsx +29 -0
- package/src/components/forms/ThemeSwitcher.tsx +290 -0
- package/src/components/forms/ThemeToggle.tsx +151 -0
- package/src/components/forms/index.ts +19 -0
- package/src/components/layout/Accordion.test.tsx +66 -0
- package/src/components/layout/Accordion.tsx +64 -0
- package/src/components/layout/AspectRatio.tsx +7 -0
- package/src/components/layout/Carousel.tsx +277 -0
- package/src/components/layout/Collapsible.test.tsx +40 -0
- package/src/components/layout/Collapsible.tsx +31 -0
- package/src/components/layout/Container.test.tsx +45 -0
- package/src/components/layout/Container.tsx +99 -0
- package/src/components/layout/CustomizerPanel.tsx +400 -0
- package/src/components/layout/DatePicker.tsx +57 -0
- package/src/components/layout/Footer/Footer.tsx +175 -0
- package/src/components/layout/Footer/index.ts +2 -0
- package/src/components/layout/GlassSurface.tsx +82 -0
- package/src/components/layout/Grid.test.tsx +31 -0
- package/src/components/layout/Grid.tsx +130 -0
- package/src/components/layout/Header/Header.tsx +450 -0
- package/src/components/layout/Header/index.ts +2 -0
- package/src/components/layout/PageLayout.tsx +180 -0
- package/src/components/layout/PageTemplate.tsx +158 -0
- package/src/components/layout/Resizable.tsx +48 -0
- package/src/components/layout/ScrollArea.tsx +53 -0
- package/src/components/layout/Separator.test.tsx +28 -0
- package/src/components/layout/Separator.tsx +29 -0
- package/src/components/layout/Sidebar.tsx +171 -0
- package/src/components/layout/Stack.test.tsx +41 -0
- package/src/components/layout/Stack.tsx +89 -0
- package/src/components/layout/glass-surface.css +60 -0
- package/src/components/layout/index.ts +18 -0
- package/src/components/motion/AnimatedBeam.tsx +159 -0
- package/src/components/navigation/Breadcrumb.test.tsx +57 -0
- package/src/components/navigation/Breadcrumb.tsx +119 -0
- package/src/components/navigation/Breadcrumbs.tsx +221 -0
- package/src/components/navigation/Command.tsx +159 -0
- package/src/components/navigation/Menubar.tsx +115 -0
- package/src/components/navigation/NavLink.tsx +55 -0
- package/src/components/navigation/NavigationMenu.tsx +125 -0
- package/src/components/navigation/Pagination.tsx +121 -0
- package/src/components/navigation/SecondaryNav.tsx +100 -0
- package/src/components/navigation/Tabs.test.tsx +47 -0
- package/src/components/navigation/Tabs.tsx +60 -0
- package/src/components/navigation/TertiaryNav.tsx +90 -0
- package/src/components/navigation/index.ts +10 -0
- package/src/components/overlays/AlertDialog.test.tsx +69 -0
- package/src/components/overlays/AlertDialog.tsx +166 -0
- package/src/components/overlays/ContextMenu.tsx +243 -0
- package/src/components/overlays/Dialog.test.tsx +79 -0
- package/src/components/overlays/Dialog.tsx +158 -0
- package/src/components/overlays/Drawer.tsx +128 -0
- package/src/components/overlays/Dropdown.tsx +253 -0
- package/src/components/overlays/DropdownMenu.tsx +242 -0
- package/src/components/overlays/HoverCard.tsx +32 -0
- package/src/components/overlays/Modal.tsx +250 -0
- package/src/components/overlays/NotificationCenter.tsx +364 -0
- package/src/components/overlays/Popover.test.tsx +40 -0
- package/src/components/overlays/Popover.tsx +46 -0
- package/src/components/overlays/Sheet.tsx +163 -0
- package/src/components/overlays/Tooltip.test.tsx +33 -0
- package/src/components/overlays/Tooltip.tsx +32 -0
- package/src/components/overlays/index.ts +12 -0
- package/src/dates.ts +2 -0
- package/src/dnd.ts +1 -0
- package/src/forms.ts +1 -0
- package/src/globals.css +187 -0
- package/src/hooks/index.ts +6 -0
- package/src/hooks/useForm.ts +247 -0
- package/src/hooks/useMotionPreference.test.ts +102 -0
- package/src/hooks/useMotionPreference.ts +78 -0
- package/src/hooks/useTheme.ts +58 -0
- package/src/hooks.ts +9 -0
- package/src/index.ts +168 -0
- package/src/lib/animations.ts +356 -0
- package/src/lib/breadcrumbs.ts +94 -0
- package/src/lib/colors.ts +493 -0
- package/src/lib/store/customizer.ts +482 -0
- package/src/lib/store/index.ts +3 -0
- package/src/lib/store/theme.ts +55 -0
- package/src/lib/syntax-parser/index.ts +50 -0
- package/src/lib/syntax-parser/patterns.ts +64 -0
- package/src/lib/syntax-parser/tokenizer.ts +117 -0
- package/src/lib/syntax-parser/types.ts +27 -0
- package/src/lib/utils.ts +6 -0
- package/src/lib/validation.ts +204 -0
- package/src/lib/webgl/Color.ts +11 -0
- package/src/lib/webgl/Mesh.ts +41 -0
- package/src/lib/webgl/Program.ts +118 -0
- package/src/lib/webgl/Renderer.ts +51 -0
- package/src/lib/webgl/Triangle.ts +27 -0
- package/src/lib/webgl/Vec3.ts +18 -0
- package/src/lib/webgl/index.ts +13 -0
- package/src/nativewind-env.d.ts +1 -0
- package/src/providers/ThemeProvider.tsx +461 -0
- package/src/providers/index.ts +1 -0
- package/src/providers.ts +7 -0
- package/src/tables.ts +1 -0
- package/src/test/setup.ts +39 -0
- package/src/theme.css +158 -0
- package/src/tokens.ts +7 -0
- package/src/utils.ts +12 -0
- package/src/webgl.ts +1 -0
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import * as React from "react"
|
|
3
|
+
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"
|
|
4
|
+
import { Check, ChevronRight, Circle } from "lucide-react"
|
|
5
|
+
|
|
6
|
+
import { cn } from "../../lib/utils"
|
|
7
|
+
|
|
8
|
+
const DropdownMenu = DropdownMenuPrimitive.Root
|
|
9
|
+
|
|
10
|
+
const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger
|
|
11
|
+
|
|
12
|
+
const DropdownMenuGroup = DropdownMenuPrimitive.Group
|
|
13
|
+
|
|
14
|
+
const DropdownMenuPortal = DropdownMenuPrimitive.Portal
|
|
15
|
+
|
|
16
|
+
const DropdownMenuSub = DropdownMenuPrimitive.Sub
|
|
17
|
+
|
|
18
|
+
const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup
|
|
19
|
+
|
|
20
|
+
const DropdownMenuSubTrigger = (
|
|
21
|
+
{
|
|
22
|
+
ref,
|
|
23
|
+
className,
|
|
24
|
+
inset,
|
|
25
|
+
children,
|
|
26
|
+
...props
|
|
27
|
+
}: React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {
|
|
28
|
+
inset?: boolean
|
|
29
|
+
} & {
|
|
30
|
+
ref?: React.Ref<React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>>;
|
|
31
|
+
}
|
|
32
|
+
) => (<DropdownMenuPrimitive.SubTrigger
|
|
33
|
+
ref={ref}
|
|
34
|
+
className={cn(
|
|
35
|
+
"flex cursor-default select-none items-center rounded-xs px-2 py-1.5 text-sm outline-none focus:bg-primary focus:text-primary-foreground data-[state=open]:bg-primary data-[state=open]:text-primary-foreground",
|
|
36
|
+
inset && "pl-8",
|
|
37
|
+
className
|
|
38
|
+
)}
|
|
39
|
+
{...props}
|
|
40
|
+
>
|
|
41
|
+
{children}
|
|
42
|
+
<ChevronRight className="ml-auto h-4 w-4" />
|
|
43
|
+
</DropdownMenuPrimitive.SubTrigger>)
|
|
44
|
+
|
|
45
|
+
const DropdownMenuSubContent = (
|
|
46
|
+
{
|
|
47
|
+
ref,
|
|
48
|
+
className,
|
|
49
|
+
style,
|
|
50
|
+
...props
|
|
51
|
+
}: React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent> & {
|
|
52
|
+
ref?: React.Ref<React.ElementRef<typeof DropdownMenuPrimitive.SubContent>>;
|
|
53
|
+
}
|
|
54
|
+
) => (<DropdownMenuPrimitive.SubContent
|
|
55
|
+
ref={ref}
|
|
56
|
+
className={cn(
|
|
57
|
+
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
|
58
|
+
className
|
|
59
|
+
)}
|
|
60
|
+
style={{
|
|
61
|
+
backgroundColor: 'var(--color-popover, #ffffff)',
|
|
62
|
+
color: 'var(--color-popover-foreground, #0a0a0a)',
|
|
63
|
+
border: '1px solid var(--color-border, #d4d4d4)',
|
|
64
|
+
borderRadius: 'var(--radius, 0.5rem)',
|
|
65
|
+
boxShadow: '0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)',
|
|
66
|
+
zIndex: 50,
|
|
67
|
+
overflow: 'hidden',
|
|
68
|
+
...style,
|
|
69
|
+
}}
|
|
70
|
+
{...props}
|
|
71
|
+
/>)
|
|
72
|
+
|
|
73
|
+
const DropdownMenuContent = (
|
|
74
|
+
{
|
|
75
|
+
ref,
|
|
76
|
+
className,
|
|
77
|
+
sideOffset = 4,
|
|
78
|
+
style,
|
|
79
|
+
...props
|
|
80
|
+
}: React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content> & {
|
|
81
|
+
ref?: React.Ref<React.ElementRef<typeof DropdownMenuPrimitive.Content>>;
|
|
82
|
+
}
|
|
83
|
+
) => (<DropdownMenuPrimitive.Portal>
|
|
84
|
+
<DropdownMenuPrimitive.Content
|
|
85
|
+
ref={ref}
|
|
86
|
+
sideOffset={sideOffset}
|
|
87
|
+
className={cn(
|
|
88
|
+
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
|
89
|
+
className
|
|
90
|
+
)}
|
|
91
|
+
style={{
|
|
92
|
+
backgroundColor: 'var(--color-popover, #ffffff)',
|
|
93
|
+
color: 'var(--color-popover-foreground, #0a0a0a)',
|
|
94
|
+
border: '1px solid var(--color-border, #d4d4d4)',
|
|
95
|
+
borderRadius: 'var(--radius, 0.5rem)',
|
|
96
|
+
boxShadow: '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)',
|
|
97
|
+
zIndex: 50,
|
|
98
|
+
overflow: 'hidden',
|
|
99
|
+
...style,
|
|
100
|
+
}}
|
|
101
|
+
{...props}
|
|
102
|
+
/>
|
|
103
|
+
</DropdownMenuPrimitive.Portal>)
|
|
104
|
+
|
|
105
|
+
const DropdownMenuItem = (
|
|
106
|
+
{
|
|
107
|
+
ref,
|
|
108
|
+
className,
|
|
109
|
+
inset,
|
|
110
|
+
...props
|
|
111
|
+
}: React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {
|
|
112
|
+
inset?: boolean
|
|
113
|
+
} & {
|
|
114
|
+
ref?: React.Ref<React.ElementRef<typeof DropdownMenuPrimitive.Item>>;
|
|
115
|
+
}
|
|
116
|
+
) => (<DropdownMenuPrimitive.Item
|
|
117
|
+
ref={ref}
|
|
118
|
+
className={cn(
|
|
119
|
+
"relative flex cursor-default select-none items-center rounded-xs px-2 py-1.5 text-sm outline-none transition-colors focus:bg-primary focus:text-primary-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
120
|
+
inset && "pl-8",
|
|
121
|
+
className
|
|
122
|
+
)}
|
|
123
|
+
{...props}
|
|
124
|
+
/>)
|
|
125
|
+
|
|
126
|
+
const DropdownMenuCheckboxItem = (
|
|
127
|
+
{
|
|
128
|
+
ref,
|
|
129
|
+
className,
|
|
130
|
+
children,
|
|
131
|
+
checked,
|
|
132
|
+
...props
|
|
133
|
+
}: React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem> & {
|
|
134
|
+
ref?: React.Ref<React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>>;
|
|
135
|
+
}
|
|
136
|
+
) => (<DropdownMenuPrimitive.CheckboxItem
|
|
137
|
+
ref={ref}
|
|
138
|
+
className={cn(
|
|
139
|
+
"relative flex cursor-default select-none items-center rounded-xs py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-primary focus:text-primary-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
140
|
+
className
|
|
141
|
+
)}
|
|
142
|
+
checked={checked}
|
|
143
|
+
{...props}
|
|
144
|
+
>
|
|
145
|
+
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
|
146
|
+
<DropdownMenuPrimitive.ItemIndicator>
|
|
147
|
+
<Check className="h-4 w-4" />
|
|
148
|
+
</DropdownMenuPrimitive.ItemIndicator>
|
|
149
|
+
</span>
|
|
150
|
+
{children}
|
|
151
|
+
</DropdownMenuPrimitive.CheckboxItem>)
|
|
152
|
+
|
|
153
|
+
const DropdownMenuRadioItem = (
|
|
154
|
+
{
|
|
155
|
+
ref,
|
|
156
|
+
className,
|
|
157
|
+
children,
|
|
158
|
+
...props
|
|
159
|
+
}: React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem> & {
|
|
160
|
+
ref?: React.Ref<React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>>;
|
|
161
|
+
}
|
|
162
|
+
) => (<DropdownMenuPrimitive.RadioItem
|
|
163
|
+
ref={ref}
|
|
164
|
+
className={cn(
|
|
165
|
+
"relative flex cursor-default select-none items-center rounded-xs py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-primary focus:text-primary-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
166
|
+
className
|
|
167
|
+
)}
|
|
168
|
+
{...props}
|
|
169
|
+
>
|
|
170
|
+
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
|
171
|
+
<DropdownMenuPrimitive.ItemIndicator>
|
|
172
|
+
<Circle className="h-2 w-2 fill-current" />
|
|
173
|
+
</DropdownMenuPrimitive.ItemIndicator>
|
|
174
|
+
</span>
|
|
175
|
+
{children}
|
|
176
|
+
</DropdownMenuPrimitive.RadioItem>)
|
|
177
|
+
|
|
178
|
+
const DropdownMenuLabel = (
|
|
179
|
+
{
|
|
180
|
+
ref,
|
|
181
|
+
className,
|
|
182
|
+
inset,
|
|
183
|
+
...props
|
|
184
|
+
}: React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {
|
|
185
|
+
inset?: boolean
|
|
186
|
+
} & {
|
|
187
|
+
ref?: React.Ref<React.ElementRef<typeof DropdownMenuPrimitive.Label>>;
|
|
188
|
+
}
|
|
189
|
+
) => (<DropdownMenuPrimitive.Label
|
|
190
|
+
ref={ref}
|
|
191
|
+
className={cn(
|
|
192
|
+
"px-2 py-1.5 text-sm font-semibold",
|
|
193
|
+
inset && "pl-8",
|
|
194
|
+
className
|
|
195
|
+
)}
|
|
196
|
+
{...props}
|
|
197
|
+
/>)
|
|
198
|
+
|
|
199
|
+
const DropdownMenuSeparator = (
|
|
200
|
+
{
|
|
201
|
+
ref,
|
|
202
|
+
className,
|
|
203
|
+
...props
|
|
204
|
+
}: React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator> & {
|
|
205
|
+
ref?: React.Ref<React.ElementRef<typeof DropdownMenuPrimitive.Separator>>;
|
|
206
|
+
}
|
|
207
|
+
) => (<DropdownMenuPrimitive.Separator
|
|
208
|
+
ref={ref}
|
|
209
|
+
className={cn("-mx-1 my-1 h-px bg-muted", className)}
|
|
210
|
+
{...props}
|
|
211
|
+
/>)
|
|
212
|
+
|
|
213
|
+
const DropdownMenuShortcut = ({
|
|
214
|
+
className,
|
|
215
|
+
...props
|
|
216
|
+
}: React.HTMLAttributes<HTMLSpanElement>) => {
|
|
217
|
+
return (
|
|
218
|
+
<span
|
|
219
|
+
className={cn("ml-auto text-xs tracking-widest opacity-60", className)}
|
|
220
|
+
{...props}
|
|
221
|
+
/>
|
|
222
|
+
)
|
|
223
|
+
}
|
|
224
|
+
DropdownMenuShortcut.displayName = "DropdownMenuShortcut"
|
|
225
|
+
|
|
226
|
+
export {
|
|
227
|
+
DropdownMenu,
|
|
228
|
+
DropdownMenuTrigger,
|
|
229
|
+
DropdownMenuContent,
|
|
230
|
+
DropdownMenuItem,
|
|
231
|
+
DropdownMenuCheckboxItem,
|
|
232
|
+
DropdownMenuRadioItem,
|
|
233
|
+
DropdownMenuLabel,
|
|
234
|
+
DropdownMenuSeparator,
|
|
235
|
+
DropdownMenuShortcut,
|
|
236
|
+
DropdownMenuGroup,
|
|
237
|
+
DropdownMenuPortal,
|
|
238
|
+
DropdownMenuSub,
|
|
239
|
+
DropdownMenuSubContent,
|
|
240
|
+
DropdownMenuSubTrigger,
|
|
241
|
+
DropdownMenuRadioGroup,
|
|
242
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import * as React from "react"
|
|
3
|
+
import * as HoverCardPrimitive from "@radix-ui/react-hover-card"
|
|
4
|
+
|
|
5
|
+
import { cn } from "../../lib/utils"
|
|
6
|
+
|
|
7
|
+
const HoverCard = HoverCardPrimitive.Root
|
|
8
|
+
|
|
9
|
+
const HoverCardTrigger = HoverCardPrimitive.Trigger
|
|
10
|
+
|
|
11
|
+
const HoverCardContent = (
|
|
12
|
+
{
|
|
13
|
+
ref,
|
|
14
|
+
className,
|
|
15
|
+
align = "center",
|
|
16
|
+
sideOffset = 4,
|
|
17
|
+
...props
|
|
18
|
+
}: React.ComponentPropsWithoutRef<typeof HoverCardPrimitive.Content> & {
|
|
19
|
+
ref?: React.Ref<React.ElementRef<typeof HoverCardPrimitive.Content>>;
|
|
20
|
+
}
|
|
21
|
+
) => (<HoverCardPrimitive.Content
|
|
22
|
+
ref={ref}
|
|
23
|
+
align={align}
|
|
24
|
+
sideOffset={sideOffset}
|
|
25
|
+
className={cn(
|
|
26
|
+
"z-50 w-64 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
|
27
|
+
className
|
|
28
|
+
)}
|
|
29
|
+
{...props}
|
|
30
|
+
/>)
|
|
31
|
+
|
|
32
|
+
export { HoverCard, HoverCardTrigger, HoverCardContent }
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React, { useEffect } from 'react';
|
|
4
|
+
import { useMotionPreference } from '../../hooks';
|
|
5
|
+
|
|
6
|
+
export interface ModalProps {
|
|
7
|
+
/**
|
|
8
|
+
* Whether the modal is open
|
|
9
|
+
*/
|
|
10
|
+
isOpen: boolean;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Callback fired when the modal should close
|
|
14
|
+
*/
|
|
15
|
+
onClose: () => void;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Modal title (optional)
|
|
19
|
+
*/
|
|
20
|
+
title?: string;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Modal content
|
|
24
|
+
*/
|
|
25
|
+
children: React.ReactNode;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Footer content (typically buttons)
|
|
29
|
+
*/
|
|
30
|
+
footer?: React.ReactNode;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Size of the modal
|
|
34
|
+
* @default 'md'
|
|
35
|
+
*/
|
|
36
|
+
size?: 'sm' | 'md' | 'lg' | 'xl' | 'full';
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Whether clicking the overlay closes the modal
|
|
40
|
+
* @default true
|
|
41
|
+
*/
|
|
42
|
+
closeOnOverlayClick?: boolean;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Whether pressing Escape closes the modal
|
|
46
|
+
* @default true
|
|
47
|
+
*/
|
|
48
|
+
closeOnEscape?: boolean;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Additional CSS classes for the modal content
|
|
52
|
+
*/
|
|
53
|
+
className?: string;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Modal Component
|
|
58
|
+
*
|
|
59
|
+
* A dialog overlay for focused user interactions.
|
|
60
|
+
*
|
|
61
|
+
* Features:
|
|
62
|
+
* - Keyboard accessible (Escape to close, focus trap)
|
|
63
|
+
* - Click outside to close (optional)
|
|
64
|
+
* - Multiple size variants
|
|
65
|
+
* - Smooth animations
|
|
66
|
+
* - Blocks body scroll when open
|
|
67
|
+
* - Theme-aware styling
|
|
68
|
+
*
|
|
69
|
+
* Example:
|
|
70
|
+
* ```tsx
|
|
71
|
+
* const [isOpen, setIsOpen] = useState(false);
|
|
72
|
+
*
|
|
73
|
+
* <Modal
|
|
74
|
+
* isOpen={isOpen}
|
|
75
|
+
* onClose={() => setIsOpen(false)}
|
|
76
|
+
* title="Confirm Action"
|
|
77
|
+
* footer={
|
|
78
|
+
* <>
|
|
79
|
+
* <Button variant="ghost" onClick={() => setIsOpen(false)}>Cancel</Button>
|
|
80
|
+
* <Button onClick={handleConfirm}>Confirm</Button>
|
|
81
|
+
* </>
|
|
82
|
+
* }
|
|
83
|
+
* >
|
|
84
|
+
* <p>Are you sure you want to continue?</p>
|
|
85
|
+
* </Modal>
|
|
86
|
+
* ```
|
|
87
|
+
*/
|
|
88
|
+
export const Modal: React.FC<ModalProps> = ({
|
|
89
|
+
isOpen,
|
|
90
|
+
onClose,
|
|
91
|
+
title,
|
|
92
|
+
children,
|
|
93
|
+
footer,
|
|
94
|
+
size = 'md',
|
|
95
|
+
closeOnOverlayClick = true,
|
|
96
|
+
closeOnEscape = true,
|
|
97
|
+
className = '',
|
|
98
|
+
}) => {
|
|
99
|
+
const { shouldAnimate, scale } = useMotionPreference();
|
|
100
|
+
|
|
101
|
+
const animationDuration = shouldAnimate && scale > 0 ? `${0.2 * (5 / scale)}s` : '0s';
|
|
102
|
+
|
|
103
|
+
// Lock body scroll when modal is open
|
|
104
|
+
useEffect(() => {
|
|
105
|
+
if (isOpen) {
|
|
106
|
+
document.body.style.overflow = 'hidden';
|
|
107
|
+
} else {
|
|
108
|
+
document.body.style.overflow = '';
|
|
109
|
+
}
|
|
110
|
+
return () => {
|
|
111
|
+
document.body.style.overflow = '';
|
|
112
|
+
};
|
|
113
|
+
}, [isOpen]);
|
|
114
|
+
|
|
115
|
+
// Handle escape key
|
|
116
|
+
useEffect(() => {
|
|
117
|
+
if (!isOpen || !closeOnEscape) return;
|
|
118
|
+
|
|
119
|
+
const handleEscape = (e: KeyboardEvent) => {
|
|
120
|
+
if (e.key === 'Escape') {
|
|
121
|
+
onClose();
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
document.addEventListener('keydown', handleEscape);
|
|
126
|
+
return () => document.removeEventListener('keydown', handleEscape);
|
|
127
|
+
}, [isOpen, closeOnEscape, onClose]);
|
|
128
|
+
|
|
129
|
+
if (!isOpen) return null;
|
|
130
|
+
|
|
131
|
+
const sizeClasses = {
|
|
132
|
+
sm: 'max-w-md',
|
|
133
|
+
md: 'max-w-lg',
|
|
134
|
+
lg: 'max-w-2xl',
|
|
135
|
+
xl: 'max-w-4xl',
|
|
136
|
+
full: 'max-w-[95vw] max-h-[95vh]',
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const handleOverlayClick = (e: React.MouseEvent<HTMLDivElement>) => {
|
|
140
|
+
if (closeOnOverlayClick && e.target === e.currentTarget) {
|
|
141
|
+
onClose();
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
return (
|
|
146
|
+
<div
|
|
147
|
+
className={`
|
|
148
|
+
fixed inset-0 z-[9999] flex items-center justify-center p-4
|
|
149
|
+
bg-black/50 backdrop-blur-xs
|
|
150
|
+
${shouldAnimate ? 'animate-fade-in' : ''}
|
|
151
|
+
`}
|
|
152
|
+
style={{ animationDuration }}
|
|
153
|
+
onClick={handleOverlayClick}
|
|
154
|
+
role="dialog"
|
|
155
|
+
aria-modal="true"
|
|
156
|
+
aria-labelledby={title ? 'modal-title' : undefined}
|
|
157
|
+
>
|
|
158
|
+
<div
|
|
159
|
+
className={`
|
|
160
|
+
${sizeClasses[size]}
|
|
161
|
+
w-full
|
|
162
|
+
bg-[var(--color-surface)]
|
|
163
|
+
rounded-2xl
|
|
164
|
+
shadow-2xl
|
|
165
|
+
border border-[var(--color-border)]
|
|
166
|
+
flex flex-col
|
|
167
|
+
max-h-[90vh]
|
|
168
|
+
${shouldAnimate ? 'animate-scale-in' : ''}
|
|
169
|
+
${className}
|
|
170
|
+
`}
|
|
171
|
+
style={{ animationDuration }}
|
|
172
|
+
>
|
|
173
|
+
{/* Header */}
|
|
174
|
+
{title && (
|
|
175
|
+
<div className="flex items-center justify-between px-6 py-4 border-b border-[var(--color-border)]">
|
|
176
|
+
<h2
|
|
177
|
+
id="modal-title"
|
|
178
|
+
className="text-xl font-semibold text-[var(--color-text-primary)]"
|
|
179
|
+
>
|
|
180
|
+
{title}
|
|
181
|
+
</h2>
|
|
182
|
+
<button
|
|
183
|
+
onClick={onClose}
|
|
184
|
+
className="
|
|
185
|
+
p-2 rounded-lg
|
|
186
|
+
text-[var(--color-text-primary)]
|
|
187
|
+
hover:bg-[var(--color-hover)]
|
|
188
|
+
transition-colors
|
|
189
|
+
focus-visible:outline-none
|
|
190
|
+
focus-visible:ring-2
|
|
191
|
+
focus-visible:ring-[var(--color-focus)]
|
|
192
|
+
"
|
|
193
|
+
aria-label="Close modal"
|
|
194
|
+
>
|
|
195
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
|
196
|
+
<line x1="18" y1="6" x2="6" y2="18" />
|
|
197
|
+
<line x1="6" y1="6" x2="18" y2="18" />
|
|
198
|
+
</svg>
|
|
199
|
+
</button>
|
|
200
|
+
</div>
|
|
201
|
+
)}
|
|
202
|
+
|
|
203
|
+
{/* Content */}
|
|
204
|
+
<div className="flex-1 overflow-y-auto px-6 py-4">
|
|
205
|
+
{children}
|
|
206
|
+
</div>
|
|
207
|
+
|
|
208
|
+
{/* Footer */}
|
|
209
|
+
{footer && (
|
|
210
|
+
<div className="flex items-center justify-end gap-3 px-6 py-4 border-t border-[var(--color-border)]">
|
|
211
|
+
{footer}
|
|
212
|
+
</div>
|
|
213
|
+
)}
|
|
214
|
+
</div>
|
|
215
|
+
</div>
|
|
216
|
+
);
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
Modal.displayName = 'Modal';
|
|
220
|
+
|
|
221
|
+
// Add animation keyframes
|
|
222
|
+
if (typeof document !== 'undefined') {
|
|
223
|
+
const style = document.createElement('style');
|
|
224
|
+
style.textContent = `
|
|
225
|
+
@keyframes fade-in {
|
|
226
|
+
from { opacity: 0; }
|
|
227
|
+
to { opacity: 1; }
|
|
228
|
+
}
|
|
229
|
+
@keyframes scale-in {
|
|
230
|
+
from {
|
|
231
|
+
opacity: 0;
|
|
232
|
+
transform: scale(0.95);
|
|
233
|
+
}
|
|
234
|
+
to {
|
|
235
|
+
opacity: 1;
|
|
236
|
+
transform: scale(1);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
.animate-fade-in {
|
|
240
|
+
animation: fade-in 0.2s ease-out;
|
|
241
|
+
}
|
|
242
|
+
.animate-scale-in {
|
|
243
|
+
animation: scale-in 0.2s ease-out;
|
|
244
|
+
}
|
|
245
|
+
`;
|
|
246
|
+
if (!document.querySelector('style[data-modal-animations]')) {
|
|
247
|
+
style.setAttribute('data-modal-animations', 'true');
|
|
248
|
+
document.head.appendChild(style);
|
|
249
|
+
}
|
|
250
|
+
}
|