mirror of
https://github.com/0xShay/ticketchain.git
synced 2026-01-11 21:23:24 +00:00
Merge pull request #4 from Ayush272002/sid-frontend-initial
Sid frontend initial - removing package-lock.json to reinitialise it
This commit is contained in:
60
app/Home.tsx
60
app/Home.tsx
@@ -1,40 +1,36 @@
|
|||||||
import Header from '../components/custom/header';
|
import Header from '../components/custom/header';
|
||||||
import Footer from '../components/custom/footer';
|
import Footer from '../components/custom/footer';
|
||||||
|
import Test from '../components/scripts/Test';
|
||||||
|
import MetaMask from '../components/scripts/MetaMask';
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
return (
|
return (
|
||||||
<>
|
<div className="container mx-auto p-4">
|
||||||
<Header />
|
<div>
|
||||||
<div className="bg-gradient-to-b from-blue-900 to-gray-900 min-h-screen pt-16">
|
<Header />
|
||||||
<div className="container mx-auto p-4">
|
{/* Other page content */}
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
placeholder="Search events..."
|
|
||||||
className="search-bar mt-4 p-2 border border-gray-300 rounded w-full max-w-md mx-auto"
|
|
||||||
/>
|
|
||||||
<main>
|
|
||||||
<section className="mb-8">
|
|
||||||
<h2 className="text-2xl font-semibold text-white mb-4">
|
|
||||||
Featured Events
|
|
||||||
</h2>
|
|
||||||
<p className="text-gray-300">
|
|
||||||
No events available at the moment.
|
|
||||||
</p>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<h2 className="text-2xl font-semibold text-white mb-4">
|
|
||||||
Upcoming Events
|
|
||||||
</h2>
|
|
||||||
<ul className="list-disc list-inside text-gray-300">
|
|
||||||
<li>Event 1 - Date</li>
|
|
||||||
<li>Event 2 - Date</li>
|
|
||||||
<li>Event 3 - Date</li>
|
|
||||||
</ul>
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
<Footer />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</>
|
<main>
|
||||||
|
<section className="mb-8">
|
||||||
|
<h2 className="text-2xl font-semibold mb-4">Featured Events</h2>
|
||||||
|
<p className="text-gray-600">No events available at the moment.</p>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<h2 className="text-2xl font-semibold mb-4">Upcoming Events</h2>
|
||||||
|
<ul className="list-disc list-inside">
|
||||||
|
<li>Event 1 - Date</li>
|
||||||
|
<li>Event 2 - Date</li>
|
||||||
|
<li>Event 3 - Date</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
<section className="mb-8">
|
||||||
|
<Test />
|
||||||
|
</section>
|
||||||
|
<section className="mb-8">
|
||||||
|
<MetaMask />
|
||||||
|
</section>
|
||||||
|
<Footer />
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import EventDescription from '@/components/custom/EventDescription';
|
||||||
import Home from './Home';
|
import Home from './Home';
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
|
|||||||
64
components/Confirmation.tsx
Normal file
64
components/Confirmation.tsx
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
'use client';
|
||||||
|
import React from 'react';
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
CardHeader,
|
||||||
|
CardContent,
|
||||||
|
CardFooter,
|
||||||
|
} from '@/components/ui/card';
|
||||||
|
import { Badge } from '@/components/ui/badge';
|
||||||
|
import { Separator } from '@/components/ui/separator';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { CheckCircle } from 'lucide-react';
|
||||||
|
|
||||||
|
interface props {
|
||||||
|
ticketTitle: string;
|
||||||
|
eventDate: string;
|
||||||
|
ticketID: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ConfirmationTicket = ({ ticketTitle, eventDate, ticketID }: props) => {
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
className="max-w-md mx-auto bg-blue-900 text-blue-100 rounded-xl shadow-md p-6
|
||||||
|
transition-all duration-300 hover:shadow-[0px_0px_20px_5px_rgba(59,130,246,0.5)]"
|
||||||
|
>
|
||||||
|
<CardHeader className="flex flex-col items-center space-y-4">
|
||||||
|
<CheckCircle className="text-green-400 w-10 h-10" />
|
||||||
|
<h1 className="text-2xl font-semibold text-blue-50">
|
||||||
|
Ticket Confirmed!
|
||||||
|
</h1>
|
||||||
|
<Badge
|
||||||
|
variant="outline"
|
||||||
|
className="text-blue-200 bg-blue-800 px-3 py-1 rounded-full"
|
||||||
|
>
|
||||||
|
#{ticketID}
|
||||||
|
</Badge>
|
||||||
|
</CardHeader>
|
||||||
|
|
||||||
|
<CardContent className="text-center">
|
||||||
|
<Separator className="my-4 border-blue-700" />
|
||||||
|
<h2 className="text-lg font-medium">{ticketTitle}</h2>
|
||||||
|
<p className="text-blue-300 mt-2">Event Date: {eventDate}</p>
|
||||||
|
<Separator className="my-4 border-blue-700" />
|
||||||
|
</CardContent>
|
||||||
|
|
||||||
|
<CardFooter className="flex flex-col items-center space-y-3">
|
||||||
|
<Button
|
||||||
|
variant="default"
|
||||||
|
className="w-full bg-blue-700 text-blue-100 font-semibold rounded-lg hover:bg-blue-600"
|
||||||
|
>
|
||||||
|
View Ticket
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
className="w-full text-blue-200 border-blue-700 hover:bg-blue-800"
|
||||||
|
>
|
||||||
|
Download PDF
|
||||||
|
</Button>
|
||||||
|
</CardFooter>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ConfirmationTicket;
|
||||||
18
components/EventPage.tsx
Normal file
18
components/EventPage.tsx
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const EventPage = () => {
|
||||||
|
return (
|
||||||
|
// <>Header</>
|
||||||
|
// <EventDescription>
|
||||||
|
// <Footer>
|
||||||
|
<div
|
||||||
|
className="relative bg-black-100 flex
|
||||||
|
justify-center items-center, flex-col overflow-hidden
|
||||||
|
mx-auto sm:px-10 px-5"
|
||||||
|
>
|
||||||
|
<div className="max-w-7xl w-full"></div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default EventPage;
|
||||||
101
components/Profile.tsx
Normal file
101
components/Profile.tsx
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
'use client';
|
||||||
|
import * as React from 'react';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
CardContent,
|
||||||
|
CardDescription,
|
||||||
|
CardFooter,
|
||||||
|
CardHeader,
|
||||||
|
CardTitle,
|
||||||
|
} from '@/components/ui/card';
|
||||||
|
import { Input } from '@/components/ui/input';
|
||||||
|
import { Label } from '@/components/ui/label';
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from '@/components/ui/select';
|
||||||
|
import { motion } from 'framer-motion';
|
||||||
|
|
||||||
|
// Dark theme and animation setup
|
||||||
|
const cardVariants = {
|
||||||
|
hidden: { opacity: 0, y: 10 },
|
||||||
|
visible: { opacity: 1, y: 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
const Profile = () => {
|
||||||
|
return (
|
||||||
|
<motion.div
|
||||||
|
initial="hidden"
|
||||||
|
animate="visible"
|
||||||
|
variants={cardVariants}
|
||||||
|
transition={{ duration: 0.5, ease: 'easeOut' }}
|
||||||
|
>
|
||||||
|
<Card className="w-[350px] bg-[#1e2a3a] text-white shadow-lg">
|
||||||
|
<CardHeader>
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, y: -10 }}
|
||||||
|
animate={{ opacity: 1, y: 0 }}
|
||||||
|
transition={{ delay: 0.1, duration: 0.5 }}
|
||||||
|
>
|
||||||
|
<CardTitle className="text-lg font-semibold">Profile</CardTitle>
|
||||||
|
</motion.div>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<form>
|
||||||
|
<div className="grid w-full items-center gap-4">
|
||||||
|
<motion.div
|
||||||
|
className="flex flex-col space-y-1.5"
|
||||||
|
initial={{ opacity: 0, x: -10 }}
|
||||||
|
animate={{ opacity: 1, x: 0 }}
|
||||||
|
transition={{ delay: 0.2, duration: 0.4 }}
|
||||||
|
>
|
||||||
|
<Label htmlFor="name" className="text-gray-300">
|
||||||
|
Name
|
||||||
|
</Label>
|
||||||
|
<Input
|
||||||
|
id="name"
|
||||||
|
placeholder="Name"
|
||||||
|
className="bg-[#2b3a4a] text-white placeholder-gray-400"
|
||||||
|
/>
|
||||||
|
</motion.div>
|
||||||
|
<motion.div
|
||||||
|
className="flex flex-col space-y-1.5"
|
||||||
|
initial={{ opacity: 0, x: -10 }}
|
||||||
|
animate={{ opacity: 1, x: 0 }}
|
||||||
|
transition={{ delay: 0.3, duration: 0.4 }}
|
||||||
|
>
|
||||||
|
<Label htmlFor="framework" className="text-gray-300">
|
||||||
|
MetaMask Public Key
|
||||||
|
</Label>
|
||||||
|
<Input
|
||||||
|
id="name"
|
||||||
|
placeholder="Key"
|
||||||
|
className="bg-[#2b3a4a] text-white placeholder-gray-400"
|
||||||
|
/>
|
||||||
|
</motion.div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</CardContent>
|
||||||
|
<CardFooter className="flex justify-between">
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, y: 10 }}
|
||||||
|
animate={{ opacity: 1, y: 0 }}
|
||||||
|
transition={{ delay: 0.4, duration: 0.4 }}
|
||||||
|
>
|
||||||
|
<Button className="bg-[#365b85] text-white hover:bg-[#2b4a70]">
|
||||||
|
Save
|
||||||
|
</Button>
|
||||||
|
</motion.div>
|
||||||
|
</CardFooter>
|
||||||
|
</Card>
|
||||||
|
</motion.div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default Profile;
|
||||||
|
// return <EventDescription/>
|
||||||
|
// return <ConfirmationTicket ticketTitle='taylor swift' ticketID='2' eventDate='27th september'/>;
|
||||||
|
// return <Profile/>;
|
||||||
7
components/custom/BuyTicket.tsx
Normal file
7
components/custom/BuyTicket.tsx
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const BuyTicket = () => {
|
||||||
|
return <div>BuyTicket</div>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default BuyTicket;
|
||||||
62
components/custom/EventDescription.tsx
Normal file
62
components/custom/EventDescription.tsx
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
'use client';
|
||||||
|
import React from 'react';
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
CardHeader,
|
||||||
|
CardContent,
|
||||||
|
CardFooter,
|
||||||
|
} from '@/components/ui/card';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { Badge } from '@/components/ui/badge';
|
||||||
|
import { Separator } from '@/components/ui/separator';
|
||||||
|
import ImageCarousel from './ImageCarousel';
|
||||||
|
import BuyTicket from './BuyTicket';
|
||||||
|
import TicketButton from './TicketButton';
|
||||||
|
|
||||||
|
const EventDescription = () => {
|
||||||
|
return (
|
||||||
|
<Card className="pt-10 pb-16 px-6 bg-gradient-to-r from-blue-50 to-gray-50 rounded-xl shadow-lg max-w-4xl mx-auto">
|
||||||
|
<CardHeader className="flex flex-col items-start space-y-4">
|
||||||
|
<h1 className="text-3xl font-semibold text-gray-800">TicketTitle</h1>
|
||||||
|
<Badge
|
||||||
|
variant="outline"
|
||||||
|
className="text-blue-600 bg-blue-100 px-3 py-1 rounded-full"
|
||||||
|
>
|
||||||
|
Price: $99
|
||||||
|
</Badge>
|
||||||
|
</CardHeader>
|
||||||
|
|
||||||
|
<CardContent className="flex flex-col md:flex-row items-start space-y-8 md:space-y-0 md:space-x-10">
|
||||||
|
<div className="md:w-1/2 flex justify-center">
|
||||||
|
<ImageCarousel />
|
||||||
|
</div>
|
||||||
|
<div className="md:w-1/2 text-gray-700">
|
||||||
|
<Separator className="my-4" />
|
||||||
|
<p className="leading-relaxed">
|
||||||
|
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do
|
||||||
|
eiusmod tempor incididunt ut labore et dolore magna aliqua. Lorem
|
||||||
|
ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod
|
||||||
|
tempor incididunt ut labore et dolore magna aliqua. Lorem ipsum
|
||||||
|
dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor
|
||||||
|
incididunt ut labore et dolore magna aliqua.
|
||||||
|
</p>
|
||||||
|
<Separator className="my-4" />
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
|
||||||
|
<CardFooter className="flex flex-col md:flex-row items-center justify-between space-y-4 md:space-y-0">
|
||||||
|
<Button
|
||||||
|
variant="default"
|
||||||
|
className="w-full md:w-auto bg-gradient-to-r from-blue-500 to-purple-600 text-white font-semibold rounded-lg hover:from-blue-600 hover:to-purple-700"
|
||||||
|
>
|
||||||
|
Buy now Using MetaMask
|
||||||
|
</Button>
|
||||||
|
<div className="relative md:left-5">
|
||||||
|
<TicketButton />
|
||||||
|
</div>
|
||||||
|
</CardFooter>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default EventDescription;
|
||||||
33
components/custom/ImageCarousel.tsx
Normal file
33
components/custom/ImageCarousel.tsx
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
import { Card, CardContent } from '@/components/ui/card';
|
||||||
|
import {
|
||||||
|
Carousel,
|
||||||
|
CarouselContent,
|
||||||
|
CarouselItem,
|
||||||
|
CarouselNext,
|
||||||
|
CarouselPrevious,
|
||||||
|
} from '@/components/ui/carousel';
|
||||||
|
|
||||||
|
export default function ImageCarousel() {
|
||||||
|
return (
|
||||||
|
<Carousel className="w-full max-w-xs">
|
||||||
|
<CarouselContent>
|
||||||
|
{/* map your images here from the page */}
|
||||||
|
{Array.from({ length: 5 }).map((_, index) => (
|
||||||
|
<CarouselItem key={index}>
|
||||||
|
<div className="p-1">
|
||||||
|
<Card>
|
||||||
|
<CardContent className="flex aspect-square items-center justify-center p-6">
|
||||||
|
<span className="text-4xl font-semibold">{index + 1}</span>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</CarouselItem>
|
||||||
|
))}
|
||||||
|
</CarouselContent>
|
||||||
|
<CarouselPrevious />
|
||||||
|
<CarouselNext />
|
||||||
|
</Carousel>
|
||||||
|
);
|
||||||
|
}
|
||||||
48
components/custom/TicketButton.tsx
Normal file
48
components/custom/TicketButton.tsx
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import { Button } from '../ui/button'; // Adjust import path to where your shadcn Button component is located
|
||||||
|
|
||||||
|
interface NumberPickerProps {
|
||||||
|
initialCount?: number;
|
||||||
|
min?: number;
|
||||||
|
max?: number;
|
||||||
|
onChange?: (count: number) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const NumberPicker: React.FC<NumberPickerProps> = ({
|
||||||
|
initialCount = 1,
|
||||||
|
min = 1,
|
||||||
|
max = 10,
|
||||||
|
onChange,
|
||||||
|
}) => {
|
||||||
|
const [count, setCount] = useState<number>(initialCount);
|
||||||
|
|
||||||
|
const increment = () => {
|
||||||
|
if (count < max) {
|
||||||
|
const newCount = count + 1;
|
||||||
|
setCount(newCount);
|
||||||
|
onChange && onChange(newCount);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const decrement = () => {
|
||||||
|
if (count > min) {
|
||||||
|
const newCount = count - 1;
|
||||||
|
setCount(newCount);
|
||||||
|
onChange && onChange(newCount);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<Button variant="outline" onClick={decrement} disabled={count === min}>
|
||||||
|
-
|
||||||
|
</Button>
|
||||||
|
<span className="text-center w-8">{count}</span>
|
||||||
|
<Button variant="outline" onClick={increment} disabled={count === max}>
|
||||||
|
+
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default NumberPicker;
|
||||||
16
components/scripts/Test.tsx
Normal file
16
components/scripts/Test.tsx
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
'use client';
|
||||||
|
import React, { useEffect } from 'react';
|
||||||
|
|
||||||
|
const Test = () => {
|
||||||
|
useEffect(() => {
|
||||||
|
console.log('Print some shit');
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<p>Hellao!</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Test;
|
||||||
36
components/ui/badge.tsx
Normal file
36
components/ui/badge.tsx
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import { cva, type VariantProps } from 'class-variance-authority';
|
||||||
|
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
|
const badgeVariants = cva(
|
||||||
|
'inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2',
|
||||||
|
{
|
||||||
|
variants: {
|
||||||
|
variant: {
|
||||||
|
default:
|
||||||
|
'border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80',
|
||||||
|
secondary:
|
||||||
|
'border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80',
|
||||||
|
destructive:
|
||||||
|
'border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80',
|
||||||
|
outline: 'text-foreground',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
variant: 'default',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export interface BadgeProps
|
||||||
|
extends React.HTMLAttributes<HTMLDivElement>,
|
||||||
|
VariantProps<typeof badgeVariants> {}
|
||||||
|
|
||||||
|
function Badge({ className, variant, ...props }: BadgeProps) {
|
||||||
|
return (
|
||||||
|
<div className={cn(badgeVariants({ variant }), className)} {...props} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Badge, badgeVariants };
|
||||||
83
components/ui/card.tsx
Normal file
83
components/ui/card.tsx
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
|
const Card = React.forwardRef<
|
||||||
|
HTMLDivElement,
|
||||||
|
React.HTMLAttributes<HTMLDivElement>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<div
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
'rounded-xl border bg-card text-card-foreground shadow',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
Card.displayName = 'Card';
|
||||||
|
|
||||||
|
const CardHeader = React.forwardRef<
|
||||||
|
HTMLDivElement,
|
||||||
|
React.HTMLAttributes<HTMLDivElement>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<div
|
||||||
|
ref={ref}
|
||||||
|
className={cn('flex flex-col space-y-1.5 p-6', className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
CardHeader.displayName = 'CardHeader';
|
||||||
|
|
||||||
|
const CardTitle = React.forwardRef<
|
||||||
|
HTMLParagraphElement,
|
||||||
|
React.HTMLAttributes<HTMLHeadingElement>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<h3
|
||||||
|
ref={ref}
|
||||||
|
className={cn('font-semibold leading-none tracking-tight', className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
CardTitle.displayName = 'CardTitle';
|
||||||
|
|
||||||
|
const CardDescription = React.forwardRef<
|
||||||
|
HTMLParagraphElement,
|
||||||
|
React.HTMLAttributes<HTMLParagraphElement>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<p
|
||||||
|
ref={ref}
|
||||||
|
className={cn('text-sm text-muted-foreground', className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
CardDescription.displayName = 'CardDescription';
|
||||||
|
|
||||||
|
const CardContent = React.forwardRef<
|
||||||
|
HTMLDivElement,
|
||||||
|
React.HTMLAttributes<HTMLDivElement>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<div ref={ref} className={cn('p-6 pt-0', className)} {...props} />
|
||||||
|
));
|
||||||
|
CardContent.displayName = 'CardContent';
|
||||||
|
|
||||||
|
const CardFooter = React.forwardRef<
|
||||||
|
HTMLDivElement,
|
||||||
|
React.HTMLAttributes<HTMLDivElement>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<div
|
||||||
|
ref={ref}
|
||||||
|
className={cn('flex items-center p-6 pt-0', className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
CardFooter.displayName = 'CardFooter';
|
||||||
|
|
||||||
|
export {
|
||||||
|
Card,
|
||||||
|
CardHeader,
|
||||||
|
CardFooter,
|
||||||
|
CardTitle,
|
||||||
|
CardDescription,
|
||||||
|
CardContent,
|
||||||
|
};
|
||||||
262
components/ui/carousel.tsx
Normal file
262
components/ui/carousel.tsx
Normal file
@@ -0,0 +1,262 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import * as React from 'react';
|
||||||
|
import { ArrowLeftIcon, ArrowRightIcon } from '@radix-ui/react-icons';
|
||||||
|
import useEmblaCarousel, {
|
||||||
|
type UseEmblaCarouselType,
|
||||||
|
} from 'embla-carousel-react';
|
||||||
|
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
|
||||||
|
type CarouselApi = UseEmblaCarouselType[1];
|
||||||
|
type UseCarouselParameters = Parameters<typeof useEmblaCarousel>;
|
||||||
|
type CarouselOptions = UseCarouselParameters[0];
|
||||||
|
type CarouselPlugin = UseCarouselParameters[1];
|
||||||
|
|
||||||
|
type CarouselProps = {
|
||||||
|
opts?: CarouselOptions;
|
||||||
|
plugins?: CarouselPlugin;
|
||||||
|
orientation?: 'horizontal' | 'vertical';
|
||||||
|
setApi?: (api: CarouselApi) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
type CarouselContextProps = {
|
||||||
|
carouselRef: ReturnType<typeof useEmblaCarousel>[0];
|
||||||
|
api: ReturnType<typeof useEmblaCarousel>[1];
|
||||||
|
scrollPrev: () => void;
|
||||||
|
scrollNext: () => void;
|
||||||
|
canScrollPrev: boolean;
|
||||||
|
canScrollNext: boolean;
|
||||||
|
} & CarouselProps;
|
||||||
|
|
||||||
|
const CarouselContext = React.createContext<CarouselContextProps | null>(null);
|
||||||
|
|
||||||
|
function useCarousel() {
|
||||||
|
const context = React.useContext(CarouselContext);
|
||||||
|
|
||||||
|
if (!context) {
|
||||||
|
throw new Error('useCarousel must be used within a <Carousel />');
|
||||||
|
}
|
||||||
|
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Carousel = React.forwardRef<
|
||||||
|
HTMLDivElement,
|
||||||
|
React.HTMLAttributes<HTMLDivElement> & CarouselProps
|
||||||
|
>(
|
||||||
|
(
|
||||||
|
{
|
||||||
|
orientation = 'horizontal',
|
||||||
|
opts,
|
||||||
|
setApi,
|
||||||
|
plugins,
|
||||||
|
className,
|
||||||
|
children,
|
||||||
|
...props
|
||||||
|
},
|
||||||
|
ref
|
||||||
|
) => {
|
||||||
|
const [carouselRef, api] = useEmblaCarousel(
|
||||||
|
{
|
||||||
|
...opts,
|
||||||
|
axis: orientation === 'horizontal' ? 'x' : 'y',
|
||||||
|
},
|
||||||
|
plugins
|
||||||
|
);
|
||||||
|
const [canScrollPrev, setCanScrollPrev] = React.useState(false);
|
||||||
|
const [canScrollNext, setCanScrollNext] = React.useState(false);
|
||||||
|
|
||||||
|
const onSelect = React.useCallback((api: CarouselApi) => {
|
||||||
|
if (!api) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setCanScrollPrev(api.canScrollPrev());
|
||||||
|
setCanScrollNext(api.canScrollNext());
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const scrollPrev = React.useCallback(() => {
|
||||||
|
api?.scrollPrev();
|
||||||
|
}, [api]);
|
||||||
|
|
||||||
|
const scrollNext = React.useCallback(() => {
|
||||||
|
api?.scrollNext();
|
||||||
|
}, [api]);
|
||||||
|
|
||||||
|
const handleKeyDown = React.useCallback(
|
||||||
|
(event: React.KeyboardEvent<HTMLDivElement>) => {
|
||||||
|
if (event.key === 'ArrowLeft') {
|
||||||
|
event.preventDefault();
|
||||||
|
scrollPrev();
|
||||||
|
} else if (event.key === 'ArrowRight') {
|
||||||
|
event.preventDefault();
|
||||||
|
scrollNext();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[scrollPrev, scrollNext]
|
||||||
|
);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (!api || !setApi) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setApi(api);
|
||||||
|
}, [api, setApi]);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (!api) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
onSelect(api);
|
||||||
|
api.on('reInit', onSelect);
|
||||||
|
api.on('select', onSelect);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
api?.off('select', onSelect);
|
||||||
|
};
|
||||||
|
}, [api, onSelect]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<CarouselContext.Provider
|
||||||
|
value={{
|
||||||
|
carouselRef,
|
||||||
|
api: api,
|
||||||
|
opts,
|
||||||
|
orientation:
|
||||||
|
orientation || (opts?.axis === 'y' ? 'vertical' : 'horizontal'),
|
||||||
|
scrollPrev,
|
||||||
|
scrollNext,
|
||||||
|
canScrollPrev,
|
||||||
|
canScrollNext,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
ref={ref}
|
||||||
|
onKeyDownCapture={handleKeyDown}
|
||||||
|
className={cn('relative', className)}
|
||||||
|
role="region"
|
||||||
|
aria-roledescription="carousel"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</CarouselContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
Carousel.displayName = 'Carousel';
|
||||||
|
|
||||||
|
const CarouselContent = React.forwardRef<
|
||||||
|
HTMLDivElement,
|
||||||
|
React.HTMLAttributes<HTMLDivElement>
|
||||||
|
>(({ className, ...props }, ref) => {
|
||||||
|
const { carouselRef, orientation } = useCarousel();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div ref={carouselRef} className="overflow-hidden">
|
||||||
|
<div
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
'flex',
|
||||||
|
orientation === 'horizontal' ? '-ml-4' : '-mt-4 flex-col',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
CarouselContent.displayName = 'CarouselContent';
|
||||||
|
|
||||||
|
const CarouselItem = React.forwardRef<
|
||||||
|
HTMLDivElement,
|
||||||
|
React.HTMLAttributes<HTMLDivElement>
|
||||||
|
>(({ className, ...props }, ref) => {
|
||||||
|
const { orientation } = useCarousel();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
ref={ref}
|
||||||
|
role="group"
|
||||||
|
aria-roledescription="slide"
|
||||||
|
className={cn(
|
||||||
|
'min-w-0 shrink-0 grow-0 basis-full',
|
||||||
|
orientation === 'horizontal' ? 'pl-4' : 'pt-4',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
CarouselItem.displayName = 'CarouselItem';
|
||||||
|
|
||||||
|
const CarouselPrevious = React.forwardRef<
|
||||||
|
HTMLButtonElement,
|
||||||
|
React.ComponentProps<typeof Button>
|
||||||
|
>(({ className, variant = 'outline', size = 'icon', ...props }, ref) => {
|
||||||
|
const { orientation, scrollPrev, canScrollPrev } = useCarousel();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
ref={ref}
|
||||||
|
variant={variant}
|
||||||
|
size={size}
|
||||||
|
className={cn(
|
||||||
|
'absolute h-8 w-8 rounded-full',
|
||||||
|
orientation === 'horizontal'
|
||||||
|
? '-left-12 top-1/2 -translate-y-1/2'
|
||||||
|
: '-top-12 left-1/2 -translate-x-1/2 rotate-90',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
disabled={!canScrollPrev}
|
||||||
|
onClick={scrollPrev}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<ArrowLeftIcon className="h-4 w-4" />
|
||||||
|
<span className="sr-only">Previous slide</span>
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
CarouselPrevious.displayName = 'CarouselPrevious';
|
||||||
|
|
||||||
|
const CarouselNext = React.forwardRef<
|
||||||
|
HTMLButtonElement,
|
||||||
|
React.ComponentProps<typeof Button>
|
||||||
|
>(({ className, variant = 'outline', size = 'icon', ...props }, ref) => {
|
||||||
|
const { orientation, scrollNext, canScrollNext } = useCarousel();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
ref={ref}
|
||||||
|
variant={variant}
|
||||||
|
size={size}
|
||||||
|
className={cn(
|
||||||
|
'absolute h-8 w-8 rounded-full',
|
||||||
|
orientation === 'horizontal'
|
||||||
|
? '-right-12 top-1/2 -translate-y-1/2'
|
||||||
|
: '-bottom-12 left-1/2 -translate-x-1/2 rotate-90',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
disabled={!canScrollNext}
|
||||||
|
onClick={scrollNext}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<ArrowRightIcon className="h-4 w-4" />
|
||||||
|
<span className="sr-only">Next slide</span>
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
CarouselNext.displayName = 'CarouselNext';
|
||||||
|
|
||||||
|
export {
|
||||||
|
type CarouselApi,
|
||||||
|
Carousel,
|
||||||
|
CarouselContent,
|
||||||
|
CarouselItem,
|
||||||
|
CarouselPrevious,
|
||||||
|
CarouselNext,
|
||||||
|
};
|
||||||
25
components/ui/input.tsx
Normal file
25
components/ui/input.tsx
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
|
export interface InputProps
|
||||||
|
extends React.InputHTMLAttributes<HTMLInputElement> {}
|
||||||
|
|
||||||
|
const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
||||||
|
({ className, type, ...props }, ref) => {
|
||||||
|
return (
|
||||||
|
<input
|
||||||
|
type={type}
|
||||||
|
className={cn(
|
||||||
|
'flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
ref={ref}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
Input.displayName = 'Input';
|
||||||
|
|
||||||
|
export { Input };
|
||||||
26
components/ui/label.tsx
Normal file
26
components/ui/label.tsx
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import * as React from 'react';
|
||||||
|
import * as LabelPrimitive from '@radix-ui/react-label';
|
||||||
|
import { cva, type VariantProps } from 'class-variance-authority';
|
||||||
|
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
|
const labelVariants = cva(
|
||||||
|
'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70'
|
||||||
|
);
|
||||||
|
|
||||||
|
const Label = React.forwardRef<
|
||||||
|
React.ElementRef<typeof LabelPrimitive.Root>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
|
||||||
|
VariantProps<typeof labelVariants>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<LabelPrimitive.Root
|
||||||
|
ref={ref}
|
||||||
|
className={cn(labelVariants(), className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
Label.displayName = LabelPrimitive.Root.displayName;
|
||||||
|
|
||||||
|
export { Label };
|
||||||
164
components/ui/select.tsx
Normal file
164
components/ui/select.tsx
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import * as React from 'react';
|
||||||
|
import {
|
||||||
|
CaretSortIcon,
|
||||||
|
CheckIcon,
|
||||||
|
ChevronDownIcon,
|
||||||
|
ChevronUpIcon,
|
||||||
|
} from '@radix-ui/react-icons';
|
||||||
|
import * as SelectPrimitive from '@radix-ui/react-select';
|
||||||
|
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
|
const Select = SelectPrimitive.Root;
|
||||||
|
|
||||||
|
const SelectGroup = SelectPrimitive.Group;
|
||||||
|
|
||||||
|
const SelectValue = SelectPrimitive.Value;
|
||||||
|
|
||||||
|
const SelectTrigger = React.forwardRef<
|
||||||
|
React.ElementRef<typeof SelectPrimitive.Trigger>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
|
||||||
|
>(({ className, children, ...props }, ref) => (
|
||||||
|
<SelectPrimitive.Trigger
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
'flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
<SelectPrimitive.Icon asChild>
|
||||||
|
<CaretSortIcon className="h-4 w-4 opacity-50" />
|
||||||
|
</SelectPrimitive.Icon>
|
||||||
|
</SelectPrimitive.Trigger>
|
||||||
|
));
|
||||||
|
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
|
||||||
|
|
||||||
|
const SelectScrollUpButton = React.forwardRef<
|
||||||
|
React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<SelectPrimitive.ScrollUpButton
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
'flex cursor-default items-center justify-center py-1',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<ChevronUpIcon />
|
||||||
|
</SelectPrimitive.ScrollUpButton>
|
||||||
|
));
|
||||||
|
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
|
||||||
|
|
||||||
|
const SelectScrollDownButton = React.forwardRef<
|
||||||
|
React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<SelectPrimitive.ScrollDownButton
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
'flex cursor-default items-center justify-center py-1',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<ChevronDownIcon />
|
||||||
|
</SelectPrimitive.ScrollDownButton>
|
||||||
|
));
|
||||||
|
SelectScrollDownButton.displayName =
|
||||||
|
SelectPrimitive.ScrollDownButton.displayName;
|
||||||
|
|
||||||
|
const SelectContent = React.forwardRef<
|
||||||
|
React.ElementRef<typeof SelectPrimitive.Content>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
|
||||||
|
>(({ className, children, position = 'popper', ...props }, ref) => (
|
||||||
|
<SelectPrimitive.Portal>
|
||||||
|
<SelectPrimitive.Content
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
'relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
||||||
|
position === 'popper' &&
|
||||||
|
'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
position={position}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<SelectScrollUpButton />
|
||||||
|
<SelectPrimitive.Viewport
|
||||||
|
className={cn(
|
||||||
|
'p-1',
|
||||||
|
position === 'popper' &&
|
||||||
|
'h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</SelectPrimitive.Viewport>
|
||||||
|
<SelectScrollDownButton />
|
||||||
|
</SelectPrimitive.Content>
|
||||||
|
</SelectPrimitive.Portal>
|
||||||
|
));
|
||||||
|
SelectContent.displayName = SelectPrimitive.Content.displayName;
|
||||||
|
|
||||||
|
const SelectLabel = React.forwardRef<
|
||||||
|
React.ElementRef<typeof SelectPrimitive.Label>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<SelectPrimitive.Label
|
||||||
|
ref={ref}
|
||||||
|
className={cn('px-2 py-1.5 text-sm font-semibold', className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
SelectLabel.displayName = SelectPrimitive.Label.displayName;
|
||||||
|
|
||||||
|
const SelectItem = React.forwardRef<
|
||||||
|
React.ElementRef<typeof SelectPrimitive.Item>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
|
||||||
|
>(({ className, children, ...props }, ref) => (
|
||||||
|
<SelectPrimitive.Item
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
'relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<span className="absolute right-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||||
|
<SelectPrimitive.ItemIndicator>
|
||||||
|
<CheckIcon className="h-4 w-4" />
|
||||||
|
</SelectPrimitive.ItemIndicator>
|
||||||
|
</span>
|
||||||
|
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
|
||||||
|
</SelectPrimitive.Item>
|
||||||
|
));
|
||||||
|
SelectItem.displayName = SelectPrimitive.Item.displayName;
|
||||||
|
|
||||||
|
const SelectSeparator = React.forwardRef<
|
||||||
|
React.ElementRef<typeof SelectPrimitive.Separator>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<SelectPrimitive.Separator
|
||||||
|
ref={ref}
|
||||||
|
className={cn('-mx-1 my-1 h-px bg-muted', className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
|
||||||
|
|
||||||
|
export {
|
||||||
|
Select,
|
||||||
|
SelectGroup,
|
||||||
|
SelectValue,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectContent,
|
||||||
|
SelectLabel,
|
||||||
|
SelectItem,
|
||||||
|
SelectSeparator,
|
||||||
|
SelectScrollUpButton,
|
||||||
|
SelectScrollDownButton,
|
||||||
|
};
|
||||||
31
components/ui/separator.tsx
Normal file
31
components/ui/separator.tsx
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import * as React from 'react';
|
||||||
|
import * as SeparatorPrimitive from '@radix-ui/react-separator';
|
||||||
|
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
|
const Separator = React.forwardRef<
|
||||||
|
React.ElementRef<typeof SeparatorPrimitive.Root>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>
|
||||||
|
>(
|
||||||
|
(
|
||||||
|
{ className, orientation = 'horizontal', decorative = true, ...props },
|
||||||
|
ref
|
||||||
|
) => (
|
||||||
|
<SeparatorPrimitive.Root
|
||||||
|
ref={ref}
|
||||||
|
decorative={decorative}
|
||||||
|
orientation={orientation}
|
||||||
|
className={cn(
|
||||||
|
'shrink-0 bg-border',
|
||||||
|
orientation === 'horizontal' ? 'h-[1px] w-full' : 'h-full w-[1px]',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
Separator.displayName = SeparatorPrimitive.Root.displayName;
|
||||||
|
|
||||||
|
export { Separator };
|
||||||
15010
package-lock.json
generated
15010
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
12
package.json
12
package.json
@@ -12,14 +12,22 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@metamask/sdk-react": "^0.30.0",
|
"@metamask/sdk-react": "^0.30.0",
|
||||||
"@radix-ui/react-icons": "^1.3.0",
|
"@radix-ui/react-icons": "^1.3.0",
|
||||||
|
"@radix-ui/react-label": "^2.1.0",
|
||||||
|
"@radix-ui/react-select": "^2.1.2",
|
||||||
|
"@radix-ui/react-separator": "^1.1.0",
|
||||||
"@radix-ui/react-slot": "^1.1.0",
|
"@radix-ui/react-slot": "^1.1.0",
|
||||||
"class-variance-authority": "^0.7.0",
|
"class-variance-authority": "^0.7.0",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
|
|
||||||
|
"embla-carousel-react": "^8.3.0",
|
||||||
|
"framer-motion": "^11.11.10",
|
||||||
|
|
||||||
"ethers": "^5.7.2",
|
"ethers": "^5.7.2",
|
||||||
|
|
||||||
"lucide-react": "^0.446.0",
|
"lucide-react": "^0.446.0",
|
||||||
"next": "14.2.13",
|
"next": "14.2.13",
|
||||||
"react": "^18",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18",
|
"react-dom": "^18.3.1",
|
||||||
"tailwind-merge": "^2.5.2",
|
"tailwind-merge": "^2.5.2",
|
||||||
"tailwindcss-animate": "^1.0.7"
|
"tailwindcss-animate": "^1.0.7"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -10,6 +10,10 @@ const config: Config = {
|
|||||||
theme: {
|
theme: {
|
||||||
extend: {
|
extend: {
|
||||||
colors: {
|
colors: {
|
||||||
|
black: {
|
||||||
|
DEFAULT: '#000',
|
||||||
|
100: '#000319',
|
||||||
|
},
|
||||||
background: 'hsl(var(--background))',
|
background: 'hsl(var(--background))',
|
||||||
foreground: 'hsl(var(--foreground))',
|
foreground: 'hsl(var(--foreground))',
|
||||||
card: {
|
card: {
|
||||||
|
|||||||
Reference in New Issue
Block a user