From d08c949b14ccc5897855c55ae8f5bf4716625e17 Mon Sep 17 00:00:00 2001 From: Maurice Date: Fri, 9 Oct 2026 15:12:43 +0200 Subject: [PATCH] refactor(time-picker): draw the hour, minute and AM/PM columns with one spinner column The three columns of the time picker each repeated the same chevron up, value box and chevron down markup. TimeSpinnerColumn draws one, and the button style it shares with the clear button moves to timeSpinnerStyle. The AM/PM column keeps its narrower box, smaller type and leading gap as props, and the hour and minute boxes keep their tabular digits. --- .../components/shared/CustomTimePicker.tsx | 85 +++++-------------- .../shared/TimeSpinnerColumn.test.tsx | 64 ++++++++++++++ .../components/shared/TimeSpinnerColumn.tsx | 56 ++++++++++++ .../src/components/shared/timeSpinnerStyle.ts | 13 +++ 4 files changed, 153 insertions(+), 65 deletions(-) create mode 100644 client/src/components/shared/TimeSpinnerColumn.test.tsx create mode 100644 client/src/components/shared/TimeSpinnerColumn.tsx create mode 100644 client/src/components/shared/timeSpinnerStyle.ts diff --git a/client/src/components/shared/CustomTimePicker.tsx b/client/src/components/shared/CustomTimePicker.tsx index 1b64e7519..c7b6d962c 100644 --- a/client/src/components/shared/CustomTimePicker.tsx +++ b/client/src/components/shared/CustomTimePicker.tsx @@ -1,9 +1,14 @@ import React, { useState, useRef, useEffect } from 'react' import { createPortal } from 'react-dom' -import { Clock, ChevronUp, ChevronDown } from 'lucide-react' +import { Clock } from 'lucide-react' import { useSettingsStore } from '../../store/settingsStore' import { formatClockTime, parseMeridiemTime } from '../../utils/formatters' import { useAnchoredPosition } from '../../hooks/useAnchoredPosition' +import { TimeSpinnerColumn } from './TimeSpinnerColumn' +import { spinnerButtonStyle } from './timeSpinnerStyle' + +// The hour and minute boxes, wide enough for two digits that do not jump as they change. +const DIGITS: React.CSSProperties = { width: 44, fontSize: 'calc(22px * var(--fs-scale-title, 1))', fontVariantNumeric: 'tabular-nums' } interface CustomTimePickerProps { value: string @@ -72,10 +77,9 @@ export default function CustomTimePicker({ value, onChange, placeholder = '00:00 update(newH, newM) } - const btnStyle: React.CSSProperties = { - background: 'none', border: 'none', cursor: 'pointer', padding: 2, - color: 'var(--text-faint)', display: 'flex', borderRadius: 4, - transition: 'color 0.15s', + const toggleMeridiem = () => { + if (hour === null) return + if (hour < 12) update(hour + 12, minute ?? 0); else update(hour - 12, minute ?? 0) } const handleInput = (e: React.ChangeEvent) => { @@ -179,78 +183,29 @@ export default function CustomTimePicker({ value, onChange, placeholder = '00:00 backdropFilter: 'blur(24px)', WebkitBackdropFilter: 'blur(24px)', }}> {/* Hours */} -
- -
- {hour !== null ? (is12h ? String(hour === 0 ? 12 : hour > 12 ? hour - 12 : hour) : String(hour).padStart(2, '0')) : '--'} -
- -
+ + {hour !== null ? (is12h ? String(hour === 0 ? 12 : hour > 12 ? hour - 12 : hour) : String(hour).padStart(2, '0')) : '--'} + : {/* Minutes */} -
- -
- {minute !== null ? String(minute).padStart(2, '0') : '--'} -
- -
+ + {minute !== null ? String(minute).padStart(2, '0') : '--'} + {/* AM/PM Toggle */} {is12h && hour !== null && ( -
- -
- {hour >= 12 ? 'PM' : 'AM'} -
- -
+ + {hour >= 12 ? 'PM' : 'AM'} + )} {/* Clear */} {value && ( + ); +} diff --git a/client/src/components/shared/timeSpinnerStyle.ts b/client/src/components/shared/timeSpinnerStyle.ts new file mode 100644 index 000000000..0de2bc797 --- /dev/null +++ b/client/src/components/shared/timeSpinnerStyle.ts @@ -0,0 +1,13 @@ +import type { CSSProperties } from 'react'; + +/** The look of the time picker's small chevron and clear buttons. */ +export const spinnerButtonStyle: CSSProperties = { + background: 'none', + border: 'none', + cursor: 'pointer', + padding: 2, + color: 'var(--text-faint)', + display: 'flex', + borderRadius: 4, + transition: 'color 0.15s', +};