Skip to content

Commit 75ced68

Browse files
authored
feat(registry): add time-picker (#193)
* feat: add time-picker component - Add TypeScript types with comprehensive interfaces - Create time-picker UI component with hour, minute, second selection - Support both 12-hour and 24-hour formats - Add 6 demo examples (basic, 12-hour, seconds, controlled, form, step) - Include full documentation with API reference and keyboard shortcuts - Update registry-ui.ts and registry-examples.ts - Add form integration with validation support - Include customizable step intervals for time selection * feat(time-picker): refactor with native behavior and React 19 patterns - Add native HTML time input behavior (click to select segments) - Implement inline editing with arrow key navigation (↑↓ to change values) - Add AM/PM support with instant keyboard shortcuts (A/P keys) - Introduce InputGroup pattern for cleaner, composable API - Remove forwardRef in favor of React 19 direct ref props - Memoize callbacks and compose event handlers properly - Add arrow key navigation in dropdown columns with wrapping - Simplify all examples to use new InputGroup pattern - Update documentation with keyboard shortcuts and usage examples Breaking changes: - InputGroup now wraps inputs and trigger together - Trigger is now a button that goes inside InputGroup - Removed separate inline demo examples (all use same pattern) * refactor(time-picker): use shadcn Popover with anchor positioning - Replace custom popover implementation with shadcn Popover components - Use PopoverAnchor to position popover relative to InputGroup - Leverage Radix CSS variables (--radix-popover-trigger-width) for width - Fix store context issue by splitting Root into Root/RootImpl pattern - Remove manual width measurement and useEffect - Improve accessibility and positioning consistency * reactor: api * feat: better demo * refactor: improve time-picker and color-picker components - Add open/defaultOpen props to time-picker for controlled state - Fix duplicate hour key bug in 12-hour format - Refactor time-picker store to inline pattern (matches stepper) - Optimize column items with getItems() pattern for better performance - Add use12Hours demo to show period functionality - Refactor color-picker store to inline pattern - Remove redundant onOpenChange callback in color-picker - Fix CSS linter warning in color-picker * feat(time-picker): add visually hidden scrollbar * fix(time-picker): auto-focus selected item when popover opens * fix(time-picker): use onOpenAutoFocus for better focus management * feat(time-picker): add group context and Tab navigation between columns * feat(time-picker): add Tab navigation support for AM/PM column * fix(time-picker): properly track period item refs for Tab navigation * refactor(time-picker): use TimePickerColumn for Period component, make components reusable * debug(time-picker): add console logs for focus debugging * fix(time-picker): stabilize getSelectedItemRef to prevent re-registrations * debug(time-picker): add onFocus logs to track focus events * feat(time-picker): add Left/Right arrow navigation between columns * chore(time-picker): remove all debug console.logs * refactor: update time-picker-form-demo to use shadcn form pattern * chore: update page * refactor(time-picker): replace use12Hours with locale-based auto-detection - Replace use12Hours prop with optional locale prop - Add uses12HourFormat() utility using Intl.DateTimeFormat - Calculate is12Hour once in root and share via context for better performance - Always store values in 24-hour format (HH:mm or HH:mm:ss) internally - Auto-detect display format from user's locale, matching native input behavior - Remove time-picker-12hour-demo (no longer needed with auto-detection) - Update documentation and examples - Rename time-picker-step-demo to time-picker-minute-step-demo for clarity * refine time-picker segments styling: make more compact and less blocky * fix time-picker empty state: show '--' placeholders instead of blank segments * feat: display placeholder text in time-picker when no value is set * fix time-picker: prevent auto-fill on blur and fix placeholder overlap * chore: revert * refactor(time-picker): improve focus management and registration flow - Add focusFirst utility for fallback focus strategy when no value is set - Fix focus management when value/defaultValue is empty - Consolidate redundant types (ItemData, ColumnData) at top of file - Create reusable sortNodes utility for DOM position sorting - Simplify column registration to use ColumnData object instead of flat params - Add proper error handling to useTimePickerGroupContext hook - Optimize getSelectedItemRef to reuse getItems() (DRY principle) - Add COLUMN_NAME constant for consistency - Add SegmentFormat type alias for better type reusability - Remove redundant comments and clean up code structure - Match stepper component's cleaner, more performant patterns * chore: build registry * chore: update docs * feat(time-picker): add -- placeholder for empty segments - Show -- placeholder in each segment when no value (matches native HTML time input) - Keep -- visible and selected on focus for better UX - Handle arrow key navigation from -- placeholder state * feat(time-picker): initialize with current time when opening popover - When popover opens with no value/defaultValue, initialize with current time - Matches native HTML time input behavior - Selecting hour preserves current minutes/seconds from initialization * chore: revert * feat(time-picker): show current time as reference when no value set - Display current time in columns when no value/defaultValue is set - Use current time segments as defaults when user selects a segment - Matches HTML time input behavior (visual reference only, no auto-update) - When selecting hour=8 at current time 2:30 PM, creates '8:30' - Applies to hours, minutes, seconds, and AM/PM period * fix(time-picker): Tab navigation skipping columns - Replace focusFirst with direct focus for Tab/Arrow column navigation - Fixes timing conflicts between browser Tab behavior and custom handler - Tab now consistently moves to the next/previous column without skipping * chore: revert again * fix: focus ring * fix(time-picker): resolve inconsistent tab focus navigation between columns - Added queueMicrotask to ensure DOM stability before column navigation - Added safety checks for column existence and valid refs - Tab navigation now consistently moves between columns (Hour → Minute → Period) - Arrow key navigation remains unchanged and working correctly * fix(time-picker): prevent column items from being cut off during scroll - Changed scrollIntoView from 'block: nearest' to 'block: center' - Selected items now center in viewport, ensuring full visibility - Eliminates partial clipping at container edges * chore: build registry * feat: native like * feat(time-picker): implement native HTML time input behavior - Auto-pad single digits (1 -> 01) with instant display - Auto-advance to next segment after two digits - Smart digit combination (typing 1 then 1 makes 11) - Keep segments empty (--) until explicitly set - Support partial time values (10:-- instead of 10:00) - Fix period segment to stay empty until user sets it - Add keyboard shortcuts for period: A/P/1/2 keys, arrow up/down - Preserve selection state after period changes - Use refs-based input registry instead of querySelector - Match native HTML time input behavior exactly * feat: add arrow left and arrow right navigation * feat(time-picker): add arrow navigation and backspace to clear segments - Add left/right arrow keys to navigate between segments - Add backspace/delete to clear selected segment - Preserve selection after clearing segment - Fix selection state to match native HTML time input - Improve accessibility with full keyboard navigation support * docs(time-picker): update accessibility and keyboard interaction documentation - Add comprehensive keyboard shortcuts organized by category - Document all input navigation options (Tab, Arrow keys) - Add value editing shortcuts (digits, arrows, backspace/delete) - Document period (AM/PM) shortcuts (A/P/1/2, arrows) - Update notes section with detailed behavior explanations - Organize documentation into Behavior, Format & Locale, and Customization sections * refactor(time-picker): clean up input group context naming and structure - Rename InputRegistryContext to TimePickerInputGroupContext - Add proper error handling with context validation - Inline provider logic directly in TimePickerInputGroup component - Rename methods to onInputRegister/onInputUnregister for consistency - Use InputElement type alias instead of HTMLInputElement - Replace useEffect with useIsomorphicLayoutEffect for input registration * feat(time-picker): add customizable segment placeholders and cleanup - Add segmentPlaceholder prop to customize empty segment display - Supports string (e.g., "--") or object (e.g., { hour: "hh", minute: "mm", period: "aa" }) - Normalized internally to object format for consistency - Remove unused placeholder prop - Clean up redundant comments (71 → 1) - Fix input width to dynamically adjust based on placeholder length - Add inputMode, autoComplete, and aria-invalid attributes to prevent browser validation errors - Update demo to show custom placeholder format * feat: add placeholder demo * feat: update css variables * feat: update docs * fix(time-picker): period column width, blur reset, and arrow navigation - Increase period column width to prevent text cutoff - Fix period value reset on blur by deriving from hour value - Fix arrow key navigation in popover by properly selecting current period - Default to current time's AM/PM when no value is set * feat(time-picker): auto-fill missing segments with current time - Auto-fill missing hour/minute/second when selecting any segment - Works in both input fields (on blur) and popover columns (on select) - Matches HTML native time input behavior - Missing segments populate with current time values * refactor: ad dconst * refactor(time-picker): use DEFAULT_SEGMENT_PLACEHOLDER constant Replace all hardcoded "--" strings with DEFAULT_SEGMENT_PLACEHOLDER constant for better maintainability and consistency. * refactor: time picker const
1 parent cc0b56d commit 75ced68

31 files changed

+3750
-436
lines changed

docs/__registry__/index.tsx

Lines changed: 243 additions & 137 deletions
Large diffs are not rendered by default.

docs/app/(lobby)/pg/page.tsx

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,22 @@
1+
"use client";
2+
13
import { Demo } from "@/components/demo";
24
import { Shell } from "@/components/shell";
35
import ColorPickerDemo from "@/registry/default/examples/color-picker-demo";
4-
import TimelineAlternateDemo from "@/registry/default/examples/timeline-alternate-demo";
5-
import TimelineCustomDotDemo from "@/registry/default/examples/timeline-custom-dot-demo";
6-
import TimelineDemo from "@/registry/default/examples/timeline-demo";
7-
import TimelineHorizontalAlternateDemo from "@/registry/default/examples/timeline-horizontal-alternate-demo";
8-
import TimelineHorizontalDemo from "@/registry/default/examples/timeline-horizontal-demo";
6+
import TimePickerDemo from "@/registry/default/examples/time-picker-demo";
7+
import TimePickerPlaceholderDemo from "@/registry/default/examples/time-picker-placeholder-demo";
8+
import TimePickerSecondsDemo from "@/registry/default/examples/time-picker-seconds-demo";
99

1010
export default function PlaygroundPage() {
1111
return (
1212
<Shell>
1313
<Demo>
14-
<TimelineDemo />
15-
<TimelineCustomDotDemo />
16-
<TimelineHorizontalDemo />
17-
<TimelineAlternateDemo />
18-
<TimelineHorizontalAlternateDemo />
14+
<div className="flex flex-col gap-6">
15+
<input type="time" />
16+
<TimePickerDemo />
17+
<TimePickerSecondsDemo />
18+
<TimePickerPlaceholderDemo />
19+
</div>
1920
<ColorPickerDemo />
2021
</Demo>
2122
</Shell>

0 commit comments

Comments
 (0)