
Summary
Weekly availability / opening-hours editor with per-day toggles, from/to time slots, optional break entries, copy-to-weekdays, searchable timezone selector, and locked days. Time pickers use the compact FlexTimeSegmentsField dropdown (hour + minute columns). Default control size issm; the timezone selector always renders at md.
Basic usage
State format
Day keys use three-letter English abbreviations:mon, tue, wed, thu, fri, sat, sun.
Times are HH:MM strings in 24-hour format (09:00, 17:30). On save, invalid slot entries are dropped and times are zero-padded.
Minimal example
Split shift with lunch break
Overnight slots
Add"overnight": true when a work slot crosses midnight (e.g. 22:00 → 06:00). The UI exposes an Overnight toggle on work slots; overlap validation uses the same ScheduleV2 / interval-engine rules as PHP.
timezone(null) hides the selector, omit the timezone key from state — it is not required or validated.
Export & audit helpers
Default state
The field default callsScheduleField::defaultSchedule():
Configuration API
All methods acceptClosure for dynamic configuration.
days(array|Closure $days)
Which days to render. Invalid day codes are ignored. Empty array falls back to all seven days.
Default: ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun'].
timezone(string|Closure|null $timezone)
Default timezone identifier when the selector is shown, or null to hide the timezone block entirely.
Default: 'UTC' (selector visible).
md size regardless of the schedule field size().
timeStep(int|Closure $minutes)
Minute step for FlexTimeSegmentsField dropdowns (From / To). Clamped to 1–60.
Default: 5.
minSlots(int|Closure $count) / maxSlots(int|Closure $count)
Per-day slot limits (includes both work slots and breaks).
Defaults: 1 / 10.
requireSlotsForEnabledDays(bool|Closure $condition = true)
When true (default), each enabled day must have at least minSlots() valid entries.
When false, an enabled day may have zero slots (useful for “open but hours TBD” flows — use carefully).
allowCopyToWeekdays(bool|Closure $condition = true)
Shows Copy to weekdays on the copy source day.
Default: true.
copySourceDay(string|Closure $day = 'mon')
Which day row displays the copy button. Must be one of mon … sun.
Default: mon.
workdays(array|Closure $days)
Target days for Copy to weekdays. Invalid entries are filtered. Empty after filter falls back to Mon–Fri.
Default: ['mon', 'tue', 'wed', 'thu', 'fri'].
enabled flags.
lockedDays(array|Closure $days)
Days that cannot be toggled on/off. Shows a lock icon instead of the switch. Locked days still display their schedule when enabled in state.
Only days present in days() are kept.
variant(string|Closure $variant)
Visual container style.
size(string|ControlSize|Closure $size)
Size of time inputs and day rows. Default: sm.
Timezone selector remains md.
readOnly(bool|Closure $condition = true) / disabled(bool|Closure $condition = true)
Inherited from Filament. Disables toggles, time pickers, copy, and slot toolbar actions.
focusOutline(bool|Closure $condition = true)
Inherited from HasFieldFocusOutline. Default: false. Adds focus ring on time input shells when enabled.
Public helper methods
Validation
Built-in rule runs on submit (and when Livewire validates). Custom Filamentrequired is mapped to nullable at rule level — emptiness is checked inside the schedule validator when required() is set.
Real-time UI validation mirrors server messages via Alpine (
from_before_to, min_slots, max_slots, overlap).
Disabled days skip slot validation entirely.
Model & persistence
Recipe: restaurant — weekdays + lunch break
Recipe: support desk — weekdays only, no weekends in UI
Recipe: always-closed weekends (locked)
Recipe: no timezone selector (local hours)
Recipe: 30-minute steps, larger time inputs
Recipe: read-only preview on view page
UI behaviour
CSS classes
Timezone block reuses TimezoneField classes (
fff-timezone-field, teleported menu classes).
Assets
Lazy-loaded bundles (FlexFieldAssets::stylesheetsFor('schedule-field')):
flex-text-inputswitchteleported-menutimezone-fieldflex-time-segments
schedule-field(main coordinator)flex-time-segments(preloaded per slot — shared chunk)
wire:ignore on the field root. After deploy, run:
Playground
Slug:schedule-field
/admin/flex-fields-playground/schedule-field — see Playground.
Implementation notes
- Normalization:
ScheduleNormalizer— pads times, drops invalid slots, coercestypetoslotorbreak. - Validation:
ScheduleValidator— overlap detection sorts slots by start time. - Day constants:
ScheduleDays::ALL,ScheduleDays::WEEKDAYS. - Click-outside on the field closes open time menus and the timezone dropdown.
- Mobile layout reflows day header (copy button moves below label row).