create-ab-app 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +60 -0
- package/index.js +380 -0
- package/package.json +36 -0
- package/template/.env.example +8 -0
- package/template/.oxlintrc.json +8 -0
- package/template/README.md +196 -0
- package/template/_gitignore +24 -0
- package/template/components.json +25 -0
- package/template/index.html +13 -0
- package/template/package-lock.json +6096 -0
- package/template/package.json +40 -0
- package/template/public/_redirects +1 -0
- package/template/public/favicon.svg +1 -0
- package/template/public/icons.svg +24 -0
- package/template/src/App.tsx +60 -0
- package/template/src/assets/hero.png +0 -0
- package/template/src/assets/vite.svg +1 -0
- package/template/src/components/app-sidebar.tsx +53 -0
- package/template/src/components/credit-balance.tsx +31 -0
- package/template/src/components/docs/doc-primitives.tsx +125 -0
- package/template/src/components/full-page-loader.tsx +14 -0
- package/template/src/components/header-user.tsx +64 -0
- package/template/src/components/layouts/app-layout.tsx +80 -0
- package/template/src/components/missing-env.tsx +24 -0
- package/template/src/components/nav-main.tsx +87 -0
- package/template/src/components/nav-user.tsx +69 -0
- package/template/src/components/theme-toggle.tsx +39 -0
- package/template/src/components/ui/avatar.tsx +106 -0
- package/template/src/components/ui/breadcrumb.tsx +124 -0
- package/template/src/components/ui/button.tsx +57 -0
- package/template/src/components/ui/card.tsx +102 -0
- package/template/src/components/ui/collapsible.tsx +19 -0
- package/template/src/components/ui/dropdown-menu.tsx +267 -0
- package/template/src/components/ui/field.tsx +238 -0
- package/template/src/components/ui/input.tsx +19 -0
- package/template/src/components/ui/label.tsx +19 -0
- package/template/src/components/ui/separator.tsx +22 -0
- package/template/src/components/ui/sheet.tsx +136 -0
- package/template/src/components/ui/sidebar.tsx +721 -0
- package/template/src/components/ui/skeleton.tsx +13 -0
- package/template/src/components/ui/sonner.tsx +47 -0
- package/template/src/components/ui/tooltip.tsx +65 -0
- package/template/src/components/user-menu.tsx +96 -0
- package/template/src/config/env.ts +14 -0
- package/template/src/config/navigation.ts +52 -0
- package/template/src/hooks/use-mobile.ts +19 -0
- package/template/src/hooks/use-user-identity.ts +27 -0
- package/template/src/index.css +134 -0
- package/template/src/lib/http.ts +40 -0
- package/template/src/lib/sso/account-settings.tsx +53 -0
- package/template/src/lib/sso/auth-context.ts +16 -0
- package/template/src/lib/sso/auth-provider.tsx +42 -0
- package/template/src/lib/sso/auth-screens.tsx +152 -0
- package/template/src/lib/sso/config.ts +13 -0
- package/template/src/lib/sso/entitlements.ts +101 -0
- package/template/src/lib/sso/index.ts +33 -0
- package/template/src/lib/sso/protected-route.tsx +72 -0
- package/template/src/lib/sso/queries.ts +55 -0
- package/template/src/lib/sso/require-plan.tsx +19 -0
- package/template/src/lib/sso/token-store.ts +62 -0
- package/template/src/lib/utils.ts +1 -0
- package/template/src/main.tsx +42 -0
- package/template/src/routes/app/example.tsx +125 -0
- package/template/src/routes/auth/forgot-password.tsx +5 -0
- package/template/src/routes/auth/login.tsx +5 -0
- package/template/src/routes/auth/signup.tsx +5 -0
- package/template/src/routes/auth/verify-email.tsx +5 -0
- package/template/src/routes/docs/api.tsx +66 -0
- package/template/src/routes/docs/auth.tsx +145 -0
- package/template/src/routes/docs/billing.tsx +103 -0
- package/template/src/routes/docs/configuration.tsx +113 -0
- package/template/src/routes/docs/overview.tsx +97 -0
- package/template/src/routes/docs/routing.tsx +126 -0
- package/template/src/routes/docs/structure.tsx +150 -0
- package/template/src/routes/docs/theming.tsx +88 -0
- package/template/src/routes/not-found.tsx +12 -0
- package/template/src/styles/aas-theme.css +21 -0
- package/template/src/vite-env.d.ts +12 -0
- package/template/tsconfig.app.json +30 -0
- package/template/tsconfig.json +12 -0
- package/template/tsconfig.node.json +23 -0
- package/template/vercel.json +3 -0
- package/template/vite.config.ts +17 -0
- package/variants/minimal/src/App.tsx +43 -0
- package/variants/minimal/src/components/app-sidebar.tsx +50 -0
- package/variants/minimal/src/config/navigation.ts +19 -0
- package/variants/minimal/src/routes/app/home.tsx +45 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { ChevronsUpDownIcon } from "lucide-react"
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
DropdownMenu,
|
|
5
|
+
DropdownMenuContent,
|
|
6
|
+
DropdownMenuGroup,
|
|
7
|
+
DropdownMenuLabel,
|
|
8
|
+
DropdownMenuTrigger,
|
|
9
|
+
} from "@/components/ui/dropdown-menu"
|
|
10
|
+
import { Skeleton } from "@/components/ui/skeleton"
|
|
11
|
+
import {
|
|
12
|
+
SidebarMenu,
|
|
13
|
+
SidebarMenuButton,
|
|
14
|
+
SidebarMenuItem,
|
|
15
|
+
useSidebar,
|
|
16
|
+
} from "@/components/ui/sidebar"
|
|
17
|
+
import { UserIdentity, UserMenuItems } from "@/components/user-menu"
|
|
18
|
+
import { useUserIdentity } from "@/hooks/use-user-identity"
|
|
19
|
+
|
|
20
|
+
export function NavUser() {
|
|
21
|
+
const { isMobile } = useSidebar()
|
|
22
|
+
const { isPending } = useUserIdentity()
|
|
23
|
+
|
|
24
|
+
if (isPending) {
|
|
25
|
+
return (
|
|
26
|
+
<SidebarMenu>
|
|
27
|
+
<SidebarMenuItem>
|
|
28
|
+
<div className="flex items-center gap-2 p-2">
|
|
29
|
+
<Skeleton className="size-8 rounded-full" />
|
|
30
|
+
<div className="grid flex-1 gap-1 group-data-[collapsible=icon]:hidden">
|
|
31
|
+
<Skeleton className="h-3 w-24" />
|
|
32
|
+
<Skeleton className="h-3 w-32" />
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
</SidebarMenuItem>
|
|
36
|
+
</SidebarMenu>
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<SidebarMenu>
|
|
42
|
+
<SidebarMenuItem>
|
|
43
|
+
<DropdownMenu>
|
|
44
|
+
<DropdownMenuTrigger
|
|
45
|
+
render={<SidebarMenuButton size="lg" className="aria-expanded:bg-muted" />}
|
|
46
|
+
>
|
|
47
|
+
<UserIdentity />
|
|
48
|
+
<ChevronsUpDownIcon className="ml-auto size-4" />
|
|
49
|
+
</DropdownMenuTrigger>
|
|
50
|
+
<DropdownMenuContent
|
|
51
|
+
className="w-56"
|
|
52
|
+
side={isMobile ? "bottom" : "right"}
|
|
53
|
+
align="end"
|
|
54
|
+
sideOffset={4}
|
|
55
|
+
>
|
|
56
|
+
<DropdownMenuGroup>
|
|
57
|
+
<DropdownMenuLabel className="p-0 font-normal">
|
|
58
|
+
<div className="flex items-center gap-2 px-1 py-1.5 text-left text-sm">
|
|
59
|
+
<UserIdentity />
|
|
60
|
+
</div>
|
|
61
|
+
</DropdownMenuLabel>
|
|
62
|
+
</DropdownMenuGroup>
|
|
63
|
+
<UserMenuItems />
|
|
64
|
+
</DropdownMenuContent>
|
|
65
|
+
</DropdownMenu>
|
|
66
|
+
</SidebarMenuItem>
|
|
67
|
+
</SidebarMenu>
|
|
68
|
+
)
|
|
69
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { useTheme } from "next-themes"
|
|
2
|
+
import { CheckIcon, MoonIcon, SunIcon } from "lucide-react"
|
|
3
|
+
|
|
4
|
+
import { Button } from "@/components/ui/button"
|
|
5
|
+
import {
|
|
6
|
+
DropdownMenu,
|
|
7
|
+
DropdownMenuContent,
|
|
8
|
+
DropdownMenuItem,
|
|
9
|
+
DropdownMenuTrigger,
|
|
10
|
+
} from "@/components/ui/dropdown-menu"
|
|
11
|
+
|
|
12
|
+
const THEMES = [
|
|
13
|
+
{ value: "light", label: "Light" },
|
|
14
|
+
{ value: "dark", label: "Dark" },
|
|
15
|
+
{ value: "system", label: "System" },
|
|
16
|
+
] as const
|
|
17
|
+
|
|
18
|
+
export function ThemeToggle() {
|
|
19
|
+
const { theme, setTheme } = useTheme()
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<DropdownMenu>
|
|
23
|
+
<DropdownMenuTrigger
|
|
24
|
+
render={<Button variant="ghost" size="icon" aria-label="Toggle theme" />}
|
|
25
|
+
>
|
|
26
|
+
<SunIcon className="size-4 dark:hidden" />
|
|
27
|
+
<MoonIcon className="hidden size-4 dark:block" />
|
|
28
|
+
</DropdownMenuTrigger>
|
|
29
|
+
<DropdownMenuContent align="end" sideOffset={8}>
|
|
30
|
+
{THEMES.map((option) => (
|
|
31
|
+
<DropdownMenuItem key={option.value} onClick={() => setTheme(option.value)}>
|
|
32
|
+
{option.label}
|
|
33
|
+
{theme === option.value && <CheckIcon className="ml-auto size-4" />}
|
|
34
|
+
</DropdownMenuItem>
|
|
35
|
+
))}
|
|
36
|
+
</DropdownMenuContent>
|
|
37
|
+
</DropdownMenu>
|
|
38
|
+
)
|
|
39
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { Avatar as AvatarPrimitive } from "@base-ui/react/avatar"
|
|
3
|
+
import { cn } from "cn"
|
|
4
|
+
|
|
5
|
+
function Avatar({
|
|
6
|
+
className,
|
|
7
|
+
size = "default",
|
|
8
|
+
...props
|
|
9
|
+
}: AvatarPrimitive.Root.Props & {
|
|
10
|
+
size?: "default" | "sm" | "lg"
|
|
11
|
+
}) {
|
|
12
|
+
return (
|
|
13
|
+
<AvatarPrimitive.Root
|
|
14
|
+
data-slot="avatar"
|
|
15
|
+
data-size={size}
|
|
16
|
+
className={cn(
|
|
17
|
+
"group/avatar relative flex size-8 shrink-0 rounded-full select-none after:absolute after:inset-0 after:rounded-full after:border after:border-border after:mix-blend-darken data-[size=lg]:size-10 data-[size=sm]:size-6 dark:after:mix-blend-lighten",
|
|
18
|
+
className
|
|
19
|
+
)}
|
|
20
|
+
{...props}
|
|
21
|
+
/>
|
|
22
|
+
)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function AvatarImage({ className, ...props }: AvatarPrimitive.Image.Props) {
|
|
26
|
+
return (
|
|
27
|
+
<AvatarPrimitive.Image
|
|
28
|
+
data-slot="avatar-image"
|
|
29
|
+
className={cn(
|
|
30
|
+
"aspect-square size-full rounded-full object-cover",
|
|
31
|
+
className
|
|
32
|
+
)}
|
|
33
|
+
{...props}
|
|
34
|
+
/>
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function AvatarFallback({
|
|
39
|
+
className,
|
|
40
|
+
...props
|
|
41
|
+
}: AvatarPrimitive.Fallback.Props) {
|
|
42
|
+
return (
|
|
43
|
+
<AvatarPrimitive.Fallback
|
|
44
|
+
data-slot="avatar-fallback"
|
|
45
|
+
className={cn(
|
|
46
|
+
"flex size-full items-center justify-center rounded-full bg-muted text-sm text-muted-foreground group-data-[size=sm]/avatar:text-xs",
|
|
47
|
+
className
|
|
48
|
+
)}
|
|
49
|
+
{...props}
|
|
50
|
+
/>
|
|
51
|
+
)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function AvatarBadge({ className, ...props }: React.ComponentProps<"span">) {
|
|
55
|
+
return (
|
|
56
|
+
<span
|
|
57
|
+
data-slot="avatar-badge"
|
|
58
|
+
className={cn(
|
|
59
|
+
"absolute right-0 bottom-0 z-10 inline-flex items-center justify-center rounded-full bg-primary text-primary-foreground bg-blend-color ring-2 ring-background select-none",
|
|
60
|
+
"group-data-[size=sm]/avatar:size-2 group-data-[size=sm]/avatar:[&>svg]:hidden",
|
|
61
|
+
"group-data-[size=default]/avatar:size-2.5 group-data-[size=default]/avatar:[&>svg]:size-2",
|
|
62
|
+
"group-data-[size=lg]/avatar:size-3 group-data-[size=lg]/avatar:[&>svg]:size-2",
|
|
63
|
+
className
|
|
64
|
+
)}
|
|
65
|
+
{...props}
|
|
66
|
+
/>
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function AvatarGroup({ className, ...props }: React.ComponentProps<"div">) {
|
|
71
|
+
return (
|
|
72
|
+
<div
|
|
73
|
+
data-slot="avatar-group"
|
|
74
|
+
className={cn(
|
|
75
|
+
"group/avatar-group flex -space-x-2 *:data-[slot=avatar]:ring-2 *:data-[slot=avatar]:ring-background",
|
|
76
|
+
className
|
|
77
|
+
)}
|
|
78
|
+
{...props}
|
|
79
|
+
/>
|
|
80
|
+
)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function AvatarGroupCount({
|
|
84
|
+
className,
|
|
85
|
+
...props
|
|
86
|
+
}: React.ComponentProps<"div">) {
|
|
87
|
+
return (
|
|
88
|
+
<div
|
|
89
|
+
data-slot="avatar-group-count"
|
|
90
|
+
className={cn(
|
|
91
|
+
"relative flex size-8 shrink-0 items-center justify-center rounded-full bg-muted text-sm text-muted-foreground ring-2 ring-background group-has-data-[size=lg]/avatar-group:size-10 group-has-data-[size=sm]/avatar-group:size-6 [&>svg]:size-4 group-has-data-[size=lg]/avatar-group:[&>svg]:size-5 group-has-data-[size=sm]/avatar-group:[&>svg]:size-3",
|
|
92
|
+
className
|
|
93
|
+
)}
|
|
94
|
+
{...props}
|
|
95
|
+
/>
|
|
96
|
+
)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export {
|
|
100
|
+
Avatar,
|
|
101
|
+
AvatarImage,
|
|
102
|
+
AvatarFallback,
|
|
103
|
+
AvatarGroup,
|
|
104
|
+
AvatarGroupCount,
|
|
105
|
+
AvatarBadge,
|
|
106
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { mergeProps } from "@base-ui/react/merge-props"
|
|
3
|
+
import { useRender } from "@base-ui/react/use-render"
|
|
4
|
+
import { cn } from "cn"
|
|
5
|
+
import { ChevronRightIcon, MoreHorizontalIcon } from "lucide-react"
|
|
6
|
+
|
|
7
|
+
function Breadcrumb({ className, ...props }: React.ComponentProps<"nav">) {
|
|
8
|
+
return (
|
|
9
|
+
<nav
|
|
10
|
+
aria-label="breadcrumb"
|
|
11
|
+
data-slot="breadcrumb"
|
|
12
|
+
className={cn(className)}
|
|
13
|
+
{...props}
|
|
14
|
+
/>
|
|
15
|
+
)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function BreadcrumbList({ className, ...props }: React.ComponentProps<"ol">) {
|
|
19
|
+
return (
|
|
20
|
+
<ol
|
|
21
|
+
data-slot="breadcrumb-list"
|
|
22
|
+
className={cn(
|
|
23
|
+
"flex flex-wrap items-center gap-1.5 text-sm wrap-break-word text-muted-foreground",
|
|
24
|
+
className
|
|
25
|
+
)}
|
|
26
|
+
{...props}
|
|
27
|
+
/>
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function BreadcrumbItem({ className, ...props }: React.ComponentProps<"li">) {
|
|
32
|
+
return (
|
|
33
|
+
<li
|
|
34
|
+
data-slot="breadcrumb-item"
|
|
35
|
+
className={cn("inline-flex items-center gap-1", className)}
|
|
36
|
+
{...props}
|
|
37
|
+
/>
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function BreadcrumbLink({
|
|
42
|
+
className,
|
|
43
|
+
render,
|
|
44
|
+
...props
|
|
45
|
+
}: useRender.ComponentProps<"a">) {
|
|
46
|
+
return useRender({
|
|
47
|
+
defaultTagName: "a",
|
|
48
|
+
props: mergeProps<"a">(
|
|
49
|
+
{
|
|
50
|
+
className: cn("transition-colors hover:text-foreground", className),
|
|
51
|
+
},
|
|
52
|
+
props
|
|
53
|
+
),
|
|
54
|
+
render,
|
|
55
|
+
state: {
|
|
56
|
+
slot: "breadcrumb-link",
|
|
57
|
+
},
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function BreadcrumbPage({ className, ...props }: React.ComponentProps<"span">) {
|
|
62
|
+
return (
|
|
63
|
+
<span
|
|
64
|
+
data-slot="breadcrumb-page"
|
|
65
|
+
role="link"
|
|
66
|
+
aria-disabled="true"
|
|
67
|
+
aria-current="page"
|
|
68
|
+
className={cn("font-normal text-foreground", className)}
|
|
69
|
+
{...props}
|
|
70
|
+
/>
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function BreadcrumbSeparator({
|
|
75
|
+
children,
|
|
76
|
+
className,
|
|
77
|
+
...props
|
|
78
|
+
}: React.ComponentProps<"li">) {
|
|
79
|
+
return (
|
|
80
|
+
<li
|
|
81
|
+
data-slot="breadcrumb-separator"
|
|
82
|
+
role="presentation"
|
|
83
|
+
aria-hidden="true"
|
|
84
|
+
className={cn("[&>svg]:size-3.5", className)}
|
|
85
|
+
{...props}
|
|
86
|
+
>
|
|
87
|
+
{children ?? (
|
|
88
|
+
<ChevronRightIcon />
|
|
89
|
+
)}
|
|
90
|
+
</li>
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function BreadcrumbEllipsis({
|
|
95
|
+
className,
|
|
96
|
+
...props
|
|
97
|
+
}: React.ComponentProps<"span">) {
|
|
98
|
+
return (
|
|
99
|
+
<span
|
|
100
|
+
data-slot="breadcrumb-ellipsis"
|
|
101
|
+
role="presentation"
|
|
102
|
+
aria-hidden="true"
|
|
103
|
+
className={cn(
|
|
104
|
+
"flex size-5 items-center justify-center [&>svg]:size-4",
|
|
105
|
+
className
|
|
106
|
+
)}
|
|
107
|
+
{...props}
|
|
108
|
+
>
|
|
109
|
+
<MoreHorizontalIcon
|
|
110
|
+
/>
|
|
111
|
+
<span className="sr-only">More</span>
|
|
112
|
+
</span>
|
|
113
|
+
)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export {
|
|
117
|
+
Breadcrumb,
|
|
118
|
+
BreadcrumbList,
|
|
119
|
+
BreadcrumbItem,
|
|
120
|
+
BreadcrumbLink,
|
|
121
|
+
BreadcrumbPage,
|
|
122
|
+
BreadcrumbSeparator,
|
|
123
|
+
BreadcrumbEllipsis,
|
|
124
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { Button as ButtonPrimitive } from "@base-ui/react/button"
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
3
|
+
import { cn } from "cn"
|
|
4
|
+
|
|
5
|
+
const buttonVariants = cva(
|
|
6
|
+
"group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
7
|
+
{
|
|
8
|
+
variants: {
|
|
9
|
+
variant: {
|
|
10
|
+
default: "bg-primary text-primary-foreground hover:bg-primary/80",
|
|
11
|
+
outline:
|
|
12
|
+
"border-border bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
|
|
13
|
+
secondary:
|
|
14
|
+
"bg-secondary text-secondary-foreground hover:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)] aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
|
|
15
|
+
ghost:
|
|
16
|
+
"hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50",
|
|
17
|
+
destructive:
|
|
18
|
+
"bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40",
|
|
19
|
+
link: "text-primary underline-offset-4 hover:underline",
|
|
20
|
+
},
|
|
21
|
+
size: {
|
|
22
|
+
default:
|
|
23
|
+
"h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
|
|
24
|
+
xs: "h-6 gap-1 rounded-[min(var(--radius-md),10px)] px-2 text-xs in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3",
|
|
25
|
+
sm: "h-7 gap-1 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5",
|
|
26
|
+
lg: "h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
|
|
27
|
+
icon: "size-8",
|
|
28
|
+
"icon-xs":
|
|
29
|
+
"size-6 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-lg [&_svg:not([class*='size-'])]:size-3",
|
|
30
|
+
"icon-sm":
|
|
31
|
+
"size-7 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg",
|
|
32
|
+
"icon-lg": "size-9",
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
defaultVariants: {
|
|
36
|
+
variant: "default",
|
|
37
|
+
size: "default",
|
|
38
|
+
},
|
|
39
|
+
}
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
function Button({
|
|
43
|
+
className,
|
|
44
|
+
variant = "default",
|
|
45
|
+
size = "default",
|
|
46
|
+
...props
|
|
47
|
+
}: ButtonPrimitive.Props & VariantProps<typeof buttonVariants>) {
|
|
48
|
+
return (
|
|
49
|
+
<ButtonPrimitive
|
|
50
|
+
data-slot="button"
|
|
51
|
+
className={cn(buttonVariants({ variant, size, className }))}
|
|
52
|
+
{...props}
|
|
53
|
+
/>
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export { Button, buttonVariants }
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { cn } from "cn"
|
|
3
|
+
|
|
4
|
+
function Card({
|
|
5
|
+
className,
|
|
6
|
+
size = "default",
|
|
7
|
+
...props
|
|
8
|
+
}: React.ComponentProps<"div"> & { size?: "default" | "sm" }) {
|
|
9
|
+
return (
|
|
10
|
+
<div
|
|
11
|
+
data-slot="card"
|
|
12
|
+
data-size={size}
|
|
13
|
+
className={cn(
|
|
14
|
+
"group/card flex flex-col gap-(--card-spacing) overflow-hidden rounded-xl bg-card py-(--card-spacing) text-sm text-card-foreground ring-1 ring-foreground/10 [--card-spacing:--spacing(4)] has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:[--card-spacing:--spacing(3)] data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl",
|
|
15
|
+
className
|
|
16
|
+
)}
|
|
17
|
+
{...props}
|
|
18
|
+
/>
|
|
19
|
+
)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
|
|
23
|
+
return (
|
|
24
|
+
<div
|
|
25
|
+
data-slot="card-header"
|
|
26
|
+
className={cn(
|
|
27
|
+
"group/card-header @container/card-header grid auto-rows-min items-start gap-1 rounded-t-xl px-(--card-spacing) has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto] [.border-b]:pb-(--card-spacing)",
|
|
28
|
+
className
|
|
29
|
+
)}
|
|
30
|
+
{...props}
|
|
31
|
+
/>
|
|
32
|
+
)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
|
|
36
|
+
return (
|
|
37
|
+
<div
|
|
38
|
+
data-slot="card-title"
|
|
39
|
+
className={cn(
|
|
40
|
+
"font-heading text-base leading-snug font-medium group-data-[size=sm]/card:text-sm",
|
|
41
|
+
className
|
|
42
|
+
)}
|
|
43
|
+
{...props}
|
|
44
|
+
/>
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
|
|
49
|
+
return (
|
|
50
|
+
<div
|
|
51
|
+
data-slot="card-description"
|
|
52
|
+
className={cn("text-sm text-muted-foreground", className)}
|
|
53
|
+
{...props}
|
|
54
|
+
/>
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function CardAction({ className, ...props }: React.ComponentProps<"div">) {
|
|
59
|
+
return (
|
|
60
|
+
<div
|
|
61
|
+
data-slot="card-action"
|
|
62
|
+
className={cn(
|
|
63
|
+
"col-start-2 row-span-2 row-start-1 self-start justify-self-end",
|
|
64
|
+
className
|
|
65
|
+
)}
|
|
66
|
+
{...props}
|
|
67
|
+
/>
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function CardContent({ className, ...props }: React.ComponentProps<"div">) {
|
|
72
|
+
return (
|
|
73
|
+
<div
|
|
74
|
+
data-slot="card-content"
|
|
75
|
+
className={cn("px-(--card-spacing)", className)}
|
|
76
|
+
{...props}
|
|
77
|
+
/>
|
|
78
|
+
)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
|
|
82
|
+
return (
|
|
83
|
+
<div
|
|
84
|
+
data-slot="card-footer"
|
|
85
|
+
className={cn(
|
|
86
|
+
"flex items-center rounded-b-xl border-t bg-muted/50 p-(--card-spacing)",
|
|
87
|
+
className
|
|
88
|
+
)}
|
|
89
|
+
{...props}
|
|
90
|
+
/>
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export {
|
|
95
|
+
Card,
|
|
96
|
+
CardHeader,
|
|
97
|
+
CardFooter,
|
|
98
|
+
CardTitle,
|
|
99
|
+
CardAction,
|
|
100
|
+
CardDescription,
|
|
101
|
+
CardContent,
|
|
102
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Collapsible as CollapsiblePrimitive } from "@base-ui/react/collapsible"
|
|
2
|
+
|
|
3
|
+
function Collapsible({ ...props }: CollapsiblePrimitive.Root.Props) {
|
|
4
|
+
return <CollapsiblePrimitive.Root data-slot="collapsible" {...props} />
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function CollapsibleTrigger({ ...props }: CollapsiblePrimitive.Trigger.Props) {
|
|
8
|
+
return (
|
|
9
|
+
<CollapsiblePrimitive.Trigger data-slot="collapsible-trigger" {...props} />
|
|
10
|
+
)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function CollapsibleContent({ ...props }: CollapsiblePrimitive.Panel.Props) {
|
|
14
|
+
return (
|
|
15
|
+
<CollapsiblePrimitive.Panel data-slot="collapsible-content" {...props} />
|
|
16
|
+
)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export { Collapsible, CollapsibleTrigger, CollapsibleContent }
|