कैलेंडर
बुकिंग, शेड्यूलिंग और रेंज चयन के लिए एक मासिक ग्रिड दिनांक चयनकर्ता — नियोब्रूटलिस्ट बॉर्डर्स-एंड-शैडोज़ ट्रीटमेंट के साथ।
"use client"
import * as React from "react"कैलेंडर एक महीना ग्रिड रेंडर करता है — एकल तारीख़, कई तारीख़ें या रेंज चुनने के लिए। React DayPicker पर बना है — दोनों बैकएंड उसी लाइब्रेरी को रैप करते हैं — और नियोब्रूटलिस्ट रेसिपी के अनुसार स्टाइल किया गया है: मोटे बॉर्डर, कठोर छायाएँ और बोल्ड टाइपोग्राफ़ी।
इसे इन स्थितियों में चुनें:
- फ़ॉर्म डेट फ़ील्ड — Date Picker बनाने के लिए पॉपओवर के साथ जोड़ें: जन्मदिन, डेडलाइन, डिलीवरी तारीख़।
- बुकिंग फ़्लो — चेक-इन/चेक-आउट के लिए
mode="range", पहले से बुक दिनों के लिएdisabled। - डैशबोर्ड फ़िल्टर — रिपोर्ट डेट रेंज, उपलब्धता व्यू, और "last 7 days" जैसे प्रीसेट।
इंस्टॉलेशन#
pnpm dlx shadcn@latest add https://neobrutalism.com/r/base/calendar.jsonnpx shadcn@latest add https://neobrutalism.com/r/base/calendar.jsonyarn dlx shadcn@latest add https://neobrutalism.com/r/base/calendar.jsonbunx --bun shadcn@latest add https://neobrutalism.com/r/base/calendar.json
निम्नलिखित डिपेंडेंसी इंस्टॉल करें:
pnpm add react-day-picker date-fnsnpm install react-day-picker date-fnsyarn add react-day-picker date-fnsbun add react-day-picker date-fns
अपने प्रोजेक्ट में Button कंपोनेंट जोड़ें।
Calendar कंपोनेंट Button कंपोनेंट का उपयोग करता है। सुनिश्चित करें कि यह आपके प्रोजेक्ट में इंस्टॉल है।
निम्नलिखित कोड को कॉपी करके अपने प्रोजेक्ट में पेस्ट करें।
"use client"
import * as React from "react"
import {
ChevronDownIcon,
ChevronLeftIcon,
ChevronRightIcon,
} from "lucide-react"
import {
DayPicker,
getDefaultClassNames,
type DayButton,
type Locale,
} from "react-day-picker"
import { cn } from "@/lib/utils"
import { Button, buttonVariants } from "@/components/ui/button"
function Calendar({
className,
classNames,
showOutsideDays = true,
fixedWeeks = true,
captionLayout = "label",
buttonVariant = "ghost",
locale,
formatters,
components,
...props
}: React.ComponentProps<typeof DayPicker> & {
buttonVariant?: React.ComponentProps<typeof Button>["variant"]
}) {
const defaultClassNames = getDefaultClassNames()
return (
<DayPicker
showOutsideDays={showOutsideDays}
fixedWeeks={fixedWeeks}
className={cn(
"group/calendar bg-card p-2 [--cell-radius:var(--radius-md)] [--cell-size:--spacing(8)] in-data-[slot=card-content]:bg-transparent in-data-[slot=popover-content]:bg-transparent",
String.raw`rtl:**:[.rdp-button\_next>svg]:rotate-180`,
String.raw`rtl:**:[.rdp-button\_previous>svg]:rotate-180`,
className
)}
captionLayout={captionLayout}
locale={locale}
formatters={{
formatMonthDropdown: (date, dateLib) =>
dateLib
? dateLib.format(date, "LLL")
: date.toLocaleString(locale?.code, { month: "short" }),
...formatters,
}}
classNames={{
root: cn("w-fit", defaultClassNames.root),
months: cn(
"relative flex flex-col gap-4 md:flex-row",
defaultClassNames.months
),
month: cn("flex w-full flex-col gap-4", defaultClassNames.month),
nav: cn(
"absolute inset-x-0 top-0 flex w-full items-center justify-between gap-1",
defaultClassNames.nav
),
button_previous: cn(
buttonVariants({ variant: buttonVariant }),
"size-(--cell-size) rounded border-2 p-0 shadow-sm select-none aria-disabled:opacity-50",
defaultClassNames.button_previous
),
button_next: cn(
buttonVariants({ variant: buttonVariant }),
"size-(--cell-size) rounded border-2 p-0 shadow-sm select-none aria-disabled:opacity-50",
defaultClassNames.button_next
),
month_caption: cn(
"flex h-(--cell-size) w-full items-center justify-center px-(--cell-size)",
defaultClassNames.month_caption
),
dropdowns: cn(
"flex h-(--cell-size) w-full items-center justify-center gap-1.5 text-sm font-medium",
defaultClassNames.dropdowns
),
dropdown_root: cn(
"cn-calendar-dropdown-root relative rounded-(--cell-radius)",
defaultClassNames.dropdown_root
),
dropdown: cn(
"absolute inset-0 bg-popover opacity-0",
defaultClassNames.dropdown
),
caption_label: cn(
"font-head font-medium select-none",
captionLayout === "label"
? "cn-calendar-caption text-sm"
: "cn-calendar-caption-label flex items-center gap-1 rounded-(--cell-radius) text-sm [&>svg]:size-3.5 [&>svg]:text-muted-foreground",
defaultClassNames.caption_label
),
month_grid: cn("w-full border-collapse", defaultClassNames.month_grid),
weekdays: cn("flex", defaultClassNames.weekdays),
weekday: cn(
"flex w-(--cell-size) items-center justify-center rounded-(--cell-radius) text-[0.8rem] font-normal text-muted-foreground select-none",
defaultClassNames.weekday
),
week: cn("mt-2 flex w-full", defaultClassNames.week),
week_number_header: cn(
"w-(--cell-size) select-none",
defaultClassNames.week_number_header
),
week_number: cn(
"text-[0.8rem] text-muted-foreground select-none",
defaultClassNames.week_number
),
day: cn(
"group/day relative size-(--cell-size) rounded-(--cell-radius) p-0 text-center select-none [&:last-child[data-selected=true]_button]:rounded-r-(--cell-radius)",
props.showWeekNumber
? "[&:nth-child(2)[data-selected=true]_button]:rounded-l-(--cell-radius)"
: "[&:first-child[data-selected=true]_button]:rounded-l-(--cell-radius)",
defaultClassNames.day
),
range_start: cn(
"relative isolate z-0 rounded-l-(--cell-radius) bg-primary/30 after:absolute after:inset-y-0 after:right-0 after:w-4 after:bg-primary/30",
defaultClassNames.range_start
),
range_middle: cn("rounded-none", defaultClassNames.range_middle),
range_end: cn(
"relative isolate z-0 rounded-r-(--cell-radius) bg-primary/30 after:absolute after:inset-y-0 after:left-0 after:w-4 after:bg-primary/30",
defaultClassNames.range_end
),
today: cn(
"rounded-(--cell-radius) border-2 border-primary text-foreground data-[selected=true]:rounded-none",
defaultClassNames.today
),
outside: cn(
"text-muted-foreground aria-selected:text-muted-foreground",
defaultClassNames.outside
),
disabled: cn(
"text-muted-foreground opacity-50",
defaultClassNames.disabled
),
hidden: cn("invisible", defaultClassNames.hidden),
...classNames,
}}
components={{
Root: ({ className, rootRef, ...props }) => {
return (
<div
data-slot="calendar"
ref={rootRef}
className={cn(className)}
{...props}
/>
)
},
Chevron: ({ className, orientation, ...props }) => {
if (orientation === "left") {
return (
<ChevronLeftIcon
className={cn("cn-rtl-flip size-4", className)}
{...props}
/>
)
}
if (orientation === "right") {
return (
<ChevronRightIcon
className={cn("cn-rtl-flip size-4", className)}
{...props}
/>
)
}
return (
<ChevronDownIcon className={cn("size-4", className)} {...props} />
)
},
DayButton: ({ ...props }) => (
<CalendarDayButton locale={locale} {...props} />
),
WeekNumber: ({ children, ...props }) => {
return (
<td {...props}>
<div className="flex size-(--cell-size) items-center justify-center text-center">
{children}
</div>
</td>
)
},
...components,
}}
{...props}
/>
)
}
function CalendarDayButton({
className,
day,
modifiers,
locale,
...props
}: React.ComponentProps<typeof DayButton> & { locale?: Partial<Locale> }) {
const defaultClassNames = getDefaultClassNames()
const ref = React.useRef<HTMLButtonElement>(null)
React.useEffect(() => {
if (modifiers.focused) ref.current?.focus()
}, [modifiers.focused])
return (
<Button
ref={ref}
variant="ghost"
size="icon"
data-day={`${day.date.getFullYear()}-${String(
day.date.getMonth() + 1
).padStart(2, "0")}-${String(day.date.getDate()).padStart(2, "0")}`}
data-selected-single={
modifiers.selected &&
!modifiers.range_start &&
!modifiers.range_end &&
!modifiers.range_middle
}
data-range-start={modifiers.range_start}
data-range-end={modifiers.range_end}
data-range-middle={modifiers.range_middle}
className={cn(
"relative isolate z-10 flex size-(--cell-size) flex-col items-center justify-center gap-1 border-0 leading-none font-normal group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:outline-2 group-data-[focused=true]/day:outline-offset-2 group-data-[focused=true]/day:outline-primary data-[range-end=true]:rounded-(--cell-radius) data-[range-end=true]:rounded-r-(--cell-radius) data-[range-end=true]:border-2 data-[range-end=true]:bg-primary data-[range-end=true]:text-primary-foreground data-[range-middle=true]:rounded-none data-[range-middle=true]:bg-primary/30 data-[range-middle=true]:text-foreground data-[range-start=true]:rounded-(--cell-radius) data-[range-start=true]:rounded-l-(--cell-radius) data-[range-start=true]:border-2 data-[range-start=true]:bg-primary data-[range-start=true]:text-primary-foreground data-[selected-single=true]:border-2 data-[selected-single=true]:bg-primary data-[selected-single=true]:text-primary-foreground dark:hover:text-foreground [&>span]:text-xs [&>span]:opacity-70",
defaultClassNames.day,
className
)}
{...props}
/>
)
}
export { Calendar, CalendarDayButton }
अपने प्रोजेक्ट सेटअप से मेल खाने के लिए import पाथ अपडेट करें।
उपयोग#
import { Calendar } from "@/components/ui/calendar"const [date, setDate] = React.useState<Date | undefined>(new Date())
return (
<Calendar
mode="single"
selected={date}
onSelect={setDate}
className="rounded border-2 shadow-md"
/>
)अधिक जानकारी के लिए React DayPicker डॉक्यूमेंटेशन देखें।
परिचय#
Calendar कंपोनेंट React DayPicker के ऊपर बना है।
Date Picker#
आप date picker बनाने के लिए <Calendar> कंपोनेंट का उपयोग कर सकते हैं। अधिक जानकारी के लिए Date Picker पेज देखें।
फ़ारसी / हिजरी / जलाली कैलेंडर#
फ़ारसी कैलेंडर इस्तेमाल करने के लिए, components/ui/calendar.tsx को एडिट करें और react-day-picker को react-day-picker/persian से बदलें।
- import { DayPicker } from "react-day-picker"
+ import { DayPicker } from "react-day-picker/persian""use client"
import * as React from "react"चयनित तारीख़ (टाइमज़ोन के साथ)#
Calendar कंपोनेंट एक timeZone prop स्वीकार करता है, ताकि तारीख़ें यूज़र के लोकल टाइमज़ोन में दिखाई और चुनी जाएँ।
export function CalendarWithTimezone() {
const [date, setDate] = React.useState<Date | undefined>(undefined)
const [timeZone, setTimeZone] = React.useState<string | undefined>(undefined)
React.useEffect(() => {
setTimeZone(Intl.DateTimeFormat().resolvedOptions().timeZone)
}, [])
return (
<Calendar
mode="single"
selected={date}
onSelect={setDate}
timeZone={timeZone}
/>
)
}नोट: यदि आपको चयनित तारीख़ में कोई ऑफ़सेट दिखे (उदाहरण के लिए, 20 चुनने पर 19 हाइलाइट हो), तो सुनिश्चित करें कि timeZone prop यूज़र के लोकल टाइमज़ोन पर सेट है।
क्लाइंट-साइड क्यों? सर्वर-साइड रेंडरिंग के साथ संगतता सुनिश्चित करने के लिए टाइमज़ोन को एक useEffect के अंदर Intl.DateTimeFormat().resolvedOptions().timeZone का उपयोग करके डिटेक्ट किया जाता है। रेंडर के दौरान टाइमज़ोन डिटेक्ट करने से हाइड्रेशन मिसमैच होगा, क्योंकि सर्वर और क्लाइंट अलग-अलग टाइमज़ोन में हो सकते हैं।
उदाहरण#
बेसिक#
एक बेसिक कैलेंडर कंपोनेंट। कैलेंडर को स्टाइल करने के लिए हमने className="rounded border-2 shadow-md" का उपयोग किया।
"use client"
import { Calendar } from "@/components/ui/calendar"रेंज कैलेंडर#
रेंज चयन सक्षम करने के लिए mode="range" prop का उपयोग करें।
"use client"
import * as React from "react"महीना और साल सिलेक्टर#
महीने और साल के dropdown दिखाने के लिए captionLayout="dropdown" का उपयोग करें।
"use client"
import { Calendar } from "@/components/ui/calendar"प्रीसेट#
"use client"
import * as React from "react"तारीख़ और समय पिकर#
"use client"
import * as React from "react"बुक की गई तारीख़ें#
"use client"
import * as React from "react"कस्टम सेल साइज़#
"use client"
import * as React from "react"आप --cell-size CSS वेरिएबल का उपयोग करके कैलेंडर सेल का साइज़ कस्टमाइज़ कर सकते हैं। ब्रेकपॉइंट-विशिष्ट वैल्यू का उपयोग करके आप इसे रिस्पॉन्सिव भी बना सकते हैं:
<Calendar
mode="single"
selected={date}
onSelect={setDate}
className="rounded border-2 shadow-md [--cell-size:--spacing(11)] md:[--cell-size:--spacing(12)]"
/>या फ़िक्स्ड वैल्यू का उपयोग करें:
<Calendar
mode="single"
selected={date}
onSelect={setDate}
className="rounded border-2 shadow-md [--cell-size:2.75rem] md:[--cell-size:3rem]"
/>सप्ताह संख्या#
सप्ताह संख्या दिखाने के लिए showWeekNumber का उपयोग करें।
"use client"
import * as React from "react"RTL#
Neobrutalism में RTL सपोर्ट सक्षम करने के लिए, RTL कॉन्फ़िगरेशन गाइड देखें।
फ़ारसी / हिजरी / जलाली कैलेंडर सक्षम करने के लिए हिजरी गाइड भी देखें।
"use client"
import * as React from "react"RTL का उपयोग करते समय, react-day-picker/locale से locale import करें और Calendar कंपोनेंट को locale तथा dir दोनों props पास करें:
import { arSA } from "react-day-picker/locale"
;<Calendar
mode="single"
selected={date}
onSelect={setDate}
locale={arSA}
dir="rtl"
/>एक्सेसिबिलिटी#
कैलेंडर WAI-ARIA Date Picker Dialog पैटर्न के ग्रिड नेविगेशन मॉडल का पालन करता है: React DayPicker महीने को role="grid" के रूप में रेंडर करता है, हर दिन असली बटन होता है, और चुने/अक्षम दिनों की घोषणा स्क्रीन रीडर को होती है। दोनों बैकएंड वैरिएंट यही व्यवहार साझा करते हैं क्योंकि वे एक ही लाइब्रेरी रैप करते हैं।
कीबोर्ड इंटरैक्शन:
| कुंजी | क्रिया |
|---|---|
Tab / Shift + Tab | नेव बटन और दिन ग्रिड के बीच फ़ोकस ले जाएँ |
ArrowLeft / ArrowRight | पिछले / अगले दिन पर फ़ोकस ले जाएँ |
ArrowUp / ArrowDown | पिछले / अगले सप्ताह के उसी सप्ताह-दिन पर फ़ोकस ले जाएँ |
Home / End | सप्ताह के पहले / आख़िरी दिन पर फ़ोकस ले जाएँ |
PageUp / PageDown | पिछले / अगले महीने पर जाएँ |
Shift + PageUp / Shift + PageDown | पिछले / अगले साल पर जाएँ |
Space / Enter | फ़ोकस किए गए दिन को चुनें |
API रेफ़रेंस#
Calendar कंपोनेंट के बारे में अधिक जानकारी के लिए React DayPicker डॉक्यूमेंटेशन देखें।
चेंजलॉग#
RTL सपोर्ट#
यदि आप Calendar कंपोनेंट के किसी पिछले वर्शन से अपग्रेड कर रहे हैं, तो locale सपोर्ट जोड़ने के लिए आपको निम्नलिखित अपडेट लागू करने होंगे:
Locale टाइप import करें।
react-day-picker से अपने imports में Locale जोड़ें:
import {
DayPicker,
getDefaultClassNames,
type DayButton,
+ type Locale,
} from "react-day-picker"Calendar कंपोनेंट में locale prop जोड़ें।
कंपोनेंट के props में locale prop जोड़ें:
function Calendar({
className,
classNames,
showOutsideDays = true,
captionLayout = "label",
buttonVariant = "ghost",
+ locale,
formatters,
components,
...props
}: React.ComponentProps<typeof DayPicker> & {
buttonVariant?: React.ComponentProps<typeof Button>["variant"]
}) {DayPicker को locale पास करें।
DayPicker कंपोनेंट को locale prop पास करें:
<DayPicker
showOutsideDays={showOutsideDays}
className={cn(...)}
captionLayout={captionLayout}
+ locale={locale}
formatters={{
formatMonthDropdown: (date) =>
- date.toLocaleString("default", { month: "short" }),
+ date.toLocaleString(locale?.code, { month: "short" }),
...formatters,
}}CalendarDayButton को locale स्वीकार करने के लिए अपडेट करें।
CalendarDayButton कंपोनेंट सिग्नेचर अपडेट करें और locale पास करें:
function CalendarDayButton({
className,
day,
modifiers,
+ locale,
...props
- }: React.ComponentProps<typeof DayButton>) {
+ }: React.ComponentProps<typeof DayButton> & { locale?: Partial<Locale> }) {CalendarDayButton में तारीख़ फ़ॉर्मैटिंग अपडेट करें।
तारीख़ फ़ॉर्मैटिंग में locale?.code का उपयोग करें:
<Button
variant="ghost"
size="icon"
- data-day={day.date.toLocaleDateString()}
+ data-day={day.date.toLocaleDateString(locale?.code)}
...
/>DayButton कंपोनेंट को locale पास करें।
locale prop पास करने के लिए DayButton कंपोनेंट के उपयोग को अपडेट करें:
components={{
...
- DayButton: CalendarDayButton,
+ DayButton: ({ ...props }) => (
+ <CalendarDayButton locale={locale} {...props} />
+ ),
...
}}RTL-अवेयर CSS क्लास अपडेट करें।
बेहतर RTL सपोर्ट के लिए दिशात्मक क्लास को लॉजिकल प्रॉपर्टीज़ से बदलें:
// In the day classNames:
- [&:last-child[data-selected=true]_button]:rounded-r-(--cell-radius)
+ [&:last-child[data-selected=true]_button]:rounded-e-(--cell-radius)
- [&:nth-child(2)[data-selected=true]_button]:rounded-l-(--cell-radius)
+ [&:nth-child(2)[data-selected=true]_button]:rounded-s-(--cell-radius)
- [&:first-child[data-selected=true]_button]:rounded-l-(--cell-radius)
+ [&:first-child[data-selected=true]_button]:rounded-s-(--cell-radius)
// In range_start classNames:
- rounded-l-(--cell-radius) ... after:right-0
+ rounded-s-(--cell-radius) ... after:end-0
// In range_end classNames:
- rounded-r-(--cell-radius) ... after:left-0
+ rounded-e-(--cell-radius) ... after:start-0
// In CalendarDayButton className:
- data-[range-end=true]:rounded-r-(--cell-radius)
+ data-[range-end=true]:rounded-e-(--cell-radius)
- data-[range-start=true]:rounded-l-(--cell-radius)
+ data-[range-start=true]:rounded-s-(--cell-radius)इन बदलावों को लागू करने के बाद, आप locale-विशिष्ट फ़ॉर्मैटिंग देने के लिए locale prop का उपयोग कर सकते हैं:
import { enUS } from "react-day-picker/locale"
;<Calendar mode="single" selected={date} onSelect={setDate} locale={enUS} />