diff --git a/resources/css/components/timepicker.css b/resources/css/components/timepicker.css index 414a4b48..da35f8d4 100644 --- a/resources/css/components/timepicker.css +++ b/resources/css/components/timepicker.css @@ -11,7 +11,9 @@ * [data-timepicker-display] the time selector: hour and minute boxes 96×80 (display-large, * small corner; primary-container when selected, otherwise * surface-container-highest), a 24px separator, and the period - * selector 52×80 beside them, 4px away + * selector 52×80 beside them, 4px away. On a 24-hour clock + * (data-cycle="24") there is no period selector and the boxes are + * 114px, TimeSelector24HVerticalContainerWidth. * [data-timepicker-dial] 256px, surface-container-highest; numbers in body-large at radius * 101 (the 24-hour inner ring at 69), a primary selector — a 2px line, * an 8px centre and a 48px handle — with the number under the handle @@ -20,9 +22,11 @@ * "Minute" (or the error) below in body-small, the period 52×72 * [data-timepicker-actions] a 48px row: the mode toggle, then Cancel and OK * - * The period selector is Compose's current one (ComposeMaterial3Flags.isUpdatedTimepickerToggleEnabled, - * on by default): two toggle buttons 4px apart, round and surface-container-lowest when off, a 12px - * corner and primary-container with a bold label when on, not the outlined pair of the tokens. + * The period selector keeps Compose's current shape (ComposeMaterial3Flags.isUpdatedTimepickerToggleEnabled, + * on by default): two buttons 4px apart, round and surface-container-lowest when off, a 12px corner + * when on, not the outlined pair of the tokens. The selected colour is M3's own tertiary-container + * with a bold label (PeriodSelectorSelectedContainerColor), which keeps AM/PM apart from the + * primary-container hour and minute boxes. They are radios, so the state is `aria-checked`. * * In a landscape window with no room for the upright dial the dial variant lies on its side, as * Compose's HorizontalTimePicker does: the display with the period selector (216×38, 16px under it) @@ -130,6 +134,13 @@ color: var(--md-sys-color-on-primary-container); } + /* A 24-hour clock has no period selector beside the boxes, and M3 widens them into the room + (TimePickerTokens.TimeSelector24HVerticalContainerWidth = 114dp). Landscape keeps 96px: the + display is a 216px column there. */ + [data-timepicker-display][data-cycle="24"] [data-timepicker-box] { + width: 7.125rem; + } + [data-timepicker-separator] { display: grid; place-items: center; @@ -165,20 +176,22 @@ } [data-timepicker-period] > button:active, - [data-timepicker-period] > button[aria-pressed="true"] { + [data-timepicker-period] > button[aria-checked="true"] { border-radius: 0.75rem; } - [data-timepicker-period] > button[aria-pressed="true"] { - background-color: var(--md-sys-color-primary-container); - color: var(--md-sys-color-on-primary-container); + /* Tertiary, not the primary the hour and minute boxes take: M3 gives the two selectors different + emphases (PeriodSelectorSelectedContainerColor = TertiaryContainer). */ + [data-timepicker-period] > button[aria-checked="true"] { + background-color: var(--md-sys-color-tertiary-container); + color: var(--md-sys-color-on-tertiary-container); font-weight: var(--md-ref-typeface-weight-bold); } [data-timepicker-period] > button:disabled { cursor: default; color: color-mix(in srgb, var(--md-sys-color-on-surface) 38%, transparent); - background-color: color-mix(in srgb, var(--md-sys-color-on-surface) 10%, transparent); + background-color: color-mix(in srgb, var(--md-sys-color-on-surface) 12%, transparent); } /* ---- The dial ----------------------------------------------------------------------------- */ @@ -436,6 +449,10 @@ margin: 0; } + [data-timepicker-surface][data-mode="dial"] [data-timepicker-display][data-cycle="24"] [data-timepicker-box] { + width: 6rem; + } + [data-timepicker-surface][data-mode="dial"] [data-timepicker-display] [data-timepicker-period] { flex-direction: row; width: 13.5rem; diff --git a/resources/js/timepicker.js b/resources/js/timepicker.js index 9c4b69bb..85ff7558 100644 --- a/resources/js/timepicker.js +++ b/resources/js/timepicker.js @@ -380,6 +380,21 @@ document.addEventListener('alpine:init', () => { this.aim() }, + /** + * AM and PM are a radio group, which M3's labelling table asks for: an arrow key moves to + * the other option and selects it, and focus follows, as WAI-ARIA's radio group does. + */ + movePeriod(event) { + const pm = !this.isPm + + if (!this.periodAllowed(pm)) { + return + } + + this.setPeriod(pm) + event.currentTarget.querySelector(`[data-timepicker-period-option="${pm ? 'pm' : 'am'}"]`)?.focus() + }, + /** The allowed time in a period nearest to `time` (periodAllowed has said there is one). */ nearestInPeriod(pm, time) { let best = null diff --git a/resources/views/components/timepicker.blade.php b/resources/views/components/timepicker.blade.php index 14fa9b11..81de4e7a 100644 --- a/resources/views/components/timepicker.blade.php +++ b/resources/views/components/timepicker.blade.php @@ -31,14 +31,16 @@ TimePickerTokens and TimeInputTokens — a surface-container-high dialog with an extra-large corner and elevation 3, a 256dp surface-container-highest dial with body-large numbers, a primary selector with a 48dp handle, a 2dp line and an 8dp centre, 96×80 time selector boxes in - display-large (primary-container when selected), 96×72 time fields in display-medium — and - TimePicker.kt and TimePickerDialog.kt for the layout, the 24-hour inner ring (12–23, at 69dp; - 00–11 outside at 101dp, as Material Components for Android labels them too), the gestures, the - move on to minutes and the error texts. The period selector is Compose's current default - (`isUpdatedTimepickerToggleEnabled`): two separate shape-morphing toggle buttons in - primary-container, not the outlined pair its tokens still describe. The dial's numbers are - aria-hidden; the dial is a `slider` whose value text names the hour or minute. The dialog is - `wire:ignore`, so a Livewire render never closes an open picker or resets its draft. --}} + display-large (primary-container when selected; 114dp wide on a 24-hour clock, where there is no + period selector beside them, per TimeSelector24HVerticalContainerWidth), 96×72 time fields in + display-medium — and TimePicker.kt and TimePickerDialog.kt for the layout, the 24-hour inner + ring (12–23, at 69dp; 00–11 outside at 101dp, as Material Components for Android labels them + too), the gestures, the move on to minutes and the error texts. The period selector keeps + Compose's current default shape morph (`isUpdatedTimepickerToggleEnabled`) but M3's own + tertiary-container, which is what holds hour/minute (primary-container) and AM/PM apart; it is a + radio group, as M3's labelling table says, so the arrows move between AM and PM. The dial's + numbers are aria-hidden; the dial is a `slider` whose value text names the hour or minute. The + dialog is `wire:ignore`, so a Livewire render never closes an open picker or resets its draft. --}} @props([ 'label' => null, @@ -211,7 +213,7 @@