GrTimePicker

  • chrono
  • chrono
  • API · 28 props
import { GrTimePicker } from '@feugene/granularity-chrono/components/GrTimePicker'

Overview

When to take it

  • a time without a date — the start of a shift, opening hours, a reminder: the day here either does not matter or is set separately;
  • the time is more convenient to type — editable gives typing from the keyboard, and the parsing goes by the locale, the 12-hour form included where it is customary;
  • seconds are needed — enableSeconds adds a third column;
  • the step is not a minute — minuteStep builds a column with the required step, and “14:07” can no longer be entered where appointments go by half-hours.

When to take something else

NeedTake
A date together with a timeGrDateTimePicker
A date onlyGrDatePicker
A “from — to” period by datesGrDateRangePicker
Enter a duration rather than a moment on the clockGrNumberInput
Show a durationGrDuration
Several ready slots to choose fromGrSelect

A duration is not a time. “An hour and a half” is a number with a unit rather than a moment: the columns of hours and minutes lie about it, because 90 minutes cannot be assembled in them. It is entered as a number and shown with GrDuration.

Examples 3 each with a live demo and its code

  • Opening hours, in the API’s own strings

    The shop API keeps hours as '08:00', so a small value-adapter turns the string into a Date for the picker and back — the day never leaves the component. A 15-minute minute-step keeps the hours on the grid the map shows, editable lets a manager type «7:45», and min starts the closing column half an hour after opening.

    Opening hours · Kiosk AltonaShown on the map and in the app · local time, Europe/Berlin
    • Closed
    Open 63 h 30 min a week
    Sent to the shop API
    {
      "mon": ["08:00", "19:00"],
      "tue": ["08:00", "19:00"],
      "wed": ["08:00", "19:00"],
      "thu": ["08:00", "20:30"],
      "fri": ["08:00", "19:00"],
      "sat": ["09:00", "16:00"],
      "sun": null
    }
  • A callback in the next free slot

    The time is the only question on this step, so the list stays open (inline). min is the next free slot and max the last one: past and after-hours times are not offered at all. The footer gets select and canSelect, so «As soon as possible» goes through the same rules and is rounded up to the grid — 11:10 becomes 11:30.

    00
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    00
    30
    Call me backBilling support · today, Tuesday 13 October
    Number
    +49 40 1234 5678
    Topic
    Invoice INV-2041
    Time
    14:30 – 15:00
    Calls are taken until 17:30, Berlin time.
  • An incident timed to the second

    enable-seconds adds the third column and the seconds in the field; editable takes a time pasted from the logs. The value is an ISO string (value-adapter="isoDateTime") with the day from today, use-12-hours follows the clock switch, «Now» in the footer lands on the grid like any other choice, and GrDuration turns the steps into the figures the review asks for.

    INC-2041 · Payments webhooks delayedTimeline · today, 13 October
    Time to acknowledge
    Time to resolve
    Still open

API generated from the package · cannot drift

Props

PropTypedefaultDescription
modelValuev-modelTValue | undefinedundefinedThe time (`v-model`) — a `Date` whose time of day is what matters. The type is set by `valueAdapter`; `null` — nothing is selected.
valueAdapter
"date" | "isoDate" | "isoDateTime" | "timestamp" | GrChronoA"date" | "isoDate" | "isoDateTime" | "timestamp" | GrChronoAdapter<TValue> | undefined
undefinedHow the value goes outward and comes back: the name of a ready adapter (`date`, `isoDate`, `isoDateTime`, `timestamp`) or your own.
minDate | undefinedundefinedLower bound. Only the time of day is taken into account.
maxDate | undefinedundefinedUpper bound. Only the time of day is taken into account.
minuteStepnumber | undefined1Step of the minutes column, in minutes.
secondStepnumber | undefined1Step of the seconds column, in seconds.
enableSecondsboolean | undefinedfalseA seconds column in the panel and seconds in the field. Off by default.
use12Hoursboolean | undefinedundefined12-hour view. Unset — from the locale via `Intl`.
todayDate | undefinedundefinedThe date the chosen time is bound to while there is no value yet. Set for the sake of reproducible tests and snapshots.
formatIntl.DateTimeFormatOptions | undefinedundefinedThe look of the value in the field — through `Intl` options, not a pattern string.
placeholderstring | undefinedundefinedHint in an empty field.
editableboolean | undefinedfalseThe time can be typed by hand: `9:30`, `09:30:45`, `3:30 PM`. There is deliberately no mask here — time has a single separator, and there is nothing to fill in.
applyOnBlurboolean | undefinedtrueThe parsed text goes outward on blur, not only on `Enter`.
clearableboolean | undefinedundefinedA clear button in the field: it removes the value and emits `clear`. Not set — from `GrConfigProvider`, otherwise `false`.

