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.
Files changed (145) hide show
  1. package/.eslintrc.js +10 -0
  2. package/.vscode/settings.json +3 -0
  3. package/README.md +31 -0
  4. package/apps/web/.env.example +1 -0
  5. package/apps/web/app/favicon.ico +0 -0
  6. package/apps/web/app/layout.tsx +23 -0
  7. package/apps/web/app/page.tsx +12 -0
  8. package/apps/web/components.json +20 -0
  9. package/apps/web/eslint.config.js +4 -0
  10. package/apps/web/hooks/.gitkeep +0 -0
  11. package/apps/web/next-env.d.ts +5 -0
  12. package/apps/web/next.config.mjs +6 -0
  13. package/apps/web/package.json +32 -0
  14. package/apps/web/postcss.config.mjs +1 -0
  15. package/apps/web/tsconfig.json +23 -0
  16. package/docs/CODE_OF_CONDUCT.md +128 -0
  17. package/docs/CONTRIBUTING.md +45 -0
  18. package/docs/ISSUE_TEMPLATE/bug_report.md +31 -0
  19. package/docs/ISSUE_TEMPLATE/feature_request.md +17 -0
  20. package/docs/SECURITY.md +9 -0
  21. package/docs/pull_request_template.md +24 -0
  22. package/package.json +38 -0
  23. package/packages/eslint-config/README.md +3 -0
  24. package/packages/eslint-config/base.js +32 -0
  25. package/packages/eslint-config/next.js +51 -0
  26. package/packages/eslint-config/package.json +26 -0
  27. package/packages/eslint-config/react-internal.js +41 -0
  28. package/packages/typescript-config/README.md +3 -0
  29. package/packages/typescript-config/base.json +20 -0
  30. package/packages/typescript-config/nextjs.json +13 -0
  31. package/packages/typescript-config/package.json +9 -0
  32. package/packages/typescript-config/react-library.json +8 -0
  33. package/packages/ui/components.json +20 -0
  34. package/packages/ui/eslint.config.js +4 -0
  35. package/packages/ui/package.json +86 -0
  36. package/packages/ui/postcss.config.mjs +6 -0
  37. package/packages/ui/src/components/.gitkeep +0 -0
  38. package/packages/ui/src/components/aceternity/3d-card.tsx +155 -0
  39. package/packages/ui/src/components/aceternity/3d-marquee.tsx +142 -0
  40. package/packages/ui/src/components/aceternity/Spotlight.tsx +57 -0
  41. package/packages/ui/src/components/aceternity/animated-testimonials.tsx +165 -0
  42. package/packages/ui/src/components/aceternity/animated-tooltip.tsx +92 -0
  43. package/packages/ui/src/components/aceternity/aurora-background.tsx +61 -0
  44. package/packages/ui/src/components/aceternity/background-beams.tsx +141 -0
  45. package/packages/ui/src/components/aceternity/background-gradient.tsx +72 -0
  46. package/packages/ui/src/components/aceternity/card-hover-effect.tsx +111 -0
  47. package/packages/ui/src/components/aceternity/compare.tsx +244 -0
  48. package/packages/ui/src/components/aceternity/container-scroll-animation.tsx +95 -0
  49. package/packages/ui/src/components/aceternity/lamp.tsx +104 -0
  50. package/packages/ui/src/components/aceternity/sparkles.tsx +434 -0
  51. package/packages/ui/src/components/aceternity/spotlight-new.tsx +128 -0
  52. package/packages/ui/src/components/mdx/Alert.tsx +14 -0
  53. package/packages/ui/src/components/mdx/Badge.tsx +10 -0
  54. package/packages/ui/src/components/mdx/blockquote.tsx +11 -0
  55. package/packages/ui/src/components/mdx/code.tsx +16 -0
  56. package/packages/ui/src/components/mdx/h1.tsx +18 -0
  57. package/packages/ui/src/components/mdx/h2.tsx +18 -0
  58. package/packages/ui/src/components/mdx/h3.tsx +18 -0
  59. package/packages/ui/src/components/mdx/hr.tsx +7 -0
  60. package/packages/ui/src/components/mdx/li.tsx +7 -0
  61. package/packages/ui/src/components/mdx/mdxComponents.tsx +25 -0
  62. package/packages/ui/src/components/mdx/ol.tsx +7 -0
  63. package/packages/ui/src/components/mdx/pre.tsx +13 -0
  64. package/packages/ui/src/components/mdx/tableOfContents.tsx +23 -0
  65. package/packages/ui/src/components/mdx/ul.tsx +7 -0
  66. package/packages/ui/src/components/mdx/ulNumbered.tsx +7 -0
  67. package/packages/ui/src/components/shadcn/accordion.tsx +66 -0
  68. package/packages/ui/src/components/shadcn/alert-dialog.tsx +157 -0
  69. package/packages/ui/src/components/shadcn/alert.tsx +66 -0
  70. package/packages/ui/src/components/shadcn/aspect-ratio.tsx +11 -0
  71. package/packages/ui/src/components/shadcn/avatar.tsx +53 -0
  72. package/packages/ui/src/components/shadcn/badge.tsx +46 -0
  73. package/packages/ui/src/components/shadcn/breadcrumb.tsx +109 -0
  74. package/packages/ui/src/components/shadcn/button-group.tsx +83 -0
  75. package/packages/ui/src/components/shadcn/button.tsx +60 -0
  76. package/packages/ui/src/components/shadcn/calendar.tsx +216 -0
  77. package/packages/ui/src/components/shadcn/card.tsx +92 -0
  78. package/packages/ui/src/components/shadcn/carousel.tsx +241 -0
  79. package/packages/ui/src/components/shadcn/chart.tsx +357 -0
  80. package/packages/ui/src/components/shadcn/checkbox.tsx +32 -0
  81. package/packages/ui/src/components/shadcn/collapsible.tsx +33 -0
  82. package/packages/ui/src/components/shadcn/command.tsx +184 -0
  83. package/packages/ui/src/components/shadcn/context-menu.tsx +252 -0
  84. package/packages/ui/src/components/shadcn/dialog.tsx +143 -0
  85. package/packages/ui/src/components/shadcn/drawer.tsx +135 -0
  86. package/packages/ui/src/components/shadcn/dropdown-menu.tsx +257 -0
  87. package/packages/ui/src/components/shadcn/empty.tsx +104 -0
  88. package/packages/ui/src/components/shadcn/field.tsx +248 -0
  89. package/packages/ui/src/components/shadcn/form.tsx +167 -0
  90. package/packages/ui/src/components/shadcn/hover-card.tsx +44 -0
  91. package/packages/ui/src/components/shadcn/input-group.tsx +170 -0
  92. package/packages/ui/src/components/shadcn/input-otp.tsx +77 -0
  93. package/packages/ui/src/components/shadcn/input.tsx +21 -0
  94. package/packages/ui/src/components/shadcn/item.tsx +193 -0
  95. package/packages/ui/src/components/shadcn/kbd.tsx +28 -0
  96. package/packages/ui/src/components/shadcn/label.tsx +24 -0
  97. package/packages/ui/src/components/shadcn/menubar.tsx +276 -0
  98. package/packages/ui/src/components/shadcn/native-select.tsx +48 -0
  99. package/packages/ui/src/components/shadcn/navigation-menu.tsx +168 -0
  100. package/packages/ui/src/components/shadcn/pagination.tsx +127 -0
  101. package/packages/ui/src/components/shadcn/popover.tsx +48 -0
  102. package/packages/ui/src/components/shadcn/progress.tsx +31 -0
  103. package/packages/ui/src/components/shadcn/radio-group.tsx +45 -0
  104. package/packages/ui/src/components/shadcn/resizable.tsx +56 -0
  105. package/packages/ui/src/components/shadcn/scroll-area.tsx +58 -0
  106. package/packages/ui/src/components/shadcn/select.tsx +187 -0
  107. package/packages/ui/src/components/shadcn/separator.tsx +28 -0
  108. package/packages/ui/src/components/shadcn/sheet.tsx +139 -0
  109. package/packages/ui/src/components/shadcn/sidebar.tsx +726 -0
  110. package/packages/ui/src/components/shadcn/skeleton.tsx +13 -0
  111. package/packages/ui/src/components/shadcn/slider.tsx +63 -0
  112. package/packages/ui/src/components/shadcn/sonner.tsx +40 -0
  113. package/packages/ui/src/components/shadcn/spinner.tsx +16 -0
  114. package/packages/ui/src/components/shadcn/switch.tsx +31 -0
  115. package/packages/ui/src/components/shadcn/table.tsx +116 -0
  116. package/packages/ui/src/components/shadcn/tabs.tsx +66 -0
  117. package/packages/ui/src/components/shadcn/textarea.tsx +18 -0
  118. package/packages/ui/src/components/shadcn/toggle-group.tsx +83 -0
  119. package/packages/ui/src/components/shadcn/toggle.tsx +47 -0
  120. package/packages/ui/src/components/shadcn/tooltip.tsx +61 -0
  121. package/packages/ui/src/hooks/.gitkeep +0 -0
  122. package/packages/ui/src/hooks/use-device.tsx +29 -0
  123. package/packages/ui/src/hooks/use-mobile.tsx +19 -0
  124. package/packages/ui/src/hooks/use-outside-click.tsx +23 -0
  125. package/packages/ui/src/lib/utils.ts +6 -0
  126. package/packages/ui/src/providers/theme-provider.tsx +18 -0
  127. package/packages/ui/src/styles/globals.css +65 -0
  128. package/packages/ui/src/styles/shadcn-blue.css +69 -0
  129. package/packages/ui/src/styles/shadcn-green.css +68 -0
  130. package/packages/ui/src/styles/shadcn-neutral.css +69 -0
  131. package/packages/ui/src/styles/shadcn-orange.css +68 -0
  132. package/packages/ui/src/styles/shadcn-red.css +68 -0
  133. package/packages/ui/src/styles/shadcn-rose.css +68 -0
  134. package/packages/ui/src/styles/shadcn-violet.css +68 -0
  135. package/packages/ui/src/styles/shadcn-yellow.css +68 -0
  136. package/packages/ui/src/typography/font.tsx +19 -0
  137. package/packages/ui/src/typography/fontFiles/Cyberdyne.woff2 +0 -0
  138. package/packages/ui/src/typography/fontFiles/GeistMonoVF.woff +0 -0
  139. package/packages/ui/src/typography/fontFiles/GeistVF.woff +0 -0
  140. package/packages/ui/tsconfig.json +11 -0
  141. package/packages/ui/tsconfig.lint.json +8 -0
  142. package/pnpm-workspace.yaml +3 -0
  143. package/scripts/cli.js +44 -0
  144. package/tsconfig.json +4 -0
  145. 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,9 @@
1
+ {
2
+ "name": "@workspace/typescript-config",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "license": "PROPRIETARY",
6
+ "publishConfig": {
7
+ "access": "public"
8
+ }
9
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/tsconfig",
3
+ "display": "React Library",
4
+ "extends": "./base.json",
5
+ "compilerOptions": {
6
+ "jsx": "react-jsx",
7
+ }
8
+ }
@@ -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,4 @@
1
+ import { config } from "@workspace/eslint-config/react-internal"
2
+
3
+ /** @type {import("eslint").Linter.Config} */
4
+ export default config
@@ -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
+ }
@@ -0,0 +1,6 @@
1
+ /** @type {import('postcss-load-config').Config} */
2
+ const config = {
3
+ plugins: { "@tailwindcss/postcss": {} },
4
+ };
5
+
6
+ export default config;
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
+ };