@notionx/create-notionx-app 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/README.md +139 -0
- package/dist/answers.js +332 -0
- package/dist/answers.js.map +1 -0
- package/dist/cli-notionx.js +388 -0
- package/dist/cli-notionx.js.map +1 -0
- package/dist/cli-notionx.test.js +277 -0
- package/dist/cli-notionx.test.js.map +1 -0
- package/dist/diff.js +40 -0
- package/dist/diff.js.map +1 -0
- package/dist/diff.test.js +90 -0
- package/dist/diff.test.js.map +1 -0
- package/dist/index.js +99 -0
- package/dist/index.js.map +1 -0
- package/dist/locale-add/apply.js +39 -0
- package/dist/locale-add/apply.js.map +1 -0
- package/dist/locale-add/format.js +38 -0
- package/dist/locale-add/format.js.map +1 -0
- package/dist/locale-add/list.js +44 -0
- package/dist/locale-add/list.js.map +1 -0
- package/dist/locale-add/list.test.js +45 -0
- package/dist/locale-add/list.test.js.map +1 -0
- package/dist/locale-add/plan.js +128 -0
- package/dist/locale-add/plan.js.map +1 -0
- package/dist/locale-add/validate.js +46 -0
- package/dist/locale-add/validate.js.map +1 -0
- package/dist/metadata.js +41 -0
- package/dist/metadata.js.map +1 -0
- package/dist/notion-translation-sources/apply.js +61 -0
- package/dist/notion-translation-sources/apply.js.map +1 -0
- package/dist/notion-translation-sources/index.js +3 -0
- package/dist/notion-translation-sources/index.js.map +1 -0
- package/dist/notion-translation-sources/plan.js +33 -0
- package/dist/notion-translation-sources/plan.js.map +1 -0
- package/dist/notionx-source.js +142 -0
- package/dist/notionx-source.js.map +1 -0
- package/dist/notionx-source.test.js +144 -0
- package/dist/notionx-source.test.js.map +1 -0
- package/dist/password.js +18 -0
- package/dist/password.js.map +1 -0
- package/dist/presets.js +83 -0
- package/dist/presets.js.map +1 -0
- package/dist/presets.test.js +50 -0
- package/dist/presets.test.js.map +1 -0
- package/dist/prompt.js +218 -0
- package/dist/prompt.js.map +1 -0
- package/dist/provision/cloudflare.js +236 -0
- package/dist/provision/cloudflare.js.map +1 -0
- package/dist/provision/dependencies.js +219 -0
- package/dist/provision/dependencies.js.map +1 -0
- package/dist/provision/index.js +681 -0
- package/dist/provision/index.js.map +1 -0
- package/dist/provision/index.test.js +54 -0
- package/dist/provision/index.test.js.map +1 -0
- package/dist/provision/inspect.js +109 -0
- package/dist/provision/inspect.js.map +1 -0
- package/dist/provision/inspect.test.js +75 -0
- package/dist/provision/inspect.test.js.map +1 -0
- package/dist/provision/notion.js +1981 -0
- package/dist/provision/notion.js.map +1 -0
- package/dist/provision/notion.test.js +542 -0
- package/dist/provision/notion.test.js.map +1 -0
- package/dist/provision/ntn-credentials.js +198 -0
- package/dist/provision/ntn-credentials.js.map +1 -0
- package/dist/provision/options.js +15 -0
- package/dist/provision/options.js.map +1 -0
- package/dist/provision/password-hash.js +78 -0
- package/dist/provision/password-hash.js.map +1 -0
- package/dist/provision/prompts.js +115 -0
- package/dist/provision/prompts.js.map +1 -0
- package/dist/provision/repair.js +48 -0
- package/dist/provision/repair.js.map +1 -0
- package/dist/provision/repair.test.js +141 -0
- package/dist/provision/repair.test.js.map +1 -0
- package/dist/provision/shell.js +84 -0
- package/dist/provision/shell.js.map +1 -0
- package/dist/provision/wire.js +78 -0
- package/dist/provision/wire.js.map +1 -0
- package/dist/registry/doctor.js +181 -0
- package/dist/registry/doctor.js.map +1 -0
- package/dist/registry/doctor.test.js +180 -0
- package/dist/registry/doctor.test.js.map +1 -0
- package/dist/registry/install.js +217 -0
- package/dist/registry/install.js.map +1 -0
- package/dist/registry/install.test.js +168 -0
- package/dist/registry/install.test.js.map +1 -0
- package/dist/registry/load-registry.js +24 -0
- package/dist/registry/load-registry.js.map +1 -0
- package/dist/registry/load-registry.test.js +59 -0
- package/dist/registry/load-registry.test.js.map +1 -0
- package/dist/registry/migration-planner.js +204 -0
- package/dist/registry/migration-planner.js.map +1 -0
- package/dist/registry/migration-planner.test.js +340 -0
- package/dist/registry/migration-planner.test.js.map +1 -0
- package/dist/registry/migrations-store.js +125 -0
- package/dist/registry/migrations-store.js.map +1 -0
- package/dist/registry/migrations-store.test.js +163 -0
- package/dist/registry/migrations-store.test.js.map +1 -0
- package/dist/registry/migrations-types.js +25 -0
- package/dist/registry/migrations-types.js.map +1 -0
- package/dist/registry/project-meta.js +84 -0
- package/dist/registry/project-meta.js.map +1 -0
- package/dist/registry/registry-items.js +354 -0
- package/dist/registry/registry-items.js.map +1 -0
- package/dist/registry/registry-items.test.js +99 -0
- package/dist/registry/registry-items.test.js.map +1 -0
- package/dist/registry/registry-store.js +232 -0
- package/dist/registry/registry-store.js.map +1 -0
- package/dist/registry/registry-store.test.js +136 -0
- package/dist/registry/registry-store.test.js.map +1 -0
- package/dist/registry/registry-types.js +18 -0
- package/dist/registry/registry-types.js.map +1 -0
- package/dist/registry/registry-types.test.js +146 -0
- package/dist/registry/registry-types.test.js.map +1 -0
- package/dist/registry/render-content-source-files.js +158 -0
- package/dist/registry/render-content-source-files.js.map +1 -0
- package/dist/registry/render-multi-source.js +296 -0
- package/dist/registry/render-multi-source.js.map +1 -0
- package/dist/registry/render-multi-source.test.js +110 -0
- package/dist/registry/render-multi-source.test.js.map +1 -0
- package/dist/registry/text-utils.js +42 -0
- package/dist/registry/text-utils.js.map +1 -0
- package/dist/registry/uninstall.js +250 -0
- package/dist/registry/uninstall.js.map +1 -0
- package/dist/registry/uninstall.test.js +264 -0
- package/dist/registry/uninstall.test.js.map +1 -0
- package/dist/registry/update.js +280 -0
- package/dist/registry/update.js.map +1 -0
- package/dist/registry/update.test.js +229 -0
- package/dist/registry/update.test.js.map +1 -0
- package/dist/render.js +549 -0
- package/dist/render.js.map +1 -0
- package/dist/render.test.js +414 -0
- package/dist/render.test.js.map +1 -0
- package/dist/templates/.dev.vars.example.tmpl +32 -0
- package/dist/templates/.gitignore.tmpl +58 -0
- package/dist/templates/README.md.tmpl +417 -0
- package/dist/templates/app/[slug]/page.tsx.tmpl +55 -0
- package/dist/templates/app/admin/account/page.tsx.tmpl +18 -0
- package/dist/templates/app/admin/content-models/page.tsx.tmpl +6 -0
- package/dist/templates/app/admin/layout.tsx.tmpl +90 -0
- package/dist/templates/app/admin/loading.tsx.tmpl +6 -0
- package/dist/templates/app/admin/page.tsx.tmpl +17 -0
- package/dist/templates/app/api/auth/google/callback/route.ts.tmpl +3 -0
- package/dist/templates/app/api/auth/google/route.ts.tmpl +3 -0
- package/dist/templates/app/api/auth/verify-email/route.ts.tmpl +3 -0
- package/dist/templates/app/api/auth/viewer/route.ts.tmpl +3 -0
- package/dist/templates/app/api/health/route.ts.tmpl +3 -0
- package/dist/templates/app/api/{{contentSourceId}}/[slug]/route.ts.tmpl +27 -0
- package/dist/templates/app/api/{{contentSourceId}}/route.ts.tmpl +18 -0
- package/dist/templates/app/globals.css.tmpl +109 -0
- package/dist/templates/app/layout.tsx.tmpl +56 -0
- package/dist/templates/app/login/page.tsx.tmpl +154 -0
- package/dist/templates/app/page.fallback.tsx.tmpl +31 -0
- package/dist/templates/app/page.tsx.tmpl +42 -0
- package/dist/templates/app/register/page.tsx.tmpl +138 -0
- package/dist/templates/app/{{contentSourceListPath}}/[slug]/page.tsx.tmpl +113 -0
- package/dist/templates/app/{{contentSourceListPath}}/page.tsx.tmpl +74 -0
- package/dist/templates/components/content/post-card.tsx.tmpl +80 -0
- package/dist/templates/components/notion-blocks.tsx.tmpl +668 -0
- package/dist/templates/components/page-blocks/feature-grid-block.tsx.tmpl +68 -0
- package/dist/templates/components/page-blocks/hero-block.tsx.tmpl +73 -0
- package/dist/templates/components/page-blocks/latest-posts-block.tsx.tmpl +59 -0
- package/dist/templates/components/page-blocks/story-block.tsx.tmpl +70 -0
- package/dist/templates/components/page-blocks.fallback.tsx.tmpl +17 -0
- package/dist/templates/components/page-blocks.tsx.tmpl +32 -0
- package/dist/templates/components/search/search-dialog.tsx.tmpl +171 -0
- package/dist/templates/components/site/locale-switcher.tsx.tmpl +65 -0
- package/dist/templates/components/site/site-footer.tsx.tmpl +106 -0
- package/dist/templates/components/site/site-header.tsx.tmpl +80 -0
- package/dist/templates/components/site/site-shell.tsx.tmpl +20 -0
- package/dist/templates/components/site/theme-bootstrap.tsx.tmpl +51 -0
- package/dist/templates/components/theme-provider.tsx.tmpl +14 -0
- package/dist/templates/components/theme-toggle.tsx.tmpl +38 -0
- package/dist/templates/components/ui/accordion.tsx.tmpl +56 -0
- package/dist/templates/components/ui/alert.tsx.tmpl +59 -0
- package/dist/templates/components/ui/aspect-ratio.tsx.tmpl +8 -0
- package/dist/templates/components/ui/avatar.tsx.tmpl +44 -0
- package/dist/templates/components/ui/badge.tsx.tmpl +33 -0
- package/dist/templates/components/ui/button.tsx.tmpl +56 -0
- package/dist/templates/components/ui/card.tsx.tmpl +61 -0
- package/dist/templates/components/ui/checkbox.tsx.tmpl +28 -0
- package/dist/templates/components/ui/dialog.tsx.tmpl +104 -0
- package/dist/templates/components/ui/dropdown-menu.tsx.tmpl +183 -0
- package/dist/templates/components/ui/input.tsx.tmpl +21 -0
- package/dist/templates/components/ui/label.tsx.tmpl +25 -0
- package/dist/templates/components/ui/popover.tsx.tmpl +30 -0
- package/dist/templates/components/ui/radio-group.tsx.tmpl +44 -0
- package/dist/templates/components/ui/select.tsx.tmpl +150 -0
- package/dist/templates/components/ui/separator.tsx.tmpl +30 -0
- package/dist/templates/components/ui/sheet.tsx.tmpl +125 -0
- package/dist/templates/components/ui/skeleton.tsx.tmpl +15 -0
- package/dist/templates/components/ui/sonner.tsx.tmpl +30 -0
- package/dist/templates/components/ui/switch.tsx.tmpl +29 -0
- package/dist/templates/components/ui/table.tsx.tmpl +107 -0
- package/dist/templates/components/ui/tabs.tsx.tmpl +55 -0
- package/dist/templates/components/ui/textarea.tsx.tmpl +24 -0
- package/dist/templates/components/ui/tooltip.tsx.tmpl +30 -0
- package/dist/templates/components.json.tmpl +21 -0
- package/dist/templates/env.d.ts.tmpl +32 -0
- package/dist/templates/lib/admin/actions.ts.tmpl +43 -0
- package/dist/templates/lib/admin/context.tsx.tmpl +209 -0
- package/dist/templates/lib/admin/nav.ts.tmpl +23 -0
- package/dist/templates/lib/auth.config.fallback.ts.tmpl +10 -0
- package/dist/templates/lib/auth.config.ts.tmpl +45 -0
- package/dist/templates/lib/blocks/translations.ts.tmpl +44 -0
- package/dist/templates/lib/blog/translations.ts.tmpl +52 -0
- package/dist/templates/lib/content/models.ts.tmpl +53 -0
- package/dist/templates/lib/i18n/config.ts.tmpl +18 -0
- package/dist/templates/lib/i18n/index.ts.tmpl +1 -0
- package/dist/templates/lib/locale-contract/built-in.ts.tmpl +19 -0
- package/dist/templates/lib/locale-contract/index.ts.tmpl +3 -0
- package/dist/templates/lib/locale-contract/paths.ts.tmpl +29 -0
- package/dist/templates/lib/pages/model.ts.tmpl +16 -0
- package/dist/templates/lib/pages/source.ts.tmpl +566 -0
- package/dist/templates/lib/pages/translations.ts.tmpl +34 -0
- package/dist/templates/lib/search/config.fallback.ts.tmpl +11 -0
- package/dist/templates/lib/search/config.ts.tmpl +25 -0
- package/dist/templates/lib/site/config.ts.tmpl +120 -0
- package/dist/templates/lib/site/request-env.ts.tmpl +71 -0
- package/dist/templates/lib/site/settings.fallback.ts.tmpl +21 -0
- package/dist/templates/lib/site/settings.ts.tmpl +320 -0
- package/dist/templates/lib/site/translations.ts.tmpl +30 -0
- package/dist/templates/lib/utils.ts.tmpl +9 -0
- package/dist/templates/migrations/0001_init.sql.tmpl +57 -0
- package/dist/templates/migrations/0002_admin_seed.sql.tmpl +30 -0
- package/dist/templates/migrations/0003_search_index.sql.tmpl +29 -0
- package/dist/templates/next.config.ts.tmpl +18 -0
- package/dist/templates/package.json.tmpl +40 -0
- package/dist/templates/shims/cloudflare-workers-empty.mjs +4 -0
- package/dist/templates/shims/next-headers-empty.mjs +4 -0
- package/dist/templates/tests/smoke.test.ts.tmpl +83 -0
- package/dist/templates/tsconfig.json.tmpl +31 -0
- package/dist/templates/vite.config.ts.tmpl +53 -0
- package/dist/templates/vitest.config.ts.tmpl +13 -0
- package/dist/templates/worker/index.ts.tmpl +52 -0
- package/dist/templates/wrangler.jsonc.tmpl +44 -0
- package/dist/ui-presets.js +60 -0
- package/dist/ui-presets.js.map +1 -0
- package/package.json +60 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import Link from "next/link";
|
|
2
|
+
import { LogIn, ChevronDown } from "lucide-react";
|
|
3
|
+
import { Button } from "@/components/ui/button";
|
|
4
|
+
import {
|
|
5
|
+
DropdownMenu,
|
|
6
|
+
DropdownMenuContent,
|
|
7
|
+
DropdownMenuItem,
|
|
8
|
+
DropdownMenuTrigger,
|
|
9
|
+
} from "@/components/ui/dropdown-menu";
|
|
10
|
+
import { ThemeToggle } from "@/components/theme-toggle";
|
|
11
|
+
import { LocaleSwitcher } from "./locale-switcher";
|
|
12
|
+
import { i18n } from "@/lib/i18n";
|
|
13
|
+
import { getSiteNavigation } from "@/lib/pages/source";
|
|
14
|
+
import { getSiteSettings } from "@/lib/site/settings";
|
|
15
|
+
|
|
16
|
+
type NavItem = {
|
|
17
|
+
label: string;
|
|
18
|
+
href: string;
|
|
19
|
+
children?: NavItem[];
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export async function SiteHeader() {
|
|
23
|
+
const [pageNav, settings] = await Promise.all([
|
|
24
|
+
getSiteNavigation() as Promise<NavItem[]>,
|
|
25
|
+
getSiteSettings(),
|
|
26
|
+
]);
|
|
27
|
+
const nav =
|
|
28
|
+
pageNav.length > 0 ? pageNav : (settings.navigation.main as unknown as NavItem[]);
|
|
29
|
+
const cta = settings.navigation.cta;
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<header className="border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/80">
|
|
33
|
+
<div className="container mx-auto flex min-h-14 items-center justify-between gap-4 px-4">
|
|
34
|
+
<Link href="/" className="font-semibold tracking-tight">
|
|
35
|
+
{settings.name}
|
|
36
|
+
</Link>
|
|
37
|
+
<nav className="flex items-center gap-1">
|
|
38
|
+
{nav.map((item) =>
|
|
39
|
+
item.children?.length ? (
|
|
40
|
+
<DropdownMenu key={item.href}>
|
|
41
|
+
<DropdownMenuTrigger asChild>
|
|
42
|
+
<Button variant="ghost" size="sm">
|
|
43
|
+
{item.label}
|
|
44
|
+
<ChevronDown className="ml-1 h-4 w-4" />
|
|
45
|
+
</Button>
|
|
46
|
+
</DropdownMenuTrigger>
|
|
47
|
+
<DropdownMenuContent>
|
|
48
|
+
{item.children.map((child) => (
|
|
49
|
+
<DropdownMenuItem key={child.href} asChild>
|
|
50
|
+
<Link href={child.href}>{child.label}</Link>
|
|
51
|
+
</DropdownMenuItem>
|
|
52
|
+
))}
|
|
53
|
+
</DropdownMenuContent>
|
|
54
|
+
</DropdownMenu>
|
|
55
|
+
) : (
|
|
56
|
+
<Button key={item.href} asChild variant="ghost" size="sm">
|
|
57
|
+
<Link href={item.href}>{item.label}</Link>
|
|
58
|
+
</Button>
|
|
59
|
+
)
|
|
60
|
+
)}
|
|
61
|
+
{cta ? (
|
|
62
|
+
<Button asChild size="sm">
|
|
63
|
+
<Link href={cta.href}>{cta.label}</Link>
|
|
64
|
+
</Button>
|
|
65
|
+
) : null}
|
|
66
|
+
{i18n.supportedLocales.length > 1 ? (
|
|
67
|
+
<LocaleSwitcher currentLocale={i18n.defaultLocale} />
|
|
68
|
+
) : null}
|
|
69
|
+
<ThemeToggle />
|
|
70
|
+
<Button asChild variant="outline" size="sm">
|
|
71
|
+
<Link href={settings.navigation.adminHref}>
|
|
72
|
+
<LogIn className="mr-2 h-4 w-4" />
|
|
73
|
+
Admin
|
|
74
|
+
</Link>
|
|
75
|
+
</Button>
|
|
76
|
+
</nav>
|
|
77
|
+
</div>
|
|
78
|
+
</header>
|
|
79
|
+
);
|
|
80
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { SiteFooter } from "./site-footer";
|
|
2
|
+
import { SiteHeader } from "./site-header";
|
|
3
|
+
|
|
4
|
+
export async function SiteShell({
|
|
5
|
+
children,
|
|
6
|
+
showHeader = true,
|
|
7
|
+
showFooter = true,
|
|
8
|
+
}: {
|
|
9
|
+
children: React.ReactNode;
|
|
10
|
+
showHeader?: boolean;
|
|
11
|
+
showFooter?: boolean;
|
|
12
|
+
}) {
|
|
13
|
+
return (
|
|
14
|
+
<div className="flex min-h-screen flex-col bg-background">
|
|
15
|
+
{showHeader ? <SiteHeader /> : null}
|
|
16
|
+
<div className="flex-1">{children}</div>
|
|
17
|
+
{showFooter ? <SiteFooter /> : null}
|
|
18
|
+
</div>
|
|
19
|
+
);
|
|
20
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// Theme bootstrap: read the data-attributes set by the root layout
|
|
2
|
+
// and inject the matching CSS custom properties. Rendered once in
|
|
3
|
+
// the root layout (see `app/layout.tsx`). The 8 color palettes are
|
|
4
|
+
// hard-coded here so the scaffolder ships without a runtime theme
|
|
5
|
+
// resolution pass — operators pick a palette in Notion, the next
|
|
6
|
+
// page load applies it.
|
|
7
|
+
|
|
8
|
+
"use client";
|
|
9
|
+
|
|
10
|
+
import { useEffect } from "react";
|
|
11
|
+
|
|
12
|
+
const PRIMARY_PALETTES: Record<string, Record<number, string>> = {
|
|
13
|
+
slate: { 50: "#f8fafc", 100: "#f1f5f9", 200: "#e2e8f0", 300: "#cbd5e1", 400: "#94a3b8", 500: "#64748b", 600: "#475569", 700: "#334155", 800: "#1e293b", 900: "#0f172a" },
|
|
14
|
+
gray: { 50: "#f9fafb", 100: "#f3f4f6", 200: "#e5e7eb", 300: "#d1d5db", 400: "#9ca3af", 500: "#6b7280", 600: "#4b5563", 700: "#374151", 800: "#1f2937", 900: "#111827" },
|
|
15
|
+
zinc: { 50: "#fafafa", 100: "#f4f4f5", 200: "#e4e4e7", 300: "#d4d4d8", 400: "#a1a1aa", 500: "#71717a", 600: "#52525b", 700: "#3f3f46", 800: "#27272a", 900: "#18181b" },
|
|
16
|
+
red: { 50: "#fef2f2", 100: "#fee2e2", 200: "#fecaca", 300: "#fca5a5", 400: "#f87171", 500: "#ef4444", 600: "#dc2626", 700: "#b91c1c", 800: "#991b1b", 900: "#7f1d1d" },
|
|
17
|
+
orange: { 50: "#fff7ed", 100: "#ffedd5", 200: "#fed7aa", 300: "#fdba74", 400: "#fb923c", 500: "#f97316", 600: "#ea580c", 700: "#c2410c", 800: "#9a3412", 900: "#7c2d12" },
|
|
18
|
+
amber: { 50: "#fffbeb", 100: "#fef3c7", 200: "#fde68a", 300: "#fcd34d", 400: "#fbbf24", 500: "#f59e0b", 600: "#d97706", 700: "#b45309", 800: "#92400e", 900: "#78350f" },
|
|
19
|
+
green: { 50: "#f0fdf4", 100: "#dcfce7", 200: "#bbf7d0", 300: "#86efac", 400: "#4ade80", 500: "#22c55e", 600: "#16a34a", 700: "#15803d", 800: "#166534", 900: "#14532d" },
|
|
20
|
+
blue: { 50: "#eff6ff", 100: "#dbeafe", 200: "#bfdbfe", 300: "#93c5fd", 400: "#60a5fa", 500: "#3b82f6", 600: "#2563eb", 700: "#1d4ed8", 800: "#1e40af", 900: "#1e3a8a" },
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const FONT_FAMILIES: Record<string, string> = {
|
|
24
|
+
inter: "Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif",
|
|
25
|
+
geist: "Geist, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif",
|
|
26
|
+
system: "ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif",
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export function ThemeBootstrap() {
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
const root = document.documentElement;
|
|
32
|
+
const primary = root.dataset.themePrimary ?? "slate";
|
|
33
|
+
const accent = root.dataset.themeAccent ?? "blue";
|
|
34
|
+
const font = root.dataset.themeFont ?? "inter";
|
|
35
|
+
|
|
36
|
+
const palette = PRIMARY_PALETTES[primary] ?? PRIMARY_PALETTES.slate;
|
|
37
|
+
for (const [shade, color] of Object.entries(palette)) {
|
|
38
|
+
root.style.setProperty(`--theme-primary-${shade}`, color);
|
|
39
|
+
}
|
|
40
|
+
const accentPalette = PRIMARY_PALETTES[accent] ?? PRIMARY_PALETTES.blue;
|
|
41
|
+
for (const [shade, color] of Object.entries(accentPalette)) {
|
|
42
|
+
root.style.setProperty(`--theme-accent-${shade}`, color);
|
|
43
|
+
}
|
|
44
|
+
root.style.setProperty(
|
|
45
|
+
"--theme-font",
|
|
46
|
+
FONT_FAMILIES[font] ?? FONT_FAMILIES.inter
|
|
47
|
+
);
|
|
48
|
+
}, []);
|
|
49
|
+
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// shadcn 标准 ThemeProvider:包装 next-themes,
|
|
4
|
+
// 让根 layout 用 .class 作为 theme attribute(与 Tailwind v4 的 .dark 类匹配)。
|
|
5
|
+
|
|
6
|
+
import * as React from "react";
|
|
7
|
+
import { ThemeProvider as NextThemesProvider } from "next-themes";
|
|
8
|
+
|
|
9
|
+
export function ThemeProvider({
|
|
10
|
+
children,
|
|
11
|
+
...props
|
|
12
|
+
}: React.ComponentProps<typeof NextThemesProvider>) {
|
|
13
|
+
return <NextThemesProvider {...props}>{children}</NextThemesProvider>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// 主题切换按钮:按当前实际显示效果在 light / dark 之间直接切换。
|
|
4
|
+
// 配合 components/theme-provider.tsx 使用。
|
|
5
|
+
|
|
6
|
+
import * as React from "react";
|
|
7
|
+
import { Moon, Sun } from "lucide-react";
|
|
8
|
+
import { useTheme } from "next-themes";
|
|
9
|
+
import { Button } from "@/components/ui/button";
|
|
10
|
+
|
|
11
|
+
export function ThemeToggle() {
|
|
12
|
+
const { resolvedTheme, setTheme } = useTheme();
|
|
13
|
+
const [mounted, setMounted] = React.useState(false);
|
|
14
|
+
React.useEffect(() => setMounted(true), []);
|
|
15
|
+
|
|
16
|
+
// 避免 hydration mismatch:服务器渲染时只显示一个静态图标
|
|
17
|
+
if (!mounted) {
|
|
18
|
+
return (
|
|
19
|
+
<Button variant="ghost" size="icon" aria-label="Toggle theme" disabled>
|
|
20
|
+
<Sun className="h-4 w-4" />
|
|
21
|
+
</Button>
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const toggle = () => {
|
|
26
|
+
setTheme(resolvedTheme === "dark" ? "light" : "dark");
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<Button variant="ghost" size="icon" onClick={toggle} aria-label="Toggle theme">
|
|
31
|
+
{resolvedTheme === "dark" ? (
|
|
32
|
+
<Moon className="h-4 w-4" />
|
|
33
|
+
) : (
|
|
34
|
+
<Sun className="h-4 w-4" />
|
|
35
|
+
)}
|
|
36
|
+
</Button>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import * as AccordionPrimitive from "@radix-ui/react-accordion";
|
|
5
|
+
import { ChevronDown } from "lucide-react";
|
|
6
|
+
import { cn } from "@/lib/utils";
|
|
7
|
+
|
|
8
|
+
const Accordion = AccordionPrimitive.Root;
|
|
9
|
+
|
|
10
|
+
const AccordionItem = React.forwardRef<
|
|
11
|
+
React.ElementRef<typeof AccordionPrimitive.Item>,
|
|
12
|
+
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
|
|
13
|
+
>(({ className, ...props }, ref) => (
|
|
14
|
+
<AccordionPrimitive.Item
|
|
15
|
+
ref={ref}
|
|
16
|
+
className={cn("border-b", className)}
|
|
17
|
+
{...props}
|
|
18
|
+
/>
|
|
19
|
+
));
|
|
20
|
+
AccordionItem.displayName = "AccordionItem";
|
|
21
|
+
|
|
22
|
+
const AccordionTrigger = React.forwardRef<
|
|
23
|
+
React.ElementRef<typeof AccordionPrimitive.Trigger>,
|
|
24
|
+
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
|
|
25
|
+
>(({ className, children, ...props }, ref) => (
|
|
26
|
+
<AccordionPrimitive.Header className="flex">
|
|
27
|
+
<AccordionPrimitive.Trigger
|
|
28
|
+
ref={ref}
|
|
29
|
+
className={cn(
|
|
30
|
+
"flex flex-1 items-center justify-between py-4 text-left text-sm font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180",
|
|
31
|
+
className
|
|
32
|
+
)}
|
|
33
|
+
{...props}
|
|
34
|
+
>
|
|
35
|
+
{children}
|
|
36
|
+
<ChevronDown className="h-4 w-4 shrink-0 text-muted-foreground transition-transform duration-200" />
|
|
37
|
+
</AccordionPrimitive.Trigger>
|
|
38
|
+
</AccordionPrimitive.Header>
|
|
39
|
+
));
|
|
40
|
+
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName;
|
|
41
|
+
|
|
42
|
+
const AccordionContent = React.forwardRef<
|
|
43
|
+
React.ElementRef<typeof AccordionPrimitive.Content>,
|
|
44
|
+
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
|
|
45
|
+
>(({ className, children, ...props }, ref) => (
|
|
46
|
+
<AccordionPrimitive.Content
|
|
47
|
+
ref={ref}
|
|
48
|
+
className="overflow-hidden text-sm"
|
|
49
|
+
{...props}
|
|
50
|
+
>
|
|
51
|
+
<div className={cn("pb-4 pt-0", className)}>{children}</div>
|
|
52
|
+
</AccordionPrimitive.Content>
|
|
53
|
+
));
|
|
54
|
+
AccordionContent.displayName = AccordionPrimitive.Content.displayName;
|
|
55
|
+
|
|
56
|
+
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
3
|
+
import { cn } from "@/lib/utils";
|
|
4
|
+
|
|
5
|
+
const alertVariants = cva(
|
|
6
|
+
"relative w-full rounded-lg border p-4 [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg~*]:pl-7",
|
|
7
|
+
{
|
|
8
|
+
variants: {
|
|
9
|
+
variant: {
|
|
10
|
+
default: "bg-background text-foreground",
|
|
11
|
+
destructive:
|
|
12
|
+
"border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive",
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
defaultVariants: {
|
|
16
|
+
variant: "default",
|
|
17
|
+
},
|
|
18
|
+
}
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
const Alert = React.forwardRef<
|
|
22
|
+
HTMLDivElement,
|
|
23
|
+
React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants>
|
|
24
|
+
>(({ className, variant, ...props }, ref) => (
|
|
25
|
+
<div
|
|
26
|
+
ref={ref}
|
|
27
|
+
role="alert"
|
|
28
|
+
className={cn(alertVariants({ variant }), className)}
|
|
29
|
+
{...props}
|
|
30
|
+
/>
|
|
31
|
+
));
|
|
32
|
+
Alert.displayName = "Alert";
|
|
33
|
+
|
|
34
|
+
const AlertTitle = React.forwardRef<
|
|
35
|
+
HTMLParagraphElement,
|
|
36
|
+
React.HTMLAttributes<HTMLHeadingElement>
|
|
37
|
+
>(({ className, ...props }, ref) => (
|
|
38
|
+
<h5
|
|
39
|
+
ref={ref}
|
|
40
|
+
className={cn("mb-1 font-medium leading-none tracking-tight", className)}
|
|
41
|
+
{...props}
|
|
42
|
+
/>
|
|
43
|
+
));
|
|
44
|
+
AlertTitle.displayName = "AlertTitle";
|
|
45
|
+
|
|
46
|
+
const AlertDescription = React.forwardRef<
|
|
47
|
+
HTMLParagraphElement,
|
|
48
|
+
React.HTMLAttributes<HTMLParagraphElement>
|
|
49
|
+
>(({ className, ...props }, ref) => (
|
|
50
|
+
<div
|
|
51
|
+
ref={ref}
|
|
52
|
+
className={cn("text-sm leading-relaxed text-muted-foreground", className)}
|
|
53
|
+
{...props}
|
|
54
|
+
/>
|
|
55
|
+
));
|
|
56
|
+
AlertDescription.displayName = "AlertDescription";
|
|
57
|
+
|
|
58
|
+
export { Alert, AlertTitle, AlertDescription };
|
|
59
|
+
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import * as AvatarPrimitive from "@radix-ui/react-avatar";
|
|
5
|
+
import { cn } from "@/lib/utils";
|
|
6
|
+
|
|
7
|
+
const Avatar = React.forwardRef<
|
|
8
|
+
React.ElementRef<typeof AvatarPrimitive.Root>,
|
|
9
|
+
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>
|
|
10
|
+
>(({ className, ...props }, ref) => (
|
|
11
|
+
<AvatarPrimitive.Root
|
|
12
|
+
ref={ref}
|
|
13
|
+
className={cn("relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full", className)}
|
|
14
|
+
{...props}
|
|
15
|
+
/>
|
|
16
|
+
));
|
|
17
|
+
Avatar.displayName = AvatarPrimitive.Root.displayName;
|
|
18
|
+
|
|
19
|
+
const AvatarImage = React.forwardRef<
|
|
20
|
+
React.ElementRef<typeof AvatarPrimitive.Image>,
|
|
21
|
+
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>
|
|
22
|
+
>(({ className, ...props }, ref) => (
|
|
23
|
+
<AvatarPrimitive.Image
|
|
24
|
+
ref={ref}
|
|
25
|
+
className={cn("aspect-square h-full w-full", className)}
|
|
26
|
+
{...props}
|
|
27
|
+
/>
|
|
28
|
+
));
|
|
29
|
+
AvatarImage.displayName = AvatarPrimitive.Image.displayName;
|
|
30
|
+
|
|
31
|
+
const AvatarFallback = React.forwardRef<
|
|
32
|
+
React.ElementRef<typeof AvatarPrimitive.Fallback>,
|
|
33
|
+
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>
|
|
34
|
+
>(({ className, ...props }, ref) => (
|
|
35
|
+
<AvatarPrimitive.Fallback
|
|
36
|
+
ref={ref}
|
|
37
|
+
className={cn("flex h-full w-full items-center justify-center rounded-full bg-muted", className)}
|
|
38
|
+
{...props}
|
|
39
|
+
/>
|
|
40
|
+
));
|
|
41
|
+
AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName;
|
|
42
|
+
|
|
43
|
+
export { Avatar, AvatarImage, AvatarFallback };
|
|
44
|
+
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
3
|
+
import { cn } from "@/lib/utils";
|
|
4
|
+
|
|
5
|
+
const badgeVariants = cva(
|
|
6
|
+
"inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
|
|
7
|
+
{
|
|
8
|
+
variants: {
|
|
9
|
+
variant: {
|
|
10
|
+
default:
|
|
11
|
+
"border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
|
|
12
|
+
secondary:
|
|
13
|
+
"border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
14
|
+
destructive:
|
|
15
|
+
"border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
|
|
16
|
+
outline: "text-foreground",
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
defaultVariants: {
|
|
20
|
+
variant: "default",
|
|
21
|
+
},
|
|
22
|
+
}
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
export interface BadgeProps
|
|
26
|
+
extends React.HTMLAttributes<HTMLDivElement>,
|
|
27
|
+
VariantProps<typeof badgeVariants> {}
|
|
28
|
+
|
|
29
|
+
function Badge({ className, variant, ...props }: BadgeProps) {
|
|
30
|
+
return <div className={cn(badgeVariants({ variant }), className)} {...props} />;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export { Badge, badgeVariants };
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
3
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
4
|
+
import { cn } from "@/lib/utils";
|
|
5
|
+
|
|
6
|
+
// shadcn/ui 标准的 Button 组件(new-york 风格)
|
|
7
|
+
const buttonVariants = cva(
|
|
8
|
+
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
|
|
9
|
+
{
|
|
10
|
+
variants: {
|
|
11
|
+
variant: {
|
|
12
|
+
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
13
|
+
destructive:
|
|
14
|
+
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
|
15
|
+
outline:
|
|
16
|
+
"border border-input bg-background hover:bg-accent hover:text-accent-foreground",
|
|
17
|
+
secondary:
|
|
18
|
+
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
19
|
+
ghost: "hover:bg-accent hover:text-accent-foreground",
|
|
20
|
+
link: "text-primary underline-offset-4 hover:underline",
|
|
21
|
+
},
|
|
22
|
+
size: {
|
|
23
|
+
default: "h-10 px-4 py-2",
|
|
24
|
+
sm: "h-9 rounded-md px-3",
|
|
25
|
+
lg: "h-11 rounded-md px-8",
|
|
26
|
+
icon: "h-10 w-10",
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
defaultVariants: {
|
|
30
|
+
variant: "default",
|
|
31
|
+
size: "default",
|
|
32
|
+
},
|
|
33
|
+
}
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
export interface ButtonProps
|
|
37
|
+
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
38
|
+
VariantProps<typeof buttonVariants> {
|
|
39
|
+
asChild?: boolean;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
43
|
+
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
44
|
+
const Comp = asChild ? Slot : "button";
|
|
45
|
+
return (
|
|
46
|
+
<Comp
|
|
47
|
+
className={cn(buttonVariants({ variant, size, className }))}
|
|
48
|
+
ref={ref}
|
|
49
|
+
{...props}
|
|
50
|
+
/>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
);
|
|
54
|
+
Button.displayName = "Button";
|
|
55
|
+
|
|
56
|
+
export { Button, buttonVariants };
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cn } from "@/lib/utils";
|
|
3
|
+
|
|
4
|
+
const Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
|
5
|
+
({ className, ...props }, ref) => (
|
|
6
|
+
<div
|
|
7
|
+
ref={ref}
|
|
8
|
+
className={cn("rounded-lg border bg-card text-card-foreground shadow-sm", className)}
|
|
9
|
+
{...props}
|
|
10
|
+
/>
|
|
11
|
+
)
|
|
12
|
+
);
|
|
13
|
+
Card.displayName = "Card";
|
|
14
|
+
|
|
15
|
+
const CardHeader = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
|
16
|
+
({ className, ...props }, ref) => (
|
|
17
|
+
<div ref={ref} className={cn("flex flex-col space-y-1.5 p-6", className)} {...props} />
|
|
18
|
+
)
|
|
19
|
+
);
|
|
20
|
+
CardHeader.displayName = "CardHeader";
|
|
21
|
+
|
|
22
|
+
const CardTitle = React.forwardRef<HTMLHeadingElement, React.HTMLAttributes<HTMLHeadingElement>>(
|
|
23
|
+
({ className, ...props }, ref) => (
|
|
24
|
+
<h3
|
|
25
|
+
ref={ref}
|
|
26
|
+
className={cn("text-2xl font-semibold leading-none tracking-tight", className)}
|
|
27
|
+
{...props}
|
|
28
|
+
/>
|
|
29
|
+
)
|
|
30
|
+
);
|
|
31
|
+
CardTitle.displayName = "CardTitle";
|
|
32
|
+
|
|
33
|
+
const CardDescription = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLParagraphElement>>(
|
|
34
|
+
({ className, ...props }, ref) => (
|
|
35
|
+
<p ref={ref} className={cn("text-sm text-muted-foreground", className)} {...props} />
|
|
36
|
+
)
|
|
37
|
+
);
|
|
38
|
+
CardDescription.displayName = "CardDescription";
|
|
39
|
+
|
|
40
|
+
const CardContent = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
|
41
|
+
({ className, ...props }, ref) => (
|
|
42
|
+
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
|
|
43
|
+
)
|
|
44
|
+
);
|
|
45
|
+
CardContent.displayName = "CardContent";
|
|
46
|
+
|
|
47
|
+
const CardFooter = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
|
48
|
+
({ className, ...props }, ref) => (
|
|
49
|
+
<div ref={ref} className={cn("flex items-center p-6 pt-0", className)} {...props} />
|
|
50
|
+
)
|
|
51
|
+
);
|
|
52
|
+
CardFooter.displayName = "CardFooter";
|
|
53
|
+
|
|
54
|
+
export {
|
|
55
|
+
Card,
|
|
56
|
+
CardHeader,
|
|
57
|
+
CardFooter,
|
|
58
|
+
CardTitle,
|
|
59
|
+
CardDescription,
|
|
60
|
+
CardContent,
|
|
61
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
|
|
5
|
+
import { Check } from "lucide-react";
|
|
6
|
+
import { cn } from "@/lib/utils";
|
|
7
|
+
|
|
8
|
+
const Checkbox = React.forwardRef<
|
|
9
|
+
React.ElementRef<typeof CheckboxPrimitive.Root>,
|
|
10
|
+
React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>
|
|
11
|
+
>(({ className, ...props }, ref) => (
|
|
12
|
+
<CheckboxPrimitive.Root
|
|
13
|
+
ref={ref}
|
|
14
|
+
className={cn(
|
|
15
|
+
"peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
|
|
16
|
+
className
|
|
17
|
+
)}
|
|
18
|
+
{...props}
|
|
19
|
+
>
|
|
20
|
+
<CheckboxPrimitive.Indicator className="flex items-center justify-center text-current">
|
|
21
|
+
<Check className="h-4 w-4" />
|
|
22
|
+
</CheckboxPrimitive.Indicator>
|
|
23
|
+
</CheckboxPrimitive.Root>
|
|
24
|
+
));
|
|
25
|
+
Checkbox.displayName = CheckboxPrimitive.Root.displayName;
|
|
26
|
+
|
|
27
|
+
export { Checkbox };
|
|
28
|
+
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
|
5
|
+
import { X } from "lucide-react";
|
|
6
|
+
import { cn } from "@/lib/utils";
|
|
7
|
+
|
|
8
|
+
const Dialog = DialogPrimitive.Root;
|
|
9
|
+
const DialogTrigger = DialogPrimitive.Trigger;
|
|
10
|
+
const DialogPortal = DialogPrimitive.Portal;
|
|
11
|
+
const DialogClose = DialogPrimitive.Close;
|
|
12
|
+
|
|
13
|
+
const DialogOverlay = React.forwardRef<
|
|
14
|
+
React.ElementRef<typeof DialogPrimitive.Overlay>,
|
|
15
|
+
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
|
|
16
|
+
>(({ className, ...props }, ref) => (
|
|
17
|
+
<DialogPrimitive.Overlay
|
|
18
|
+
ref={ref}
|
|
19
|
+
className={cn(
|
|
20
|
+
"fixed inset-0 z-50 bg-background/80 backdrop-blur-sm data-[state=closed]:animate-out data-[state=open]:animate-in data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
|
21
|
+
className
|
|
22
|
+
)}
|
|
23
|
+
{...props}
|
|
24
|
+
/>
|
|
25
|
+
));
|
|
26
|
+
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
|
|
27
|
+
|
|
28
|
+
const DialogContent = React.forwardRef<
|
|
29
|
+
React.ElementRef<typeof DialogPrimitive.Content>,
|
|
30
|
+
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
|
|
31
|
+
>(({ className, children, ...props }, ref) => (
|
|
32
|
+
<DialogPortal>
|
|
33
|
+
<DialogOverlay />
|
|
34
|
+
<DialogPrimitive.Content
|
|
35
|
+
ref={ref}
|
|
36
|
+
className={cn(
|
|
37
|
+
"fixed left-1/2 top-1/2 z-50 grid w-[calc(100%-2rem)] max-w-lg -translate-x-1/2 -translate-y-1/2 gap-4 rounded-lg border bg-background p-6 shadow-lg duration-200 data-[state=closed]:animate-out data-[state=open]:animate-in data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 sm:w-full",
|
|
38
|
+
className
|
|
39
|
+
)}
|
|
40
|
+
{...props}
|
|
41
|
+
>
|
|
42
|
+
{children}
|
|
43
|
+
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none">
|
|
44
|
+
<X className="h-4 w-4" />
|
|
45
|
+
<span className="sr-only">Close</span>
|
|
46
|
+
</DialogPrimitive.Close>
|
|
47
|
+
</DialogPrimitive.Content>
|
|
48
|
+
</DialogPortal>
|
|
49
|
+
));
|
|
50
|
+
DialogContent.displayName = DialogPrimitive.Content.displayName;
|
|
51
|
+
|
|
52
|
+
const DialogHeader = ({
|
|
53
|
+
className,
|
|
54
|
+
...props
|
|
55
|
+
}: React.HTMLAttributes<HTMLDivElement>) => (
|
|
56
|
+
<div className={cn("flex flex-col space-y-1.5 text-center sm:text-left", className)} {...props} />
|
|
57
|
+
);
|
|
58
|
+
DialogHeader.displayName = "DialogHeader";
|
|
59
|
+
|
|
60
|
+
const DialogFooter = ({
|
|
61
|
+
className,
|
|
62
|
+
...props
|
|
63
|
+
}: React.HTMLAttributes<HTMLDivElement>) => (
|
|
64
|
+
<div className={cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", className)} {...props} />
|
|
65
|
+
);
|
|
66
|
+
DialogFooter.displayName = "DialogFooter";
|
|
67
|
+
|
|
68
|
+
const DialogTitle = React.forwardRef<
|
|
69
|
+
React.ElementRef<typeof DialogPrimitive.Title>,
|
|
70
|
+
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
|
|
71
|
+
>(({ className, ...props }, ref) => (
|
|
72
|
+
<DialogPrimitive.Title
|
|
73
|
+
ref={ref}
|
|
74
|
+
className={cn("text-lg font-semibold leading-none tracking-tight", className)}
|
|
75
|
+
{...props}
|
|
76
|
+
/>
|
|
77
|
+
));
|
|
78
|
+
DialogTitle.displayName = DialogPrimitive.Title.displayName;
|
|
79
|
+
|
|
80
|
+
const DialogDescription = React.forwardRef<
|
|
81
|
+
React.ElementRef<typeof DialogPrimitive.Description>,
|
|
82
|
+
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
|
|
83
|
+
>(({ className, ...props }, ref) => (
|
|
84
|
+
<DialogPrimitive.Description
|
|
85
|
+
ref={ref}
|
|
86
|
+
className={cn("text-sm text-muted-foreground", className)}
|
|
87
|
+
{...props}
|
|
88
|
+
/>
|
|
89
|
+
));
|
|
90
|
+
DialogDescription.displayName = DialogPrimitive.Description.displayName;
|
|
91
|
+
|
|
92
|
+
export {
|
|
93
|
+
Dialog,
|
|
94
|
+
DialogPortal,
|
|
95
|
+
DialogOverlay,
|
|
96
|
+
DialogClose,
|
|
97
|
+
DialogTrigger,
|
|
98
|
+
DialogContent,
|
|
99
|
+
DialogHeader,
|
|
100
|
+
DialogFooter,
|
|
101
|
+
DialogTitle,
|
|
102
|
+
DialogDescription,
|
|
103
|
+
};
|
|
104
|
+
|