import type { HTMLAttributes } from 'svelte/elements';
import type { MintProp } from '../../mint/index.js';
import type { MonthIndex, WeekdayIndex } from '../../utils/date.js';
import type { DateRange } from '../Calendar/calendar.types.js';
import type { DateFormatOptions } from './datepicker.engine.js';
/**
* @summary A date field with a calendar to pick from.
* @description Date input with calendar popup for selecting a single date. Supports min/max constraints, disabled dates, and format customization.
*
* The `value` prop accepts either a `Date` or an ISO timestamp string —
* useful when surrounding state is hydrated from JSON or a SQL driver
* that serialises timestamps as strings. `onValueChange` always reports
* back a `Date` (or `undefined`); use `toDateInputValue` from
* `@urbicon-ui/blocks` to project it back into a string state.
*
* @tag form
* @related DateRangePicker
* @related Calendar
* @related Input
*
* @example
* ```svelte
*
* ```
*
* @example ISO string state (e.g. from JSON / Drizzle timestamp)
* ```svelte
*
* (isoDate = toDateInputValue(d))}
* label="Period start"
* />
* ```
*
* @example
* ```svelte
* d.getDay() === 0 || d.getDay() === 6}
* required
* error={deadlineError}
* />
* ```
*/
export interface DatePickerProps extends Omit, 'children'> {
/**
* Currently selected date. Supports bind:value.
* Accepts a `Date`, an ISO timestamp string, or `null` / `undefined`.
* Internally coerced to a `Date`; the picker emits `Date` instances
* via {@link DatePickerProps.onValueChange}. When both `bind:value` and
* `onValueChange` are wired, both fire on every user-driven change —
* pick one to drive side-effects (saves, analytics) to avoid duplicates.
*/
value?: Date | string | null;
/** Label above the input. */
label?: string;
/** Placeholder when no date is selected. */
placeholder?: string;
/** Intl.DateTimeFormat options for the displayed date. */
displayFormat?: DateFormatOptions;
/** Error message shown below the input. */
error?: string;
/** Helper text shown below the input. */
helper?: string;
/** Mark input as required. @default false */
required?: boolean;
/** Allow clearing the selected date. @default true */
clearable?: boolean;
/** Close popover after selecting a date. @default true */
closeOnSelect?: boolean;
/** Whether the popover closes on Escape key. @default true */
closeOnEscape?: boolean;
/** Whether the popover closes on outside click. @default true */
closeOnClickOutside?: boolean;
/**
* Fires after Escape closes the popover. Notification only — does NOT
* govern whether close happens. That is controlled by `closeOnEscape`.
*/
onEscape?: () => void;
/**
* Fires after an outside click closes the popover. Notification only —
* does NOT govern whether close happens. That is controlled by
* `closeOnClickOutside`.
*/
onClickOutside?: () => void;
/**
* BCP 47 locale tag for date formatting — month names, weekday names and the
* header title. Defaults to `'auto'`, which follows the active
* `` locale, so an app that already declares its language does
* not have to repeat it here. SSR-safe: the locale comes from context, so the
* server and the client resolve the same tag (`Intl` with `undefined` would
* follow the runtime and disagree across hydration). Falls back to the base
* locale (`en`) when no provider is mounted. Pass an explicit tag
* (e.g. `'de-DE'`, `'ja-JP'`) to override.
* @default 'auto'
* @summary Which language the month and weekday names are rendered in.
*/
locale?: string;
/** First day of the week. 0 = Sunday, 1 = Monday. @default 1 */
weekStartsOn?: WeekdayIndex;
/** Show ISO week numbers. @default false */
showWeekNumbers?: boolean;
/** Show days from adjacent months. @default true */
showOutsideDays?: boolean;
/**
* Always show 6 week rows, so the overlay keeps its height while paging
* months. Set `false` to let it shrink to 4 or 5 rows.
* @default true
*/
fixedWeeks?: boolean;
/** Earliest selectable date. */
minDate?: Date;
/** Latest selectable date. */
maxDate?: Date;
/** Specific dates that are disabled. */
disabledDates?: Date[];
/**
* Predicate that disables specific dates. Errors thrown by the
* predicate are caught and logged; the date is then treated as
* allowed so a faulty consumer callback can't take the picker down.
*/
isDateDisabled?: (date: Date) => boolean;
/** Visual style of the calendar popup. @default 'default' */
calendarVariant?: 'default' | 'bordered' | 'ghost';
/** Input variant. @default 'outlined' */
inputVariant?: 'outlined' | 'filled' | 'ghost' | 'underline';
/** Component size. @default 'md' */
size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
/** Fires when the selected date changes. */
onValueChange?: (value: Date | undefined) => void;
/** Fires when the popover opens or closes. */
onOpenChange?: (open: boolean) => void;
/** Disable the entire picker. @default false */
disabled?: boolean;
/**
* Micro-interaction preset forwarded to the inner Input.
* @default 'none'
*/
mint?: MintProp;
class?: string;
/** Default month shown when the picker opens without a value. `0`–`11`. */
defaultMonth?: MonthIndex;
/** Default year shown when the picker opens without a value. */
defaultYear?: number;
/**
* Shared `name` for native form submission. When set, a hidden input
* is rendered carrying the serialized date — matching what the user
* picked, so the visible input's locale-formatted display string is
* never submitted instead.
*
* Empty / unset values submit as `""` so the field still appears in
* the FormData payload.
*/
name?: string;
/**
* Format used to serialise the date for the hidden form input.
* - `'date'` (default): `YYYY-MM-DD` in the local timezone — matches
* the native `` payload and Zod schemas like
* `z.string().regex(/^\d{4}-\d{2}-\d{2}$/).transform((v) => new Date(v))`.
* - `'iso'`: full ISO-8601 with `Z` suffix (UTC). Use this when the
* downstream schema expects a parseable timestamp string (e.g. a
* Drizzle `timestamp({ withTimezone: true, mode: 'date' })` column).
*
* Only relevant when {@link DatePickerProps.name} is set.
* @default 'date'
*/
valueFormat?: 'date' | 'iso';
}
/**
* @summary From and to, in two clicks on one calendar.
* @description Date range picker with a dual-calendar popup for selecting a start and end date — min/max constraints, disabled dates, and native form submission via paired hidden inputs.
*
* @tag form
* @related DatePicker
* @related Calendar
* @standalone
*/
export interface DateRangePickerProps extends Omit, 'children'> {
/** Currently selected date range. Supports bind:value. */
value?: DateRange;
/** Label above the input. */
label?: string;
/** Placeholder when no range is selected. */
placeholder?: string;
/** Intl.DateTimeFormat options for displayed dates. */
displayFormat?: DateFormatOptions;
error?: string;
helper?: string;
required?: boolean;
clearable?: boolean;
/** Close popover after selecting both dates. @default true */
closeOnSelect?: boolean;
/** Whether the popover closes on Escape key. @default true */
closeOnEscape?: boolean;
/** Whether the popover closes on outside click. @default true */
closeOnClickOutside?: boolean;
/** Notification only — does NOT govern close behavior. */
onEscape?: () => void;
/** Notification only — does NOT govern close behavior. */
onClickOutside?: () => void;
/**
* BCP 47 locale tag for date formatting — month names, weekday names and the
* header title. Defaults to `'auto'`, which follows the active
* `` locale, so an app that already declares its language does
* not have to repeat it here. SSR-safe: the locale comes from context, so the
* server and the client resolve the same tag (`Intl` with `undefined` would
* follow the runtime and disagree across hydration). Falls back to the base
* locale (`en`) when no provider is mounted. Pass an explicit tag
* (e.g. `'de-DE'`, `'ja-JP'`) to override.
* @default 'auto'
* @summary Which language the month and weekday names are rendered in.
*/
locale?: string;
weekStartsOn?: WeekdayIndex;
showWeekNumbers?: boolean;
showOutsideDays?: boolean;
fixedWeeks?: boolean;
minDate?: Date;
maxDate?: Date;
disabledDates?: Date[];
/**
* Predicate that disables specific dates. Throws are caught and logged;
* the date is then treated as allowed.
*/
isDateDisabled?: (date: Date) => boolean;
calendarVariant?: 'default' | 'bordered' | 'ghost';
inputVariant?: 'outlined' | 'filled' | 'ghost' | 'underline';
size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
/**
* Fires when the selected range changes. During calendar selection
* the user clicks twice — once to set the start, once to set the
* end. `onValueChange` only fires when the range is *complete*
* (start ≠ end); the intermediate `{ start: d, end: d }` state
* does NOT fire this callback. Use `bind:value` if you need the
* in-progress state.
*/
onValueChange?: (value: DateRange | undefined) => void;
onOpenChange?: (open: boolean) => void;
disabled?: boolean;
/**
* Micro-interaction preset forwarded to the inner Input.
* @default 'none'
*/
mint?: MintProp;
class?: string;
/** Default month shown when the picker opens without a value. `0`–`11`. */
defaultMonth?: MonthIndex;
/** Default year shown when the picker opens without a value. */
defaultYear?: number;
/**
* Shared base `name` for native form submission. When set, two hidden
* inputs are rendered — `{name}_start` and `{name}_end` — each
* carrying the serialized date, so the visible input's locale-formatted
* display string is never submitted instead.
*
* Empty range submits both halves as `""`. The `_start` / `_end`
* convention reflects the picker's domain language — date ranges read
* naturally as start/end rather than min/max.
*/
name?: string;
/**
* Format used to serialise both range halves. See
* {@link DatePickerProps.valueFormat} for semantics.
* @default 'date'
*/
valueFormat?: 'date' | 'iso';
}
export interface DatePickerPreset {
label: string;
value: Date;
}
export interface DateRangePreset {
label: string;
value: DateRange;
}
export type { DateRange } from '../Calendar/calendar.types.js';
export { default as DatePicker } from './DatePicker.svelte';
export { default as DateRangePicker } from './DateRangePicker.svelte';
export type { DateFormatOptions } from './datepicker.engine.js';