create-app-ui 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +117 -0
- package/boilerplate/README.md +18 -0
- package/boilerplate/react-base/.env.example +1 -0
- package/boilerplate/react-base/README.md +3 -0
- package/boilerplate/react-base/components.json +19 -0
- package/boilerplate/react-base/eslint.config.js +32 -0
- package/boilerplate/react-base/index.html +12 -0
- package/boilerplate/react-base/package.json +71 -0
- package/boilerplate/react-base/postcss.config.js +6 -0
- package/boilerplate/react-base/prettier.config.js +6 -0
- package/boilerplate/react-base/src/api/axios.ts +20 -0
- package/boilerplate/react-base/src/app/store.ts +13 -0
- package/boilerplate/react-base/src/components/data-table.tsx +919 -0
- package/boilerplate/react-base/src/components/ui/accordion.tsx +44 -0
- package/boilerplate/react-base/src/components/ui/alert-dialog.tsx +105 -0
- package/boilerplate/react-base/src/components/ui/alert.tsx +40 -0
- package/boilerplate/react-base/src/components/ui/avatar.tsx +30 -0
- package/boilerplate/react-base/src/components/ui/badge.tsx +27 -0
- package/boilerplate/react-base/src/components/ui/bar-chart.tsx +76 -0
- package/boilerplate/react-base/src/components/ui/breadcrumb.tsx +87 -0
- package/boilerplate/react-base/src/components/ui/button.tsx +34 -0
- package/boilerplate/react-base/src/components/ui/calendar.tsx +63 -0
- package/boilerplate/react-base/src/components/ui/card.tsx +36 -0
- package/boilerplate/react-base/src/components/ui/chart.tsx +280 -0
- package/boilerplate/react-base/src/components/ui/checkbox.tsx +51 -0
- package/boilerplate/react-base/src/components/ui/context-menu.tsx +173 -0
- package/boilerplate/react-base/src/components/ui/date-picker.tsx +42 -0
- package/boilerplate/react-base/src/components/ui/dialog.tsx +87 -0
- package/boilerplate/react-base/src/components/ui/drawer.tsx +81 -0
- package/boilerplate/react-base/src/components/ui/dropdown-menu.tsx +81 -0
- package/boilerplate/react-base/src/components/ui/dropdown-types.ts +28 -0
- package/boilerplate/react-base/src/components/ui/field.tsx +194 -0
- package/boilerplate/react-base/src/components/ui/hover-card.tsx +26 -0
- package/boilerplate/react-base/src/components/ui/input-group.tsx +98 -0
- package/boilerplate/react-base/src/components/ui/input-otp.tsx +63 -0
- package/boilerplate/react-base/src/components/ui/input.tsx +12 -0
- package/boilerplate/react-base/src/components/ui/item.tsx +152 -0
- package/boilerplate/react-base/src/components/ui/kbd.tsx +13 -0
- package/boilerplate/react-base/src/components/ui/label.tsx +14 -0
- package/boilerplate/react-base/src/components/ui/line-chart.tsx +65 -0
- package/boilerplate/react-base/src/components/ui/menubar.tsx +217 -0
- package/boilerplate/react-base/src/components/ui/multi-select-dropdown.tsx +200 -0
- package/boilerplate/react-base/src/components/ui/navigation-menu.tsx +120 -0
- package/boilerplate/react-base/src/components/ui/pie-chart.tsx +87 -0
- package/boilerplate/react-base/src/components/ui/popover.tsx +29 -0
- package/boilerplate/react-base/src/components/ui/progress.tsx +19 -0
- package/boilerplate/react-base/src/components/ui/radio-group.tsx +36 -0
- package/boilerplate/react-base/src/components/ui/scroll-area.tsx +38 -0
- package/boilerplate/react-base/src/components/ui/searchable-dropdown.tsx +118 -0
- package/boilerplate/react-base/src/components/ui/select.tsx +140 -0
- package/boilerplate/react-base/src/components/ui/separator.tsx +20 -0
- package/boilerplate/react-base/src/components/ui/sheet.tsx +70 -0
- package/boilerplate/react-base/src/components/ui/sidebar.tsx +470 -0
- package/boilerplate/react-base/src/components/ui/skeleton.tsx +11 -0
- package/boilerplate/react-base/src/components/ui/slider.tsx +23 -0
- package/boilerplate/react-base/src/components/ui/sonner.tsx +21 -0
- package/boilerplate/react-base/src/components/ui/sparkline.tsx +38 -0
- package/boilerplate/react-base/src/components/ui/spinner.tsx +10 -0
- package/boilerplate/react-base/src/components/ui/switch.tsx +16 -0
- package/boilerplate/react-base/src/components/ui/table.tsx +80 -0
- package/boilerplate/react-base/src/components/ui/tabs.tsx +32 -0
- package/boilerplate/react-base/src/components/ui/textarea.tsx +12 -0
- package/boilerplate/react-base/src/components/ui/toggle-group.tsx +49 -0
- package/boilerplate/react-base/src/components/ui/toggle.tsx +33 -0
- package/boilerplate/react-base/src/components/ui/tooltip.tsx +23 -0
- package/boilerplate/react-base/src/components/ui/typography.tsx +76 -0
- package/boilerplate/react-base/src/config/constants.ts +3 -0
- package/boilerplate/react-base/src/config/theme.ts +432 -0
- package/boilerplate/react-base/src/config/user.ts +52 -0
- package/boilerplate/react-base/src/context/theme-provider.tsx +12 -0
- package/boilerplate/react-base/src/features/auth/authSlice.ts +19 -0
- package/boilerplate/react-base/src/hooks/index.ts +1 -0
- package/boilerplate/react-base/src/hooks/use-mobile.ts +17 -0
- package/boilerplate/react-base/src/lib/utils.ts +6 -0
- package/boilerplate/react-base/src/routes/index.tsx +7 -0
- package/boilerplate/react-base/src/styles/globals.css +15 -0
- package/boilerplate/react-base/src/vite-env.d.ts +31 -0
- package/boilerplate/react-base/tailwind.config.ts +75 -0
- package/boilerplate/react-base/tsconfig.app.json +20 -0
- package/boilerplate/react-base/tsconfig.json +7 -0
- package/boilerplate/react-base/tsconfig.node.json +16 -0
- package/boilerplate/react-base/vite.config.ts +12 -0
- package/dist/bin/index.js +8 -0
- package/dist/src/cli-args.js +52 -0
- package/dist/src/generator.js +85 -0
- package/dist/src/installer.js +7 -0
- package/dist/src/paths.js +61 -0
- package/dist/src/prompts.js +79 -0
- package/dist/src/replace-placeholders.js +22 -0
- package/dist/src/utils.js +16 -0
- package/package.json +63 -0
- package/templates/admin-portal/README.md +26 -0
- package/templates/admin-portal/src/App.tsx +85 -0
- package/templates/admin-portal/src/assets/auth-hero.jpg +0 -0
- package/templates/admin-portal/src/assets/brand-logo.png +0 -0
- package/templates/admin-portal/src/components/app-breadcrumb.tsx +41 -0
- package/templates/admin-portal/src/components/app-header.tsx +20 -0
- package/templates/admin-portal/src/components/app-sidebar.tsx +78 -0
- package/templates/admin-portal/src/components/auth-layout.tsx +66 -0
- package/templates/admin-portal/src/components/dashboard-metric-card.tsx +105 -0
- package/templates/admin-portal/src/components/data-table.tsx +919 -0
- package/templates/admin-portal/src/components/layout-shell.tsx +23 -0
- package/templates/admin-portal/src/components/notifications-sheet.tsx +91 -0
- package/templates/admin-portal/src/components/sidebar-nav.tsx +164 -0
- package/templates/admin-portal/src/components/user-avatar.tsx +26 -0
- package/templates/admin-portal/src/components/user-menu.tsx +163 -0
- package/templates/admin-portal/src/config/branding.ts +17 -0
- package/templates/admin-portal/src/config/chart-data.ts +44 -0
- package/templates/admin-portal/src/config/navigation.ts +42 -0
- package/templates/admin-portal/src/context/auth-context.tsx +32 -0
- package/templates/admin-portal/src/lib/breadcrumbs.ts +58 -0
- package/templates/admin-portal/src/main.tsx +18 -0
- package/templates/admin-portal/src/pages/components/demo-columns.tsx +170 -0
- package/templates/admin-portal/src/pages/components.tsx +1368 -0
- package/templates/admin-portal/src/pages/dashboard.tsx +143 -0
- package/templates/admin-portal/src/pages/login.tsx +81 -0
- package/templates/admin-portal/src/pages/settings/notifications.tsx +31 -0
- package/templates/admin-portal/src/pages/settings/profile.tsx +26 -0
- package/templates/admin-portal/src/pages/signup.tsx +81 -0
- package/templates/admin-portal/src/pages/users.tsx +12 -0
- package/templates/admin-portal/tsconfig.json +10 -0
- package/templates/blank/README.md +15 -0
- package/templates/blank/src/App.tsx +5 -0
- package/templates/blank/src/main.tsx +15 -0
- package/templates/blank/src/pages/home.tsx +20 -0
- package/templates/blank/tsconfig.json +10 -0
- package/templates/tsconfig.overlay.base.json +7 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import * as NavigationMenuPrimitive from "@radix-ui/react-navigation-menu";
|
|
2
|
+
import { cva } from "class-variance-authority";
|
|
3
|
+
import { ChevronDown } from "lucide-react";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
import { ui } from "@/config/theme";
|
|
6
|
+
import { cn } from "@/lib/utils";
|
|
7
|
+
|
|
8
|
+
const NavigationMenu = React.forwardRef<
|
|
9
|
+
React.ElementRef<typeof NavigationMenuPrimitive.Root>,
|
|
10
|
+
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Root>
|
|
11
|
+
>(({ className, children, ...props }, ref) => (
|
|
12
|
+
<NavigationMenuPrimitive.Root
|
|
13
|
+
ref={ref}
|
|
14
|
+
className={cn("relative z-10 flex max-w-max flex-1 items-center justify-center", className)}
|
|
15
|
+
{...props}
|
|
16
|
+
>
|
|
17
|
+
{children}
|
|
18
|
+
<NavigationMenuViewport />
|
|
19
|
+
</NavigationMenuPrimitive.Root>
|
|
20
|
+
));
|
|
21
|
+
NavigationMenu.displayName = NavigationMenuPrimitive.Root.displayName;
|
|
22
|
+
|
|
23
|
+
const NavigationMenuList = React.forwardRef<
|
|
24
|
+
React.ElementRef<typeof NavigationMenuPrimitive.List>,
|
|
25
|
+
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.List>
|
|
26
|
+
>(({ className, ...props }, ref) => (
|
|
27
|
+
<NavigationMenuPrimitive.List
|
|
28
|
+
ref={ref}
|
|
29
|
+
className={cn("group flex flex-1 list-none items-center justify-center space-x-1", className)}
|
|
30
|
+
{...props}
|
|
31
|
+
/>
|
|
32
|
+
));
|
|
33
|
+
NavigationMenuList.displayName = NavigationMenuPrimitive.List.displayName;
|
|
34
|
+
|
|
35
|
+
const NavigationMenuItem = NavigationMenuPrimitive.Item;
|
|
36
|
+
|
|
37
|
+
const navigationMenuTriggerStyle = cva(
|
|
38
|
+
"group inline-flex h-9 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[state=open]:bg-accent/50",
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
const NavigationMenuTrigger = React.forwardRef<
|
|
42
|
+
React.ElementRef<typeof NavigationMenuPrimitive.Trigger>,
|
|
43
|
+
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Trigger>
|
|
44
|
+
>(({ className, children, ...props }, ref) => (
|
|
45
|
+
<NavigationMenuPrimitive.Trigger
|
|
46
|
+
ref={ref}
|
|
47
|
+
className={cn(navigationMenuTriggerStyle(), "group", className)}
|
|
48
|
+
{...props}
|
|
49
|
+
>
|
|
50
|
+
{children}{" "}
|
|
51
|
+
<ChevronDown
|
|
52
|
+
className="relative top-[1px] ml-1 h-3 w-3 transition duration-300 group-data-[state=open]:rotate-180"
|
|
53
|
+
aria-hidden="true"
|
|
54
|
+
/>
|
|
55
|
+
</NavigationMenuPrimitive.Trigger>
|
|
56
|
+
));
|
|
57
|
+
NavigationMenuTrigger.displayName = NavigationMenuPrimitive.Trigger.displayName;
|
|
58
|
+
|
|
59
|
+
const NavigationMenuContent = React.forwardRef<
|
|
60
|
+
React.ElementRef<typeof NavigationMenuPrimitive.Content>,
|
|
61
|
+
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Content>
|
|
62
|
+
>(({ className, ...props }, ref) => (
|
|
63
|
+
<NavigationMenuPrimitive.Content
|
|
64
|
+
ref={ref}
|
|
65
|
+
className={cn(
|
|
66
|
+
"left-0 top-0 w-full data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out md:absolute md:w-auto",
|
|
67
|
+
className,
|
|
68
|
+
)}
|
|
69
|
+
{...props}
|
|
70
|
+
/>
|
|
71
|
+
));
|
|
72
|
+
NavigationMenuContent.displayName = NavigationMenuPrimitive.Content.displayName;
|
|
73
|
+
|
|
74
|
+
const NavigationMenuLink = NavigationMenuPrimitive.Link;
|
|
75
|
+
|
|
76
|
+
const NavigationMenuViewport = React.forwardRef<
|
|
77
|
+
React.ElementRef<typeof NavigationMenuPrimitive.Viewport>,
|
|
78
|
+
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Viewport>
|
|
79
|
+
>(({ className, ...props }, ref) => (
|
|
80
|
+
<div className={cn("absolute left-0 top-full flex justify-center")}>
|
|
81
|
+
<NavigationMenuPrimitive.Viewport
|
|
82
|
+
className={cn(
|
|
83
|
+
ui("navigationViewport"),
|
|
84
|
+
className,
|
|
85
|
+
)}
|
|
86
|
+
ref={ref}
|
|
87
|
+
{...props}
|
|
88
|
+
/>
|
|
89
|
+
</div>
|
|
90
|
+
));
|
|
91
|
+
NavigationMenuViewport.displayName = NavigationMenuPrimitive.Viewport.displayName;
|
|
92
|
+
|
|
93
|
+
const NavigationMenuIndicator = React.forwardRef<
|
|
94
|
+
React.ElementRef<typeof NavigationMenuPrimitive.Indicator>,
|
|
95
|
+
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Indicator>
|
|
96
|
+
>(({ className, ...props }, ref) => (
|
|
97
|
+
<NavigationMenuPrimitive.Indicator
|
|
98
|
+
ref={ref}
|
|
99
|
+
className={cn(
|
|
100
|
+
"top-full z-[1] flex h-1.5 items-end justify-center overflow-hidden data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in",
|
|
101
|
+
className,
|
|
102
|
+
)}
|
|
103
|
+
{...props}
|
|
104
|
+
>
|
|
105
|
+
<div className="relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm bg-border shadow-md" />
|
|
106
|
+
</NavigationMenuPrimitive.Indicator>
|
|
107
|
+
));
|
|
108
|
+
NavigationMenuIndicator.displayName = NavigationMenuPrimitive.Indicator.displayName;
|
|
109
|
+
|
|
110
|
+
export {
|
|
111
|
+
NavigationMenu,
|
|
112
|
+
NavigationMenuContent,
|
|
113
|
+
NavigationMenuIndicator,
|
|
114
|
+
NavigationMenuItem,
|
|
115
|
+
NavigationMenuLink,
|
|
116
|
+
NavigationMenuList,
|
|
117
|
+
NavigationMenuTrigger,
|
|
118
|
+
NavigationMenuViewport,
|
|
119
|
+
navigationMenuTriggerStyle,
|
|
120
|
+
};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { Cell, Label, Pie, PieChart as RechartsPieChart } from "recharts";
|
|
2
|
+
import {
|
|
3
|
+
ChartContainer,
|
|
4
|
+
ChartLegend,
|
|
5
|
+
ChartLegendContent,
|
|
6
|
+
ChartTooltip,
|
|
7
|
+
ChartTooltipContent,
|
|
8
|
+
type ChartConfig,
|
|
9
|
+
} from "@/components/ui/chart";
|
|
10
|
+
import { chartColor, type ChartColorIndex } from "@/config/theme";
|
|
11
|
+
import { cn } from "@/lib/utils";
|
|
12
|
+
|
|
13
|
+
export type PieChartDataPoint = Record<string, string | number> & {
|
|
14
|
+
fill?: string;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
type PieChartProps = {
|
|
18
|
+
data: PieChartDataPoint[];
|
|
19
|
+
config: ChartConfig;
|
|
20
|
+
dataKey: string;
|
|
21
|
+
nameKey: string;
|
|
22
|
+
className?: string;
|
|
23
|
+
showLegend?: boolean;
|
|
24
|
+
innerRadius?: number;
|
|
25
|
+
showCenterLabel?: boolean;
|
|
26
|
+
centerLabel?: string;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export function PieChart({
|
|
30
|
+
data,
|
|
31
|
+
config,
|
|
32
|
+
dataKey,
|
|
33
|
+
nameKey,
|
|
34
|
+
className,
|
|
35
|
+
showLegend = true,
|
|
36
|
+
innerRadius = 0,
|
|
37
|
+
showCenterLabel = false,
|
|
38
|
+
centerLabel,
|
|
39
|
+
}: PieChartProps) {
|
|
40
|
+
const total = data.reduce((sum, item) => sum + Number(item[dataKey] ?? 0), 0);
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<ChartContainer config={config} className={cn("mx-auto aspect-square h-[300px] w-full max-w-[300px]", className)}>
|
|
44
|
+
<RechartsPieChart>
|
|
45
|
+
<ChartTooltip cursor={false} content={<ChartTooltipContent hideLabel nameKey={nameKey} />} />
|
|
46
|
+
{showLegend && <ChartLegend content={<ChartLegendContent nameKey={nameKey} />} />}
|
|
47
|
+
<Pie
|
|
48
|
+
data={data}
|
|
49
|
+
dataKey={dataKey}
|
|
50
|
+
nameKey={nameKey}
|
|
51
|
+
innerRadius={innerRadius}
|
|
52
|
+
strokeWidth={2}
|
|
53
|
+
>
|
|
54
|
+
{data.map((entry, index) => (
|
|
55
|
+
<Cell
|
|
56
|
+
key={`cell-${index}`}
|
|
57
|
+
fill={entry.fill ?? chartColor(((index % 5) + 1) as ChartColorIndex)}
|
|
58
|
+
/>
|
|
59
|
+
))}
|
|
60
|
+
{showCenterLabel && (
|
|
61
|
+
<Label
|
|
62
|
+
content={({ viewBox }) => {
|
|
63
|
+
if (viewBox && "cx" in viewBox && "cy" in viewBox) {
|
|
64
|
+
return (
|
|
65
|
+
<text
|
|
66
|
+
x={viewBox.cx}
|
|
67
|
+
y={viewBox.cy}
|
|
68
|
+
textAnchor="middle"
|
|
69
|
+
dominantBaseline="middle"
|
|
70
|
+
>
|
|
71
|
+
<tspan x={viewBox.cx} y={viewBox.cy} className="fill-foreground text-2xl font-bold">
|
|
72
|
+
{total.toLocaleString()}
|
|
73
|
+
</tspan>
|
|
74
|
+
<tspan x={viewBox.cx} y={(viewBox.cy || 0) + 20} className="fill-muted-foreground text-xs">
|
|
75
|
+
{centerLabel ?? "Total"}
|
|
76
|
+
</tspan>
|
|
77
|
+
</text>
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
}}
|
|
81
|
+
/>
|
|
82
|
+
)}
|
|
83
|
+
</Pie>
|
|
84
|
+
</RechartsPieChart>
|
|
85
|
+
</ChartContainer>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import * as PopoverPrimitive from "@radix-ui/react-popover";
|
|
3
|
+
import { ui } from "@/config/theme";
|
|
4
|
+
import { cn } from "@/lib/utils";
|
|
5
|
+
|
|
6
|
+
const Popover = PopoverPrimitive.Root;
|
|
7
|
+
const PopoverTrigger = PopoverPrimitive.Trigger;
|
|
8
|
+
const PopoverAnchor = PopoverPrimitive.Anchor;
|
|
9
|
+
|
|
10
|
+
const PopoverContent = React.forwardRef<
|
|
11
|
+
React.ElementRef<typeof PopoverPrimitive.Content>,
|
|
12
|
+
React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
|
|
13
|
+
>(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
|
|
14
|
+
<PopoverPrimitive.Portal>
|
|
15
|
+
<PopoverPrimitive.Content
|
|
16
|
+
ref={ref}
|
|
17
|
+
align={align}
|
|
18
|
+
sideOffset={sideOffset}
|
|
19
|
+
className={cn(
|
|
20
|
+
ui("popoverContent"),
|
|
21
|
+
className,
|
|
22
|
+
)}
|
|
23
|
+
{...props}
|
|
24
|
+
/>
|
|
25
|
+
</PopoverPrimitive.Portal>
|
|
26
|
+
));
|
|
27
|
+
PopoverContent.displayName = PopoverPrimitive.Content.displayName;
|
|
28
|
+
|
|
29
|
+
export { Popover, PopoverAnchor, PopoverContent, PopoverTrigger };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import * as ProgressPrimitive from "@radix-ui/react-progress";
|
|
3
|
+
import { ui } from "@/config/theme";
|
|
4
|
+
import { cn } from "@/lib/utils";
|
|
5
|
+
|
|
6
|
+
const Progress = React.forwardRef<
|
|
7
|
+
React.ElementRef<typeof ProgressPrimitive.Root>,
|
|
8
|
+
React.ComponentPropsWithoutRef<typeof ProgressPrimitive.Root>
|
|
9
|
+
>(({ className, value, ...props }, ref) => (
|
|
10
|
+
<ProgressPrimitive.Root ref={ref} className={cn(ui("progressTrack"), className)} {...props}>
|
|
11
|
+
<ProgressPrimitive.Indicator
|
|
12
|
+
className={ui("progressIndicator")}
|
|
13
|
+
style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
|
|
14
|
+
/>
|
|
15
|
+
</ProgressPrimitive.Root>
|
|
16
|
+
));
|
|
17
|
+
Progress.displayName = ProgressPrimitive.Root.displayName;
|
|
18
|
+
|
|
19
|
+
export { Progress };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import * as RadioGroupPrimitive from "@radix-ui/react-radio-group";
|
|
3
|
+
import { Circle } from "lucide-react";
|
|
4
|
+
import { ui } from "@/config/theme";
|
|
5
|
+
import { cn } from "@/lib/utils";
|
|
6
|
+
|
|
7
|
+
const RadioGroup = React.forwardRef<
|
|
8
|
+
React.ElementRef<typeof RadioGroupPrimitive.Root>,
|
|
9
|
+
React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Root>
|
|
10
|
+
>(({ className, ...props }, ref) => {
|
|
11
|
+
return <RadioGroupPrimitive.Root className={cn("grid gap-2", className)} {...props} ref={ref} />;
|
|
12
|
+
});
|
|
13
|
+
RadioGroup.displayName = RadioGroupPrimitive.Root.displayName;
|
|
14
|
+
|
|
15
|
+
const RadioGroupItem = React.forwardRef<
|
|
16
|
+
React.ElementRef<typeof RadioGroupPrimitive.Item>,
|
|
17
|
+
React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Item>
|
|
18
|
+
>(({ className, ...props }, ref) => {
|
|
19
|
+
return (
|
|
20
|
+
<RadioGroupPrimitive.Item
|
|
21
|
+
ref={ref}
|
|
22
|
+
className={cn(
|
|
23
|
+
ui("radioItem"),
|
|
24
|
+
className,
|
|
25
|
+
)}
|
|
26
|
+
{...props}
|
|
27
|
+
>
|
|
28
|
+
<RadioGroupPrimitive.Indicator className="flex items-center justify-center">
|
|
29
|
+
<Circle className="h-2.5 w-2.5 fill-primary" />
|
|
30
|
+
</RadioGroupPrimitive.Indicator>
|
|
31
|
+
</RadioGroupPrimitive.Item>
|
|
32
|
+
);
|
|
33
|
+
});
|
|
34
|
+
RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName;
|
|
35
|
+
|
|
36
|
+
export { RadioGroup, RadioGroupItem };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area";
|
|
3
|
+
import { ui } from "@/config/theme";
|
|
4
|
+
import { cn } from "@/lib/utils";
|
|
5
|
+
|
|
6
|
+
const ScrollArea = React.forwardRef<
|
|
7
|
+
React.ElementRef<typeof ScrollAreaPrimitive.Root>,
|
|
8
|
+
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root>
|
|
9
|
+
>(({ className, children, ...props }, ref) => (
|
|
10
|
+
<ScrollAreaPrimitive.Root ref={ref} className={cn("relative overflow-hidden", className)} {...props}>
|
|
11
|
+
<ScrollAreaPrimitive.Viewport className="h-full w-full rounded-[inherit]">{children}</ScrollAreaPrimitive.Viewport>
|
|
12
|
+
<ScrollBar />
|
|
13
|
+
<ScrollAreaPrimitive.Corner />
|
|
14
|
+
</ScrollAreaPrimitive.Root>
|
|
15
|
+
));
|
|
16
|
+
ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName;
|
|
17
|
+
|
|
18
|
+
const ScrollBar = React.forwardRef<
|
|
19
|
+
React.ElementRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>,
|
|
20
|
+
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>
|
|
21
|
+
>(({ className, orientation = "vertical", ...props }, ref) => (
|
|
22
|
+
<ScrollAreaPrimitive.ScrollAreaScrollbar
|
|
23
|
+
ref={ref}
|
|
24
|
+
orientation={orientation}
|
|
25
|
+
className={cn(
|
|
26
|
+
"flex touch-none select-none transition-colors",
|
|
27
|
+
orientation === "vertical" && "h-full w-2.5 border-l border-l-transparent p-px",
|
|
28
|
+
orientation === "horizontal" && "h-2.5 flex-col border-t border-t-transparent p-px",
|
|
29
|
+
className,
|
|
30
|
+
)}
|
|
31
|
+
{...props}
|
|
32
|
+
>
|
|
33
|
+
<ScrollAreaPrimitive.ScrollAreaThumb className={ui("scrollThumb")} />
|
|
34
|
+
</ScrollAreaPrimitive.ScrollAreaScrollbar>
|
|
35
|
+
));
|
|
36
|
+
ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName;
|
|
37
|
+
|
|
38
|
+
export { ScrollArea, ScrollBar };
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { Check, ChevronsUpDown, Search } from "lucide-react";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { Button } from "@/components/ui/button";
|
|
4
|
+
import { dropdownClasses, type DropdownOption } from "@/components/ui/dropdown-types";
|
|
5
|
+
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
|
6
|
+
import { ScrollArea } from "@/components/ui/scroll-area";
|
|
7
|
+
import { cn } from "@/lib/utils";
|
|
8
|
+
|
|
9
|
+
function filterOptions(options: DropdownOption[], search: string) {
|
|
10
|
+
const query = search.trim().toLowerCase();
|
|
11
|
+
if (!query) {
|
|
12
|
+
return options;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return options.filter(
|
|
16
|
+
(option) =>
|
|
17
|
+
option.label.toLowerCase().includes(query) || option.description?.toLowerCase().includes(query),
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type SearchableDropdownProps = {
|
|
22
|
+
options: DropdownOption[];
|
|
23
|
+
value?: string;
|
|
24
|
+
onValueChange?: (value: string) => void;
|
|
25
|
+
placeholder?: string;
|
|
26
|
+
searchPlaceholder?: string;
|
|
27
|
+
emptyText?: string;
|
|
28
|
+
disabled?: boolean;
|
|
29
|
+
className?: string;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export function SearchableDropdown({
|
|
33
|
+
options,
|
|
34
|
+
value,
|
|
35
|
+
onValueChange,
|
|
36
|
+
placeholder = "Select option...",
|
|
37
|
+
searchPlaceholder = "Search...",
|
|
38
|
+
emptyText = "No results found.",
|
|
39
|
+
disabled = false,
|
|
40
|
+
className,
|
|
41
|
+
}: SearchableDropdownProps) {
|
|
42
|
+
const [open, setOpen] = React.useState(false);
|
|
43
|
+
const [search, setSearch] = React.useState("");
|
|
44
|
+
|
|
45
|
+
const selected = options.find((option) => option.value === value);
|
|
46
|
+
const filteredOptions = filterOptions(options, search);
|
|
47
|
+
|
|
48
|
+
const handleOpenChange = (nextOpen: boolean) => {
|
|
49
|
+
setOpen(nextOpen);
|
|
50
|
+
if (!nextOpen) {
|
|
51
|
+
setSearch("");
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const handleSelect = (optionValue: string) => {
|
|
56
|
+
onValueChange?.(optionValue);
|
|
57
|
+
setOpen(false);
|
|
58
|
+
setSearch("");
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<Popover open={open} onOpenChange={handleOpenChange}>
|
|
63
|
+
<PopoverTrigger asChild>
|
|
64
|
+
<Button
|
|
65
|
+
variant="outline"
|
|
66
|
+
role="combobox"
|
|
67
|
+
aria-expanded={open}
|
|
68
|
+
disabled={disabled}
|
|
69
|
+
className={cn(dropdownClasses.trigger, !selected && dropdownClasses.emptyText, className)}
|
|
70
|
+
>
|
|
71
|
+
<span className="truncate">{selected ? selected.label : placeholder}</span>
|
|
72
|
+
<ChevronsUpDown className={dropdownClasses.chevron} />
|
|
73
|
+
</Button>
|
|
74
|
+
</PopoverTrigger>
|
|
75
|
+
<PopoverContent align="start" className={dropdownClasses.popoverContent}>
|
|
76
|
+
<div className={dropdownClasses.searchBar}>
|
|
77
|
+
<Search className={dropdownClasses.searchIcon} />
|
|
78
|
+
<input
|
|
79
|
+
value={search}
|
|
80
|
+
onChange={(event) => setSearch(event.target.value)}
|
|
81
|
+
placeholder={searchPlaceholder}
|
|
82
|
+
className={dropdownClasses.searchInput}
|
|
83
|
+
/>
|
|
84
|
+
</div>
|
|
85
|
+
<ScrollArea className="max-h-60">
|
|
86
|
+
<div className="p-1">
|
|
87
|
+
{filteredOptions.length === 0 ? (
|
|
88
|
+
<p className={cn(dropdownClasses.empty, dropdownClasses.emptyText)}>{emptyText}</p>
|
|
89
|
+
) : (
|
|
90
|
+
filteredOptions.map((option) => (
|
|
91
|
+
<button
|
|
92
|
+
key={option.value}
|
|
93
|
+
type="button"
|
|
94
|
+
disabled={option.disabled}
|
|
95
|
+
onClick={() => handleSelect(option.value)}
|
|
96
|
+
className={cn(
|
|
97
|
+
dropdownClasses.optionItem,
|
|
98
|
+
value === option.value && dropdownClasses.optionItemActive,
|
|
99
|
+
)}
|
|
100
|
+
>
|
|
101
|
+
<Check
|
|
102
|
+
className={cn(dropdownClasses.checkIcon, value === option.value ? "opacity-100" : "opacity-0")}
|
|
103
|
+
/>
|
|
104
|
+
<div className="flex flex-col items-start text-left">
|
|
105
|
+
<span>{option.label}</span>
|
|
106
|
+
{option.description ? (
|
|
107
|
+
<span className={cn("text-xs", dropdownClasses.optionDescription)}>{option.description}</span>
|
|
108
|
+
) : null}
|
|
109
|
+
</div>
|
|
110
|
+
</button>
|
|
111
|
+
))
|
|
112
|
+
)}
|
|
113
|
+
</div>
|
|
114
|
+
</ScrollArea>
|
|
115
|
+
</PopoverContent>
|
|
116
|
+
</Popover>
|
|
117
|
+
);
|
|
118
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import * as SelectPrimitive from "@radix-ui/react-select";
|
|
3
|
+
import { Check, ChevronDown, ChevronUp } from "lucide-react";
|
|
4
|
+
import { ui } from "@/config/theme";
|
|
5
|
+
import { cn } from "@/lib/utils";
|
|
6
|
+
|
|
7
|
+
const Select = SelectPrimitive.Root;
|
|
8
|
+
const SelectGroup = SelectPrimitive.Group;
|
|
9
|
+
const SelectValue = SelectPrimitive.Value;
|
|
10
|
+
|
|
11
|
+
const SelectTrigger = React.forwardRef<
|
|
12
|
+
React.ElementRef<typeof SelectPrimitive.Trigger>,
|
|
13
|
+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
|
|
14
|
+
>(({ className, children, ...props }, ref) => (
|
|
15
|
+
<SelectPrimitive.Trigger
|
|
16
|
+
ref={ref}
|
|
17
|
+
className={cn(
|
|
18
|
+
ui("selectTrigger"),
|
|
19
|
+
className,
|
|
20
|
+
)}
|
|
21
|
+
{...props}
|
|
22
|
+
>
|
|
23
|
+
{children}
|
|
24
|
+
<SelectPrimitive.Icon asChild>
|
|
25
|
+
<ChevronDown className="h-4 w-4 opacity-50" />
|
|
26
|
+
</SelectPrimitive.Icon>
|
|
27
|
+
</SelectPrimitive.Trigger>
|
|
28
|
+
));
|
|
29
|
+
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
|
|
30
|
+
|
|
31
|
+
const SelectScrollUpButton = React.forwardRef<
|
|
32
|
+
React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,
|
|
33
|
+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>
|
|
34
|
+
>(({ className, ...props }, ref) => (
|
|
35
|
+
<SelectPrimitive.ScrollUpButton
|
|
36
|
+
ref={ref}
|
|
37
|
+
className={cn("flex cursor-default items-center justify-center py-1", className)}
|
|
38
|
+
{...props}
|
|
39
|
+
>
|
|
40
|
+
<ChevronUp className="h-4 w-4" />
|
|
41
|
+
</SelectPrimitive.ScrollUpButton>
|
|
42
|
+
));
|
|
43
|
+
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
|
|
44
|
+
|
|
45
|
+
const SelectScrollDownButton = React.forwardRef<
|
|
46
|
+
React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,
|
|
47
|
+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>
|
|
48
|
+
>(({ className, ...props }, ref) => (
|
|
49
|
+
<SelectPrimitive.ScrollDownButton
|
|
50
|
+
ref={ref}
|
|
51
|
+
className={cn("flex cursor-default items-center justify-center py-1", className)}
|
|
52
|
+
{...props}
|
|
53
|
+
>
|
|
54
|
+
<ChevronDown className="h-4 w-4" />
|
|
55
|
+
</SelectPrimitive.ScrollDownButton>
|
|
56
|
+
));
|
|
57
|
+
SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName;
|
|
58
|
+
|
|
59
|
+
const SelectContent = React.forwardRef<
|
|
60
|
+
React.ElementRef<typeof SelectPrimitive.Content>,
|
|
61
|
+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
|
|
62
|
+
>(({ className, children, position = "popper", ...props }, ref) => (
|
|
63
|
+
<SelectPrimitive.Portal>
|
|
64
|
+
<SelectPrimitive.Content
|
|
65
|
+
ref={ref}
|
|
66
|
+
className={cn(
|
|
67
|
+
ui("selectContent"),
|
|
68
|
+
position === "popper" &&
|
|
69
|
+
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
|
|
70
|
+
className,
|
|
71
|
+
)}
|
|
72
|
+
position={position}
|
|
73
|
+
{...props}
|
|
74
|
+
>
|
|
75
|
+
<SelectScrollUpButton />
|
|
76
|
+
<SelectPrimitive.Viewport
|
|
77
|
+
className={cn(
|
|
78
|
+
"p-1",
|
|
79
|
+
position === "popper" &&
|
|
80
|
+
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]",
|
|
81
|
+
)}
|
|
82
|
+
>
|
|
83
|
+
{children}
|
|
84
|
+
</SelectPrimitive.Viewport>
|
|
85
|
+
<SelectScrollDownButton />
|
|
86
|
+
</SelectPrimitive.Content>
|
|
87
|
+
</SelectPrimitive.Portal>
|
|
88
|
+
));
|
|
89
|
+
SelectContent.displayName = SelectPrimitive.Content.displayName;
|
|
90
|
+
|
|
91
|
+
const SelectLabel = React.forwardRef<
|
|
92
|
+
React.ElementRef<typeof SelectPrimitive.Label>,
|
|
93
|
+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
|
|
94
|
+
>(({ className, ...props }, ref) => (
|
|
95
|
+
<SelectPrimitive.Label ref={ref} className={cn("px-2 py-1.5 text-sm font-semibold", className)} {...props} />
|
|
96
|
+
));
|
|
97
|
+
SelectLabel.displayName = SelectPrimitive.Label.displayName;
|
|
98
|
+
|
|
99
|
+
const SelectItem = React.forwardRef<
|
|
100
|
+
React.ElementRef<typeof SelectPrimitive.Item>,
|
|
101
|
+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
|
|
102
|
+
>(({ className, children, ...props }, ref) => (
|
|
103
|
+
<SelectPrimitive.Item
|
|
104
|
+
ref={ref}
|
|
105
|
+
className={cn(
|
|
106
|
+
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
107
|
+
className,
|
|
108
|
+
)}
|
|
109
|
+
{...props}
|
|
110
|
+
>
|
|
111
|
+
<span className="absolute right-2 flex h-3.5 w-3.5 items-center justify-center">
|
|
112
|
+
<SelectPrimitive.ItemIndicator>
|
|
113
|
+
<Check className="h-4 w-4" />
|
|
114
|
+
</SelectPrimitive.ItemIndicator>
|
|
115
|
+
</span>
|
|
116
|
+
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
|
|
117
|
+
</SelectPrimitive.Item>
|
|
118
|
+
));
|
|
119
|
+
SelectItem.displayName = SelectPrimitive.Item.displayName;
|
|
120
|
+
|
|
121
|
+
const SelectSeparator = React.forwardRef<
|
|
122
|
+
React.ElementRef<typeof SelectPrimitive.Separator>,
|
|
123
|
+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
|
|
124
|
+
>(({ className, ...props }, ref) => (
|
|
125
|
+
<SelectPrimitive.Separator ref={ref} className={cn("-mx-1 my-1 h-px bg-muted", className)} {...props} />
|
|
126
|
+
));
|
|
127
|
+
SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
|
|
128
|
+
|
|
129
|
+
export {
|
|
130
|
+
Select,
|
|
131
|
+
SelectContent,
|
|
132
|
+
SelectGroup,
|
|
133
|
+
SelectItem,
|
|
134
|
+
SelectLabel,
|
|
135
|
+
SelectScrollDownButton,
|
|
136
|
+
SelectScrollUpButton,
|
|
137
|
+
SelectSeparator,
|
|
138
|
+
SelectTrigger,
|
|
139
|
+
SelectValue,
|
|
140
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import * as SeparatorPrimitive from "@radix-ui/react-separator";
|
|
3
|
+
import { ui } from "@/config/theme";
|
|
4
|
+
import { cn } from "@/lib/utils";
|
|
5
|
+
|
|
6
|
+
const Separator = React.forwardRef<
|
|
7
|
+
React.ElementRef<typeof SeparatorPrimitive.Root>,
|
|
8
|
+
React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>
|
|
9
|
+
>(({ className, orientation = "horizontal", ...props }, ref) => (
|
|
10
|
+
<SeparatorPrimitive.Root
|
|
11
|
+
ref={ref}
|
|
12
|
+
decorative
|
|
13
|
+
orientation={orientation}
|
|
14
|
+
className={cn(ui("separator"), orientation === "horizontal" ? "h-px w-full" : "h-full w-px", className)}
|
|
15
|
+
{...props}
|
|
16
|
+
/>
|
|
17
|
+
));
|
|
18
|
+
Separator.displayName = SeparatorPrimitive.Root.displayName;
|
|
19
|
+
|
|
20
|
+
export { Separator };
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
|
3
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
4
|
+
import { X } from "lucide-react";
|
|
5
|
+
import { ui } from "@/config/theme";
|
|
6
|
+
import { cn } from "@/lib/utils";
|
|
7
|
+
|
|
8
|
+
export const Sheet = DialogPrimitive.Root;
|
|
9
|
+
export const SheetTrigger = DialogPrimitive.Trigger;
|
|
10
|
+
export const SheetClose = DialogPrimitive.Close;
|
|
11
|
+
|
|
12
|
+
const sheetVariants = cva(
|
|
13
|
+
ui("sheetContent"),
|
|
14
|
+
{
|
|
15
|
+
variants: {
|
|
16
|
+
side: {
|
|
17
|
+
left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm",
|
|
18
|
+
right:
|
|
19
|
+
"inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm",
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
defaultVariants: {
|
|
23
|
+
side: "left",
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
export const SheetContent = React.forwardRef<
|
|
29
|
+
React.ElementRef<typeof DialogPrimitive.Content>,
|
|
30
|
+
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> & VariantProps<typeof sheetVariants>
|
|
31
|
+
>(({ side = "left", className, children, ...props }, ref) => (
|
|
32
|
+
<DialogPrimitive.Portal>
|
|
33
|
+
<DialogPrimitive.Overlay
|
|
34
|
+
className={cn(
|
|
35
|
+
ui("overlayLight"),
|
|
36
|
+
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
|
37
|
+
)}
|
|
38
|
+
/>
|
|
39
|
+
<DialogPrimitive.Content ref={ref} className={cn(sheetVariants({ side }), className)} {...props}>
|
|
40
|
+
{children}
|
|
41
|
+
<DialogPrimitive.Close className={ui("iconClose")}>
|
|
42
|
+
<X className="h-4 w-4" />
|
|
43
|
+
<span className="sr-only">Close</span>
|
|
44
|
+
</DialogPrimitive.Close>
|
|
45
|
+
</DialogPrimitive.Content>
|
|
46
|
+
</DialogPrimitive.Portal>
|
|
47
|
+
));
|
|
48
|
+
SheetContent.displayName = "SheetContent";
|
|
49
|
+
|
|
50
|
+
function SheetHeader({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
|
|
51
|
+
return <div className={cn("flex flex-col space-y-2 text-center sm:text-left", className)} {...props} />;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const SheetTitle = React.forwardRef<
|
|
55
|
+
React.ElementRef<typeof DialogPrimitive.Title>,
|
|
56
|
+
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
|
|
57
|
+
>(({ className, ...props }, ref) => (
|
|
58
|
+
<DialogPrimitive.Title ref={ref} className={cn("text-lg font-semibold text-foreground", className)} {...props} />
|
|
59
|
+
));
|
|
60
|
+
SheetTitle.displayName = DialogPrimitive.Title.displayName;
|
|
61
|
+
|
|
62
|
+
const SheetDescription = React.forwardRef<
|
|
63
|
+
React.ElementRef<typeof DialogPrimitive.Description>,
|
|
64
|
+
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
|
|
65
|
+
>(({ className, ...props }, ref) => (
|
|
66
|
+
<DialogPrimitive.Description ref={ref} className={cn(ui("typographyMuted"), className)} {...props} />
|
|
67
|
+
));
|
|
68
|
+
SheetDescription.displayName = DialogPrimitive.Description.displayName;
|
|
69
|
+
|
|
70
|
+
export { SheetDescription, SheetHeader, SheetTitle };
|