TypeScript
Types for options, values and events in @pglejzer/datepicker-ui, the companion of timepicker-ui.
Installation
#The type definitions ship in the package, so there is nothing else to install. DatepickerUI is the default export and also a named export.
import DatepickerUI, {DEFAULT_DATEPICKER_OPTIONS,EventEmitter,type DatepickerOptions,type PickerValue,} from "@pglejzer/datepicker-ui";
| Entry | Exports |
|---|---|
@pglejzer/datepicker-ui | DatepickerUI (default and named), DEFAULT_DATEPICKER_OPTIONS, EventEmitter and every type listed below. |
@pglejzer/datepicker-ui/kit | Named exports only: PopoverHost, DialogHost, InlineHost, waitForExit, resolveSurfaceMode, isFullscreenRange, detectColorScheme, watchColorScheme, resolvePattern, formatDate, parseDate, maskDateText, formatHint and the types SurfaceHost, SurfaceMode, Scheme, FormatPattern, FormatOption, DatePart and PlainDate. |
Exported types
#Every type below is exported from @pglejzer/datepicker-ui and works with import type.
| Group | Types |
|---|---|
| Options | DatepickerOptions, CalendarOptions, UIOptions, LabelsOptions, BehaviorOptions, CallbacksOptions |
| Option helpers | ThemeName, PluralLabel, DateDecoration, RangePreset, RangePresetValue |
| Option unions | CalendarMode, Precision, PageBy, RangeEnd |
| Values | DateValue, RangeValue, PickerValue, PickerValueInput, PlainDate |
| Events | DatepickerEventMap, OpenEventData, CloseEventData, CancelEventData, ConfirmEventData, UpdateEventData, ErrorEventData, SelectDayEventData, SelectMonthEventData, SelectYearEventData, NavigateEventData, RangeStartEventData, RangeEndEventData, UnavailableReason |
Main interfaces
#DatepickerOptions
The grouped options object. Each group has its own exported interface. See the Options page for every field.
interface DatepickerOptions {calendar?: CalendarOptions; // mode, precision, months, limits, disabled days, localeui?: UIOptions; // theme, displayMode, inline, format, footer, buttons, ...labels?: LabelsOptions; // every visible and assistive textbehavior?: BehaviorOptions; // closeOnSelect, commitOnChange, returnFocus, id, ...callbacks?: CallbacksOptions; // onConfirm, onCancel, onOpen, ...}
DateValue, RangeValue and PickerValue
What getValue() returns and what confirm carries. The shape depends on calendar.mode. value is YYYY-MM-DD, YYYY-MM for month precision and YYYY for year precision.
interface DateValue {readonly value: string; // "2025-03-14"readonly date: Date; // local midnightreadonly formatted: string; // as the input shows it}interface RangeValue {readonly start: DateValue;readonly end: DateValue;}type PickerValue = DateValue | RangeValue | readonly DateValue[];
PickerValueInput
What setValue() accepts. The shape has to match calendar.mode.
type PickerValueInput =| Date| string| { readonly start: Date | string; readonly end: Date | string }| readonly (Date | string)[];
Event payloads
Each event has a payload type, all exported from the package root.
interface OpenEventData {mode: "docked" | "modal" | "inline";}type CloseEventData = Record<string, never>;interface CancelEventData {reason: "button" | "escape" | "outside";}interface ConfirmEventData {value: PickerValue | null;autoCommit: boolean;}interface UpdateEventData {options: DatepickerOptions;}interface SelectDayEventData {value: DateValue;selected: boolean;}interface RangeStartEventData {value: DateValue;}interface RangeEndEventData {start: DateValue;end: DateValue;}interface SelectMonthEventData {year: number;month: number; // 1-12}interface SelectYearEventData {year: number;}interface NavigateEventData {year: number;month: number; // first month shown, 1-12direction: -1 | 0 | 1;source: "drag" | "wheel" | "button" | "keyboard" | "menu";}interface ErrorEventData {code: "invalid-date" | "unavailable-date" | "invalid-option" | "invalid-value";message: string;input?: string;option?: string;reason?: UnavailableReason;}
DatepickerEventMap
Maps every event name to its payload type. on(), once() and off() are typed against this map, so a handler gets the right payload without an annotation.
interface DatepickerEventMap {open: OpenEventData;close: CloseEventData;cancel: CancelEventData;confirm: ConfirmEventData;update: UpdateEventData;error: ErrorEventData;"select:day": SelectDayEventData;"select:month": SelectMonthEventData;"select:year": SelectYearEventData;"range:start": RangeStartEventData;"range:end": RangeEndEventData;navigate: NavigateEventData;}
Unions and small shapes
#ThemeName
The built-in theme names.
type ThemeName =| "basic"| "m3-green"| "crane"| "crane-straight"| "m2"| "glassmorphic"| "pastel"| "ai"| "cyberpunk"| "blueprint";
CalendarMode, Precision, PageBy and RangeEnd
The unions behind calendar.mode, calendar.precision, calendar.pageBy and setActiveEnd().
type CalendarMode = "single" | "range" | "multiple";type Precision = "day" | "month" | "year";type PageBy = "month" | "view";type RangeEnd = "start" | "end";
UnavailableReason
Why a typed date cannot be selected, in the order the rules are checked.
type UnavailableReason =| "min"| "max"| "disabled"| "weekday"| "range-length"| "multiple-max";
PluralLabel
A label that is a string, or a map from Intl.PluralRules categories to strings. {count} is replaced by the number.
type PluralLabel =string | Readonly<Partial<Record<Intl.LDMLPluralRule, string>>>;
DateDecoration, RangePreset and RangePresetValue
Shapes returned by or passed to calendar.decorateDate and calendar.range.presets.
interface DateDecoration {className?: string;badge?: boolean;label?: string;}interface RangePresetValue {start: Date | string;end: Date | string;}interface RangePreset {label: string;value: RangePresetValue | (() => RangePresetValue);}
Typed options
#Annotate an options object with DatepickerOptions to get completion for every group, and typed arguments in the function options such as isDateDisabled and the callbacks.
import DatepickerUI, { type DatepickerOptions } from "@pglejzer/datepicker-ui";const options: DatepickerOptions = {calendar: {mode: "range",minDate: "today",isDateDisabled: (date) => date.getDay() === 0,},ui: { theme: "m3-green" },callbacks: {onConfirm: ({ value }) => {console.log(value);},},};const input = document.querySelector<HTMLInputElement>("#date");if (input) {new DatepickerUI(input, options).create();}
Typed handlers
#on(), once() and off() are typed against DatepickerEventMap. The handler payload is inferred from the event name.
import DatepickerUI, {type ConfirmEventData,type DatepickerEventMap,} from "@pglejzer/datepicker-ui";const picker = new DatepickerUI("#date");picker.on("select:day", ({ value, selected }) => {console.log(value.value, selected);});const onConfirm = (data: ConfirmEventData) => {console.log(data.value, data.autoCommit);};picker.on("confirm", onConfirm);picker.off("confirm", onConfirm);function listen<K extends keyof DatepickerEventMap>(event: K,handler: (data: DatepickerEventMap[K]) => void,) {return picker.on(event, handler);}const stop = listen("navigate", ({ year, month, source }) => {console.log(year, month, source);});stop();
Narrowing PickerValue
#getValue() returns PickerValue | null. Which member you get depends on calendar.mode: DateValue for "single", RangeValue for "range" and DateValue[] for "multiple". The confirm payload has the same type. When you only know the mode at runtime, narrow with the in operator:
import {type DateValue,type PickerValue,type RangeValue,} from "@pglejzer/datepicker-ui";function isRange(value: PickerValue): value is RangeValue {return "start" in value;}function isSingle(value: PickerValue): value is DateValue {return "value" in value;}function describe(value: PickerValue | null): string {if (value === null) return "Nothing selected";if (isRange(value)) return value.start.value + " to " + value.end.value;if (isSingle(value)) return value.value;return value.map((entry) => entry.value).join(", ");}
When the mode is fixed in your code, assert the member that mode returns:
import DatepickerUI, {type DateValue,type RangeValue,} from "@pglejzer/datepicker-ui";const picker = new DatepickerUI("#date", { calendar: { mode: "range" } });picker.create();const range = picker.getValue() as RangeValue | null;if (range) {console.log(range.start.value, range.end.value);}const single = new DatepickerUI("#birthday");single.create();const day = single.getValue() as DateValue | null;
React with TypeScript
#Create the picker in an effect and destroy it on cleanup. Keep options stable, for example at module scope, because a new object recreates the picker.
import { useEffect, useRef } from "react";import DatepickerUI, { type DatepickerOptions } from "@pglejzer/datepicker-ui";import "@pglejzer/datepicker-ui/index.css";type DateFieldProps = {options?: DatepickerOptions;onDate: (iso: string | null) => void;};export function DateField({ options, onDate }: DateFieldProps) {const ref = useRef<HTMLInputElement>(null);useEffect(() => {if (!ref.current) return;const picker = new DatepickerUI(ref.current, options);picker.create();picker.on("confirm", ({ value }) => {onDate(value && "value" in value ? value.value : null);});return () => picker.destroy();}, [options, onDate]);return <input ref={ref} />;}
Kit types
#The kit entry has its own types for companion packages. PlainDate is also exported from the package root.
import {parseDate,resolvePattern,type FormatPattern,type PlainDate,} from "@pglejzer/datepicker-ui/kit";const pattern: FormatPattern = resolvePattern("dd.MM.yyyy", "en-GB");const date: PlainDate | null = parseDate("14.03.2025", pattern);