generator-kodly-react-app 1.0.7 → 1.0.11
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 +63 -18
- package/generators/app/templates/.env.dev +4 -0
- package/generators/app/templates/.env.sandbox +4 -0
- package/generators/app/templates/STRUCTURE.md +661 -0
- package/generators/app/templates/components.json +22 -0
- package/generators/app/templates/gitignore.template +78 -2
- package/generators/app/templates/index.html +14 -6
- package/generators/app/templates/openapi-ts.config.ts +29 -0
- package/generators/app/templates/package.json +40 -26
- package/generators/app/templates/public/favicon.svg +4 -0
- package/generators/app/templates/src/app.tsx +8 -8
- package/generators/app/templates/src/components/layout/language-switcher.tsx +40 -0
- package/generators/app/templates/src/components/layout/theme-switcher.tsx +37 -0
- package/generators/app/templates/src/components/theme/theme-provider.tsx +22 -28
- package/generators/app/templates/src/components/ui/button.tsx +34 -32
- package/generators/app/templates/src/components/ui/card.tsx +76 -0
- package/generators/app/templates/src/components/ui/field.tsx +242 -0
- package/generators/app/templates/src/components/ui/form.tsx +129 -0
- package/generators/app/templates/src/components/ui/input-group.tsx +170 -0
- package/generators/app/templates/src/components/ui/input.tsx +19 -21
- package/generators/app/templates/src/components/ui/label.tsx +24 -0
- package/generators/app/templates/src/components/ui/select.tsx +184 -0
- package/generators/app/templates/src/components/ui/separator.tsx +31 -0
- package/generators/app/templates/src/components/ui/textarea.tsx +22 -0
- package/generators/app/templates/src/index.css +83 -26
- package/generators/app/templates/src/lib/i18n.ts +31 -16
- package/generators/app/templates/src/lib/routes.ts +14 -0
- package/generators/app/templates/src/lib/utils.ts +3 -4
- package/generators/app/templates/src/locales/en/common.json +5 -0
- package/generators/app/templates/src/locales/en/theme.json +5 -0
- package/generators/app/templates/src/locales/index.ts +31 -0
- package/generators/app/templates/src/locales/ja/common.json +5 -0
- package/generators/app/templates/src/locales/ja/theme.json +6 -0
- package/generators/app/templates/src/main.tsx +19 -15
- package/generators/app/templates/src/modules/app/layouts/app-layout.tsx +37 -0
- package/generators/app/templates/src/modules/app/locales/app-en.json +9 -0
- package/generators/app/templates/src/modules/app/locales/app-ja.json +9 -0
- package/generators/app/templates/src/modules/auth/components/forgot-password-form.tsx +74 -0
- package/generators/app/templates/src/modules/auth/components/login-form.tsx +95 -0
- package/generators/app/templates/src/modules/auth/components/reset-password-form.tsx +112 -0
- package/generators/app/templates/src/modules/auth/components/signup-form.tsx +92 -0
- package/generators/app/templates/src/modules/auth/{auth-context.tsx → contexts/auth-context.tsx} +3 -3
- package/generators/app/templates/src/modules/auth/hooks/use-auth-hook.ts +180 -0
- package/generators/app/templates/src/modules/auth/layouts/auth-layout.tsx +28 -0
- package/generators/app/templates/src/modules/auth/locales/auth-en.json +105 -0
- package/generators/app/templates/src/modules/auth/locales/auth-ja.json +105 -0
- package/generators/app/templates/src/modules/auth/schemas/form-schemas.ts +26 -0
- package/generators/app/templates/src/modules/landing/components/auth-hero.tsx +34 -0
- package/generators/app/templates/src/modules/landing/components/welcome-hero.tsx +24 -0
- package/generators/app/templates/src/modules/landing/landing-page-layout.tsx +24 -0
- package/generators/app/templates/src/modules/landing/landing-page.tsx +17 -0
- package/generators/app/templates/src/modules/landing/layouts/landing-page-layout.tsx +24 -0
- package/generators/app/templates/src/modules/landing/locales/landing-en.json +12 -0
- package/generators/app/templates/src/modules/landing/locales/landing-ja.json +11 -0
- package/generators/app/templates/src/openapi-client-config.ts +6 -0
- package/generators/app/templates/src/routeTree.gen.ts +268 -3
- package/generators/app/templates/src/router.tsx +2 -2
- package/generators/app/templates/src/routes/__root.tsx +2 -2
- package/generators/app/templates/src/routes/_landing/index.tsx +10 -0
- package/generators/app/templates/src/routes/_landing/route.tsx +14 -0
- package/generators/app/templates/src/routes/app/index.tsx +4 -21
- package/generators/app/templates/src/routes/app/route.tsx +12 -8
- package/generators/app/templates/src/routes/auth/forgot-password.tsx +10 -0
- package/generators/app/templates/src/routes/auth/login.tsx +6 -7
- package/generators/app/templates/src/routes/auth/reset-password.tsx +15 -0
- package/generators/app/templates/src/routes/auth/route.tsx +23 -6
- package/generators/app/templates/src/routes/auth/signup.tsx +11 -0
- package/generators/app/templates/src/sdk/@tanstack/react-query.gen.ts +91 -0
- package/generators/app/templates/src/sdk/client/client.gen.ts +167 -0
- package/generators/app/templates/src/sdk/client/index.ts +23 -0
- package/generators/app/templates/src/sdk/client/types.gen.ts +197 -0
- package/generators/app/templates/src/sdk/client/utils.gen.ts +213 -0
- package/generators/app/templates/src/sdk/client.gen.ts +18 -0
- package/generators/app/templates/src/sdk/core/auth.gen.ts +42 -0
- package/generators/app/templates/src/sdk/core/bodySerializer.gen.ts +100 -0
- package/generators/app/templates/src/sdk/core/params.gen.ts +176 -0
- package/generators/app/templates/src/sdk/core/pathSerializer.gen.ts +181 -0
- package/generators/app/templates/src/sdk/core/queryKeySerializer.gen.ts +136 -0
- package/generators/app/templates/src/sdk/core/serverSentEvents.gen.ts +266 -0
- package/generators/app/templates/src/sdk/core/types.gen.ts +118 -0
- package/generators/app/templates/src/sdk/core/utils.gen.ts +143 -0
- package/generators/app/templates/src/sdk/index.ts +4 -0
- package/generators/app/templates/src/sdk/schemas.gen.ts +195 -0
- package/generators/app/templates/src/sdk/sdk.gen.ts +80 -0
- package/generators/app/templates/src/sdk/types.gen.ts +158 -0
- package/generators/app/templates/src/sdk/zod.gen.ts +148 -0
- package/generators/app/templates/src/vite-env.d.ts +2 -1
- package/generators/app/templates/tsconfig.json +1 -1
- package/generators/app/templates/vite.config.js +35 -21
- package/generators/constants.js +1 -1
- package/package.json +3 -2
- package/generators/app/templates/.env.example +0 -5
- package/generators/app/templates/README.md +0 -62
- package/generators/app/templates/src/lib/api/client.ts +0 -35
- package/generators/app/templates/src/locales/en.json +0 -18
- package/generators/app/templates/src/modules/auth/auth-types.ts +0 -24
- package/generators/app/templates/src/modules/auth/login/login-form.tsx +0 -49
- package/generators/app/templates/src/modules/auth/login/login-page.tsx +0 -12
- package/generators/app/templates/src/modules/auth/use-auth-hook.ts +0 -88
- package/generators/app/templates/src/routes/index.tsx +0 -12
- package/generators/app/templates/types.d.ts +0 -3
|
@@ -1,10 +1,86 @@
|
|
|
1
|
+
# Dependencies
|
|
1
2
|
node_modules
|
|
2
|
-
.
|
|
3
|
+
.pnp
|
|
4
|
+
.pnp.js
|
|
5
|
+
.yarn/cache
|
|
6
|
+
.yarn/unplugged
|
|
7
|
+
.yarn/build-state.yml
|
|
8
|
+
.yarn/install-state.gz
|
|
9
|
+
|
|
10
|
+
# Build outputs
|
|
3
11
|
dist
|
|
4
12
|
dist-ssr
|
|
5
13
|
dist-electron
|
|
6
|
-
|
|
14
|
+
build
|
|
15
|
+
out
|
|
16
|
+
.next
|
|
17
|
+
.nuxt
|
|
18
|
+
.output
|
|
19
|
+
|
|
20
|
+
# Environment variables
|
|
7
21
|
.env
|
|
8
22
|
.env.local
|
|
23
|
+
.env.development.local
|
|
24
|
+
.env.test.local
|
|
25
|
+
.env.production.local
|
|
9
26
|
.env.*.local
|
|
27
|
+
*.local
|
|
28
|
+
|
|
29
|
+
# Logs
|
|
30
|
+
logs
|
|
31
|
+
*.log
|
|
32
|
+
npm-debug.log*
|
|
33
|
+
yarn-debug.log*
|
|
34
|
+
yarn-error.log*
|
|
35
|
+
pnpm-debug.log*
|
|
36
|
+
lerna-debug.log*
|
|
37
|
+
|
|
38
|
+
# OS files
|
|
39
|
+
.DS_Store
|
|
40
|
+
.DS_Store?
|
|
41
|
+
._*
|
|
42
|
+
.Spotlight-V100
|
|
43
|
+
.Trashes
|
|
44
|
+
ehthumbs.db
|
|
45
|
+
Thumbs.db
|
|
46
|
+
Desktop.ini
|
|
47
|
+
|
|
48
|
+
# IDE and editors
|
|
49
|
+
.vscode/*
|
|
50
|
+
!.vscode/extensions.json
|
|
51
|
+
!.vscode/settings.json
|
|
52
|
+
.idea
|
|
53
|
+
*.swp
|
|
54
|
+
*.swo
|
|
55
|
+
*~
|
|
56
|
+
.project
|
|
57
|
+
.classpath
|
|
58
|
+
.settings/
|
|
59
|
+
*.sublime-project
|
|
60
|
+
*.sublime-workspace
|
|
61
|
+
|
|
62
|
+
# Testing
|
|
63
|
+
coverage
|
|
64
|
+
*.lcov
|
|
65
|
+
.nyc_output
|
|
66
|
+
.jest
|
|
67
|
+
.cache
|
|
68
|
+
|
|
69
|
+
# TypeScript
|
|
70
|
+
*.tsbuildinfo
|
|
71
|
+
.tsbuildinfo
|
|
72
|
+
|
|
73
|
+
# Temporary files
|
|
74
|
+
*.tmp
|
|
75
|
+
*.temp
|
|
76
|
+
.cache
|
|
77
|
+
.parcel-cache
|
|
78
|
+
.turbo
|
|
79
|
+
|
|
80
|
+
# Package manager
|
|
81
|
+
.pnpm-store
|
|
10
82
|
|
|
83
|
+
# Misc
|
|
84
|
+
*.pem
|
|
85
|
+
.vite
|
|
86
|
+
.swc
|
|
@@ -4,15 +4,23 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta
|
|
6
6
|
name="viewport"
|
|
7
|
-
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
|
8
|
+
<meta
|
|
9
|
+
name="theme-color"
|
|
10
|
+
content="#000000" />
|
|
11
|
+
<meta
|
|
12
|
+
name="description"
|
|
13
|
+
content="<%= appNameTitleCase %>" />
|
|
14
|
+
<link
|
|
15
|
+
rel="icon"
|
|
16
|
+
type="image/svg+xml"
|
|
17
|
+
href="/favicon.svg" />
|
|
11
18
|
<title><%= appNameTitleCase %></title>
|
|
12
19
|
</head>
|
|
13
20
|
<body>
|
|
14
21
|
<div id="app"></div>
|
|
15
|
-
<script
|
|
22
|
+
<script
|
|
23
|
+
type="module"
|
|
24
|
+
src="/src/main.tsx"></script>
|
|
16
25
|
</body>
|
|
17
26
|
</html>
|
|
18
|
-
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { defineConfig } from '@hey-api/openapi-ts';
|
|
2
|
+
import { loadEnv } from 'vite';
|
|
3
|
+
|
|
4
|
+
// Get the mode from environment variable (set by npm scripts)
|
|
5
|
+
const mode = process.env.MODE || 'development';
|
|
6
|
+
|
|
7
|
+
// Load environment variables from .env files
|
|
8
|
+
const env = loadEnv(mode, process.cwd(), '');
|
|
9
|
+
|
|
10
|
+
export default defineConfig({
|
|
11
|
+
input: `${env.VITE_API_BASE_URL}/v3/api-docs`,
|
|
12
|
+
output: 'src/sdk',
|
|
13
|
+
plugins: [
|
|
14
|
+
{
|
|
15
|
+
name: '@hey-api/client-axios',
|
|
16
|
+
runtimeConfigPath: '@/openapi-client-config.ts',
|
|
17
|
+
},
|
|
18
|
+
'@tanstack/react-query',
|
|
19
|
+
'zod',
|
|
20
|
+
{
|
|
21
|
+
name: '@hey-api/sdk',
|
|
22
|
+
validator: true,
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
name: '@hey-api/schemas',
|
|
26
|
+
type: 'json',
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
});
|
|
@@ -1,47 +1,61 @@
|
|
|
1
1
|
{
|
|
2
|
-
"name": "
|
|
2
|
+
"name": "app",
|
|
3
3
|
"private": true,
|
|
4
4
|
"type": "module",
|
|
5
5
|
"version": "0.0.1",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"generate-routes": "tsr generate",
|
|
8
8
|
"watch-routes": "tsr watch",
|
|
9
|
-
"vite
|
|
9
|
+
"vite": "vite",
|
|
10
|
+
"vite:dev": "vite --mode dev",
|
|
11
|
+
"vite:sandbox": "vite --mode sandbox",
|
|
10
12
|
"dev": "npm-run-all --parallel watch-routes vite:dev",
|
|
11
|
-
"
|
|
12
|
-
"build": "npm run generate-routes && vite build && tsc",
|
|
13
|
-
"
|
|
13
|
+
"dev:sandbox": "npm-run-all --parallel watch-routes vite:sandbox",
|
|
14
|
+
"build": "npm run generate-routes && vite build --mode dev && tsc",
|
|
15
|
+
"build:sandbox": "npm run generate-routes && vite build --mode sandbox && tsc",
|
|
16
|
+
"serve": "vite preview",
|
|
17
|
+
"openapi-ts:dev": "MODE=dev openapi-ts",
|
|
18
|
+
"openapi-ts:sandbox": "MODE=sandbox openapi-ts"
|
|
14
19
|
},
|
|
15
20
|
"dependencies": {
|
|
21
|
+
"@hey-api/openapi-ts": "^0.90.2",
|
|
22
|
+
"@hookform/resolvers": "^5.2.2",
|
|
23
|
+
"@radix-ui/react-label": "^2.1.8",
|
|
24
|
+
"@radix-ui/react-select": "^2.2.6",
|
|
25
|
+
"@radix-ui/react-separator": "^1.1.8",
|
|
16
26
|
"@radix-ui/react-slot": "^1.2.4",
|
|
17
|
-
"@tanstack/react-query": "
|
|
18
|
-
"@tanstack/react-router": "
|
|
19
|
-
"@tanstack/router-plugin": "
|
|
20
|
-
"axios": "
|
|
27
|
+
"@tanstack/react-query": "^5.90.16",
|
|
28
|
+
"@tanstack/react-router": "^1.145.7",
|
|
29
|
+
"@tanstack/router-plugin": "^1.145.7",
|
|
30
|
+
"axios": "^1.13.2",
|
|
21
31
|
"class-variance-authority": "^0.7.1",
|
|
22
32
|
"clsx": "^2.1.1",
|
|
23
|
-
"dotenv": "
|
|
24
|
-
"i18next": "
|
|
25
|
-
"jotai": "
|
|
26
|
-
"lucide-react": "^0.
|
|
27
|
-
"react": "
|
|
28
|
-
"react-dom": "
|
|
29
|
-
"react-
|
|
33
|
+
"dotenv": "^17.2.3",
|
|
34
|
+
"i18next": "^25.7.3",
|
|
35
|
+
"jotai": "^2.16.1",
|
|
36
|
+
"lucide-react": "^0.562.0",
|
|
37
|
+
"react": "^19.2.3",
|
|
38
|
+
"react-dom": "^19.2.3",
|
|
39
|
+
"react-hook-form": "^7.70.0",
|
|
40
|
+
"react-i18next": "^16.5.1",
|
|
30
41
|
"sonner": "^2.0.7",
|
|
31
|
-
"tailwind-merge": "^3.4.0"
|
|
42
|
+
"tailwind-merge": "^3.4.0",
|
|
43
|
+
"tailwindcss-animate": "^1.0.7",
|
|
44
|
+
"tw-animate-css": "^1.4.0",
|
|
45
|
+
"zod": "^4.3.5"
|
|
32
46
|
},
|
|
33
47
|
"devDependencies": {
|
|
34
|
-
"@tailwindcss/postcss": "
|
|
35
|
-
"@tailwindcss/vite": "
|
|
36
|
-
"@tanstack/router-cli": "^1.
|
|
37
|
-
"@types/node": "^
|
|
48
|
+
"@tailwindcss/postcss": "^4.1.18",
|
|
49
|
+
"@tailwindcss/vite": "^4.1.18",
|
|
50
|
+
"@tanstack/router-cli": "^1.145.7",
|
|
51
|
+
"@types/node": "^25.0.3",
|
|
38
52
|
"@types/react": "^19.2.7",
|
|
39
53
|
"@types/react-dom": "^19.2.3",
|
|
40
|
-
"@vitejs/plugin-react": "^5.1.
|
|
41
|
-
"
|
|
54
|
+
"@vitejs/plugin-react": "^5.1.2",
|
|
55
|
+
"babel-plugin-react-compiler": "^1.0.0",
|
|
42
56
|
"npm-run-all": "^4.1.5",
|
|
43
|
-
"tailwindcss": "
|
|
44
|
-
"typescript": "
|
|
45
|
-
"vite": "
|
|
57
|
+
"tailwindcss": "^4.1.18",
|
|
58
|
+
"typescript": "^5.9.3",
|
|
59
|
+
"vite": "^7.3.1"
|
|
46
60
|
}
|
|
47
61
|
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
|
2
|
+
<rect width="32" height="32" rx="10" fill="#000000"/>
|
|
3
|
+
<path d="M 9 7 L 9 25 M 9 16 L 19 7 M 9 16 L 19 25" stroke="#ffffff" stroke-width="3.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
|
|
4
|
+
</svg>
|
|
@@ -3,11 +3,11 @@ import { useAtomValue } from 'jotai';
|
|
|
3
3
|
import { Loader2 } from 'lucide-react';
|
|
4
4
|
import { useEffect } from 'react';
|
|
5
5
|
import { Toaster } from 'sonner';
|
|
6
|
-
import '
|
|
7
|
-
import { AuthProvider } from '
|
|
8
|
-
import { authTokenAtom, currentUserDetailsAtom, useValidateToken,
|
|
9
|
-
import { router } from '
|
|
10
|
-
import { getLanguage } from '
|
|
6
|
+
import '@/index.css';
|
|
7
|
+
import { AuthProvider } from '@/modules/auth/contexts/auth-context';
|
|
8
|
+
import { authTokenAtom, currentUserDetailsAtom, useValidateToken, setLocaleInClient } from '@/modules/auth/hooks/use-auth-hook';
|
|
9
|
+
import { router } from '@/router';
|
|
10
|
+
import { getLanguage, SupportedLanguages } from '@/lib/i18n';
|
|
11
11
|
|
|
12
12
|
declare module '@tanstack/react-router' {
|
|
13
13
|
interface Register {
|
|
@@ -31,13 +31,13 @@ export default function App() {
|
|
|
31
31
|
const token = useAtomValue(authTokenAtom);
|
|
32
32
|
|
|
33
33
|
useEffect(() => {
|
|
34
|
-
const currentLanguage = getLanguage() as
|
|
35
|
-
|
|
34
|
+
const currentLanguage = getLanguage() as SupportedLanguages;
|
|
35
|
+
setLocaleInClient(currentLanguage);
|
|
36
36
|
}, []);
|
|
37
37
|
|
|
38
38
|
useEffect(() => {
|
|
39
39
|
if (token) {
|
|
40
|
-
mutate();
|
|
40
|
+
mutate({});
|
|
41
41
|
}
|
|
42
42
|
}, [token, mutate]);
|
|
43
43
|
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
|
2
|
+
import { getLanguage, changeLanguage } from '@/lib/i18n';
|
|
3
|
+
import { useTranslation } from 'react-i18next';
|
|
4
|
+
import { useEffect, useState } from 'react';
|
|
5
|
+
|
|
6
|
+
export function LanguageSwitcher() {
|
|
7
|
+
const { i18n } = useTranslation();
|
|
8
|
+
const [currentLang, setCurrentLang] = useState<'en' | 'ja'>(() => {
|
|
9
|
+
return (getLanguage() as 'en' | 'ja') || 'en';
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
const handleLanguageChange = (lng: string) => {
|
|
14
|
+
setCurrentLang(lng as 'en' | 'ja');
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
i18n.on('languageChanged', handleLanguageChange);
|
|
18
|
+
return () => {
|
|
19
|
+
i18n.off('languageChanged', handleLanguageChange);
|
|
20
|
+
};
|
|
21
|
+
}, [i18n]);
|
|
22
|
+
|
|
23
|
+
const handleLanguageChange = (value: string) => {
|
|
24
|
+
changeLanguage(value as 'en' | 'ja');
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<Select
|
|
29
|
+
value={currentLang}
|
|
30
|
+
onValueChange={handleLanguageChange}>
|
|
31
|
+
<SelectTrigger className='min-w-[120px]'>
|
|
32
|
+
<SelectValue />
|
|
33
|
+
</SelectTrigger>
|
|
34
|
+
<SelectContent>
|
|
35
|
+
<SelectItem value='en'>English</SelectItem>
|
|
36
|
+
<SelectItem value='ja'>日本語</SelectItem>
|
|
37
|
+
</SelectContent>
|
|
38
|
+
</Select>
|
|
39
|
+
);
|
|
40
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
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
|
+
|
|
5
|
+
export function ThemeSwitcher() {
|
|
6
|
+
const { theme, setTheme } = useTheme();
|
|
7
|
+
|
|
8
|
+
const cycleTheme = () => {
|
|
9
|
+
const themes: ThemeTypes[] = ['light', 'dark', 'system'];
|
|
10
|
+
const currentIndex = themes.indexOf(theme);
|
|
11
|
+
const nextIndex = (currentIndex + 1) % themes.length;
|
|
12
|
+
setTheme(themes[nextIndex]);
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const getIcon = () => {
|
|
16
|
+
switch (theme) {
|
|
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
|
+
default:
|
|
24
|
+
return <Monitor className='h-4 w-4' />;
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<Button
|
|
30
|
+
variant='outline'
|
|
31
|
+
size='icon'
|
|
32
|
+
onClick={cycleTheme}
|
|
33
|
+
aria-label='Toggle theme'>
|
|
34
|
+
{getIcon()}
|
|
35
|
+
</Button>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
@@ -1,45 +1,41 @@
|
|
|
1
|
-
import { createContext, useContext, useEffect, useState } from
|
|
1
|
+
import { createContext, useContext, useEffect, useState } from 'react';
|
|
2
2
|
|
|
3
|
-
type
|
|
3
|
+
export type ThemeTypes = 'dark' | 'light' | 'system';
|
|
4
4
|
|
|
5
5
|
type ThemeProviderProps = {
|
|
6
6
|
children: React.ReactNode;
|
|
7
|
-
defaultTheme?:
|
|
7
|
+
defaultTheme?: ThemeTypes;
|
|
8
8
|
storageKey?: string;
|
|
9
9
|
};
|
|
10
10
|
|
|
11
11
|
type ThemeProviderState = {
|
|
12
|
-
theme:
|
|
13
|
-
setTheme: (theme:
|
|
12
|
+
theme: ThemeTypes;
|
|
13
|
+
setTheme: (theme: ThemeTypes) => void;
|
|
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({
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
);
|
|
23
|
+
export function ThemeProvider({ children, defaultTheme = 'system', storageKey = 'vite-ui-theme', ...props }: ThemeProviderProps) {
|
|
24
|
+
const [theme, setTheme] = useState<ThemeTypes>(() => {
|
|
25
|
+
const stored = localStorage.getItem(storageKey);
|
|
26
|
+
if (stored && ['dark', 'light', 'system'].includes(stored)) {
|
|
27
|
+
return stored as ThemeTypes;
|
|
28
|
+
}
|
|
29
|
+
return 'system';
|
|
30
|
+
});
|
|
32
31
|
|
|
33
32
|
useEffect(() => {
|
|
34
33
|
const root = window.document.documentElement;
|
|
35
34
|
|
|
36
|
-
root.classList.remove(
|
|
35
|
+
root.classList.remove('light', 'dark');
|
|
37
36
|
|
|
38
|
-
if (theme ===
|
|
39
|
-
const systemTheme = window.matchMedia(
|
|
40
|
-
.matches
|
|
41
|
-
? "dark"
|
|
42
|
-
: "light";
|
|
37
|
+
if (theme === 'system') {
|
|
38
|
+
const systemTheme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
|
43
39
|
root.classList.add(systemTheme);
|
|
44
40
|
return;
|
|
45
41
|
}
|
|
@@ -49,14 +45,16 @@ export function ThemeProvider({
|
|
|
49
45
|
|
|
50
46
|
const value = {
|
|
51
47
|
theme,
|
|
52
|
-
setTheme: (theme:
|
|
48
|
+
setTheme: (theme: ThemeTypes) => {
|
|
53
49
|
localStorage.setItem(storageKey, theme);
|
|
54
50
|
setTheme(theme);
|
|
55
51
|
},
|
|
56
52
|
};
|
|
57
53
|
|
|
58
54
|
return (
|
|
59
|
-
<ThemeProviderContext.Provider
|
|
55
|
+
<ThemeProviderContext.Provider
|
|
56
|
+
{...props}
|
|
57
|
+
value={value}>
|
|
60
58
|
{children}
|
|
61
59
|
</ThemeProviderContext.Provider>
|
|
62
60
|
);
|
|
@@ -64,10 +62,6 @@ export function ThemeProvider({
|
|
|
64
62
|
|
|
65
63
|
export const useTheme = () => {
|
|
66
64
|
const context = useContext(ThemeProviderContext);
|
|
67
|
-
|
|
68
|
-
if (context === undefined)
|
|
69
|
-
throw new Error("useTheme must be used within a ThemeProvider");
|
|
70
|
-
|
|
65
|
+
if (context === undefined) throw new Error('useTheme must be used within a ThemeProvider');
|
|
71
66
|
return context;
|
|
72
67
|
};
|
|
73
|
-
|
|
@@ -1,23 +1,28 @@
|
|
|
1
|
-
import * as React from "react"
|
|
2
|
-
import { Slot } from "@radix-ui/react-slot"
|
|
3
|
-
import { cva, type VariantProps } from "class-variance-authority"
|
|
4
|
-
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { Slot } from "@radix-ui/react-slot"
|
|
3
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
4
|
+
|
|
5
|
+
import { cn } from "@/lib/utils"
|
|
5
6
|
|
|
6
7
|
const buttonVariants = cva(
|
|
7
|
-
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-
|
|
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",
|
|
8
9
|
{
|
|
9
10
|
variants: {
|
|
10
11
|
variant: {
|
|
11
|
-
default:
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
default:
|
|
13
|
+
"bg-primary text-primary-foreground shadow hover:bg-primary/90",
|
|
14
|
+
destructive:
|
|
15
|
+
"bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
|
|
16
|
+
outline:
|
|
17
|
+
"border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
|
|
18
|
+
secondary:
|
|
19
|
+
"bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
|
|
15
20
|
ghost: "hover:bg-accent hover:text-accent-foreground",
|
|
16
21
|
link: "text-primary underline-offset-4 hover:underline",
|
|
17
22
|
},
|
|
18
23
|
size: {
|
|
19
24
|
default: "h-9 px-4 py-2",
|
|
20
|
-
sm: "h-8 rounded-md px-3",
|
|
25
|
+
sm: "h-8 rounded-md px-3 text-xs",
|
|
21
26
|
lg: "h-10 rounded-md px-8",
|
|
22
27
|
icon: "h-9 w-9",
|
|
23
28
|
},
|
|
@@ -27,29 +32,26 @@ const buttonVariants = cva(
|
|
|
27
32
|
size: "default",
|
|
28
33
|
},
|
|
29
34
|
}
|
|
30
|
-
)
|
|
31
|
-
|
|
32
|
-
function Button({
|
|
33
|
-
className,
|
|
34
|
-
variant,
|
|
35
|
-
size,
|
|
36
|
-
asChild = false,
|
|
37
|
-
type,
|
|
38
|
-
...props
|
|
39
|
-
}: React.ComponentProps<"button"> &
|
|
40
|
-
VariantProps<typeof buttonVariants> & {
|
|
41
|
-
asChild?: boolean;
|
|
42
|
-
}) {
|
|
43
|
-
const Comp = asChild ? Slot : "button";
|
|
35
|
+
)
|
|
44
36
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
{...props}
|
|
50
|
-
/>
|
|
51
|
-
);
|
|
37
|
+
export interface ButtonProps
|
|
38
|
+
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
39
|
+
VariantProps<typeof buttonVariants> {
|
|
40
|
+
asChild?: boolean
|
|
52
41
|
}
|
|
53
42
|
|
|
54
|
-
|
|
43
|
+
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
44
|
+
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
45
|
+
const Comp = asChild ? Slot : "button"
|
|
46
|
+
return (
|
|
47
|
+
<Comp
|
|
48
|
+
className={cn(buttonVariants({ variant, size, className }))}
|
|
49
|
+
ref={ref}
|
|
50
|
+
{...props}
|
|
51
|
+
/>
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
)
|
|
55
|
+
Button.displayName = "Button"
|
|
55
56
|
|
|
57
|
+
export { Button, buttonVariants }
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
|
|
3
|
+
import { cn } from "@/lib/utils"
|
|
4
|
+
|
|
5
|
+
const Card = React.forwardRef<
|
|
6
|
+
HTMLDivElement,
|
|
7
|
+
React.HTMLAttributes<HTMLDivElement>
|
|
8
|
+
>(({ className, ...props }, ref) => (
|
|
9
|
+
<div
|
|
10
|
+
ref={ref}
|
|
11
|
+
className={cn(
|
|
12
|
+
"rounded-xl border bg-card text-card-foreground shadow",
|
|
13
|
+
className
|
|
14
|
+
)}
|
|
15
|
+
{...props}
|
|
16
|
+
/>
|
|
17
|
+
))
|
|
18
|
+
Card.displayName = "Card"
|
|
19
|
+
|
|
20
|
+
const CardHeader = React.forwardRef<
|
|
21
|
+
HTMLDivElement,
|
|
22
|
+
React.HTMLAttributes<HTMLDivElement>
|
|
23
|
+
>(({ className, ...props }, ref) => (
|
|
24
|
+
<div
|
|
25
|
+
ref={ref}
|
|
26
|
+
className={cn("flex flex-col space-y-1.5 p-6", className)}
|
|
27
|
+
{...props}
|
|
28
|
+
/>
|
|
29
|
+
))
|
|
30
|
+
CardHeader.displayName = "CardHeader"
|
|
31
|
+
|
|
32
|
+
const CardTitle = React.forwardRef<
|
|
33
|
+
HTMLDivElement,
|
|
34
|
+
React.HTMLAttributes<HTMLDivElement>
|
|
35
|
+
>(({ className, ...props }, ref) => (
|
|
36
|
+
<div
|
|
37
|
+
ref={ref}
|
|
38
|
+
className={cn("font-semibold leading-none tracking-tight", className)}
|
|
39
|
+
{...props}
|
|
40
|
+
/>
|
|
41
|
+
))
|
|
42
|
+
CardTitle.displayName = "CardTitle"
|
|
43
|
+
|
|
44
|
+
const CardDescription = React.forwardRef<
|
|
45
|
+
HTMLDivElement,
|
|
46
|
+
React.HTMLAttributes<HTMLDivElement>
|
|
47
|
+
>(({ className, ...props }, ref) => (
|
|
48
|
+
<div
|
|
49
|
+
ref={ref}
|
|
50
|
+
className={cn("text-sm text-muted-foreground", className)}
|
|
51
|
+
{...props}
|
|
52
|
+
/>
|
|
53
|
+
))
|
|
54
|
+
CardDescription.displayName = "CardDescription"
|
|
55
|
+
|
|
56
|
+
const CardContent = React.forwardRef<
|
|
57
|
+
HTMLDivElement,
|
|
58
|
+
React.HTMLAttributes<HTMLDivElement>
|
|
59
|
+
>(({ className, ...props }, ref) => (
|
|
60
|
+
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
|
|
61
|
+
))
|
|
62
|
+
CardContent.displayName = "CardContent"
|
|
63
|
+
|
|
64
|
+
const CardFooter = React.forwardRef<
|
|
65
|
+
HTMLDivElement,
|
|
66
|
+
React.HTMLAttributes<HTMLDivElement>
|
|
67
|
+
>(({ className, ...props }, ref) => (
|
|
68
|
+
<div
|
|
69
|
+
ref={ref}
|
|
70
|
+
className={cn("flex items-center p-6 pt-0", className)}
|
|
71
|
+
{...props}
|
|
72
|
+
/>
|
|
73
|
+
))
|
|
74
|
+
CardFooter.displayName = "CardFooter"
|
|
75
|
+
|
|
76
|
+
export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
|