parent
011c55f61c
commit
55022e5ed2
@ -1,26 +1,122 @@
|
|||||||
@import "tailwindcss";
|
@import "tailwindcss";
|
||||||
|
@import "tw-animate-css";
|
||||||
|
|
||||||
:root {
|
@custom-variant dark (&:is(.dark *));
|
||||||
--background: #ffffff;
|
|
||||||
--foreground: #171717;
|
|
||||||
}
|
|
||||||
|
|
||||||
@theme inline {
|
@theme inline {
|
||||||
--color-background: var(--background);
|
--color-background: var(--background);
|
||||||
--color-foreground: var(--foreground);
|
--color-foreground: var(--foreground);
|
||||||
--font-sans: var(--font-geist-sans);
|
--font-sans: var(--font-geist-sans);
|
||||||
--font-mono: var(--font-geist-mono);
|
--font-mono: var(--font-geist-mono);
|
||||||
|
--color-sidebar-ring: var(--sidebar-ring);
|
||||||
|
--color-sidebar-border: var(--sidebar-border);
|
||||||
|
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
||||||
|
--color-sidebar-accent: var(--sidebar-accent);
|
||||||
|
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
||||||
|
--color-sidebar-primary: var(--sidebar-primary);
|
||||||
|
--color-sidebar-foreground: var(--sidebar-foreground);
|
||||||
|
--color-sidebar: var(--sidebar);
|
||||||
|
--color-chart-5: var(--chart-5);
|
||||||
|
--color-chart-4: var(--chart-4);
|
||||||
|
--color-chart-3: var(--chart-3);
|
||||||
|
--color-chart-2: var(--chart-2);
|
||||||
|
--color-chart-1: var(--chart-1);
|
||||||
|
--color-ring: var(--ring);
|
||||||
|
--color-input: var(--input);
|
||||||
|
--color-border: var(--border);
|
||||||
|
--color-destructive: var(--destructive);
|
||||||
|
--color-accent-foreground: var(--accent-foreground);
|
||||||
|
--color-accent: var(--accent);
|
||||||
|
--color-muted-foreground: var(--muted-foreground);
|
||||||
|
--color-muted: var(--muted);
|
||||||
|
--color-secondary-foreground: var(--secondary-foreground);
|
||||||
|
--color-secondary: var(--secondary);
|
||||||
|
--color-primary-foreground: var(--primary-foreground);
|
||||||
|
--color-primary: var(--primary);
|
||||||
|
--color-popover-foreground: var(--popover-foreground);
|
||||||
|
--color-popover: var(--popover);
|
||||||
|
--color-card-foreground: var(--card-foreground);
|
||||||
|
--color-card: var(--card);
|
||||||
|
--radius-sm: calc(var(--radius) - 4px);
|
||||||
|
--radius-md: calc(var(--radius) - 2px);
|
||||||
|
--radius-lg: var(--radius);
|
||||||
|
--radius-xl: calc(var(--radius) + 4px);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
:root {
|
:root {
|
||||||
--background: #0a0a0a;
|
--radius: 0.625rem;
|
||||||
--foreground: #ededed;
|
--background: oklch(1 0 0);
|
||||||
|
--foreground: oklch(0.145 0 0);
|
||||||
|
--card: oklch(1 0 0);
|
||||||
|
--card-foreground: oklch(0.145 0 0);
|
||||||
|
--popover: oklch(1 0 0);
|
||||||
|
--popover-foreground: oklch(0.145 0 0);
|
||||||
|
--primary: oklch(0.205 0 0);
|
||||||
|
--primary-foreground: oklch(0.985 0 0);
|
||||||
|
--secondary: oklch(0.97 0 0);
|
||||||
|
--secondary-foreground: oklch(0.205 0 0);
|
||||||
|
--muted: oklch(0.97 0 0);
|
||||||
|
--muted-foreground: oklch(0.556 0 0);
|
||||||
|
--accent: oklch(0.97 0 0);
|
||||||
|
--accent-foreground: oklch(0.205 0 0);
|
||||||
|
--destructive: oklch(0.577 0.245 27.325);
|
||||||
|
--border: oklch(0.922 0 0);
|
||||||
|
--input: oklch(0.922 0 0);
|
||||||
|
--ring: oklch(0.708 0 0);
|
||||||
|
--chart-1: oklch(0.646 0.222 41.116);
|
||||||
|
--chart-2: oklch(0.6 0.118 184.704);
|
||||||
|
--chart-3: oklch(0.398 0.07 227.392);
|
||||||
|
--chart-4: oklch(0.828 0.189 84.429);
|
||||||
|
--chart-5: oklch(0.769 0.188 70.08);
|
||||||
|
--sidebar: oklch(0.985 0 0);
|
||||||
|
--sidebar-foreground: oklch(0.145 0 0);
|
||||||
|
--sidebar-primary: oklch(0.205 0 0);
|
||||||
|
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||||||
|
--sidebar-accent: oklch(0.97 0 0);
|
||||||
|
--sidebar-accent-foreground: oklch(0.205 0 0);
|
||||||
|
--sidebar-border: oklch(0.922 0 0);
|
||||||
|
--sidebar-ring: oklch(0.708 0 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dark {
|
||||||
|
--background: oklch(0.145 0 0);
|
||||||
|
--foreground: oklch(0.985 0 0);
|
||||||
|
--card: oklch(0.205 0 0);
|
||||||
|
--card-foreground: oklch(0.985 0 0);
|
||||||
|
--popover: oklch(0.205 0 0);
|
||||||
|
--popover-foreground: oklch(0.985 0 0);
|
||||||
|
--primary: oklch(0.922 0 0);
|
||||||
|
--primary-foreground: oklch(0.205 0 0);
|
||||||
|
--secondary: oklch(0.269 0 0);
|
||||||
|
--secondary-foreground: oklch(0.985 0 0);
|
||||||
|
--muted: oklch(0.269 0 0);
|
||||||
|
--muted-foreground: oklch(0.708 0 0);
|
||||||
|
--accent: oklch(0.269 0 0);
|
||||||
|
--accent-foreground: oklch(0.985 0 0);
|
||||||
|
--destructive: oklch(0.704 0.191 22.216);
|
||||||
|
--border: oklch(1 0 0 / 10%);
|
||||||
|
--input: oklch(1 0 0 / 15%);
|
||||||
|
--ring: oklch(0.556 0 0);
|
||||||
|
--chart-1: oklch(0.488 0.243 264.376);
|
||||||
|
--chart-2: oklch(0.696 0.17 162.48);
|
||||||
|
--chart-3: oklch(0.769 0.188 70.08);
|
||||||
|
--chart-4: oklch(0.627 0.265 303.9);
|
||||||
|
--chart-5: oklch(0.645 0.246 16.439);
|
||||||
|
--sidebar: oklch(0.205 0 0);
|
||||||
|
--sidebar-foreground: oklch(0.985 0 0);
|
||||||
|
--sidebar-primary: oklch(0.488 0.243 264.376);
|
||||||
|
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||||||
|
--sidebar-accent: oklch(0.269 0 0);
|
||||||
|
--sidebar-accent-foreground: oklch(0.985 0 0);
|
||||||
|
--sidebar-border: oklch(1 0 0 / 10%);
|
||||||
|
--sidebar-ring: oklch(0.556 0 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@layer base {
|
||||||
|
* {
|
||||||
|
@apply border-border outline-ring/50;
|
||||||
|
}
|
||||||
body {
|
body {
|
||||||
background: var(--background);
|
@apply bg-background text-foreground;
|
||||||
color: var(--foreground);
|
}
|
||||||
font-family: Arial, Helvetica, sans-serif;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,65 +1,264 @@
|
|||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
import UploadArea from "@/components/UploadArea";
|
||||||
|
import {
|
||||||
|
NavigationMenu,
|
||||||
|
NavigationMenuList,
|
||||||
|
NavigationMenuItem,
|
||||||
|
NavigationMenuLink,
|
||||||
|
} from "@/components/ui/navigation-menu";
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
return (
|
return (
|
||||||
<div className="flex min-h-screen items-center justify-center bg-zinc-50 font-sans dark:bg-black">
|
<div className="min-h-screen" style={{ backgroundColor: "#FFFFFF" }}>
|
||||||
<main className="flex min-h-screen w-full max-w-3xl flex-col items-center justify-between py-32 px-16 bg-white dark:bg-black sm:items-start">
|
{/* Sticky Navigation */}
|
||||||
<Image
|
<nav className="sticky top-0 z-50 bg-white/95 backdrop-blur-sm border-b border-gray-200">
|
||||||
className="dark:invert"
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||||
src="/next.svg"
|
<div className="flex justify-between items-center h-16">
|
||||||
alt="Next.js logo"
|
{/* Logo */}
|
||||||
width={100}
|
<div className="flex-shrink-0">
|
||||||
height={20}
|
<Image src="/reya2.svg" alt="Reya Logo" width={120} height={40} />
|
||||||
priority
|
</div>
|
||||||
/>
|
|
||||||
<div className="flex flex-col items-center gap-6 text-center sm:items-start sm:text-left">
|
{/* Navigation Menu */}
|
||||||
<h1 className="max-w-xs text-3xl font-semibold leading-10 tracking-tight text-black dark:text-zinc-50">
|
<NavigationMenu className="hidden md:flex">
|
||||||
To get started, edit the page.tsx file.
|
<NavigationMenuList>
|
||||||
</h1>
|
<NavigationMenuItem>
|
||||||
<p className="max-w-md text-lg leading-8 text-zinc-600 dark:text-zinc-400">
|
<NavigationMenuLink
|
||||||
Looking for a starting point or more instructions? Head over to{" "}
|
href="/contact"
|
||||||
<a
|
className="px-4 py-2 text-gray-700 hover:text-gray-900"
|
||||||
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
|
||||||
className="font-medium text-zinc-950 dark:text-zinc-50"
|
|
||||||
>
|
>
|
||||||
Templates
|
Contact
|
||||||
</a>{" "}
|
</NavigationMenuLink>
|
||||||
or the{" "}
|
</NavigationMenuItem>
|
||||||
<a
|
<NavigationMenuItem>
|
||||||
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
<NavigationMenuLink
|
||||||
className="font-medium text-zinc-950 dark:text-zinc-50"
|
href="/pricing"
|
||||||
|
className="px-4 py-2 text-gray-700 hover:text-gray-900"
|
||||||
>
|
>
|
||||||
Learning
|
Pricing
|
||||||
</a>{" "}
|
</NavigationMenuLink>
|
||||||
center.
|
</NavigationMenuItem>
|
||||||
</p>
|
</NavigationMenuList>
|
||||||
|
</NavigationMenu>
|
||||||
|
|
||||||
|
{/* Auth Buttons */}
|
||||||
|
<div className="flex items-center space-x-4">
|
||||||
|
<button className="text-gray-700 hover:text-gray-900 px-4 py-2 border border-gray-300 hover:border-gray-400 rounded-md cursor-pointer transition-colors">
|
||||||
|
Log In
|
||||||
|
</button>
|
||||||
|
<button className="bg-orange-500 hover:bg-orange-600 text-white px-6 py-2 rounded-md font-medium cursor-pointer transition-colors">
|
||||||
|
Sign Up
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-4 text-base font-medium sm:flex-row">
|
</div>
|
||||||
<a
|
</nav>
|
||||||
className="flex h-12 w-full items-center justify-center gap-2 rounded-full bg-foreground px-5 text-background transition-colors hover:bg-[#383838] dark:hover:bg-[#ccc] md:w-[158px]"
|
|
||||||
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
{/* Main Content */}
|
||||||
target="_blank"
|
<div className="flex items-center justify-center min-h-screen">
|
||||||
rel="noopener noreferrer"
|
<div
|
||||||
|
className="w-[90%] h-[600px] rounded-2xl flex justify-between items-start relative"
|
||||||
|
style={{
|
||||||
|
backgroundColor: "#FF580F",
|
||||||
|
margin: "20px",
|
||||||
|
marginTop: "5px",
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Image
|
<div className="ml-8 mt-16">
|
||||||
className="dark:invert"
|
<h1
|
||||||
src="/vercel.svg"
|
className="text-white text-6xl font-bold mb-4"
|
||||||
alt="Vercel logomark"
|
style={{ fontFamily: "Inter, sans-serif" }}
|
||||||
width={16}
|
>
|
||||||
height={16}
|
Welcome to reYa Render
|
||||||
/>
|
</h1>
|
||||||
Deploy Now
|
<p
|
||||||
</a>
|
className="text-white text-2xl"
|
||||||
<a
|
style={{ width: "600px", fontFamily: "Inter, sans-serif" }}
|
||||||
className="flex h-12 w-full items-center justify-center rounded-full border border-solid border-black/[.08] px-5 transition-colors hover:border-transparent hover:bg-black/[.04] dark:border-white/[.145] dark:hover:bg-[#1a1a1a] md:w-[158px]"
|
|
||||||
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
>
|
||||||
Documentation
|
Whether you're working on animations, visualizations, or still
|
||||||
</a>
|
images, our render farm is optimized to deliver high-quality
|
||||||
|
results efficiently. Simply upload your files, and our system will
|
||||||
|
take care of the rendering process, saving you time and computing
|
||||||
|
power so you can focus on the creative side of your work.
|
||||||
|
</p>
|
||||||
|
<div className="flex gap-4 mt-6">
|
||||||
|
<button className="bg-white text-orange-500 px-6 py-3 rounded-md font-medium hover:bg-gray-100 cursor-pointer transition-colors">
|
||||||
|
Contact Us
|
||||||
|
</button>
|
||||||
|
<button className="bg-transparent border-2 border-white text-white px-6 py-3 rounded-md font-medium hover:bg-white hover:text-orange-500 cursor-pointer transition-colors">
|
||||||
|
Get Started
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="absolute bottom-4 right-4">
|
||||||
|
<img
|
||||||
|
src="/3d-model.png"
|
||||||
|
alt="3D Model"
|
||||||
|
width={500}
|
||||||
|
height={500}
|
||||||
|
style={{ display: "block" }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Pricing Section */}
|
||||||
|
<div className="flex justify-center mt-16 mb-16">
|
||||||
|
<div className="w-[90%]">
|
||||||
|
<h2 className="text-4xl font-bold text-center mb-12 text-gray-800">
|
||||||
|
Available Plans
|
||||||
|
</h2>
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
|
||||||
|
{/* Basic Plan */}
|
||||||
|
<div className="bg-white border-2 border-gray-200 rounded-xl p-8 text-center transition-all hover:transform hover:-translate-y-2 hover:shadow-xl hover:border-orange-500 cursor-pointer">
|
||||||
|
<div className="text-2xl font-bold text-orange-500 mb-2">
|
||||||
|
Basic
|
||||||
|
</div>
|
||||||
|
<div className="text-4xl font-bold text-orange-500 mb-8">
|
||||||
|
€9.99{" "}
|
||||||
|
<span className="text-lg font-normal text-gray-500">
|
||||||
|
/month
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<button className="w-full bg-orange-500 text-white py-3 px-6 rounded-lg font-medium mb-6 hover:bg-orange-600 transition-colors hover:cursor-pointer">
|
||||||
|
Choose Basic
|
||||||
|
</button>
|
||||||
|
<ul className="text-left space-y-3">
|
||||||
|
<li className="flex items-center">
|
||||||
|
<span className="text-green-500 font-bold mr-3">✓</span>
|
||||||
|
250 GB storage
|
||||||
|
</li>
|
||||||
|
<li className="flex items-center">
|
||||||
|
<span className="text-green-500 font-bold mr-3">✓</span>
|
||||||
|
Custom subdomain
|
||||||
|
</li>
|
||||||
|
<li className="flex items-center">
|
||||||
|
<span className="text-green-500 font-bold mr-3">✓</span>
|
||||||
|
Unlimited downloads
|
||||||
|
</li>
|
||||||
|
<li className="flex items-center">
|
||||||
|
<span className="text-green-500 font-bold mr-3">✓</span>
|
||||||
|
Password protected links
|
||||||
|
</li>
|
||||||
|
<li className="flex items-center">
|
||||||
|
<span className="text-green-500 font-bold mr-3">✓</span>
|
||||||
|
Basic analytics
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Standard Plan */}
|
||||||
|
<div className="bg-white border-2 border-gray-200 rounded-xl p-8 text-center transition-all hover:transform hover:-translate-y-2 hover:shadow-xl hover:border-orange-500 cursor-pointer">
|
||||||
|
<div className="text-2xl font-bold text-orange-500 mb-2">
|
||||||
|
Standard
|
||||||
|
</div>
|
||||||
|
<div className="text-4xl font-bold text-orange-500 mb-8">
|
||||||
|
€19.99{" "}
|
||||||
|
<span className="text-lg font-normal text-gray-500">
|
||||||
|
/month
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<button className="w-full bg-orange-500 text-white py-3 px-6 rounded-lg font-medium mb-6 hover:bg-orange-600 transition-colors hover:cursor-pointer">
|
||||||
|
Choose Standard
|
||||||
|
</button>
|
||||||
|
<ul className="text-left space-y-3">
|
||||||
|
<li className="flex items-center">
|
||||||
|
<span className="text-green-500 font-bold mr-3">✓</span>
|
||||||
|
500 GB storage
|
||||||
|
</li>
|
||||||
|
<li className="flex items-center">
|
||||||
|
<span className="text-green-500 font-bold mr-3">✓</span>
|
||||||
|
Custom subdomain
|
||||||
|
</li>
|
||||||
|
<li className="flex items-center">
|
||||||
|
<span className="text-green-500 font-bold mr-3">✓</span>
|
||||||
|
Unlimited downloads
|
||||||
|
</li>
|
||||||
|
<li className="flex items-center">
|
||||||
|
<span className="text-green-500 font-bold mr-3">✓</span>
|
||||||
|
Password protected links
|
||||||
|
</li>
|
||||||
|
<li className="flex items-center">
|
||||||
|
<span className="text-green-500 font-bold mr-3">✓</span>
|
||||||
|
Basic analytics
|
||||||
|
</li>
|
||||||
|
<li className="flex items-center">
|
||||||
|
<span className="text-green-500 font-bold mr-3">✓</span>
|
||||||
|
Custom branding & logo
|
||||||
|
</li>
|
||||||
|
<li className="flex items-center">
|
||||||
|
<span className="text-green-500 font-bold mr-3">✓</span>
|
||||||
|
Priority support
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Premium Plan */}
|
||||||
|
<div className="bg-white border-2 border-gray-200 rounded-xl p-8 text-center transition-all hover:transform hover:-translate-y-2 hover:shadow-xl hover:border-orange-500 cursor-pointer">
|
||||||
|
<div className="text-2xl font-bold text-orange-500 mb-2">
|
||||||
|
Premium
|
||||||
|
</div>
|
||||||
|
<div className="text-4xl font-bold text-orange-500 mb-8">
|
||||||
|
€39.99{" "}
|
||||||
|
<span className="text-lg font-normal text-gray-500">
|
||||||
|
/month
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<button className="w-full bg-orange-500 text-white py-3 px-6 rounded-lg font-medium mb-6 hover:bg-orange-600 transition-colors hover:cursor-pointer">
|
||||||
|
Choose Premium
|
||||||
|
</button>
|
||||||
|
<ul className="text-left space-y-3">
|
||||||
|
<li className="flex items-center">
|
||||||
|
<span className="text-green-500 font-bold mr-3">✓</span>1 TB
|
||||||
|
storage
|
||||||
|
</li>
|
||||||
|
<li className="flex items-center">
|
||||||
|
<span className="text-green-500 font-bold mr-3">✓</span>
|
||||||
|
Custom subdomain
|
||||||
|
</li>
|
||||||
|
<li className="flex items-center">
|
||||||
|
<span className="text-green-500 font-bold mr-3">✓</span>
|
||||||
|
Unlimited downloads
|
||||||
|
</li>
|
||||||
|
<li className="flex items-center">
|
||||||
|
<span className="text-green-500 font-bold mr-3">✓</span>
|
||||||
|
Password protected links
|
||||||
|
</li>
|
||||||
|
<li className="flex items-center">
|
||||||
|
<span className="text-green-500 font-bold mr-3">✓</span>
|
||||||
|
Basic analytics
|
||||||
|
</li>
|
||||||
|
<li className="flex items-center">
|
||||||
|
<span className="text-green-500 font-bold mr-3">✓</span>
|
||||||
|
Custom branding & logo
|
||||||
|
</li>
|
||||||
|
<li className="flex items-center">
|
||||||
|
<span className="text-green-500 font-bold mr-3">✓</span>
|
||||||
|
Priority support
|
||||||
|
</li>
|
||||||
|
<li className="flex items-center">
|
||||||
|
<span className="text-green-500 font-bold mr-3">✓</span>
|
||||||
|
Multiple user accounts
|
||||||
|
</li>
|
||||||
|
<li className="flex items-center">
|
||||||
|
<span className="text-green-500 font-bold mr-3">✓</span>
|
||||||
|
Team management
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Upload area below hero, same width as orange container */}
|
||||||
|
<div className="flex justify-center">
|
||||||
|
<div className="w-[90%] mt-6 mb-12">
|
||||||
|
<h2 className="text-4xl font-bold text-left mb-8 text-gray-800">
|
||||||
|
Start by uploading at least one .blend file
|
||||||
|
</h2>
|
||||||
|
<UploadArea className="w-full shadow-sm" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://ui.shadcn.com/schema.json",
|
||||||
|
"style": "new-york",
|
||||||
|
"rsc": true,
|
||||||
|
"tsx": true,
|
||||||
|
"tailwind": {
|
||||||
|
"config": "",
|
||||||
|
"css": "app/globals.css",
|
||||||
|
"baseColor": "neutral",
|
||||||
|
"cssVariables": true,
|
||||||
|
"prefix": ""
|
||||||
|
},
|
||||||
|
"iconLibrary": "lucide",
|
||||||
|
"aliases": {
|
||||||
|
"components": "@/components",
|
||||||
|
"utils": "@/lib/utils",
|
||||||
|
"ui": "@/components/ui",
|
||||||
|
"lib": "@/lib",
|
||||||
|
"hooks": "@/hooks"
|
||||||
|
},
|
||||||
|
"registries": {}
|
||||||
|
}
|
||||||
@ -0,0 +1,89 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import React, { useRef, useState } from "react";
|
||||||
|
|
||||||
|
type UploadAreaProps = {
|
||||||
|
onFilesSelected?: (files: FileList) => void;
|
||||||
|
className?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function UploadArea({
|
||||||
|
onFilesSelected,
|
||||||
|
className,
|
||||||
|
}: UploadAreaProps) {
|
||||||
|
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||||
|
const [isDragging, setIsDragging] = useState(false);
|
||||||
|
|
||||||
|
const handleClick = () => fileInputRef.current?.click();
|
||||||
|
|
||||||
|
const handleDragOver: React.DragEventHandler<HTMLDivElement> = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setIsDragging(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDragLeave: React.DragEventHandler<HTMLDivElement> = () => {
|
||||||
|
setIsDragging(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDrop: React.DragEventHandler<HTMLDivElement> = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setIsDragging(false);
|
||||||
|
if (e.dataTransfer?.files?.length) {
|
||||||
|
onFilesSelected?.(e.dataTransfer.files);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleChange: React.ChangeEventHandler<HTMLInputElement> = (e) => {
|
||||||
|
if (e.target.files?.length) {
|
||||||
|
onFilesSelected?.(e.target.files);
|
||||||
|
// reset input to allow re-selecting the same file
|
||||||
|
e.currentTarget.value = "";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
role="button"
|
||||||
|
aria-label="Upload files"
|
||||||
|
onClick={handleClick}
|
||||||
|
onDragOver={handleDragOver}
|
||||||
|
onDragLeave={handleDragLeave}
|
||||||
|
onDrop={handleDrop}
|
||||||
|
className={`rounded-xl border-2 border-dashed bg-white text-gray-700 cursor-pointer transition-colors ${
|
||||||
|
isDragging
|
||||||
|
? "border-gray-400 bg-gray-50"
|
||||||
|
: "border-gray-300 hover:border-gray-400"
|
||||||
|
} ${
|
||||||
|
// striped background (grey interrupted)
|
||||||
|
"[background-image:repeating-linear-gradient(135deg,rgba(0,0,0,0.06)_0_10px,transparent_10px_20px)]"
|
||||||
|
} p-10 min-h-56 text-center select-none ${className ?? ""}`}
|
||||||
|
>
|
||||||
|
<div className="mx-auto mb-3 flex h-12 w-12 items-center justify-center rounded-lg border border-gray-300 bg-white/60">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
className="h-6 w-6 text-gray-700"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth="2"
|
||||||
|
d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div className="text-lg font-medium text-gray-700">Upload files here</div>
|
||||||
|
<div className="mt-1 text-sm text-gray-500">or click to browse files</div>
|
||||||
|
|
||||||
|
<input
|
||||||
|
ref={fileInputRef}
|
||||||
|
type="file"
|
||||||
|
multiple
|
||||||
|
className="hidden"
|
||||||
|
onChange={handleChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,168 @@
|
|||||||
|
import * as React from "react"
|
||||||
|
import * as NavigationMenuPrimitive from "@radix-ui/react-navigation-menu"
|
||||||
|
import { cva } from "class-variance-authority"
|
||||||
|
import { ChevronDownIcon } from "lucide-react"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
function NavigationMenu({
|
||||||
|
className,
|
||||||
|
children,
|
||||||
|
viewport = true,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof NavigationMenuPrimitive.Root> & {
|
||||||
|
viewport?: boolean
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<NavigationMenuPrimitive.Root
|
||||||
|
data-slot="navigation-menu"
|
||||||
|
data-viewport={viewport}
|
||||||
|
className={cn(
|
||||||
|
"group/navigation-menu relative flex max-w-max flex-1 items-center justify-center",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
{viewport && <NavigationMenuViewport />}
|
||||||
|
</NavigationMenuPrimitive.Root>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function NavigationMenuList({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof NavigationMenuPrimitive.List>) {
|
||||||
|
return (
|
||||||
|
<NavigationMenuPrimitive.List
|
||||||
|
data-slot="navigation-menu-list"
|
||||||
|
className={cn(
|
||||||
|
"group flex flex-1 list-none items-center justify-center gap-1",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function NavigationMenuItem({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof NavigationMenuPrimitive.Item>) {
|
||||||
|
return (
|
||||||
|
<NavigationMenuPrimitive.Item
|
||||||
|
data-slot="navigation-menu-item"
|
||||||
|
className={cn("relative", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const navigationMenuTriggerStyle = cva(
|
||||||
|
"group inline-flex h-9 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=open]:hover:bg-accent data-[state=open]:text-accent-foreground data-[state=open]:focus:bg-accent data-[state=open]:bg-accent/50 focus-visible:ring-ring/50 outline-none transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1"
|
||||||
|
)
|
||||||
|
|
||||||
|
function NavigationMenuTrigger({
|
||||||
|
className,
|
||||||
|
children,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof NavigationMenuPrimitive.Trigger>) {
|
||||||
|
return (
|
||||||
|
<NavigationMenuPrimitive.Trigger
|
||||||
|
data-slot="navigation-menu-trigger"
|
||||||
|
className={cn(navigationMenuTriggerStyle(), "group", className)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{children}{" "}
|
||||||
|
<ChevronDownIcon
|
||||||
|
className="relative top-[1px] ml-1 size-3 transition duration-300 group-data-[state=open]:rotate-180"
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
</NavigationMenuPrimitive.Trigger>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function NavigationMenuContent({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof NavigationMenuPrimitive.Content>) {
|
||||||
|
return (
|
||||||
|
<NavigationMenuPrimitive.Content
|
||||||
|
data-slot="navigation-menu-content"
|
||||||
|
className={cn(
|
||||||
|
"data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 top-0 left-0 w-full p-2 pr-2.5 md:absolute md:w-auto",
|
||||||
|
"group-data-[viewport=false]/navigation-menu:bg-popover group-data-[viewport=false]/navigation-menu:text-popover-foreground group-data-[viewport=false]/navigation-menu:data-[state=open]:animate-in group-data-[viewport=false]/navigation-menu:data-[state=closed]:animate-out group-data-[viewport=false]/navigation-menu:data-[state=closed]:zoom-out-95 group-data-[viewport=false]/navigation-menu:data-[state=open]:zoom-in-95 group-data-[viewport=false]/navigation-menu:data-[state=open]:fade-in-0 group-data-[viewport=false]/navigation-menu:data-[state=closed]:fade-out-0 group-data-[viewport=false]/navigation-menu:top-full group-data-[viewport=false]/navigation-menu:mt-1.5 group-data-[viewport=false]/navigation-menu:overflow-hidden group-data-[viewport=false]/navigation-menu:rounded-md group-data-[viewport=false]/navigation-menu:border group-data-[viewport=false]/navigation-menu:shadow group-data-[viewport=false]/navigation-menu:duration-200 **:data-[slot=navigation-menu-link]:focus:ring-0 **:data-[slot=navigation-menu-link]:focus:outline-none",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function NavigationMenuViewport({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof NavigationMenuPrimitive.Viewport>) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"absolute top-full left-0 isolate z-50 flex justify-center"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<NavigationMenuPrimitive.Viewport
|
||||||
|
data-slot="navigation-menu-viewport"
|
||||||
|
className={cn(
|
||||||
|
"origin-top-center bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border shadow md:w-[var(--radix-navigation-menu-viewport-width)]",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function NavigationMenuLink({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof NavigationMenuPrimitive.Link>) {
|
||||||
|
return (
|
||||||
|
<NavigationMenuPrimitive.Link
|
||||||
|
data-slot="navigation-menu-link"
|
||||||
|
className={cn(
|
||||||
|
"data-[active=true]:focus:bg-accent data-[active=true]:hover:bg-accent data-[active=true]:bg-accent/50 data-[active=true]:text-accent-foreground hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus-visible:ring-ring/50 [&_svg:not([class*='text-'])]:text-muted-foreground flex flex-col gap-1 rounded-sm p-2 text-sm transition-all outline-none focus-visible:ring-[3px] focus-visible:outline-1 [&_svg:not([class*='size-'])]:size-4",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function NavigationMenuIndicator({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof NavigationMenuPrimitive.Indicator>) {
|
||||||
|
return (
|
||||||
|
<NavigationMenuPrimitive.Indicator
|
||||||
|
data-slot="navigation-menu-indicator"
|
||||||
|
className={cn(
|
||||||
|
"data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in top-full z-[1] flex h-1.5 items-end justify-center overflow-hidden",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<div className="bg-border relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm shadow-md" />
|
||||||
|
</NavigationMenuPrimitive.Indicator>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
NavigationMenu,
|
||||||
|
NavigationMenuList,
|
||||||
|
NavigationMenuItem,
|
||||||
|
NavigationMenuContent,
|
||||||
|
NavigationMenuTrigger,
|
||||||
|
NavigationMenuLink,
|
||||||
|
NavigationMenuIndicator,
|
||||||
|
NavigationMenuViewport,
|
||||||
|
navigationMenuTriggerStyle,
|
||||||
|
}
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
import { clsx, type ClassValue } from "clsx"
|
||||||
|
import { twMerge } from "tailwind-merge"
|
||||||
|
|
||||||
|
export function cn(...inputs: ClassValue[]) {
|
||||||
|
return twMerge(clsx(inputs))
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
Loading…
Reference in new issue