GrDatePicker

  • chrono
  • chrono
  • API · 31 props
import { GrDatePicker } from '@feugene/granularity-chrono/components/GrDatePicker'

Overview

When to take it

  • the date is the value of a form field — the form-control contract of the core works as a whole: the error, the requiredness and the label arrive from GrFormField with no edits at all;
  • the date can be both typed and picked — editable switches typing on, and the parsing goes by the locale rather than by one rigid template;
  • some of the dates are unavailable — disabledDates accepts both a list and a predicate: weekends, taken slots, “no earlier than today” through min/max;
  • something other than a Date goes to the backend — valueAdapter changes the type of the model to an ISO string, a timestamp or a format of your own, and that is visible in the types.

When to take something else

NeedTake
A “from — to” periodGrDateRangePicker
A date together with a timeGrDateTimePicker
A time onlyGrTimePicker
The month grid on its own, without a fieldGrCalendar
Show a moment rather than choose itGrRelativeTime
The choice is only among ready values, an arbitrary date is not neededGrSelect

Examples 4 each with a live demo and its code

  • Issue and due dates of an invoice

    Presets can be functions: «Net 30» is counted from the issue date at the moment of the click, not when the code was written. min keeps the due date after the issue date, weekends are disabled-dates, and editable lets the date be typed. value-adapter="isoDate" makes the model the «2026-11-05» string the API stores, and name sends it with a native form.

    Invoice INV-0422 · draft

    Weekends are not offered

    Payment terms: 30 days · sent as due_date=2026-11-05
  • A tax period, monthly or quarterly

    mode changes what is picked, not how it is stored: the panel shows months or quarters, and the model stays a Date — the first day of the period. max keeps future periods out, because a return is filed for what has passed.

    VAT return
    1 Jul – 30 Sept · due 10 Nov
  • Courier days off, as a set

    multiple makes the model an array — separate dates, not a range. separator and format decide how the set reads in the field, show-week-numbers matches the courier’s schedule, and min/max limit the quarter.

    Courier days off · Q4Orders placed the day before ship one day later
    4 days off in the quarter
  • Booking a call with the panel on the page

    inline draws the panel in place — no field, no popover, the same model. The day slot adds a dot per free slot under each number, and days without slots are disabled-dates, so they are skipped by the keyboard too.

    October 2026
    MonTueWedThuFriSatSun
    2829301234
    567891011
    12131415161718
    19202122232425
    2627282930311
    2345678
    Onboarding call · 30 minWith Grace Wu, product specialist. Dots show free slots.Pick a day with free slots.

API generated from the package · cannot drift

Props

PropTypedefaultDescription
mode"day" | "week" | "month" | "quarter" | "year" | undefined"day"What is picked: a day, a month or a year. In period modes the panel shows twelve cells, and the value becomes the first day of the period.
modelValuev-modelTValue | readonly TValue[] | undefinedundefinedThe value. With `multiple` it is an array: a set, not a range. Nothing tighter than the union can be expressed in a generic SFC: Vue infers a conditional type from a boolean prop unreliably, and a promise that sometimes fails is worse than an honest `T | readonly T[]`. The runtime contract stays exact — exactly what the prop declares is emitted.
multipleboolean | undefinedfalseA set of dates instead of one: schedules, exception dates, bookings. The difference from `GrDateRangePicker` is substantive: there a continuous segment with two ends, here an arbitrary set. The panel does not close after a pick, and a click on a selected date clears it.
separatorstring | undefined", "What separates the dates of the set in the field.
valueAdapter
"date" | "isoDate" | "isoDateTime" | "timestamp" | GrChronoA"date" | "isoDate" | "isoDateTime" | "timestamp" | GrChronoAdapter<TValue> | undefined
undefinedHow the value is emitted and comes back: the name of a built-in adapter (`date`, `isoDate`, `isoDateTime`, `timestamp`) or a custom one. A separate prop rather than a swap of the model type: in the predecessor a string set the actual type, and deriving it from the types was impossible.
minDate | undefinedundefinedThe earliest date that can be selected. Days before it are visible in the panel but disabled.
maxDate | undefinedundefinedThe latest date that can be selected. Days after it are visible in the panel but disabled.
disabledDatesreadonly Date[] | ((date: Date) => boolean) | undefinedundefinedForbidden dates: a list or a predicate.
presetsreadonly GrDatePreset[] | undefinedundefinedReady-made dates in the panel footer: "Today", "Tomorrow". A date can be given as a function — "today" is computed when the panel is shown, not when the prop is declared. The function must be pure: it is called both to decide whether the shortcut is available and to apply it. A date outside `min`/`max` or from `disabledDates` arrives disabled — as does its cell in the grid.
weekStartIsoWeekday | undefinedundefinedFirst day of the week per ISO (1 is Monday). Not set — from the locale.
showWeekNumbersboolean | undefinedundefinedA column of ISO 8601 week numbers in the panel. Not set — the `GrCalendar` setting from `GrConfigProvider`, otherwise `false`.
todayDate | undefinedundefinedWhat counts as today. Set for the sake of reproducible tests and snapshots.
localestring | undefinedundefinedDisplay locale. Not set — from the application's i18n adapter.
formatIntl.DateTimeFormatOptions | undefinedundefinedHow the value looks in the field — via `Intl` options, not a pattern string: a pattern would have to be parsed by hand and still would not know the order of the parts in a foreign locale.

