create-reactivite 1.3.0 → 1.6.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 +326 -235
- package/index.js +157 -34
- package/package.json +5 -3
- package/template/package.json +22 -22
- package/template/pnpm-lock.yaml +3274 -3274
- package/template/src/components/author-credit.tsx +25 -25
- package/template2/.env.example +8 -8
- package/template2/.husky/pre-commit +4 -4
- package/template2/.prettierrc +5 -5
- package/template2/README.md +73 -73
- package/template2/__tests__/example.test.ts +20 -20
- package/template2/_gitignore +37 -37
- package/template2/app/[locale]/(private)/dashboard/page.tsx +52 -52
- package/template2/app/[locale]/(public)/login/page.tsx +83 -83
- package/template2/app/[locale]/layout.tsx +58 -58
- package/template2/app/[locale]/locales.ts +10 -10
- package/template2/app/[locale]/page.tsx +38 -38
- package/template2/app/api/clear-session/route.ts +10 -10
- package/template2/app/globals.css +127 -127
- package/template2/app/layout.tsx +7 -7
- package/template2/app/page.tsx +6 -6
- package/template2/components/AuthEventListener.tsx +22 -22
- package/template2/components/author-credit.tsx +25 -25
- package/template2/components/theme-provider.tsx +78 -78
- package/template2/components/ui/button.tsx +60 -60
- package/template2/components/ui/card.tsx +92 -92
- package/template2/components/ui/input.tsx +21 -21
- package/template2/components/ui/label.tsx +24 -24
- package/template2/components/ui/sonner.tsx +40 -40
- package/template2/components.json +22 -22
- package/template2/config/constants.ts +7 -7
- package/template2/config/env.ts +5 -5
- package/template2/contexts/translation-context.tsx +70 -70
- package/template2/eslint.config.mjs +18 -18
- package/template2/hoc/provider.tsx +27 -27
- package/template2/lib/paramsSerializer.ts +40 -40
- package/template2/lib/utils.ts +6 -6
- package/template2/locales/az.json +20 -20
- package/template2/locales/en.json +20 -20
- package/template2/next-env.d.ts +1 -1
- package/template2/next.config.ts +17 -17
- package/template2/orval.config.ts +66 -66
- package/template2/package.json +62 -62
- package/template2/postcss.config.mjs +7 -7
- package/template2/scripts/fix-generated-types.mjs +13 -13
- package/template2/services/generated/.gitkeep +2 -2
- package/template2/services/httpClient/httpClient.ts +70 -70
- package/template2/services/httpClient/orvalMutator.ts +10 -10
- package/template2/store/example-store.tsx +16 -16
- package/template2/store/user-store.tsx +29 -29
- package/template2/testing/msw/handlers/index.ts +6 -6
- package/template2/testing/msw/server.ts +4 -4
- package/template2/tsconfig.json +34 -34
- package/template2/vitest.config.ts +17 -17
- package/template2/vitest.setup.ts +7 -7
- package/template3/README.md +34 -34
- package/template3/_gitignore +16 -16
- package/template3/components.json +21 -0
- package/template3/index.html +8 -2
- package/template3/package-lock.json +3934 -0
- package/template3/package.json +48 -22
- package/template3/postcss.config.mjs +5 -0
- package/template3/rspack.config.mjs +59 -51
- package/template3/src/App.tsx +16 -11
- package/template3/src/components/author-credit.tsx +42 -42
- package/template3/src/components/layout.tsx +59 -0
- package/template3/src/components/matrix-rain.tsx +71 -0
- package/template3/src/components/ui/accordion.tsx +64 -0
- package/template3/src/components/ui/alert-dialog.tsx +196 -0
- package/template3/src/components/ui/alert.tsx +66 -0
- package/template3/src/components/ui/aspect-ratio.tsx +11 -0
- package/template3/src/components/ui/avatar.tsx +107 -0
- package/template3/src/components/ui/badge.tsx +48 -0
- package/template3/src/components/ui/breadcrumb.tsx +109 -0
- package/template3/src/components/ui/button-group.tsx +83 -0
- package/template3/src/components/ui/button.tsx +64 -0
- package/template3/src/components/ui/calendar.tsx +218 -0
- package/template3/src/components/ui/card.tsx +92 -0
- package/template3/src/components/ui/carousel.tsx +241 -0
- package/template3/src/components/ui/chart.tsx +372 -0
- package/template3/src/components/ui/checkbox.tsx +32 -0
- package/template3/src/components/ui/collapsible.tsx +31 -0
- package/template3/src/components/ui/combobox.tsx +310 -0
- package/template3/src/components/ui/command.tsx +184 -0
- package/template3/src/components/ui/context-menu.tsx +252 -0
- package/template3/src/components/ui/dialog.tsx +156 -0
- package/template3/src/components/ui/direction.tsx +22 -0
- package/template3/src/components/ui/drawer.tsx +133 -0
- package/template3/src/components/ui/dropdown-menu.tsx +257 -0
- package/template3/src/components/ui/empty.tsx +104 -0
- package/template3/src/components/ui/field.tsx +248 -0
- package/template3/src/components/ui/form.tsx +165 -0
- package/template3/src/components/ui/hover-card.tsx +42 -0
- package/template3/src/components/ui/input-group.tsx +168 -0
- package/template3/src/components/ui/input-otp.tsx +77 -0
- package/template3/src/components/ui/input.tsx +21 -0
- package/template3/src/components/ui/item.tsx +193 -0
- package/template3/src/components/ui/kbd.tsx +28 -0
- package/template3/src/components/ui/label.tsx +22 -0
- package/template3/src/components/ui/menubar.tsx +276 -0
- package/template3/src/components/ui/native-select.tsx +62 -0
- package/template3/src/components/ui/navigation-menu.tsx +168 -0
- package/template3/src/components/ui/pagination.tsx +127 -0
- package/template3/src/components/ui/popover.tsx +87 -0
- package/template3/src/components/ui/progress.tsx +31 -0
- package/template3/src/components/ui/radio-group.tsx +43 -0
- package/template3/src/components/ui/resizable.tsx +53 -0
- package/template3/src/components/ui/scroll-area.tsx +56 -0
- package/template3/src/components/ui/select.tsx +190 -0
- package/template3/src/components/ui/separator.tsx +26 -0
- package/template3/src/components/ui/sheet.tsx +143 -0
- package/template3/src/components/ui/sidebar.tsx +724 -0
- package/template3/src/components/ui/skeleton.tsx +13 -0
- package/template3/src/components/ui/slider.tsx +61 -0
- package/template3/src/components/ui/sonner.tsx +40 -0
- package/template3/src/components/ui/spinner.tsx +16 -0
- package/template3/src/components/ui/switch.tsx +33 -0
- package/template3/src/components/ui/table.tsx +116 -0
- package/template3/src/components/ui/tabs.tsx +89 -0
- package/template3/src/components/ui/textarea.tsx +18 -0
- package/template3/src/components/ui/toggle-group.tsx +83 -0
- package/template3/src/components/ui/toggle.tsx +47 -0
- package/template3/src/components/ui/tooltip.tsx +55 -0
- package/template3/src/hooks/use-mobile.ts +19 -0
- package/template3/src/index.css +175 -32
- package/template3/src/lib/utils.ts +6 -0
- package/template3/src/main.tsx +10 -10
- package/template3/src/pages/about.tsx +113 -0
- package/template3/src/pages/contact.tsx +111 -0
- package/template3/src/pages/home.tsx +81 -0
- package/template3/tsconfig.json +24 -20
- package/template2/tsconfig.tsbuildinfo +0 -1
package/template3/package.json
CHANGED
|
@@ -1,22 +1,48 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "rspack-simple",
|
|
3
|
-
"private": true,
|
|
4
|
-
"version": "0.1.0",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"scripts": {
|
|
7
|
-
"dev": "rspack serve",
|
|
8
|
-
"build": "rspack build",
|
|
9
|
-
"preview": "rspack serve --mode production"
|
|
10
|
-
},
|
|
11
|
-
"dependencies": {
|
|
12
|
-
"react": "^
|
|
13
|
-
"
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
|
|
22
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "rspack-simple",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "rspack serve",
|
|
8
|
+
"build": "rspack build",
|
|
9
|
+
"preview": "rspack serve --mode production"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"@base-ui/react": "^1.6.0",
|
|
13
|
+
"@hookform/resolvers": "^5.4.0",
|
|
14
|
+
"class-variance-authority": "^0.7.1",
|
|
15
|
+
"clsx": "^2.1.1",
|
|
16
|
+
"cmdk": "^1.1.1",
|
|
17
|
+
"date-fns": "^4.4.0",
|
|
18
|
+
"embla-carousel-react": "^8.6.0",
|
|
19
|
+
"input-otp": "^1.4.2",
|
|
20
|
+
"lucide-react": "^1.21.0",
|
|
21
|
+
"next-themes": "^0.4.6",
|
|
22
|
+
"radix-ui": "^1.6.0",
|
|
23
|
+
"react": "^19.2.7",
|
|
24
|
+
"react-day-picker": "^10.0.1",
|
|
25
|
+
"react-dom": "^19.2.7",
|
|
26
|
+
"react-hook-form": "^7.80.0",
|
|
27
|
+
"react-resizable-panels": "^4.11.2",
|
|
28
|
+
"react-router-dom": "^7.18.0",
|
|
29
|
+
"recharts": "^3.8.0",
|
|
30
|
+
"sonner": "^2.0.7",
|
|
31
|
+
"tailwind-merge": "^3.6.0",
|
|
32
|
+
"tw-animate-css": "^1.4.0",
|
|
33
|
+
"vaul": "^1.1.2",
|
|
34
|
+
"zod": "^4.4.3"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@rspack/cli": "^2.0.8",
|
|
38
|
+
"@rspack/core": "^2.0.8",
|
|
39
|
+
"@rspack/dev-server": "^2.1.0",
|
|
40
|
+
"@tailwindcss/postcss": "^4.3.1",
|
|
41
|
+
"@types/react": "^19",
|
|
42
|
+
"@types/react-dom": "^19",
|
|
43
|
+
"postcss": "^8.5.15",
|
|
44
|
+
"postcss-loader": "^8.2.1",
|
|
45
|
+
"tailwindcss": "^4.3.1",
|
|
46
|
+
"typescript": "^6.0.3"
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -1,51 +1,59 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
},
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
1
|
+
import { fileURLToPath } from 'node:url';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { defineConfig } from '@rspack/cli';
|
|
4
|
+
import { rspack } from '@rspack/core';
|
|
5
|
+
|
|
6
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
|
|
8
|
+
export default defineConfig({
|
|
9
|
+
entry: {
|
|
10
|
+
main: './src/main.tsx',
|
|
11
|
+
},
|
|
12
|
+
resolve: {
|
|
13
|
+
extensions: ['.ts', '.tsx', '.js', '.jsx'],
|
|
14
|
+
alias: {
|
|
15
|
+
'@': path.resolve(__dirname, 'src'),
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
module: {
|
|
19
|
+
rules: [
|
|
20
|
+
{
|
|
21
|
+
test: /\.css$/,
|
|
22
|
+
type: 'css',
|
|
23
|
+
use: ['postcss-loader'],
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
test: /\.(jsx?|tsx?)$/,
|
|
27
|
+
use: {
|
|
28
|
+
loader: 'builtin:swc-loader',
|
|
29
|
+
options: {
|
|
30
|
+
jsc: {
|
|
31
|
+
parser: {
|
|
32
|
+
syntax: 'typescript',
|
|
33
|
+
tsx: true,
|
|
34
|
+
},
|
|
35
|
+
transform: {
|
|
36
|
+
react: {
|
|
37
|
+
runtime: 'automatic',
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
type: 'javascript/auto',
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
},
|
|
47
|
+
plugins: [
|
|
48
|
+
new rspack.HtmlRspackPlugin({
|
|
49
|
+
template: './index.html',
|
|
50
|
+
}),
|
|
51
|
+
],
|
|
52
|
+
experiments: {
|
|
53
|
+
css: true,
|
|
54
|
+
},
|
|
55
|
+
devServer: {
|
|
56
|
+
port: 5174,
|
|
57
|
+
historyApiFallback: true,
|
|
58
|
+
},
|
|
59
|
+
});
|
package/template3/src/App.tsx
CHANGED
|
@@ -1,16 +1,21 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { BrowserRouter, Route, Routes } from 'react-router-dom';
|
|
2
|
+
import { Layout } from './components/layout';
|
|
3
|
+
import { Toaster } from '@/components/ui/sonner';
|
|
4
|
+
import Home from './pages/home';
|
|
5
|
+
import About from './pages/about';
|
|
6
|
+
import Contact from './pages/contact';
|
|
2
7
|
|
|
3
8
|
export default function App() {
|
|
4
9
|
return (
|
|
5
|
-
|
|
6
|
-
<
|
|
7
|
-
<
|
|
8
|
-
<
|
|
9
|
-
<
|
|
10
|
-
<
|
|
11
|
-
</
|
|
12
|
-
</
|
|
13
|
-
<
|
|
14
|
-
|
|
10
|
+
<BrowserRouter>
|
|
11
|
+
<Routes>
|
|
12
|
+
<Route element={<Layout />}>
|
|
13
|
+
<Route index element={<Home />} />
|
|
14
|
+
<Route path="about" element={<About />} />
|
|
15
|
+
<Route path="contact" element={<Contact />} />
|
|
16
|
+
</Route>
|
|
17
|
+
</Routes>
|
|
18
|
+
<Toaster position="bottom-center" theme="dark" />
|
|
19
|
+
</BrowserRouter>
|
|
15
20
|
);
|
|
16
21
|
}
|
|
@@ -1,42 +1,42 @@
|
|
|
1
|
-
const links = [
|
|
2
|
-
{ label: 'GitHub', href: 'https://github.com/javidselimov' },
|
|
3
|
-
{ label: 'LinkedIn', href: 'https://www.linkedin.com/in/javidsalim/' },
|
|
4
|
-
{ label: 'npm', href: 'https://www.npmjs.com/~ubuligan' },
|
|
5
|
-
];
|
|
6
|
-
|
|
7
|
-
export function AuthorCredit() {
|
|
8
|
-
return (
|
|
9
|
-
<div
|
|
10
|
-
style={{
|
|
11
|
-
position: 'fixed',
|
|
12
|
-
bottom: 16,
|
|
13
|
-
right: 16,
|
|
14
|
-
zIndex: 50,
|
|
15
|
-
display: 'flex',
|
|
16
|
-
alignItems: 'center',
|
|
17
|
-
gap: 12,
|
|
18
|
-
padding: '8px 16px',
|
|
19
|
-
borderRadius: 9999,
|
|
20
|
-
border: '1px solid #2a2f3a',
|
|
21
|
-
background: 'rgba(13, 16, 22, 0.8)',
|
|
22
|
-
backdropFilter: 'blur(8px)',
|
|
23
|
-
fontSize: 13,
|
|
24
|
-
color: '#e7eaf0',
|
|
25
|
-
}}
|
|
26
|
-
>
|
|
27
|
-
<strong>Javid Salimov</strong>
|
|
28
|
-
<span style={{ color: '#3a3f4a' }}>·</span>
|
|
29
|
-
{links.map((l) => (
|
|
30
|
-
<a
|
|
31
|
-
key={l.label}
|
|
32
|
-
href={l.href}
|
|
33
|
-
target="_blank"
|
|
34
|
-
rel="noreferrer"
|
|
35
|
-
style={{ color: '#8b93a7', textDecoration: 'none' }}
|
|
36
|
-
>
|
|
37
|
-
{l.label}
|
|
38
|
-
</a>
|
|
39
|
-
))}
|
|
40
|
-
</div>
|
|
41
|
-
);
|
|
42
|
-
}
|
|
1
|
+
const links = [
|
|
2
|
+
{ label: 'GitHub', href: 'https://github.com/javidselimov' },
|
|
3
|
+
{ label: 'LinkedIn', href: 'https://www.linkedin.com/in/javidsalim/' },
|
|
4
|
+
{ label: 'npm', href: 'https://www.npmjs.com/~ubuligan' },
|
|
5
|
+
];
|
|
6
|
+
|
|
7
|
+
export function AuthorCredit() {
|
|
8
|
+
return (
|
|
9
|
+
<div
|
|
10
|
+
style={{
|
|
11
|
+
position: 'fixed',
|
|
12
|
+
bottom: 16,
|
|
13
|
+
right: 16,
|
|
14
|
+
zIndex: 50,
|
|
15
|
+
display: 'flex',
|
|
16
|
+
alignItems: 'center',
|
|
17
|
+
gap: 12,
|
|
18
|
+
padding: '8px 16px',
|
|
19
|
+
borderRadius: 9999,
|
|
20
|
+
border: '1px solid #2a2f3a',
|
|
21
|
+
background: 'rgba(13, 16, 22, 0.8)',
|
|
22
|
+
backdropFilter: 'blur(8px)',
|
|
23
|
+
fontSize: 13,
|
|
24
|
+
color: '#e7eaf0',
|
|
25
|
+
}}
|
|
26
|
+
>
|
|
27
|
+
<strong>Javid Salimov</strong>
|
|
28
|
+
<span style={{ color: '#3a3f4a' }}>·</span>
|
|
29
|
+
{links.map((l) => (
|
|
30
|
+
<a
|
|
31
|
+
key={l.label}
|
|
32
|
+
href={l.href}
|
|
33
|
+
target="_blank"
|
|
34
|
+
rel="noreferrer"
|
|
35
|
+
style={{ color: '#8b93a7', textDecoration: 'none' }}
|
|
36
|
+
>
|
|
37
|
+
{l.label}
|
|
38
|
+
</a>
|
|
39
|
+
))}
|
|
40
|
+
</div>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { NavLink, Outlet, useLocation } from 'react-router-dom';
|
|
2
|
+
import { MatrixRain } from './matrix-rain';
|
|
3
|
+
import { AuthorCredit } from './author-credit';
|
|
4
|
+
import { cn } from '@/lib/utils';
|
|
5
|
+
|
|
6
|
+
const NAV = [
|
|
7
|
+
{ to: '/', label: '~/home', end: true },
|
|
8
|
+
{ to: '/about', label: '~/about' },
|
|
9
|
+
{ to: '/contact', label: '~/contact' },
|
|
10
|
+
];
|
|
11
|
+
|
|
12
|
+
export function Layout() {
|
|
13
|
+
const { pathname } = useLocation();
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<div className="scanlines relative min-h-screen overflow-x-hidden">
|
|
17
|
+
<MatrixRain opacity={0.35} />
|
|
18
|
+
|
|
19
|
+
{/* top terminal bar */}
|
|
20
|
+
<header className="sticky top-0 z-20 border-b border-primary/20 bg-background/70 backdrop-blur-sm">
|
|
21
|
+
<div className="mx-auto flex max-w-5xl items-center justify-between px-5 py-3">
|
|
22
|
+
<NavLink to="/" className="font-display text-sm font-bold tracking-[0.25em] text-primary text-glow">
|
|
23
|
+
J.SALIMOV<span className="animate-pulse">_</span>
|
|
24
|
+
</NavLink>
|
|
25
|
+
<nav className="flex items-center gap-1 text-xs sm:gap-2 sm:text-sm">
|
|
26
|
+
{NAV.map((item) => (
|
|
27
|
+
<NavLink
|
|
28
|
+
key={item.to}
|
|
29
|
+
to={item.to}
|
|
30
|
+
end={item.end}
|
|
31
|
+
className={({ isActive }) =>
|
|
32
|
+
cn(
|
|
33
|
+
'rounded px-2 py-1 transition-colors duration-150 hover:text-primary hover:text-glow',
|
|
34
|
+
isActive
|
|
35
|
+
? 'text-primary text-glow'
|
|
36
|
+
: 'text-muted-foreground',
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
>
|
|
40
|
+
{item.label}
|
|
41
|
+
</NavLink>
|
|
42
|
+
))}
|
|
43
|
+
</nav>
|
|
44
|
+
</div>
|
|
45
|
+
</header>
|
|
46
|
+
|
|
47
|
+
<main key={pathname} className="relative z-10 mx-auto max-w-5xl px-5 py-10 sm:py-16">
|
|
48
|
+
<Outlet />
|
|
49
|
+
</main>
|
|
50
|
+
|
|
51
|
+
<footer className="relative z-10 border-t border-primary/15 py-6 text-center text-xs text-muted-foreground">
|
|
52
|
+
<span className="text-primary/70">SYS:</span> connection stable —
|
|
53
|
+
decoded by Rspack + shadcn/ui
|
|
54
|
+
</footer>
|
|
55
|
+
|
|
56
|
+
<AuthorCredit />
|
|
57
|
+
</div>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { useEffect, useRef } from 'react';
|
|
2
|
+
|
|
3
|
+
const GLYPHS =
|
|
4
|
+
'アイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホ0123456789:.=*+-<>¦JAVIDSALIMOV';
|
|
5
|
+
|
|
6
|
+
export function MatrixRain({ opacity = 0.4 }: { opacity?: number }) {
|
|
7
|
+
const ref = useRef<HTMLCanvasElement>(null);
|
|
8
|
+
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
const canvas = ref.current;
|
|
11
|
+
if (!canvas) return;
|
|
12
|
+
const ctx = canvas.getContext('2d');
|
|
13
|
+
if (!ctx) return;
|
|
14
|
+
|
|
15
|
+
let raf = 0;
|
|
16
|
+
let cols = 0;
|
|
17
|
+
let drops: number[] = [];
|
|
18
|
+
const fontSize = 16;
|
|
19
|
+
|
|
20
|
+
const resize = () => {
|
|
21
|
+
canvas.width = canvas.offsetWidth * devicePixelRatio;
|
|
22
|
+
canvas.height = canvas.offsetHeight * devicePixelRatio;
|
|
23
|
+
ctx.scale(devicePixelRatio, devicePixelRatio);
|
|
24
|
+
cols = Math.floor(canvas.offsetWidth / fontSize);
|
|
25
|
+
drops = Array.from({ length: cols }, () =>
|
|
26
|
+
Math.floor((Math.random() * canvas.offsetHeight) / fontSize),
|
|
27
|
+
);
|
|
28
|
+
};
|
|
29
|
+
resize();
|
|
30
|
+
|
|
31
|
+
let frame = 0;
|
|
32
|
+
const draw = () => {
|
|
33
|
+
frame++;
|
|
34
|
+
// throttle to ~20fps for that staggered glyph cadence
|
|
35
|
+
if (frame % 3 === 0) {
|
|
36
|
+
ctx.fillStyle = 'rgba(4, 7, 10, 0.08)';
|
|
37
|
+
ctx.fillRect(0, 0, canvas.offsetWidth, canvas.offsetHeight);
|
|
38
|
+
ctx.font = `${fontSize}px 'Share Tech Mono', monospace`;
|
|
39
|
+
|
|
40
|
+
for (let i = 0; i < drops.length; i++) {
|
|
41
|
+
const char = GLYPHS[Math.floor(Math.random() * GLYPHS.length)];
|
|
42
|
+
const x = i * fontSize;
|
|
43
|
+
const y = drops[i] * fontSize;
|
|
44
|
+
// lead glyph bright, trail dim
|
|
45
|
+
ctx.fillStyle = Math.random() > 0.975 ? '#d5ffe4' : '#1fd65f';
|
|
46
|
+
ctx.fillText(char, x, y);
|
|
47
|
+
|
|
48
|
+
if (y > canvas.offsetHeight && Math.random() > 0.975) drops[i] = 0;
|
|
49
|
+
drops[i]++;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
raf = requestAnimationFrame(draw);
|
|
53
|
+
};
|
|
54
|
+
draw();
|
|
55
|
+
|
|
56
|
+
window.addEventListener('resize', resize);
|
|
57
|
+
return () => {
|
|
58
|
+
cancelAnimationFrame(raf);
|
|
59
|
+
window.removeEventListener('resize', resize);
|
|
60
|
+
};
|
|
61
|
+
}, []);
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<canvas
|
|
65
|
+
ref={ref}
|
|
66
|
+
aria-hidden
|
|
67
|
+
className="pointer-events-none fixed inset-0 h-full w-full"
|
|
68
|
+
style={{ opacity }}
|
|
69
|
+
/>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { ChevronDownIcon } from "lucide-react"
|
|
3
|
+
import { Accordion as AccordionPrimitive } from "radix-ui"
|
|
4
|
+
|
|
5
|
+
import { cn } from "@/lib/utils"
|
|
6
|
+
|
|
7
|
+
function Accordion({
|
|
8
|
+
...props
|
|
9
|
+
}: React.ComponentProps<typeof AccordionPrimitive.Root>) {
|
|
10
|
+
return <AccordionPrimitive.Root data-slot="accordion" {...props} />
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function AccordionItem({
|
|
14
|
+
className,
|
|
15
|
+
...props
|
|
16
|
+
}: React.ComponentProps<typeof AccordionPrimitive.Item>) {
|
|
17
|
+
return (
|
|
18
|
+
<AccordionPrimitive.Item
|
|
19
|
+
data-slot="accordion-item"
|
|
20
|
+
className={cn("border-b last:border-b-0", className)}
|
|
21
|
+
{...props}
|
|
22
|
+
/>
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function AccordionTrigger({
|
|
27
|
+
className,
|
|
28
|
+
children,
|
|
29
|
+
...props
|
|
30
|
+
}: React.ComponentProps<typeof AccordionPrimitive.Trigger>) {
|
|
31
|
+
return (
|
|
32
|
+
<AccordionPrimitive.Header className="flex">
|
|
33
|
+
<AccordionPrimitive.Trigger
|
|
34
|
+
data-slot="accordion-trigger"
|
|
35
|
+
className={cn(
|
|
36
|
+
"flex flex-1 items-start justify-between gap-4 rounded-md py-4 text-left text-sm font-medium transition-all outline-none hover:underline focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 [&[data-state=open]>svg]:rotate-180",
|
|
37
|
+
className
|
|
38
|
+
)}
|
|
39
|
+
{...props}
|
|
40
|
+
>
|
|
41
|
+
{children}
|
|
42
|
+
<ChevronDownIcon className="pointer-events-none size-4 shrink-0 translate-y-0.5 text-muted-foreground transition-transform duration-200" />
|
|
43
|
+
</AccordionPrimitive.Trigger>
|
|
44
|
+
</AccordionPrimitive.Header>
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function AccordionContent({
|
|
49
|
+
className,
|
|
50
|
+
children,
|
|
51
|
+
...props
|
|
52
|
+
}: React.ComponentProps<typeof AccordionPrimitive.Content>) {
|
|
53
|
+
return (
|
|
54
|
+
<AccordionPrimitive.Content
|
|
55
|
+
data-slot="accordion-content"
|
|
56
|
+
className="overflow-hidden text-sm data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
|
|
57
|
+
{...props}
|
|
58
|
+
>
|
|
59
|
+
<div className={cn("pt-0 pb-4", className)}>{children}</div>
|
|
60
|
+
</AccordionPrimitive.Content>
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }
|