saas-forge 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +10 -0
- package/.vscode/settings.json +3 -0
- package/README.md +31 -0
- package/apps/web/.env.example +1 -0
- package/apps/web/app/favicon.ico +0 -0
- package/apps/web/app/layout.tsx +23 -0
- package/apps/web/app/page.tsx +12 -0
- package/apps/web/components.json +20 -0
- package/apps/web/eslint.config.js +4 -0
- package/apps/web/hooks/.gitkeep +0 -0
- package/apps/web/next-env.d.ts +5 -0
- package/apps/web/next.config.mjs +6 -0
- package/apps/web/package.json +32 -0
- package/apps/web/postcss.config.mjs +1 -0
- package/apps/web/tsconfig.json +23 -0
- package/docs/CODE_OF_CONDUCT.md +128 -0
- package/docs/CONTRIBUTING.md +45 -0
- package/docs/ISSUE_TEMPLATE/bug_report.md +31 -0
- package/docs/ISSUE_TEMPLATE/feature_request.md +17 -0
- package/docs/SECURITY.md +9 -0
- package/docs/pull_request_template.md +24 -0
- package/package.json +38 -0
- package/packages/eslint-config/README.md +3 -0
- package/packages/eslint-config/base.js +32 -0
- package/packages/eslint-config/next.js +51 -0
- package/packages/eslint-config/package.json +26 -0
- package/packages/eslint-config/react-internal.js +41 -0
- package/packages/typescript-config/README.md +3 -0
- package/packages/typescript-config/base.json +20 -0
- package/packages/typescript-config/nextjs.json +13 -0
- package/packages/typescript-config/package.json +9 -0
- package/packages/typescript-config/react-library.json +8 -0
- package/packages/ui/components.json +20 -0
- package/packages/ui/eslint.config.js +4 -0
- package/packages/ui/package.json +86 -0
- package/packages/ui/postcss.config.mjs +6 -0
- package/packages/ui/src/components/.gitkeep +0 -0
- package/packages/ui/src/components/aceternity/3d-card.tsx +155 -0
- package/packages/ui/src/components/aceternity/3d-marquee.tsx +142 -0
- package/packages/ui/src/components/aceternity/Spotlight.tsx +57 -0
- package/packages/ui/src/components/aceternity/animated-testimonials.tsx +165 -0
- package/packages/ui/src/components/aceternity/animated-tooltip.tsx +92 -0
- package/packages/ui/src/components/aceternity/aurora-background.tsx +61 -0
- package/packages/ui/src/components/aceternity/background-beams.tsx +141 -0
- package/packages/ui/src/components/aceternity/background-gradient.tsx +72 -0
- package/packages/ui/src/components/aceternity/card-hover-effect.tsx +111 -0
- package/packages/ui/src/components/aceternity/compare.tsx +244 -0
- package/packages/ui/src/components/aceternity/container-scroll-animation.tsx +95 -0
- package/packages/ui/src/components/aceternity/lamp.tsx +104 -0
- package/packages/ui/src/components/aceternity/sparkles.tsx +434 -0
- package/packages/ui/src/components/aceternity/spotlight-new.tsx +128 -0
- package/packages/ui/src/components/mdx/Alert.tsx +14 -0
- package/packages/ui/src/components/mdx/Badge.tsx +10 -0
- package/packages/ui/src/components/mdx/blockquote.tsx +11 -0
- package/packages/ui/src/components/mdx/code.tsx +16 -0
- package/packages/ui/src/components/mdx/h1.tsx +18 -0
- package/packages/ui/src/components/mdx/h2.tsx +18 -0
- package/packages/ui/src/components/mdx/h3.tsx +18 -0
- package/packages/ui/src/components/mdx/hr.tsx +7 -0
- package/packages/ui/src/components/mdx/li.tsx +7 -0
- package/packages/ui/src/components/mdx/mdxComponents.tsx +25 -0
- package/packages/ui/src/components/mdx/ol.tsx +7 -0
- package/packages/ui/src/components/mdx/pre.tsx +13 -0
- package/packages/ui/src/components/mdx/tableOfContents.tsx +23 -0
- package/packages/ui/src/components/mdx/ul.tsx +7 -0
- package/packages/ui/src/components/mdx/ulNumbered.tsx +7 -0
- package/packages/ui/src/components/shadcn/accordion.tsx +66 -0
- package/packages/ui/src/components/shadcn/alert-dialog.tsx +157 -0
- package/packages/ui/src/components/shadcn/alert.tsx +66 -0
- package/packages/ui/src/components/shadcn/aspect-ratio.tsx +11 -0
- package/packages/ui/src/components/shadcn/avatar.tsx +53 -0
- package/packages/ui/src/components/shadcn/badge.tsx +46 -0
- package/packages/ui/src/components/shadcn/breadcrumb.tsx +109 -0
- package/packages/ui/src/components/shadcn/button-group.tsx +83 -0
- package/packages/ui/src/components/shadcn/button.tsx +60 -0
- package/packages/ui/src/components/shadcn/calendar.tsx +216 -0
- package/packages/ui/src/components/shadcn/card.tsx +92 -0
- package/packages/ui/src/components/shadcn/carousel.tsx +241 -0
- package/packages/ui/src/components/shadcn/chart.tsx +357 -0
- package/packages/ui/src/components/shadcn/checkbox.tsx +32 -0
- package/packages/ui/src/components/shadcn/collapsible.tsx +33 -0
- package/packages/ui/src/components/shadcn/command.tsx +184 -0
- package/packages/ui/src/components/shadcn/context-menu.tsx +252 -0
- package/packages/ui/src/components/shadcn/dialog.tsx +143 -0
- package/packages/ui/src/components/shadcn/drawer.tsx +135 -0
- package/packages/ui/src/components/shadcn/dropdown-menu.tsx +257 -0
- package/packages/ui/src/components/shadcn/empty.tsx +104 -0
- package/packages/ui/src/components/shadcn/field.tsx +248 -0
- package/packages/ui/src/components/shadcn/form.tsx +167 -0
- package/packages/ui/src/components/shadcn/hover-card.tsx +44 -0
- package/packages/ui/src/components/shadcn/input-group.tsx +170 -0
- package/packages/ui/src/components/shadcn/input-otp.tsx +77 -0
- package/packages/ui/src/components/shadcn/input.tsx +21 -0
- package/packages/ui/src/components/shadcn/item.tsx +193 -0
- package/packages/ui/src/components/shadcn/kbd.tsx +28 -0
- package/packages/ui/src/components/shadcn/label.tsx +24 -0
- package/packages/ui/src/components/shadcn/menubar.tsx +276 -0
- package/packages/ui/src/components/shadcn/native-select.tsx +48 -0
- package/packages/ui/src/components/shadcn/navigation-menu.tsx +168 -0
- package/packages/ui/src/components/shadcn/pagination.tsx +127 -0
- package/packages/ui/src/components/shadcn/popover.tsx +48 -0
- package/packages/ui/src/components/shadcn/progress.tsx +31 -0
- package/packages/ui/src/components/shadcn/radio-group.tsx +45 -0
- package/packages/ui/src/components/shadcn/resizable.tsx +56 -0
- package/packages/ui/src/components/shadcn/scroll-area.tsx +58 -0
- package/packages/ui/src/components/shadcn/select.tsx +187 -0
- package/packages/ui/src/components/shadcn/separator.tsx +28 -0
- package/packages/ui/src/components/shadcn/sheet.tsx +139 -0
- package/packages/ui/src/components/shadcn/sidebar.tsx +726 -0
- package/packages/ui/src/components/shadcn/skeleton.tsx +13 -0
- package/packages/ui/src/components/shadcn/slider.tsx +63 -0
- package/packages/ui/src/components/shadcn/sonner.tsx +40 -0
- package/packages/ui/src/components/shadcn/spinner.tsx +16 -0
- package/packages/ui/src/components/shadcn/switch.tsx +31 -0
- package/packages/ui/src/components/shadcn/table.tsx +116 -0
- package/packages/ui/src/components/shadcn/tabs.tsx +66 -0
- package/packages/ui/src/components/shadcn/textarea.tsx +18 -0
- package/packages/ui/src/components/shadcn/toggle-group.tsx +83 -0
- package/packages/ui/src/components/shadcn/toggle.tsx +47 -0
- package/packages/ui/src/components/shadcn/tooltip.tsx +61 -0
- package/packages/ui/src/hooks/.gitkeep +0 -0
- package/packages/ui/src/hooks/use-device.tsx +29 -0
- package/packages/ui/src/hooks/use-mobile.tsx +19 -0
- package/packages/ui/src/hooks/use-outside-click.tsx +23 -0
- package/packages/ui/src/lib/utils.ts +6 -0
- package/packages/ui/src/providers/theme-provider.tsx +18 -0
- package/packages/ui/src/styles/globals.css +65 -0
- package/packages/ui/src/styles/shadcn-blue.css +69 -0
- package/packages/ui/src/styles/shadcn-green.css +68 -0
- package/packages/ui/src/styles/shadcn-neutral.css +69 -0
- package/packages/ui/src/styles/shadcn-orange.css +68 -0
- package/packages/ui/src/styles/shadcn-red.css +68 -0
- package/packages/ui/src/styles/shadcn-rose.css +68 -0
- package/packages/ui/src/styles/shadcn-violet.css +68 -0
- package/packages/ui/src/styles/shadcn-yellow.css +68 -0
- package/packages/ui/src/typography/font.tsx +19 -0
- package/packages/ui/src/typography/fontFiles/Cyberdyne.woff2 +0 -0
- package/packages/ui/src/typography/fontFiles/GeistMonoVF.woff +0 -0
- package/packages/ui/src/typography/fontFiles/GeistVF.woff +0 -0
- package/packages/ui/tsconfig.json +11 -0
- package/packages/ui/tsconfig.lint.json +8 -0
- package/pnpm-workspace.yaml +3 -0
- package/scripts/cli.js +44 -0
- package/tsconfig.json +4 -0
- package/turbo.json +21 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/tsconfig",
|
|
3
|
+
"display": "Default",
|
|
4
|
+
"compilerOptions": {
|
|
5
|
+
"declaration": true,
|
|
6
|
+
"declarationMap": true,
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
"incremental": false,
|
|
9
|
+
"isolatedModules": true,
|
|
10
|
+
"lib": ["es2022", "DOM", "DOM.Iterable"],
|
|
11
|
+
"module": "NodeNext",
|
|
12
|
+
"moduleDetection": "force",
|
|
13
|
+
"moduleResolution": "NodeNext",
|
|
14
|
+
"noUncheckedIndexedAccess": true,
|
|
15
|
+
"resolveJsonModule": true,
|
|
16
|
+
"skipLibCheck": true,
|
|
17
|
+
"strict": true,
|
|
18
|
+
"target": "ES2022"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/tsconfig",
|
|
3
|
+
"display": "Next.js",
|
|
4
|
+
"extends": "./base.json",
|
|
5
|
+
"compilerOptions": {
|
|
6
|
+
"plugins": [{ "name": "next" }],
|
|
7
|
+
"module": "ESNext",
|
|
8
|
+
"moduleResolution": "Bundler",
|
|
9
|
+
"allowJs": true,
|
|
10
|
+
"jsx": "preserve",
|
|
11
|
+
"noEmit": true
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema.json",
|
|
3
|
+
"style": "new-york",
|
|
4
|
+
"rsc": true,
|
|
5
|
+
"tsx": true,
|
|
6
|
+
"tailwind": {
|
|
7
|
+
"config": "",
|
|
8
|
+
"css": "src/styles/globals.css",
|
|
9
|
+
"baseColor": "neutral",
|
|
10
|
+
"cssVariables": true
|
|
11
|
+
},
|
|
12
|
+
"iconLibrary": "lucide",
|
|
13
|
+
"aliases": {
|
|
14
|
+
"components": "@workspace/ui/components/shadcn",
|
|
15
|
+
"utils": "@workspace/ui/lib/utils",
|
|
16
|
+
"hooks": "@workspace/ui/hooks",
|
|
17
|
+
"lib": "@workspace/ui/lib",
|
|
18
|
+
"ui": "@workspace/ui/components/shadcn"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@workspace/ui",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"scripts": {
|
|
6
|
+
"lint": "eslint . --max-warnings 0"
|
|
7
|
+
},
|
|
8
|
+
"dependencies": {
|
|
9
|
+
"@hookform/resolvers": "^5.2.2",
|
|
10
|
+
"@radix-ui/react-accordion": "^1.2.12",
|
|
11
|
+
"@radix-ui/react-alert-dialog": "^1.1.15",
|
|
12
|
+
"@radix-ui/react-aspect-ratio": "^1.1.8",
|
|
13
|
+
"@radix-ui/react-avatar": "^1.1.11",
|
|
14
|
+
"@radix-ui/react-checkbox": "^1.3.3",
|
|
15
|
+
"@radix-ui/react-collapsible": "^1.1.12",
|
|
16
|
+
"@radix-ui/react-context-menu": "^2.2.16",
|
|
17
|
+
"@radix-ui/react-dialog": "^1.1.15",
|
|
18
|
+
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
|
19
|
+
"@radix-ui/react-hover-card": "^1.1.15",
|
|
20
|
+
"@radix-ui/react-label": "^2.1.8",
|
|
21
|
+
"@radix-ui/react-menubar": "^1.1.16",
|
|
22
|
+
"@radix-ui/react-navigation-menu": "^1.2.14",
|
|
23
|
+
"@radix-ui/react-popover": "^1.1.15",
|
|
24
|
+
"@radix-ui/react-progress": "^1.1.8",
|
|
25
|
+
"@radix-ui/react-radio-group": "^1.3.8",
|
|
26
|
+
"@radix-ui/react-scroll-area": "^1.2.10",
|
|
27
|
+
"@radix-ui/react-select": "^2.2.6",
|
|
28
|
+
"@radix-ui/react-separator": "^1.1.8",
|
|
29
|
+
"@radix-ui/react-slider": "^1.3.6",
|
|
30
|
+
"@radix-ui/react-slot": "^1.2.3",
|
|
31
|
+
"@radix-ui/react-switch": "^1.2.6",
|
|
32
|
+
"@radix-ui/react-tabs": "^1.1.13",
|
|
33
|
+
"@radix-ui/react-toggle": "^1.1.10",
|
|
34
|
+
"@radix-ui/react-toggle-group": "^1.1.11",
|
|
35
|
+
"@radix-ui/react-tooltip": "^1.2.8",
|
|
36
|
+
"@tabler/icons-react": "^3.35.0",
|
|
37
|
+
"@tsparticles/engine": "^3.9.1",
|
|
38
|
+
"@tsparticles/react": "^3.0.0",
|
|
39
|
+
"@tsparticles/slim": "^3.9.1",
|
|
40
|
+
"class-variance-authority": "^0.7.1",
|
|
41
|
+
"clsx": "^2.1.1",
|
|
42
|
+
"cmdk": "^1.1.1",
|
|
43
|
+
"date-fns": "^4.1.0",
|
|
44
|
+
"embla-carousel-react": "^8.6.0",
|
|
45
|
+
"framer-motion": "^12.23.25",
|
|
46
|
+
"input-otp": "^1.4.2",
|
|
47
|
+
"local": "link:next/font/local",
|
|
48
|
+
"lucide-react": "^0.475.0",
|
|
49
|
+
"motion": "^12.23.25",
|
|
50
|
+
"next": "^15.4.5",
|
|
51
|
+
"next-themes": "^0.4.6",
|
|
52
|
+
"react": "^19.1.1",
|
|
53
|
+
"react-day-picker": "^9.11.3",
|
|
54
|
+
"react-dom": "^19.1.1",
|
|
55
|
+
"react-hook-form": "^7.67.0",
|
|
56
|
+
"react-resizable-panels": "^3.0.6",
|
|
57
|
+
"recharts": "2.15.4",
|
|
58
|
+
"sonner": "^2.0.7",
|
|
59
|
+
"tailwind-merge": "^3.3.1",
|
|
60
|
+
"tw-animate-css": "^1.3.6",
|
|
61
|
+
"vaul": "^1.1.2",
|
|
62
|
+
"zod": "^3.25.76"
|
|
63
|
+
},
|
|
64
|
+
"devDependencies": {
|
|
65
|
+
"@tailwindcss/postcss": "^4.1.11",
|
|
66
|
+
"@turbo/gen": "^2.5.5",
|
|
67
|
+
"@types/node": "^20.19.9",
|
|
68
|
+
"@types/react": "^19.1.9",
|
|
69
|
+
"@types/react-dom": "^19.1.7",
|
|
70
|
+
"@workspace/eslint-config": "workspace:*",
|
|
71
|
+
"@workspace/typescript-config": "workspace:*",
|
|
72
|
+
"eslint": "^9.32.0",
|
|
73
|
+
"tailwindcss": "^4.1.11",
|
|
74
|
+
"typescript": "^5.9.2"
|
|
75
|
+
},
|
|
76
|
+
"exports": {
|
|
77
|
+
"./globals.css": "./src/styles/globals.css",
|
|
78
|
+
"./postcss.config": "./postcss.config.mjs",
|
|
79
|
+
"./lib/*": "./src/lib/*.ts",
|
|
80
|
+
"./components/*": "./src/components/*.tsx",
|
|
81
|
+
"./hooks/*": "./src/hooks/*.ts",
|
|
82
|
+
"./utils/*": "./src/lib/utils/*.ts",
|
|
83
|
+
"./providers/*": "./src/providers/*.tsx",
|
|
84
|
+
"./typography/*": "./src/components/typography/*.tsx"
|
|
85
|
+
}
|
|
86
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
import React, {
|
|
5
|
+
createContext,
|
|
6
|
+
useState,
|
|
7
|
+
useContext,
|
|
8
|
+
useRef,
|
|
9
|
+
useEffect,
|
|
10
|
+
} from "react";
|
|
11
|
+
import { cn } from "../../lib/utils";
|
|
12
|
+
|
|
13
|
+
const MouseEnterContext = createContext<
|
|
14
|
+
[boolean, React.Dispatch<React.SetStateAction<boolean>>] | undefined
|
|
15
|
+
>(undefined);
|
|
16
|
+
|
|
17
|
+
export const CardContainer = ({
|
|
18
|
+
children,
|
|
19
|
+
className,
|
|
20
|
+
containerClassName,
|
|
21
|
+
}: {
|
|
22
|
+
children?: React.ReactNode;
|
|
23
|
+
className?: string;
|
|
24
|
+
containerClassName?: string;
|
|
25
|
+
}) => {
|
|
26
|
+
const containerRef = useRef<HTMLDivElement>(null);
|
|
27
|
+
const [isMouseEntered, setIsMouseEntered] = useState(false);
|
|
28
|
+
|
|
29
|
+
const handleMouseMove = (e: React.MouseEvent<HTMLDivElement>) => {
|
|
30
|
+
if (!containerRef.current) return;
|
|
31
|
+
const { left, top, width, height } =
|
|
32
|
+
containerRef.current.getBoundingClientRect();
|
|
33
|
+
const x = (e.clientX - left - width / 2) / 25;
|
|
34
|
+
const y = (e.clientY - top - height / 2) / 25;
|
|
35
|
+
containerRef.current.style.transform = `rotateY(${x}deg) rotateX(${y}deg)`;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const handleMouseEnter = (e: React.MouseEvent<HTMLDivElement>) => {
|
|
39
|
+
setIsMouseEntered(true);
|
|
40
|
+
if (!containerRef.current) return;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const handleMouseLeave = (e: React.MouseEvent<HTMLDivElement>) => {
|
|
44
|
+
if (!containerRef.current) return;
|
|
45
|
+
setIsMouseEntered(false);
|
|
46
|
+
containerRef.current.style.transform = `rotateY(0deg) rotateX(0deg)`;
|
|
47
|
+
};
|
|
48
|
+
return (
|
|
49
|
+
<MouseEnterContext.Provider value={[isMouseEntered, setIsMouseEntered]}>
|
|
50
|
+
<div
|
|
51
|
+
className={cn(
|
|
52
|
+
"py-20 flex items-center justify-center",
|
|
53
|
+
containerClassName
|
|
54
|
+
)}
|
|
55
|
+
style={{
|
|
56
|
+
perspective: "1000px",
|
|
57
|
+
}}
|
|
58
|
+
>
|
|
59
|
+
<div
|
|
60
|
+
ref={containerRef}
|
|
61
|
+
onMouseEnter={handleMouseEnter}
|
|
62
|
+
onMouseMove={handleMouseMove}
|
|
63
|
+
onMouseLeave={handleMouseLeave}
|
|
64
|
+
className={cn(
|
|
65
|
+
"flex items-center justify-center relative transition-all duration-200 ease-linear",
|
|
66
|
+
className
|
|
67
|
+
)}
|
|
68
|
+
style={{
|
|
69
|
+
transformStyle: "preserve-3d",
|
|
70
|
+
}}
|
|
71
|
+
>
|
|
72
|
+
{children}
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
</MouseEnterContext.Provider>
|
|
76
|
+
);
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
export const CardBody = ({
|
|
80
|
+
children,
|
|
81
|
+
className,
|
|
82
|
+
}: {
|
|
83
|
+
children: React.ReactNode;
|
|
84
|
+
className?: string;
|
|
85
|
+
}) => {
|
|
86
|
+
return (
|
|
87
|
+
<div
|
|
88
|
+
className={cn(
|
|
89
|
+
"h-96 w-96 [transform-style:preserve-3d] [&>*]:[transform-style:preserve-3d]",
|
|
90
|
+
className
|
|
91
|
+
)}
|
|
92
|
+
>
|
|
93
|
+
{children}
|
|
94
|
+
</div>
|
|
95
|
+
);
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
export const CardItem = ({
|
|
99
|
+
as: Tag = "div",
|
|
100
|
+
children,
|
|
101
|
+
className,
|
|
102
|
+
translateX = 0,
|
|
103
|
+
translateY = 0,
|
|
104
|
+
translateZ = 0,
|
|
105
|
+
rotateX = 0,
|
|
106
|
+
rotateY = 0,
|
|
107
|
+
rotateZ = 0,
|
|
108
|
+
...rest
|
|
109
|
+
}: {
|
|
110
|
+
as?: React.ElementType;
|
|
111
|
+
children: React.ReactNode;
|
|
112
|
+
className?: string;
|
|
113
|
+
translateX?: number | string;
|
|
114
|
+
translateY?: number | string;
|
|
115
|
+
translateZ?: number | string;
|
|
116
|
+
rotateX?: number | string;
|
|
117
|
+
rotateY?: number | string;
|
|
118
|
+
rotateZ?: number | string;
|
|
119
|
+
[key: string]: any;
|
|
120
|
+
}) => {
|
|
121
|
+
const ref = useRef<HTMLDivElement>(null);
|
|
122
|
+
const [isMouseEntered] = useMouseEnter();
|
|
123
|
+
|
|
124
|
+
useEffect(() => {
|
|
125
|
+
handleAnimations();
|
|
126
|
+
}, [isMouseEntered]);
|
|
127
|
+
|
|
128
|
+
const handleAnimations = () => {
|
|
129
|
+
if (!ref.current) return;
|
|
130
|
+
if (isMouseEntered) {
|
|
131
|
+
ref.current.style.transform = `translateX(${translateX}px) translateY(${translateY}px) translateZ(${translateZ}px) rotateX(${rotateX}deg) rotateY(${rotateY}deg) rotateZ(${rotateZ}deg)`;
|
|
132
|
+
} else {
|
|
133
|
+
ref.current.style.transform = `translateX(0px) translateY(0px) translateZ(0px) rotateX(0deg) rotateY(0deg) rotateZ(0deg)`;
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
return (
|
|
138
|
+
<Tag
|
|
139
|
+
ref={ref}
|
|
140
|
+
className={cn("w-fit transition duration-200 ease-linear", className)}
|
|
141
|
+
{...rest}
|
|
142
|
+
>
|
|
143
|
+
{children}
|
|
144
|
+
</Tag>
|
|
145
|
+
);
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
// Create a hook to use the context
|
|
149
|
+
export const useMouseEnter = () => {
|
|
150
|
+
const context = useContext(MouseEnterContext);
|
|
151
|
+
if (context === undefined) {
|
|
152
|
+
throw new Error("useMouseEnter must be used within a MouseEnterProvider");
|
|
153
|
+
}
|
|
154
|
+
return context;
|
|
155
|
+
};
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { motion } from "motion/react";
|
|
4
|
+
import { cn } from "../../lib/utils";
|
|
5
|
+
export const ThreeDMarquee = ({
|
|
6
|
+
images,
|
|
7
|
+
className,
|
|
8
|
+
}: {
|
|
9
|
+
images: string[];
|
|
10
|
+
className?: string;
|
|
11
|
+
}) => {
|
|
12
|
+
// Split the images array into 4 equal parts
|
|
13
|
+
const chunkSize = Math.ceil(images.length / 4);
|
|
14
|
+
const chunks = Array.from({ length: 4 }, (_, colIndex) => {
|
|
15
|
+
const start = colIndex * chunkSize;
|
|
16
|
+
return images.slice(start, start + chunkSize);
|
|
17
|
+
});
|
|
18
|
+
return (
|
|
19
|
+
<div
|
|
20
|
+
className={cn(
|
|
21
|
+
"mx-auto block h-[600px] overflow-hidden rounded-2xl max-sm:h-100",
|
|
22
|
+
className,
|
|
23
|
+
)}
|
|
24
|
+
>
|
|
25
|
+
<div className="flex size-full items-center justify-center">
|
|
26
|
+
<div className="size-[1720px] shrink-0 scale-50 sm:scale-75 lg:scale-100">
|
|
27
|
+
<div
|
|
28
|
+
style={{
|
|
29
|
+
transform: "rotateX(55deg) rotateY(0deg) rotateZ(-45deg)",
|
|
30
|
+
}}
|
|
31
|
+
className="relative top-96 right-[50%] grid size-full origin-top-left grid-cols-4 gap-8 transform-3d"
|
|
32
|
+
>
|
|
33
|
+
{chunks.map((subarray, colIndex) => (
|
|
34
|
+
<motion.div
|
|
35
|
+
animate={{ y: colIndex % 2 === 0 ? 100 : -100 }}
|
|
36
|
+
transition={{
|
|
37
|
+
duration: colIndex % 2 === 0 ? 10 : 15,
|
|
38
|
+
repeat: Infinity,
|
|
39
|
+
repeatType: "reverse",
|
|
40
|
+
}}
|
|
41
|
+
key={colIndex + "marquee"}
|
|
42
|
+
className="flex flex-col items-start gap-8"
|
|
43
|
+
>
|
|
44
|
+
<GridLineVertical className="-left-4" offset="80px" />
|
|
45
|
+
{subarray.map((image, imageIndex) => (
|
|
46
|
+
<div className="relative" key={imageIndex + image}>
|
|
47
|
+
<GridLineHorizontal className="-top-4" offset="20px" />
|
|
48
|
+
<motion.img
|
|
49
|
+
whileHover={{
|
|
50
|
+
y: -10,
|
|
51
|
+
}}
|
|
52
|
+
transition={{
|
|
53
|
+
duration: 0.3,
|
|
54
|
+
ease: "easeInOut",
|
|
55
|
+
}}
|
|
56
|
+
key={imageIndex + image}
|
|
57
|
+
src={image}
|
|
58
|
+
alt={`Image ${imageIndex + 1}`}
|
|
59
|
+
className="aspect-[970/700] rounded-lg object-cover ring ring-gray-950/5 hover:shadow-2xl"
|
|
60
|
+
width={970}
|
|
61
|
+
height={700}
|
|
62
|
+
/>
|
|
63
|
+
</div>
|
|
64
|
+
))}
|
|
65
|
+
</motion.div>
|
|
66
|
+
))}
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
);
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const GridLineHorizontal = ({
|
|
75
|
+
className,
|
|
76
|
+
offset,
|
|
77
|
+
}: {
|
|
78
|
+
className?: string;
|
|
79
|
+
offset?: string;
|
|
80
|
+
}) => {
|
|
81
|
+
return (
|
|
82
|
+
<div
|
|
83
|
+
style={
|
|
84
|
+
{
|
|
85
|
+
"--background": "#ffffff",
|
|
86
|
+
"--color": "rgba(0, 0, 0, 0.2)",
|
|
87
|
+
"--height": "1px",
|
|
88
|
+
"--width": "5px",
|
|
89
|
+
"--fade-stop": "90%",
|
|
90
|
+
"--offset": offset || "200px", //-100px if you want to keep the line inside
|
|
91
|
+
"--color-dark": "rgba(255, 255, 255, 0.2)",
|
|
92
|
+
maskComposite: "exclude",
|
|
93
|
+
} as React.CSSProperties
|
|
94
|
+
}
|
|
95
|
+
className={cn(
|
|
96
|
+
"absolute left-[calc(var(--offset)/2*-1)] h-[var(--height)] w-[calc(100%+var(--offset))]",
|
|
97
|
+
"bg-[linear-gradient(to_right,var(--color),var(--color)_50%,transparent_0,transparent)]",
|
|
98
|
+
"[background-size:var(--width)_var(--height)]",
|
|
99
|
+
"[mask:linear-gradient(to_left,var(--background)_var(--fade-stop),transparent),_linear-gradient(to_right,var(--background)_var(--fade-stop),transparent),_linear-gradient(black,black)]",
|
|
100
|
+
"[mask-composite:exclude]",
|
|
101
|
+
"z-30",
|
|
102
|
+
"dark:bg-[linear-gradient(to_right,var(--color-dark),var(--color-dark)_50%,transparent_0,transparent)]",
|
|
103
|
+
className,
|
|
104
|
+
)}
|
|
105
|
+
></div>
|
|
106
|
+
);
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const GridLineVertical = ({
|
|
110
|
+
className,
|
|
111
|
+
offset,
|
|
112
|
+
}: {
|
|
113
|
+
className?: string;
|
|
114
|
+
offset?: string;
|
|
115
|
+
}) => {
|
|
116
|
+
return (
|
|
117
|
+
<div
|
|
118
|
+
style={
|
|
119
|
+
{
|
|
120
|
+
"--background": "#ffffff",
|
|
121
|
+
"--color": "rgba(0, 0, 0, 0.2)",
|
|
122
|
+
"--height": "5px",
|
|
123
|
+
"--width": "1px",
|
|
124
|
+
"--fade-stop": "90%",
|
|
125
|
+
"--offset": offset || "150px", //-100px if you want to keep the line inside
|
|
126
|
+
"--color-dark": "rgba(255, 255, 255, 0.2)",
|
|
127
|
+
maskComposite: "exclude",
|
|
128
|
+
} as React.CSSProperties
|
|
129
|
+
}
|
|
130
|
+
className={cn(
|
|
131
|
+
"absolute top-[calc(var(--offset)/2*-1)] h-[calc(100%+var(--offset))] w-[var(--width)]",
|
|
132
|
+
"bg-[linear-gradient(to_bottom,var(--color),var(--color)_50%,transparent_0,transparent)]",
|
|
133
|
+
"[background-size:var(--width)_var(--height)]",
|
|
134
|
+
"[mask:linear-gradient(to_top,var(--background)_var(--fade-stop),transparent),_linear-gradient(to_bottom,var(--background)_var(--fade-stop),transparent),_linear-gradient(black,black)]",
|
|
135
|
+
"[mask-composite:exclude]",
|
|
136
|
+
"z-30",
|
|
137
|
+
"dark:bg-[linear-gradient(to_bottom,var(--color-dark),var(--color-dark)_50%,transparent_0,transparent)]",
|
|
138
|
+
className,
|
|
139
|
+
)}
|
|
140
|
+
></div>
|
|
141
|
+
);
|
|
142
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { cn } from "../../lib/utils";
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
type SpotlightProps = {
|
|
6
|
+
className?: string;
|
|
7
|
+
fill?: string;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const Spotlight = ({ className, fill }: SpotlightProps) => {
|
|
11
|
+
return (
|
|
12
|
+
<svg
|
|
13
|
+
className={cn(
|
|
14
|
+
"animate-spotlight pointer-events-none absolute z-[1] h-[169%] w-[138%] lg:w-[84%] opacity-0",
|
|
15
|
+
className
|
|
16
|
+
)}
|
|
17
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
18
|
+
viewBox="0 0 3787 2842"
|
|
19
|
+
fill="none"
|
|
20
|
+
>
|
|
21
|
+
<g filter="url(#filter)">
|
|
22
|
+
<ellipse
|
|
23
|
+
cx="1924.71"
|
|
24
|
+
cy="273.501"
|
|
25
|
+
rx="1924.71"
|
|
26
|
+
ry="273.501"
|
|
27
|
+
transform="matrix(-0.822377 -0.568943 -0.568943 0.822377 3631.88 2291.09)"
|
|
28
|
+
fill={fill || "white"}
|
|
29
|
+
fillOpacity="0.21"
|
|
30
|
+
></ellipse>
|
|
31
|
+
</g>
|
|
32
|
+
<defs>
|
|
33
|
+
<filter
|
|
34
|
+
id="filter"
|
|
35
|
+
x="0.860352"
|
|
36
|
+
y="0.838989"
|
|
37
|
+
width="3785.16"
|
|
38
|
+
height="2840.26"
|
|
39
|
+
filterUnits="userSpaceOnUse"
|
|
40
|
+
colorInterpolationFilters="sRGB"
|
|
41
|
+
>
|
|
42
|
+
<feFlood floodOpacity="0" result="BackgroundImageFix"></feFlood>
|
|
43
|
+
<feBlend
|
|
44
|
+
mode="normal"
|
|
45
|
+
in="SourceGraphic"
|
|
46
|
+
in2="BackgroundImageFix"
|
|
47
|
+
result="shape"
|
|
48
|
+
></feBlend>
|
|
49
|
+
<feGaussianBlur
|
|
50
|
+
stdDeviation="151"
|
|
51
|
+
result="effect1_foregroundBlur_1065_8"
|
|
52
|
+
></feGaussianBlur>
|
|
53
|
+
</filter>
|
|
54
|
+
</defs>
|
|
55
|
+
</svg>
|
|
56
|
+
);
|
|
57
|
+
};
|