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

Datetime Picker V1

Datetime Picker V2

); }; export default DateTimePickerComp; ================================================ FILE: src/app/globals.css ================================================ @tailwind base; @tailwind components; @tailwind utilities; html, body, :root { height: 100%; } @layer base { :root { --background: 0 0% 100%; --foreground: 222.2 84% 4.9%; --card: 0 0% 100%; --card-foreground: 222.2 84% 4.9%; --popover: 0 0% 100%; --popover-foreground: 222.2 84% 4.9%; --primary: 222.2 47.4% 11.2%; --primary-foreground: 210 40% 98%; --secondary: 210 40% 96.1%; --secondary-foreground: 222.2 47.4% 11.2%; --muted: 210 40% 96.1%; --muted-foreground: 215.4 16.3% 46.9%; --accent: 210 40% 96.1%; --accent-foreground: 222.2 47.4% 11.2%; --destructive: 0 84.2% 60.2%; --destructive-foreground: 210 40% 98%; --border: 214.3 31.8% 91.4%; --input: 214.3 31.8% 91.4%; --ring: 222.2 84% 4.9%; --radius: 0.5rem; --chart-1: 12 76% 61%; --chart-2: 173 58% 39%; --chart-3: 197 37% 24%; --chart-4: 43 74% 66%; --chart-5: 27 87% 67%; --sidebar-background: 0 0% 98%; --sidebar-foreground: 240 5.3% 26.1%; --sidebar-primary: 240 5.9% 10%; --sidebar-primary-foreground: 0 0% 98%; --sidebar-accent: 240 4.8% 95.9%; --sidebar-accent-foreground: 240 5.9% 10%; --sidebar-border: 220 13% 91%; --sidebar-ring: 217.2 91.2% 59.8%; } .dark { --background: 222.2 84% 4.9%; --foreground: 210 40% 98%; --card: 222.2 84% 4.9%; --card-foreground: 210 40% 98%; --popover: 222.2 84% 4.9%; --popover-foreground: 210 40% 98%; --primary: 210 40% 98%; --primary-foreground: 222.2 47.4% 11.2%; --secondary: 217.2 32.6% 17.5%; --secondary-foreground: 210 40% 98%; --muted: 217.2 32.6% 17.5%; --muted-foreground: 215 20.2% 65.1%; --accent: 217.2 32.6% 17.5%; --accent-foreground: 210 40% 98%; --destructive: 0 62.8% 30.6%; --destructive-foreground: 210 40% 98%; --border: 217.2 32.6% 17.5%; --input: 217.2 32.6% 17.5%; --ring: 212.7 26.8% 83.9%; --chart-1: 220 70% 50%; --chart-2: 160 60% 45%; --chart-3: 30 80% 55%; --chart-4: 280 65% 60%; --chart-5: 340 75% 55%; --sidebar-background: 240 5.9% 10%; --sidebar-foreground: 240 4.8% 95.9%; --sidebar-primary: 224.3 76.3% 48%; --sidebar-primary-foreground: 0 0% 100%; --sidebar-accent: 240 3.7% 15.9%; --sidebar-accent-foreground: 240 4.8% 95.9%; --sidebar-border: 240 3.7% 15.9%; --sidebar-ring: 217.2 91.2% 59.8%; } } @layer base { * { @apply border-border; } body { @apply bg-background text-foreground; } } ================================================ FILE: src/app/input-typewriter/page.tsx ================================================ import TypeWriter from "@/components/typewriter"; const TypeWriterInput = () => { return (

TypeWriter Input

); }; export default TypeWriterInput; ================================================ FILE: src/app/layout.tsx ================================================ import type { Metadata } from "next"; import { Inter } from "next/font/google"; import "./globals.css"; import { Toaster } from "sonner"; import { cn } from "@/lib/utils"; import { SidebarProvider, SidebarTrigger } from "@/components/ui/sidebar"; import AppSidebar from "@/components/app-sidebar"; const inter = Inter({ subsets: ["latin"] }); export const metadata: Metadata = { title: "Shadcn Datetime Picker", description: "Shadcn Datetime Picker is a simple and easy-to-use datetime picker component for React. It is built with Tailwind CSS and Shadcn UI.", }; export default function RootLayout({ children, }: Readonly<{ children: React.ReactNode; }>) { return (
{children}
); } ================================================ FILE: src/app/page.tsx ================================================ const HomePage = () => { return (

Welcome to Shadcn Components

); }; export default HomePage; ================================================ FILE: src/components/app-sidebar.tsx ================================================ import Link from "next/link"; import { Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarMenu, SidebarMenuButton, SidebarMenuItem, } from "./ui/sidebar"; import Image from "next/image"; const AppSidebar = () => { return (

Shadcn Components

Components Datetime Picker Input Typewriter

Made with 💖 by{" "} Saad

Github Logo Give a star on GitHub

); }; export default AppSidebar; ================================================ FILE: src/components/date-time-picker-v1.tsx ================================================ "use client"; import { zodResolver } from "@hookform/resolvers/zod"; import { CalendarIcon } from "lucide-react"; import { format } from "date-fns"; import { useRef, useState } from "react"; import { useForm } from "react-hook-form"; import { z } from "zod"; import { cn } from "../lib/utils"; import { Button } from "./ui/button"; import { Calendar } from "./ui/calendar"; import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, } from "./ui/form"; import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover"; import { ScrollArea } from "./ui/scroll-area"; import { toast } from "sonner"; const FormSchema = z.object({ datetime: z.date({ required_error: "Date & time is required!", }), }); export function DatetimePickerV1() { const [isOpen, setIsOpen] = useState(false); const [time, setTime] = useState("05:00"); const [date, setDate] = useState(new Date()); // Default button height const calendarRef = useRef(null); const form = useForm>({ resolver: zodResolver(FormSchema), }); async function onSubmit(data: z.infer) { toast.success(`Meeting at: ${format(data.datetime, "PPP, p")}`); } return (
( Datetime
{ if (selectedDate) { const [hours, minutes] = time.split(":"); selectedDate.setHours( parseInt(hours), parseInt(minutes) ); setDate(selectedDate); field.onChange(selectedDate); } }} onDayClick={() => setIsOpen(false)} fromYear={2000} toYear={new Date().getFullYear()} disabled={(date) => Number(date) < Date.now() - 1000 * 60 * 60 * 24 || Number(date) > Date.now() + 1000 * 60 * 60 * 24 * 30 } />
{Array.from({ length: 96 }).map((_, i) => { const hour = Math.floor(i / 4) .toString() .padStart(2, "0"); const minute = ((i % 4) * 15) .toString() .padStart(2, "0"); const timeValue = `${hour}:${minute}`; return ( ); })}
Set your date and time.
)} /> ); } ================================================ FILE: src/components/date-time-picker-v2.tsx ================================================ "use client"; import { zodResolver } from "@hookform/resolvers/zod"; import { CalendarIcon } from "lucide-react"; import { format } from "date-fns"; import { useForm } from "react-hook-form"; import { z } from "zod"; import { cn } from "../lib/utils"; import { Button } from "./ui/button"; import { Calendar } from "./ui/calendar"; import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, } from "./ui/form"; import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover"; import { useState } from "react"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "./ui/select"; import { ScrollArea } from "./ui/scroll-area"; import { toast } from "sonner"; const FormSchema = z.object({ datetime: z.date({ required_error: "Date & time is required!.", }), }); export function DateTimePickerV2() { const [isOpen, setIsOpen] = useState(false); const [time, setTime] = useState("05:00"); const [date, setDate] = useState(null); const form = useForm>({ resolver: zodResolver(FormSchema), }); async function onSubmit(data: z.infer) { toast.success(`Meeting at: ${format(data.datetime, "PPP, p")}`); } return ( <>
( Date { const [hours, minutes] = time.split(":")!; selectedDate?.setHours( parseInt(hours), parseInt(minutes) ); setDate(selectedDate!); field.onChange(selectedDate); }} onDayClick={() => setIsOpen(false)} fromYear={2000} toYear={new Date().getFullYear()} // disabled={(date) => // Number(date) < Date.now() - 1000 * 60 * 60 * 24 || // Number(date) > Date.now() + 1000 * 60 * 60 * 24 * 30 // } defaultMonth={field.value} /> Set your date and time. )} /> ( Time )} />
); } ================================================ FILE: src/components/types.d.ts ================================================ declare module "typewriter-effect"; declare module "typewriter-effect/dist/core"; ================================================ FILE: src/components/typewriter.tsx ================================================ "use client"; import { Form } from "@/components/ui/form"; import { useForm } from "react-hook-form"; import { useEffect, useRef } from "react"; import Typewriter from "typewriter-effect/dist/core"; import { z } from "zod"; import { zodResolver } from "@hookform/resolvers/zod"; import { FormField, FormItem, FormControl, FormDescription, FormLabel, FormMessage, } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; import { toast } from "sonner"; const TypeWriter = () => { const inputFormRef = useRef(null); useEffect(() => { const typewriter: typeof Typewriter | null = null; if (inputFormRef.current) { const customNodeCreator = function (character: string) { if (inputFormRef.current) { inputFormRef.current!.placeholder += character; } return null; }; const onRemoveNode = function () { if (inputFormRef.current) { inputFormRef.current!.placeholder = inputFormRef.current!.placeholder.slice(0, -1); } }; const typewriter = new Typewriter(null, { loop: true, delay: 20, deleteSpeed: 20, onCreateTextNode: customNodeCreator, onRemoveNode: onRemoveNode, }); typewriter .typeString("Generate an image of Solar System?") .pauseFor(1000) .deleteAll(20) .typeString("Generate an image of book Shelf?") .pauseFor(1000) .start(); } return () => { if (typewriter) { typewriter.stop(); } }; }, []); const promptSchema = z.object({ prompt: z.string(), }); const form = useForm>({ resolver: zodResolver(promptSchema), defaultValues: { prompt: "", }, }); const onSubmit = async (data: z.infer) => { toast.info(data.prompt); }; return (
( Prompt
Write a prompt for the AI to generate beautiful Images.
)} /> ); }; export default TypeWriter; ================================================ FILE: src/components/ui/button.tsx ================================================ import * as React from "react" import { Slot } from "@radix-ui/react-slot" import { cva, type VariantProps } from "class-variance-authority" import { cn } from "@/lib/utils" const buttonVariants = cva( "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", { variants: { variant: { default: "bg-primary text-primary-foreground hover:bg-primary/90", destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90", outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground", secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80", ghost: "hover:bg-accent hover:text-accent-foreground", link: "text-primary underline-offset-4 hover:underline", }, size: { default: "h-10 px-4 py-2", sm: "h-9 rounded-md px-3", lg: "h-11 rounded-md px-8", icon: "h-10 w-10", }, }, defaultVariants: { variant: "default", size: "default", }, } ) export interface ButtonProps extends React.ButtonHTMLAttributes, VariantProps { asChild?: boolean } const Button = React.forwardRef( ({ className, variant, size, asChild = false, ...props }, ref) => { const Comp = asChild ? Slot : "button" return ( ) } ) Button.displayName = "Button" export { Button, buttonVariants } ================================================ FILE: src/components/ui/calendar.tsx ================================================ "use client"; import * as React from "react"; import { DayPicker, Dropdown as DropDownDayPicker } from "react-day-picker"; import { buttonVariants } from "@/components/ui/button"; import { ScrollArea } from "@/components/ui/scroll-area"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { cn } from "@/lib/utils"; export type CalendarProps = React.ComponentProps & { captionLabelClassName?: string; dayClassName?: string; dayButtonClassName?: string; dropdownsClassName?: string; footerClassName?: string; monthClassName?: string; monthCaptionClassName?: string; monthGridClassName?: string; monthsClassName?: string; weekClassName?: string; weekdayClassName?: string; weekdaysClassName?: string; rangeEndClassName?: string; rangeMiddleClassName?: string; rangeStartClassName?: string; selectedClassName?: string; disabledClassName?: string; hiddenClassName?: string; outsideClassName?: string; todayClassName?: string; selectTriggerClassName?: string; }; function Calendar({ className, classNames, hideNavigation, showOutsideDays = true, components: customComponents, ...props }: CalendarProps) { const _monthsClassName = cn( "relative flex flex-col gap-4 sm:flex-row", props.monthsClassName ); const _monthCaptionClassName = cn( "relative flex h-7 items-center justify-center", props.monthCaptionClassName ); const _dropdownsClassName = cn( "flex items-center justify-center gap-2 w-full", hideNavigation ? "w-full" : "", props.dropdownsClassName ); const _footerClassName = cn("pt-3 text-sm", props.footerClassName); const _weekdaysClassName = cn("flex", props.weekdaysClassName); const _weekdayClassName = cn( "w-9 text-sm font-normal text-muted-foreground", props.weekdayClassName ); const _captionLabelClassName = cn( "truncate text-sm font-medium", props.captionLabelClassName ); const _monthGridClassName = cn("mx-auto mt-4", props.monthGridClassName); const _weekClassName = cn("mt-2 flex w-max items-start", props.weekClassName); const _dayClassName = cn( "flex size-9 flex-1 items-center justify-center p-0 text-sm", props.dayClassName ); const _dayButtonClassName = cn( buttonVariants({ variant: "ghost" }), "size-9 rounded-md p-0 font-normal transition-none aria-selected:opacity-100", props.dayButtonClassName ); const buttonRangeClassName = "bg-accent [&>button]:bg-primary [&>button]:text-primary-foreground [&>button]:hover:bg-primary [&>button]:hover:text-primary-foreground"; const _rangeStartClassName = cn( buttonRangeClassName, "rounded-s-md", props.rangeStartClassName ); const _rangeEndClassName = cn( buttonRangeClassName, "rounded-e-md", props.rangeEndClassName ); const _rangeMiddleClassName = cn( "bg-accent !text-foreground [&>button]:bg-transparent [&>button]:!text-foreground [&>button]:hover:bg-transparent [&>button]:hover:!text-foreground", props.rangeMiddleClassName ); const _selectedClassName = cn( "[&>button]:bg-primary [&>button]:text-primary-foreground [&>button]:hover:bg-primary [&>button]:hover:text-primary-foreground", props.selectedClassName ); const _todayClassName = cn( "[&>button]:bg-accent [&>button]:text-accent-foreground", props.todayClassName ); const _outsideClassName = cn( "text-muted-foreground opacity-50 aria-selected:bg-accent/50 aria-selected:text-muted-foreground aria-selected:opacity-30", props.outsideClassName ); const _disabledClassName = cn( "text-muted-foreground opacity-50", props.disabledClassName ); const _hiddenClassName = cn("invisible flex-1", props.hiddenClassName); const Dropdown = React.useCallback( ({ value, onChange, options, }: React.ComponentProps) => { const selected = options?.find((option) => option.value === value); const handleChange = (value: string) => { const changeEvent = { target: { value }, } as React.ChangeEvent; onChange?.(changeEvent); }; return ( ); }, [] ); return (