generator-kodly-react-app 1.0.15 → 1.0.17
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/generators/app/index.js +15 -1
- package/generators/app/templates/.env.sandbox +4 -3
- package/generators/app/templates/.node-version +1 -0
- package/generators/app/templates/.pnpmfile.cjs +14 -0
- package/generators/app/templates/.prettierignore +13 -0
- package/generators/app/templates/AGENTS.md +14 -0
- package/generators/app/templates/I18N_MIGRATION.md +13 -13
- package/generators/app/templates/STRUCTURE.md +26 -26
- package/generators/app/templates/TESTING.md +30 -0
- package/generators/app/templates/index.html +6 -14
- package/generators/app/templates/openapi-ts.config.ts +16 -12
- package/generators/app/templates/oxlint.config.ts +72 -0
- package/generators/app/templates/package.json +66 -52
- package/generators/app/templates/pnpm-workspace.yaml +19 -0
- package/generators/app/templates/scripts/check-duplicates.mjs +28 -0
- package/generators/app/templates/scripts/generated-files.mjs +21 -0
- package/generators/app/templates/src/app.tsx +20 -20
- package/generators/app/templates/src/components/layout/language-switcher.tsx +20 -16
- package/generators/app/templates/src/components/layout/page-layout.tsx +31 -0
- package/generators/app/templates/src/components/layout/theme-switcher.tsx +16 -15
- package/generators/app/templates/src/components/theme/theme-provider.tsx +20 -13
- package/generators/app/templates/src/components/ui/button.tsx +15 -14
- package/generators/app/templates/src/components/ui/card.tsx +23 -16
- package/generators/app/templates/src/components/ui/field.tsx +35 -35
- package/generators/app/templates/src/components/ui/form.tsx +66 -30
- package/generators/app/templates/src/components/ui/input-group.tsx +22 -28
- package/generators/app/templates/src/components/ui/input.tsx +8 -8
- package/generators/app/templates/src/components/ui/label.tsx +9 -9
- package/generators/app/templates/src/components/ui/select.tsx +0 -1
- package/generators/app/templates/src/components/ui/separator.tsx +10 -10
- package/generators/app/templates/src/components/ui/textarea.tsx +7 -7
- package/generators/app/templates/src/index.css +78 -68
- package/generators/app/templates/src/lib/i18n.ts +40 -31
- package/generators/app/templates/src/lib/routes.ts +2 -3
- package/generators/app/templates/src/lib/utils/error-handler.ts +21 -14
- package/generators/app/templates/src/lib/utils.ts +3 -3
- package/generators/app/templates/src/locales/index.ts +12 -12
- package/generators/app/templates/src/locales/ja/theme.json +0 -1
- package/generators/app/templates/src/main.tsx +10 -12
- package/generators/app/templates/src/modules/app/layouts/app-layout.tsx +14 -25
- package/generators/app/templates/src/modules/app/locales/app-en.json +0 -1
- package/generators/app/templates/src/modules/app/locales/app-ja.json +0 -1
- package/generators/app/templates/src/modules/auth/components/auth-form.tsx +86 -0
- package/generators/app/templates/src/modules/auth/components/forgot-password-form.tsx +27 -52
- package/generators/app/templates/src/modules/auth/components/login-form.tsx +42 -72
- package/generators/app/templates/src/modules/auth/components/login-link.tsx +12 -0
- package/generators/app/templates/src/modules/auth/components/reset-password-form.tsx +46 -92
- package/generators/app/templates/src/modules/auth/components/signup-form.tsx +35 -71
- package/generators/app/templates/src/modules/auth/contexts/auth-context.tsx +10 -6
- package/generators/app/templates/src/modules/auth/hooks/use-auth-hook.ts +54 -39
- package/generators/app/templates/src/modules/auth/layouts/auth-layout.tsx +12 -15
- package/generators/app/templates/src/modules/auth/locales/auth-en.json +0 -1
- package/generators/app/templates/src/modules/auth/locales/auth-ja.json +0 -1
- package/generators/app/templates/src/modules/auth/schemas/form-schemas.ts +14 -10
- package/generators/app/templates/src/modules/landing/components/auth-hero.tsx +20 -18
- package/generators/app/templates/src/modules/landing/components/welcome-hero.tsx +13 -11
- package/generators/app/templates/src/modules/landing/landing-page-layout.tsx +1 -24
- package/generators/app/templates/src/modules/landing/landing-page.tsx +11 -6
- package/generators/app/templates/src/modules/landing/layouts/landing-page-layout.tsx +2 -21
- package/generators/app/templates/src/modules/landing/locales/landing-en.json +0 -1
- package/generators/app/templates/src/openapi-client-config.ts +1 -1
- package/generators/app/templates/src/routeTree.gen.ts +188 -188
- package/generators/app/templates/src/router.tsx +4 -4
- package/generators/app/templates/src/routes/$.tsx +0 -1
- package/generators/app/templates/src/routes/__root.tsx +3 -3
- package/generators/app/templates/src/routes/_landing/index.tsx +3 -3
- package/generators/app/templates/src/routes/_landing/route.tsx +3 -3
- package/generators/app/templates/src/routes/app/index.tsx +6 -6
- package/generators/app/templates/src/routes/app/route.tsx +4 -4
- package/generators/app/templates/src/routes/auth/forgot-password.tsx +1 -1
- package/generators/app/templates/src/routes/auth/login.tsx +3 -3
- package/generators/app/templates/src/routes/auth/reset-password.tsx +3 -3
- package/generators/app/templates/src/routes/auth/route.tsx +16 -8
- package/generators/app/templates/src/routes/auth/signup.tsx +1 -2
- package/generators/app/templates/src/sdk/@tanstack/react-query.gen.ts +157 -76
- package/generators/app/templates/src/sdk/client/client.gen.ts +35 -37
- package/generators/app/templates/src/sdk/client/index.ts +8 -8
- package/generators/app/templates/src/sdk/client/types.gen.ts +22 -19
- package/generators/app/templates/src/sdk/client/utils.gen.ts +53 -45
- package/generators/app/templates/src/sdk/client.gen.ts +14 -5
- package/generators/app/templates/src/sdk/core/auth.gen.ts +6 -6
- package/generators/app/templates/src/sdk/core/bodySerializer.gen.ts +25 -27
- package/generators/app/templates/src/sdk/core/params.gen.ts +12 -12
- package/generators/app/templates/src/sdk/core/pathSerializer.gen.ts +40 -41
- package/generators/app/templates/src/sdk/core/queryKeySerializer.gen.ts +12 -29
- package/generators/app/templates/src/sdk/core/serverSentEvents.gen.ts +26 -24
- package/generators/app/templates/src/sdk/core/types.gen.ts +17 -15
- package/generators/app/templates/src/sdk/core/utils.gen.ts +18 -18
- package/generators/app/templates/src/sdk/index.ts +40 -2
- package/generators/app/templates/src/sdk/schemas.gen.ts +108 -166
- package/generators/app/templates/src/sdk/sdk.gen.ts +145 -63
- package/generators/app/templates/src/sdk/types.gen.ts +90 -84
- package/generators/app/templates/src/sdk/zod.gen.ts +57 -74
- package/generators/app/templates/src/types/i18n.d.ts +0 -1
- package/generators/app/templates/stylelint.config.js +55 -0
- package/generators/app/templates/tailwind.config.js +0 -1
- package/generators/app/templates/tests/form-controls.test.tsx +39 -0
- package/generators/app/templates/tests/sdk.test.ts +49 -0
- package/generators/app/templates/tests/setup.ts +5 -0
- package/generators/app/templates/tsconfig.app.json +0 -2
- package/generators/app/templates/tsconfig.json +2 -2
- package/generators/app/templates/tsr.config.json +4 -0
- package/generators/app/templates/vite.config.js +14 -16
- package/generators/app/templates/vitest.config.ts +14 -0
- package/package.json +9 -10
- package/generators/app/templates/postcss.config.js +0 -6
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { generatedFiles } from "./generated-files.mjs";
|
|
5
|
+
|
|
6
|
+
const require = createRequire(import.meta.url);
|
|
7
|
+
const binary = path.join(
|
|
8
|
+
path.dirname(require.resolve("jscpd/package.json")),
|
|
9
|
+
"run-jscpd.js",
|
|
10
|
+
);
|
|
11
|
+
const result = spawnSync(
|
|
12
|
+
process.execPath,
|
|
13
|
+
[
|
|
14
|
+
binary,
|
|
15
|
+
"src",
|
|
16
|
+
"--min-tokens",
|
|
17
|
+
"50",
|
|
18
|
+
"--threshold",
|
|
19
|
+
"0.5",
|
|
20
|
+
"--reporters",
|
|
21
|
+
"consoleFull",
|
|
22
|
+
"--ignore",
|
|
23
|
+
generatedFiles().join(","),
|
|
24
|
+
],
|
|
25
|
+
{ stdio: "inherit" },
|
|
26
|
+
);
|
|
27
|
+
if (result.error) console.error(result.error.message);
|
|
28
|
+
process.exitCode = result.status ?? 1;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
// Keep this policy in source: handwritten SDK wrappers must still be checked.
|
|
5
|
+
/** @returns {string[]} */
|
|
6
|
+
export function generatedFiles(directory = "src") {
|
|
7
|
+
if (!fs.existsSync(directory)) return [];
|
|
8
|
+
return fs.readdirSync(directory, { withFileTypes: true }).flatMap((entry) => {
|
|
9
|
+
const file = path.join(directory, entry.name);
|
|
10
|
+
if (entry.isSymbolicLink()) return [];
|
|
11
|
+
if (entry.isDirectory()) return generatedFiles(file);
|
|
12
|
+
const generated =
|
|
13
|
+
/\.gen\.[cm]?[jt]sx?$/.test(file) || /\/client\/index\.ts$/.test(file);
|
|
14
|
+
return generated &&
|
|
15
|
+
/^\/\/ This file is auto-generated by @hey-api\/openapi-ts\r?\n/.test(
|
|
16
|
+
fs.readFileSync(file, "utf8"),
|
|
17
|
+
)
|
|
18
|
+
? [file.replaceAll(path.sep, "/")]
|
|
19
|
+
: [];
|
|
20
|
+
});
|
|
21
|
+
}
|
|
@@ -1,15 +1,20 @@
|
|
|
1
|
-
import { RouterProvider } from
|
|
2
|
-
import { useAtomValue } from
|
|
3
|
-
import { Loader2 } from
|
|
4
|
-
import { useEffect } from
|
|
5
|
-
import { Toaster } from
|
|
6
|
-
import
|
|
7
|
-
import { AuthProvider } from
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
import { RouterProvider } from "@tanstack/react-router";
|
|
2
|
+
import { useAtomValue } from "jotai";
|
|
3
|
+
import { Loader2 } from "lucide-react";
|
|
4
|
+
import { useEffect } from "react";
|
|
5
|
+
import { Toaster } from "sonner";
|
|
6
|
+
import "@/index.css";
|
|
7
|
+
import { AuthProvider } from "@/modules/auth/contexts/auth-context";
|
|
8
|
+
import {
|
|
9
|
+
authTokenAtom,
|
|
10
|
+
currentUserDetailsAtom,
|
|
11
|
+
useValidateToken,
|
|
12
|
+
setLocaleInClient,
|
|
13
|
+
} from "@/modules/auth/hooks/use-auth-hook";
|
|
14
|
+
import { router } from "@/router";
|
|
15
|
+
import { getLanguage, SupportedLanguages } from "@/lib/i18n";
|
|
11
16
|
|
|
12
|
-
declare module
|
|
17
|
+
declare module "@tanstack/react-router" {
|
|
13
18
|
interface Register {
|
|
14
19
|
router: typeof router;
|
|
15
20
|
}
|
|
@@ -18,12 +23,7 @@ declare module '@tanstack/react-router' {
|
|
|
18
23
|
function InnerApp() {
|
|
19
24
|
const token = useAtomValue(authTokenAtom);
|
|
20
25
|
const data = useAtomValue(currentUserDetailsAtom);
|
|
21
|
-
return
|
|
22
|
-
<RouterProvider
|
|
23
|
-
router={router}
|
|
24
|
-
context={{ token, data }}
|
|
25
|
-
/>
|
|
26
|
-
);
|
|
26
|
+
return <RouterProvider router={router} context={{ token, data }} />;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
export default function App() {
|
|
@@ -44,14 +44,14 @@ export default function App() {
|
|
|
44
44
|
if (!token || (!isPending && (isError || isSuccess))) {
|
|
45
45
|
return (
|
|
46
46
|
<AuthProvider>
|
|
47
|
-
<Toaster position=
|
|
47
|
+
<Toaster position="top-right" />
|
|
48
48
|
<InnerApp />
|
|
49
49
|
</AuthProvider>
|
|
50
50
|
);
|
|
51
51
|
}
|
|
52
52
|
return (
|
|
53
|
-
<div className=
|
|
54
|
-
<Loader2 className=
|
|
53
|
+
<div className="flex h-screen w-screen items-center justify-center">
|
|
54
|
+
<Loader2 className="h-6 w-6 animate-spin" />
|
|
55
55
|
</div>
|
|
56
56
|
);
|
|
57
57
|
}
|
|
@@ -1,39 +1,43 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import {
|
|
2
|
+
Select,
|
|
3
|
+
SelectContent,
|
|
4
|
+
SelectItem,
|
|
5
|
+
SelectTrigger,
|
|
6
|
+
SelectValue,
|
|
7
|
+
} from "@/components/ui/select";
|
|
8
|
+
import { getLanguage, changeLanguage } from "@/lib/i18n";
|
|
9
|
+
import { useTranslation } from "react-i18next";
|
|
10
|
+
import { useEffect, useState } from "react";
|
|
5
11
|
|
|
6
12
|
export function LanguageSwitcher() {
|
|
7
13
|
const { i18n } = useTranslation();
|
|
8
|
-
const [currentLang, setCurrentLang] = useState<
|
|
9
|
-
return (getLanguage() as
|
|
14
|
+
const [currentLang, setCurrentLang] = useState<"en" | "ja">(() => {
|
|
15
|
+
return (getLanguage() as "en" | "ja") || "en";
|
|
10
16
|
});
|
|
11
17
|
|
|
12
18
|
useEffect(() => {
|
|
13
19
|
const handleLanguageChange = (lng: string) => {
|
|
14
|
-
setCurrentLang(lng as
|
|
20
|
+
setCurrentLang(lng as "en" | "ja");
|
|
15
21
|
};
|
|
16
22
|
|
|
17
|
-
i18n.on(
|
|
23
|
+
i18n.on("languageChanged", handleLanguageChange);
|
|
18
24
|
return () => {
|
|
19
|
-
i18n.off(
|
|
25
|
+
i18n.off("languageChanged", handleLanguageChange);
|
|
20
26
|
};
|
|
21
27
|
}, [i18n]);
|
|
22
28
|
|
|
23
29
|
const handleLanguageChange = (value: string) => {
|
|
24
|
-
changeLanguage(value as
|
|
30
|
+
changeLanguage(value as "en" | "ja");
|
|
25
31
|
};
|
|
26
32
|
|
|
27
33
|
return (
|
|
28
|
-
<Select
|
|
29
|
-
|
|
30
|
-
onValueChange={handleLanguageChange}>
|
|
31
|
-
<SelectTrigger className='min-w-[120px]'>
|
|
34
|
+
<Select value={currentLang} onValueChange={handleLanguageChange}>
|
|
35
|
+
<SelectTrigger className="min-w-30">
|
|
32
36
|
<SelectValue />
|
|
33
37
|
</SelectTrigger>
|
|
34
38
|
<SelectContent>
|
|
35
|
-
<SelectItem value=
|
|
36
|
-
<SelectItem value=
|
|
39
|
+
<SelectItem value="en">English</SelectItem>
|
|
40
|
+
<SelectItem value="ja">日本語</SelectItem>
|
|
37
41
|
</SelectContent>
|
|
38
42
|
</Select>
|
|
39
43
|
);
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import { useTranslation } from "react-i18next";
|
|
3
|
+
import { LanguageSwitcher } from "./language-switcher";
|
|
4
|
+
import { ThemeSwitcher } from "./theme-switcher";
|
|
5
|
+
|
|
6
|
+
export function PageLayout({
|
|
7
|
+
children,
|
|
8
|
+
actions,
|
|
9
|
+
mainClassName,
|
|
10
|
+
}: {
|
|
11
|
+
children: ReactNode;
|
|
12
|
+
actions?: ReactNode;
|
|
13
|
+
mainClassName?: string;
|
|
14
|
+
}) {
|
|
15
|
+
const { t } = useTranslation();
|
|
16
|
+
return (
|
|
17
|
+
<div className="min-h-screen bg-background">
|
|
18
|
+
<header className="border-b">
|
|
19
|
+
<div className="container mx-auto flex h-16 items-center justify-between px-4">
|
|
20
|
+
<h1 className="text-xl font-semibold">{t("app.title")}</h1>
|
|
21
|
+
<div className="flex items-center gap-4">
|
|
22
|
+
<ThemeSwitcher />
|
|
23
|
+
<LanguageSwitcher />
|
|
24
|
+
{actions}
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
27
|
+
</header>
|
|
28
|
+
<main className={mainClassName}>{children}</main>
|
|
29
|
+
</div>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
@@ -1,36 +1,37 @@
|
|
|
1
|
-
import { Button } from
|
|
2
|
-
import { ThemeTypes, useTheme } from
|
|
3
|
-
import { Moon, Sun, Monitor } from
|
|
1
|
+
import { Button } from "@/components/ui/button";
|
|
2
|
+
import { ThemeTypes, useTheme } from "@/components/theme/theme-provider";
|
|
3
|
+
import { Moon, Sun, Monitor } from "lucide-react";
|
|
4
4
|
|
|
5
5
|
export function ThemeSwitcher() {
|
|
6
6
|
const { theme, setTheme } = useTheme();
|
|
7
7
|
|
|
8
8
|
const cycleTheme = () => {
|
|
9
|
-
const themes: ThemeTypes[] = [
|
|
9
|
+
const themes: ThemeTypes[] = ["light", "dark", "system"];
|
|
10
10
|
const currentIndex = themes.indexOf(theme);
|
|
11
11
|
const nextIndex = (currentIndex + 1) % themes.length;
|
|
12
|
-
setTheme(themes[nextIndex]);
|
|
12
|
+
setTheme(themes[nextIndex] ?? "system");
|
|
13
13
|
};
|
|
14
14
|
|
|
15
15
|
const getIcon = () => {
|
|
16
16
|
switch (theme) {
|
|
17
|
-
case
|
|
18
|
-
return <Sun className=
|
|
19
|
-
case
|
|
20
|
-
return <Moon className=
|
|
21
|
-
case
|
|
22
|
-
return <Monitor className=
|
|
17
|
+
case "light":
|
|
18
|
+
return <Sun className="h-4 w-4" />;
|
|
19
|
+
case "dark":
|
|
20
|
+
return <Moon className="h-4 w-4" />;
|
|
21
|
+
case "system":
|
|
22
|
+
return <Monitor className="h-4 w-4" />;
|
|
23
23
|
default:
|
|
24
|
-
return <Monitor className=
|
|
24
|
+
return <Monitor className="h-4 w-4" />;
|
|
25
25
|
}
|
|
26
26
|
};
|
|
27
27
|
|
|
28
28
|
return (
|
|
29
29
|
<Button
|
|
30
|
-
variant=
|
|
31
|
-
size=
|
|
30
|
+
variant="outline"
|
|
31
|
+
size="icon"
|
|
32
32
|
onClick={cycleTheme}
|
|
33
|
-
aria-label=
|
|
33
|
+
aria-label="Toggle theme"
|
|
34
|
+
>
|
|
34
35
|
{getIcon()}
|
|
35
36
|
</Button>
|
|
36
37
|
);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { createContext, useContext, useEffect, useState } from
|
|
1
|
+
import { createContext, useContext, useEffect, useState } from "react";
|
|
2
2
|
|
|
3
|
-
export type ThemeTypes =
|
|
3
|
+
export type ThemeTypes = "dark" | "light" | "system";
|
|
4
4
|
|
|
5
5
|
type ThemeProviderProps = {
|
|
6
6
|
children: React.ReactNode;
|
|
@@ -14,28 +14,36 @@ type ThemeProviderState = {
|
|
|
14
14
|
};
|
|
15
15
|
|
|
16
16
|
const initialState: ThemeProviderState = {
|
|
17
|
-
theme:
|
|
17
|
+
theme: "system" as ThemeTypes,
|
|
18
18
|
setTheme: () => null,
|
|
19
19
|
};
|
|
20
20
|
|
|
21
21
|
const ThemeProviderContext = createContext<ThemeProviderState>(initialState);
|
|
22
22
|
|
|
23
|
-
export function ThemeProvider({
|
|
23
|
+
export function ThemeProvider({
|
|
24
|
+
children,
|
|
25
|
+
defaultTheme = "system",
|
|
26
|
+
storageKey = "vite-ui-theme",
|
|
27
|
+
...props
|
|
28
|
+
}: ThemeProviderProps) {
|
|
24
29
|
const [theme, setTheme] = useState<ThemeTypes>(() => {
|
|
25
30
|
const stored = localStorage.getItem(storageKey);
|
|
26
|
-
if (stored && [
|
|
31
|
+
if (stored && ["dark", "light", "system"].includes(stored)) {
|
|
27
32
|
return stored as ThemeTypes;
|
|
28
33
|
}
|
|
29
|
-
return
|
|
34
|
+
return defaultTheme;
|
|
30
35
|
});
|
|
31
36
|
|
|
32
37
|
useEffect(() => {
|
|
33
38
|
const root = window.document.documentElement;
|
|
34
39
|
|
|
35
|
-
root.classList.remove(
|
|
40
|
+
root.classList.remove("light", "dark");
|
|
36
41
|
|
|
37
|
-
if (theme ===
|
|
38
|
-
const systemTheme = window.matchMedia(
|
|
42
|
+
if (theme === "system") {
|
|
43
|
+
const systemTheme = window.matchMedia("(prefers-color-scheme: dark)")
|
|
44
|
+
.matches
|
|
45
|
+
? "dark"
|
|
46
|
+
: "light";
|
|
39
47
|
root.classList.add(systemTheme);
|
|
40
48
|
return;
|
|
41
49
|
}
|
|
@@ -52,9 +60,7 @@ export function ThemeProvider({ children, defaultTheme = 'system', storageKey =
|
|
|
52
60
|
};
|
|
53
61
|
|
|
54
62
|
return (
|
|
55
|
-
<ThemeProviderContext.Provider
|
|
56
|
-
{...props}
|
|
57
|
-
value={value}>
|
|
63
|
+
<ThemeProviderContext.Provider {...props} value={value}>
|
|
58
64
|
{children}
|
|
59
65
|
</ThemeProviderContext.Provider>
|
|
60
66
|
);
|
|
@@ -62,6 +68,7 @@ export function ThemeProvider({ children, defaultTheme = 'system', storageKey =
|
|
|
62
68
|
|
|
63
69
|
export const useTheme = () => {
|
|
64
70
|
const context = useContext(ThemeProviderContext);
|
|
65
|
-
if (context === undefined)
|
|
71
|
+
if (context === undefined)
|
|
72
|
+
throw new Error("useTheme must be used within a ThemeProvider");
|
|
66
73
|
return context;
|
|
67
74
|
};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import * as React from "react"
|
|
2
|
-
import { Slot } from "@radix-ui/react-slot"
|
|
3
|
-
import { cva, type VariantProps } from "class-variance-authority"
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
3
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
4
4
|
|
|
5
|
-
import { cn } from "@/lib/utils"
|
|
5
|
+
import { cn } from "@/lib/utils";
|
|
6
6
|
|
|
7
7
|
const buttonVariants = cva(
|
|
8
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-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
|
@@ -31,27 +31,28 @@ const buttonVariants = cva(
|
|
|
31
31
|
variant: "default",
|
|
32
32
|
size: "default",
|
|
33
33
|
},
|
|
34
|
-
}
|
|
35
|
-
)
|
|
34
|
+
},
|
|
35
|
+
);
|
|
36
36
|
|
|
37
37
|
export interface ButtonProps
|
|
38
|
-
extends
|
|
38
|
+
extends
|
|
39
|
+
React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
39
40
|
VariantProps<typeof buttonVariants> {
|
|
40
|
-
asChild?: boolean
|
|
41
|
+
asChild?: boolean;
|
|
41
42
|
}
|
|
42
43
|
|
|
43
44
|
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
44
45
|
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
45
|
-
const Comp = asChild ? Slot : "button"
|
|
46
|
+
const Comp = asChild ? Slot : "button";
|
|
46
47
|
return (
|
|
47
48
|
<Comp
|
|
48
49
|
className={cn(buttonVariants({ variant, size, className }))}
|
|
49
50
|
ref={ref}
|
|
50
51
|
{...props}
|
|
51
52
|
/>
|
|
52
|
-
)
|
|
53
|
-
}
|
|
54
|
-
)
|
|
55
|
-
Button.displayName = "Button"
|
|
53
|
+
);
|
|
54
|
+
},
|
|
55
|
+
);
|
|
56
|
+
Button.displayName = "Button";
|
|
56
57
|
|
|
57
|
-
export { Button, buttonVariants }
|
|
58
|
+
export { Button, buttonVariants };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import * as React from "react"
|
|
1
|
+
import * as React from "react";
|
|
2
2
|
|
|
3
|
-
import { cn } from "@/lib/utils"
|
|
3
|
+
import { cn } from "@/lib/utils";
|
|
4
4
|
|
|
5
5
|
const Card = React.forwardRef<
|
|
6
6
|
HTMLDivElement,
|
|
@@ -10,12 +10,12 @@ const Card = React.forwardRef<
|
|
|
10
10
|
ref={ref}
|
|
11
11
|
className={cn(
|
|
12
12
|
"rounded-xl border bg-card text-card-foreground shadow",
|
|
13
|
-
className
|
|
13
|
+
className,
|
|
14
14
|
)}
|
|
15
15
|
{...props}
|
|
16
16
|
/>
|
|
17
|
-
))
|
|
18
|
-
Card.displayName = "Card"
|
|
17
|
+
));
|
|
18
|
+
Card.displayName = "Card";
|
|
19
19
|
|
|
20
20
|
const CardHeader = React.forwardRef<
|
|
21
21
|
HTMLDivElement,
|
|
@@ -26,8 +26,8 @@ const CardHeader = React.forwardRef<
|
|
|
26
26
|
className={cn("flex flex-col space-y-1.5 p-6", className)}
|
|
27
27
|
{...props}
|
|
28
28
|
/>
|
|
29
|
-
))
|
|
30
|
-
CardHeader.displayName = "CardHeader"
|
|
29
|
+
));
|
|
30
|
+
CardHeader.displayName = "CardHeader";
|
|
31
31
|
|
|
32
32
|
const CardTitle = React.forwardRef<
|
|
33
33
|
HTMLDivElement,
|
|
@@ -38,8 +38,8 @@ const CardTitle = React.forwardRef<
|
|
|
38
38
|
className={cn("font-semibold leading-none tracking-tight", className)}
|
|
39
39
|
{...props}
|
|
40
40
|
/>
|
|
41
|
-
))
|
|
42
|
-
CardTitle.displayName = "CardTitle"
|
|
41
|
+
));
|
|
42
|
+
CardTitle.displayName = "CardTitle";
|
|
43
43
|
|
|
44
44
|
const CardDescription = React.forwardRef<
|
|
45
45
|
HTMLDivElement,
|
|
@@ -50,16 +50,16 @@ const CardDescription = React.forwardRef<
|
|
|
50
50
|
className={cn("text-sm text-muted-foreground", className)}
|
|
51
51
|
{...props}
|
|
52
52
|
/>
|
|
53
|
-
))
|
|
54
|
-
CardDescription.displayName = "CardDescription"
|
|
53
|
+
));
|
|
54
|
+
CardDescription.displayName = "CardDescription";
|
|
55
55
|
|
|
56
56
|
const CardContent = React.forwardRef<
|
|
57
57
|
HTMLDivElement,
|
|
58
58
|
React.HTMLAttributes<HTMLDivElement>
|
|
59
59
|
>(({ className, ...props }, ref) => (
|
|
60
60
|
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
|
|
61
|
-
))
|
|
62
|
-
CardContent.displayName = "CardContent"
|
|
61
|
+
));
|
|
62
|
+
CardContent.displayName = "CardContent";
|
|
63
63
|
|
|
64
64
|
const CardFooter = React.forwardRef<
|
|
65
65
|
HTMLDivElement,
|
|
@@ -70,7 +70,14 @@ const CardFooter = React.forwardRef<
|
|
|
70
70
|
className={cn("flex items-center p-6 pt-0", className)}
|
|
71
71
|
{...props}
|
|
72
72
|
/>
|
|
73
|
-
))
|
|
74
|
-
CardFooter.displayName = "CardFooter"
|
|
73
|
+
));
|
|
74
|
+
CardFooter.displayName = "CardFooter";
|
|
75
75
|
|
|
76
|
-
export {
|
|
76
|
+
export {
|
|
77
|
+
Card,
|
|
78
|
+
CardHeader,
|
|
79
|
+
CardFooter,
|
|
80
|
+
CardTitle,
|
|
81
|
+
CardDescription,
|
|
82
|
+
CardContent,
|
|
83
|
+
};
|