Slots

SlotTypeDescription
day{ cell: CalendarCell; selected: boolean; }A custom day cell instead of the number.
header{ title: string; goToPeriod: (delta: number) => void; }A custom panel header instead of the title and the arrows.
weekday{ label: string; full: string; isoWeekday: IsoWeekday; }A custom weekday header cell instead of the abbreviated day name.
footer
{ select: (date: Date) => boolean; canSelect: (date: Date) ={ select: (date: Date) => boolean; canSelect: (date: Date) => boolean; close: () => void; }
The panel footer. Picking is delegated inward: a date's ban is composed of `min`, `max` and `disabledDates`, while `readonly` forbids picking entirely — from outside these rules are invisible.

Events

EventSignatureDescription
update:modelValue(e: "update:modelValue", value: TValue | readonly TValue[] | null): voidThe new value (`v-model`); `null` — the value was removed.
change(e: "change", value: TValue | readonly TValue[] | null): voidThe value was committed: by a selection in the panel, by typing or by clearing.
update:open(e: "update:open", value: boolean): voidThe panel opened or closed (`v-model:open`).
clear(e: "clear"): voidThe value was removed with the clear button; only with `clearable`.
focus(e: "focus", event: FocusEvent): voidFocus entered the field.
blur(e: "blur", event: FocusEvent): voidFocus left the field.

Types

Every exported type of the subpath, grouped by the file it lives in. Names, generics and bodies come from the package’s declaration files and cannot drift.

Import
import type { GrDatePickerConfigurableProps, GrDatePickerSize, GrDatePickerProps } from '@feugene/granularity-chrono/components/GrDatePicker'

Filedist/types/components/GrDatePicker/config.d.ts

grDatePickerConfigconst
const grDatePickerConfig: import("@feugene/granum/contract").GranumComponentDescriptor<"GrDatePicker">;

Filedist/types/components/GrDatePicker/defaults.d.ts

GrDatePickerConfigurablePropsinterface
interface GrDatePickerConfigurableProps {
    size: GrDatePickerSize;
    clearable: boolean;
    placement: UseFloatingPlacement;
}

Filedist/types/components/GrDatePicker/grDatePickerStyles.d.ts

GrDatePickerSizetype
type GrDatePickerSize = GrPickerSize;

Filedist/types/components/GrDatePicker/safelist.d.ts

grDatePickerSafelistconst
const grDatePickerSafelist: string[];

Filedist/types/components/GrDatePicker/GrDatePicker.vue.d.ts

GrDatePickerPropsinterface
interface GrDatePickerProps<T = Date | null> {
    mode?: 'day' | 'week' | 'month' | 'quarter' | 'year';
    modelValue?: T | readonly T[];
    multiple?: boolean;
    separator?: string;
    valueAdapter?: GrChronoAdapterName | GrChronoAdapter<T>;
    min?: Date;
    max?: Date;
    disabledDates?: readonly Date[] | ((date: Date) => boolean);
GrDatePickerEmitsinterface
interface GrDatePickerEmits<T = Date | null> {
    (e: 'update:modelValue', value: T | readonly T[] | null): void;
    (e: 'change', value: T | readonly T[] | null): void;
    (e: 'update:open', value: boolean): void;
    (e: 'clear'): void;
    (e: 'focus', event: FocusEvent): void;
    (e: 'blur', event: FocusEvent): void;
}
GrDatePresetinterface
interface GrDatePreset {
    label: string;
    date: Date | (() => Date);

Usage from the library prose

The ready dates live inside the panel

presets draws a row of shortcuts in the footer: “Today”, “Tomorrow”, “Monday”. A date can be set with a function — “today” is computed at the moment of showing rather than at the moment the prop is declared.

A shortcut bypasses the grid, so it checks the prohibitions itself: a date outside min/max or from disabledDates arrives switched off — exactly like its cell in the grid. A button that does nothing deceives.

A footer of your own is the footer slot; it replaces the row as a whole and receives select, canSelect and close.

The value is local midnight rather than a moment

A date without a time is assembled as local midnight: the user chooses a day in their own calendar rather than a point on the world line. Whoever needs precisely a moment serialises it with an adapter of their own; the package will not substitute UTC silently, because “12 August” in two time zones is two different moments and one and the same day.

Not only days: a week, a month, a quarter, a year

mode is passed into the calendar as it is, and the field shows the selection in the ordinary date format:

vue
<GrDatePicker v-model="reportPeriod" mode="quarter" />

A week puts its beginning into the model, a quarter and a month the first day, and a year the first of January. The shape of the value does not depend on the mode: there is still one date in the field, and valueAdapter works as it worked.

The details of the modes — ./GrCalendar.md; there too is why a week is drawn as a grid of days rather than as a grid of periods, and why the week number is not shown.

A set of dates is not a range

multiple collects an arbitrary set: a schedule of lessons, exception dates in a plan, booked days. The model becomes an array.

vue
<GrDatePicker v-model="lessons" multiple />

The difference from GrDateRangePicker is one of essence rather than of convenience: there there is a continuous segment with two edges and rules about its length, here a set where adjacency means nothing.

A click on a selected date removes it. A set is a toggle rather than an accumulator: otherwise there would be nothing to remove a date taken by mistake with.

The panel does not close after a choice. A set is collected, while a single date is chosen once — and closing after the first click would turn a set of ten dates into ten openings of the panel.

The order is always ascending, wherever the clicks landed. The model has to be comparable: a rearrangement of the elements must not read as a change — otherwise “there are unsaved edits” fires out of nowhere.

Typing in this mode is switched off, even with editable. A string describing N dates requires parsing of its own: how many of them have been collected does not follow from the string, and an incomplete entry cannot be told from a short set. A range and a date with a time describe exactly two values and are therefore typeable (GrDateRangePicker, GrDateTimePicker); an arbitrary set is not.

The first three dates and the remainder as a number (“and 2 more”) are visible in the field: without a ceiling the label overflows already at the fifth. A hidden field per date goes into the form — with one name, the way FormData.getAll reads it.

min, max and disabledDates apply to every date separately, by the same rules as with a single choice.

applyOnBlur is switched on

A choice in the calendar is applied at once, and the loss of focus from the field commits what was typed by hand. Otherwise a date that had been typed but not confirmed would disappear on moving to the next field — while the user believes they entered it.

An editable field shows what it accepts back

With editable the value is output in digits (08/12/2026) rather than as Aug 12, 2026. Otherwise editing a number right in the field would leave the parser two groups of digits instead of three: what was typed would roll back silently, although the user broke nothing.

A format of your own overrides that rule — then the readability of what is typed is the consumer’s responsibility. Without editable the display stays as it was.

A forbidden date is not accepted as text either. disabledDates, min and max apply equally to a click and to Enter; a field bypassing the restrictions of the panel would mean there are no restrictions.