timepicker-ui

A time picker for web forms. Attach it to an <input> and users pick a time on a clock face or a scroll wheel.

npm version downloads license Coverage Status Tests Socket Badge

Live demo · Documentation · Changelog · React wrapper

Why timepicker-ui

Features

Installation

npm install timepicker-ui

Quick start

<input id="timepicker" type="text" />
import { TimepickerUI } from "timepicker-ui";
import "timepicker-ui/main.css";

const input = document.querySelector("#timepicker");
const picker = new TimepickerUI(input, {
  clock: { type: "24h" },
  ui: { theme: "dark" },
  callbacks: {
    onConfirm: (data) => console.log("Selected:", data),
  },
});
picker.create();

No global CSS reset is needed: the picker sets box-sizing: border-box on its own elements (details).

API

picker.create();               // render the picker and attach it to the input
picker.open();
picker.close();
picker.destroy();
picker.getValue();             // { hour, minutes, seconds, type, time }, seconds only with clock.showSeconds
picker.setValue("14:30");      // "2:30 PM" works too
picker.setValue("14:30:15");   // with clock.showSeconds
picker.setOptions({ ui: { theme: "crane" } });
picker.setRange({ from: "09:00", to: "17:00" });  // needs RangePlugin
picker.getRange();
picker.setActivePart("to");
picker.refreshDisabledTime();  // after the data behind clock.isTimeDisabled changes

const off = picker.on("confirm", (data) => {});  // on() and once() return a function that removes the handler
off();
picker.on("confirm", (data) => {});
picker.on("cancel", (data) => {});
picker.on("open", () => {});
picker.on("update", (data) => {});
picker.on("clear", (data) => {});
picker.on("error", (data) => {});
picker.once("confirm", handler);
picker.off("confirm", handler);

TimepickerUI.getById("my-id");
TimepickerUI.getAllInstances();
TimepickerUI.isAvailable("#timepicker");
TimepickerUI.destroyAll();

Docs: methods, events

Options

Grouped by area. Pass only what you need, the rest has defaults (full list).

new TimepickerUI(input, {
  clock: {
    type: "12h" | "24h" | "auto", // default "12h"; "auto" follows the browser locale
    incrementHours: 1,
    incrementMinutes: 1,
    showSeconds: false,         // true adds seconds (HH:MM:SS)
    incrementSeconds: 1,
    disabledTime: { hours: [], minutes: [], seconds: [], ranges: "" },
    isTimeDisabled: ({ hour, minutes, seconds }) => false,  // hour is 0-23
    defaultTime: "now" | { time, updateInput },
  },
  ui: {
    theme: "basic" | "dark" | "m3-green" | "crane" | "blueprint" | ...,
    mode: "clock" | "wheel" | "compact-wheel",   // default "clock"
    animation: true,
    backdrop: true,
    initialView: "clock",       // or "keyboard"
    editable: false,
    inline: { enabled: false, container: "#picker" | element, showButtons: false, autoUpdate: false, embedded: false },
    clearButton: false,         // or { clearInput: false } to keep the input value on Clear
    cssClass: "",
  },
  labels: { am, pm, ok, cancel, clear, clockTitle, hour, minute, second, rangeFrom, rangeTo, timezone, ... },
  behavior: { focusTrap: true, returnFocus: false, delayHandler: 0, id: "" },
  callbacks: { onConfirm, onCancel, onOpen, onUpdate, onSelectHour, onSelectMinute, onSelectSecond, onSelectPeriod, onClear, onError, ... },
  wheel: { placement: "auto" | "top" | "bottom", hideFooter: false, commitOnScroll: false, hideDisabled: false, ignoreOutsideClick: false },
  range: { enabled: false, linked: true, minDuration, maxDuration },
  timezone: { enabled: false, default, list },
});

Themes

Preview all of them: basic, crane, crane-straight, m3-green, m2, dark, glassmorphic, pastel, ai, cyberpunk, blueprint, blueprint-dark.

main.css is required and already includes basic. The rest are separate files:

import "timepicker-ui/main.css";
import "timepicker-ui/theme-dark.css";

new TimepickerUI(input, { ui: { theme: "dark" } });

Or import timepicker-ui/index.css to get all of them. Writing your own theme: styling guide.

Plugins

Range, timezone and wheel ship as separate imports. Register them once before creating pickers. You need WheelPlugin for the wheel and compact-wheel modes.

import { TimepickerUI, PluginRegistry } from "timepicker-ui";
import { RangePlugin } from "timepicker-ui/plugins/range";
import { TimezonePlugin } from "timepicker-ui/plugins/timezone";
import { WheelPlugin } from "timepicker-ui/plugins/wheel";

PluginRegistry.register(RangePlugin);
PluginRegistry.register(TimezonePlugin);
PluginRegistry.register(WheelPlugin);

new TimepickerUI(input, { range: { enabled: true } });

Upgrading

From 4.5 nothing breaks. 4.6 adds ui.inline.embedded, range.linked, clock.isTimeDisabled with refreshDisabledTime(), the setRange() / getRange() / setActivePart() methods, and a remover returned by on() / once(). See the CHANGELOG.

From 4.4, 4.5 adds optional seconds (clock.showSeconds) and fixes many bugs. It also renames many options to clearer names, for example ui.initialView instead of ui.mobile, and adds setOptions() in place of update(). The old names keep working until 5.0, and the options page lists every pair. A few changes can affect an existing setup: the AM/PM part of the value is always AM / PM whatever labels.am / labels.pm say, out-of-range clock.disabledTime.ranges edges such as 25:00 are reported as errors, the old .tp-ui-ripple classes are gone, and some focus-ring and radius tokens changed. The CHANGELOG lists them under “Check before upgrading”.

From 4.3 nothing breaks. 4.4 adds the blueprint and blueprint-dark themes, 16 new labels.* for screen-reader text, and Home/End/PageUp/PageDown keys. See the CHANGELOG.

From 3.x, options are now grouped (clock, ui, labels, …), events go through picker.on(), and CSS class names changed. There’s a migration guide.

From 2.x, you have to import the CSS yourself and some event names changed (legacy guide).

Frameworks

React:

import { useEffect, useRef } from "react";
import { TimepickerUI } from "timepicker-ui";
import "timepicker-ui/main.css";

function TimePicker() {
  const ref = useRef<HTMLInputElement>(null);
  useEffect(() => {
    if (!ref.current) return;
    const picker = new TimepickerUI(ref.current);
    picker.create();
    return () => picker.destroy();
  }, []);
  return <input ref={ref} />;
}

There’s also a React wrapper (docs). Vue and Angular examples are in the quick start docs.

Need a date picker?

datepicker-ui is the companion calendar from the same author. It has no dependencies, follows Material Design 3, is safe to import during server rendering and uses the same theme names, so the two pickers match. It covers single dates, ranges and multiple dates.

npm install @pglejzer/datepicker-ui

The date and time guide shows how to use both pickers in one form.

Coming in 5.0

5.0.0 is planned but has no release date yet. 4.5.0 prepares for it. Planned changes:

Details are on the roadmap.

Development

See app/README.md.

Contributing

Issues and PRs welcome on GitHub.

License

MIT © Piotr Glejzer