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,81 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Drawer as DrawerPrimitive } from "vaul";
|
|
3
|
+
import { ui } from "@/config/theme";
|
|
4
|
+
import { cn } from "@/lib/utils";
|
|
5
|
+
|
|
6
|
+
const Drawer = ({ shouldScaleBackground = true, ...props }: React.ComponentProps<typeof DrawerPrimitive.Root>) => (
|
|
7
|
+
<DrawerPrimitive.Root shouldScaleBackground={shouldScaleBackground} {...props} />
|
|
8
|
+
);
|
|
9
|
+
Drawer.displayName = "Drawer";
|
|
10
|
+
|
|
11
|
+
const DrawerTrigger = DrawerPrimitive.Trigger;
|
|
12
|
+
const DrawerPortal = DrawerPrimitive.Portal;
|
|
13
|
+
const DrawerClose = DrawerPrimitive.Close;
|
|
14
|
+
|
|
15
|
+
const DrawerOverlay = React.forwardRef<
|
|
16
|
+
React.ElementRef<typeof DrawerPrimitive.Overlay>,
|
|
17
|
+
React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Overlay>
|
|
18
|
+
>(({ className, ...props }, ref) => (
|
|
19
|
+
<DrawerPrimitive.Overlay ref={ref} className={cn(ui("overlay"), className)} {...props} />
|
|
20
|
+
));
|
|
21
|
+
DrawerOverlay.displayName = DrawerPrimitive.Overlay.displayName;
|
|
22
|
+
|
|
23
|
+
const DrawerContent = React.forwardRef<
|
|
24
|
+
React.ElementRef<typeof DrawerPrimitive.Content>,
|
|
25
|
+
React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Content>
|
|
26
|
+
>(({ className, children, ...props }, ref) => (
|
|
27
|
+
<DrawerPortal>
|
|
28
|
+
<DrawerOverlay />
|
|
29
|
+
<DrawerPrimitive.Content
|
|
30
|
+
ref={ref}
|
|
31
|
+
className={cn(
|
|
32
|
+
ui("drawerContent"),
|
|
33
|
+
className,
|
|
34
|
+
)}
|
|
35
|
+
{...props}
|
|
36
|
+
>
|
|
37
|
+
<div className="mx-auto mt-4 h-2 w-[100px] rounded-full bg-muted" />
|
|
38
|
+
{children}
|
|
39
|
+
</DrawerPrimitive.Content>
|
|
40
|
+
</DrawerPortal>
|
|
41
|
+
));
|
|
42
|
+
DrawerContent.displayName = "DrawerContent";
|
|
43
|
+
|
|
44
|
+
function DrawerHeader({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
|
|
45
|
+
return <div className={cn("grid gap-1.5 p-4 text-center sm:text-left", className)} {...props} />;
|
|
46
|
+
}
|
|
47
|
+
DrawerHeader.displayName = "DrawerHeader";
|
|
48
|
+
|
|
49
|
+
function DrawerFooter({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
|
|
50
|
+
return <div className={cn("mt-auto flex flex-col gap-2 p-4", className)} {...props} />;
|
|
51
|
+
}
|
|
52
|
+
DrawerFooter.displayName = "DrawerFooter";
|
|
53
|
+
|
|
54
|
+
const DrawerTitle = React.forwardRef<
|
|
55
|
+
React.ElementRef<typeof DrawerPrimitive.Title>,
|
|
56
|
+
React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Title>
|
|
57
|
+
>(({ className, ...props }, ref) => (
|
|
58
|
+
<DrawerPrimitive.Title ref={ref} className={cn("text-lg font-semibold leading-none tracking-tight", className)} {...props} />
|
|
59
|
+
));
|
|
60
|
+
DrawerTitle.displayName = DrawerPrimitive.Title.displayName;
|
|
61
|
+
|
|
62
|
+
const DrawerDescription = React.forwardRef<
|
|
63
|
+
React.ElementRef<typeof DrawerPrimitive.Description>,
|
|
64
|
+
React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Description>
|
|
65
|
+
>(({ className, ...props }, ref) => (
|
|
66
|
+
<DrawerPrimitive.Description ref={ref} className={cn(ui("typographyMuted"), className)} {...props} />
|
|
67
|
+
));
|
|
68
|
+
DrawerDescription.displayName = DrawerPrimitive.Description.displayName;
|
|
69
|
+
|
|
70
|
+
export {
|
|
71
|
+
Drawer,
|
|
72
|
+
DrawerClose,
|
|
73
|
+
DrawerContent,
|
|
74
|
+
DrawerDescription,
|
|
75
|
+
DrawerFooter,
|
|
76
|
+
DrawerHeader,
|
|
77
|
+
DrawerOverlay,
|
|
78
|
+
DrawerPortal,
|
|
79
|
+
DrawerTitle,
|
|
80
|
+
DrawerTrigger,
|
|
81
|
+
};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
|
|
3
|
+
import { Check } from "lucide-react";
|
|
4
|
+
import { ui } from "@/config/theme";
|
|
5
|
+
import { cn } from "@/lib/utils";
|
|
6
|
+
|
|
7
|
+
export const DropdownMenu = DropdownMenuPrimitive.Root;
|
|
8
|
+
export const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
|
|
9
|
+
|
|
10
|
+
export const DropdownMenuContent = React.forwardRef<
|
|
11
|
+
React.ElementRef<typeof DropdownMenuPrimitive.Content>,
|
|
12
|
+
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>
|
|
13
|
+
>(({ className, sideOffset = 4, ...props }, ref) => (
|
|
14
|
+
<DropdownMenuPrimitive.Portal>
|
|
15
|
+
<DropdownMenuPrimitive.Content
|
|
16
|
+
ref={ref}
|
|
17
|
+
sideOffset={sideOffset}
|
|
18
|
+
className={cn(ui("dropdownContent"), className)}
|
|
19
|
+
{...props}
|
|
20
|
+
/>
|
|
21
|
+
</DropdownMenuPrimitive.Portal>
|
|
22
|
+
));
|
|
23
|
+
DropdownMenuContent.displayName = "DropdownMenuContent";
|
|
24
|
+
|
|
25
|
+
export const DropdownMenuItem = React.forwardRef<
|
|
26
|
+
React.ElementRef<typeof DropdownMenuPrimitive.Item>,
|
|
27
|
+
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item>
|
|
28
|
+
>(({ className, ...props }, ref) => (
|
|
29
|
+
<DropdownMenuPrimitive.Item
|
|
30
|
+
ref={ref}
|
|
31
|
+
className={cn(
|
|
32
|
+
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
33
|
+
className,
|
|
34
|
+
)}
|
|
35
|
+
{...props}
|
|
36
|
+
/>
|
|
37
|
+
));
|
|
38
|
+
DropdownMenuItem.displayName = "DropdownMenuItem";
|
|
39
|
+
|
|
40
|
+
export const DropdownMenuCheckboxItem = React.forwardRef<
|
|
41
|
+
React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,
|
|
42
|
+
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>
|
|
43
|
+
>(({ className, children, checked, ...props }, ref) => (
|
|
44
|
+
<DropdownMenuPrimitive.CheckboxItem
|
|
45
|
+
ref={ref}
|
|
46
|
+
className={cn(
|
|
47
|
+
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
48
|
+
className,
|
|
49
|
+
)}
|
|
50
|
+
checked={checked}
|
|
51
|
+
{...props}
|
|
52
|
+
>
|
|
53
|
+
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
|
54
|
+
<DropdownMenuPrimitive.ItemIndicator>
|
|
55
|
+
<Check className="h-4 w-4" />
|
|
56
|
+
</DropdownMenuPrimitive.ItemIndicator>
|
|
57
|
+
</span>
|
|
58
|
+
{children}
|
|
59
|
+
</DropdownMenuPrimitive.CheckboxItem>
|
|
60
|
+
));
|
|
61
|
+
DropdownMenuCheckboxItem.displayName = DropdownMenuPrimitive.CheckboxItem.displayName;
|
|
62
|
+
|
|
63
|
+
export const DropdownMenuLabel = React.forwardRef<
|
|
64
|
+
React.ElementRef<typeof DropdownMenuPrimitive.Label>,
|
|
65
|
+
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & { inset?: boolean }
|
|
66
|
+
>(({ className, inset, ...props }, ref) => (
|
|
67
|
+
<DropdownMenuPrimitive.Label
|
|
68
|
+
ref={ref}
|
|
69
|
+
className={cn("px-2 py-1.5 text-sm font-semibold", inset && "pl-8", className)}
|
|
70
|
+
{...props}
|
|
71
|
+
/>
|
|
72
|
+
));
|
|
73
|
+
DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;
|
|
74
|
+
|
|
75
|
+
export const DropdownMenuSeparator = React.forwardRef<
|
|
76
|
+
React.ElementRef<typeof DropdownMenuPrimitive.Separator>,
|
|
77
|
+
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>
|
|
78
|
+
>(({ className, ...props }, ref) => (
|
|
79
|
+
<DropdownMenuPrimitive.Separator ref={ref} className={cn("-mx-1 my-1 h-px bg-muted", className)} {...props} />
|
|
80
|
+
));
|
|
81
|
+
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { ui } from "@/config/theme";
|
|
2
|
+
|
|
3
|
+
export type DropdownOption = {
|
|
4
|
+
value: string;
|
|
5
|
+
label: string;
|
|
6
|
+
description?: string;
|
|
7
|
+
disabled?: boolean;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
/** Theme-backed class tokens for searchable & multi-select dropdowns */
|
|
11
|
+
export const dropdownClasses = {
|
|
12
|
+
trigger: ui("dropdownTrigger"),
|
|
13
|
+
triggerMulti: ui("dropdownTriggerMulti"),
|
|
14
|
+
popoverContent: ui("dropdownPopoverContent"),
|
|
15
|
+
searchBar: ui("dropdownSearchBar"),
|
|
16
|
+
searchInput: ui("dropdownSearchInput"),
|
|
17
|
+
searchIcon: ui("dropdownSearchIcon"),
|
|
18
|
+
empty: ui("dropdownEmpty"),
|
|
19
|
+
emptyText: ui("typographyMuted"),
|
|
20
|
+
optionItem: ui("dropdownOptionItem"),
|
|
21
|
+
optionItemActive: ui("dropdownOptionItemActive"),
|
|
22
|
+
optionItemMulti: ui("dropdownOptionItemMulti"),
|
|
23
|
+
optionItemMultiSelected: ui("dropdownOptionItemMultiSelected"),
|
|
24
|
+
optionDescription: ui("typographyMuted"),
|
|
25
|
+
footer: ui("dropdownFooter"),
|
|
26
|
+
checkIcon: ui("dropdownCheckIcon"),
|
|
27
|
+
chevron: ui("dropdownChevron"),
|
|
28
|
+
} as const;
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Label } from "@/components/ui/label";
|
|
3
|
+
import { Separator } from "@/components/ui/separator";
|
|
4
|
+
import { ui } from "@/config/theme";
|
|
5
|
+
import { cn } from "@/lib/utils";
|
|
6
|
+
|
|
7
|
+
function FieldSet({ className, ...props }: React.ComponentProps<"fieldset">) {
|
|
8
|
+
return (
|
|
9
|
+
<fieldset
|
|
10
|
+
data-slot="field-set"
|
|
11
|
+
className={cn("flex flex-col gap-6", "has-[>[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3", className)}
|
|
12
|
+
{...props}
|
|
13
|
+
/>
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function FieldLegend({
|
|
18
|
+
className,
|
|
19
|
+
variant = "legend",
|
|
20
|
+
...props
|
|
21
|
+
}: React.ComponentProps<"legend"> & { variant?: "legend" | "label" }) {
|
|
22
|
+
return (
|
|
23
|
+
<legend
|
|
24
|
+
data-slot="field-legend"
|
|
25
|
+
data-variant={variant}
|
|
26
|
+
className={cn("mb-3 font-medium", variant === "legend" ? "text-base" : "text-sm", className)}
|
|
27
|
+
{...props}
|
|
28
|
+
/>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function FieldGroup({ className, ...props }: React.ComponentProps<"div">) {
|
|
33
|
+
return (
|
|
34
|
+
<div
|
|
35
|
+
data-slot="field-group"
|
|
36
|
+
className={cn("flex w-full flex-col gap-7 [&>[data-slot=field-group]]:gap-4", className)}
|
|
37
|
+
{...props}
|
|
38
|
+
/>
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function Field({
|
|
43
|
+
className,
|
|
44
|
+
orientation = "vertical",
|
|
45
|
+
...props
|
|
46
|
+
}: React.ComponentProps<"div"> & {
|
|
47
|
+
orientation?: "vertical" | "horizontal" | "responsive";
|
|
48
|
+
}) {
|
|
49
|
+
return (
|
|
50
|
+
<div
|
|
51
|
+
role="group"
|
|
52
|
+
data-slot="field"
|
|
53
|
+
data-orientation={orientation}
|
|
54
|
+
className={cn(
|
|
55
|
+
"group/field flex w-full gap-3 data-[invalid=true]:text-destructive",
|
|
56
|
+
orientation === "vertical" && "flex-col [&>*]:w-full",
|
|
57
|
+
orientation === "horizontal" && "flex-row items-center [&>[data-slot=field-label]]:min-w-32",
|
|
58
|
+
orientation === "responsive" && "flex-col md:flex-row md:items-center [&>*]:w-full md:[&>*]:w-auto",
|
|
59
|
+
className,
|
|
60
|
+
)}
|
|
61
|
+
{...props}
|
|
62
|
+
/>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function FieldContent({ className, ...props }: React.ComponentProps<"div">) {
|
|
67
|
+
return (
|
|
68
|
+
<div data-slot="field-content" className={cn("group/field-content flex flex-1 flex-col gap-1.5", className)} {...props} />
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function FieldLabel({ className, ...props }: React.ComponentProps<typeof Label>) {
|
|
73
|
+
return (
|
|
74
|
+
<Label
|
|
75
|
+
data-slot="field-label"
|
|
76
|
+
className={cn(
|
|
77
|
+
"group/field-label peer/field-label flex w-fit gap-2 leading-snug group-data-[disabled=true]/field:opacity-50",
|
|
78
|
+
className,
|
|
79
|
+
)}
|
|
80
|
+
{...props}
|
|
81
|
+
/>
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function FieldTitle({ className, ...props }: React.ComponentProps<"div">) {
|
|
86
|
+
return (
|
|
87
|
+
<div
|
|
88
|
+
data-slot="field-label"
|
|
89
|
+
className={cn(
|
|
90
|
+
"flex w-fit items-center gap-2 text-sm font-medium leading-snug group-data-[disabled=true]/field:opacity-50",
|
|
91
|
+
className,
|
|
92
|
+
)}
|
|
93
|
+
{...props}
|
|
94
|
+
/>
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function FieldDescription({ className, ...props }: React.ComponentProps<"p">) {
|
|
99
|
+
return (
|
|
100
|
+
<p
|
|
101
|
+
data-slot="field-description"
|
|
102
|
+
className={cn(
|
|
103
|
+
ui("fieldDescription"),
|
|
104
|
+
"[&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary",
|
|
105
|
+
className,
|
|
106
|
+
)}
|
|
107
|
+
{...props}
|
|
108
|
+
/>
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function FieldSeparator({
|
|
113
|
+
children,
|
|
114
|
+
className,
|
|
115
|
+
...props
|
|
116
|
+
}: React.ComponentProps<"div"> & {
|
|
117
|
+
children?: React.ReactNode;
|
|
118
|
+
}) {
|
|
119
|
+
return (
|
|
120
|
+
<div
|
|
121
|
+
data-slot="field-separator"
|
|
122
|
+
data-content={!!children}
|
|
123
|
+
className={cn("relative -my-2 h-5 text-sm", className)}
|
|
124
|
+
{...props}
|
|
125
|
+
>
|
|
126
|
+
<Separator className="absolute inset-0 top-1/2" />
|
|
127
|
+
{children && (
|
|
128
|
+
<span
|
|
129
|
+
className={cn("relative mx-auto block w-fit bg-background px-2", ui("typographyMuted"))}
|
|
130
|
+
data-slot="field-separator-content"
|
|
131
|
+
>
|
|
132
|
+
{children}
|
|
133
|
+
</span>
|
|
134
|
+
)}
|
|
135
|
+
</div>
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function FieldError({
|
|
140
|
+
className,
|
|
141
|
+
children,
|
|
142
|
+
errors,
|
|
143
|
+
...props
|
|
144
|
+
}: React.ComponentProps<"div"> & {
|
|
145
|
+
errors?: Array<{ message?: string } | undefined>;
|
|
146
|
+
}) {
|
|
147
|
+
const content = React.useMemo(() => {
|
|
148
|
+
if (children) {
|
|
149
|
+
return children;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (!errors?.length) {
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (errors.length === 1 && errors[0]?.message) {
|
|
157
|
+
return errors[0].message;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return (
|
|
161
|
+
<ul className="ml-4 flex list-disc flex-col gap-1">
|
|
162
|
+
{errors.map((error, index) => (error?.message ? <li key={index}>{error.message}</li> : null))}
|
|
163
|
+
</ul>
|
|
164
|
+
);
|
|
165
|
+
}, [children, errors]);
|
|
166
|
+
|
|
167
|
+
if (!content) {
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return (
|
|
172
|
+
<div
|
|
173
|
+
role="alert"
|
|
174
|
+
data-slot="field-error"
|
|
175
|
+
className={cn("text-sm font-normal text-destructive", className)}
|
|
176
|
+
{...props}
|
|
177
|
+
>
|
|
178
|
+
{content}
|
|
179
|
+
</div>
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export {
|
|
184
|
+
Field,
|
|
185
|
+
FieldContent,
|
|
186
|
+
FieldDescription,
|
|
187
|
+
FieldError,
|
|
188
|
+
FieldGroup,
|
|
189
|
+
FieldLabel,
|
|
190
|
+
FieldLegend,
|
|
191
|
+
FieldSeparator,
|
|
192
|
+
FieldSet,
|
|
193
|
+
FieldTitle,
|
|
194
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import * as HoverCardPrimitive from "@radix-ui/react-hover-card";
|
|
3
|
+
import { ui } from "@/config/theme";
|
|
4
|
+
import { cn } from "@/lib/utils";
|
|
5
|
+
|
|
6
|
+
const HoverCard = HoverCardPrimitive.Root;
|
|
7
|
+
const HoverCardTrigger = HoverCardPrimitive.Trigger;
|
|
8
|
+
|
|
9
|
+
const HoverCardContent = React.forwardRef<
|
|
10
|
+
React.ElementRef<typeof HoverCardPrimitive.Content>,
|
|
11
|
+
React.ComponentPropsWithoutRef<typeof HoverCardPrimitive.Content>
|
|
12
|
+
>(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
|
|
13
|
+
<HoverCardPrimitive.Content
|
|
14
|
+
ref={ref}
|
|
15
|
+
align={align}
|
|
16
|
+
sideOffset={sideOffset}
|
|
17
|
+
className={cn(
|
|
18
|
+
ui("popoverHover"),
|
|
19
|
+
className,
|
|
20
|
+
)}
|
|
21
|
+
{...props}
|
|
22
|
+
/>
|
|
23
|
+
));
|
|
24
|
+
HoverCardContent.displayName = HoverCardPrimitive.Content.displayName;
|
|
25
|
+
|
|
26
|
+
export { HoverCard, HoverCardContent, HoverCardTrigger };
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { Button } from "@/components/ui/button";
|
|
4
|
+
import { Input } from "@/components/ui/input";
|
|
5
|
+
import { ui } from "@/config/theme";
|
|
6
|
+
import { cn } from "@/lib/utils";
|
|
7
|
+
|
|
8
|
+
function InputGroup({ className, ...props }: React.ComponentProps<"div">) {
|
|
9
|
+
return (
|
|
10
|
+
<div
|
|
11
|
+
data-slot="input-group"
|
|
12
|
+
className={cn(ui("inputGroup"), className)}
|
|
13
|
+
{...props}
|
|
14
|
+
/>
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const inputGroupAddonVariants = cva(
|
|
19
|
+
"flex shrink-0 items-center justify-center text-sm text-muted-foreground [&>svg]:size-4",
|
|
20
|
+
{
|
|
21
|
+
variants: {
|
|
22
|
+
align: {
|
|
23
|
+
"inline-start": "order-first pl-3 has-[~[data-slot=input-group-control]]:pr-0",
|
|
24
|
+
"inline-end": "order-last pr-3 has-[~[data-slot=input-group-control]]:pl-0",
|
|
25
|
+
"block-start": "order-first w-full justify-start border-b px-3 py-2",
|
|
26
|
+
"block-end": "order-last w-full justify-start border-t px-3 py-2",
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
defaultVariants: {
|
|
30
|
+
align: "inline-start",
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
function InputGroupAddon({
|
|
36
|
+
className,
|
|
37
|
+
align = "inline-start",
|
|
38
|
+
...props
|
|
39
|
+
}: React.ComponentProps<"div"> & VariantProps<typeof inputGroupAddonVariants>) {
|
|
40
|
+
return (
|
|
41
|
+
<div
|
|
42
|
+
role="group"
|
|
43
|
+
data-slot="input-group-addon"
|
|
44
|
+
data-align={align}
|
|
45
|
+
className={cn(inputGroupAddonVariants({ align }), className)}
|
|
46
|
+
{...props}
|
|
47
|
+
/>
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function InputGroupButton({
|
|
52
|
+
className,
|
|
53
|
+
type = "button",
|
|
54
|
+
variant = "outline",
|
|
55
|
+
size = "sm",
|
|
56
|
+
...props
|
|
57
|
+
}: React.ComponentProps<typeof Button>) {
|
|
58
|
+
return (
|
|
59
|
+
<Button
|
|
60
|
+
type={type}
|
|
61
|
+
data-size={size}
|
|
62
|
+
variant={variant}
|
|
63
|
+
size={size}
|
|
64
|
+
className={cn(
|
|
65
|
+
"h-8 shrink-0 rounded-none border-0 bg-transparent shadow-none hover:bg-accent",
|
|
66
|
+
className,
|
|
67
|
+
)}
|
|
68
|
+
{...props}
|
|
69
|
+
/>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function InputGroupText({ className, ...props }: React.ComponentProps<"span">) {
|
|
74
|
+
return (
|
|
75
|
+
<span
|
|
76
|
+
data-slot="input-group-text"
|
|
77
|
+
className={cn("flex items-center gap-2 text-sm text-muted-foreground [&_svg]:size-4", className)}
|
|
78
|
+
{...props}
|
|
79
|
+
/>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const InputGroupInput = React.forwardRef<HTMLInputElement, React.ComponentProps<typeof Input>>(
|
|
84
|
+
({ className, ...props }, ref) => (
|
|
85
|
+
<Input
|
|
86
|
+
ref={ref}
|
|
87
|
+
data-slot="input-group-control"
|
|
88
|
+
className={cn(
|
|
89
|
+
"flex-1 rounded-none border-0 bg-transparent shadow-none focus-visible:ring-0 focus-visible:ring-offset-0",
|
|
90
|
+
className,
|
|
91
|
+
)}
|
|
92
|
+
{...props}
|
|
93
|
+
/>
|
|
94
|
+
),
|
|
95
|
+
);
|
|
96
|
+
InputGroupInput.displayName = "InputGroupInput";
|
|
97
|
+
|
|
98
|
+
export { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText };
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Minus } from "lucide-react";
|
|
2
|
+
import { OTPInput, OTPInputContext } from "input-otp";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { ui } from "@/config/theme";
|
|
5
|
+
import { cn } from "@/lib/utils";
|
|
6
|
+
|
|
7
|
+
const InputOTP = React.forwardRef<
|
|
8
|
+
React.ElementRef<typeof OTPInput>,
|
|
9
|
+
React.ComponentPropsWithoutRef<typeof OTPInput> & {
|
|
10
|
+
containerClassName?: string;
|
|
11
|
+
}
|
|
12
|
+
>(({ className, containerClassName, ...props }, ref) => (
|
|
13
|
+
<OTPInput
|
|
14
|
+
ref={ref}
|
|
15
|
+
containerClassName={cn("flex items-center gap-2 has-[:disabled]:opacity-50", containerClassName)}
|
|
16
|
+
className={cn("disabled:cursor-not-allowed", className)}
|
|
17
|
+
{...props}
|
|
18
|
+
/>
|
|
19
|
+
));
|
|
20
|
+
InputOTP.displayName = "InputOTP";
|
|
21
|
+
|
|
22
|
+
const InputOTPGroup = React.forwardRef<HTMLDivElement, React.ComponentProps<"div">>(
|
|
23
|
+
({ className, ...props }, ref) => <div ref={ref} className={cn("flex items-center", className)} {...props} />,
|
|
24
|
+
);
|
|
25
|
+
InputOTPGroup.displayName = "InputOTPGroup";
|
|
26
|
+
|
|
27
|
+
const InputOTPSlot = React.forwardRef<HTMLDivElement, React.ComponentProps<"div"> & { index: number }>(
|
|
28
|
+
({ index, className, ...props }, ref) => {
|
|
29
|
+
const inputOTPContext = React.useContext(OTPInputContext);
|
|
30
|
+
const { char, hasFakeCaret, isActive } = inputOTPContext.slots[index];
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<div
|
|
34
|
+
ref={ref}
|
|
35
|
+
className={cn(
|
|
36
|
+
ui("inputOtpSlot"),
|
|
37
|
+
isActive && "z-10 ring-1 ring-ring",
|
|
38
|
+
className,
|
|
39
|
+
)}
|
|
40
|
+
{...props}
|
|
41
|
+
>
|
|
42
|
+
{char}
|
|
43
|
+
{hasFakeCaret && (
|
|
44
|
+
<div className="pointer-events-none absolute inset-0 flex items-center justify-center">
|
|
45
|
+
<div className="h-4 w-px animate-caret-blink bg-foreground duration-1000" />
|
|
46
|
+
</div>
|
|
47
|
+
)}
|
|
48
|
+
</div>
|
|
49
|
+
);
|
|
50
|
+
},
|
|
51
|
+
);
|
|
52
|
+
InputOTPSlot.displayName = "InputOTPSlot";
|
|
53
|
+
|
|
54
|
+
const InputOTPSeparator = React.forwardRef<HTMLDivElement, React.ComponentProps<"div">>(
|
|
55
|
+
({ ...props }, ref) => (
|
|
56
|
+
<div ref={ref} role="separator" {...props}>
|
|
57
|
+
<Minus />
|
|
58
|
+
</div>
|
|
59
|
+
),
|
|
60
|
+
);
|
|
61
|
+
InputOTPSeparator.displayName = "InputOTPSeparator";
|
|
62
|
+
|
|
63
|
+
export { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { ui } from "@/config/theme";
|
|
3
|
+
import { cn } from "@/lib/utils";
|
|
4
|
+
|
|
5
|
+
const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<"input">>(
|
|
6
|
+
({ className, type, ...props }, ref) => {
|
|
7
|
+
return <input type={type} className={cn(ui("input"), className)} ref={ref} {...props} />;
|
|
8
|
+
},
|
|
9
|
+
);
|
|
10
|
+
Input.displayName = "Input";
|
|
11
|
+
|
|
12
|
+
export { Input };
|