Search by

devletes / filament-time-picker

devletes

A clock-face time and date-time picker for Filament — hour and minute dials, drag or type, natively in Livewire and Alpine. No jQuery, no date library, no build step.

Package info

github.com/devletes/filament-time-picker

pkg:composer/devletes/filament-time-picker

Statistics

Installs: 24

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.0 2026-10-26 03:18 UTC

This package is auto-updated.

Last update: 2026-10-26 03:21:44 UTC


README

Latest Version on Packagist Total Downloads License GitHub Stars

A clock face for Filament's time and date-time fields — hour and minute dials, drag or tap or type.

  • Drop-in replacements. TimePicker and DateTimePicker extend Filament's own, so swapping the import is the whole migration
  • No dependencies. No jQuery, no date library, no build step — vanilla JavaScript on the Alpine you already have
  • The dial follows the field. g:i A gets a 12-hour face with AM/PM, H:i gets a 24-hour face with an inner ring
  • Every minute is reachable, not every fifth, and the steps still coarsen it when you want that
  • Bounds that hold. minTime() / maxTime() dim the dial, clamp typing, and are re-checked on the server
  • A calendar to match, with min and max dates, disabled dates and your locale's month and weekday names
  • Themed off Filament's palette — follows your panel's colour, light and dark
The clock open on a form (light) The clock open on a form (dark)

Requirements

  • PHP ^8.2
  • Filament ^5.0 on Laravel 12 or 13

Installation

composer require devletes/filament-time-picker

Then publish Filament's assets so the stylesheet and script are served on your panels:

php artisan filament:assets

The package registers its own stylesheet and script with Filament's asset manager — there is nothing to add to your custom Tailwind theme.

Quick start

use Devletes\TimePicker\Forms\Components\DateTimePicker;
use Devletes\TimePicker\Forms\Components\TimePicker;

TimePicker::make('deliver_at')
    ->seconds(false)
    ->displayFormat('g:i A');

DateTimePicker::make('ships_at')
    ->seconds(false)
    ->displayFormat('M j, Y g:i A');

A drop-in replacement

Both components extend Filament's own, so swapping the import is the whole migration:

-use Filament\Forms\Components\TimePicker;
+use Devletes\TimePicker\Forms\Components\TimePicker;

-use Filament\Forms\Components\DateTimePicker;
+use Devletes\TimePicker\Forms\Components\DateTimePicker;

Everything you already use still works and still means the same thing — seconds(), the steps, format(), displayFormat(), timezone(), live(), disabled(), the affixes and the validation. The state cast is Filament's own, so nothing about what is stored changes.

The dial follows the field

Say nothing, and the face is read off the display format. A field that writes its hour as 1–12 gets the 12-hour face with an AM/PM switch; anything else gets the 24-hour face, with 13–23 and midnight on an inner ring.

TimePicker::make('deliver_at')->displayFormat('g:i A');   // 12-hour face
TimePicker::make('cutoff')->displayFormat('H:i');         // 24-hour face
TimePicker::make('cutoff')->hoursCycle(24);               // say so yourself
The 12-hour face (light) The 12-hour face (dark)
The 24-hour face with its inner ring (light) The 24-hour face with its inner ring (dark)

Set time-picker.hours_cycle in the config to make one face the rule everywhere.

Minutes and seconds

Picking an hour moves to the minute dial. The dial takes every minute: the numerals are drawn at the fives, but dragging between them lands on 9:07 the way you would expect. A small label above the pin says which unit you are setting.

TimePicker::make('slot')->minutesStep(15);   // quarter hours only
TimePicker::make('slot')->hoursStep(2);
The minute dial (light) The minute dial (dark)

seconds() adds a third dial, and secondsStep() coarsens it the same way.

TimePicker::make('loaded_at')
    ->seconds()
    ->displayFormat('H:i:s');
The seconds dial (light) The seconds dial (dark)

Bounds

TimePicker::make('pickup')
    ->minTime('09:00')
    ->maxTime('17:00');

Numerals outside the range are dimmed and refuse the pointer, typing is pulled back inside, and the same bound is checked again on the server — the panel is not the only way a value arrives. The message names the bound in the field's own format ("must be no earlier than 9:00 AM"). Both accept a string, a Carbon instance, or a closure.

Hours outside 9 AM–5 PM dimmed (light) Hours outside 9 AM–5 PM dimmed (dark)

Date and time

DateTimePicker pairs the same clock with a calendar. Picking a day moves straight to the clock, and the switch at the top of the panel moves between the two.

