@oppulence/design-system 1.0.2

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 (80) hide show
  1. package/README.md +115 -0
  2. package/components.json +21 -0
  3. package/hooks/use-mobile.tsx +21 -0
  4. package/lib/utils.ts +6 -0
  5. package/package.json +104 -0
  6. package/postcss.config.mjs +8 -0
  7. package/src/components/atoms/aspect-ratio.tsx +21 -0
  8. package/src/components/atoms/avatar.tsx +91 -0
  9. package/src/components/atoms/badge.tsx +47 -0
  10. package/src/components/atoms/button.tsx +128 -0
  11. package/src/components/atoms/checkbox.tsx +24 -0
  12. package/src/components/atoms/container.tsx +42 -0
  13. package/src/components/atoms/heading.tsx +56 -0
  14. package/src/components/atoms/index.ts +21 -0
  15. package/src/components/atoms/input.tsx +18 -0
  16. package/src/components/atoms/kbd.tsx +23 -0
  17. package/src/components/atoms/label.tsx +15 -0
  18. package/src/components/atoms/logo.tsx +52 -0
  19. package/src/components/atoms/progress.tsx +79 -0
  20. package/src/components/atoms/separator.tsx +17 -0
  21. package/src/components/atoms/skeleton.tsx +13 -0
  22. package/src/components/atoms/slider.tsx +56 -0
  23. package/src/components/atoms/spinner.tsx +14 -0
  24. package/src/components/atoms/stack.tsx +126 -0
  25. package/src/components/atoms/switch.tsx +26 -0
  26. package/src/components/atoms/text.tsx +69 -0
  27. package/src/components/atoms/textarea.tsx +19 -0
  28. package/src/components/atoms/toggle.tsx +40 -0
  29. package/src/components/molecules/accordion.tsx +72 -0
  30. package/src/components/molecules/ai-chat.tsx +251 -0
  31. package/src/components/molecules/alert.tsx +131 -0
  32. package/src/components/molecules/breadcrumb.tsx +301 -0
  33. package/src/components/molecules/button-group.tsx +96 -0
  34. package/src/components/molecules/card.tsx +184 -0
  35. package/src/components/molecules/collapsible.tsx +21 -0
  36. package/src/components/molecules/command-search.tsx +148 -0
  37. package/src/components/molecules/empty.tsx +98 -0
  38. package/src/components/molecules/field.tsx +217 -0
  39. package/src/components/molecules/grid.tsx +141 -0
  40. package/src/components/molecules/hover-card.tsx +45 -0
  41. package/src/components/molecules/index.ts +29 -0
  42. package/src/components/molecules/input-group.tsx +151 -0
  43. package/src/components/molecules/input-otp.tsx +74 -0
  44. package/src/components/molecules/item.tsx +194 -0
  45. package/src/components/molecules/page-header.tsx +89 -0
  46. package/src/components/molecules/pagination.tsx +130 -0
  47. package/src/components/molecules/popover.tsx +96 -0
  48. package/src/components/molecules/radio-group.tsx +37 -0
  49. package/src/components/molecules/resizable.tsx +52 -0
  50. package/src/components/molecules/scroll-area.tsx +45 -0
  51. package/src/components/molecules/section.tsx +108 -0
  52. package/src/components/molecules/select.tsx +201 -0
  53. package/src/components/molecules/settings.tsx +197 -0
  54. package/src/components/molecules/table.tsx +111 -0
  55. package/src/components/molecules/tabs.tsx +74 -0
  56. package/src/components/molecules/theme-switcher.tsx +187 -0
  57. package/src/components/molecules/toggle-group.tsx +89 -0
  58. package/src/components/molecules/tooltip.tsx +66 -0
  59. package/src/components/organisms/alert-dialog.tsx +152 -0
  60. package/src/components/organisms/app-shell.tsx +939 -0
  61. package/src/components/organisms/calendar.tsx +212 -0
  62. package/src/components/organisms/carousel.tsx +230 -0
  63. package/src/components/organisms/chart.tsx +333 -0
  64. package/src/components/organisms/combobox.tsx +274 -0
  65. package/src/components/organisms/command.tsx +200 -0
  66. package/src/components/organisms/context-menu.tsx +229 -0
  67. package/src/components/organisms/dialog.tsx +134 -0
  68. package/src/components/organisms/drawer.tsx +123 -0
  69. package/src/components/organisms/dropdown-menu.tsx +256 -0
  70. package/src/components/organisms/index.ts +17 -0
  71. package/src/components/organisms/menubar.tsx +203 -0
  72. package/src/components/organisms/navigation-menu.tsx +143 -0
  73. package/src/components/organisms/page-layout.tsx +105 -0
  74. package/src/components/organisms/sheet.tsx +126 -0
  75. package/src/components/organisms/sidebar.tsx +723 -0
  76. package/src/components/organisms/sonner.tsx +41 -0
  77. package/src/components/ui/index.ts +3 -0
  78. package/src/index.ts +3 -0
  79. package/src/styles/globals.css +297 -0
  80. package/tailwind.config.ts +77 -0
