GrDateTimePicker
Берут, когда момент, а не день.
Обзор
Когда брать
- момент, а не день — начало встречи, дедлайн, время публикации: дата без времени здесь означала бы полночь, чего пользователь не имел в виду;
- выбор из нескольких шагов — день, час, минута;
autoApply: falseдаёт подтверждение, чтобы значение не уезжало после первого же клика; - нужны секунды —
enableSecondsдобавляет третью колонку; - на бэкенд уходит строка —
valueAdapter="isoDateTime"отдаёт'2026-08-12T14:30:00'без смещения: это то, что человек видит на часах.
Когда взять другое
| Нужно | Берите |
|---|---|
| Только дата | GrDatePicker |
| Только время | GrTimePicker |
| Период «с — по» | GrDateRangePicker |
| Сетка месяца без поля | GrCalendar |
| Показать момент, а не выбрать | GrRelativeTime |
Период с временем на границах этот пакет не закрывает: GrDateRangePicker
работает с датами. Собирается из двух GrDateTimePicker с проверкой порядка на
стороне формы.
Примеры 3 у каждого — живое демо и его код
Отложенное письмо с напоминанием
Одно значение держит и день, и время — два отдельных поля допустили бы день без времени.
minute-stepставит время на четверти часа,min— это момент, «сейчас», аvalue-adapter="isoDateTime"отдаёт API локальную ISO-строку, которую он хранит;nameотправляет её с нативной формой.Schedule the reminder Sends Wednesday 7 October at 09:00 · Northwind Traders, INV-0418Переход, подтверждаемый кнопкой
auto-apply="false"держит черновик до подтверждения: момент собирается из дня, часов, минут и секунд, и применить его после первого клика значило бы назначить полувыбранный ответ. Слотfooterназывает ответы, аenable-secondsздесь потому, что переход на новый прайс действительно идёт с точностью до секунды.Price list 2027 · cutoverScheduled Визит в границах моментов, а не дней
minиmaxрежут моменты, а не дни: 8 утра завтра можно, а 7:30 нельзя, хотя день тот же. Выходные —disabled-dates,use-12-hoursследует привычке американского читателя, аformatрешает, как выбранный момент читается в поле.Book a home visitBayview Clinic · Dr. Grace Wu Weekdays, 8 AM – 6 PM
API порождается из пакета · разойтись не может
Props
| Prop | Type | по умолчанию | Описание |
|---|---|---|---|
modelValuev-model | TValue | undefined | undefined | Дата со временем (`v-model`). Тип задаёт `valueAdapter`; `null` — ничего не выбрано. |
valueAdapter |
| undefined | Как значение уходит наружу и приходит обратно: имя готового адаптера (`date`, `isoDate`, `isoDateTime`, `timestamp`) либо свой. |
min | Date | undefined | undefined | Нижняя граница: дата — сеткой, время — колонками. |
max | Date | undefined | undefined | Верхняя граница. |
disabledDates | readonly Date[] | ((date: Date) => boolean) | undefined | undefined | Запрещённые даты: список или предикат. |
weekStart | IsoWeekday | undefined | undefined | Первый день недели по ISO (1 — понедельник). Не задан — из локали. |
showWeekNumbers | boolean | undefined | undefined | Колонка номеров недель по ISO 8601 в панели. Не задан — как у `GrCalendar` в `GrConfigProvider`, иначе `false`. |
minuteStep | number | undefined | 1 | Шаг колонки минут в минутах. По умолчанию `1`. |
secondStep | number | undefined | 1 | Шаг колонки секунд в секундах. По умолчанию `1`; осмыслен с `enableSeconds`. |
enableSeconds | boolean | undefined | false | Колонка секунд в панели и секунды в поле. По умолчанию выключена. |
use12Hours | boolean | undefined | undefined | 12-часовой вид колонок. Не задан — из локали через `Intl`. |
autoApply | boolean | undefined | true | Каждый шаг уходит наружу сразу. `false` — панель правит черновик, а модель меняется кнопкой подтверждения. |
today | Date | undefined | undefined | Что считать сегодняшним днём. Задаётся ради воспроизводимых тестов. |
format | Intl.DateTimeFormatOptions | undefined | undefined | Вид значения в поле — опциями `Intl`, а не строкой-паттерном. |
placeholder | string | undefined | undefined | Подсказка в пустом поле. Не задана, а поле редактируемое (`editable`) — показывается порядок частей даты из локали. |
editable | boolean | undefined | false | Значение можно набрать руками. Разбор идёт от `Intl`: порядок частей и разделители берутся у локали, а не у строки-паттерна. |
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`. |
locale | string | undefined | undefined | Локаль показа. Не задана — из адаптера i18n приложения. |
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 | { apply: () => void; cancel: () => void; } | Подвал панели вместо кнопок подтверждения. |
События
| Event | Сигнатура | Описание |
|---|---|---|
update:modelValue | (e: "update:modelValue", value: TValue | null): void | Новое значение (`v-model`); `null` — значение снято. |
change | (e: "change", value: 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 { GrDateTimePickerConfigurableProps, GrDateTimePickerSize, GrDateTimePickerProps } from '@feugene/granularity-chrono/components/GrDateTimePicker'Файлdist/types/components/GrDateTimePicker/config.d.ts
Панель собрана из чужой разметки целиком: сетка — GrCalendar, колонки
времени — внутренний компонент GrTimePicker, кнопки подтверждения —
GrButton ядра. Все три ребра объявлены: granum подмешивает safelist и CSS
только тем компонентам, что попали в селекцию, и без графа потребитель,
выбравший один GrDateTimePicker, получил бы панель без стилей.
const grDateTimePickerConfig: import("@feugene/granum/contract").GranumComponentDescriptor<"GrDateTimePicker">;Файлdist/types/components/GrDateTimePicker/defaults.d.ts
Пропы GrDateTimePicker, настраиваемые глобально через
<GrConfigProvider :component-defaults="{ GrDateTimePicker: { … } }">.
interface GrDateTimePickerConfigurableProps {
size: GrDateTimePickerSize;
clearable: boolean;
placement: UseFloatingPlacement;
}Файлdist/types/components/GrDateTimePicker/grDateTimePickerStyles.d.ts
type GrDateTimePickerSize = GrPickerSize;Файлdist/types/components/GrDateTimePicker/safelist.d.ts
const grDateTimePickerSafelist: string[];Файлdist/types/components/GrDateTimePicker/GrDateTimePicker.vue.d.ts
GrDateTimePicker — дата и время в одной панели.
Здесь впервые появляется autoApply: выбор становится многошаговым — день,
час, минута, — и «применить» перестаёт быть тавтологией. При autoApply
каждый шаг уходит наружу сразу, иначе панель правит черновик, а модель
меняется только кнопкой.
У GrDatePicker такого пропа нет намеренно: выбор даты атомарен, и
подтверждать там нечего.
interface GrDateTimePickerProps<T = Date | null> {
/** Дата со временем (`v-model`). Тип задаёт `valueAdapter`; `null` — ничего не выбрано. */
modelValue?: T;
/**
* Как значение уходит наружу и приходит обратно: имя готового адаптера
* (`date`, `isoDate`, `isoDateTime`, `timestamp`) либо свой.
*/
valueAdapter?: GrChronoAdapterName | GrChronoAdapter<T>;
/** Нижняя граница: дата — сеткой, время — колонками. */
min?: Date;
/** Верхняя граница. */
max?: Date;
/** Запрещённые даты: список или предикат. */
disabledDates?: readonly Date[] | ((date: Date) => boolean);interface GrDateTimePickerEmits<T = Date | null> {
/** Новое значение (`v-model`); `null` — значение снято. */
(e: 'update:modelValue', value: T | null): void;
/** Значение зафиксировано: выбором в панели, вводом или очисткой. */
(e: 'change', value: 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;
}Использование из прозы библиотеки
autoApply здесь значит не то же, что у даты
У выбора одной даты клик по дню — это и есть готовое значение, и подтверждать
нечего. Здесь значение собирается минимум из двух частей, поэтому «применить»
означает «я закончил», а не повторяет уже сделанное. Дефолт — true: чаще
достаточно даты плюс часа, и лишняя кнопка на каждом выборе раздражает.
Ручной ввод
editable включает набор с клавиатуры: одна строка задаёт и дату, и время.
<GrDateTimePicker v-model="startsAt" editable />Разбор идёт от Intl: где в строке дата, а где время, какой у частей порядок и чем они
разделены, знает локаль, а не паттерн. Поэтому набирать можно как удобно — 8/14/2026 10:15,
8/14/2026, 10:15, 08.14.2026 10:15: разделители разбору безразличны, он смотрит на группы цифр.
Локали, в которых время идёт перед датой (vi — 15:30 12/8/26), разбираются так же.
Набранная дата без времени время не сбрасывает — остаётся то, что стоит в модели; при пустой модели это полночь. Не набранное просто не меняется.
Текст подтверждает сам себя, минуя autoApply. Проп управляет панелью, где выбор многошаговый
и «применить» значит «я закончил». Enter в поле — это уже законченное действие, и уход в черновик
выглядел бы как «ничего не произошло»: поле-то показывает модель.
Панель идёт за набором. Как только дата набрана целиком, она подсвечена в сетке, и сетка
перешла на её месяц; набранный час подсвечен в колонке часов, минуты — в минутах. Модель не
меняется: её меняет Enter или уход фокуса. Набирать вслепую, глядя на панель с прежним значением,
— это ровно та ошибка, которую поле должно было убрать.
Запрещённое значение текстом не принимается — ни дата из disabledDates, ни момент за min или
max. Правило одно на клик и на Enter, иначе поле обходило бы ограничения панели.
Неразобранный текст на уходе фокуса откатывается к модели; apply-on-blur="false" оставляет
подтверждение только за Enter. Плейсхолдер по умолчанию — подсказка формата (MM/DD/YYYY, HH:MM
буквами локали), потому что порядок частей у локалей разный.
С editable поле показывает значение цифрами, а не Aug 12, 2026, 09:30: набор обязан читать
обратно то, что показан, иначе правка числа прямо в поле оставила бы разбору две группы цифр вместо
трёх и молча откатилась бы. Свой format это правило перебивает — тогда за читаемость отвечает
потребитель.
Маски у поля нет намеренно: она угадывала бы границу между датой и временем и мешала бы стирать.
Разбор работает по цифрам 0–9. Локали с собственными цифрами (ar-EG, fa-IR) набором пока не
поддерживаются — показ у них правильный, а ввод нужно вести европейскими цифрами.
Пропы этого примера