DateTimePicker::make('ships_at')
    ->seconds(false)
    ->displayFormat('M j, Y g:i A')
    ->minDate(now()->startOfDay())
    ->maxDate(now()->addMonths(3))
    ->disabledDates(['2026-12-25', '2026-12-26'])
    ->minTime('08:00')
    ->maxTime('20:00');
The calendar (light) The calendar (dark)
The clock after picking a day (light) The clock after picking a day (dark)

minDate() and maxDate() disable the days outside them, and on the first and last day their times narrow the clock too. minTime() / maxTime() stay a daily window, applied on every day. Month and weekday names follow the field's locale.

Filament's other date options apply as they do there:

DateTimePicker::make('ships_at')
    ->weekStartsOnSunday()                  // or firstDayOfWeek(0–7)
    ->defaultFocusedDate(now()->addWeek())  // the month an empty field opens on
    ->closeOnDateSelection();               // pick a day and close, without the clock

Without a time, it is a calendar only:

DateTimePicker::make('delivered_on')->time(false);

Typing and the keyboard

The big digits in the panel are inputs. Click one and type; it moves to the minute on its own when the hour cannot be extended. The arrow keys nudge by the field's own step, Escape closes the panel, and Backspace on the closed field clears it.

Closing and the footer

By default the panel waits for OK. To save and close as soon as the last dial is set (the minute, or the second when seconds are on), and hide OK:

TimePicker::make('call_back_at')->closeOnComplete();

The footer's buttons are Filament's own. Relabel them, or drop the "Now" shortcut:

TimePicker::make('deliver_at')
    ->okLabel('Set time')
    ->cancelLabel('Back')
    ->nowLabel('Right now')
    ->nowButton(false);

On phones

native() hands rendering back to Filament, which gives the browser's own control — on a phone the platform's picker is the better choice:

TimePicker::make('deliver_at')
    ->native(fn (): bool => str_contains((string) request()->userAgent(), 'Mobile'));

Configuration

config/time-picker.php sets the defaults every field starts from:

php artisan vendor:publish --tag=devletes-time-picker-config
return [
    'hours_cycle' => null,         // 12, 24, or null to read it off each field's display format
    'close_on_complete' => false,  // save and close once the last dial is set
    'now_button' => true,          // show the "Now" shortcut
];

Theming

Every colour resolves through Filament's palette variables, so the picker follows your panel's primary colour and its light and dark themes without being told. To change it further, override the --fi-dtp-* variables in your theme stylesheet:

html .fi-dtp {
    --fi-dtp-accent: var(--success-600);
    --fi-dtp-plate: var(--gray-50);
}

html.dark .fi-dtp {
    --fi-dtp-accent: var(--success-400);
}

The variables are surface, plate (the dial), border, text, muted, faded, accent, on-accent and hover.

Or publish the views to change the markup:

php artisan vendor:publish --tag=devletes-time-picker-views

Translations

Every user-facing string goes through devletes-time-picker::time-picker.*. Publish the language files to reword them or add a locale:

php artisan vendor:publish --tag=devletes-time-picker-translations

Then edit or add files under lang/vendor/devletes-time-picker/.

API reference

Everything Filament's TimePicker and DateTimePicker offer, plus:

Method What it does
hoursCycle(12|24) Fixes the face rather than reading it off the display format
minTime($time) The earliest time of day the field will take. String, Carbon, or closure
maxTime($time) The latest time of day the field will take. String, Carbon, or closure
closeOnComplete() Save and close once the last dial is set, and hide OK
nowButton(false) Drops the "Now" shortcut from the footer
okLabel($label) Relabels the confirm button
cancelLabel($label) Relabels the cancel button
nowLabel($label) Relabels the "Now" shortcut
native() Hands rendering back to Filament for the browser's own control

On DateTimePicker, Filament's minDate(), maxDate(), disabledDates(), firstDayOfWeek(), weekStartsOnMonday(), weekStartsOnSunday(), defaultFocusedDate(), closeOnDateSelection(), time() and seconds() all apply to the calendar and the clock.

Testing

composer install
composer test

The suite runs on Testbench with the real Filament stack, against PHP 8.2–8.4 on Laravel 12 and 13.

Need something custom?

We build production Filament panels and plugins for teams that want to ship fast without compromising on polish. If you need a custom feature, an extended variant of this package, or a fully bespoke component built for your stack, we can help.

Typical engagements: new Filament plugins, custom resources/widgets/actions, theme + UX work, integrations with your existing services, and one-off tailored forks of our open-source packages.

Have you seen our Orbit theme?

Orbit is our dark-first theme for Filament 5. Every panel surface is restyled — sidebar, topbar, forms, tables, modals, notifications.

Orbit theme, light mode Orbit theme, dark mode

Check it out here →

Credits

License

MIT. See LICENSE.md.