GrDatePicker
Берут, когда дата — значение поля формы.
Обзор
Когда брать
- дата — значение поля формы — контракт форм-контрола ядра работает целиком:
ошибка, обязательность и подпись приезжают из
GrFormFieldбез единой правки; - дату можно и набрать, и выбрать —
editableвключает ввод с клавиатуры, разбор идёт по локали, а не по одному жёсткому шаблону; - часть дат недоступна —
disabledDatesпринимает и список, и предикат: выходные, занятые слоты, «не раньше сегодня» черезmin/max; - на бэкенд уходит не
Date—valueAdapterменяет тип модели на строку ISO, timestamp или свой формат, и это видно в типах.
Когда взять другое
| Нужно | Берите |
|---|---|
| Период «с — по» | GrDateRangePicker |
| Дата вместе со временем | GrDateTimePicker |
| Только время | GrTimePicker |
| Сетка месяца сама по себе, без поля | GrCalendar |
| Показать момент, а не выбрать его | GrRelativeTime |
| Выбирают только из готовых значений, произвольная дата не нужна | GrSelect |
Примеры 4 у каждого — живое демо и его код
Дата выставления и срок оплаты счёта
Пресеты бывают функциями: «Net 30» считается от даты выставления в момент нажатия, а не написания кода.
minдержит срок оплаты не раньше выставления, выходные —disabled-dates, аeditableпозволяет набрать дату руками.value-adapter="isoDate"делает моделью строку «2026-11-05», которую хранит API, аnameотправляет её с нативной формой.Налоговый период: месяц или квартал
modeменяет то, что выбирается, а не то, как хранится: панель показывает месяцы или кварталы, а модель остаётсяDate— первым днём периода.maxне пускает в будущие периоды: декларацию подают за прошедшее.VAT return1 Jul – 30 Sept · due 10 NovВыходные курьера набором
multipleделает модель массивом — отдельные даты, а не диапазон.separatorиformatрешают, как набор читается в поле,show-week-numbersсверяет с расписанием курьера, аmin/maxограничивают квартал.Courier days off · Q4Orders placed the day before ship one day later 4 days off in the quarterЗапись на звонок с панелью на странице
inlineрисует панель на месте — без поля и поповера, с той же моделью. Слотdayдобавляет под числом точку на каждый свободный слот, а дни без слотов —disabled-dates, их пропускает и клавиатура.октябрь 2026 г.пн вт ср чт пт сб вс 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 порождается из пакета · разойтись не может
Props
| Prop | Type | по умолчанию | Описание |
|---|---|---|---|
mode | "day" | "week" | "month" | "quarter" | "year" | undefined | "day" | Что выбирается: день, месяц или год. В режимах периода панель показывает двенадцать ячеек, а значением становится первое число периода. |
modelValuev-model | TValue | readonly TValue[] | undefined | undefined | Значение. При `multiple` — массив: набор, а не диапазон. Точнее объединения в генерик-SFC не выразить: условный тип от булева пропа Vue выводит ненадёжно, а обещание, которое иногда не работает, хуже честного `T | readonly T[]`. Рантайм-контракт при этом точный — наружу уходит ровно то, что объявлено пропом. |
multiple | boolean | undefined | false | Набор дат вместо одной: расписание, даты-исключения, брони. Отличие от `GrDateRangePicker` — в существе: там непрерывный отрезок с двумя краями, здесь произвольное множество. Панель после выбора не закрывается, а клик по выбранной дате её снимает. |
separator | string | undefined | ", " | Чем разделять даты набора в поле. |
valueAdapter |
| undefined | Как значение уходит наружу и приходит обратно: имя готового адаптера (`date`, `isoDate`, `isoDateTime`, `timestamp`) либо свой. Отдельным пропом, а не подменой типа модели: у предшественника фактический тип задавала строка, и вывести его из типов было невозможно. |
min | Date | undefined | undefined | Самая ранняя дата, которую можно выбрать. Дни раньше неё в панели видны, но выключены. |
max | Date | undefined | undefined | Самая поздняя дата, которую можно выбрать. Дни позже неё в панели видны, но выключены. |
disabledDates | readonly Date[] | ((date: Date) => boolean) | undefined | undefined | Запрещённые даты: список или предикат. |
presets | readonly GrDatePreset[] | undefined | undefined | Готовые даты в подвале панели: «Сегодня», «Завтра». Дату можно задать функцией — «сегодня» считается в момент показа, а не в момент объявления пропа. Функция обязана быть чистой: её зовут и чтобы решить, доступен ли шорткат, и чтобы применить его. Дата вне `min`/`max` или из `disabledDates` приходит выключенной — как и её ячейка в сетке. |
weekStart | IsoWeekday | undefined | undefined | Первый день недели по ISO (1 — понедельник). Не задан — из локали. |
showWeekNumbers | boolean | undefined | undefined | Колонка номеров недель по ISO 8601 в панели. Не задан — как у `GrCalendar` в `GrConfigProvider`, иначе `false`. |
today | Date | undefined | undefined | Что считать сегодняшним днём. Задаётся ради воспроизводимых тестов и снимков. |
locale | string | undefined | undefined | Локаль показа. Не задана — из адаптера i18n приложения. |
format | Intl.DateTimeFormatOptions | undefined | undefined | Вид значения в поле — опциями `Intl`, а не строкой-паттерном: паттерн пришлось бы разбирать самим и он всё равно не знает порядка частей в чужой локали. |
placeholder | string | undefined | undefined | Подсказка в пустом поле. Не задана, а поле редактируемое (`editable`) — показывается порядок частей даты из локали. |
editable | boolean | undefined | false | Дату можно набрать руками. Порядок частей и разделитель берутся из локали, поэтому свой паттерн задавать не нужно — и нельзя: формат показа задаёт `format`, а разбор идёт по локали. В режимах `month` и `year` ввод не включается: набирать «август 2026» текстом — это разбор названий месяцев, то есть другая задача. |
applyOnBlur | boolean | undefined | true | Разобранный текст уходит наружу на уходе фокуса, а не только по `Enter`. |
clearable | boolean | undefined | undefined | Кнопка очистки в поле: снимает значение и шлёт `clear`. Не задан — из `GrConfigProvider`, иначе `false`. |
openv-model | boolean | undefined | undefined | Контролируемое состояние панели (`v-model:open`). |
inline | boolean | undefined | false | Панель рисуется на месте: ни поля, ни поповера. Модель, адаптер и `name` остаются пикеровскими — этим `inline` и отличается от голого `GrCalendar`, который говорит кортежами. |
placement | Placement | undefined | undefined | Сторона поля, у которой раскрывается панель. Не задан — из `GrConfigProvider`, иначе `bottom-start`. |
teleportTo | string | HTMLElement | undefined | undefined | Точечное переопределение точки монтирования панели. |
size | "xs" | "sm" | "md" | "lg" | undefined | undefined | Высота и кегль поля по шкале контролов. По умолчанию `md`; берётся из `GrConfigProvider`. |
id | string | undefined | undefined | Собственный `id` поля. Не задан — берётся из `GrFormField`. |
name | string | undefined | undefined | Имя для нативной формы: сериализованное значение уходит скрытым полем. |
disabled | boolean | undefined | false | Поле недоступно: панель не открывается, значение не меняется. Наследуется от `GrFormField`. |
readonly | boolean | undefined | false | Значение видно, панель открывается, но выбор не меняется. |
invalid | boolean | undefined | false | Визуальное и ARIA-состояние ошибки (`aria-invalid`). Наследуется от `GrFormField`. |
required | boolean | undefined | false | Обязательное поле (`aria-required`). Наследуется от `GrFormField`. |
loading | boolean | undefined | false | Фоновая работа: спиннер в поле и `aria-busy`. |
ariaLabel | string | undefined | undefined | Доступное имя поля, когда видимой подписи нет. Внутри `GrFormField` имя даёт его `label`. |
Слоты
| Slot | Type | Описание |
|---|---|---|
day | { cell: CalendarCell; selected: boolean; } | Своя ячейка дня вместо числа. |
header | { title: string; goToPeriod: (delta: number) => void; } | Своя шапка панели вместо заголовка и стрелок. |
weekday | { label: string; full: string; isoWeekday: IsoWeekday; } | Своя ячейка шапки недели вместо сокращённого названия дня. |
footer |
| Подвал панели. Выбор отдаётся внутрь: запрет даты складывается из `min`, `max` и `disabledDates`, а `readonly` запрещает выбор целиком — снаружи этих правил не видно. |
События
| Event | Сигнатура | Описание |
|---|---|---|
update:modelValue | (e: "update:modelValue", value: TValue | readonly TValue[] | null): void | Новое значение (`v-model`); `null` — значение снято. |
change | (e: "change", value: TValue | readonly TValue[] | null): void | Значение зафиксировано: выбором в панели, вводом или очисткой. |
update:open | (e: "update:open", value: boolean): void | Панель открылась или закрылась (`v-model:open`). |
clear | (e: "clear"): void | Значение снято кнопкой очистки; только при `clearable`. |
focus | (e: "focus", event: FocusEvent): void | Фокус вошёл в поле. |
blur | (e: "blur", event: FocusEvent): void | Фокус ушёл из поля. |
Типы
Все экспортируемые типы подпути, по файлам, где они объявлены. Имена, дженерики и тела читаются из файлов деклараций пакета и разойтись не могут.
import type { GrDatePickerConfigurableProps, GrDatePickerSize, GrDatePickerProps } from '@feugene/granularity-chrono/components/GrDatePicker'Файлdist/types/components/GrDatePicker/config.d.ts
Пикер рендерит сетку своего пакета и поповер ядра — оба ребра объявлены.
Без второго потребитель, выбравший только GrDatePicker, получил бы панель
без фона, рамки и тени: granum подмешивает safelist и CSS лишь тем
компонентам, что попали в селекцию, а GrPopover попадает в неё только через
этот граф.
const grDatePickerConfig: import("@feugene/granum/contract").GranumComponentDescriptor<"GrDatePicker">;Файлdist/types/components/GrDatePicker/defaults.d.ts
Пропы GrDatePicker, настраиваемые глобально через
<GrConfigProvider :component-defaults="{ GrDatePicker: { … } }">.
Только оформление и поведение оболочки: границы дат задаются на месте.
interface GrDatePickerConfigurableProps {
size: GrDatePickerSize;
clearable: boolean;
placement: UseFloatingPlacement;
}Файлdist/types/components/GrDatePicker/grDatePickerStyles.d.ts
Публичное имя типа размера компонента. Сами классы поля общие для всех
пикеров пакета и живут в internal/pickerFieldStyles.
type GrDatePickerSize = GrPickerSize;Файлdist/types/components/GrDatePicker/safelist.d.ts
const grDatePickerSafelist: string[];Файлdist/types/components/GrDatePicker/GrDatePicker.vue.d.ts
GrDatePicker — поле с датой и панелью-календарём.
Границей между мирами служит именно этот компонент: наружу он говорит на
Date (или на том, что задаёт valueAdapter), внутрь — кортежами
PlainDate, на которых считает весь пакет.
Пока editable не включён, поле честно помечено readonly: значение
меняется только через панель.
interface GrDatePickerProps<T = Date | null> {
/**
* Что выбирается: день, месяц или год. В режимах периода панель показывает
* двенадцать ячеек, а значением становится первое число периода.
*/
mode?: 'day' | 'week' | 'month' | 'quarter' | 'year';
/**
* Значение. При `multiple` — массив: набор, а не диапазон.
*
* Точнее объединения в генерик-SFC не выразить: условный тип от булева пропа
* Vue выводит ненадёжно, а обещание, которое иногда не работает, хуже
* честного `T | readonly T[]`. Рантайм-контракт при этом точный — наружу
* уходит ровно то, что объявлено пропом.
*/
modelValue?: T | readonly T[];
/**
* Набор дат вместо одной: расписание, даты-исключения, брони.
*
* Отличие от `GrDateRangePicker` — в существе: там непрерывный отрезок с
* двумя краями, здесь произвольное множество. Панель после выбора не
* закрывается, а клик по выбранной дате её снимает.
*/
multiple?: boolean;
/** Чем разделять даты набора в поле. */
separator?: string;
/**
* Как значение уходит наружу и приходит обратно: имя готового адаптера
* (`date`, `isoDate`, `isoDateTime`, `timestamp`) либо свой.
*
* Отдельным пропом, а не подменой типа модели: у предшественника фактический
* тип задавала строка, и вывести его из типов было невозможно.
*/
valueAdapter?: GrChronoAdapterName | GrChronoAdapter<T>;
/** Самая ранняя дата, которую можно выбрать. Дни раньше неё в панели видны, но выключены. */
min?: Date;
/** Самая поздняя дата, которую можно выбрать. Дни позже неё в панели видны, но выключены. */
max?: Date;
/** Запрещённые даты: список или предикат. */
disabledDates?: readonly Date[] | ((date: Date) => boolean);interface GrDatePickerEmits<T = Date | null> {
/** Новое значение (`v-model`); `null` — значение снято. */
(e: 'update:modelValue', value: T | readonly T[] | null): void;
/** Значение зафиксировано: выбором в панели, вводом или очисткой. */
(e: 'change', value: T | readonly T[] | null): void;
/** Панель открылась или закрылась (`v-model:open`). */
(e: 'update:open', value: boolean): void;
/** Значение снято кнопкой очистки; только при `clearable`. */
(e: 'clear'): void;
/** Фокус вошёл в поле. */
(e: 'focus', event: FocusEvent): void;
/** Фокус ушёл из поля. */
(e: 'blur', event: FocusEvent): void;
}Готовая дата в подвале панели.
interface GrDatePreset {
label: string;
/** Дата или функция, считающая её в момент показа и нажатия. */
date: Date | (() => Date);Использование из прозы библиотеки
Готовые даты живут внутри панели
presets рисует ряд шорткатов в подвале: «Сегодня», «Завтра», «Понедельник».
Дату можно задать функцией — «сегодня» считается в момент показа, а не в момент
объявления пропа.
Шорткат идёт мимо сетки, поэтому проверяет запреты сам: дата вне min/max или
из disabledDates приходит выключенной — ровно как её ячейка в сетке. Кнопка,
которая ничего не делает, обманывает.
Свой подвал — слот footer; он заменяет ряд целиком и получает select,
canSelect и close.
Значение — локальная полночь, а не момент
Дата без времени собирается как локальная полночь: пользователь выбирает день в своём календаре, а не точку на мировой линии. Кому нужен именно момент — тот сериализует его своим адаптером; молча подставлять UTC пакет не станет, потому что «12 августа» в двух часовых поясах это два разных момента и один и тот же день.
Не только дни: неделя, месяц, квартал, год
mode пробрасывается в календарь как есть, и поле показывает выбранное обычным форматом даты:
<GrDatePicker v-model="reportPeriod" mode="quarter" />Неделя кладёт в модель своё начало, квартал и месяц — первое число, год — первое января. Форма
значения от режима не зависит: в поле по-прежнему одна дата, и valueAdapter работает как работал.
Разбор режимов — ./GrCalendar.md; там же о том, почему неделя рисуется сеткой
дней, а не сеткой периодов, и почему номер недели не показывается.
Набор дат — не диапазон
multiple собирает произвольное множество: расписание занятий, даты-исключения в графике,
забронированные дни. Модель становится массивом.
<GrDatePicker v-model="lessons" multiple />Отличие от GrDateRangePicker — в существе, а не в удобстве: там
непрерывный отрезок с двумя краями и правилами длины, здесь — множество, где соседство ничего не
значит.
Клик по выбранной дате её снимает. Набор — переключатель, а не накопитель: иначе снять ошибочно взятую дату было бы нечем.
Панель не закрывается после выбора. Набор набирают, а одиночную дату выбирают однажды — и закрытие после первого клика превратило бы набор из десяти дат в десять открытий панели.
Порядок всегда по возрастанию, куда бы ни кликнули. Модель обязана быть сравнима: перестановка элементов не должна читаться как изменение — иначе «есть несохранённые правки» срабатывает на пустом месте.
Ручной ввод в этом режиме выключен, даже при editable. Строка, описывающая N дат, требует
своего разбора: сколько их набрано, из строки не следует, а неполный ввод не отличить от короткого
набора. Диапазон и дата со временем описывают ровно два значения, и потому набираются
(GrDateRangePicker, GrDateTimePicker); произвольный набор — нет.
В поле видны первые три даты и остаток числом («и ещё 2»): без потолка подпись переполняется уже на
пятой. В форму уходит по скрытому полю на дату — с одним именем, как читает FormData.getAll.
min, max и disabledDates действуют на каждую дату по отдельности, теми же правилами, что и при
одиночном выборе.
applyOnBlur включён
Выбор в календаре применяется сразу, а уход фокуса из поля фиксирует набранное руками. Иначе набранная, но не подтверждённая дата исчезала бы при переходе к следующему полю — а пользователь считает, что он её ввёл.
Редактируемое поле показывает то, что принимает обратно
С editable значение выводится цифрами (08/12/2026), а не Aug 12, 2026.
Иначе правка числа прямо в поле оставляла бы разбору две группы цифр вместо
трёх: набранное молча откатывалось бы, хотя пользователь ничего не нарушил.
Свой format это правило перебивает — тогда за читаемость набранного отвечает
потребитель. Без editable показ остаётся прежним.
Запрещённая дата не принимается и текстом. disabledDates, min и max
действуют одинаково на клик и на Enter; поле, обходящее ограничения панели,
означало бы, что ограничений нет.
Пропы этого примера