{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"mini-calendar","type":"registry:ui","title":"mini-calendar","description":"A composable mini calendar component for picking dates close to today.","author":"Hayden Bleasel <hello@haydenbleasel.com>","dependencies":["@radix-ui/react-use-controllable-state","date-fns","lucide-react","radix-ui"],"devDependencies":[],"registryDependencies":["button"],"files":[{"type":"registry:ui","path":"index.tsx","content":"\"use client\";\n\nimport { useControllableState } from \"@radix-ui/react-use-controllable-state\";\nimport { addDays, format, isSameDay, isToday } from \"date-fns\";\nimport { ChevronLeftIcon, ChevronRightIcon } from \"lucide-react\";\nimport { Slot } from \"radix-ui\";\nimport {\n  type ButtonHTMLAttributes,\n  type ComponentProps,\n  createContext,\n  type HTMLAttributes,\n  type MouseEventHandler,\n  type ReactNode,\n  useContext,\n} from \"react\";\nimport { Button } from \"@/components/ui/button\";\nimport { cn } from \"@/lib/utils\";\n\n// Context for sharing state between components\ntype MiniCalendarContextType = {\n  selectedDate: Date | null | undefined;\n  onDateSelect: (date: Date) => void;\n  startDate: Date;\n  onNavigate: (direction: \"prev\" | \"next\") => void;\n  days: number;\n};\n\nconst MiniCalendarContext = createContext<MiniCalendarContextType | null>(null);\n\nconst useMiniCalendar = () => {\n  const context = useContext(MiniCalendarContext);\n\n  if (!context) {\n    throw new Error(\"MiniCalendar components must be used within MiniCalendar\");\n  }\n\n  return context;\n};\n\n// Helper function to get array of consecutive dates\nconst getDays = (startDate: Date, count: number): Date[] => {\n  const days: Date[] = [];\n  for (let i = 0; i < count; i++) {\n    days.push(addDays(startDate, i));\n  }\n  return days;\n};\n\n// Helper function to format date\nconst formatDate = (date: Date) => {\n  const month = format(date, \"MMM\");\n  const day = format(date, \"d\");\n\n  return { month, day };\n};\n\nexport type MiniCalendarProps = HTMLAttributes<HTMLDivElement> & {\n  value?: Date;\n  defaultValue?: Date;\n  onValueChange?: (date: Date | undefined) => void;\n  startDate?: Date;\n  defaultStartDate?: Date;\n  onStartDateChange?: (date: Date | undefined) => void;\n  days?: number;\n};\n\nexport const MiniCalendar = ({\n  value,\n  defaultValue,\n  onValueChange,\n  startDate,\n  defaultStartDate = new Date(),\n  onStartDateChange,\n  days = 5,\n  className,\n  children,\n  ...props\n}: MiniCalendarProps) => {\n  const [selectedDate, setSelectedDate] = useControllableState<\n    Date | undefined\n  >({\n    prop: value,\n    defaultProp: defaultValue,\n    onChange: onValueChange,\n  });\n\n  const [currentStartDate, setCurrentStartDate] = useControllableState({\n    prop: startDate,\n    defaultProp: defaultStartDate,\n    onChange: onStartDateChange,\n  });\n\n  const handleDateSelect = (date: Date) => {\n    setSelectedDate(date);\n  };\n\n  const handleNavigate = (direction: \"prev\" | \"next\") => {\n    const newStartDate = addDays(\n      currentStartDate || new Date(),\n      direction === \"next\" ? days : -days\n    );\n    setCurrentStartDate(newStartDate);\n  };\n\n  const contextValue: MiniCalendarContextType = {\n    selectedDate: selectedDate || null,\n    onDateSelect: handleDateSelect,\n    startDate: currentStartDate || new Date(),\n    onNavigate: handleNavigate,\n    days,\n  };\n\n  return (\n    <MiniCalendarContext.Provider value={contextValue}>\n      <div\n        className={cn(\n          \"flex items-center gap-2 rounded-lg border bg-background p-2\",\n          className\n        )}\n        {...props}\n      >\n        {children}\n      </div>\n    </MiniCalendarContext.Provider>\n  );\n};\n\nexport type MiniCalendarNavigationProps =\n  ButtonHTMLAttributes<HTMLButtonElement> & {\n    direction: \"prev\" | \"next\";\n    asChild?: boolean;\n  };\n\nexport const MiniCalendarNavigation = ({\n  direction,\n  asChild = false,\n  children,\n  onClick,\n  ...props\n}: MiniCalendarNavigationProps) => {\n  const { onNavigate } = useMiniCalendar();\n  const Icon = direction === \"prev\" ? ChevronLeftIcon : ChevronRightIcon;\n\n  const handleClick: MouseEventHandler<HTMLButtonElement> = (event) => {\n    onNavigate(direction);\n    onClick?.(event);\n  };\n\n  if (asChild) {\n    return (\n      <Slot.Root onClick={handleClick} {...props}>\n        {children}\n      </Slot.Root>\n    );\n  }\n\n  return (\n    <Button\n      onClick={handleClick}\n      size={asChild ? undefined : \"icon\"}\n      type=\"button\"\n      variant={asChild ? undefined : \"ghost\"}\n      {...props}\n    >\n      {children ?? <Icon className=\"size-4\" />}\n    </Button>\n  );\n};\n\nexport type MiniCalendarDaysProps = Omit<\n  HTMLAttributes<HTMLDivElement>,\n  \"children\"\n> & {\n  children: (date: Date) => ReactNode;\n};\n\nexport const MiniCalendarDays = ({\n  className,\n  children,\n  ...props\n}: MiniCalendarDaysProps) => {\n  const { startDate, days: dayCount } = useMiniCalendar();\n  const days = getDays(startDate, dayCount);\n\n  return (\n    <div className={cn(\"flex items-center gap-1\", className)} {...props}>\n      {days.map((date) => children(date))}\n    </div>\n  );\n};\n\nexport type MiniCalendarDayProps = ComponentProps<typeof Button> & {\n  date: Date;\n};\n\nexport const MiniCalendarDay = ({\n  date,\n  className,\n  ...props\n}: MiniCalendarDayProps) => {\n  const { selectedDate, onDateSelect } = useMiniCalendar();\n  const { month, day } = formatDate(date);\n  const isSelected = selectedDate && isSameDay(date, selectedDate);\n  const isTodayDate = isToday(date);\n\n  return (\n    <Button\n      className={cn(\n        \"h-auto min-w-[3rem] flex-col gap-0 p-2 text-xs\",\n        isTodayDate && !isSelected && \"bg-accent\",\n        className\n      )}\n      onClick={() => onDateSelect(date)}\n      size=\"sm\"\n      type=\"button\"\n      variant={isSelected ? \"default\" : \"ghost\"}\n      {...props}\n    >\n      <span\n        className={cn(\n          \"font-medium text-[10px] text-muted-foreground\",\n          isSelected && \"text-primary-foreground/70\"\n        )}\n      >\n        {month}\n      </span>\n      <span className=\"font-semibold text-sm\">{day}</span>\n    </Button>\n  );\n};\n","target":"components/kibo-ui/mini-calendar/index.tsx"}],"css":{}}