harvirsidhu/filament-timepicker
A Filament form field that adds a customizable time picker to Laravel admin panels, making it easy to select and store time values with a clean, user-friendly input.
3p, 330, 1530, 9.30. That is a measurable input-speed and error-rate decision, and it is cheap to test behind a single field swap.H:i with a deliberate "never introduce a timezone shift" contract and a regression test guarding it. Adopting it means a schedule slot is a local wall-clock fact — which is right for "the 3:00 PM appointment at this location," but wrong for cross-timezone coordination. That is a product decision to make explicitly, before the schema is written, not after.minTime()/maxTime() are now enforced both client-side and via a server validation rule (so pastes, $set, and imports can't bypass them), and strict() additionally confines input to the interval grid. Business rules like "no bookings before 08:00 or after 18:00, on the quarter hour" become field config rather than custom validation code — a roadmap lever we can flip per-market or per-plan.durationFrom() and defaultDuration() let a start time auto-fill an end time and label options with their duration ("3:00 PM — 30 min"). That is a real scheduling product feature (avoiding zero-length or overlapping bookings) available as two chained calls.Home/End/PageUp/PageDown), aria-invalid on error, and the removal of a focus-commit bug that silently wrote values into untouched optional fields are procurement-relevant for enterprise/health customers running accessibility audits.filament/forms and not the full panel package, we can ship the same field in a plain Livewire admin surface without dragging a panel dependency in — which affects whether an internal-tools roadmap item is one sprint or three.Adopt when:
--gray-* variables, so no @source line or theme surgery is required.Look elsewhere when:
timezone() method is an explicit no-op by design; using it as if it worked is the most likely integration mistake.seconds() exists but the parsing grammar and UX are minute-granular.TimePicker without behaviour change — native() and timezone() are no-ops for signature compatibility only; if a migration must be invisible, that's a docs-and-expectations conversation, not a free swap.For executives (one paragraph): We have a recurring, cheap-to-solve usability problem wherever staff or customers type a time into our admin tools — 96-item dropdowns, inconsistent formats, and validation that only catches bad values after submit. This is an MIT-licensed, actively maintained form field for Filament (our admin stack) that turns a free-typed entry like "3p" or "330" into a validated, business-hours-bounded time value, with keyboard and screen-reader support, in roughly the time it takes to install a dependency. It was extracted from a real production scheduling app, which is why it has opinionated defaults on the two things that actually break scheduling software: timezone drift on stored times, and validation that can be bypassed. Cost is near zero; the main residual risk is that it is a small, single-maintainer package, which we mitigate by pinning a version and keeping our usage to the documented public API. Recommendation: pilot it on one scheduling form this cycle and expand only if input-error rates and entry time improve.
For engineering (the honest version): It's a standalone Filament Standalone Plugin — no Plugin object, all config in the service provider — requiring filament/forms only, so no panel dependency leaks in. The public API (interval, minTime, maxTime, durationFrom, defaultDuration, displayFormat, seconds, strict) is declared a stable contract. It stores wall-block H:i with parsing normalisation in dehydrateStateUsing; do not treat timezone() as functional. It mirrors a PHP and a JS parser that are kept in lockstep by a shared JSON fixture in both suites — so if we add format support, we change both parsers and add a fixture row, or CI fails. There is a server-side validation rule that catches anything bypassing the client. The known sharp edges are documented in the changelog and AGENTS.md: wire:ignore on the Alpine root is load-bearing (removing it breaks the teleported panel after Livewire morphs), reactive minTime/maxTime/interval closures reach the browser through a data-config sibling element, and the panel is positioned imperatively rather than via a :style binding. Read the first two of those before touching the view. Test coverage is genuinely good for a package this size — JS suite, PHP suite, architecture tests, and feature tests rendering through a real Livewire component. Pin the version, run php artisan filament:assets on upgrade, and keep our consumer-side regression test that asserts slot times store as wall-clock.
How can I help you explore Laravel packages today?