Slots

SlotTypeDescription
footer
{ select: (date: Date) => boolean; canSelect: (date: Date) ={ select: (date: Date) => boolean; canSelect: (date: Date) => boolean; close: () => void; }
Panel footer: "now", "clear". The choice is handed inward, as with the date pickers: what is forbidden comes from `min`, `max` and the step, and `readonly` forbids the choice entirely — none of these rules are visible from the outside.

Events

EventSignatureDescription
update:modelValue(e: "update:modelValue", value: TValue | null): voidThe new value (`v-model`); `null` — the value was removed.
change(e: "change", value: TValue | null): voidThe value was committed: by a selection in the panel, by typing or by clearing.
update:open(e: "update:open", value: boolean): voidThe panel opened or closed (`v-model:open`).
clear(e: "clear"): voidThe value was removed with the clear button; only with `clearable`.
focus(e: "focus", event: FocusEvent): voidFocus entered the field.
blur(e: "blur", event: FocusEvent): voidFocus 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
import type { GrTimePickerConfigurableProps, GrTimePickerSize, GrTimePickerProps } from '@feugene/granularity-chrono/components/GrTimePicker'

Filedist/types/components/GrTimePicker/config.d.ts

grTimePickerConfigconst
const grTimePickerConfig: import("@feugene/granum/contract").GranumComponentDescriptor<"GrTimePicker">;

Filedist/types/components/GrTimePicker/defaults.d.ts

GrTimePickerConfigurablePropsinterface
interface GrTimePickerConfigurableProps {
    size: GrTimePickerSize;
    clearable: boolean;
    placement: UseFloatingPlacement;
}

Filedist/types/components/GrTimePicker/grTimePickerStyles.d.ts

GrTimePickerSizetype
type GrTimePickerSize = GrPickerSize;

Filedist/types/components/GrTimePicker/safelist.d.ts

grTimePickerSafelistconst
const grTimePickerSafelist: string[];

Filedist/types/components/GrTimePicker/GrTimePicker.vue.d.ts

GrTimePickerPropsinterface
interface GrTimePickerProps<T = Date | null> {
    modelValue?: T;
    valueAdapter?: GrChronoAdapterName | GrChronoAdapter<T>;
    min?: Date;
    max?: Date;
    minuteStep?: number;
    secondStep?: number;
    enableSeconds?: boolean;
    use12Hours?: boolean;
    today?: Date;
    format?: Intl.DateTimeFormatOptions;
    placeholder?: string;
    editable?: boolean;
    applyOnBlur?: boolean;
    clearable?: boolean;
    open?: boolean;
    inline?: boolean;
    placement?: UseFloatingPlacement;
    teleportTo?: string | HTMLElement;
    size?: GrTimePickerSize;
    locale?: string;
    id?: string;
    name?: string;
    disabled?: boolean;
    readonly?: boolean;
    invalid?: boolean;
    required?: boolean;
    loading?: boolean;
    ariaLabel?: string;
}
GrTimePickerEmitsinterface
interface GrTimePickerEmits<T = Date | null> {
    (e: 'update:modelValue', value: T | null): void;
    (e: 'change', value: T | null): void;
    (e: 'update:open', value: boolean): void;
    (e: 'clear'): void;
    (e: 'focus', event: FocusEvent): void;
    (e: 'blur', event: FocusEvent): void;
}

Usage from the library prose

The panel does not close on a selection

The hour, the minute and the period are three separate decisions, and closing after the first of them would mean the user chose a time they did not choose. That is precisely the reason the time panel behaves unlike a calendar.

The columns show the selection and move by themselves

With a panel or inline, every column settles with the selected value in the centre — right after mounting rather than on the first action: in inline nobody opens the panel, and the column of hours would otherwise stay on 00–05. Without a value the column settles on the first available one — with a min of 11:30 that is 11 rather than the forbidden zero.

Only the column itself scrolls, its scrollTop. scrollIntoView does not fit here: it moves every scrollable ancestor up to the window, and a picker below the first screen would drag the page to itself on load and on hydration. A step with an arrow brings the cursor to the nearest edge of the column, again without touching the page.

The value stays a Date

Even when the date does not matter, a full moment goes outside — with the date from today or from the current value. There is no separate “time of day” type in the model: it would require arithmetic of its own and an adapter of its own for the sake of one component. If a '14:30' string is needed, that is a valueAdapter on the consumer’s side.