{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "date-picker",
  "title": "Date Picker",
  "description": "Date Picker from the Get IT Done design system.",
  "dependencies": [
    "date-fns@^2.30.0",
    "lucide-react@^0.379.0",
    "react-day-picker@^8.9.1"
  ],
  "registryDependencies": [
    "https://zero-design.itdone.de/r/button.json",
    "https://zero-design.itdone.de/r/calendar.json",
    "https://zero-design.itdone.de/r/dialog.json",
    "https://zero-design.itdone.de/r/labels-context.json",
    "https://zero-design.itdone.de/r/popover.json",
    "https://zero-design.itdone.de/r/recipes.json",
    "https://zero-design.itdone.de/r/utils.json",
    "https://zero-design.itdone.de/r/theme.json"
  ],
  "files": [
    {
      "path": "registry/ui/date-picker.tsx",
      "content": "'use client';\n\nimport { format as formatDate } from 'date-fns';\nimport { Calendar as CalendarIcon } from 'lucide-react';\nimport * as React from 'react';\nimport type { DateRange } from 'react-day-picker';\n\nimport { fieldHaloZero, focusRing, focusRingField, openRing, transitionBase } from '@/lib/recipes';\nimport { cn } from '@/lib/utils';\nimport { Button } from '@/components/ui/button';\nimport { Calendar } from '@/components/ui/calendar';\nimport { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@/components/ui/dialog';\nimport { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';\nimport { useLabels, useLocale, usePreset, type DesignSystemLocale } from '@/components/ui/labels-context';\n// explicit file path; date-fns 2 has no exports map, so directory imports break Node ESM consumers (vitest, SSR)\nimport de from 'date-fns/locale/de/index.js';\n\nexport type { DateRange };\n\nexport type DatePickerMode = 'single' | 'range';\nexport type DatePickerValue = Date | DateRange | undefined;\n\nexport interface DatePickerPreset {\n  label: string;\n  // A date for single mode or a range for range mode.\n  value: Date | DateRange;\n}\n\n// Token-driven day grid for react-day-picker (the plain Calendar keeps its own look).\nexport const datePickerCalendarClassNames = {\n  months: 'flex flex-col gap-4 sm:flex-row',\n  month: 'flex flex-col gap-3',\n  caption: 'relative flex h-control-md items-center justify-center',\n  caption_label: 'typography-ui-semibold text-foreground',\n  nav: 'flex items-center',\n  nav_button: cn(\n    'inline-flex h-control-sm w-control-sm items-center justify-center rounded-item bg-transparent p-0 text-icon-subtle transition-colors hover:bg-muted hover:text-icon-default motion-reduce:transition-none',\n    focusRing,\n  ),\n  nav_button_previous: 'absolute left-0',\n  nav_button_next: 'absolute right-0',\n  table: 'w-full border-collapse',\n  head_row: 'flex',\n  head_cell: 'h-calendar-cell w-calendar-cell typography-ui-medium font-normal text-text-subtle',\n  row: 'mt-1 flex w-full',\n  cell: 'relative h-calendar-cell w-calendar-cell p-0 text-center',\n  day: cn(\n    'inline-flex h-calendar-cell w-calendar-cell items-center justify-center rounded-full p-0 typography-ui text-foreground transition-colors hover:bg-muted motion-reduce:transition-none',\n    focusRing,\n  ),\n  day_selected: 'bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground',\n  day_today: 'border border-solid border-border-bold',\n  day_outside: 'text-text-disabled',\n  day_disabled: 'pointer-events-none text-text-disabled opacity-disabled',\n  day_range_start: '',\n  day_range_end: '',\n  day_range_middle: '!rounded-none !bg-background-brand-subtle !text-foreground',\n  day_hidden: 'invisible',\n};\n\nconst isRange = (value: DatePickerValue): value is DateRange => value != null && !(value instanceof Date);\n\nconst defaultFormat = (date: Date, locale: DesignSystemLocale = 'en') =>\n  locale === 'de' ? formatDate(date, 'd. MMM yyyy', { locale: de }) : formatDate(date, 'MMM d, yyyy');\n\nexport const formatDatePickerValue = (\n  value: DatePickerValue,\n  formatValue?: (date: Date) => string,\n  locale: DesignSystemLocale = 'en',\n): string | undefined => {\n  const format = formatValue ?? ((date: Date) => defaultFormat(date, locale));\n  if (value == null) return undefined;\n  if (value instanceof Date) return format(value);\n  if (!value.from) return undefined;\n  return value.to ? `${format(value.from)} - ${format(value.to)}` : `${format(value.from)} -`;\n};\n\nexport interface DatePickerPanelProps {\n  mode?: DatePickerMode;\n  value?: DatePickerValue;\n  onValueChange?: (value: DatePickerValue) => void;\n  presets?: DatePickerPreset[];\n  // Months shown side by side. Defaults to 2 in range mode, 1 in single mode.\n  numberOfMonths?: number;\n  // Footer buttons. Provide `onApply` to show the Cancel / Apply row.\n  onApply?: () => void;\n  onCancel?: () => void;\n  applyLabel?: string;\n  cancelLabel?: string;\n  disabledDays?: React.ComponentProps<typeof Calendar>['disabled'];\n  fromDate?: Date;\n  toDate?: Date;\n  defaultMonth?: Date;\n  className?: string;\n}\n\n// Figma \"_Date picker menu\": optional preset list, one or two month grids and an optional action row.\nconst DatePickerPanel = ({\n  mode = 'single',\n  value,\n  onValueChange,\n  presets,\n  numberOfMonths,\n  onApply,\n  onCancel,\n  applyLabel: applyLabelProp,\n  cancelLabel: cancelLabelProp,\n  disabledDays,\n  fromDate,\n  toDate,\n  defaultMonth,\n  className,\n}: DatePickerPanelProps) => {\n  const l = useLabels();\n  const applyLabel = applyLabelProp ?? l.apply;\n  const cancelLabel = cancelLabelProp ?? l.cancel;\n  const months = numberOfMonths ?? (mode === 'range' ? 2 : 1);\n  const calendarProps =\n    mode === 'range'\n      ? ({ mode: 'range', selected: isRange(value) ? value : undefined, onSelect: (next: DateRange | undefined) => onValueChange?.(next) } as const)\n      : ({ mode: 'single', selected: value instanceof Date ? value : undefined, onSelect: (next: Date | undefined) => onValueChange?.(next) } as const);\n  const start = defaultMonth ?? (value instanceof Date ? value : isRange(value) ? value.from : undefined);\n\n  return (\n    <div className={cn('flex flex-col', className)}>\n      <div className=\"flex flex-col sm:flex-row\">\n        {presets && presets.length > 0 ? (\n          <ul aria-label={l.presets} className=\"flex flex-row flex-wrap gap-1 border-b border-solid border-border-subtle p-3 sm:w-40 sm:flex-col sm:flex-nowrap sm:border-b-0 sm:border-r\">\n            {presets.map((preset) => (\n              <li key={preset.label}>\n                <Button type=\"button\" variant=\"ghost\" size=\"sm\" className=\"w-full justify-start\" onClick={() => onValueChange?.(preset.value)}>\n                  {preset.label}\n                </Button>\n              </li>\n            ))}\n          </ul>\n        ) : null}\n        <Calendar\n          {...(calendarProps as React.ComponentProps<typeof Calendar>)}\n          numberOfMonths={months}\n          showOutsideDays={false}\n          defaultMonth={start}\n          disabled={disabledDays}\n          fromDate={fromDate}\n          toDate={toDate}\n          classNames={datePickerCalendarClassNames}\n          className=\"p-3\"\n        />\n      </div>\n      {onApply ? (\n        <div className=\"flex items-center justify-end gap-2 border-t border-solid border-border-subtle p-3\">\n          <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={onCancel}>\n            {cancelLabel}\n          </Button>\n          <Button type=\"button\" size=\"sm\" onClick={onApply}>\n            {applyLabel}\n          </Button>\n        </div>\n      ) : null}\n    </div>\n  );\n};\nDatePickerPanel.displayName = 'DatePickerPanel';\n\ninterface ValueProps {\n  mode?: DatePickerMode;\n  value?: DatePickerValue;\n  defaultValue?: DatePickerValue;\n  onValueChange?: (value: DatePickerValue) => void;\n}\n\nconst useDateValue = ({ value, defaultValue, onValueChange }: ValueProps) => {\n  const [inner, setInner] = React.useState<DatePickerValue>(defaultValue);\n  const current = value !== undefined ? value : inner;\n  const set = (next: DatePickerValue) => {\n    setInner(next);\n    onValueChange?.(next);\n  };\n  return [current, set] as const;\n};\n\nconst isComplete = (mode: DatePickerMode, value: DatePickerValue) => (mode === 'range' ? isRange(value) && Boolean(value.from && value.to) : value instanceof Date);\n\nexport interface DatePickerProps extends ValueProps, Omit<DatePickerPanelProps, 'value' | 'onValueChange' | 'onApply' | 'onCancel' | 'className' | 'mode'> {\n  placeholder?: string;\n  // Text format for the trigger. Receives each date.\n  formatValue?: (date: Date) => string;\n  disabled?: boolean;\n  invalid?: boolean;\n  readOnly?: boolean;\n  // Shows Cancel / Apply: picking only changes a draft until Apply.\n  actions?: boolean;\n  open?: boolean;\n  onOpenChange?: (open: boolean) => void;\n  id?: string;\n  name?: string;\n  className?: string;\n  'aria-label'?: string;\n  'aria-labelledby'?: string;\n  'aria-describedby'?: string;\n}\n\n// Figma \"Date picker dropdown\": field-like trigger that opens the picker panel in a popover.\nconst DatePicker = React.forwardRef<HTMLButtonElement, DatePickerProps>(\n  (\n    {\n      mode = 'single',\n      value,\n      defaultValue,\n      onValueChange,\n      placeholder: placeholderProp,\n      formatValue,\n      disabled = false,\n      invalid = false,\n      readOnly = false,\n      actions = false,\n      open: openProp,\n      onOpenChange,\n      className,\n      name,\n      ...rest\n    },\n    ref,\n  ) => {\n    const l = useLabels();\n    const locale = useLocale();\n    const zero = usePreset() === 'zero';\n    const placeholder = placeholderProp ?? l.selectDate;\n    const [selected, setSelected] = useDateValue({ value, defaultValue, onValueChange });\n    const [draft, setDraft] = React.useState<DatePickerValue>(selected);\n    const [openInner, setOpenInner] = React.useState(false);\n    const open = openProp ?? openInner;\n    const setOpen = (next: boolean) => {\n      if (next) setDraft(selected);\n      setOpenInner(next);\n      onOpenChange?.(next);\n    };\n    const { presets, numberOfMonths, disabledDays, fromDate, toDate, defaultMonth, ...ariaRest } = rest;\n    const text = formatDatePickerValue(selected, formatValue, locale);\n\n    const handleChange = (next: DatePickerValue) => {\n      if (actions) {\n        setDraft(next);\n        return;\n      }\n      setSelected(next);\n      if (isComplete(mode, next)) setOpen(false);\n    };\n\n    return (\n      <Popover open={readOnly ? false : open} onOpenChange={setOpen}>\n        <PopoverTrigger asChild>\n          <button\n            ref={ref}\n            type=\"button\"\n            disabled={disabled}\n            data-readonly={readOnly || undefined}\n            data-invalid={invalid || undefined}\n            data-placeholder={text ? undefined : ''}\n            className={cn(\n              'flex w-full items-center gap-2 rounded-control border border-solid border-input bg-background text-left typography-input-text leading-normal text-foreground',\n              // Zero: the md control height with the control padding and the halo focus, the other themes keep the 40px field.\n              zero ? 'h-control-md px-control-px-sm py-0' : 'px-4 py-2',\n              transitionBase,\n              'hover:border-border-bold hover:shadow-field-hover',\n              zero ? fieldHaloZero : [focusRingField, openRing],\n              'data-[invalid=true]:border-destructive data-[invalid=true]:ring-destructive/20 data-[invalid=true]:focus-visible:ring-destructive data-[invalid=true]:data-[state=open]:ring-destructive',\n              'data-[placeholder]:text-text-subtle',\n              'disabled:cursor-not-allowed disabled:opacity-disabled',\n              readOnly && 'cursor-default bg-muted hover:border-input hover:shadow-none',\n              className,\n            )}\n            {...ariaRest}\n          >\n            <CalendarIcon aria-hidden=\"true\" className=\"h-icon-xs w-icon-xs shrink-0 text-icon-subtle\" />\n            <span className=\"min-w-0 flex-1 truncate\">{text ?? placeholder}</span>\n          </button>\n        </PopoverTrigger>\n        {name ? <input type=\"hidden\" name={name} value={text ?? ''} /> : null}\n        <PopoverContent align=\"start\" className=\"w-auto p-0\">\n          <DatePickerPanel\n            mode={mode}\n            value={actions ? draft : selected}\n            onValueChange={handleChange}\n            presets={presets}\n            numberOfMonths={numberOfMonths}\n            disabledDays={disabledDays}\n            fromDate={fromDate}\n            toDate={toDate}\n            defaultMonth={defaultMonth}\n            onApply={\n              actions\n                ? () => {\n                    setSelected(draft);\n                    setOpen(false);\n                  }\n                : undefined\n            }\n            onCancel={() => setOpen(false)}\n          />\n        </PopoverContent>\n      </Popover>\n    );\n  },\n);\nDatePicker.displayName = 'DatePicker';\n\nexport interface DatePickerModalProps extends ValueProps, Omit<DatePickerPanelProps, 'value' | 'onValueChange' | 'onApply' | 'onCancel' | 'className' | 'mode'> {\n  open?: boolean;\n  onOpenChange?: (open: boolean) => void;\n  title?: string;\n  description?: string;\n  onApply?: (value: DatePickerValue) => void;\n}\n\n// Figma \"Date picker modal\": the same panel in a dialog, for small screens. Cancel / Apply always shown.\nconst DatePickerModal = ({\n  mode = 'single',\n  value,\n  defaultValue,\n  onValueChange,\n  open,\n  onOpenChange,\n  title: titleProp,\n  description,\n  onApply,\n  ...panel\n}: DatePickerModalProps) => {\n  const l = useLabels();\n  const title = titleProp ?? l.selectDate;\n  const [selected, setSelected] = useDateValue({ value, defaultValue, onValueChange });\n  const [draft, setDraft] = React.useState<DatePickerValue>(selected);\n  React.useEffect(() => {\n    if (open) setDraft(selected);\n    // The draft restarts from the committed value each time the dialog opens.\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [open]);\n\n  return (\n    <Dialog open={open} onOpenChange={onOpenChange}>\n      <DialogContent className=\"w-auto max-w-full gap-0 p-0 sm:max-w-fit\">\n        <DialogHeader className=\"p-4 pr-12\">\n          <DialogTitle>{title}</DialogTitle>\n          <DialogDescription className={description ? undefined : 'sr-only'}>{description ?? title}</DialogDescription>\n        </DialogHeader>\n        <DatePickerPanel\n          {...panel}\n          mode={mode}\n          value={draft}\n          onValueChange={setDraft}\n          onApply={() => {\n            setSelected(draft);\n            onApply?.(draft);\n            onOpenChange?.(false);\n          }}\n          onCancel={() => onOpenChange?.(false)}\n        />\n      </DialogContent>\n    </Dialog>\n  );\n};\nDatePickerModal.displayName = 'DatePickerModal';\n\nexport { DatePicker, DatePickerModal, DatePickerPanel };\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}