{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "datetime-picker",
  "title": "Datetime Picker",
  "description": "Segmented date and time input with keyboard stepping, powered by timescape.",
  "dependencies": [
    "timescape"
  ],
  "registryDependencies": [
    "input"
  ],
  "files": [
    {
      "path": "components/ui/datetime-picker.tsx",
      "content": "'use client'\nimport React, { forwardRef, useCallback, useState } from 'react'\nimport { useTimescape, type Options } from 'timescape/react'\n\nimport { Input } from '@/components/ui/input'\nimport { cn } from '@/lib/utils'\n// @source: https://github.com/dan-lee/timescape?tab=readme-ov-file\n\nconst timePickerInputBase =\n  'p-1 inline tabular-nums h-fit border-none outline-none select-none content-box caret-transparent rounded-sm min-w-8 text-center focus:bg-foreground/20 focus-visible:ring-0 focus-visible:outline-none'\nconst timePickerSeparatorBase = 'text-xs text-gray-400'\n\ntype DateFormat = 'days' | 'months' | 'years'\ntype TimeFormat = 'hours' | 'minutes' | 'seconds' | 'am/pm'\n\ntype DateTimeArray<T extends DateFormat | TimeFormat> = T[]\ntype DateTimeFormatDefaults = [\n  DateTimeArray<DateFormat>,\n  DateTimeArray<TimeFormat>,\n]\n\nconst DEFAULTS = [\n  ['months', 'days', 'years'],\n  ['hours', 'minutes', 'am/pm'],\n] as DateTimeFormatDefaults\n\ntype TimescapeReturn = ReturnType<typeof useTimescape>\ntype InputPlaceholders = Record<DateFormat | TimeFormat, string>\nconst INPUT_PLACEHOLDERS: InputPlaceholders = {\n  months: 'MM',\n  days: 'DD',\n  years: 'YYYY',\n  hours: 'HH',\n  minutes: 'MM',\n  seconds: 'SS',\n  'am/pm': 'AM/PM',\n}\n\n/**\n * Date time picker Docs: {@link: https://shadcn-extension.vercel.app/docs/otp-input}\n */\n\nconst DatetimeGrid = forwardRef<\n  HTMLDivElement,\n  {\n    format: DateTimeFormatDefaults\n    className?: string\n    timescape: Pick<TimescapeReturn, 'getRootProps' | 'getInputProps'>\n    placeholders: InputPlaceholders\n  }\n>(\n  (\n    {\n      format,\n      className,\n      timescape,\n      placeholders,\n    }: {\n      format: DateTimeFormatDefaults\n      className?: string\n      timescape: Pick<TimescapeReturn, 'getRootProps' | 'getInputProps'>\n      placeholders: InputPlaceholders\n    },\n    ref,\n  ) => {\n    return (\n      <div\n        className={cn(\n          'flex items-center w-fit p-1 border',\n          className,\n          'border-input rounded-md gap-1 selection:bg-transparent selection:text-foreground',\n        )}\n        {...timescape.getRootProps()}\n        ref={ref}\n      >\n        {!!format?.length\n          ? format.map((group, i) => (\n              <React.Fragment key={i === 0 ? 'dates' : 'times'}>\n                {!!group?.length\n                  ? group.map((unit, j) => (\n                      <React.Fragment key={unit}>\n                        <Input\n                          className={cn(timePickerInputBase, 'min-w-8', {\n                            'min-w-10': unit === 'years',\n                            'bg-foreground/15': unit === 'am/pm',\n                          })}\n                          {...timescape.getInputProps(unit)}\n                          placeholder={placeholders[unit]}\n                        />\n                        {i === 0 && j < group.length - 1 ? (\n                          // date separator\n                          <span className={timePickerSeparatorBase}>/</span>\n                        ) : (\n                          j < group.length - 2 && (\n                            // time separator\n                            <span className={timePickerSeparatorBase}>:</span>\n                          )\n                        )}\n                      </React.Fragment>\n                    ))\n                  : null}\n                {format[1]?.length && !i ? (\n                  // date-time separator - only if both date and time are present\n                  <span\n                    className={cn(\n                      timePickerSeparatorBase,\n                      'opacity-30 text-xl',\n                    )}\n                  >\n                    |\n                  </span>\n                ) : null}\n              </React.Fragment>\n            ))\n          : null}\n      </div>\n    )\n  },\n)\n\nDatetimeGrid.displayName = 'DatetimeGrid'\n\ninterface DateTimeInput {\n  value?: Date\n  format: DateTimeFormatDefaults\n  placeholders?: InputPlaceholders\n  onChange?: (date: Date | undefined) => void\n  dtOptions?: Options\n  className?: string\n}\n\nconst DEFAULT_TS_OPTIONS = {\n  date: new Date(),\n  hour12: true,\n}\nexport const DatetimePicker = forwardRef<HTMLDivElement, DateTimeInput>(\n  (\n    {\n      value = new Date(),\n      format = DEFAULTS,\n      placeholders,\n      dtOptions = DEFAULT_TS_OPTIONS,\n      onChange,\n      className,\n    },\n    ref,\n  ) => {\n    const handleDateChange = useCallback(\n      (nextDate: Date | null) => {\n        onChange ? onChange(nextDate ?? undefined) : console.log(nextDate)\n      },\n      [onChange],\n    )\n    const timescape = useTimescape({\n      defaultDate: value,\n      onDateChange: handleDateChange,\n      ...dtOptions,\n    })\n    return (\n      <DatetimeGrid\n        format={format}\n        className={className}\n        timescape={timescape}\n        placeholders={placeholders ?? INPUT_PLACEHOLDERS}\n        ref={ref}\n      />\n    )\n  },\n)\n\nDatetimePicker.displayName = 'DatetimePicker'\n",
      "type": "registry:ui"
    }
  ],
  "categories": [
    "input",
    "date-time"
  ],
  "type": "registry:ui"
}