{
  "name": "tsf-datetime-field",
  "type": "registry:ui",
  "dependencies": [
    "date-fns",
    "lucide-react",
    "react"
  ],
  "registryDependencies": [
    "button",
    "calendar",
    "field",
    "input-group",
    "popover",
    "tsf-form-context"
  ],
  "files": [
    {
      "path": "./items/tanstack-form/datetime-field/component.tsx",
      "type": "registry:ui",
      "target": "./components/ui/shuip/tanstack-form/datetime-field.tsx",
      "content": "'use client';\n\nimport type { Locale } from 'date-fns';\nimport { format, setHours, setMinutes } from 'date-fns';\nimport { CalendarIcon } from 'lucide-react';\nimport * as React from 'react';\nimport { Button } from '@/components/ui/button';\nimport { Calendar } from '@/components/ui/calendar';\nimport { Field, FieldDescription, FieldError, FieldLabel } from '@/components/ui/field';\nimport { InputGroup, InputGroupInput } from '@/components/ui/input-group';\nimport { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';\nimport { useFieldContext } from '@/components/ui/shuip/tanstack-form/form-context';\nimport { cn } from '@/lib/utils';\n\nexport interface DatetimeFieldProps {\n  label?: string;\n  description?: string;\n  placeholder?: string;\n  minDate?: Date;\n  maxDate?: Date;\n  locale?: Locale;\n  disabled?: boolean;\n  step?: number;\n}\n\nconst pad2 = (n: number): string => n.toString().padStart(2, '0');\n\nconst toTimeInputValue = (date: Date | undefined): string =>\n  date ? `${pad2(date.getHours())}:${pad2(date.getMinutes())}` : '';\n\nexport function DatetimeField({\n  label,\n  description,\n  placeholder = 'Pick a date & time',\n  minDate,\n  maxDate,\n  locale,\n  disabled,\n  step = 60,\n}: DatetimeFieldProps) {\n  const field = useFieldContext<Date | undefined>();\n  const { isValid, errors } = field.state.meta;\n  const [open, setOpen] = React.useState(false);\n\n  const value = field.state.value;\n\n  const handleDateSelect = (next: Date | undefined) => {\n    if (!next) {\n      field.handleChange(undefined);\n      return;\n    }\n    const hours = value?.getHours() ?? 0;\n    const minutes = value?.getMinutes() ?? 0;\n    field.handleChange(setMinutes(setHours(next, hours), minutes));\n  };\n\n  const handleTimeChange = (event: React.ChangeEvent<HTMLInputElement>) => {\n    if (!value) return;\n    const raw = event.target.value;\n    if (!raw) return;\n    const [h, m] = raw.split(':').map((part) => Number.parseInt(part, 10));\n    if (Number.isNaN(h) || Number.isNaN(m)) return;\n    field.handleChange(setMinutes(setHours(value, h), m));\n  };\n\n  const triggerLabel = value ? format(value, 'PPp', locale ? { locale } : undefined) : placeholder;\n\n  return (\n    <Field className='gap-2' data-invalid={!isValid}>\n      {label && <FieldLabel htmlFor={field.name}>{label}</FieldLabel>}\n      <Popover open={open} onOpenChange={setOpen}>\n        <PopoverTrigger asChild>\n          <Button\n            id={field.name}\n            type='button'\n            variant='outline'\n            disabled={disabled}\n            aria-invalid={!isValid}\n            className={cn('w-full justify-between font-normal', !value && 'text-muted-foreground')}\n            onBlur={field.handleBlur}\n          >\n            <span className='truncate'>{triggerLabel}</span>\n            <CalendarIcon className='size-4 opacity-70' />\n          </Button>\n        </PopoverTrigger>\n        <PopoverContent className='flex w-auto flex-col gap-3 p-3' align='start'>\n          <Calendar\n            mode='single'\n            selected={value}\n            onSelect={handleDateSelect}\n            disabled={[...(minDate ? [{ before: minDate }] : []), ...(maxDate ? [{ after: maxDate }] : [])]}\n            locale={locale}\n            autoFocus\n          />\n          <InputGroup>\n            <InputGroupInput\n              type='time'\n              step={step}\n              value={toTimeInputValue(value)}\n              onChange={handleTimeChange}\n              disabled={disabled || !value}\n              aria-label='Time'\n            />\n          </InputGroup>\n          <Button type='button' size='sm' onClick={() => setOpen(false)}>\n            OK\n          </Button>\n        </PopoverContent>\n      </Popover>\n      {!isValid && (\n        <FieldError\n          className='text-xs text-left'\n          errors={errors.map((error) => ({ message: typeof error === 'string' ? error : error?.message }))}\n        />\n      )}\n      {description && <FieldDescription className='text-xs'>{description}</FieldDescription>}\n    </Field>\n  );\n}\n"
    }
  ],
  "$schema": "https://ui.shadcn.com/schema/registry-item.json"
}
