@spear-ai/spectral 1.16.3 → 1.16.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -84,7 +84,7 @@ const SegmentInput = ({ ariaLabel, config, disabled, onLeftFocus, onRightFocus,
84
84
  case "year": return "yyyy";
85
85
  case "hour": return config.max === 23 ? "HH" : "hh";
86
86
  case "minute": return "mm";
87
- case "period": return (periodLabels?.am ?? "am").toLowerCase();
87
+ case "period": return periodLabels?.am ?? "am";
88
88
  default: return "––";
89
89
  }
90
90
  }, [
@@ -94,7 +94,7 @@ const SegmentInput = ({ ariaLabel, config, disabled, onLeftFocus, onRightFocus,
94
94
  ]);
95
95
  const displayValue = useMemo(() => {
96
96
  if (value === void 0) return placeholderText;
97
- if (config.type === "period") return (value === 0 ? periodLabels?.am ?? "am" : periodLabels?.pm ?? "pm").toLowerCase();
97
+ if (config.type === "period") return value === 0 ? periodLabels?.am ?? "am" : periodLabels?.pm ?? "pm";
98
98
  return value.toString().padStart(config.length, "0");
99
99
  }, [
100
100
  value,
@@ -1 +1 @@
1
- {"version":3,"file":"DateTimeDisplayInput.js","names":[],"sources":["../../src/components/DateTimePicker/DateTimeDisplayInput.tsx"],"sourcesContent":["import { Label } from '@components/Label/Label'\nimport { useUncontrolledState } from '@hooks/useUncontrolledState'\nimport { getInputClasses } from '@utils/formFieldUtils'\nimport { cn } from '@utils/twUtils'\nimport { forwardRef, useCallback, useEffect, useId, useMemo, useRef, type ComponentProps, type KeyboardEvent, type ReactNode } from 'react'\nimport { formatSelectPeriodLabel, getLocalizedPeriodLabels, getResolvedLocale, type HourFormat, type PeriodLabels } from './DateTimeUtils'\n\nexport interface DateTimeDisplayInputProps extends Omit<ComponentProps<'div'>, 'onChange' | 'defaultValue'> {\n defaultValue?: Date\n disabled?: boolean\n endIcon?: ReactNode\n hourFormat?: HourFormat\n label?: string\n locale?: string\n onChange?: (date: Date | undefined) => void\n showTime?: boolean\n state?: 'default' | 'disabled' | 'error'\n value?: Date\n // Legacy prop names for backward compatibility\n /** @deprecated Use `value` instead */\n date?: Date\n /** @deprecated Use `onChange` instead */\n onDateChange?: (date: Date | undefined) => void\n}\n\ninterface SegmentConfig {\n length: number\n max: number\n min: number\n type: 'month' | 'day' | 'year' | 'hour' | 'minute' | 'period'\n}\n\nconst SEGMENT_CONFIGS: Record<string, SegmentConfig> = {\n month: { type: 'month', min: 1, max: 12, length: 2 },\n day: { type: 'day', min: 1, max: 31, length: 2 },\n year: { type: 'year', min: 1900, max: 2100, length: 4 },\n hour12: { type: 'hour', min: 1, max: 12, length: 2 },\n hour24: { type: 'hour', min: 0, max: 23, length: 2 },\n minute: { type: 'minute', min: 0, max: 59, length: 2 },\n period: { type: 'period', min: 0, max: 1, length: 2 },\n}\n\n/**\n * Get max days for a given month/year\n */\nconst getMaxDaysInMonth = (month: number, year: number): number => {\n if (month === 2) {\n const isLeapYear = (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0\n return isLeapYear ? 29 : 28\n }\n if ([4, 6, 9, 11].includes(month)) return 30\n return 31\n}\n\n/**\n * A single segment input (month, day, year, hour, minute)\n */\ninterface SegmentInputProps {\n ariaLabel: string\n config: SegmentConfig\n disabled?: boolean\n onLeftFocus?: () => void\n onRightFocus?: () => void\n onValueChange: (value: number) => void\n periodLabels?: PeriodLabels\n value: number | undefined\n}\n\n/**\n * SegmentInput - A single editable segment (month, day, year, hour, minute, period)\n *\n * Uses an uncontrolled input pattern to avoid React re-render issues during typing:\n * - Edit buffer stored in ref (not state) to prevent re-renders\n * - DOM value manipulated directly during typing\n * - Only syncs with React when value is committed (blur, tab, arrow keys)\n */\nconst SegmentInput = ({ ariaLabel, config, disabled, onLeftFocus, onRightFocus, onValueChange, periodLabels, value }: SegmentInputProps) => {\n const inputRef = useRef<HTMLInputElement>(null)\n // Use refs to avoid re-renders during typing - this is the key to making it work\n const editBufferRef = useRef('')\n const isEditingRef = useRef(false)\n\n // Placeholder text for empty segments\n const placeholderText = useMemo(() => {\n switch (config.type) {\n case 'month':\n return 'MM'\n case 'day':\n return 'dd'\n case 'year':\n return 'yyyy'\n case 'hour':\n return config.max === 23 ? 'HH' : 'hh'\n case 'minute':\n return 'mm'\n case 'period':\n return (periodLabels?.am ?? 'am').toLowerCase()\n default:\n return '––'\n }\n }, [config.type, config.max, periodLabels])\n\n // Format display value from the committed value prop\n const displayValue = useMemo(() => {\n if (value === undefined) {\n return placeholderText\n }\n if (config.type === 'period') {\n const label = value === 0 ? (periodLabels?.am ?? 'am') : (periodLabels?.pm ?? 'pm')\n return label.toLowerCase()\n }\n return value.toString().padStart(config.length, '0')\n }, [value, config, periodLabels, placeholderText])\n\n // Sync input value when external value changes (and we're not editing)\n useEffect(() => {\n if (inputRef.current && !isEditingRef.current) {\n inputRef.current.value = displayValue\n }\n }, [displayValue])\n\n // Commit the edit buffer - called on blur, tab, arrow navigation\n const commitEdit = useCallback(() => {\n const buffer = editBufferRef.current\n if (buffer) {\n let numValue = parseInt(buffer, 10)\n if (!isNaN(numValue)) {\n numValue = Math.max(config.min, Math.min(config.max, numValue))\n onValueChange(numValue)\n }\n }\n // Reset edit state\n editBufferRef.current = ''\n isEditingRef.current = false\n // Sync display value after commit\n if (inputRef.current) {\n inputRef.current.value = displayValue\n }\n }, [config.min, config.max, displayValue, onValueChange])\n\n const handleKeyDown = useCallback(\n (e: KeyboardEvent<HTMLInputElement>) => {\n if (disabled) return\n\n // Tab navigation (let it bubble naturally)\n if (e.key === 'Tab') {\n commitEdit()\n return\n }\n\n // Arrow navigation between segments\n if (e.key === 'ArrowRight') {\n e.preventDefault()\n commitEdit()\n onRightFocus?.()\n return\n }\n if (e.key === 'ArrowLeft') {\n e.preventDefault()\n commitEdit()\n onLeftFocus?.()\n return\n }\n\n // Period toggle (AM/PM)\n if (config.type === 'period') {\n e.preventDefault()\n if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {\n onValueChange(value === 0 ? 1 : 0)\n } else if (e.key.toLowerCase() === 'a') {\n onValueChange(0)\n } else if (e.key.toLowerCase() === 'p') {\n onValueChange(1)\n }\n return\n }\n\n // Arrow up/down to increment/decrement\n if (e.key === 'ArrowUp') {\n e.preventDefault()\n editBufferRef.current = ''\n isEditingRef.current = false\n const newValue = value === undefined ? config.min : value >= config.max ? config.min : value + 1\n onValueChange(newValue)\n return\n }\n if (e.key === 'ArrowDown') {\n e.preventDefault()\n editBufferRef.current = ''\n isEditingRef.current = false\n const newValue = value === undefined ? config.max : value <= config.min ? config.max : value - 1\n onValueChange(newValue)\n return\n }\n\n // Home/End for min/max\n if (e.key === 'Home') {\n e.preventDefault()\n editBufferRef.current = ''\n isEditingRef.current = false\n onValueChange(config.min)\n return\n }\n if (e.key === 'End') {\n e.preventDefault()\n editBufferRef.current = ''\n isEditingRef.current = false\n onValueChange(config.max)\n return\n }\n\n // Numeric input - directly manipulate DOM, no React state\n if (e.key >= '0' && e.key <= '9') {\n e.preventDefault()\n const newBuffer = editBufferRef.current + e.key\n const numValue = parseInt(newBuffer, 10)\n\n // For 2-digit fields\n if (config.length === 2) {\n if (newBuffer.length >= 2) {\n // Two digits entered - commit and advance\n const clamped = Math.max(config.min, Math.min(config.max, numValue))\n editBufferRef.current = ''\n isEditingRef.current = false\n onValueChange(clamped)\n onRightFocus?.()\n } else if (numValue * 10 > config.max) {\n // First digit is too large to form a valid two-digit number\n const clamped = Math.max(config.min, Math.min(config.max, numValue))\n editBufferRef.current = ''\n isEditingRef.current = false\n onValueChange(clamped)\n onRightFocus?.()\n } else {\n // First digit could lead to valid values, wait for second digit\n // Update refs and DOM directly - NO state update\n editBufferRef.current = newBuffer\n isEditingRef.current = true\n if (inputRef.current) {\n inputRef.current.value = newBuffer\n }\n }\n } else if (config.length === 4) {\n // Year field - advance after 4 digits\n if (newBuffer.length >= 4) {\n const clamped = Math.max(config.min, Math.min(config.max, numValue))\n editBufferRef.current = ''\n isEditingRef.current = false\n onValueChange(clamped)\n onRightFocus?.()\n } else {\n // Update refs and DOM directly - NO state update\n editBufferRef.current = newBuffer\n isEditingRef.current = true\n if (inputRef.current) {\n inputRef.current.value = newBuffer\n }\n }\n }\n return\n }\n\n if (e.key === 'Backspace') {\n e.preventDefault()\n if (editBufferRef.current) {\n const newBuffer = editBufferRef.current.slice(0, -1)\n editBufferRef.current = newBuffer\n isEditingRef.current = newBuffer.length > 0\n if (inputRef.current) {\n inputRef.current.value = newBuffer || displayValue\n }\n } else if (value !== undefined) {\n editBufferRef.current = ''\n isEditingRef.current = true\n onValueChange(config.min)\n if (inputRef.current) {\n inputRef.current.value = placeholderText\n }\n }\n }\n },\n [disabled, config, value, displayValue, placeholderText, onValueChange, onLeftFocus, onRightFocus, commitEdit],\n )\n\n const handleBlur = useCallback(() => {\n // Only commit if we have a partial edit\n if (editBufferRef.current) {\n commitEdit()\n }\n }, [commitEdit])\n\n const handleFocus = useCallback(() => {\n // Only clear buffer if we're not in the middle of an edit\n if (!isEditingRef.current) {\n editBufferRef.current = ''\n }\n // Move cursor to end\n const input = inputRef.current\n if (input) {\n const len = input.value.length\n input.setSelectionRange(len, len)\n }\n }, [])\n\n // Prevent browser from changing the input value directly\n const handleChange = useCallback(() => {\n // Reset to our controlled value if browser tries to change it\n if (inputRef.current) {\n inputRef.current.value = editBufferRef.current || displayValue\n }\n }, [displayValue])\n\n // Width and padding based on segment type\n const getSegmentStyle = useCallback(() => {\n switch (config.type) {\n case 'year':\n return { width: 46, paddingLeft: 0 }\n case 'period':\n return { width: 28, paddingLeft: 0, marginLeft: 4 }\n case 'month':\n return { width: 28, paddingLeft: 0 }\n case 'day':\n return { width: 28, paddingLeft: 0 }\n case 'hour':\n return { width: 28, paddingLeft: 0 }\n case 'minute':\n return { width: 30, paddingLeft: 0 }\n default:\n return { width: 24, paddingLeft: 0 }\n }\n }, [config.type])\n\n const segmentStyle = useMemo(() => getSegmentStyle(), [getSegmentStyle])\n const isPlaceholder = useMemo(() => value === undefined, [value])\n\n return (\n <input\n aria-label={ariaLabel}\n aria-valuemax={config.max}\n aria-valuemin={config.min}\n aria-valuenow={value}\n className={cn(\n 'rounded inline-flex items-center justify-center text-center tabular-nums',\n 'border-none bg-transparent outline-none',\n 'focus:bg-bg-tertiary focus:text-text-primary focus:ring-1 focus:ring-accent',\n 'hover:bg-bg-secondary',\n disabled && 'cursor-not-allowed opacity-50',\n isPlaceholder && 'text-text-placeholder text-sm',\n )}\n data-segment={config.type}\n defaultValue={displayValue}\n disabled={disabled}\n inputMode='numeric'\n onBlur={handleBlur}\n onChange={handleChange}\n onFocus={handleFocus}\n onKeyDown={handleKeyDown}\n ref={inputRef}\n role='spinbutton'\n style={segmentStyle}\n tabIndex={disabled ? -1 : 0}\n />\n )\n}\n\n/**\n * A segmented date/time input that mimics native date input behavior.\n * Each segment (month, day, year, hour, minute, period) is separately focusable.\n *\n * Supports both controlled and uncontrolled usage:\n * - Controlled: `<DateTimeDisplayInput value={date} onChange={setDate} />`\n * - Uncontrolled: `<DateTimeDisplayInput defaultValue={new Date()} />`\n *\n * @example\n * ```tsx\n * // Controlled usage\n * const [date, setDate] = useState<Date | undefined>(new Date())\n * <DateTimeDisplayInput value={date} onChange={setDate} />\n *\n * // Uncontrolled usage\n * <DateTimeDisplayInput defaultValue={new Date()} />\n *\n * // Legacy props (deprecated, use value/onChange instead)\n * <DateTimeDisplayInput date={date} onDateChange={setDate} />\n * ```\n */\nexport const DateTimeDisplayInput = forwardRef<HTMLDivElement, DateTimeDisplayInputProps>(\n (\n {\n className,\n defaultValue,\n disabled,\n endIcon,\n hourFormat = '12',\n id,\n label,\n locale,\n onChange,\n showTime = true,\n state = 'default',\n value,\n 'aria-labelledby': ariaLabelledBy,\n // Legacy props (deprecated)\n date: legacyDate,\n onDateChange: legacyOnDateChange,\n ...props\n },\n ref,\n ) => {\n const generatedId = useId()\n const displayInputId = id ?? `datetime-display-input-${generatedId}`\n const displayLabelId = `${displayInputId}-label`\n const containerRef = useRef<HTMLDivElement>(null)\n const resolvedAriaLabelledBy = [label ? displayLabelId : undefined, ariaLabelledBy].filter(Boolean).join(' ') || undefined\n\n // Support both new (value/onChange) and legacy (date/onDateChange) prop names\n const effectiveValue = value ?? legacyDate\n const effectiveOnChange = onChange ?? legacyOnDateChange\n\n // Use controllable state to support both controlled and uncontrolled usage\n const [date, setDate] = useUncontrolledState<Date | undefined>({\n defaultValue: defaultValue,\n onChange: effectiveOnChange,\n value: effectiveValue,\n })\n\n // Resolve locale and get period labels\n const resolvedLocale = useMemo(() => getResolvedLocale(locale), [locale])\n const periodLabels = useMemo(() => getLocalizedPeriodLabels(resolvedLocale), [resolvedLocale])\n\n // Extract values from date\n const values = useMemo(() => {\n if (!date) return { month: undefined, day: undefined, year: undefined, hour: undefined, minute: undefined, period: undefined as 0 | 1 | undefined }\n\n const hours = date.getHours()\n let displayHour: number\n let period: 0 | 1\n\n if (hourFormat === '24') {\n displayHour = hours\n period = 0\n } else {\n period = hours >= 12 ? 1 : 0\n displayHour = hours % 12\n if (displayHour === 0) displayHour = 12\n }\n\n return {\n month: date.getMonth() + 1,\n day: date.getDate(),\n year: date.getFullYear(),\n hour: displayHour,\n minute: date.getMinutes(),\n period,\n }\n }, [date, hourFormat])\n\n // Check if date is complete (all date parts filled in)\n // Time inputs should be disabled until a valid date is entered\n const isDateComplete = values.month !== undefined && values.day !== undefined && values.year !== undefined\n\n // Build the date from segment values\n const updateDate = useCallback(\n (updates: Partial<typeof values>) => {\n const newValues = { ...values, ...updates }\n\n // If all date parts are undefined, clear the date\n if (newValues.month === undefined && newValues.day === undefined && newValues.year === undefined) {\n setDate(undefined)\n return\n }\n\n // Use current date as base for any undefined values\n const now = new Date()\n const month = (newValues.month ?? now.getMonth() + 1) - 1\n const day = newValues.day ?? now.getDate()\n const year = newValues.year ?? now.getFullYear()\n\n // Clamp day to max days in month\n const maxDays = getMaxDaysInMonth(month + 1, year)\n const clampedDay = Math.min(day, maxDays)\n\n // Check if date just became complete (was incomplete before, complete now)\n const wasDateComplete = values.month !== undefined && values.day !== undefined && values.year !== undefined\n const isNowDateComplete = newValues.month !== undefined && newValues.day !== undefined && newValues.year !== undefined\n const dateJustCompleted = !wasDateComplete && isNowDateComplete\n\n let hours: number\n if (hourFormat === '24') {\n // Default to 0:00 (midnight) when date first completed in 24-hour format\n hours = dateJustCompleted ? 0 : (newValues.hour ?? 0)\n } else {\n // Default to 12:00 PM when date first completed in 12-hour format\n const defaultHour = dateJustCompleted ? 12 : (newValues.hour ?? 12)\n const defaultPeriod = dateJustCompleted ? 1 : (newValues.period ?? 0) // PM by default\n const hour12 = defaultHour\n const period = defaultPeriod\n if (hour12 === 12) {\n hours = period === 0 ? 0 : 12\n } else {\n hours = period === 0 ? hour12 : hour12 + 12\n }\n }\n\n const minutes = dateJustCompleted ? 0 : (newValues.minute ?? 0)\n\n const newDate = new Date(year, month, clampedDay, hours, minutes, 0, 0)\n setDate(newDate)\n },\n [values, hourFormat, setDate],\n )\n\n // Define segments based on format\n const segments = useMemo(() => {\n const dateSegments = [\n { key: 'month', config: SEGMENT_CONFIGS.month, ariaLabel: 'Month' },\n { key: 'day', config: SEGMENT_CONFIGS.day, ariaLabel: 'Day' },\n { key: 'year', config: SEGMENT_CONFIGS.year, ariaLabel: 'Year' },\n ]\n\n if (!showTime) return dateSegments\n\n const timeSegments =\n hourFormat === '24'\n ? [\n { key: 'hour', config: SEGMENT_CONFIGS.hour24, ariaLabel: 'Hours' },\n { key: 'minute', config: SEGMENT_CONFIGS.minute, ariaLabel: 'Minutes' },\n ]\n : [\n { key: 'hour', config: SEGMENT_CONFIGS.hour12, ariaLabel: 'Hours' },\n { key: 'minute', config: SEGMENT_CONFIGS.minute, ariaLabel: 'Minutes' },\n { key: 'period', config: SEGMENT_CONFIGS.period, ariaLabel: formatSelectPeriodLabel(periodLabels, 'Select {am} or {pm}') },\n ]\n\n return [...dateSegments, ...timeSegments]\n }, [showTime, hourFormat, periodLabels])\n\n // Focus helpers\n const focusSegment = useCallback((index: number) => {\n const segment = containerRef.current?.querySelectorAll(`[role='spinbutton']`)[index] as HTMLElement\n segment?.focus()\n }, [])\n\n const inputClasses = getInputClasses(state)\n\n return (\n <div className='gap-1.5 flex flex-col'>\n {label && (\n <Label className='text-sm font-medium text-text-primary' id={displayLabelId}>\n {label}\n </Label>\n )}\n <div className={cn(inputClasses, 'relative', (disabled ?? state === 'disabled') && 'cursor-not-allowed', className)} data-slot='datetime-display-input' id={displayInputId} ref={ref ?? containerRef} aria-labelledby={resolvedAriaLabelledBy} {...props}>\n <div className='flex items-center' ref={containerRef}>\n {segments.map((segment, index) => {\n const isDateSegment = ['month', 'day', 'year'].includes(segment.key)\n const isTimeSegment = ['hour', 'minute', 'period'].includes(segment.key)\n const isLastDateSegment = segment.key === 'year'\n const isFirstTimeSegment = segment.key === 'hour'\n\n // Disable time segments until date is complete\n const isSegmentDisabled = disabled ?? (isTimeSegment && !isDateComplete)\n\n return (\n <span key={segment.key} className='flex items-center'>\n {/* Add comma and space before time section */}\n {isFirstTimeSegment && (\n <span aria-hidden='true' className='text-text-secondary select-none'>\n ,&nbsp;\n </span>\n )}\n\n <SegmentInput\n ariaLabel={segment.ariaLabel}\n config={segment.config}\n disabled={isSegmentDisabled}\n onLeftFocus={() => index > 0 && focusSegment(index - 1)}\n onRightFocus={() => index < segments.length - 1 && focusSegment(index + 1)}\n onValueChange={(val) => updateDate({ [segment.key]: val })}\n periodLabels={periodLabels}\n value={values[segment.key as keyof typeof values]}\n />\n\n {/* Date separators */}\n {isDateSegment && !isLastDateSegment && (\n <span aria-hidden='true' className='text-text-secondary select-none'>\n /\n </span>\n )}\n\n {/* Time separator - colon between hour and minute */}\n {isFirstTimeSegment && (\n <span aria-hidden='true' className='text-inherit select-none'>\n :\n </span>\n )}\n </span>\n )\n })}\n </div>\n {endIcon}\n </div>\n </div>\n )\n },\n)\nDateTimeDisplayInput.displayName = 'DateTimeDisplayInput'\n"],"mappings":"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"}
1
+ {"version":3,"file":"DateTimeDisplayInput.js","names":[],"sources":["../../src/components/DateTimePicker/DateTimeDisplayInput.tsx"],"sourcesContent":["import { Label } from '@components/Label/Label'\nimport { useUncontrolledState } from '@hooks/useUncontrolledState'\nimport { getInputClasses } from '@utils/formFieldUtils'\nimport { cn } from '@utils/twUtils'\nimport { forwardRef, useCallback, useEffect, useId, useMemo, useRef, type ComponentProps, type KeyboardEvent, type ReactNode } from 'react'\nimport { formatSelectPeriodLabel, getLocalizedPeriodLabels, getResolvedLocale, type HourFormat, type PeriodLabels } from './DateTimeUtils'\n\nexport interface DateTimeDisplayInputProps extends Omit<ComponentProps<'div'>, 'onChange' | 'defaultValue'> {\n defaultValue?: Date\n disabled?: boolean\n endIcon?: ReactNode\n hourFormat?: HourFormat\n label?: string\n locale?: string\n onChange?: (date: Date | undefined) => void\n showTime?: boolean\n state?: 'default' | 'disabled' | 'error'\n value?: Date\n // Legacy prop names for backward compatibility\n /** @deprecated Use `value` instead */\n date?: Date\n /** @deprecated Use `onChange` instead */\n onDateChange?: (date: Date | undefined) => void\n}\n\ninterface SegmentConfig {\n length: number\n max: number\n min: number\n type: 'month' | 'day' | 'year' | 'hour' | 'minute' | 'period'\n}\n\nconst SEGMENT_CONFIGS: Record<string, SegmentConfig> = {\n month: { type: 'month', min: 1, max: 12, length: 2 },\n day: { type: 'day', min: 1, max: 31, length: 2 },\n year: { type: 'year', min: 1900, max: 2100, length: 4 },\n hour12: { type: 'hour', min: 1, max: 12, length: 2 },\n hour24: { type: 'hour', min: 0, max: 23, length: 2 },\n minute: { type: 'minute', min: 0, max: 59, length: 2 },\n period: { type: 'period', min: 0, max: 1, length: 2 },\n}\n\n/**\n * Get max days for a given month/year\n */\nconst getMaxDaysInMonth = (month: number, year: number): number => {\n if (month === 2) {\n const isLeapYear = (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0\n return isLeapYear ? 29 : 28\n }\n if ([4, 6, 9, 11].includes(month)) return 30\n return 31\n}\n\n/**\n * A single segment input (month, day, year, hour, minute)\n */\ninterface SegmentInputProps {\n ariaLabel: string\n config: SegmentConfig\n disabled?: boolean\n onLeftFocus?: () => void\n onRightFocus?: () => void\n onValueChange: (value: number) => void\n periodLabels?: PeriodLabels\n value: number | undefined\n}\n\n/**\n * SegmentInput - A single editable segment (month, day, year, hour, minute, period)\n *\n * Uses an uncontrolled input pattern to avoid React re-render issues during typing:\n * - Edit buffer stored in ref (not state) to prevent re-renders\n * - DOM value manipulated directly during typing\n * - Only syncs with React when value is committed (blur, tab, arrow keys)\n */\nconst SegmentInput = ({ ariaLabel, config, disabled, onLeftFocus, onRightFocus, onValueChange, periodLabels, value }: SegmentInputProps) => {\n const inputRef = useRef<HTMLInputElement>(null)\n // Use refs to avoid re-renders during typing - this is the key to making it work\n const editBufferRef = useRef('')\n const isEditingRef = useRef(false)\n\n // Placeholder text for empty segments\n const placeholderText = useMemo(() => {\n switch (config.type) {\n case 'month':\n return 'MM'\n case 'day':\n return 'dd'\n case 'year':\n return 'yyyy'\n case 'hour':\n return config.max === 23 ? 'HH' : 'hh'\n case 'minute':\n return 'mm'\n case 'period':\n return periodLabels?.am ?? 'am'\n default:\n return '––'\n }\n }, [config.type, config.max, periodLabels])\n\n // Format display value from the committed value prop\n const displayValue = useMemo(() => {\n if (value === undefined) {\n return placeholderText\n }\n if (config.type === 'period') {\n const label = value === 0 ? (periodLabels?.am ?? 'am') : (periodLabels?.pm ?? 'pm')\n return label\n }\n return value.toString().padStart(config.length, '0')\n }, [value, config, periodLabels, placeholderText])\n\n // Sync input value when external value changes (and we're not editing)\n useEffect(() => {\n if (inputRef.current && !isEditingRef.current) {\n inputRef.current.value = displayValue\n }\n }, [displayValue])\n\n // Commit the edit buffer - called on blur, tab, arrow navigation\n const commitEdit = useCallback(() => {\n const buffer = editBufferRef.current\n if (buffer) {\n let numValue = parseInt(buffer, 10)\n if (!isNaN(numValue)) {\n numValue = Math.max(config.min, Math.min(config.max, numValue))\n onValueChange(numValue)\n }\n }\n // Reset edit state\n editBufferRef.current = ''\n isEditingRef.current = false\n // Sync display value after commit\n if (inputRef.current) {\n inputRef.current.value = displayValue\n }\n }, [config.min, config.max, displayValue, onValueChange])\n\n const handleKeyDown = useCallback(\n (e: KeyboardEvent<HTMLInputElement>) => {\n if (disabled) return\n\n // Tab navigation (let it bubble naturally)\n if (e.key === 'Tab') {\n commitEdit()\n return\n }\n\n // Arrow navigation between segments\n if (e.key === 'ArrowRight') {\n e.preventDefault()\n commitEdit()\n onRightFocus?.()\n return\n }\n if (e.key === 'ArrowLeft') {\n e.preventDefault()\n commitEdit()\n onLeftFocus?.()\n return\n }\n\n // Period toggle (am/pm)\n if (config.type === 'period') {\n e.preventDefault()\n if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {\n onValueChange(value === 0 ? 1 : 0)\n } else if (e.key.toLowerCase() === 'a') {\n onValueChange(0)\n } else if (e.key.toLowerCase() === 'p') {\n onValueChange(1)\n }\n return\n }\n\n // Arrow up/down to increment/decrement\n if (e.key === 'ArrowUp') {\n e.preventDefault()\n editBufferRef.current = ''\n isEditingRef.current = false\n const newValue = value === undefined ? config.min : value >= config.max ? config.min : value + 1\n onValueChange(newValue)\n return\n }\n if (e.key === 'ArrowDown') {\n e.preventDefault()\n editBufferRef.current = ''\n isEditingRef.current = false\n const newValue = value === undefined ? config.max : value <= config.min ? config.max : value - 1\n onValueChange(newValue)\n return\n }\n\n // Home/End for min/max\n if (e.key === 'Home') {\n e.preventDefault()\n editBufferRef.current = ''\n isEditingRef.current = false\n onValueChange(config.min)\n return\n }\n if (e.key === 'End') {\n e.preventDefault()\n editBufferRef.current = ''\n isEditingRef.current = false\n onValueChange(config.max)\n return\n }\n\n // Numeric input - directly manipulate DOM, no React state\n if (e.key >= '0' && e.key <= '9') {\n e.preventDefault()\n const newBuffer = editBufferRef.current + e.key\n const numValue = parseInt(newBuffer, 10)\n\n // For 2-digit fields\n if (config.length === 2) {\n if (newBuffer.length >= 2) {\n // Two digits entered - commit and advance\n const clamped = Math.max(config.min, Math.min(config.max, numValue))\n editBufferRef.current = ''\n isEditingRef.current = false\n onValueChange(clamped)\n onRightFocus?.()\n } else if (numValue * 10 > config.max) {\n // First digit is too large to form a valid two-digit number\n const clamped = Math.max(config.min, Math.min(config.max, numValue))\n editBufferRef.current = ''\n isEditingRef.current = false\n onValueChange(clamped)\n onRightFocus?.()\n } else {\n // First digit could lead to valid values, wait for second digit\n // Update refs and DOM directly - NO state update\n editBufferRef.current = newBuffer\n isEditingRef.current = true\n if (inputRef.current) {\n inputRef.current.value = newBuffer\n }\n }\n } else if (config.length === 4) {\n // Year field - advance after 4 digits\n if (newBuffer.length >= 4) {\n const clamped = Math.max(config.min, Math.min(config.max, numValue))\n editBufferRef.current = ''\n isEditingRef.current = false\n onValueChange(clamped)\n onRightFocus?.()\n } else {\n // Update refs and DOM directly - NO state update\n editBufferRef.current = newBuffer\n isEditingRef.current = true\n if (inputRef.current) {\n inputRef.current.value = newBuffer\n }\n }\n }\n return\n }\n\n if (e.key === 'Backspace') {\n e.preventDefault()\n if (editBufferRef.current) {\n const newBuffer = editBufferRef.current.slice(0, -1)\n editBufferRef.current = newBuffer\n isEditingRef.current = newBuffer.length > 0\n if (inputRef.current) {\n inputRef.current.value = newBuffer || displayValue\n }\n } else if (value !== undefined) {\n editBufferRef.current = ''\n isEditingRef.current = true\n onValueChange(config.min)\n if (inputRef.current) {\n inputRef.current.value = placeholderText\n }\n }\n }\n },\n [disabled, config, value, displayValue, placeholderText, onValueChange, onLeftFocus, onRightFocus, commitEdit],\n )\n\n const handleBlur = useCallback(() => {\n // Only commit if we have a partial edit\n if (editBufferRef.current) {\n commitEdit()\n }\n }, [commitEdit])\n\n const handleFocus = useCallback(() => {\n // Only clear buffer if we're not in the middle of an edit\n if (!isEditingRef.current) {\n editBufferRef.current = ''\n }\n // Move cursor to end\n const input = inputRef.current\n if (input) {\n const len = input.value.length\n input.setSelectionRange(len, len)\n }\n }, [])\n\n // Prevent browser from changing the input value directly\n const handleChange = useCallback(() => {\n // Reset to our controlled value if browser tries to change it\n if (inputRef.current) {\n inputRef.current.value = editBufferRef.current || displayValue\n }\n }, [displayValue])\n\n // Width and padding based on segment type\n const getSegmentStyle = useCallback(() => {\n switch (config.type) {\n case 'year':\n return { width: 46, paddingLeft: 0 }\n case 'period':\n return { width: 28, paddingLeft: 0, marginLeft: 4 }\n case 'month':\n return { width: 28, paddingLeft: 0 }\n case 'day':\n return { width: 28, paddingLeft: 0 }\n case 'hour':\n return { width: 28, paddingLeft: 0 }\n case 'minute':\n return { width: 30, paddingLeft: 0 }\n default:\n return { width: 24, paddingLeft: 0 }\n }\n }, [config.type])\n\n const segmentStyle = useMemo(() => getSegmentStyle(), [getSegmentStyle])\n const isPlaceholder = useMemo(() => value === undefined, [value])\n\n return (\n <input\n aria-label={ariaLabel}\n aria-valuemax={config.max}\n aria-valuemin={config.min}\n aria-valuenow={value}\n className={cn(\n 'rounded inline-flex items-center justify-center text-center tabular-nums',\n 'border-none bg-transparent outline-none',\n 'focus:bg-bg-tertiary focus:text-text-primary focus:ring-1 focus:ring-accent',\n 'hover:bg-bg-secondary',\n disabled && 'cursor-not-allowed opacity-50',\n isPlaceholder && 'text-text-placeholder text-sm',\n )}\n data-segment={config.type}\n defaultValue={displayValue}\n disabled={disabled}\n inputMode='numeric'\n onBlur={handleBlur}\n onChange={handleChange}\n onFocus={handleFocus}\n onKeyDown={handleKeyDown}\n ref={inputRef}\n role='spinbutton'\n style={segmentStyle}\n tabIndex={disabled ? -1 : 0}\n />\n )\n}\n\n/**\n * A segmented date/time input that mimics native date input behavior.\n * Each segment (month, day, year, hour, minute, period) is separately focusable.\n *\n * Supports both controlled and uncontrolled usage:\n * - Controlled: `<DateTimeDisplayInput value={date} onChange={setDate} />`\n * - Uncontrolled: `<DateTimeDisplayInput defaultValue={new Date()} />`\n *\n * @example\n * ```tsx\n * // Controlled usage\n * const [date, setDate] = useState<Date | undefined>(new Date())\n * <DateTimeDisplayInput value={date} onChange={setDate} />\n *\n * // Uncontrolled usage\n * <DateTimeDisplayInput defaultValue={new Date()} />\n *\n * // Legacy props (deprecated, use value/onChange instead)\n * <DateTimeDisplayInput date={date} onDateChange={setDate} />\n * ```\n */\nexport const DateTimeDisplayInput = forwardRef<HTMLDivElement, DateTimeDisplayInputProps>(\n (\n {\n className,\n defaultValue,\n disabled,\n endIcon,\n hourFormat = '12',\n id,\n label,\n locale,\n onChange,\n showTime = true,\n state = 'default',\n value,\n 'aria-labelledby': ariaLabelledBy,\n // Legacy props (deprecated)\n date: legacyDate,\n onDateChange: legacyOnDateChange,\n ...props\n },\n ref,\n ) => {\n const generatedId = useId()\n const displayInputId = id ?? `datetime-display-input-${generatedId}`\n const displayLabelId = `${displayInputId}-label`\n const containerRef = useRef<HTMLDivElement>(null)\n const resolvedAriaLabelledBy = [label ? displayLabelId : undefined, ariaLabelledBy].filter(Boolean).join(' ') || undefined\n\n // Support both new (value/onChange) and legacy (date/onDateChange) prop names\n const effectiveValue = value ?? legacyDate\n const effectiveOnChange = onChange ?? legacyOnDateChange\n\n // Use controllable state to support both controlled and uncontrolled usage\n const [date, setDate] = useUncontrolledState<Date | undefined>({\n defaultValue: defaultValue,\n onChange: effectiveOnChange,\n value: effectiveValue,\n })\n\n // Resolve locale and get period labels\n const resolvedLocale = useMemo(() => getResolvedLocale(locale), [locale])\n const periodLabels = useMemo(() => getLocalizedPeriodLabels(resolvedLocale), [resolvedLocale])\n\n // Extract values from date\n const values = useMemo(() => {\n if (!date) return { month: undefined, day: undefined, year: undefined, hour: undefined, minute: undefined, period: undefined as 0 | 1 | undefined }\n\n const hours = date.getHours()\n let displayHour: number\n let period: 0 | 1\n\n if (hourFormat === '24') {\n displayHour = hours\n period = 0\n } else {\n period = hours >= 12 ? 1 : 0\n displayHour = hours % 12\n if (displayHour === 0) displayHour = 12\n }\n\n return {\n month: date.getMonth() + 1,\n day: date.getDate(),\n year: date.getFullYear(),\n hour: displayHour,\n minute: date.getMinutes(),\n period,\n }\n }, [date, hourFormat])\n\n // Check if date is complete (all date parts filled in)\n // Time inputs should be disabled until a valid date is entered\n const isDateComplete = values.month !== undefined && values.day !== undefined && values.year !== undefined\n\n // Build the date from segment values\n const updateDate = useCallback(\n (updates: Partial<typeof values>) => {\n const newValues = { ...values, ...updates }\n\n // If all date parts are undefined, clear the date\n if (newValues.month === undefined && newValues.day === undefined && newValues.year === undefined) {\n setDate(undefined)\n return\n }\n\n // Use current date as base for any undefined values\n const now = new Date()\n const month = (newValues.month ?? now.getMonth() + 1) - 1\n const day = newValues.day ?? now.getDate()\n const year = newValues.year ?? now.getFullYear()\n\n // Clamp day to max days in month\n const maxDays = getMaxDaysInMonth(month + 1, year)\n const clampedDay = Math.min(day, maxDays)\n\n // Check if date just became complete (was incomplete before, complete now)\n const wasDateComplete = values.month !== undefined && values.day !== undefined && values.year !== undefined\n const isNowDateComplete = newValues.month !== undefined && newValues.day !== undefined && newValues.year !== undefined\n const dateJustCompleted = !wasDateComplete && isNowDateComplete\n\n let hours: number\n if (hourFormat === '24') {\n // Default to 0:00 (midnight) when date first completed in 24-hour format\n hours = dateJustCompleted ? 0 : (newValues.hour ?? 0)\n } else {\n // Default to 12:00 pm when date first completed in 12-hour format\n const defaultHour = dateJustCompleted ? 12 : (newValues.hour ?? 12)\n const defaultPeriod = dateJustCompleted ? 1 : (newValues.period ?? 0) // pm by default\n const hour12 = defaultHour\n const period = defaultPeriod\n if (hour12 === 12) {\n hours = period === 0 ? 0 : 12\n } else {\n hours = period === 0 ? hour12 : hour12 + 12\n }\n }\n\n const minutes = dateJustCompleted ? 0 : (newValues.minute ?? 0)\n\n const newDate = new Date(year, month, clampedDay, hours, minutes, 0, 0)\n setDate(newDate)\n },\n [values, hourFormat, setDate],\n )\n\n // Define segments based on format\n const segments = useMemo(() => {\n const dateSegments = [\n { key: 'month', config: SEGMENT_CONFIGS.month, ariaLabel: 'Month' },\n { key: 'day', config: SEGMENT_CONFIGS.day, ariaLabel: 'Day' },\n { key: 'year', config: SEGMENT_CONFIGS.year, ariaLabel: 'Year' },\n ]\n\n if (!showTime) return dateSegments\n\n const timeSegments =\n hourFormat === '24'\n ? [\n { key: 'hour', config: SEGMENT_CONFIGS.hour24, ariaLabel: 'Hours' },\n { key: 'minute', config: SEGMENT_CONFIGS.minute, ariaLabel: 'Minutes' },\n ]\n : [\n { key: 'hour', config: SEGMENT_CONFIGS.hour12, ariaLabel: 'Hours' },\n { key: 'minute', config: SEGMENT_CONFIGS.minute, ariaLabel: 'Minutes' },\n { key: 'period', config: SEGMENT_CONFIGS.period, ariaLabel: formatSelectPeriodLabel(periodLabels, 'Select {am} or {pm}') },\n ]\n\n return [...dateSegments, ...timeSegments]\n }, [showTime, hourFormat, periodLabels])\n\n // Focus helpers\n const focusSegment = useCallback((index: number) => {\n const segment = containerRef.current?.querySelectorAll(`[role='spinbutton']`)[index] as HTMLElement\n segment?.focus()\n }, [])\n\n const inputClasses = getInputClasses(state)\n\n return (\n <div className='gap-1.5 flex flex-col'>\n {label && (\n <Label className='text-sm font-medium text-text-primary' id={displayLabelId}>\n {label}\n </Label>\n )}\n <div className={cn(inputClasses, 'relative', (disabled ?? state === 'disabled') && 'cursor-not-allowed', className)} data-slot='datetime-display-input' id={displayInputId} ref={ref ?? containerRef} aria-labelledby={resolvedAriaLabelledBy} {...props}>\n <div className='flex items-center' ref={containerRef}>\n {segments.map((segment, index) => {\n const isDateSegment = ['month', 'day', 'year'].includes(segment.key)\n const isTimeSegment = ['hour', 'minute', 'period'].includes(segment.key)\n const isLastDateSegment = segment.key === 'year'\n const isFirstTimeSegment = segment.key === 'hour'\n\n // Disable time segments until date is complete\n const isSegmentDisabled = disabled ?? (isTimeSegment && !isDateComplete)\n\n return (\n <span key={segment.key} className='flex items-center'>\n {/* Add comma and space before time section */}\n {isFirstTimeSegment && (\n <span aria-hidden='true' className='text-text-secondary select-none'>\n ,&nbsp;\n </span>\n )}\n\n <SegmentInput\n ariaLabel={segment.ariaLabel}\n config={segment.config}\n disabled={isSegmentDisabled}\n onLeftFocus={() => index > 0 && focusSegment(index - 1)}\n onRightFocus={() => index < segments.length - 1 && focusSegment(index + 1)}\n onValueChange={(val) => updateDate({ [segment.key]: val })}\n periodLabels={periodLabels}\n value={values[segment.key as keyof typeof values]}\n />\n\n {/* Date separators */}\n {isDateSegment && !isLastDateSegment && (\n <span aria-hidden='true' className='text-text-secondary select-none'>\n /\n </span>\n )}\n\n {/* Time separator - colon between hour and minute */}\n {isFirstTimeSegment && (\n <span aria-hidden='true' className='text-inherit select-none'>\n :\n </span>\n )}\n </span>\n )\n })}\n </div>\n {endIcon}\n </div>\n </div>\n )\n },\n)\nDateTimeDisplayInput.displayName = 'DateTimeDisplayInput'\n"],"mappings":"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"}
@@ -8,8 +8,8 @@ declare const TimePickerTypeSchema: z.ZodEnum<{
8
8
  minutes: "minutes";
9
9
  }>;
10
10
  declare const PeriodSchema: z.ZodEnum<{
11
- AM: "AM";
12
- PM: "PM";
11
+ am: "am";
12
+ pm: "pm";
13
13
  }>;
14
14
  declare const HourFormatSchema: z.ZodEnum<{
15
15
  12: "12";
@@ -26,8 +26,8 @@ declare const DateTimeConfigSchema: z.ZodObject<{
26
26
  24: "24";
27
27
  }>>;
28
28
  period: z.ZodOptional<z.ZodEnum<{
29
- AM: "AM";
30
- PM: "PM";
29
+ am: "am";
30
+ pm: "pm";
31
31
  }>>;
32
32
  }, z.core.$strip>;
33
33
  type TimePickerType = z.infer<typeof TimePickerTypeSchema>;
@@ -38,7 +38,7 @@ interface TimePickerConfig {
38
38
  hourFormat: HourFormat;
39
39
  step?: number;
40
40
  }
41
- /** Localized period labels (AM/PM) */
41
+ /** Localized period labels (am/pm) */
42
42
  interface PeriodLabels {
43
43
  am: string;
44
44
  pm: string;
@@ -58,7 +58,7 @@ interface TimePickerTranslations {
58
58
  /**
59
59
  * ARIA label template for period select.
60
60
  * Use {am} and {pm} as placeholders for localized period names.
61
- * Example: 'Select {am} or {pm}' becomes 'Select AM or PM' in English
61
+ * Example: 'Select {am} or {pm}' becomes 'Select am or pm' in English
62
62
  */
63
63
  selectPeriodTemplate: string;
64
64
  /** Generic time value label */
@@ -100,16 +100,16 @@ declare const TIME_BOUNDS: {
100
100
  declare const getResolvedLocale: (locale?: string) => string;
101
101
  /**
102
102
  * Detects whether a locale uses 12-hour or 24-hour time format.
103
- * Uses Intl.DateTimeFormat to check if the locale produces AM/PM markers.
103
+ * Uses Intl.DateTimeFormat to check if the locale produces am/pm markers.
104
104
  */
105
105
  declare const detectHourFormat: (locale: string) => HourFormat;
106
106
  /**
107
- * Gets localized AM/PM labels using Intl.DateTimeFormat.
107
+ * Gets localized am/pm labels using Intl.DateTimeFormat.
108
108
  * Returns the locale-specific strings for morning and afternoon periods.
109
109
  */
110
110
  declare const getLocalizedPeriodLabels: (locale: string) => PeriodLabels;
111
111
  /**
112
- * Gets the localized label for a period value ('AM' or 'PM').
112
+ * Gets the localized label for a period value ('am' or 'pm').
113
113
  */
114
114
  declare const getLocalizedPeriodLabel: (period: Period, labels: PeriodLabels) => string;
115
115
  /**
@@ -163,8 +163,8 @@ declare const getDateByType: (date: Date, type: TimePickerType) => string;
163
163
  declare const getArrowByType: (step: number, type: TimePickerType, value: string) => string;
164
164
  /**
165
165
  * Converts 12-hour format to 24-hour format
166
- * 12:00 PM → 12:00 (noon)
167
- * 12:00 AM → 00:00 (midnight)
166
+ * 12:00 pm → 12:00 (noon)
167
+ * 12:00 am → 00:00 (midnight)
168
168
  */
169
169
  declare const convert12HourTo24Hour: (hour: number, period: Period) => number;
170
170
  /**
@@ -173,7 +173,7 @@ declare const convert12HourTo24Hour: (hour: number, period: Period) => number;
173
173
  */
174
174
  declare const display12HourValue: (hours: number) => string;
175
175
  /**
176
- * Gets the period (AM/PM) from a 24-hour value
176
+ * Gets the period (am/pm) from a 24-hour value
177
177
  */
178
178
  declare const getPeriodFromHours: (hours: number) => Period;
179
179
  declare const getAriaValueNow: (date: Date, type: TimePickerType) => number;
@@ -1 +1 @@
1
- {"version":3,"file":"DateTimeUtils.d.ts","names":[],"sources":["../../src/components/DateTimePicker/DateTimeUtils.ts"],"mappings":"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"}
1
+ {"version":3,"file":"DateTimeUtils.d.ts","names":[],"sources":["../../src/components/DateTimePicker/DateTimeUtils.ts"],"mappings":"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"}
@@ -7,7 +7,7 @@ const TimePickerTypeSchema = z.enum([
7
7
  "hours",
8
8
  "12hours"
9
9
  ]);
10
- const PeriodSchema = z.enum(["AM", "PM"]);
10
+ const PeriodSchema = z.enum(["am", "pm"]);
11
11
  const HourFormatSchema = z.enum(["12", "24"]);
12
12
  const TimeValueSchema = z.object({
13
13
  hours: z.number().min(0).max(23),
@@ -18,6 +18,10 @@ const DateTimeConfigSchema = z.object({
18
18
  hourFormat: HourFormatSchema.default("12"),
19
19
  period: PeriodSchema.optional()
20
20
  });
21
+ const normalizePeriodLabel = (label, locale, fallback) => {
22
+ if (!label) return fallback;
23
+ return label.toLocaleLowerCase(locale);
24
+ };
21
25
  /** Default English translations with template placeholders */
22
26
  const DEFAULT_TRANSLATIONS = {
23
27
  hours12: "Hours (1-12)",
@@ -70,7 +74,7 @@ const getResolvedLocale = (locale) => {
70
74
  };
71
75
  /**
72
76
  * Detects whether a locale uses 12-hour or 24-hour time format.
73
- * Uses Intl.DateTimeFormat to check if the locale produces AM/PM markers.
77
+ * Uses Intl.DateTimeFormat to check if the locale produces am/pm markers.
74
78
  */
75
79
  const detectHourFormat = (locale) => {
76
80
  try {
@@ -83,7 +87,7 @@ const detectHourFormat = (locale) => {
83
87
  }
84
88
  };
85
89
  /**
86
- * Gets localized AM/PM labels using Intl.DateTimeFormat.
90
+ * Gets localized am/pm labels using Intl.DateTimeFormat.
87
91
  * Returns the locale-specific strings for morning and afternoon periods.
88
92
  */
89
93
  const getLocalizedPeriodLabels = (locale) => {
@@ -95,21 +99,21 @@ const getLocalizedPeriodLabels = (locale) => {
95
99
  const amPart = formatter.formatToParts(new Date(2e3, 0, 1, 0, 0, 0)).find((part) => part.type === "dayPeriod");
96
100
  const pmPart = formatter.formatToParts(new Date(2e3, 0, 1, 12, 0, 0)).find((part) => part.type === "dayPeriod");
97
101
  return {
98
- am: amPart?.value ?? "AM",
99
- pm: pmPart?.value ?? "PM"
102
+ am: normalizePeriodLabel(amPart?.value, locale, "am"),
103
+ pm: normalizePeriodLabel(pmPart?.value, locale, "pm")
100
104
  };
101
105
  } catch {
102
106
  return {
103
- am: "AM",
104
- pm: "PM"
107
+ am: "am",
108
+ pm: "pm"
105
109
  };
106
110
  }
107
111
  };
108
112
  /**
109
- * Gets the localized label for a period value ('AM' or 'PM').
113
+ * Gets the localized label for a period value ('am' or 'pm').
110
114
  */
111
115
  const getLocalizedPeriodLabel = (period, labels) => {
112
- return period === "AM" ? labels.am : labels.pm;
116
+ return period === "am" ? labels.am : labels.pm;
113
117
  };
114
118
  /**
115
119
  * Formats a number using Intl.NumberFormat for the given locale.
@@ -237,11 +241,11 @@ const getArrowByType = (step, type, value) => {
237
241
  };
238
242
  /**
239
243
  * Converts 12-hour format to 24-hour format
240
- * 12:00 PM → 12:00 (noon)
241
- * 12:00 AM → 00:00 (midnight)
244
+ * 12:00 pm → 12:00 (noon)
245
+ * 12:00 am → 00:00 (midnight)
242
246
  */
243
247
  const convert12HourTo24Hour = (hour, period) => {
244
- if (period === "PM") return hour === 12 ? 12 : hour + 12;
248
+ if (period === "pm") return hour === 12 ? 12 : hour + 12;
245
249
  return hour === 12 ? 0 : hour;
246
250
  };
247
251
  /**
@@ -253,10 +257,10 @@ const display12HourValue = (hours) => {
253
257
  return (hours % 12).toString().padStart(2, "0");
254
258
  };
255
259
  /**
256
- * Gets the period (AM/PM) from a 24-hour value
260
+ * Gets the period (am/pm) from a 24-hour value
257
261
  */
258
262
  const getPeriodFromHours = (hours) => {
259
- return hours >= 12 ? "PM" : "AM";
263
+ return hours >= 12 ? "pm" : "am";
260
264
  };
261
265
  const getAriaValueNow = (date, type) => {
262
266
  switch (type) {
@@ -1 +1 @@
1
- {"version":3,"file":"DateTimeUtils.js","names":[],"sources":["../../src/components/DateTimePicker/DateTimeUtils.ts"],"sourcesContent":["import { z } from 'zod'\n\n// ============================================================================\n// Zod Schemas\n// ============================================================================\n\nexport const TimePickerTypeSchema = z.enum(['minutes', 'hours', '12hours'])\nexport const PeriodSchema = z.enum(['AM', 'PM'])\nexport const HourFormatSchema = z.enum(['12', '24'])\n\nexport const TimeValueSchema = z.object({\n hours: z.number().min(0).max(23),\n minutes: z.number().min(0).max(59),\n})\n\nexport const DateTimeConfigSchema = z.object({\n date: z.date().optional(),\n hourFormat: HourFormatSchema.default('12'),\n period: PeriodSchema.optional(),\n})\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type TimePickerType = z.infer<typeof TimePickerTypeSchema>\nexport type Period = z.infer<typeof PeriodSchema>\nexport type HourFormat = z.infer<typeof HourFormatSchema>\nexport type TimeValue = z.infer<typeof TimeValueSchema>\n\nexport interface TimePickerConfig {\n hourFormat: HourFormat\n step?: number\n}\n\n/** Localized period labels (AM/PM) */\nexport interface PeriodLabels {\n am: string\n pm: string\n}\n\n/** Translation strings for time picker ARIA and labels */\nexport interface TimePickerTranslations {\n /** ARIA label for the time picker group in 12-hour format */\n timePicker12Hour: string\n /** ARIA label for the time picker group in 24-hour format */\n timePicker24Hour: string\n /** ARIA label for hours input in 12-hour format */\n hours12: string\n /** ARIA label for hours input in 24-hour format */\n hours24: string\n /** ARIA label for minutes input */\n minutes: string\n /**\n * ARIA label template for period select.\n * Use {am} and {pm} as placeholders for localized period names.\n * Example: 'Select {am} or {pm}' becomes 'Select AM or PM' in English\n */\n selectPeriodTemplate: string\n /** Generic time value label */\n timeValue: string\n}\n\n/** Default English translations with template placeholders */\nexport const DEFAULT_TRANSLATIONS: TimePickerTranslations = {\n hours12: 'Hours (1-12)',\n hours24: 'Hours',\n minutes: 'Minutes',\n selectPeriodTemplate: 'Select {am} or {pm}',\n timePicker12Hour: 'Time picker (12-hour format)',\n timePicker24Hour: 'Time picker (24-hour format)',\n timeValue: 'Time value',\n}\n\n/**\n * Interpolates localized period labels into a template string.\n * Replaces {am} and {pm} placeholders with actual localized values.\n */\nexport const formatSelectPeriodLabel = (labels: PeriodLabels, template: string): string => template.replace('{am}', labels.am).replace('{pm}', labels.pm)\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nexport const PICKER_TYPES = {\n HOURS: 'hours',\n HOURS_12: '12hours',\n MINUTES: 'minutes',\n} as const\n\nexport const TIME_BOUNDS = {\n HOURS_24: { min: 0, max: 23 },\n HOURS_12: { min: 1, max: 12 },\n MINUTES: { min: 0, max: 59 },\n} as const\n\n// ============================================================================\n// Locale Utilities\n// ============================================================================\n\n/**\n * Gets the resolved locale string, with fallback chain:\n * 1. Provided locale prop\n * 2. Intl.DateTimeFormat().resolvedOptions().locale\n * 3. navigator.language (browser only)\n * 4. 'en-US' as final fallback\n */\nexport const getResolvedLocale = (locale?: string): string => {\n if (locale) return locale\n\n try {\n return Intl.DateTimeFormat().resolvedOptions().locale\n } catch {\n // Fallback for environments without Intl\n if (typeof navigator !== 'undefined' && navigator.language) {\n return navigator.language\n }\n return 'en-US'\n }\n}\n\n/**\n * Detects whether a locale uses 12-hour or 24-hour time format.\n * Uses Intl.DateTimeFormat to check if the locale produces AM/PM markers.\n */\nexport const detectHourFormat = (locale: string): HourFormat => {\n try {\n const formatter = new Intl.DateTimeFormat(locale, {\n hour: 'numeric',\n hour12: undefined, // Let the locale decide\n })\n\n // Check if formatToParts includes a dayPeriod (AM/PM marker)\n const parts = formatter.formatToParts(new Date(2000, 0, 1, 13, 0, 0))\n const hasDayPeriod = parts.some((part) => part.type === 'dayPeriod')\n\n return hasDayPeriod ? '12' : '24'\n } catch {\n // Default to 12-hour format if Intl is not available\n return '12'\n }\n}\n\n/**\n * Gets localized AM/PM labels using Intl.DateTimeFormat.\n * Returns the locale-specific strings for morning and afternoon periods.\n */\nexport const getLocalizedPeriodLabels = (locale: string): PeriodLabels => {\n try {\n const formatter = new Intl.DateTimeFormat(locale, {\n hour: 'numeric',\n hour12: true,\n })\n\n // Get AM label from midnight (00:00)\n const amParts = formatter.formatToParts(new Date(2000, 0, 1, 0, 0, 0))\n const amPart = amParts.find((part) => part.type === 'dayPeriod')\n\n // Get PM label from noon (12:00)\n const pmParts = formatter.formatToParts(new Date(2000, 0, 1, 12, 0, 0))\n const pmPart = pmParts.find((part) => part.type === 'dayPeriod')\n\n return {\n am: amPart?.value ?? 'AM',\n pm: pmPart?.value ?? 'PM',\n }\n } catch {\n // Fallback to English defaults\n return { am: 'AM', pm: 'PM' }\n }\n}\n\n/**\n * Gets the localized label for a period value ('AM' or 'PM').\n */\nexport const getLocalizedPeriodLabel = (period: Period, labels: PeriodLabels): string => {\n return period === 'AM' ? labels.am : labels.pm\n}\n\n/**\n * Formats a number using Intl.NumberFormat for the given locale.\n * Pads to 2 digits for time display.\n */\nexport const formatTimeNumber = (value: number, locale: string): string => {\n try {\n // For time display, we always want 2-digit representation\n const formatted = new Intl.NumberFormat(locale, {\n minimumIntegerDigits: 2,\n useGrouping: false,\n }).format(value)\n return formatted\n } catch {\n return value.toString().padStart(2, '0')\n }\n}\n\n// ============================================================================\n// Validation Functions\n// ============================================================================\n\n/**\n * Validates 24-hour format (00-23)\n */\nexport const isValidHour = (value: string): boolean => {\n return /^(0[0-9]|1[0-9]|2[0-3])$/.test(value)\n}\n\n/**\n * Validates 12-hour format (01-12)\n */\nexport const isValid12Hour = (value: string): boolean => {\n return /^(0[1-9]|1[0-2])$/.test(value)\n}\n\n/**\n * Validates minute format (00-59)\n */\nexport const isValidMinute = (value: string): boolean => {\n return /^[0-5][0-9]$/.test(value)\n}\n\n// ============================================================================\n// Number Utilities\n// ============================================================================\n\ninterface GetValidNumberConfig {\n loop?: boolean\n max: number\n min?: number\n}\n\nexport const getValidNumber = (value: string, { max, min = 0, loop = false }: GetValidNumberConfig): string => {\n let numericValue = parseInt(value, 10)\n if (isNaN(numericValue)) return min.toString().padStart(2, '0')\n\n if (loop) {\n if (numericValue > max) numericValue = min\n else if (numericValue < min) numericValue = max\n } else {\n numericValue = Math.min(Math.max(numericValue, min), max)\n }\n\n return numericValue.toString().padStart(2, '0')\n}\n\nexport const getValidHour = (value: string): string => {\n if (isValidHour(value)) return value\n return getValidNumber(value, TIME_BOUNDS.HOURS_24)\n}\n\nexport const getValid12Hour = (value: string): string => {\n if (isValid12Hour(value)) return value\n return getValidNumber(value, TIME_BOUNDS.HOURS_12)\n}\n\nexport const getValidMinute = (value: string): string => {\n if (isValidMinute(value)) return value\n return getValidNumber(value, TIME_BOUNDS.MINUTES)\n}\n\n// ============================================================================\n// Arrow Key Navigation\n// ============================================================================\n\ninterface GetValidArrowNumberConfig {\n max: number\n min: number\n step: number\n}\n\nexport const getValidArrowNumber = (value: string, { min, max, step }: GetValidArrowNumberConfig): string => {\n const numericValue = parseInt(value, 10)\n if (isNaN(numericValue)) return min.toString().padStart(2, '0')\n return getValidNumber(String(numericValue + step), { min, max, loop: true })\n}\n\nexport const getValidArrowHour = (value: string, step: number): string => {\n return getValidArrowNumber(value, { ...TIME_BOUNDS.HOURS_24, step })\n}\n\nexport const getValidArrow12Hour = (value: string, step: number): string => {\n return getValidArrowNumber(value, { ...TIME_BOUNDS.HOURS_12, step })\n}\n\nexport const getValidArrowMinute = (value: string, step: number): string => {\n return getValidArrowNumber(value, { ...TIME_BOUNDS.MINUTES, step })\n}\n\n// ============================================================================\n// Date Setters\n// ============================================================================\n\nexport const setMinutes = (date: Date, value: string): Date => {\n const d = new Date(date.getTime())\n const minutes = getValidMinute(value)\n d.setMinutes(parseInt(minutes, 10))\n return d\n}\n\nexport const setHours = (date: Date, value: string): Date => {\n const d = new Date(date.getTime())\n const hours = getValidHour(value)\n d.setHours(parseInt(hours, 10))\n return d\n}\n\nexport const set12Hours = (date: Date, value: string, period: Period): Date => {\n const d = new Date(date.getTime())\n const hours = parseInt(getValid12Hour(value), 10)\n const convertedHours = convert12HourTo24Hour(hours, period)\n d.setHours(convertedHours)\n return d\n}\n\nexport const setDateByType = (date: Date, type: TimePickerType, value: string, period?: Period): Date => {\n switch (type) {\n case 'minutes':\n return setMinutes(date, value)\n case 'hours':\n return setHours(date, value)\n case '12hours':\n if (!period) return new Date(date.getTime())\n return set12Hours(date, value, period)\n default:\n return new Date(date.getTime())\n }\n}\n\n// ============================================================================\n// Date Getters\n// ============================================================================\n\nexport const getDateByType = (date: Date, type: TimePickerType): string => {\n switch (type) {\n case 'minutes':\n return getValidMinute(String(date.getMinutes()))\n case 'hours':\n return getValidHour(String(date.getHours()))\n case '12hours':\n return display12HourValue(date.getHours())\n default:\n return '00'\n }\n}\n\nexport const getArrowByType = (step: number, type: TimePickerType, value: string): string => {\n switch (type) {\n case 'minutes':\n return getValidArrowMinute(value, step)\n case 'hours':\n return getValidArrowHour(value, step)\n case '12hours':\n return getValidArrow12Hour(value, step)\n default:\n return '00'\n }\n}\n\n// ============================================================================\n// 12/24 Hour Conversion\n// ============================================================================\n\n/**\n * Converts 12-hour format to 24-hour format\n * 12:00 PM → 12:00 (noon)\n * 12:00 AM → 00:00 (midnight)\n */\nexport const convert12HourTo24Hour = (hour: number, period: Period): number => {\n if (period === 'PM') {\n return hour === 12 ? 12 : hour + 12\n }\n // AM\n return hour === 12 ? 0 : hour\n}\n\n/**\n * Converts 24-hour format to 12-hour display value\n * Returns a zero-padded string (e.g., \"01\", \"12\")\n */\nexport const display12HourValue = (hours: number): string => {\n if (hours === 0 || hours === 12) return '12'\n const hour12 = hours % 12\n return hour12.toString().padStart(2, '0')\n}\n\n/**\n * Gets the period (AM/PM) from a 24-hour value\n */\nexport const getPeriodFromHours = (hours: number): Period => {\n return hours >= 12 ? 'PM' : 'AM'\n}\n\n// ============================================================================\n// ARIA Helpers\n// ============================================================================\n\nexport const getAriaValueNow = (date: Date, type: TimePickerType): number => {\n switch (type) {\n case 'minutes':\n return date.getMinutes()\n case 'hours':\n return date.getHours()\n case '12hours': {\n const hours = date.getHours()\n if (hours === 0) return 12\n if (hours > 12) return hours - 12\n return hours\n }\n default:\n return 0\n }\n}\n\nexport const getAriaValueMinMax = (type: TimePickerType): { min: number; max: number } => {\n switch (type) {\n case 'minutes':\n return TIME_BOUNDS.MINUTES\n case 'hours':\n return TIME_BOUNDS.HOURS_24\n case '12hours':\n return TIME_BOUNDS.HOURS_12\n default:\n return { min: 0, max: 0 }\n }\n}\n\nexport const getAriaLabel = (type: TimePickerType, translations: TimePickerTranslations = DEFAULT_TRANSLATIONS): string => {\n switch (type) {\n case 'minutes':\n return translations.minutes\n case 'hours':\n return translations.hours24\n case '12hours':\n return translations.hours12\n default:\n return translations.timeValue\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"DateTimeUtils.js","names":[],"sources":["../../src/components/DateTimePicker/DateTimeUtils.ts"],"sourcesContent":["import { z } from 'zod'\n\n// ============================================================================\n// Zod Schemas\n// ============================================================================\n\nexport const TimePickerTypeSchema = z.enum(['minutes', 'hours', '12hours'])\nexport const PeriodSchema = z.enum(['am', 'pm'])\nexport const HourFormatSchema = z.enum(['12', '24'])\n\nexport const TimeValueSchema = z.object({\n hours: z.number().min(0).max(23),\n minutes: z.number().min(0).max(59),\n})\n\nexport const DateTimeConfigSchema = z.object({\n date: z.date().optional(),\n hourFormat: HourFormatSchema.default('12'),\n period: PeriodSchema.optional(),\n})\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type TimePickerType = z.infer<typeof TimePickerTypeSchema>\nexport type Period = z.infer<typeof PeriodSchema>\nexport type HourFormat = z.infer<typeof HourFormatSchema>\nexport type TimeValue = z.infer<typeof TimeValueSchema>\n\nexport interface TimePickerConfig {\n hourFormat: HourFormat\n step?: number\n}\n\nconst normalizePeriodLabel = (label: string | undefined, locale: string, fallback: 'am' | 'pm'): string => {\n if (!label) return fallback\n return label.toLocaleLowerCase(locale)\n}\n\n/** Localized period labels (am/pm) */\nexport interface PeriodLabels {\n am: string\n pm: string\n}\n\n/** Translation strings for time picker ARIA and labels */\nexport interface TimePickerTranslations {\n /** ARIA label for the time picker group in 12-hour format */\n timePicker12Hour: string\n /** ARIA label for the time picker group in 24-hour format */\n timePicker24Hour: string\n /** ARIA label for hours input in 12-hour format */\n hours12: string\n /** ARIA label for hours input in 24-hour format */\n hours24: string\n /** ARIA label for minutes input */\n minutes: string\n /**\n * ARIA label template for period select.\n * Use {am} and {pm} as placeholders for localized period names.\n * Example: 'Select {am} or {pm}' becomes 'Select am or pm' in English\n */\n selectPeriodTemplate: string\n /** Generic time value label */\n timeValue: string\n}\n\n/** Default English translations with template placeholders */\nexport const DEFAULT_TRANSLATIONS: TimePickerTranslations = {\n hours12: 'Hours (1-12)',\n hours24: 'Hours',\n minutes: 'Minutes',\n selectPeriodTemplate: 'Select {am} or {pm}',\n timePicker12Hour: 'Time picker (12-hour format)',\n timePicker24Hour: 'Time picker (24-hour format)',\n timeValue: 'Time value',\n}\n\n/**\n * Interpolates localized period labels into a template string.\n * Replaces {am} and {pm} placeholders with actual localized values.\n */\nexport const formatSelectPeriodLabel = (labels: PeriodLabels, template: string): string => template.replace('{am}', labels.am).replace('{pm}', labels.pm)\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nexport const PICKER_TYPES = {\n HOURS: 'hours',\n HOURS_12: '12hours',\n MINUTES: 'minutes',\n} as const\n\nexport const TIME_BOUNDS = {\n HOURS_24: { min: 0, max: 23 },\n HOURS_12: { min: 1, max: 12 },\n MINUTES: { min: 0, max: 59 },\n} as const\n\n// ============================================================================\n// Locale Utilities\n// ============================================================================\n\n/**\n * Gets the resolved locale string, with fallback chain:\n * 1. Provided locale prop\n * 2. Intl.DateTimeFormat().resolvedOptions().locale\n * 3. navigator.language (browser only)\n * 4. 'en-US' as final fallback\n */\nexport const getResolvedLocale = (locale?: string): string => {\n if (locale) return locale\n\n try {\n return Intl.DateTimeFormat().resolvedOptions().locale\n } catch {\n // Fallback for environments without Intl\n if (typeof navigator !== 'undefined' && navigator.language) {\n return navigator.language\n }\n return 'en-US'\n }\n}\n\n/**\n * Detects whether a locale uses 12-hour or 24-hour time format.\n * Uses Intl.DateTimeFormat to check if the locale produces am/pm markers.\n */\nexport const detectHourFormat = (locale: string): HourFormat => {\n try {\n const formatter = new Intl.DateTimeFormat(locale, {\n hour: 'numeric',\n hour12: undefined, // Let the locale decide\n })\n\n // Check if formatToParts includes a dayPeriod (am/pm marker)\n const parts = formatter.formatToParts(new Date(2000, 0, 1, 13, 0, 0))\n const hasDayPeriod = parts.some((part) => part.type === 'dayPeriod')\n\n return hasDayPeriod ? '12' : '24'\n } catch {\n // Default to 12-hour format if Intl is not available\n return '12'\n }\n}\n\n/**\n * Gets localized am/pm labels using Intl.DateTimeFormat.\n * Returns the locale-specific strings for morning and afternoon periods.\n */\nexport const getLocalizedPeriodLabels = (locale: string): PeriodLabels => {\n try {\n const formatter = new Intl.DateTimeFormat(locale, {\n hour: 'numeric',\n hour12: true,\n })\n\n // Get am label from midnight (00:00)\n const amParts = formatter.formatToParts(new Date(2000, 0, 1, 0, 0, 0))\n const amPart = amParts.find((part) => part.type === 'dayPeriod')\n\n // Get pm label from noon (12:00)\n const pmParts = formatter.formatToParts(new Date(2000, 0, 1, 12, 0, 0))\n const pmPart = pmParts.find((part) => part.type === 'dayPeriod')\n\n return {\n am: normalizePeriodLabel(amPart?.value, locale, 'am'),\n pm: normalizePeriodLabel(pmPart?.value, locale, 'pm'),\n }\n } catch {\n // Fallback to English defaults\n return { am: 'am', pm: 'pm' }\n }\n}\n\n/**\n * Gets the localized label for a period value ('am' or 'pm').\n */\nexport const getLocalizedPeriodLabel = (period: Period, labels: PeriodLabels): string => {\n return period === 'am' ? labels.am : labels.pm\n}\n\n/**\n * Formats a number using Intl.NumberFormat for the given locale.\n * Pads to 2 digits for time display.\n */\nexport const formatTimeNumber = (value: number, locale: string): string => {\n try {\n // For time display, we always want 2-digit representation\n const formatted = new Intl.NumberFormat(locale, {\n minimumIntegerDigits: 2,\n useGrouping: false,\n }).format(value)\n return formatted\n } catch {\n return value.toString().padStart(2, '0')\n }\n}\n\n// ============================================================================\n// Validation Functions\n// ============================================================================\n\n/**\n * Validates 24-hour format (00-23)\n */\nexport const isValidHour = (value: string): boolean => {\n return /^(0[0-9]|1[0-9]|2[0-3])$/.test(value)\n}\n\n/**\n * Validates 12-hour format (01-12)\n */\nexport const isValid12Hour = (value: string): boolean => {\n return /^(0[1-9]|1[0-2])$/.test(value)\n}\n\n/**\n * Validates minute format (00-59)\n */\nexport const isValidMinute = (value: string): boolean => {\n return /^[0-5][0-9]$/.test(value)\n}\n\n// ============================================================================\n// Number Utilities\n// ============================================================================\n\ninterface GetValidNumberConfig {\n loop?: boolean\n max: number\n min?: number\n}\n\nexport const getValidNumber = (value: string, { max, min = 0, loop = false }: GetValidNumberConfig): string => {\n let numericValue = parseInt(value, 10)\n if (isNaN(numericValue)) return min.toString().padStart(2, '0')\n\n if (loop) {\n if (numericValue > max) numericValue = min\n else if (numericValue < min) numericValue = max\n } else {\n numericValue = Math.min(Math.max(numericValue, min), max)\n }\n\n return numericValue.toString().padStart(2, '0')\n}\n\nexport const getValidHour = (value: string): string => {\n if (isValidHour(value)) return value\n return getValidNumber(value, TIME_BOUNDS.HOURS_24)\n}\n\nexport const getValid12Hour = (value: string): string => {\n if (isValid12Hour(value)) return value\n return getValidNumber(value, TIME_BOUNDS.HOURS_12)\n}\n\nexport const getValidMinute = (value: string): string => {\n if (isValidMinute(value)) return value\n return getValidNumber(value, TIME_BOUNDS.MINUTES)\n}\n\n// ============================================================================\n// Arrow Key Navigation\n// ============================================================================\n\ninterface GetValidArrowNumberConfig {\n max: number\n min: number\n step: number\n}\n\nexport const getValidArrowNumber = (value: string, { min, max, step }: GetValidArrowNumberConfig): string => {\n const numericValue = parseInt(value, 10)\n if (isNaN(numericValue)) return min.toString().padStart(2, '0')\n return getValidNumber(String(numericValue + step), { min, max, loop: true })\n}\n\nexport const getValidArrowHour = (value: string, step: number): string => {\n return getValidArrowNumber(value, { ...TIME_BOUNDS.HOURS_24, step })\n}\n\nexport const getValidArrow12Hour = (value: string, step: number): string => {\n return getValidArrowNumber(value, { ...TIME_BOUNDS.HOURS_12, step })\n}\n\nexport const getValidArrowMinute = (value: string, step: number): string => {\n return getValidArrowNumber(value, { ...TIME_BOUNDS.MINUTES, step })\n}\n\n// ============================================================================\n// Date Setters\n// ============================================================================\n\nexport const setMinutes = (date: Date, value: string): Date => {\n const d = new Date(date.getTime())\n const minutes = getValidMinute(value)\n d.setMinutes(parseInt(minutes, 10))\n return d\n}\n\nexport const setHours = (date: Date, value: string): Date => {\n const d = new Date(date.getTime())\n const hours = getValidHour(value)\n d.setHours(parseInt(hours, 10))\n return d\n}\n\nexport const set12Hours = (date: Date, value: string, period: Period): Date => {\n const d = new Date(date.getTime())\n const hours = parseInt(getValid12Hour(value), 10)\n const convertedHours = convert12HourTo24Hour(hours, period)\n d.setHours(convertedHours)\n return d\n}\n\nexport const setDateByType = (date: Date, type: TimePickerType, value: string, period?: Period): Date => {\n switch (type) {\n case 'minutes':\n return setMinutes(date, value)\n case 'hours':\n return setHours(date, value)\n case '12hours':\n if (!period) return new Date(date.getTime())\n return set12Hours(date, value, period)\n default:\n return new Date(date.getTime())\n }\n}\n\n// ============================================================================\n// Date Getters\n// ============================================================================\n\nexport const getDateByType = (date: Date, type: TimePickerType): string => {\n switch (type) {\n case 'minutes':\n return getValidMinute(String(date.getMinutes()))\n case 'hours':\n return getValidHour(String(date.getHours()))\n case '12hours':\n return display12HourValue(date.getHours())\n default:\n return '00'\n }\n}\n\nexport const getArrowByType = (step: number, type: TimePickerType, value: string): string => {\n switch (type) {\n case 'minutes':\n return getValidArrowMinute(value, step)\n case 'hours':\n return getValidArrowHour(value, step)\n case '12hours':\n return getValidArrow12Hour(value, step)\n default:\n return '00'\n }\n}\n\n// ============================================================================\n// 12/24 Hour Conversion\n// ============================================================================\n\n/**\n * Converts 12-hour format to 24-hour format\n * 12:00 pm → 12:00 (noon)\n * 12:00 am → 00:00 (midnight)\n */\nexport const convert12HourTo24Hour = (hour: number, period: Period): number => {\n if (period === 'pm') {\n return hour === 12 ? 12 : hour + 12\n }\n // am\n return hour === 12 ? 0 : hour\n}\n\n/**\n * Converts 24-hour format to 12-hour display value\n * Returns a zero-padded string (e.g., \"01\", \"12\")\n */\nexport const display12HourValue = (hours: number): string => {\n if (hours === 0 || hours === 12) return '12'\n const hour12 = hours % 12\n return hour12.toString().padStart(2, '0')\n}\n\n/**\n * Gets the period (am/pm) from a 24-hour value\n */\nexport const getPeriodFromHours = (hours: number): Period => {\n return hours >= 12 ? 'pm' : 'am'\n}\n\n// ============================================================================\n// ARIA Helpers\n// ============================================================================\n\nexport const getAriaValueNow = (date: Date, type: TimePickerType): number => {\n switch (type) {\n case 'minutes':\n return date.getMinutes()\n case 'hours':\n return date.getHours()\n case '12hours': {\n const hours = date.getHours()\n if (hours === 0) return 12\n if (hours > 12) return hours - 12\n return hours\n }\n default:\n return 0\n }\n}\n\nexport const getAriaValueMinMax = (type: TimePickerType): { min: number; max: number } => {\n switch (type) {\n case 'minutes':\n return TIME_BOUNDS.MINUTES\n case 'hours':\n return TIME_BOUNDS.HOURS_24\n case '12hours':\n return TIME_BOUNDS.HOURS_12\n default:\n return { min: 0, max: 0 }\n }\n}\n\nexport const getAriaLabel = (type: TimePickerType, translations: TimePickerTranslations = DEFAULT_TRANSLATIONS): string => {\n switch (type) {\n case 'minutes':\n return translations.minutes\n case 'hours':\n return translations.hours24\n case '12hours':\n return translations.hours12\n default:\n return translations.timeValue\n }\n}\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  import { Period, PeriodLabels } from "./DateTimeUtils.js";
3
- import * as _$react from "react";
4
- import { ComponentProps } from "react";
3
+ import { ComponentProps, Ref } from "react";
4
+ import * as _$react_jsx_runtime0 from "react/jsx-runtime";
5
5
 
6
6
  //#region src/components/DateTimePicker/TimePeriodSelect.d.ts
7
7
  interface TimePeriodSelectProps extends Omit<ComponentProps<'button'>, 'onChange' | 'ref'> {
@@ -12,7 +12,23 @@ interface TimePeriodSelectProps extends Omit<ComponentProps<'button'>, 'onChange
12
12
  onRightFocus?: () => void;
13
13
  period: Period;
14
14
  }
15
- declare const TimePeriodSelect: _$react.ForwardRefExoticComponent<TimePeriodSelectProps & _$react.RefAttributes<HTMLButtonElement>>;
15
+ declare function TimePeriodSelect({
16
+ ariaLabel,
17
+ className,
18
+ disabled,
19
+ labels,
20
+ onLeftFocus,
21
+ onPeriodChange,
22
+ onRightFocus,
23
+ period,
24
+ ref,
25
+ ...props
26
+ }: TimePeriodSelectProps & {
27
+ ref?: Ref<HTMLButtonElement>;
28
+ }): _$react_jsx_runtime0.JSX.Element;
29
+ declare namespace TimePeriodSelect {
30
+ var displayName: string;
31
+ }
16
32
  //#endregion
17
33
  export { TimePeriodSelect, TimePeriodSelectProps };
18
34
  //# sourceMappingURL=TimePeriodSelect.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"TimePeriodSelect.d.ts","names":[],"sources":["../../src/components/DateTimePicker/TimePeriodSelect.tsx"],"mappings":";;;;;;UAKiB,qBAAA,SAA8B,IAAA,CAAK,cAAA;EAClD,SAAA;EACA,MAAA,GAAS,YAAA;EACT,WAAA;EACA,cAAA,GAAiB,MAAA,EAAQ,MAAA;EACzB,YAAA;EACA,MAAA,EAAQ,MAAA;AAAA;AAAA,cAKG,gBAAA,EAAgB,OAAA,CAAA,yBAAA,CAAA,qBAAA,GAAA,OAAA,CAAA,aAAA,CAAA,iBAAA"}
1
+ {"version":3,"file":"TimePeriodSelect.d.ts","names":[],"sources":["../../src/components/DateTimePicker/TimePeriodSelect.tsx"],"mappings":";;;;;;UAKiB,qBAAA,SAA8B,IAAA,CAAK,cAAA;EAClD,SAAA;EACA,MAAA,GAAS,YAAA;EACT,WAAA;EACA,cAAA,GAAiB,MAAA,EAAQ,MAAA;EACzB,YAAA;EACA,MAAA,EAAQ,MAAA;AAAA;AAAA;EAKyB,SAAA;EAAW,SAAA;EAAW,QAAA;EAAU,MAAA;EAAyB,WAAA;EAAa,cAAA;EAAgB,YAAA;EAAc,MAAA;EAAQ,GAAA;EAAA,GAAQ;AAAA,GAAS,qBAAA;EAA0B,GAAA,GAAM,GAAA,CAAI,iBAAA;AAAA,IAAoB,oBAAA,CAAA,GAAA,CAAA,OAAA;AAAA"}
@@ -1,17 +1,29 @@
1
1
  'use client';
2
2
  import { cn } from "../utils/twUtils.js";
3
3
  import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../primitives/select.js";
4
- import { forwardRef } from "react";
4
+ import "react";
5
5
  import { jsx, jsxs } from "react/jsx-runtime";
6
6
 
7
7
  //#region src/components/DateTimePicker/TimePeriodSelect.tsx
8
8
  const DEFAULT_LABELS = {
9
- am: "AM",
10
- pm: "PM"
9
+ am: "am",
10
+ pm: "pm"
11
11
  };
12
- const TimePeriodSelect = forwardRef(({ ariaLabel, className, disabled, labels = DEFAULT_LABELS, onLeftFocus, onPeriodChange, onRightFocus, period, ...props }, ref) => {
12
+ const TimePeriodSelect = ({ ariaLabel, className, disabled, labels = DEFAULT_LABELS, onLeftFocus, onPeriodChange, onRightFocus, period, ref, ...props }) => {
13
13
  const effectiveAriaLabel = ariaLabel ?? `Select ${labels.am} or ${labels.pm}`;
14
+ const isSpaceKey = (key) => key === " " || key === "Space" || key === "Spacebar";
14
15
  const handleKeyDown = (e) => {
16
+ const isOpen = e.currentTarget.getAttribute("data-state") === "open";
17
+ const isSelectionCommitKey = e.key === "Enter" || isSpaceKey(e.key);
18
+ if (isOpen && isSelectionCommitKey) {
19
+ const contentId = e.currentTarget.getAttribute("aria-controls");
20
+ const highlightedItem = contentId ? e.currentTarget.ownerDocument.getElementById(contentId)?.querySelector("[data-slot='select-item'][data-highlighted]") : null;
21
+ if (highlightedItem) {
22
+ e.preventDefault();
23
+ highlightedItem.click();
24
+ return;
25
+ }
26
+ }
15
27
  if (e.key === "ArrowRight") {
16
28
  e.preventDefault();
17
29
  onRightFocus?.();
@@ -21,6 +33,14 @@ const TimePeriodSelect = forwardRef(({ ariaLabel, className, disabled, labels =
21
33
  onLeftFocus?.();
22
34
  }
23
35
  };
36
+ const handleContentKeyDown = (e) => {
37
+ if (!isSpaceKey(e.key)) return;
38
+ const highlightedItem = e.currentTarget.querySelector("[data-slot='select-item'][data-highlighted]");
39
+ if (highlightedItem) {
40
+ e.preventDefault();
41
+ highlightedItem.click();
42
+ }
43
+ };
24
44
  return /* @__PURE__ */ jsxs(Select, {
25
45
  defaultValue: period,
26
46
  disabled,
@@ -28,30 +48,31 @@ const TimePeriodSelect = forwardRef(({ ariaLabel, className, disabled, labels =
28
48
  value: period,
29
49
  children: [/* @__PURE__ */ jsx(SelectTrigger, {
30
50
  "aria-label": effectiveAriaLabel,
31
- className: cn("w-fit", className),
51
+ className: cn("h-9 w-fit min-w-20 grid-cols-[max-content_auto]", className),
32
52
  "data-slot": "time-period-select",
33
53
  "data-testid": "spectral-time-period-select",
34
54
  onKeyDown: handleKeyDown,
35
55
  ref,
36
56
  size: "sm",
37
57
  ...props,
38
- children: /* @__PURE__ */ jsx(SelectValue, { children: period === "AM" ? labels.am : labels.pm })
58
+ children: /* @__PURE__ */ jsx(SelectValue, { children: period === "am" ? labels.am : labels.pm })
39
59
  }), /* @__PURE__ */ jsxs(SelectContent, {
40
60
  "data-testid": "spectral-time-period-select-content",
61
+ onKeyDown: handleContentKeyDown,
41
62
  position: "popper",
42
63
  sideOffset: 4,
43
64
  children: [/* @__PURE__ */ jsx(SelectItem, {
44
65
  "data-testid": "spectral-time-period-select-am",
45
- value: "AM",
66
+ value: "am",
46
67
  children: labels.am
47
68
  }), /* @__PURE__ */ jsx(SelectItem, {
48
69
  "data-testid": "spectral-time-period-select-pm",
49
- value: "PM",
70
+ value: "pm",
50
71
  children: labels.pm
51
72
  })]
52
73
  })]
53
74
  });
54
- });
75
+ };
55
76
  TimePeriodSelect.displayName = "TimePeriodSelect";
56
77
 
57
78
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"TimePeriodSelect.js","names":[],"sources":["../../src/components/DateTimePicker/TimePeriodSelect.tsx"],"sourcesContent":["import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@primitives/select'\nimport { cn } from '@utils/twUtils'\nimport { forwardRef, type ComponentProps, type KeyboardEvent } from 'react'\nimport { type Period, type PeriodLabels } from './DateTimeUtils'\n\nexport interface TimePeriodSelectProps extends Omit<ComponentProps<'button'>, 'onChange' | 'ref'> {\n ariaLabel?: string\n labels?: PeriodLabels\n onLeftFocus?: () => void\n onPeriodChange: (period: Period) => void\n onRightFocus?: () => void\n period: Period\n}\n\nconst DEFAULT_LABELS: PeriodLabels = { am: 'AM', pm: 'PM' }\n\nexport const TimePeriodSelect = forwardRef<HTMLButtonElement, TimePeriodSelectProps>(({ ariaLabel, className, disabled, labels = DEFAULT_LABELS, onLeftFocus, onPeriodChange, onRightFocus, period, ...props }, ref) => {\n // Generate default ARIA label from the provided labels if not explicitly provided\n const effectiveAriaLabel = ariaLabel ?? `Select ${labels.am} or ${labels.pm}`\n const handleKeyDown = (e: KeyboardEvent<HTMLButtonElement>) => {\n if (e.key === 'ArrowRight') {\n e.preventDefault()\n onRightFocus?.()\n }\n if (e.key === 'ArrowLeft') {\n e.preventDefault()\n onLeftFocus?.()\n }\n }\n\n return (\n <Select defaultValue={period} disabled={disabled} onValueChange={(value) => onPeriodChange(value as Period)} value={period}>\n <SelectTrigger aria-label={effectiveAriaLabel} className={cn('w-fit', className)} data-slot='time-period-select' data-testid='spectral-time-period-select' onKeyDown={handleKeyDown} ref={ref} size='sm' {...props}>\n <SelectValue>{period === 'AM' ? labels.am : labels.pm}</SelectValue>\n </SelectTrigger>\n <SelectContent data-testid='spectral-time-period-select-content' position='popper' sideOffset={4}>\n <SelectItem data-testid='spectral-time-period-select-am' value='AM'>\n {labels.am}\n </SelectItem>\n <SelectItem data-testid='spectral-time-period-select-pm' value='PM'>\n {labels.pm}\n </SelectItem>\n </SelectContent>\n </Select>\n )\n})\n\nTimePeriodSelect.displayName = 'TimePeriodSelect'\n"],"mappings":";;;;;;;AAcA,MAAM,iBAA+B;CAAE,IAAI;CAAM,IAAI;CAAM;AAE3D,MAAa,mBAAmB,YAAsD,EAAE,WAAW,WAAW,UAAU,SAAS,gBAAgB,aAAa,gBAAgB,cAAc,QAAQ,GAAG,SAAS,QAAQ;CAEtN,MAAM,qBAAqB,aAAa,UAAU,OAAO,GAAG,MAAM,OAAO;CACzE,MAAM,iBAAiB,MAAwC;AAC7D,MAAI,EAAE,QAAQ,cAAc;AAC1B,KAAE,gBAAgB;AAClB,mBAAgB;;AAElB,MAAI,EAAE,QAAQ,aAAa;AACzB,KAAE,gBAAgB;AAClB,kBAAe;;;AAInB,QACE,qBAAC,QAAD;EAAQ,cAAc;EAAkB;EAAU,gBAAgB,UAAU,eAAe,MAAgB;EAAE,OAAO;YAApH,CACE,oBAAC,eAAD;GAAe,cAAY;GAAoB,WAAW,GAAG,SAAS,UAAU;GAAE,aAAU;GAAqB,eAAY;GAA8B,WAAW;GAAoB;GAAK,MAAK;GAAK,GAAI;aAC3M,oBAAC,aAAD,YAAc,WAAW,OAAO,OAAO,KAAK,OAAO,IAAiB;GACtD,GAChB,qBAAC,eAAD;GAAe,eAAY;GAAsC,UAAS;GAAS,YAAY;aAA/F,CACE,oBAAC,YAAD;IAAY,eAAY;IAAiC,OAAM;cAC5D,OAAO;IACG,GACb,oBAAC,YAAD;IAAY,eAAY;IAAiC,OAAM;cAC5D,OAAO;IACG,EACC;KACT;;EAEX;AAEF,iBAAiB,cAAc"}
1
+ {"version":3,"file":"TimePeriodSelect.js","names":[],"sources":["../../src/components/DateTimePicker/TimePeriodSelect.tsx"],"sourcesContent":["import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@primitives/select'\nimport { cn } from '@utils/twUtils'\nimport { type ComponentProps, type KeyboardEvent, type Ref } from 'react'\nimport { type Period, type PeriodLabels } from './DateTimeUtils'\n\nexport interface TimePeriodSelectProps extends Omit<ComponentProps<'button'>, 'onChange' | 'ref'> {\n ariaLabel?: string\n labels?: PeriodLabels\n onLeftFocus?: () => void\n onPeriodChange: (period: Period) => void\n onRightFocus?: () => void\n period: Period\n}\n\nconst DEFAULT_LABELS: PeriodLabels = { am: 'am', pm: 'pm' }\n\nexport const TimePeriodSelect = ({ ariaLabel, className, disabled, labels = DEFAULT_LABELS, onLeftFocus, onPeriodChange, onRightFocus, period, ref, ...props }: TimePeriodSelectProps & { ref?: Ref<HTMLButtonElement> }) => {\n // Generate default ARIA label from the provided labels if not explicitly provided\n const effectiveAriaLabel = ariaLabel ?? `Select ${labels.am} or ${labels.pm}`\n const isSpaceKey = (key: string) => key === ' ' || key === 'Space' || key === 'Spacebar'\n\n const handleKeyDown = (e: KeyboardEvent<HTMLButtonElement>) => {\n const isOpen = e.currentTarget.getAttribute('data-state') === 'open'\n const isSelectionCommitKey = e.key === 'Enter' || isSpaceKey(e.key)\n\n if (isOpen && isSelectionCommitKey) {\n const contentId = e.currentTarget.getAttribute('aria-controls')\n const highlightedItem = contentId\n ? e.currentTarget.ownerDocument.getElementById(contentId)?.querySelector<HTMLElement>(\"[data-slot='select-item'][data-highlighted]\")\n : null\n if (highlightedItem) {\n e.preventDefault()\n highlightedItem.click()\n return\n }\n }\n\n if (e.key === 'ArrowRight') {\n e.preventDefault()\n onRightFocus?.()\n }\n if (e.key === 'ArrowLeft') {\n e.preventDefault()\n onLeftFocus?.()\n }\n }\n\n const handleContentKeyDown = (e: KeyboardEvent<HTMLDivElement>) => {\n if (!isSpaceKey(e.key)) return\n\n const highlightedItem = e.currentTarget.querySelector<HTMLElement>(\"[data-slot='select-item'][data-highlighted]\")\n if (highlightedItem) {\n e.preventDefault()\n highlightedItem.click()\n }\n }\n\n return (\n <Select defaultValue={period} disabled={disabled} onValueChange={(value) => onPeriodChange(value as Period)} value={period}>\n <SelectTrigger\n aria-label={effectiveAriaLabel}\n className={cn('h-9 w-fit min-w-20 grid-cols-[max-content_auto]', className)}\n data-slot='time-period-select'\n data-testid='spectral-time-period-select'\n onKeyDown={handleKeyDown}\n ref={ref}\n size='sm'\n {...props}\n >\n <SelectValue>{period === 'am' ? labels.am : labels.pm}</SelectValue>\n </SelectTrigger>\n <SelectContent data-testid='spectral-time-period-select-content' onKeyDown={handleContentKeyDown} position='popper' sideOffset={4}>\n <SelectItem data-testid='spectral-time-period-select-am' value='am'>\n {labels.am}\n </SelectItem>\n <SelectItem data-testid='spectral-time-period-select-pm' value='pm'>\n {labels.pm}\n </SelectItem>\n </SelectContent>\n </Select>\n )\n}\n\nTimePeriodSelect.displayName = 'TimePeriodSelect'\n"],"mappings":";;;;;;;AAcA,MAAM,iBAA+B;CAAE,IAAI;CAAM,IAAI;CAAM;AAE3D,MAAa,oBAAoB,EAAE,WAAW,WAAW,UAAU,SAAS,gBAAgB,aAAa,gBAAgB,cAAc,QAAQ,KAAK,GAAG,YAAsE;CAE3N,MAAM,qBAAqB,aAAa,UAAU,OAAO,GAAG,MAAM,OAAO;CACzE,MAAM,cAAc,QAAgB,QAAQ,OAAO,QAAQ,WAAW,QAAQ;CAE9E,MAAM,iBAAiB,MAAwC;EAC7D,MAAM,SAAS,EAAE,cAAc,aAAa,aAAa,KAAK;EAC9D,MAAM,uBAAuB,EAAE,QAAQ,WAAW,WAAW,EAAE,IAAI;AAEnE,MAAI,UAAU,sBAAsB;GAClC,MAAM,YAAY,EAAE,cAAc,aAAa,gBAAgB;GAC/D,MAAM,kBAAkB,YACpB,EAAE,cAAc,cAAc,eAAe,UAAU,EAAE,cAA2B,8CAA8C,GAClI;AACJ,OAAI,iBAAiB;AACnB,MAAE,gBAAgB;AAClB,oBAAgB,OAAO;AACvB;;;AAIJ,MAAI,EAAE,QAAQ,cAAc;AAC1B,KAAE,gBAAgB;AAClB,mBAAgB;;AAElB,MAAI,EAAE,QAAQ,aAAa;AACzB,KAAE,gBAAgB;AAClB,kBAAe;;;CAInB,MAAM,wBAAwB,MAAqC;AACjE,MAAI,CAAC,WAAW,EAAE,IAAI,CAAE;EAExB,MAAM,kBAAkB,EAAE,cAAc,cAA2B,8CAA8C;AACjH,MAAI,iBAAiB;AACnB,KAAE,gBAAgB;AAClB,mBAAgB,OAAO;;;AAI3B,QACE,qBAAC,QAAD;EAAQ,cAAc;EAAkB;EAAU,gBAAgB,UAAU,eAAe,MAAgB;EAAE,OAAO;YAApH,CACE,oBAAC,eAAD;GACE,cAAY;GACZ,WAAW,GAAG,mDAAmD,UAAU;GAC3E,aAAU;GACV,eAAY;GACZ,WAAW;GACN;GACL,MAAK;GACL,GAAI;aAEJ,oBAAC,aAAD,YAAc,WAAW,OAAO,OAAO,KAAK,OAAO,IAAiB;GACtD,GAChB,qBAAC,eAAD;GAAe,eAAY;GAAsC,WAAW;GAAsB,UAAS;GAAS,YAAY;aAAhI,CACE,oBAAC,YAAD;IAAY,eAAY;IAAiC,OAAM;cAC5D,OAAO;IACG,GACb,oBAAC,YAAD;IAAY,eAAY;IAAiC,OAAM;cAC5D,OAAO;IACG,EACC;KACT;;;AAIb,iBAAiB,cAAc"}