timepicker-ui
4.6.0GitHub
timepicker-ui
API Reference / TypeScript

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.

index.ts
import DatepickerUI, {
DEFAULT_DATEPICKER_OPTIONS,
EventEmitter,
type DatepickerOptions,
type PickerValue,
} from "@pglejzer/datepicker-ui";
EntryExports
@pglejzer/datepicker-uiDatepickerUI (default and named), DEFAULT_DATEPICKER_OPTIONS, EventEmitter and every type listed below.
@pglejzer/datepicker-ui/kitNamed 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.

GroupTypes
OptionsDatepickerOptions, CalendarOptions, UIOptions, LabelsOptions, BehaviorOptions, CallbacksOptions
Option helpersThemeName, PluralLabel, DateDecoration, RangePreset, RangePresetValue
Option unionsCalendarMode, Precision, PageBy, RangeEnd
ValuesDateValue, RangeValue, PickerValue, PickerValueInput, PlainDate
EventsDatepickerEventMap, 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.

index.ts
interface DatepickerOptions {
calendar?: CalendarOptions; // mode, precision, months, limits, disabled days, locale
ui?: UIOptions; // theme, displayMode, inline, format, footer, buttons, ...
labels?: LabelsOptions; // every visible and assistive text
behavior?: 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.

index.ts
interface DateValue {
readonly value: string; // "2025-03-14"
readonly date: Date; // local midnight
readonly 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.

index.ts
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.

index.ts
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-12
direction: -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.

index.ts
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.

index.ts
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().

index.ts
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.

index.ts
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.

index.ts
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.

index.ts
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.

index.ts
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.

index.ts
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:

index.ts
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:

index.ts
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.

index.tsx
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.

index.ts
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);

Related

Every option is described on the Options page, and every event on the Events page.
Esc