├── .eslintrc.json ├── .gitignore ├── LICENSE ├── README.md ├── components.json ├── eslint.config.mjs ├── image-1.png ├── image-2.png ├── image.png ├── next.config.mjs ├── next.config.ts ├── package-lock.json ├── package.json ├── postcss.config.mjs ├── public ├── github.svg ├── next.svg └── vercel.svg ├── src ├── app │ ├── datetime-picker │ │ └── page.tsx │ ├── favicon.ico │ ├── globals.css │ ├── input-typewriter │ │ └── page.tsx │ ├── layout.tsx │ └── page.tsx ├── components │ ├── app-sidebar.tsx │ ├── date-time-picker-v1.tsx │ ├── date-time-picker-v2.tsx │ ├── types.d.ts │ ├── typewriter.tsx │ └── ui │ │ ├── button.tsx │ │ ├── calendar.tsx │ │ ├── form.tsx │ │ ├── input.tsx │ │ ├── label.tsx │ │ ├── popover.tsx │ │ ├── scroll-area.tsx │ │ ├── select.tsx │ │ ├── separator.tsx │ │ ├── sheet.tsx │ │ ├── sidebar.tsx │ │ ├── skeleton.tsx │ │ ├── sonner.tsx │ │ └── tooltip.tsx ├── hooks │ └── use-mobile.tsx └── lib │ └── utils.ts ├── tailwind.config.ts └── tsconfig.json /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.* 7 | .yarn/* 8 | !.yarn/patches 9 | !.yarn/plugins 10 | !.yarn/releases 11 | !.yarn/versions 12 | 13 | # testing 14 | /coverage 15 | 16 | # next.js 17 | /.next/ 18 | /out/ 19 | 20 | # production 21 | /build 22 | 23 | # misc 24 | .DS_Store 25 | *.pem 26 | 27 | # debug 28 | npm-debug.log* 29 | yarn-debug.log* 30 | yarn-error.log* 31 | .pnpm-debug.log* 32 | 33 | # env files (can opt-in for committing if needed) 34 | .env* 35 | 36 | # vercel 37 | .vercel 38 | 39 | # typescript 40 | *.tsbuildinfo 41 | next-env.d.ts 42 | 43 | # local files 44 | bun.lockb 45 | package-lock.json 46 | yarn.lock 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Saad 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Shadcn UI - Datetime Picker 2 | 3 | This project provides a beautifully crafted datetime picker component built using the Shadcn UI. It offers an intuitive interface for selecting dates and times in React applications. 4 | 5 | ## Features 6 | 7 | - **Date and Time Selection**: Allows users to pick both date and time seamlessly. 8 | - **Customizable Layout**: Easily adaptable to fit various design requirements. 9 | - **Responsive Design**: Ensures optimal user experience across different devices. 10 | 11 | ## Installation 12 | 13 | To integrate the Shadcn Datetime Picker into your project, follow these steps: 14 | 15 | 1. **Install Packages**: 16 | 17 | ```bash 18 | npm install 19 | ``` 20 | 21 | 2. **Start the Development Server**: 22 | 23 | ```bash 24 | npm start 25 | ``` 26 | 27 | ## Contributing 28 | 29 | We welcome contributions! If you have suggestions or improvements, please feel free to submit a pull request or open an issue. 30 | 31 | ## License 32 | 33 | This project is licensed under the MIT License. 34 | -------------------------------------------------------------------------------- /components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "default", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.ts", 8 | "css": "src/app/globals.css", 9 | "baseColor": "neutral", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils", 16 | "ui": "@/components/ui", 17 | "lib": "@/lib", 18 | "hooks": "@/hooks" 19 | }, 20 | "iconLibrary": "lucide" 21 | } -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import { dirname } from "path"; 2 | import { fileURLToPath } from "url"; 3 | import { FlatCompat } from "@eslint/eslintrc"; 4 | 5 | const __filename = fileURLToPath(import.meta.url); 6 | const __dirname = dirname(__filename); 7 | 8 | const compat = new FlatCompat({ 9 | baseDirectory: __dirname, 10 | }); 11 | 12 | const eslintConfig = [ 13 | ...compat.extends("next/core-web-vitals", "next/typescript"), 14 | ]; 15 | 16 | export default eslintConfig; 17 | -------------------------------------------------------------------------------- /image-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Maliksidk19/shadcn-datetime-picker/ae6e76b232c1de4e0cbcd87d86c109bcf2022d3e/image-1.png -------------------------------------------------------------------------------- /image-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Maliksidk19/shadcn-datetime-picker/ae6e76b232c1de4e0cbcd87d86c109bcf2022d3e/image-2.png -------------------------------------------------------------------------------- /image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Maliksidk19/shadcn-datetime-picker/ae6e76b232c1de4e0cbcd87d86c109bcf2022d3e/image.png -------------------------------------------------------------------------------- /next.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | reactStrictMode: false, 4 | }; 5 | 6 | export default nextConfig; 7 | -------------------------------------------------------------------------------- /next.config.ts: -------------------------------------------------------------------------------- 1 | import type { NextConfig } from "next"; 2 | 3 | const nextConfig: NextConfig = { 4 | /* config options here */ 5 | reactStrictMode: false, 6 | }; 7 | 8 | export default nextConfig; 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "shadcn-datetime", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev --turbopack", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@hookform/resolvers": "^4.0.0", 13 | "@radix-ui/react-dialog": "^1.1.6", 14 | "@radix-ui/react-label": "^2.1.2", 15 | "@radix-ui/react-popover": "^1.1.6", 16 | "@radix-ui/react-scroll-area": "^1.2.3", 17 | "@radix-ui/react-select": "^2.1.6", 18 | "@radix-ui/react-separator": "^1.1.2", 19 | "@radix-ui/react-slot": "^1.1.2", 20 | "@radix-ui/react-tooltip": "^1.1.8", 21 | "class-variance-authority": "^0.7.1", 22 | "clsx": "^2.1.1", 23 | "date-fns": "^4.1.0", 24 | "lucide-react": "^0.475.0", 25 | "next": "15.1.7", 26 | "next-themes": "^0.4.4", 27 | "react": "^19.0.0", 28 | "react-day-picker": "^9.5.1", 29 | "react-dom": "^19.0.0", 30 | "react-hook-form": "^7.54.2", 31 | "sonner": "^1.7.4", 32 | "tailwind-merge": "^3.0.1", 33 | "tailwindcss": "3.4.1", 34 | "tailwindcss-animate": "^1.0.7", 35 | "typewriter-effect": "^2.21.0", 36 | "zod": "^3.24.2" 37 | }, 38 | "devDependencies": { 39 | "@eslint/eslintrc": "^3.2.0", 40 | "@types/node": "^22.13.4", 41 | "@types/react": "^19.0.8", 42 | "@types/react-dom": "^19.0.3", 43 | "eslint": "^9.20.1", 44 | "eslint-config-next": "15.1.7", 45 | "postcss": "^8.5.2", 46 | "typescript": "^5.7.3" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /postcss.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('postcss-load-config').Config} */ 2 | const config = { 3 | plugins: { 4 | tailwindcss: {}, 5 | }, 6 | }; 7 | 8 | export default config; 9 | -------------------------------------------------------------------------------- /public/github.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/datetime-picker/page.tsx: -------------------------------------------------------------------------------- 1 | import { DatetimePickerV1 } from "@/components/date-time-picker-v1"; 2 | import { DateTimePickerV2 } from "@/components/date-time-picker-v2"; 3 | 4 | const DateTimePickerComp = () => { 5 | return ( 6 |
7 |

Datetime Picker V1

8 | 9 |

Datetime Picker V2

10 | 11 |
12 | ); 13 | }; 14 | 15 | export default DateTimePickerComp; 16 | -------------------------------------------------------------------------------- /src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Maliksidk19/shadcn-datetime-picker/ae6e76b232c1de4e0cbcd87d86c109bcf2022d3e/src/app/favicon.ico -------------------------------------------------------------------------------- /src/app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | html, 6 | body, 7 | :root { 8 | height: 100%; 9 | } 10 | 11 | @layer base { 12 | :root { 13 | --background: 0 0% 100%; 14 | --foreground: 222.2 84% 4.9%; 15 | --card: 0 0% 100%; 16 | --card-foreground: 222.2 84% 4.9%; 17 | --popover: 0 0% 100%; 18 | --popover-foreground: 222.2 84% 4.9%; 19 | --primary: 222.2 47.4% 11.2%; 20 | --primary-foreground: 210 40% 98%; 21 | --secondary: 210 40% 96.1%; 22 | --secondary-foreground: 222.2 47.4% 11.2%; 23 | --muted: 210 40% 96.1%; 24 | --muted-foreground: 215.4 16.3% 46.9%; 25 | --accent: 210 40% 96.1%; 26 | --accent-foreground: 222.2 47.4% 11.2%; 27 | --destructive: 0 84.2% 60.2%; 28 | --destructive-foreground: 210 40% 98%; 29 | --border: 214.3 31.8% 91.4%; 30 | --input: 214.3 31.8% 91.4%; 31 | --ring: 222.2 84% 4.9%; 32 | --radius: 0.5rem; 33 | --chart-1: 12 76% 61%; 34 | --chart-2: 173 58% 39%; 35 | --chart-3: 197 37% 24%; 36 | --chart-4: 43 74% 66%; 37 | --chart-5: 27 87% 67%; 38 | --sidebar-background: 0 0% 98%; 39 | --sidebar-foreground: 240 5.3% 26.1%; 40 | --sidebar-primary: 240 5.9% 10%; 41 | --sidebar-primary-foreground: 0 0% 98%; 42 | --sidebar-accent: 240 4.8% 95.9%; 43 | --sidebar-accent-foreground: 240 5.9% 10%; 44 | --sidebar-border: 220 13% 91%; 45 | --sidebar-ring: 217.2 91.2% 59.8%; 46 | } 47 | 48 | .dark { 49 | --background: 222.2 84% 4.9%; 50 | --foreground: 210 40% 98%; 51 | --card: 222.2 84% 4.9%; 52 | --card-foreground: 210 40% 98%; 53 | --popover: 222.2 84% 4.9%; 54 | --popover-foreground: 210 40% 98%; 55 | --primary: 210 40% 98%; 56 | --primary-foreground: 222.2 47.4% 11.2%; 57 | --secondary: 217.2 32.6% 17.5%; 58 | --secondary-foreground: 210 40% 98%; 59 | --muted: 217.2 32.6% 17.5%; 60 | --muted-foreground: 215 20.2% 65.1%; 61 | --accent: 217.2 32.6% 17.5%; 62 | --accent-foreground: 210 40% 98%; 63 | --destructive: 0 62.8% 30.6%; 64 | --destructive-foreground: 210 40% 98%; 65 | --border: 217.2 32.6% 17.5%; 66 | --input: 217.2 32.6% 17.5%; 67 | --ring: 212.7 26.8% 83.9%; 68 | --chart-1: 220 70% 50%; 69 | --chart-2: 160 60% 45%; 70 | --chart-3: 30 80% 55%; 71 | --chart-4: 280 65% 60%; 72 | --chart-5: 340 75% 55%; 73 | --sidebar-background: 240 5.9% 10%; 74 | --sidebar-foreground: 240 4.8% 95.9%; 75 | --sidebar-primary: 224.3 76.3% 48%; 76 | --sidebar-primary-foreground: 0 0% 100%; 77 | --sidebar-accent: 240 3.7% 15.9%; 78 | --sidebar-accent-foreground: 240 4.8% 95.9%; 79 | --sidebar-border: 240 3.7% 15.9%; 80 | --sidebar-ring: 217.2 91.2% 59.8%; 81 | } 82 | } 83 | 84 | @layer base { 85 | * { 86 | @apply border-border; 87 | } 88 | body { 89 | @apply bg-background text-foreground; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/app/input-typewriter/page.tsx: -------------------------------------------------------------------------------- 1 | import TypeWriter from "@/components/typewriter"; 2 | 3 | const TypeWriterInput = () => { 4 | return ( 5 |
6 |

TypeWriter Input

7 | 8 |
9 | ); 10 | }; 11 | 12 | export default TypeWriterInput; 13 | -------------------------------------------------------------------------------- /src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from "next"; 2 | import { Inter } from "next/font/google"; 3 | import "./globals.css"; 4 | import { Toaster } from "sonner"; 5 | import { cn } from "@/lib/utils"; 6 | import { SidebarProvider, SidebarTrigger } from "@/components/ui/sidebar"; 7 | import AppSidebar from "@/components/app-sidebar"; 8 | 9 | const inter = Inter({ subsets: ["latin"] }); 10 | 11 | export const metadata: Metadata = { 12 | title: "Shadcn Datetime Picker", 13 | description: 14 | "Shadcn Datetime Picker is a simple and easy-to-use datetime picker component for React. It is built with Tailwind CSS and Shadcn UI.", 15 | }; 16 | 17 | export default function RootLayout({ 18 | children, 19 | }: Readonly<{ 20 | children: React.ReactNode; 21 | }>) { 22 | return ( 23 | 24 | 25 | 26 | 27 |
28 | 29 | {children} 30 | 38 |
39 |
40 | 41 | 42 | ); 43 | } 44 | -------------------------------------------------------------------------------- /src/app/page.tsx: -------------------------------------------------------------------------------- 1 | const HomePage = () => { 2 | return ( 3 |
4 |

Welcome to Shadcn Components

5 |
6 | ); 7 | }; 8 | 9 | export default HomePage; 10 | -------------------------------------------------------------------------------- /src/components/app-sidebar.tsx: -------------------------------------------------------------------------------- 1 | import Link from "next/link"; 2 | import { 3 | Sidebar, 4 | SidebarContent, 5 | SidebarFooter, 6 | SidebarGroup, 7 | SidebarGroupContent, 8 | SidebarGroupLabel, 9 | SidebarHeader, 10 | SidebarMenu, 11 | SidebarMenuButton, 12 | SidebarMenuItem, 13 | } from "./ui/sidebar"; 14 | import Image from "next/image"; 15 | 16 | const AppSidebar = () => { 17 | return ( 18 | 19 | 20 |

Shadcn Components

21 |
22 | 23 | 24 | 25 | Components 26 | 27 | 28 | 29 | 30 | Datetime Picker 31 | 32 | 33 | 34 | 35 | Input Typewriter 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 |

45 | Made with 💖 by{" "} 46 | 51 | Saad 52 | 53 |

54 |

55 | Github Logo 56 | 61 | Give a star on GitHub 62 | 63 |

64 |
65 |
66 | ); 67 | }; 68 | 69 | export default AppSidebar; 70 | -------------------------------------------------------------------------------- /src/components/date-time-picker-v1.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { zodResolver } from "@hookform/resolvers/zod"; 4 | import { CalendarIcon } from "lucide-react"; 5 | import { format } from "date-fns"; 6 | import { useRef, useState } from "react"; 7 | import { useForm } from "react-hook-form"; 8 | import { z } from "zod"; 9 | 10 | import { cn } from "../lib/utils"; 11 | import { Button } from "./ui/button"; 12 | import { Calendar } from "./ui/calendar"; 13 | import { 14 | Form, 15 | FormControl, 16 | FormDescription, 17 | FormField, 18 | FormItem, 19 | FormLabel, 20 | FormMessage, 21 | } from "./ui/form"; 22 | import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover"; 23 | import { ScrollArea } from "./ui/scroll-area"; 24 | import { toast } from "sonner"; 25 | 26 | const FormSchema = z.object({ 27 | datetime: z.date({ 28 | required_error: "Date & time is required!", 29 | }), 30 | }); 31 | 32 | export function DatetimePickerV1() { 33 | const [isOpen, setIsOpen] = useState(false); 34 | const [time, setTime] = useState("05:00"); 35 | const [date, setDate] = useState(new Date()); // Default button height 36 | 37 | const calendarRef = useRef(null); 38 | const form = useForm>({ 39 | resolver: zodResolver(FormSchema), 40 | }); 41 | 42 | async function onSubmit(data: z.infer) { 43 | toast.success(`Meeting at: ${format(data.datetime, "PPP, p")}`); 44 | } 45 | 46 | return ( 47 |
48 | 49 | ( 53 | 54 | Datetime 55 | 56 | 57 | 58 | 72 | 73 | 74 | 78 |
79 | { 84 | if (selectedDate) { 85 | const [hours, minutes] = time.split(":"); 86 | selectedDate.setHours( 87 | parseInt(hours), 88 | parseInt(minutes) 89 | ); 90 | setDate(selectedDate); 91 | field.onChange(selectedDate); 92 | } 93 | }} 94 | onDayClick={() => setIsOpen(false)} 95 | fromYear={2000} 96 | toYear={new Date().getFullYear()} 97 | disabled={(date) => 98 | Number(date) < Date.now() - 1000 * 60 * 60 * 24 || 99 | Number(date) > Date.now() + 1000 * 60 * 60 * 24 * 30 100 | } 101 | /> 102 |
103 |
104 | 105 |
106 | {Array.from({ length: 96 }).map((_, i) => { 107 | const hour = Math.floor(i / 4) 108 | .toString() 109 | .padStart(2, "0"); 110 | const minute = ((i % 4) * 15) 111 | .toString() 112 | .padStart(2, "0"); 113 | const timeValue = `${hour}:${minute}`; 114 | return ( 115 | 135 | ); 136 | })} 137 |
138 |
139 |
140 |
141 |
142 | Set your date and time. 143 | 144 |
145 | )} 146 | /> 147 | 148 | 149 | 150 | ); 151 | } 152 | -------------------------------------------------------------------------------- /src/components/date-time-picker-v2.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { zodResolver } from "@hookform/resolvers/zod"; 4 | import { CalendarIcon } from "lucide-react"; 5 | import { format } from "date-fns"; 6 | import { useForm } from "react-hook-form"; 7 | import { z } from "zod"; 8 | 9 | import { cn } from "../lib/utils"; 10 | import { Button } from "./ui/button"; 11 | import { Calendar } from "./ui/calendar"; 12 | import { 13 | Form, 14 | FormControl, 15 | FormDescription, 16 | FormField, 17 | FormItem, 18 | FormLabel, 19 | FormMessage, 20 | } from "./ui/form"; 21 | import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover"; 22 | import { useState } from "react"; 23 | import { 24 | Select, 25 | SelectContent, 26 | SelectItem, 27 | SelectTrigger, 28 | SelectValue, 29 | } from "./ui/select"; 30 | import { ScrollArea } from "./ui/scroll-area"; 31 | import { toast } from "sonner"; 32 | 33 | const FormSchema = z.object({ 34 | datetime: z.date({ 35 | required_error: "Date & time is required!.", 36 | }), 37 | }); 38 | 39 | export function DateTimePickerV2() { 40 | const [isOpen, setIsOpen] = useState(false); 41 | const [time, setTime] = useState("05:00"); 42 | const [date, setDate] = useState(null); 43 | const form = useForm>({ 44 | resolver: zodResolver(FormSchema), 45 | }); 46 | 47 | async function onSubmit(data: z.infer) { 48 | toast.success(`Meeting at: ${format(data.datetime, "PPP, p")}`); 49 | } 50 | 51 | return ( 52 | <> 53 |
54 | 55 |
56 | ( 60 | 61 | Date 62 | 63 | 64 | 65 | 79 | 80 | 81 | 82 | { 87 | const [hours, minutes] = time.split(":")!; 88 | selectedDate?.setHours( 89 | parseInt(hours), 90 | parseInt(minutes) 91 | ); 92 | setDate(selectedDate!); 93 | field.onChange(selectedDate); 94 | }} 95 | onDayClick={() => setIsOpen(false)} 96 | fromYear={2000} 97 | toYear={new Date().getFullYear()} 98 | // disabled={(date) => 99 | // Number(date) < Date.now() - 1000 * 60 * 60 * 24 || 100 | // Number(date) > Date.now() + 1000 * 60 * 60 * 24 * 30 101 | // } 102 | defaultMonth={field.value} 103 | /> 104 | 105 | 106 | Set your date and time. 107 | 108 | 109 | )} 110 | /> 111 | ( 115 | 116 | Time 117 | 118 | 152 | 153 | 154 | 155 | )} 156 | /> 157 |
158 | 159 |
160 | 161 | 162 | ); 163 | } 164 | -------------------------------------------------------------------------------- /src/components/types.d.ts: -------------------------------------------------------------------------------- 1 | declare module "typewriter-effect"; 2 | declare module "typewriter-effect/dist/core"; 3 | -------------------------------------------------------------------------------- /src/components/typewriter.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { Form } from "@/components/ui/form"; 4 | import { useForm } from "react-hook-form"; 5 | import { useEffect, useRef } from "react"; 6 | import Typewriter from "typewriter-effect/dist/core"; 7 | import { z } from "zod"; 8 | import { zodResolver } from "@hookform/resolvers/zod"; 9 | import { 10 | FormField, 11 | FormItem, 12 | FormControl, 13 | FormDescription, 14 | FormLabel, 15 | FormMessage, 16 | } from "@/components/ui/form"; 17 | import { Input } from "@/components/ui/input"; 18 | import { Button } from "@/components/ui/button"; 19 | import { toast } from "sonner"; 20 | 21 | const TypeWriter = () => { 22 | const inputFormRef = useRef(null); 23 | 24 | useEffect(() => { 25 | const typewriter: typeof Typewriter | null = null; 26 | 27 | if (inputFormRef.current) { 28 | const customNodeCreator = function (character: string) { 29 | if (inputFormRef.current) { 30 | inputFormRef.current!.placeholder += character; 31 | } 32 | return null; 33 | }; 34 | 35 | const onRemoveNode = function () { 36 | if (inputFormRef.current) { 37 | inputFormRef.current!.placeholder = 38 | inputFormRef.current!.placeholder.slice(0, -1); 39 | } 40 | }; 41 | 42 | const typewriter = new Typewriter(null, { 43 | loop: true, 44 | delay: 20, 45 | deleteSpeed: 20, 46 | onCreateTextNode: customNodeCreator, 47 | onRemoveNode: onRemoveNode, 48 | }); 49 | 50 | typewriter 51 | .typeString("Generate an image of Solar System?") 52 | .pauseFor(1000) 53 | .deleteAll(20) 54 | .typeString("Generate an image of book Shelf?") 55 | .pauseFor(1000) 56 | .start(); 57 | } 58 | 59 | return () => { 60 | if (typewriter) { 61 | typewriter.stop(); 62 | } 63 | }; 64 | }, []); 65 | 66 | const promptSchema = z.object({ 67 | prompt: z.string(), 68 | }); 69 | 70 | const form = useForm>({ 71 | resolver: zodResolver(promptSchema), 72 | defaultValues: { 73 | prompt: "", 74 | }, 75 | }); 76 | 77 | const onSubmit = async (data: z.infer) => { 78 | toast.info(data.prompt); 79 | }; 80 | 81 | return ( 82 |
83 | 88 | ( 92 | 93 | 94 | Prompt 95 | 96 | 97 |
98 | 106 | 109 |
110 |
111 | 112 | Write a prompt for the AI to generate beautiful Images. 113 | 114 | 115 |
116 | )} 117 | /> 118 | 119 | 120 | ); 121 | }; 122 | 123 | export default TypeWriter; 124 | -------------------------------------------------------------------------------- /src/components/ui/button.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import { Slot } from "@radix-ui/react-slot" 3 | import { cva, type VariantProps } from "class-variance-authority" 4 | 5 | import { cn } from "@/lib/utils" 6 | 7 | const buttonVariants = cva( 8 | "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0", 9 | { 10 | variants: { 11 | variant: { 12 | default: "bg-primary text-primary-foreground hover:bg-primary/90", 13 | destructive: 14 | "bg-destructive text-destructive-foreground hover:bg-destructive/90", 15 | outline: 16 | "border border-input bg-background hover:bg-accent hover:text-accent-foreground", 17 | secondary: 18 | "bg-secondary text-secondary-foreground hover:bg-secondary/80", 19 | ghost: "hover:bg-accent hover:text-accent-foreground", 20 | link: "text-primary underline-offset-4 hover:underline", 21 | }, 22 | size: { 23 | default: "h-10 px-4 py-2", 24 | sm: "h-9 rounded-md px-3", 25 | lg: "h-11 rounded-md px-8", 26 | icon: "h-10 w-10", 27 | }, 28 | }, 29 | defaultVariants: { 30 | variant: "default", 31 | size: "default", 32 | }, 33 | } 34 | ) 35 | 36 | export interface ButtonProps 37 | extends React.ButtonHTMLAttributes, 38 | VariantProps { 39 | asChild?: boolean 40 | } 41 | 42 | const Button = React.forwardRef( 43 | ({ className, variant, size, asChild = false, ...props }, ref) => { 44 | const Comp = asChild ? Slot : "button" 45 | return ( 46 | 51 | ) 52 | } 53 | ) 54 | Button.displayName = "Button" 55 | 56 | export { Button, buttonVariants } 57 | -------------------------------------------------------------------------------- /src/components/ui/calendar.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import * as React from "react"; 4 | import { DayPicker, Dropdown as DropDownDayPicker } from "react-day-picker"; 5 | 6 | import { buttonVariants } from "@/components/ui/button"; 7 | import { ScrollArea } from "@/components/ui/scroll-area"; 8 | import { 9 | Select, 10 | SelectContent, 11 | SelectItem, 12 | SelectTrigger, 13 | SelectValue, 14 | } from "@/components/ui/select"; 15 | import { cn } from "@/lib/utils"; 16 | 17 | export type CalendarProps = React.ComponentProps & { 18 | captionLabelClassName?: string; 19 | dayClassName?: string; 20 | dayButtonClassName?: string; 21 | dropdownsClassName?: string; 22 | footerClassName?: string; 23 | monthClassName?: string; 24 | monthCaptionClassName?: string; 25 | monthGridClassName?: string; 26 | monthsClassName?: string; 27 | weekClassName?: string; 28 | weekdayClassName?: string; 29 | weekdaysClassName?: string; 30 | rangeEndClassName?: string; 31 | rangeMiddleClassName?: string; 32 | rangeStartClassName?: string; 33 | selectedClassName?: string; 34 | disabledClassName?: string; 35 | hiddenClassName?: string; 36 | outsideClassName?: string; 37 | todayClassName?: string; 38 | selectTriggerClassName?: string; 39 | }; 40 | 41 | function Calendar({ 42 | className, 43 | classNames, 44 | hideNavigation, 45 | showOutsideDays = true, 46 | components: customComponents, 47 | ...props 48 | }: CalendarProps) { 49 | const _monthsClassName = cn( 50 | "relative flex flex-col gap-4 sm:flex-row", 51 | props.monthsClassName 52 | ); 53 | const _monthCaptionClassName = cn( 54 | "relative flex h-7 items-center justify-center", 55 | props.monthCaptionClassName 56 | ); 57 | const _dropdownsClassName = cn( 58 | "flex items-center justify-center gap-2 w-full", 59 | hideNavigation ? "w-full" : "", 60 | props.dropdownsClassName 61 | ); 62 | const _footerClassName = cn("pt-3 text-sm", props.footerClassName); 63 | const _weekdaysClassName = cn("flex", props.weekdaysClassName); 64 | const _weekdayClassName = cn( 65 | "w-9 text-sm font-normal text-muted-foreground", 66 | props.weekdayClassName 67 | ); 68 | const _captionLabelClassName = cn( 69 | "truncate text-sm font-medium", 70 | props.captionLabelClassName 71 | ); 72 | 73 | const _monthGridClassName = cn("mx-auto mt-4", props.monthGridClassName); 74 | const _weekClassName = cn("mt-2 flex w-max items-start", props.weekClassName); 75 | const _dayClassName = cn( 76 | "flex size-9 flex-1 items-center justify-center p-0 text-sm", 77 | props.dayClassName 78 | ); 79 | const _dayButtonClassName = cn( 80 | buttonVariants({ variant: "ghost" }), 81 | "size-9 rounded-md p-0 font-normal transition-none aria-selected:opacity-100", 82 | props.dayButtonClassName 83 | ); 84 | 85 | const buttonRangeClassName = 86 | "bg-accent [&>button]:bg-primary [&>button]:text-primary-foreground [&>button]:hover:bg-primary [&>button]:hover:text-primary-foreground"; 87 | const _rangeStartClassName = cn( 88 | buttonRangeClassName, 89 | "rounded-s-md", 90 | props.rangeStartClassName 91 | ); 92 | const _rangeEndClassName = cn( 93 | buttonRangeClassName, 94 | "rounded-e-md", 95 | props.rangeEndClassName 96 | ); 97 | const _rangeMiddleClassName = cn( 98 | "bg-accent !text-foreground [&>button]:bg-transparent [&>button]:!text-foreground [&>button]:hover:bg-transparent [&>button]:hover:!text-foreground", 99 | props.rangeMiddleClassName 100 | ); 101 | const _selectedClassName = cn( 102 | "[&>button]:bg-primary [&>button]:text-primary-foreground [&>button]:hover:bg-primary [&>button]:hover:text-primary-foreground", 103 | props.selectedClassName 104 | ); 105 | const _todayClassName = cn( 106 | "[&>button]:bg-accent [&>button]:text-accent-foreground", 107 | props.todayClassName 108 | ); 109 | const _outsideClassName = cn( 110 | "text-muted-foreground opacity-50 aria-selected:bg-accent/50 aria-selected:text-muted-foreground aria-selected:opacity-30", 111 | props.outsideClassName 112 | ); 113 | const _disabledClassName = cn( 114 | "text-muted-foreground opacity-50", 115 | props.disabledClassName 116 | ); 117 | const _hiddenClassName = cn("invisible flex-1", props.hiddenClassName); 118 | 119 | const Dropdown = React.useCallback( 120 | ({ 121 | value, 122 | onChange, 123 | options, 124 | }: React.ComponentProps) => { 125 | const selected = options?.find((option) => option.value === value); 126 | const handleChange = (value: string) => { 127 | const changeEvent = { 128 | target: { value }, 129 | } as React.ChangeEvent; 130 | onChange?.(changeEvent); 131 | }; 132 | return ( 133 | 156 | ); 157 | }, 158 | [] 159 | ); 160 | 161 | return ( 162 |