@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,301 @@
1
+ import { mergeProps } from "@base-ui/react/merge-props";
2
+ import { useRender } from "@base-ui/react/use-render";
3
+ import * as React from "react";
4
+
5
+ import {
6
+ ArrowRightIcon,
7
+ ChevronRightIcon,
8
+ MoreHorizontalIcon,
9
+ SlashIcon,
10
+ } from "lucide-react";
11
+ import {
12
+ DropdownMenu,
13
+ DropdownMenuContent,
14
+ DropdownMenuItem,
15
+ DropdownMenuTrigger,
16
+ } from "../organisms/dropdown-menu";
17
+
18
+ interface BreadcrumbItemData {
19
+ /** The text label for the breadcrumb item */
20
+ label?: React.ReactNode;
21
+ /** The href for the link. If omitted on the last item, it becomes the current page */
22
+ href?: string;
23
+ /** Whether this item is the current page (auto-detected if last item has no href) */
24
+ isCurrent?: boolean;
25
+ /** Render as ellipsis (collapsed items indicator) */
26
+ isEllipsis?: boolean;
27
+ /** Additional props to pass to the link or page element */
28
+ props?: Record<string, unknown>;
29
+ }
30
+
31
+ type BreadcrumbSeparatorType = "chevron" | "slash" | "arrow";
32
+
33
+ const separatorIcons: Record<BreadcrumbSeparatorType, React.ReactNode> = {
34
+ chevron: <ChevronRightIcon />,
35
+ slash: <SlashIcon />,
36
+ arrow: <ArrowRightIcon />,
37
+ };
38
+
39
+ interface BreadcrumbProps extends Omit<
40
+ React.ComponentProps<"nav">,
41
+ "children" | "className"
42
+ > {
43
+ /** Simple API: Array of breadcrumb items */
44
+ items?: BreadcrumbItemData[];
45
+ /** Separator style: 'chevron' (default), 'slash', or 'arrow' */
46
+ separator?: BreadcrumbSeparatorType;
47
+ /** Max visible items (including ellipsis). Default: 4. Set to 0 to disable auto-collapse. */
48
+ maxItems?: number;
49
+ /** Number of items to show at the start before ellipsis. Default: 1 */
50
+ itemsBeforeCollapse?: number;
51
+ /** Children for compound component pattern */
52
+ children?: React.ReactNode;
53
+ }
54
+
55
+ interface CollapseResult {
56
+ displayItems: Array<
57
+ BreadcrumbItemData & { collapsedItems?: BreadcrumbItemData[] }
58
+ >;
59
+ hasCollapsed: boolean;
60
+ }
61
+
62
+ function collapseItems({
63
+ items,
64
+ maxItems,
65
+ itemsBeforeCollapse,
66
+ }: {
67
+ items: BreadcrumbItemData[];
68
+ maxItems: number;
69
+ itemsBeforeCollapse: number;
70
+ }): CollapseResult {
71
+ // If maxItems is 0 or items already fit, return as-is
72
+ if (maxItems === 0 || items.length <= maxItems) {
73
+ return { displayItems: items, hasCollapsed: false };
74
+ }
75
+
76
+ // Ensure we have at least 1 item before ellipsis
77
+ const before = Math.max(1, Math.min(itemsBeforeCollapse, maxItems - 2));
78
+
79
+ // Calculate items after ellipsis: maxItems - before - 1 (for ellipsis)
80
+ const after = maxItems - before - 1;
81
+
82
+ // If we can't fit at least 1 after, no point collapsing
83
+ if (after < 1) {
84
+ return { displayItems: items, hasCollapsed: false };
85
+ }
86
+
87
+ // If before + after >= items.length, no need to collapse
88
+ if (before + after >= items.length) {
89
+ return { displayItems: items, hasCollapsed: false };
90
+ }
91
+
92
+ const startItems = items.slice(0, before);
93
+ const collapsedItems = items.slice(before, items.length - after);
94
+ const endItems = items.slice(items.length - after);
95
+
96
+ return {
97
+ displayItems: [
98
+ ...startItems,
99
+ { isEllipsis: true, collapsedItems },
100
+ ...endItems,
101
+ ],
102
+ hasCollapsed: true,
103
+ };
104
+ }
105
+
106
+ function Breadcrumb({
107
+ items,
108
+ separator = "chevron",
109
+ maxItems = 4,
110
+ itemsBeforeCollapse = 1,
111
+ children,
112
+ ...props
113
+ }: BreadcrumbProps) {
114
+ // If items prop is provided, render simple API
115
+ if (items && items.length > 0) {
116
+ const separatorIcon = separatorIcons[separator];
117
+
118
+ // Auto-collapse if needed
119
+ const { displayItems } = collapseItems({
120
+ items,
121
+ maxItems,
122
+ itemsBeforeCollapse,
123
+ });
124
+
125
+ return (
126
+ <nav aria-label="breadcrumb" data-slot="breadcrumb" {...props}>
127
+ <BreadcrumbList>
128
+ {displayItems.map((item, index) => {
129
+ const isLast = index === displayItems.length - 1;
130
+ const isCurrent =
131
+ item.isCurrent ?? (isLast && !item.href && !item.isEllipsis);
132
+ const collapsedItems =
133
+ "collapsedItems" in item ? item.collapsedItems : undefined;
134
+
135
+ return (
136
+ <React.Fragment key={index}>
137
+ <BreadcrumbItem>
138
+ {item.isEllipsis ? (
139
+ <BreadcrumbEllipsisMenu collapsedItems={collapsedItems} />
140
+ ) : isCurrent ? (
141
+ <BreadcrumbPage {...item.props}>
142
+ {item.label}
143
+ </BreadcrumbPage>
144
+ ) : (
145
+ <BreadcrumbLink href={item.href} {...item.props}>
146
+ {item.label}
147
+ </BreadcrumbLink>
148
+ )}
149
+ </BreadcrumbItem>
150
+ {!isLast && (
151
+ <BreadcrumbSeparator>{separatorIcon}</BreadcrumbSeparator>
152
+ )}
153
+ </React.Fragment>
154
+ );
155
+ })}
156
+ </BreadcrumbList>
157
+ </nav>
158
+ );
159
+ }
160
+
161
+ // Otherwise, render compound component pattern
162
+ return (
163
+ <nav aria-label="breadcrumb" data-slot="breadcrumb" {...props}>
164
+ {children}
165
+ </nav>
166
+ );
167
+ }
168
+
169
+ function BreadcrumbList({
170
+ ...props
171
+ }: Omit<React.ComponentProps<"ol">, "className">) {
172
+ return (
173
+ <ol
174
+ data-slot="breadcrumb-list"
175
+ className="text-muted-foreground gap-1.5 text-sm sm:gap-2.5 flex flex-wrap items-center break-words"
176
+ {...props}
177
+ />
178
+ );
179
+ }
180
+
181
+ function BreadcrumbItem({
182
+ ...props
183
+ }: Omit<React.ComponentProps<"li">, "className">) {
184
+ return (
185
+ <li
186
+ data-slot="breadcrumb-item"
187
+ className="gap-1.5 inline-flex items-center"
188
+ {...props}
189
+ />
190
+ );
191
+ }
192
+
193
+ function BreadcrumbLink({
194
+ render,
195
+ ...props
196
+ }: Omit<useRender.ComponentProps<"a">, "className">) {
197
+ return useRender({
198
+ defaultTagName: "a",
199
+ props: mergeProps<"a">(
200
+ {
201
+ className: "hover:text-foreground transition-colors",
202
+ },
203
+ props,
204
+ ),
205
+ render,
206
+ state: {
207
+ slot: "breadcrumb-link",
208
+ },
209
+ });
210
+ }
211
+
212
+ function BreadcrumbPage({
213
+ ...props
214
+ }: Omit<React.ComponentProps<"span">, "className">) {
215
+ return (
216
+ <span
217
+ data-slot="breadcrumb-page"
218
+ role="link"
219
+ aria-disabled="true"
220
+ aria-current="page"
221
+ className="text-foreground font-normal"
222
+ {...props}
223
+ />
224
+ );
225
+ }
226
+
227
+ function BreadcrumbSeparator({
228
+ children,
229
+ ...props
230
+ }: Omit<React.ComponentProps<"li">, "className">) {
231
+ return (
232
+ <li
233
+ data-slot="breadcrumb-separator"
234
+ role="presentation"
235
+ aria-hidden="true"
236
+ className="[&>svg]:size-3.5"
237
+ {...props}
238
+ >
239
+ {children ?? <ChevronRightIcon />}
240
+ </li>
241
+ );
242
+ }
243
+
244
+ function BreadcrumbEllipsis({
245
+ ...props
246
+ }: Omit<React.ComponentProps<"span">, "className">) {
247
+ return (
248
+ <span
249
+ data-slot="breadcrumb-ellipsis"
250
+ role="presentation"
251
+ aria-hidden="true"
252
+ className="size-5 [&>svg]:size-4 flex items-center justify-center"
253
+ {...props}
254
+ >
255
+ <MoreHorizontalIcon />
256
+ <span className="sr-only">More</span>
257
+ </span>
258
+ );
259
+ }
260
+
261
+ function BreadcrumbEllipsisMenu({
262
+ collapsedItems,
263
+ }: {
264
+ collapsedItems?: BreadcrumbItemData[];
265
+ }) {
266
+ if (!collapsedItems || collapsedItems.length === 0) {
267
+ return <BreadcrumbEllipsis />;
268
+ }
269
+
270
+ return (
271
+ <DropdownMenu>
272
+ <DropdownMenuTrigger
273
+ variant="ellipsis"
274
+ aria-label="Show hidden breadcrumb items"
275
+ >
276
+ <MoreHorizontalIcon />
277
+ </DropdownMenuTrigger>
278
+ <DropdownMenuContent align="start">
279
+ {collapsedItems.map((item, index) => (
280
+ <DropdownMenuItem
281
+ key={index}
282
+ render={item.href ? <a href={item.href} /> : undefined}
283
+ >
284
+ {item.label}
285
+ </DropdownMenuItem>
286
+ ))}
287
+ </DropdownMenuContent>
288
+ </DropdownMenu>
289
+ );
290
+ }
291
+
292
+ export {
293
+ Breadcrumb,
294
+ BreadcrumbEllipsis,
295
+ BreadcrumbItem,
296
+ BreadcrumbLink,
297
+ BreadcrumbList,
298
+ BreadcrumbPage,
299
+ BreadcrumbSeparator,
300
+ };
301
+ export type { BreadcrumbItemData, BreadcrumbSeparatorType };
@@ -0,0 +1,96 @@
1
+ import { mergeProps } from "@base-ui/react/merge-props";
2
+ import { Separator as SeparatorPrimitive } from "@base-ui/react/separator";
3
+ import { useRender } from "@base-ui/react/use-render";
4
+ import { cva, type VariantProps } from "class-variance-authority";
5
+
6
+ const buttonGroupVariants = cva(
7
+ "has-[>[data-slot=button-group]]:gap-2 has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-r-md flex w-fit items-stretch [&>*]:focus-visible:z-10 [&>*]:focus-visible:relative [&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&>input]:flex-1",
8
+ {
9
+ variants: {
10
+ orientation: {
11
+ horizontal:
12
+ "[&>[data-slot]:not(:has(~[data-slot]))]:rounded-r-md! [&>[data-slot]~[data-slot]]:rounded-l-none [&>[data-slot]~[data-slot]]:border-l-0 [&>[data-slot]]:rounded-r-none",
13
+ vertical:
14
+ "[&>[data-slot]:not(:has(~[data-slot]))]:rounded-b-md! flex-col [&>[data-slot]~[data-slot]]:rounded-t-none [&>[data-slot]~[data-slot]]:border-t-0 [&>[data-slot]]:rounded-b-none",
15
+ },
16
+ },
17
+ defaultVariants: {
18
+ orientation: "horizontal",
19
+ },
20
+ },
21
+ );
22
+
23
+ const buttonGroupTextVariants = cva(
24
+ "gap-2 rounded-md border px-2.5 text-sm font-medium shadow-xs [&_svg:not([class*='size-'])]:size-4 flex items-center [&_svg]:pointer-events-none",
25
+ {
26
+ variants: {
27
+ variant: {
28
+ default: "bg-muted",
29
+ display:
30
+ "bg-background border-y border-x-0 min-w-[60px] justify-center tabular-nums",
31
+ },
32
+ },
33
+ defaultVariants: {
34
+ variant: "default",
35
+ },
36
+ },
37
+ );
38
+
39
+ function ButtonGroup({
40
+ orientation,
41
+ ...props
42
+ }: Omit<React.ComponentProps<"div">, "className"> &
43
+ VariantProps<typeof buttonGroupVariants>) {
44
+ return (
45
+ <div
46
+ role="group"
47
+ data-slot="button-group"
48
+ data-orientation={orientation}
49
+ className={buttonGroupVariants({ orientation })}
50
+ {...props}
51
+ />
52
+ );
53
+ }
54
+
55
+ function ButtonGroupText({
56
+ variant,
57
+ render,
58
+ ...props
59
+ }: Omit<useRender.ComponentProps<"div">, "className"> &
60
+ VariantProps<typeof buttonGroupTextVariants>) {
61
+ return useRender({
62
+ defaultTagName: "div",
63
+ props: mergeProps<"div">(
64
+ {
65
+ className: buttonGroupTextVariants({ variant }),
66
+ },
67
+ props,
68
+ ),
69
+ render,
70
+ state: {
71
+ slot: "button-group-text",
72
+ variant,
73
+ },
74
+ });
75
+ }
76
+
77
+ function ButtonGroupSeparator({
78
+ orientation = "vertical",
79
+ ...props
80
+ }: Omit<SeparatorPrimitive.Props, "className">) {
81
+ return (
82
+ <SeparatorPrimitive
83
+ data-slot="button-group-separator"
84
+ orientation={orientation}
85
+ className="bg-input shrink-0 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:w-px data-[orientation=vertical]:self-stretch relative data-[orientation=horizontal]:mx-px data-[orientation=horizontal]:w-auto data-[orientation=vertical]:my-px data-[orientation=vertical]:h-auto"
86
+ {...props}
87
+ />
88
+ );
89
+ }
90
+
91
+ export {
92
+ ButtonGroup,
93
+ ButtonGroupSeparator,
94
+ ButtonGroupText,
95
+ buttonGroupVariants,
96
+ };
@@ -0,0 +1,184 @@
1
+ import { cva, type VariantProps } from "class-variance-authority";
2
+ import * as React from "react";
3
+
4
+ const cardVariants = cva(
5
+ "bg-card text-card-foreground border border-border/40 shadow-[0_1px_3px_0_rgb(0_0_0/0.06)] gap-4 overflow-hidden rounded-xl py-4 text-sm has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl group/card flex flex-col",
6
+ {
7
+ variants: {
8
+ width: {
9
+ auto: "", // shrink to content (default behavior)
10
+ full: "w-full", // fill parent container
11
+ sm: "w-sm", // 24rem (384px)
12
+ md: "w-md", // 28rem (448px)
13
+ lg: "w-lg", // 32rem (512px)
14
+ xl: "w-xl", // 36rem (576px)
15
+ "2xl": "w-2xl", // 42rem (672px)
16
+ "3xl": "w-3xl", // 48rem (768px)
17
+ },
18
+ maxWidth: {
19
+ sm: "max-w-sm",
20
+ md: "max-w-md",
21
+ lg: "max-w-lg",
22
+ xl: "max-w-xl",
23
+ "2xl": "max-w-2xl",
24
+ "3xl": "max-w-3xl",
25
+ full: "max-w-full",
26
+ },
27
+ spacing: {
28
+ default: "",
29
+ tight: "gap-3",
30
+ relaxed: "gap-6",
31
+ },
32
+ },
33
+ defaultVariants: {
34
+ width: "auto",
35
+ spacing: "default",
36
+ },
37
+ },
38
+ );
39
+
40
+ interface CardProps
41
+ extends
42
+ Omit<React.ComponentProps<"div">, "className" | "title">,
43
+ VariantProps<typeof cardVariants> {
44
+ size?: "default" | "sm";
45
+ /** Card title - renders in CardHeader */
46
+ title?: React.ReactNode;
47
+ /** Card description - renders below title in CardHeader */
48
+ description?: React.ReactNode;
49
+ /** Action element(s) in the header (e.g., buttons, badges) */
50
+ headerAction?: React.ReactNode;
51
+ /** Footer content */
52
+ footer?: React.ReactNode;
53
+ }
54
+
55
+ function Card({
56
+ size = "default",
57
+ width,
58
+ maxWidth,
59
+ title,
60
+ description,
61
+ headerAction,
62
+ footer,
63
+ children,
64
+ ...props
65
+ }: CardProps) {
66
+ const hasHeader = title || description || headerAction;
67
+
68
+ // Check if children contain compound components (have data-slot)
69
+ const hasCompoundChildren = React.Children.toArray(children).some((child) => {
70
+ if (React.isValidElement(child)) {
71
+ const props = child.props as Record<string, unknown>;
72
+ return (
73
+ props["data-slot"] === "card-header" ||
74
+ props["data-slot"] === "card-content" ||
75
+ props["data-slot"] === "card-footer"
76
+ );
77
+ }
78
+ return false;
79
+ });
80
+
81
+ return (
82
+ <div
83
+ data-slot="card"
84
+ data-size={size}
85
+ className={cardVariants({ width, maxWidth })}
86
+ {...props}
87
+ >
88
+ {hasHeader && (
89
+ <CardHeader>
90
+ {title && <CardTitle>{title}</CardTitle>}
91
+ {description && <CardDescription>{description}</CardDescription>}
92
+ {headerAction && <CardAction>{headerAction}</CardAction>}
93
+ </CardHeader>
94
+ )}
95
+ {hasCompoundChildren
96
+ ? children
97
+ : children && <CardContent>{children}</CardContent>}
98
+ {footer && <CardFooter>{footer}</CardFooter>}
99
+ </div>
100
+ );
101
+ }
102
+
103
+ function CardHeader({
104
+ ...props
105
+ }: Omit<React.ComponentProps<"div">, "className">) {
106
+ return (
107
+ <div
108
+ data-slot="card-header"
109
+ className="gap-1 rounded-t-xl px-4 group-data-[size=sm]/card:px-3 [.border-b]:pb-4 group-data-[size=sm]/card:[.border-b]:pb-3 group/card-header @container/card-header grid auto-rows-min items-start has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto]"
110
+ {...props}
111
+ />
112
+ );
113
+ }
114
+
115
+ function CardTitle({
116
+ ...props
117
+ }: Omit<React.ComponentProps<"div">, "className">) {
118
+ return (
119
+ <div
120
+ data-slot="card-title"
121
+ className="text-base leading-snug font-medium group-data-[size=sm]/card:text-sm"
122
+ {...props}
123
+ />
124
+ );
125
+ }
126
+
127
+ function CardDescription({
128
+ ...props
129
+ }: Omit<React.ComponentProps<"div">, "className">) {
130
+ return (
131
+ <div
132
+ data-slot="card-description"
133
+ className="text-muted-foreground text-sm"
134
+ {...props}
135
+ />
136
+ );
137
+ }
138
+
139
+ function CardAction({
140
+ ...props
141
+ }: Omit<React.ComponentProps<"div">, "className">) {
142
+ return (
143
+ <div
144
+ data-slot="card-action"
145
+ className="col-start-2 row-span-2 row-start-1 self-start justify-self-end"
146
+ {...props}
147
+ />
148
+ );
149
+ }
150
+
151
+ function CardContent({
152
+ ...props
153
+ }: Omit<React.ComponentProps<"div">, "className">) {
154
+ return (
155
+ <div
156
+ data-slot="card-content"
157
+ className="px-4 group-data-[size=sm]/card:px-3"
158
+ {...props}
159
+ />
160
+ );
161
+ }
162
+
163
+ function CardFooter({
164
+ ...props
165
+ }: Omit<React.ComponentProps<"div">, "className">) {
166
+ return (
167
+ <div
168
+ data-slot="card-footer"
169
+ className="bg-muted/50 rounded-b-xl border-t p-4 group-data-[size=sm]/card:p-3 flex items-center"
170
+ {...props}
171
+ />
172
+ );
173
+ }
174
+
175
+ export {
176
+ Card,
177
+ CardAction,
178
+ CardContent,
179
+ CardDescription,
180
+ CardFooter,
181
+ CardHeader,
182
+ CardTitle,
183
+ cardVariants,
184
+ };
@@ -0,0 +1,21 @@
1
+ "use client";
2
+
3
+ import { Collapsible as CollapsiblePrimitive } from "@base-ui/react/collapsible";
4
+
5
+ function Collapsible({ ...props }: CollapsiblePrimitive.Root.Props) {
6
+ return <CollapsiblePrimitive.Root data-slot="collapsible" {...props} />;
7
+ }
8
+
9
+ function CollapsibleTrigger({ ...props }: CollapsiblePrimitive.Trigger.Props) {
10
+ return (
11
+ <CollapsiblePrimitive.Trigger data-slot="collapsible-trigger" {...props} />
12
+ );
13
+ }
14
+
15
+ function CollapsibleContent({ ...props }: CollapsiblePrimitive.Panel.Props) {
16
+ return (
17
+ <CollapsiblePrimitive.Panel data-slot="collapsible-content" {...props} />
18
+ );
19
+ }
20
+
21
+ export { Collapsible, CollapsibleContent, CollapsibleTrigger };