GrDateTimePicker

Пакет: @feugene/granularity-chronoспутникГруппа: Прочее

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

Когда брать

  • момент, а не день — начало встречи, дедлайн, время публикации: дата без времени здесь означала бы полночь, чего пользователь не имел в виду;
  • выбор из нескольких шагов — день, час, минута; autoApply: false даёт подтверждение, чтобы значение не уезжало после первого же клика;
  • нужны секундыenableSeconds добавляет третью колонку;
  • на бэкенд уходит строкаvalueAdapter="isoDateTime" отдаёт '2026-08-12T14:30:00' без смещения: это то, что человек видит на часах.

Когда взять другое

НужноБерите
Только датаGrDatePicker
Только времяGrTimePicker
Период «с — по»GrDateRangePicker
Сетка месяца без поляGrCalendar
Показать момент, а не выбратьGrRelativeTime

Период с временем на границах этот пакет не закрывает: GrDateRangePicker работает с датами. Собирается из двух GrDateTimePicker с проверкой порядка на стороне формы.

`autoApply` здесь значит не то же, что у даты

У выбора одной даты клик по дню — это и есть готовое значение, и подтверждать нечего. Здесь значение собирается минимум из двух частей, поэтому «применить» означает «я закончил», а не повторяет уже сделанное. Дефолт — true: чаще достаточно даты плюс часа, и лишняя кнопка на каждом выборе раздражает.

Ручной ввод

editable включает набор с клавиатуры: одна строка задаёт и дату, и время.

<GrDateTimePicker v-model="startsAt" editable />

Разбор идёт от Intl: где в строке дата, а где время, какой у частей порядок и чем они разделены, знает локаль, а не паттерн. Поэтому набирать можно как удобно — 8/14/2026 10:15, 8/14/2026, 10:15, 08.14.2026 10:15: разделители разбору безразличны, он смотрит на группы цифр. Локали, в которых время идёт перед датой (vi15: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 это правило перебивает — тогда за читаемость отвечает потребитель.

Маски у поля нет намеренно: она угадывала бы границу между датой и временем и мешала бы стирать.

Разбор работает по цифрам 09. Локали с собственными цифрами (ar-EG, fa-IR) набором пока не поддерживаются — показ у них правильный, а ввод нужно вести европейскими цифрами.

Установка

npm i @feugene/granularity-chrono

Импорт

import { GrDateTimePicker } from '@feugene/granularity-chrono/components/GrDateTimePicker'

API

API этого компонента ещё не посчитан: генератор витрины пока обходит только ядро. Пока его нет, справочник — в документации пакета.

Примеры 3

Basic

Basic
<script setup lang="ts">
import { ref } from 'vue'

// `GrDateTimePicker` подставляется авто-импортом (`unplugin-vue-components`).
const value = ref<string | null>('2026-08-12T14:30:00')
</script>

<template>
  <div class="grid max-w-[360px] gap-4">
    <!-- Сетка и колонки в одной панели: смена дня сохраняет время, смена
         времени сохраняет день. -->
    <GrDateTimePicker
      v-model="value"
      value-adapter="isoDateTime"
      :minute-step="15"
      clearable
      placeholder="Pick date and time"
      aria-label="Meeting start"
    />

    <p class="showcase-demo-text text-sm">
      <span class="opacity-70">value=</span><code>{{ value ?? '—' }}</code>
    </p>
  </div>
</template>

Confirm

Confirm
<script setup lang="ts">
import { ref } from 'vue'

// `GrDateTimePicker` подставляется авто-импортом (`unplugin-vue-components`).
const value = ref<string | null>('2026-08-12T09:00:00')
const changes = ref(0)
</script>

<template>
  <div class="grid max-w-[360px] gap-4">
    <!-- `auto-apply="false"`: панель правит черновик, а модель меняется
         кнопкой. Счётчик показывает, что до подтверждения её никто не трогал. -->
    <GrDateTimePicker
      v-model="value"
      :auto-apply="false"
      value-adapter="isoDateTime"
      :minute-step="30"
      aria-label="Appointment"
      @change="changes += 1"
    />

    <p class="showcase-demo-text text-sm">
      <span class="opacity-70">value=</span><code>{{ value ?? '—' }}</code>
      <span class="opacity-70"> · changes=</span><code>{{ changes }}</code>
    </p>
  </div>
</template>

Editable

Editable
<script setup lang="ts">
import { ref } from 'vue'

// `GrDateTimePicker` подставляется авто-импортом.

/**
 * Момент, который быстрее набрать, чем найти кликами.
 *
 * Локаль здесь не для украшения: она задаёт и порядок частей, и разделители,
 * и то, чем разбор считает набранное. Два поля рядом показывают это без слов.
 */
const TODAY = new Date(2026, 7, 12)

const ru = ref<Date | null>(new Date(2026, 7, 12, 9, 30))
const us = ref<Date | null>(new Date(2026, 7, 12, 9, 30))
</script>

<template>
  <div class="grid gap-4 justify-items-start">
    <div class="grid gap-3 sm:grid-cols-2 w-full">
      <GrDateTimePicker
        v-model="ru"
        editable
        :today="TODAY"
        locale="ru-RU"
        aria-label="Начало, русская локаль"
      />

      <GrDateTimePicker
        v-model="us"
        editable
        :today="TODAY"
        locale="en-US"
        aria-label="Start, US locale"
      />
    </div>

    <p class="showcase-demo-text text-sm opacity-70">
      Наберите дату руками: слева ждут <strong>ДД.ММ.ГГГГ</strong>, справа — <strong>MM/DD/YYYY</strong>.
      Порядок частей не зашит в компонент, его знает локаль; разделители разбору безразличны —
      точка, слэш и пробел равноценны, потому что считаются группы цифр, а не символы между ними.
    </p>

    <p class="showcase-demo-text text-sm opacity-70">
      Панель идёт за набором: дата набрана целиком — она подсвечена, и сетка перешла на её месяц;
      набран час — подсвечен час, дописаны минуты — минуты. Модель при этом не меняется, её меняет
      <strong>Enter</strong> или уход фокуса. Набирать вслепую, глядя на панель с прежним значением,
      — ровно та ошибка, которую поле и должно было убрать.
    </p>

    <p class="showcase-demo-text text-sm opacity-70">
      Наберите одну дату без времени — время останется прежним: не набранное не меняется. Наберите
      мусор и уйдите фокусом — поле вернётся к значению. Поле показывает значение цифрами именно
      потому, что их можно править на месте: с «12 авг. 2026 г.» правка числа оставила бы разбору
      две группы цифр вместо трёх и молча откатилась бы.
    </p>
  </div>
</template>

Документация компонентаВсе компоненты