{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "full-calendar",
  "title": "Full Calendar",
  "description": "Month, week and day calendar for apps that show and schedule events, with a time grid and all-day events.",
  "dependencies": [
    "date-fns@^4.4.0",
    "lucide-react@^1.48.0"
  ],
  "registryDependencies": [
    "https://rdf.dev.360online.com/r/button.json",
    "https://rdf.dev.360online.com/r/popover.json",
    "https://rdf.dev.360online.com/r/toggle-group.json"
  ],
  "files": [
    {
      "path": "registry/rapid/ui/full-calendar/full-calendar.tsx",
      "content": "'use client'\n\nimport {\n\taddDays,\n\taddMonths,\n\taddWeeks,\n\tdifferenceInMinutes,\n\teachDayOfInterval,\n\tendOfDay,\n\tendOfMonth,\n\tendOfWeek,\n\tformat,\n\tgetISOWeek,\n\tisSameDay,\n\tisSameMonth,\n\tisToday,\n\ttype Locale,\n\tstartOfDay,\n\tstartOfMonth,\n\tstartOfWeek,\n} from 'date-fns'\nimport { ChevronLeftIcon, ChevronRightIcon } from 'lucide-react'\nimport * as React from 'react'\nimport { Button } from '@/components/ui/button'\nimport { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'\nimport { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group'\nimport { cn } from '@/lib/utils'\n\nexport type FullCalendarView = 'month' | 'week' | 'day'\n\nexport type CalendarEventColor =\n\t| 'default'\n\t| 'primary'\n\t| 'destructive'\n\t| 'chart-1'\n\t| 'chart-2'\n\t| 'chart-3'\n\t| 'chart-4'\n\t| 'chart-5'\n\nexport interface CalendarEvent {\n\tid: string\n\ttitle: string\n\tstart: Date\n\tend: Date\n\t/** Shown in the all-day row in week and day view, without a time */\n\tallDay?: boolean\n\tcolor?: CalendarEventColor\n}\n\ntype WeekDay = 0 | 1 | 2 | 3 | 4 | 5 | 6\n\nexport interface FullCalendarLabels {\n\ttoday: string\n\tprevious: string\n\tnext: string\n\tviews: Record<FullCalendarView, string>\n\tallDay: string\n\tweek: string\n\t/** \"2 more\" in month view */\n\tmore: (count: number) => string\n\t/** Name of the button that creates an event on a day or at a time */\n\taddEvent: (when: string) => string\n}\n\nconst defaultLabels: FullCalendarLabels = {\n\ttoday: 'Today',\n\tprevious: 'Previous',\n\tnext: 'Next',\n\tviews: { month: 'Month', week: 'Week', day: 'Day' },\n\tallDay: 'All day',\n\tweek: 'Week',\n\tmore: (count) => `${count} more`,\n\taddEvent: (when) => `Add event, ${when}`,\n}\n\nexport interface FullCalendarProps {\n\tevents: CalendarEvent[]\n\t/** The date shown (controlled). Any date in the month, week or day to show. */\n\tdate?: Date\n\tdefaultDate?: Date\n\tonDateChange?: (date: Date) => void\n\tview?: FullCalendarView\n\tdefaultView?: FullCalendarView\n\tonViewChange?: (view: FullCalendarView) => void\n\t/** Views offered in the view switcher */\n\tviews?: FullCalendarView[]\n\t/** Monday by default */\n\tweekStartsOn?: WeekDay\n\t/** date-fns locale for month and weekday names */\n\tlocale?: Locale\n\t/** First and last hour of the time grid in week and day view */\n\tstartHour?: number\n\tendHour?: number\n\t/** Events shown per day in month view before \"N more\" */\n\tmaxEventsPerDay?: number\n\tonEventClick?: (event: CalendarEvent) => void\n\t/** A free day (month view) or hour (week and day view) was chosen, e.g. to create an event there */\n\tonSlotClick?: (slot: { start: Date; end: Date; allDay: boolean }) => void\n\tlabels?: Partial<FullCalendarLabels>\n\tclassName?: string\n}\n\nconst HOUR_HEIGHT = 48\n\nconst eventColors: Record<CalendarEventColor, string> = {\n\tdefault: 'border-l-muted-foreground bg-muted',\n\tprimary: 'border-l-primary bg-primary/10',\n\tdestructive: 'border-l-destructive bg-destructive/10',\n\t'chart-1': 'border-l-chart-1 bg-chart-1/15',\n\t'chart-2': 'border-l-chart-2 bg-chart-2/15',\n\t'chart-3': 'border-l-chart-3 bg-chart-3/15',\n\t'chart-4': 'border-l-chart-4 bg-chart-4/15',\n\t'chart-5': 'border-l-chart-5 bg-chart-5/15',\n}\n\nfunction useControllable<T>(value: T | undefined, defaultValue: T, onChange?: (value: T) => void) {\n\tconst [inner, setInner] = React.useState(defaultValue)\n\tconst current = value ?? inner\n\tconst set = React.useCallback(\n\t\t(next: T) => {\n\t\t\tif (value === undefined) setInner(next)\n\t\t\tonChange?.(next)\n\t\t},\n\t\t[value, onChange],\n\t)\n\treturn [current, set] as const\n}\n\nconst timeRange = (event: CalendarEvent, locale?: Locale) =>\n\tevent.allDay ? '' : `${format(event.start, 'HH:mm', { locale })}–${format(event.end, 'HH:mm', { locale })}`\n\n/** Events that touch the given day */\nconst eventsOn = (events: CalendarEvent[], day: Date) =>\n\tevents.filter((e) => e.start <= endOfDay(day) && e.end > startOfDay(day))\n\n/** The toolbar heading: the month, the week with its date range, or the day */\nfunction viewTitle(\n\tview: FullCalendarView,\n\tdate: Date,\n\t{ weekStartsOn, locale, weekLabel }: { weekStartsOn: WeekDay; locale?: Locale; weekLabel: string },\n) {\n\tif (view === 'month') return format(date, 'LLLL yyyy', { locale })\n\tif (view === 'day') return format(date, 'EEEE d MMMM yyyy', { locale })\n\tconst from = format(startOfWeek(date, { weekStartsOn }), 'd MMM', { locale })\n\tconst to = format(endOfWeek(date, { weekStartsOn }), 'd MMM yyyy', { locale })\n\treturn `${weekLabel} ${getISOWeek(date)} · ${from} – ${to}`\n}\n\n/** A full calendar for apps that show and schedule events: month, week and day views with a time grid. */\nfunction FullCalendar({\n\tevents,\n\tdate: dateProp,\n\tdefaultDate,\n\tonDateChange,\n\tview: viewProp,\n\tdefaultView = 'month',\n\tonViewChange,\n\tviews = ['month', 'week', 'day'],\n\tweekStartsOn = 1,\n\tlocale,\n\tstartHour = 7,\n\tendHour = 19,\n\tmaxEventsPerDay = 3,\n\tonEventClick,\n\tonSlotClick,\n\tlabels: labelsProp,\n\tclassName,\n}: Readonly<FullCalendarProps>) {\n\tconst labels = { ...defaultLabels, ...labelsProp, views: { ...defaultLabels.views, ...labelsProp?.views } }\n\tconst [date, setDate] = useControllable(dateProp, defaultDate ?? new Date(), onDateChange)\n\tconst [view, setView] = useControllable(viewProp, defaultView, onViewChange)\n\tconst headingId = React.useId()\n\n\tconst step = (direction: 1 | -1) => {\n\t\tif (view === 'month') setDate(addMonths(date, direction))\n\t\telse if (view === 'week') setDate(addWeeks(date, direction))\n\t\telse setDate(addDays(date, direction))\n\t}\n\n\tconst weekStart = startOfWeek(date, { weekStartsOn })\n\tconst title = viewTitle(view, date, { weekStartsOn, locale, weekLabel: labels.week })\n\n\tconst props = { events, locale, labels, onEventClick, onSlotClick }\n\n\treturn (\n\t\t<section data-slot=\"full-calendar\" aria-labelledby={headingId} className={cn('flex flex-col gap-4', className)}>\n\t\t\t<div data-slot=\"full-calendar-toolbar\" className=\"flex flex-wrap items-center gap-2\">\n\t\t\t\t<div className=\"flex items-center gap-1\">\n\t\t\t\t\t<Button variant=\"outline\" onClick={() => setDate(new Date())}>\n\t\t\t\t\t\t{labels.today}\n\t\t\t\t\t</Button>\n\t\t\t\t\t<Button variant=\"ghost\" size=\"icon\" aria-label={labels.previous} onClick={() => step(-1)}>\n\t\t\t\t\t\t<ChevronLeftIcon aria-hidden=\"true\" />\n\t\t\t\t\t</Button>\n\t\t\t\t\t<Button variant=\"ghost\" size=\"icon\" aria-label={labels.next} onClick={() => step(1)}>\n\t\t\t\t\t\t<ChevronRightIcon aria-hidden=\"true\" />\n\t\t\t\t\t</Button>\n\t\t\t\t</div>\n\t\t\t\t<h2 id={headingId} aria-live=\"polite\" className=\"mr-auto text-lg font-semibold first-letter:uppercase\">\n\t\t\t\t\t{title}\n\t\t\t\t</h2>\n\t\t\t\t{views.length > 1 ? (\n\t\t\t\t\t<ToggleGroup\n\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\tspacing={0}\n\t\t\t\t\t\tvalue={[view]}\n\t\t\t\t\t\tonValueChange={(value: string[]) => {\n\t\t\t\t\t\t\tif (value[0]) setView(value[0] as FullCalendarView)\n\t\t\t\t\t\t}}\n\t\t\t\t\t>\n\t\t\t\t\t\t{views.map((v) => (\n\t\t\t\t\t\t\t<ToggleGroupItem key={v} value={v}>\n\t\t\t\t\t\t\t\t{labels.views[v]}\n\t\t\t\t\t\t\t</ToggleGroupItem>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</ToggleGroup>\n\t\t\t\t) : null}\n\t\t\t</div>\n\n\t\t\t{view === 'month' ? (\n\t\t\t\t<MonthView {...props} date={date} weekStartsOn={weekStartsOn} maxEventsPerDay={maxEventsPerDay} />\n\t\t\t) : (\n\t\t\t\t<TimeGridView\n\t\t\t\t\t{...props}\n\t\t\t\t\tdays={view === 'week' ? eachDayOfInterval({ start: weekStart, end: addDays(weekStart, 6) }) : [date]}\n\t\t\t\t\tstartHour={startHour}\n\t\t\t\t\tendHour={endHour}\n\t\t\t\t/>\n\t\t\t)}\n\t\t</section>\n\t)\n}\n\ntype ViewProps = {\n\tevents: CalendarEvent[]\n\tlocale?: Locale\n\tlabels: FullCalendarLabels\n\tonEventClick?: (event: CalendarEvent) => void\n\tonSlotClick?: FullCalendarProps['onSlotClick']\n}\n\nfunction EventChip({\n\tevent,\n\tlocale,\n\tonEventClick,\n\tshowTime = true,\n}: Readonly<{\n\tevent: CalendarEvent\n\tlocale?: Locale\n\tonEventClick?: (event: CalendarEvent) => void\n\tshowTime?: boolean\n}>) {\n\tconst time = showTime ? timeRange(event, locale) : ''\n\treturn (\n\t\t<button\n\t\t\ttype=\"button\"\n\t\t\tdata-slot=\"full-calendar-event\"\n\t\t\ttitle={time ? `${time} ${event.title}` : event.title}\n\t\t\tonClick={() => onEventClick?.(event)}\n\t\t\tclassName={cn(\n\t\t\t\t'flex min-h-6 w-full min-w-0 items-center gap-1.5 rounded-sm border-l-3 px-1.5 text-left text-xs text-foreground outline-none transition-colors hover:brightness-95 focus-visible:ring-2 focus-visible:ring-ring dark:hover:brightness-110',\n\t\t\t\teventColors[event.color ?? 'primary'],\n\t\t\t)}\n\t\t>\n\t\t\t{time ? (\n\t\t\t\t<span className=\"shrink-0 tabular-nums text-muted-foreground\">{format(event.start, 'HH:mm', { locale })}</span>\n\t\t\t) : null}\n\t\t\t<span className=\"truncate font-medium\">{event.title}</span>\n\t\t</button>\n\t)\n}\n\nfunction MonthView({\n\tdate,\n\tevents,\n\tlocale,\n\tweekStartsOn,\n\tlabels,\n\tonEventClick,\n\tonSlotClick,\n\tmaxEventsPerDay,\n}: Readonly<ViewProps & { date: Date; weekStartsOn: WeekDay; maxEventsPerDay: number }>) {\n\tconst start = startOfWeek(startOfMonth(date), { weekStartsOn })\n\tconst end = endOfWeek(endOfMonth(date), { weekStartsOn })\n\tconst days = eachDayOfInterval({ start, end })\n\tconst weeks = Array.from({ length: days.length / 7 }, (_, i) => days.slice(i * 7, i * 7 + 7))\n\n\treturn (\n\t\t<div className=\"overflow-x-auto rounded-lg border\">\n\t\t\t<table data-slot=\"full-calendar-month\" className=\"w-full min-w-[640px] table-fixed border-collapse text-sm\">\n\t\t\t\t<thead>\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t{weeks[0].map((day) => (\n\t\t\t\t\t\t\t<th\n\t\t\t\t\t\t\t\tkey={day.toISOString()}\n\t\t\t\t\t\t\t\tscope=\"col\"\n\t\t\t\t\t\t\t\tclassName=\"border-b bg-muted/50 py-2 text-center text-xs font-medium text-muted-foreground\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<abbr title={format(day, 'EEEE', { locale })} className=\"no-underline\">\n\t\t\t\t\t\t\t\t\t{format(day, 'EEE', { locale })}\n\t\t\t\t\t\t\t\t</abbr>\n\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</tr>\n\t\t\t\t</thead>\n\t\t\t\t<tbody>\n\t\t\t\t\t{weeks.map((week) => (\n\t\t\t\t\t\t<tr key={week[0].toISOString()}>\n\t\t\t\t\t\t\t{week.map((day) => {\n\t\t\t\t\t\t\t\tconst dayEvents = eventsOn(events, day).sort((a, b) => a.start.getTime() - b.start.getTime())\n\t\t\t\t\t\t\t\tconst visible = dayEvents.slice(0, maxEventsPerDay)\n\t\t\t\t\t\t\t\tconst hidden = dayEvents.length - visible.length\n\t\t\t\t\t\t\t\tconst outside = !isSameMonth(day, date)\n\t\t\t\t\t\t\t\tconst today = isToday(day)\n\t\t\t\t\t\t\t\tconst dayLabel = format(day, 'EEEE d MMMM', { locale })\n\t\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t\t<td\n\t\t\t\t\t\t\t\t\t\tkey={day.toISOString()}\n\t\t\t\t\t\t\t\t\t\taria-current={today ? 'date' : undefined}\n\t\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\t\t'h-28 border-t border-r align-top last:border-r-0',\n\t\t\t\t\t\t\t\t\t\t\toutside && 'bg-muted/30 text-muted-foreground',\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<div className=\"flex h-full flex-col gap-1 p-1\">\n\t\t\t\t\t\t\t\t\t\t\t{onSlotClick ? (\n\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\taria-label={labels.addEvent(dayLabel)}\n\t\t\t\t\t\t\t\t\t\t\t\t\tonClick={() => onSlotClick({ start: startOfDay(day), end: endOfDay(day), allDay: true })}\n\t\t\t\t\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t'grid size-7 place-items-center self-start rounded-full text-sm tabular-nums outline-none hover:bg-muted focus-visible:ring-2 focus-visible:ring-ring',\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttoday && 'bg-primary font-semibold text-primary-foreground hover:bg-primary/90',\n\t\t\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t{format(day, 'd')}\n\t\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t'grid size-7 place-items-center rounded-full text-sm tabular-nums',\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttoday && 'bg-primary font-semibold text-primary-foreground',\n\t\t\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t{format(day, 'd')}\n\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t<ul className=\"flex min-w-0 flex-col gap-0.5\">\n\t\t\t\t\t\t\t\t\t\t\t\t{visible.map((event) => (\n\t\t\t\t\t\t\t\t\t\t\t\t\t<li key={event.id} className=\"min-w-0\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<EventChip event={event} locale={locale} onEventClick={onEventClick} />\n\t\t\t\t\t\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t\t\t\t</ul>\n\t\t\t\t\t\t\t\t\t\t\t{hidden > 0 ? (\n\t\t\t\t\t\t\t\t\t\t\t\t<Popover>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<PopoverTrigger\n\t\t\t\t\t\t\t\t\t\t\t\t\t\trender={\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"min-h-6 self-start rounded-sm px-1.5 text-xs font-medium text-muted-foreground outline-none hover:bg-muted hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{labels.more(hidden)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t</PopoverTrigger>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<PopoverContent align=\"start\" className=\"w-64\" aria-label={dayLabel}>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<p className=\"text-sm font-medium first-letter:uppercase\">{dayLabel}</p>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<ul className=\"flex flex-col gap-1\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{dayEvents.map((event) => (\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<li key={event.id}>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<EventChip event={event} locale={locale} onEventClick={onEventClick} />\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</ul>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</PopoverContent>\n\t\t\t\t\t\t\t\t\t\t\t\t</Popover>\n\t\t\t\t\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t))}\n\t\t\t\t</tbody>\n\t\t\t</table>\n\t\t</div>\n\t)\n}\n\n/** Side-by-side lanes for overlapping timed events */\nfunction layoutDay(events: CalendarEvent[]) {\n\tconst sorted = [...events].sort((a, b) => a.start.getTime() - b.start.getTime() || b.end.getTime() - a.end.getTime())\n\tconst placed: { event: CalendarEvent; lane: number; lanes: number }[] = []\n\tlet group: { event: CalendarEvent; lane: number; lanes: number }[] = []\n\tlet groupEnd = 0\n\tconst laneEnds: number[] = []\n\tconst closeGroup = () => {\n\t\tconst lanes = Math.max(1, ...group.map((g) => g.lane + 1))\n\t\tfor (const g of group) g.lanes = lanes\n\t\tplaced.push(...group)\n\t\tgroup = []\n\t\tlaneEnds.length = 0\n\t}\n\tfor (const event of sorted) {\n\t\tif (group.length && event.start.getTime() >= groupEnd) closeGroup()\n\t\tlet lane = laneEnds.findIndex((end) => end <= event.start.getTime())\n\t\tif (lane === -1) lane = laneEnds.length\n\t\tlaneEnds[lane] = event.end.getTime()\n\t\tgroup.push({ event, lane, lanes: 1 })\n\t\tgroupEnd = Math.max(groupEnd, event.end.getTime())\n\t}\n\tif (group.length) closeGroup()\n\treturn placed\n}\n\nfunction useNow() {\n\tconst [now, setNow] = React.useState<Date | null>(null)\n\tReact.useEffect(() => {\n\t\tsetNow(new Date())\n\t\tconst id = setInterval(() => setNow(new Date()), 60_000)\n\t\treturn () => clearInterval(id)\n\t}, [])\n\treturn now\n}\n\nfunction TimeGridView({\n\tdays,\n\tevents,\n\tlocale,\n\tlabels,\n\tonEventClick,\n\tonSlotClick,\n\tstartHour,\n\tendHour,\n}: Readonly<ViewProps & { days: Date[]; startHour: number; endHour: number }>) {\n\tconst hours = Array.from({ length: endHour - startHour }, (_, i) => startHour + i)\n\tconst now = useNow()\n\tconst allDay = days.map((day) => eventsOn(events, day).filter((e) => e.allDay || !isSameDay(e.start, e.end)))\n\tconst hasAllDay = allDay.some((list) => list.length > 0)\n\tconst columns = `3.5rem repeat(${days.length}, minmax(0, 1fr))`\n\n\treturn (\n\t\t<div data-slot=\"full-calendar-time-grid\" className=\"overflow-x-auto rounded-lg border\">\n\t\t\t<div className={cn(days.length > 1 && 'min-w-[720px]')}>\n\t\t\t\t{/* Day headers */}\n\t\t\t\t<div className=\"grid border-b bg-muted/50\" style={{ gridTemplateColumns: columns }}>\n\t\t\t\t\t<div />\n\t\t\t\t\t{days.map((day) => {\n\t\t\t\t\t\tconst today = isToday(day)\n\t\t\t\t\t\tconst label = format(day, 'EEEE d MMMM', { locale })\n\t\t\t\t\t\tconst content = (\n\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t<span className=\"text-xs text-muted-foreground\">{format(day, 'EEE', { locale })}</span>\n\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\t'grid size-8 place-items-center rounded-full text-base font-medium tabular-nums',\n\t\t\t\t\t\t\t\t\t\ttoday && 'bg-primary text-primary-foreground',\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{format(day, 'd')}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t)\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\tkey={day.toISOString()}\n\t\t\t\t\t\t\t\taria-current={today ? 'date' : undefined}\n\t\t\t\t\t\t\t\tclassName=\"flex justify-center border-l py-1.5\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{onSlotClick ? (\n\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\taria-label={labels.addEvent(label)}\n\t\t\t\t\t\t\t\t\t\tonClick={() => onSlotClick({ start: startOfDay(day), end: endOfDay(day), allDay: true })}\n\t\t\t\t\t\t\t\t\t\tclassName=\"flex flex-col items-center gap-0.5 rounded-md px-2 py-0.5 outline-none hover:bg-muted focus-visible:ring-2 focus-visible:ring-ring\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{content}\n\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t\t<div className=\"flex flex-col items-center gap-0.5 px-2 py-0.5\">{content}</div>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t)\n\t\t\t\t\t})}\n\t\t\t\t</div>\n\n\t\t\t\t{/* All-day events */}\n\t\t\t\t{hasAllDay ? (\n\t\t\t\t\t<div className=\"grid border-b\" style={{ gridTemplateColumns: columns }}>\n\t\t\t\t\t\t<div className=\"px-1 py-1.5 text-right text-xs text-muted-foreground\">{labels.allDay}</div>\n\t\t\t\t\t\t{allDay.map((list, i) => (\n\t\t\t\t\t\t\t<ul key={days[i].toISOString()} className=\"flex min-w-0 flex-col gap-0.5 border-l p-1\">\n\t\t\t\t\t\t\t\t{list.map((event) => (\n\t\t\t\t\t\t\t\t\t<li key={event.id} className=\"min-w-0\">\n\t\t\t\t\t\t\t\t\t\t<EventChip event={event} locale={locale} onEventClick={onEventClick} showTime={false} />\n\t\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t</ul>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</div>\n\t\t\t\t) : null}\n\n\t\t\t\t{/* Time grid */}\n\t\t\t\t<div className=\"max-h-[576px] overflow-y-auto\">\n\t\t\t\t\t<div className=\"relative grid\" style={{ gridTemplateColumns: columns }}>\n\t\t\t\t\t\t<div aria-hidden=\"true\">\n\t\t\t\t\t\t\t{hours.map((hour) => (\n\t\t\t\t\t\t\t\t<div key={hour} className=\"relative text-right text-xs text-muted-foreground\" style={{ height: HOUR_HEIGHT }}>\n\t\t\t\t\t\t\t\t\t<span className=\"absolute -top-2 right-2 tabular-nums\">\n\t\t\t\t\t\t\t\t\t\t{hour > startHour ? `${String(hour).padStart(2, '0')}:00` : ''}\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t{days.map((day) => {\n\t\t\t\t\t\t\tconst dayStart = new Date(day)\n\t\t\t\t\t\t\tdayStart.setHours(startHour, 0, 0, 0)\n\t\t\t\t\t\t\tconst dayEnd = new Date(day)\n\t\t\t\t\t\t\tdayEnd.setHours(endHour, 0, 0, 0)\n\t\t\t\t\t\t\tconst timed = eventsOn(events, day).filter(\n\t\t\t\t\t\t\t\t(e) => !e.allDay && isSameDay(e.start, e.end) && e.end > dayStart && e.start < dayEnd,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\tconst showNow = now && isSameDay(now, day) && now >= dayStart && now < dayEnd\n\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t<div key={day.toISOString()} className=\"relative border-l\">\n\t\t\t\t\t\t\t\t\t{hours.map((hour) => {\n\t\t\t\t\t\t\t\t\t\tconst slotStart = new Date(day)\n\t\t\t\t\t\t\t\t\t\tslotStart.setHours(hour, 0, 0, 0)\n\t\t\t\t\t\t\t\t\t\tconst slotEnd = new Date(slotStart)\n\t\t\t\t\t\t\t\t\t\tslotEnd.setHours(hour + 1)\n\t\t\t\t\t\t\t\t\t\treturn onSlotClick ? (\n\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\tkey={hour}\n\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t// Mouse and touch only: keyboard users create events from the day header\n\t\t\t\t\t\t\t\t\t\t\t\ttabIndex={-1}\n\t\t\t\t\t\t\t\t\t\t\t\taria-label={labels.addEvent(format(slotStart, 'EEEE d MMMM HH:mm', { locale }))}\n\t\t\t\t\t\t\t\t\t\t\t\tonClick={() => onSlotClick({ start: slotStart, end: slotEnd, allDay: false })}\n\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"block w-full border-b border-dashed last:border-b-0 hover:bg-muted/50\"\n\t\t\t\t\t\t\t\t\t\t\t\tstyle={{ height: HOUR_HEIGHT }}\n\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t\t\t\t<div key={hour} className=\"border-b border-dashed last:border-b-0\" style={{ height: HOUR_HEIGHT }} />\n\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t\t\t\t<ul className=\"absolute inset-0 pointer-events-none\">\n\t\t\t\t\t\t\t\t\t\t{layoutDay(timed).map(({ event, lane, lanes }) => {\n\t\t\t\t\t\t\t\t\t\t\t// Clip the event to the visible hours\n\t\t\t\t\t\t\t\t\t\t\tconst start = Math.max(event.start.getTime(), dayStart.getTime())\n\t\t\t\t\t\t\t\t\t\t\tconst end = Math.min(event.end.getTime(), dayEnd.getTime())\n\t\t\t\t\t\t\t\t\t\t\tconst top = (differenceInMinutes(start, dayStart) / 60) * HOUR_HEIGHT\n\t\t\t\t\t\t\t\t\t\t\tconst height = Math.max((differenceInMinutes(end, start) / 60) * HOUR_HEIGHT, 24)\n\t\t\t\t\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t\t\t\t\t<li\n\t\t\t\t\t\t\t\t\t\t\t\t\tkey={event.id}\n\t\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"pointer-events-auto absolute px-0.5\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tstyle={{ top, height, left: `${(lane / lanes) * 100}%`, width: `${100 / lanes}%` }}\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdata-slot=\"full-calendar-event\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tonClick={() => onEventClick?.(event)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t'flex size-full min-w-0 flex-col overflow-hidden rounded-sm border-l-3 px-1.5 py-1 text-left text-xs text-foreground outline-none transition-colors hover:brightness-95 focus-visible:z-10 focus-visible:ring-2 focus-visible:ring-ring dark:hover:brightness-110',\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\teventColors[event.color ?? 'primary'],\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span className=\"truncate font-medium\">{event.title}</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span className=\"truncate tabular-nums text-muted-foreground\">{timeRange(event, locale)}</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t\t\t\t</ul>\n\t\t\t\t\t\t\t\t\t{showNow ? (\n\t\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t\t\t\t\tclassName=\"pointer-events-none absolute inset-x-0 z-10 flex items-center\"\n\t\t\t\t\t\t\t\t\t\t\tstyle={{ top: (differenceInMinutes(now, dayStart) / 60) * HOUR_HEIGHT }}\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<span className=\"-ml-1 size-2 rounded-full bg-destructive\" />\n\t\t\t\t\t\t\t\t\t\t\t<span className=\"h-0.5 flex-1 bg-destructive\" />\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t})}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t)\n}\n\nexport { FullCalendar }\n",
      "type": "registry:ui",
      "target": "components/ui/full-calendar.tsx"
    }
  ],
  "meta": {
    "new": true
  },
  "type": "registry:ui"
}