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
GrFormFieldwith no edits at all; - the date can be both typed and picked —
editableswitches typing on, and the parsing goes by the locale rather than by one rigid template; - some of the dates are unavailable —
disabledDatesaccepts both a list and a predicate: weekends, taken slots, “no earlier than today” throughmin/max; - something other than a
Dategoes to the backend —valueAdapterchanges 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
| Need | Take |
|---|---|
| A “from — to” period | GrDateRangePicker |
| A date together with a time | GrDateTimePicker |
| A time only | GrTimePicker |
| The month grid on its own, without a field | GrCalendar |
| Show a moment rather than choose it | GrRelativeTime |
| The choice is only among ready values, an arbitrary date is not needed | GrSelect |
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.
minkeeps the due date after the issue date, weekends aredisabled-dates, andeditablelets the date be typed.value-adapter="isoDate"makes the model the «2026-11-05» string the API stores, andnamesends it with a native form.A tax period, monthly or quarterly
modechanges what is picked, not how it is stored: the panel shows months or quarters, and the model stays aDate— the first day of the period.maxkeeps future periods out, because a return is filed for what has passed.VAT return1 Jul – 30 Sept · due 10 NovCourier days off, as a set
multiplemakes the model an array — separate dates, not a range.separatorandformatdecide how the set reads in the field,show-week-numbersmatches the courier’s schedule, andmin/maxlimit the quarter.Courier days off · Q4Orders placed the day before ship one day later 4 days off in the quarterBooking a call with the panel on the page
inlinedraws the panel in place — no field, no popover, the same model. Thedayslot adds a dot per free slot under each number, and days without slots aredisabled-dates, so they are skipped by the keyboard too.October 2026Mon Tue Wed Thu Fri Sat Sun 28 29 30 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 1 2 3 4 5 6 7 8 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
| Prop | Type | default | Description |
|---|---|---|---|
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-model | TValue | readonly TValue[] | undefined | undefined | The 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. |
multiple | boolean | undefined | false | A 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. |
separator | string | undefined | ", " | What separates the dates of the set in the field. |
valueAdapter |
| undefined | How 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. |
min | Date | undefined | undefined | The earliest date that can be selected. Days before it are visible in the panel but disabled. |
max | Date | undefined | undefined | The latest date that can be selected. Days after it are visible in the panel but disabled. |
disabledDates | readonly Date[] | ((date: Date) => boolean) | undefined | undefined | Forbidden dates: a list or a predicate. |
presets | readonly GrDatePreset[] | undefined | undefined | Ready-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. |
weekStart | IsoWeekday | undefined | undefined | First day of the week per ISO (1 is Monday). Not set — from the locale. |
showWeekNumbers | boolean | undefined | undefined | A column of ISO 8601 week numbers in the panel. Not set — the `GrCalendar` setting from `GrConfigProvider`, otherwise `false`. |
today | Date | undefined | undefined | What counts as today. Set for the sake of reproducible tests and snapshots. |
locale | string | undefined | undefined | Display locale. Not set — from the application's i18n adapter. |
format | Intl.DateTimeFormatOptions | undefined | undefined | How 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. |
placeholder | string | undefined | undefined | Hint in an empty field. If it is not set and the field is editable (`editable`), the order of the date parts from the locale is shown. |
editable | boolean | undefined | false | The date can be typed by hand. The order of the parts and the separator come from the locale, so a custom pattern is not needed — and not allowed: `format` sets the display format, while parsing follows the locale. In `month` and `year` modes typing is not enabled: entering "August 2026" as text means parsing month names, which is a different task. |
applyOnBlur | boolean | undefined | true | The parsed text is emitted on blur, not only on `Enter`. |
clearable | boolean | undefined | undefined | A clear button in the field: it removes the value and emits `clear`. Not set — from `GrConfigProvider`, otherwise `false`. |
openv-model | boolean | undefined | undefined | Controlled state of the panel (`v-model:open`). |
inline | boolean | undefined | false | The panel is rendered in place: no field, no popover. The model, the adapter and `name` stay the picker's — that is how `inline` differs from a bare `GrCalendar`, which speaks in tuples. |
placement | Placement | undefined | undefined | The side of the field at which the panel opens. Not set — from `GrConfigProvider`, otherwise `bottom-start`. |
teleportTo | string | HTMLElement | undefined | undefined | A pinpoint override of the panel's mount point. |
size | "xs" | "sm" | "md" | "lg" | undefined | undefined | The height and type size of the field on the control scale. `md` by default; taken from `GrConfigProvider`. |
id | string | undefined | undefined | The field's own `id`. Not set — taken from `GrFormField`. |
name | string | undefined | undefined | Name for the native form: the serialized value is submitted as a hidden field. |
disabled | boolean | undefined | false | The field is unavailable: the panel does not open, the value does not change. Inherited from `GrFormField`. |
readonly | boolean | undefined | false | The value is visible, the panel opens, but the selection does not change. |
invalid | boolean | undefined | false | The visual and ARIA state of an error (`aria-invalid`). Inherited from `GrFormField`. |
required | boolean | undefined | false | A required field (`aria-required`). Inherited from `GrFormField`. |
loading | boolean | undefined | false | Background work: a spinner in the field and `aria-busy`. |
ariaLabel | string | undefined | undefined | The accessible name of the field when there is no visible label. Inside `GrFormField` the name comes from its `label`. |
Slots
| Slot | Type | Description |
|---|---|---|
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 |
| 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
| Event | Signature | Description |
|---|---|---|
update:modelValue | (e: "update:modelValue", value: TValue | readonly TValue[] | null): void | The new value (`v-model`); `null` — the value was removed. |
change | (e: "change", value: TValue | readonly TValue[] | null): void | The value was committed: by a selection in the panel, by typing or by clearing. |
update:open | (e: "update:open", value: boolean): void | The panel opened or closed (`v-model:open`). |
clear | (e: "clear"): void | The value was removed with the clear button; only with `clearable`. |
focus | (e: "focus", event: FocusEvent): void | Focus entered the field. |
blur | (e: "blur", event: FocusEvent): void | Focus 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 type { GrDatePickerConfigurableProps, GrDatePickerSize, GrDatePickerProps } from '@feugene/granularity-chrono/components/GrDatePicker'Filedist/types/components/GrDatePicker/config.d.ts
const grDatePickerConfig: import("@feugene/granum/contract").GranumComponentDescriptor<"GrDatePicker">;Filedist/types/components/GrDatePicker/defaults.d.ts
interface GrDatePickerConfigurableProps {
size: GrDatePickerSize;
clearable: boolean;
placement: UseFloatingPlacement;
}Filedist/types/components/GrDatePicker/grDatePickerStyles.d.ts
type GrDatePickerSize = GrPickerSize;Filedist/types/components/GrDatePicker/safelist.d.ts
const grDatePickerSafelist: string[];Filedist/types/components/GrDatePicker/GrDatePicker.vue.d.ts
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);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;
}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:
<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.
<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.
Props in this example