GrDateTimePicker

  • chrono
  • Дата и время
  • API · 32 props

Берут, когда момент, а не день.

import { GrDateTimePicker } from '@feugene/granularity-chrono/components/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

PropTypeпо умолчаниюОписание
modelValuev-modelTValue | undefinedundefinedДата со временем (`v-model`). Тип задаёт `valueAdapter`; `null` — ничего не выбрано.
valueAdapter
"date" | "isoDate" | "isoDateTime" | "timestamp" | GrChronoA"date" | "isoDate" | "isoDateTime" | "timestamp" | GrChronoAdapter<TValue> | undefined
undefinedКак значение уходит наружу и приходит обратно: имя готового адаптера (`date`, `isoDate`, `isoDateTime`, `timestamp`) либо свой.
minDate | undefinedundefinedНижняя граница: дата — сеткой, время — колонками.
maxDate | undefinedundefinedВерхняя граница.
disabledDatesreadonly Date[] | ((date: Date) => boolean) | undefinedundefinedЗапрещённые даты: список или предикат.
weekStartIsoWeekday | undefinedundefinedПервый день недели по ISO (1 — понедельник). Не задан — из локали.
showWeekNumbersboolean | undefinedundefinedКолонка номеров недель по ISO 8601 в панели. Не задан — как у `GrCalendar` в `GrConfigProvider`, иначе `false`.
minuteStepnumber | undefined1Шаг колонки минут в минутах. По умолчанию `1`.
secondStepnumber | undefined1Шаг колонки секунд в секундах. По умолчанию `1`; осмыслен с `enableSeconds`.
enableSecondsboolean | undefinedfalseКолонка секунд в панели и секунды в поле. По умолчанию выключена.
use12Hoursboolean | undefinedundefined12-часовой вид колонок. Не задан — из локали через `Intl`.
autoApplyboolean | undefinedtrueКаждый шаг уходит наружу сразу. `false` — панель правит черновик, а модель меняется кнопкой подтверждения.
todayDate | undefinedundefinedЧто считать сегодняшним днём. Задаётся ради воспроизводимых тестов.
formatIntl.DateTimeFormatOptions | undefinedundefinedВид значения в поле — опциями `Intl`, а не строкой-паттерном.

Слоты

SlotTypeОписание
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

grDateTimePickerConfigconst

Панель собрана из чужой разметки целиком: сетка — GrCalendar, колонки времени — внутренний компонент GrTimePicker, кнопки подтверждения — GrButton ядра. Все три ребра объявлены: granum подмешивает safelist и CSS только тем компонентам, что попали в селекцию, и без графа потребитель, выбравший один GrDateTimePicker, получил бы панель без стилей.

const grDateTimePickerConfig: import("@feugene/granum/contract").GranumComponentDescriptor<"GrDateTimePicker">;

Файлdist/types/components/GrDateTimePicker/defaults.d.ts

GrDateTimePickerConfigurablePropsinterface

Пропы GrDateTimePicker, настраиваемые глобально через <GrConfigProvider :component-defaults="{ GrDateTimePicker: { … } }">.

interface GrDateTimePickerConfigurableProps {
    size: GrDateTimePickerSize;
    clearable: boolean;
    placement: UseFloatingPlacement;
}

Файлdist/types/components/GrDateTimePicker/grDateTimePickerStyles.d.ts

GrDateTimePickerSizetype
type GrDateTimePickerSize = GrPickerSize;

Файлdist/types/components/GrDateTimePicker/safelist.d.ts

grDateTimePickerSafelistconst
const grDateTimePickerSafelist: string[];

Файлdist/types/components/GrDateTimePicker/GrDateTimePicker.vue.d.ts

GrDateTimePickerPropsinterface

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);
GrDateTimePickerEmitsinterface
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 включает набор с клавиатуры: одна строка задаёт и дату, и время.

vue
<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) набором пока не поддерживаются — показ у них правильный, а ввод нужно вести европейскими цифрами.