@@ -0,0 +1,72 @@
1
+ "use client";
2
+
3
+ import { Accordion as AccordionPrimitive } from "@base-ui/react/accordion";
4
+ import { ChevronDownIcon, ChevronUpIcon } from "lucide-react";
5
+
6
+ function Accordion({
7
+ ...props
8
+ }: Omit<AccordionPrimitive.Root.Props, "className">) {
9
+ return (
10
+ <AccordionPrimitive.Root
11
+ data-slot="accordion"
12
+ className="flex w-full flex-col"
13
+ {...props}
14
+ />
15
+ );
16
+ }
17
+
18
+ function AccordionItem({
19
+ ...props
20
+ }: Omit<AccordionPrimitive.Item.Props, "className">) {
21
+ return (
22
+ <AccordionPrimitive.Item
23
+ data-slot="accordion-item"
24
+ className="not-last:border-b"
25
+ {...props}
26
+ />
27
+ );
28
+ }
29
+
30
+ function AccordionTrigger({
31
+ children,
32
+ ...props
33
+ }: Omit<AccordionPrimitive.Trigger.Props, "className">) {
34
+ return (
35
+ <AccordionPrimitive.Header className="flex">
36
+ <AccordionPrimitive.Trigger
37
+ data-slot="accordion-trigger"
38
+ className="focus-visible:ring-ring/50 focus-visible:border-ring focus-visible:after:border-ring **:data-[slot=accordion-trigger-icon]:text-muted-foreground rounded-md py-4 text-left text-sm font-medium hover:underline focus-visible:ring-[3px] **:data-[slot=accordion-trigger-icon]:ml-auto **:data-[slot=accordion-trigger-icon]:size-4 group/accordion-trigger relative flex flex-1 items-start justify-between border border-transparent transition-all outline-none disabled:pointer-events-none disabled:opacity-50"
39
+ {...props}
40
+ >
41
+ {children}
42
+ <ChevronDownIcon
43
+ data-slot="accordion-trigger-icon"
44
+ className="pointer-events-none shrink-0 group-aria-expanded/accordion-trigger:hidden"
45
+ />
46
+ <ChevronUpIcon
47
+ data-slot="accordion-trigger-icon"
48
+ className="pointer-events-none hidden shrink-0 group-aria-expanded/accordion-trigger:inline"
49
+ />
50
+ </AccordionPrimitive.Trigger>
51
+ </AccordionPrimitive.Header>
52
+ );
53
+ }
54
+
55
+ function AccordionContent({
56
+ children,
57
+ ...props
58
+ }: Omit<AccordionPrimitive.Panel.Props, "className">) {
59
+ return (
60
+ <AccordionPrimitive.Panel
61
+ data-slot="accordion-content"
62
+ className="data-open:animate-accordion-down data-closed:animate-accordion-up text-sm overflow-hidden"
63
+ {...props}
64
+ >
65
+ <div className="pt-0 pb-4 [&_a]:hover:text-foreground h-(--accordion-panel-height) data-ending-style:h-0 data-starting-style:h-0 [&_a]:underline [&_a]:underline-offset-3 [&_p:not(:last-child)]:mb-4">
66
+ {children}
67
+ </div>
68
+ </AccordionPrimitive.Panel>
69
+ );
70
+ }
71
+
72
+ export { Accordion, AccordionContent, AccordionItem, AccordionTrigger };
@@ -0,0 +1,251 @@
1
+ "use client";
2
+
3
+ import { cva, type VariantProps } from "class-variance-authority";
4
+ import { MessageCircleIcon, SparklesIcon, XIcon } from "lucide-react";
5
+ import * as React from "react";
6
+
7
+ const aiChatTriggerVariants = cva(
8
+ "fixed bottom-6 right-6 z-50 flex items-center justify-center rounded-full transition-all duration-200 cursor-pointer",
9
+ {
10
+ variants: {
11
+ size: {
12
+ default: "size-14",
13
+ sm: "size-12",
14
+ lg: "size-16",
15
+ },
16
+ variant: {
17
+ default:
18
+ "bg-primary text-primary-foreground hover:bg-primary/90 active:scale-95",
19
+ secondary:
20
+ "bg-foreground text-background hover:bg-foreground/90 active:scale-95",
21
+ },
22
+ },
23
+ defaultVariants: {
24
+ size: "default",
25
+ variant: "default",
26
+ },
27
+ },
28
+ );
29
+
30
+ const aiChatPanelVariants = cva(
31
+ "fixed bottom-24 right-6 z-50 flex flex-col bg-background border border-border/40 rounded-2xl overflow-hidden transition-all duration-200 origin-bottom-right",
32
+ {
33
+ variants: {
34
+ size: {
35
+ default: "w-96 h-[500px]",
36
+ sm: "w-80 h-[400px]",
37
+ lg: "w-[450px] h-[600px]",
38
+ },
39
+ },
40
+ defaultVariants: {
41
+ size: "default",
42
+ },
43
+ },
44
+ );
45
+
46
+ interface AIChatProps extends VariantProps<typeof aiChatTriggerVariants> {
47
+ /** Whether the chat panel is open */
48
+ open?: boolean;
49
+ /** Default open state (uncontrolled) */
50
+ defaultOpen?: boolean;
51
+ /** Callback when open state changes */
52
+ onOpenChange?: (open: boolean) => void;
53
+ /** Custom trigger icon */
54
+ triggerIcon?: React.ReactNode;
55
+ /** Panel size */
56
+ panelSize?: "sm" | "default" | "lg";
57
+ /** Content to render inside the chat panel */
58
+ children?: React.ReactNode;
59
+ }
60
+
61
+ function AIChat({
62
+ open: openProp,
63
+ defaultOpen = false,
64
+ onOpenChange,
65
+ triggerIcon,
66
+ size = "default",
67
+ variant = "default",
68
+ panelSize = "default",
69
+ children,
70
+ }: AIChatProps) {
71
+ const [internalOpen, setInternalOpen] = React.useState(defaultOpen);
72
+ const isOpen = openProp ?? internalOpen;
73
+
74
+ const handleToggle = () => {
75
+ const newValue = !isOpen;
76
+ if (openProp === undefined) {
77
+ setInternalOpen(newValue);
78
+ }
79
+ onOpenChange?.(newValue);
80
+ };
81
+
82
+ return (
83
+ <>
84
+ {/* Chat Panel */}
85
+ <div
86
+ data-slot="ai-chat-panel"
87
+ className={`${aiChatPanelVariants({ size: panelSize })} ${
88
+ isOpen
89
+ ? "scale-100 opacity-100"
90
+ : "scale-95 opacity-0 pointer-events-none"
91
+ }`}
92
+ style={{
93
+ boxShadow:
94
+ "0 8px 32px -4px rgb(0 0 0 / 0.12), 0 4px 16px -2px rgb(0 0 0 / 0.08)",
95
+ }}
96
+ >
97
+ {children || <AIChatDefaultContent onClose={handleToggle} />}
98
+ </div>
99
+
100
+ {/* Floating Trigger Button */}
101
+ <button
102
+ type="button"
103
+ data-slot="ai-chat-trigger"
104
+ onClick={handleToggle}
105
+ className={aiChatTriggerVariants({ size, variant })}
106
+ style={{
107
+ boxShadow:
108
+ "0 4px 16px -2px rgb(0 0 0 / 0.15), 0 2px 8px -2px rgb(0 0 0 / 0.1)",
109
+ }}
110
+ aria-label={isOpen ? "Close chat" : "Open chat"}
111
+ >
112
+ <span
113
+ className={`absolute transition-all duration-200 ${
114
+ isOpen
115
+ ? "scale-0 opacity-0 rotate-90"
116
+ : "scale-100 opacity-100 rotate-0"
117
+ }`}
118
+ >
119
+ {triggerIcon || <SparklesIcon className="size-6" />}
120
+ </span>
121
+ <span
122
+ className={`absolute transition-all duration-200 ${
123
+ isOpen
124
+ ? "scale-100 opacity-100 rotate-0"
125
+ : "scale-0 opacity-0 -rotate-90"
126
+ }`}
127
+ >
128
+ <XIcon className="size-6" />
129
+ </span>
130
+ </button>
131
+ </>
132
+ );
133
+ }
134
+
135
+ // Default content when no children provided
136
+ function AIChatDefaultContent({ onClose }: { onClose: () => void }) {
137
+ return (
138
+ <>
139
+ {/* Header */}
140
+ <div className="flex items-center justify-between px-4 py-3 border-b border-border/40">
141
+ <div className="flex items-center gap-2">
142
+ <span className="flex size-8 items-center justify-center rounded-full bg-primary/10 text-primary">
143
+ <SparklesIcon className="size-4" />
144
+ </span>
145
+ <div>
146
+ <div className="font-semibold text-sm">AI Assistant</div>
147
+ <div className="text-xs text-muted-foreground">Ask me anything</div>
148
+ </div>
149
+ </div>
150
+ <button
151
+ type="button"
152
+ onClick={onClose}
153
+ className="size-8 flex items-center justify-center rounded-md hover:bg-muted/50 text-muted-foreground hover:text-foreground transition-colors"
154
+ >
155
+ <XIcon className="size-4" />
156
+ </button>
157
+ </div>
158
+
159
+ {/* Messages Area */}
160
+ <div className="flex-1 overflow-auto p-4">
161
+ <div className="flex flex-col gap-4">
162
+ {/* AI Welcome Message */}
163
+ <div className="flex gap-3">
164
+ <span className="flex size-7 shrink-0 items-center justify-center rounded-full bg-primary/10 text-primary">
165
+ <SparklesIcon className="size-3.5" />
166
+ </span>
167
+ <div className="flex-1 rounded-2xl rounded-tl-sm bg-muted/50 dark:bg-muted px-3 py-2 text-sm">
168
+ Hi! I'm your AI assistant. How can I help you today?
169
+ </div>
170
+ </div>
171
+ </div>
172
+ </div>
173
+
174
+ {/* Input Area */}
175
+ <div className="p-3 border-t border-border/40">
176
+ <div className="flex items-center gap-2 rounded-xl bg-muted/50 dark:bg-muted px-3 py-2">
177
+ <input
178
+ type="text"
179
+ placeholder="Ask a question..."
180
+ className="flex-1 bg-transparent text-sm outline-none placeholder:text-muted-foreground"
181
+ />
182
+ <button
183
+ type="button"
184
+ className="flex size-8 items-center justify-center rounded-lg bg-primary text-primary-foreground hover:bg-primary/90 transition-colors"
185
+ >
186
+ <MessageCircleIcon className="size-4" />
187
+ </button>
188
+ </div>
189
+ <div className="mt-2 text-center text-xs text-muted-foreground">
190
+ Powered by AI
191
+ </div>
192
+ </div>
193
+ </>
194
+ );
195
+ }
196
+
197
+ // Compound components for custom content
198
+ function AIChatHeader({
199
+ children,
200
+ ...props
201
+ }: Omit<React.ComponentProps<"div">, "className">) {
202
+ return (
203
+ <div
204
+ data-slot="ai-chat-header"
205
+ className="flex items-center justify-between px-4 py-3 border-b border-border/40"
206
+ {...props}
207
+ >
208
+ {children}
209
+ </div>
210
+ );
211
+ }
212
+
213
+ function AIChatBody({
214
+ children,
215
+ ...props
216
+ }: Omit<React.ComponentProps<"div">, "className">) {
217
+ return (
218
+ <div
219
+ data-slot="ai-chat-body"
220
+ className="flex-1 overflow-auto p-4"
221
+ {...props}
222
+ >
223
+ {children}
224
+ </div>
225
+ );
226
+ }
227
+
228
+ function AIChatFooter({
229
+ children,
230
+ ...props
231
+ }: Omit<React.ComponentProps<"div">, "className">) {
232
+ return (
233
+ <div
234
+ data-slot="ai-chat-footer"
235
+ className="p-3 border-t border-border/40"
236
+ {...props}
237
+ >
238
+ {children}
239
+ </div>
240
+ );
241
+ }
242
+
243
+ export {
244
+ AIChat,
245
+ AIChatHeader,
246
+ AIChatBody,
247
+ AIChatFooter,
248
+ aiChatTriggerVariants,
249
+ aiChatPanelVariants,
250
+ };
251
+ export type { AIChatProps };
@@ -0,0 +1,131 @@
1
+ import { cva, type VariantProps } from "class-variance-authority";
2
+ import { AlertCircle, AlertTriangle, CheckCircle2, Info } from "lucide-react";
3
+ import * as React from "react";
4
+
5
+ const alertVariants = cva(
6
+ 'grid gap-0.5 rounded-lg border px-4 py-3 text-left text-sm has-data-[slot=alert-action]:relative has-data-[slot=alert-action]:pr-18 has-[>svg]:grid-cols-[auto_1fr] has-[>svg]:gap-x-2.5 *:[svg]:row-span-2 *:[svg]:translate-y-0.5 *:[svg]:text-current *:[svg:not([class*="size-"])]:size-4 w-full relative group/alert',
7
+ {
8
+ variants: {
9
+ variant: {
10
+ default: "bg-card text-card-foreground",
11
+ info: "border-blue-200 bg-blue-50 text-blue-800 dark:border-blue-800 dark:bg-blue-950/50 dark:text-blue-200 *:data-[slot=alert-description]:text-blue-700 dark:*:data-[slot=alert-description]:text-blue-300",
12
+ success:
13
+ "border-green-200 bg-green-50 text-green-800 dark:border-green-800 dark:bg-green-950/50 dark:text-green-200 *:data-[slot=alert-description]:text-green-700 dark:*:data-[slot=alert-description]:text-green-300",
14
+ warning:
15
+ "border-yellow-200 bg-yellow-50 text-yellow-800 dark:border-yellow-800 dark:bg-yellow-950/50 dark:text-yellow-200 *:data-[slot=alert-description]:text-yellow-700 dark:*:data-[slot=alert-description]:text-yellow-300",
16
+ destructive:
17
+ "border-red-200 bg-red-50 text-red-800 dark:border-red-800 dark:bg-red-950/50 dark:text-red-200 *:data-[slot=alert-description]:text-red-700 dark:*:data-[slot=alert-description]:text-red-300",
18
+ },
19
+ },
20
+ defaultVariants: {
21
+ variant: "default",
22
+ },
23
+ },
24
+ );
25
+
26
+ const variantIcons = {
27
+ default: null,
28
+ info: Info,
29
+ success: CheckCircle2,
30
+ warning: AlertTriangle,
31
+ destructive: AlertCircle,
32
+ } as const;
33
+
34
+ type AlertVariant = NonNullable<VariantProps<typeof alertVariants>["variant"]>;
35
+
36
+ interface AlertProps
37
+ extends
38
+ Omit<React.ComponentProps<"div">, "className" | "title">,
39
+ VariantProps<typeof alertVariants> {
40
+ /** Alert title - renders AlertTitle component */
41
+ title?: React.ReactNode;
42
+ /** Alert description - renders AlertDescription component */
43
+ description?: React.ReactNode;
44
+ /** Custom icon - if not provided, uses default icon for the variant */
45
+ icon?: React.ReactNode;
46
+ /** Hide the default variant icon */
47
+ hideIcon?: boolean;
48
+ }
49
+
50
+ function Alert({
51
+ variant = "default",
52
+ title,
53
+ description,
54
+ icon,
55
+ hideIcon,
56
+ children,
57
+ ...props
58
+ }: AlertProps) {
59
+ // Determine the icon to show
60
+ const showIcon = !hideIcon && (icon !== undefined || variant !== "default");
61
+ const IconComponent = variantIcons[variant as AlertVariant];
62
+ const renderedIcon = icon ?? (IconComponent ? <IconComponent /> : null);
63
+
64
+ // If title/description are provided, render the simple API
65
+ if (title || description) {
66
+ return (
67
+ <div
68
+ data-slot="alert"
69
+ role="alert"
70
+ className={alertVariants({ variant })}
71
+ {...props}
72
+ >
73
+ {showIcon && renderedIcon}
74
+ {title && <AlertTitle>{title}</AlertTitle>}
75
+ {description && <AlertDescription>{description}</AlertDescription>}
76
+ {children}
77
+ </div>
78
+ );
79
+ }
80
+
81
+ // Otherwise, render children (compound component pattern)
82
+ return (
83
+ <div
84
+ data-slot="alert"
85
+ role="alert"
86
+ className={alertVariants({ variant })}
87
+ {...props}
88
+ >
89
+ {showIcon && renderedIcon}
90
+ {children}
91
+ </div>
92
+ );
93
+ }
94
+
95
+ function AlertTitle({
96
+ ...props
97
+ }: Omit<React.ComponentProps<"div">, "className">) {
98
+ return (
99
+ <div
100
+ data-slot="alert-title"
101
+ className="font-medium group-has-[>svg]/alert:col-start-2 [&_a]:hover:text-foreground [&_a]:underline [&_a]:underline-offset-3"
102
+ {...props}
103
+ />
104
+ );
105
+ }
106
+
107
+ function AlertDescription({
108
+ ...props
109
+ }: Omit<React.ComponentProps<"div">, "className">) {
110
+ return (
111
+ <div
112
+ data-slot="alert-description"
113
+ className="text-sm text-balance md:text-pretty [&_p:not(:last-child)]:mb-4 [&_a]:hover:text-foreground [&_a]:underline [&_a]:underline-offset-3"
114
+ {...props}
115
+ />
116
+ );
117
+ }
118
+
119
+ function AlertAction({
120
+ ...props
121
+ }: Omit<React.ComponentProps<"div">, "className">) {
122
+ return (
123
+ <div
124
+ data-slot="alert-action"
125
+ className="absolute top-2.5 right-3"
126
+ {...props}
127
+ />
128
+ );
129
+ }
130
+
131
+ export { Alert, AlertAction, AlertDescription, AlertTitle, alertVariants };