'use client'
import { forwardRef, useState } from 'react'
import * as PopoverPrimitive from '@radix-ui/react-popover'
import { ChevronDown } from '../../icons'
import { cn } from '../../lib/cn'
import { Calendar, formatDateLabel, formatDateRangeLabel } from '../calendar/calendar'
import { chipVariants, TRIGGER_BORDER_CLASS } from '../chip/chip'
import { chipContentLabelClass } from '../chip/chip-chrome'
import { POPOVER_ANIMATION_CLASSES } from '../popover/popover-animation'
interface ChipDatePickerBaseProps {
/**
* Trigger chrome. `filled` (default) is the bordered field chip with the owned
* chevron; `ghost` is the bare toolbar pill — no border, no chevron — for
* visual parity with neighboring `Chip` buttons.
*/
variant?: 'filled' | 'ghost'
/**
* Overrides the trigger text, e.g. a calendar period label ("June 2026") whose
* granularity differs from the selected value.
*/
label?: string
/** Shown in the trigger when nothing is selected. */
placeholder?: string
/** Aligns the calendar popover relative to the trigger. */
align?: 'start' | 'center' | 'end'
/** Disables the trigger. */
disabled?: boolean
/** Stretch the trigger to fill its container (mirrors `Chip`'s `fullWidth`). */
fullWidth?: boolean
/** Removes the default `mx-0.5` cluster margin (mirrors `Chip`'s `flush`). */
flush?: boolean
/** Forwarded class for the trigger button. */
className?: string
}
interface ChipDatePickerSingleProps extends ChipDatePickerBaseProps {
mode?: 'single'
/** Selected date as a `YYYY-MM-DD` string. */
value?: string
/** Called with the picked date in `YYYY-MM-DD` format. */
onChange?: (value: string) => void
/**
* Today's calendar day (`YYYY-MM-DD`) in the caller's effective timezone;
* defaults to the runtime's local day (mirrors `Calendar`'s `today`).
*/
today?: string
}
interface ChipDatePickerRangeProps extends ChipDatePickerBaseProps {
mode: 'range'
/** Range start as a `YYYY-MM-DD` (or `YYYY-MM-DDTHH:mm`) string. */
startDate?: string
/** Range end as a `YYYY-MM-DD` (or `YYYY-MM-DDTHH:mm`) string. */
endDate?: string
/** Adds start/end time-of-day inputs; emits `YYYY-MM-DDTHH:mm` bounds. */
showTime?: boolean
/** Called on Apply with the ordered range bounds. */
onRangeChange: (start: string, end: string) => void
}
export type ChipDatePickerProps = ChipDatePickerSingleProps | ChipDatePickerRangeProps
/**
* Date counterpart to {@link ChipDropdown} — a chip-styled trigger that opens a
* {@link Calendar} in a popover. The default `filled` trigger reuses
* `chipVariants` (filled + border) and the owned chevron for visual parity with
* the other chip field controls; `ghost` renders the bare toolbar pill instead.
*
* `mode='single'` (default) commits on day click. `mode='range'` opens the
* range calendar — start/end staged behind Clear/Cancel/Apply, with optional
* time-of-day inputs — and commits via `onRangeChange`.
*
* @example
*
*
* @example
*
*/
const ChipDatePicker = forwardRef(
function ChipDatePicker(props, ref) {
const {
variant = 'filled',
label,
placeholder = props.mode === 'range' ? 'Select date range' : 'Select date',
align = 'start',
disabled,
fullWidth,
flush,
className,
} = props
const [open, setOpen] = useState(false)
const triggerText =
label ??
(props.mode === 'range'
? formatDateRangeLabel(props.startDate, props.endDate)
: formatDateLabel(props.value))
return (
{props.mode === 'range' ? (
{
props.onRangeChange(start, end)
setOpen(false)
}}
onCancel={() => setOpen(false)}
/>
) : (
{
props.onChange?.(next)
setOpen(false)
}}
/>
)}
)
}
)
ChipDatePicker.displayName = 'ChipDatePicker'
export { ChipDatePicker }