@mortenbrudvik/waveui 0.8.0 → 0.10.0
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.
- package/README.md +634 -167
- package/dist/components/data-display/InfoButton.cjs +2 -0
- package/dist/components/data-display/InfoButton.cjs.map +1 -0
- package/dist/components/data-display/InfoButton.mjs +173 -0
- package/dist/components/data-display/InfoButton.mjs.map +1 -0
- package/dist/components/data-display/InfoLabel.cjs +1 -1
- package/dist/components/data-display/InfoLabel.cjs.map +1 -1
- package/dist/components/data-display/InfoLabel.mjs +39 -96
- package/dist/components/data-display/InfoLabel.mjs.map +1 -1
- package/dist/components/input/Calendar.cjs +2 -0
- package/dist/components/input/Calendar.cjs.map +1 -0
- package/dist/components/input/Calendar.mjs +352 -0
- package/dist/components/input/Calendar.mjs.map +1 -0
- package/dist/components/input/Calendar.views.cjs +2 -0
- package/dist/components/input/Calendar.views.cjs.map +1 -0
- package/dist/components/input/Calendar.views.mjs +194 -0
- package/dist/components/input/Calendar.views.mjs.map +1 -0
- package/dist/components/input/Checkbox.cjs +1 -1
- package/dist/components/input/Checkbox.cjs.map +1 -1
- package/dist/components/input/Checkbox.mjs +91 -75
- package/dist/components/input/Checkbox.mjs.map +1 -1
- package/dist/components/input/ColorArea.cjs +1 -1
- package/dist/components/input/ColorArea.cjs.map +1 -1
- package/dist/components/input/ColorArea.mjs +18 -18
- package/dist/components/input/ColorArea.mjs.map +1 -1
- package/dist/components/input/ColorPicker.parts.cjs +1 -1
- package/dist/components/input/ColorPicker.parts.cjs.map +1 -1
- package/dist/components/input/ColorPicker.parts.mjs +1 -1
- package/dist/components/input/ColorPicker.parts.mjs.map +1 -1
- package/dist/components/input/Combobox.cjs +1 -1
- package/dist/components/input/Combobox.cjs.map +1 -1
- package/dist/components/input/Combobox.expand.cjs +1 -1
- package/dist/components/input/Combobox.expand.cjs.map +1 -1
- package/dist/components/input/Combobox.expand.mjs +20 -20
- package/dist/components/input/Combobox.expand.mjs.map +1 -1
- package/dist/components/input/Combobox.mjs +219 -138
- package/dist/components/input/Combobox.mjs.map +1 -1
- package/dist/components/input/Combobox.text.cjs +2 -0
- package/dist/components/input/Combobox.text.cjs.map +1 -0
- package/dist/components/input/Combobox.text.mjs +24 -0
- package/dist/components/input/Combobox.text.mjs.map +1 -0
- package/dist/components/input/DatePicker.cjs +1 -1
- package/dist/components/input/DatePicker.cjs.map +1 -1
- package/dist/components/input/DatePicker.mjs +205 -286
- package/dist/components/input/DatePicker.mjs.map +1 -1
- package/dist/components/input/Dropdown.cjs +1 -1
- package/dist/components/input/Dropdown.cjs.map +1 -1
- package/dist/components/input/Dropdown.mjs +137 -105
- package/dist/components/input/Dropdown.mjs.map +1 -1
- package/dist/components/input/Field.cjs +1 -1
- package/dist/components/input/Field.cjs.map +1 -1
- package/dist/components/input/Field.mjs +99 -66
- package/dist/components/input/Field.mjs.map +1 -1
- package/dist/components/input/Input.cjs +1 -1
- package/dist/components/input/Input.cjs.map +1 -1
- package/dist/components/input/Input.mjs +79 -55
- package/dist/components/input/Input.mjs.map +1 -1
- package/dist/components/input/Label.cjs +1 -1
- package/dist/components/input/Label.cjs.map +1 -1
- package/dist/components/input/Label.mjs +19 -13
- package/dist/components/input/Label.mjs.map +1 -1
- package/dist/components/input/Listbox.cjs +2 -0
- package/dist/components/input/Listbox.cjs.map +1 -0
- package/dist/components/input/Listbox.mjs +134 -0
- package/dist/components/input/Listbox.mjs.map +1 -0
- package/dist/components/input/Option.cjs +1 -1
- package/dist/components/input/Option.cjs.map +1 -1
- package/dist/components/input/Option.mjs +158 -97
- package/dist/components/input/Option.mjs.map +1 -1
- package/dist/components/input/Rating.cjs +1 -1
- package/dist/components/input/Rating.cjs.map +1 -1
- package/dist/components/input/Rating.item.cjs +2 -0
- package/dist/components/input/Rating.item.cjs.map +1 -0
- package/dist/components/input/Rating.item.mjs +255 -0
- package/dist/components/input/Rating.item.mjs.map +1 -0
- package/dist/components/input/Rating.mjs +150 -151
- package/dist/components/input/Rating.mjs.map +1 -1
- package/dist/components/input/SearchBox.cjs +1 -1
- package/dist/components/input/SearchBox.cjs.map +1 -1
- package/dist/components/input/SearchBox.mjs +146 -115
- package/dist/components/input/SearchBox.mjs.map +1 -1
- package/dist/components/input/Select.cjs +1 -1
- package/dist/components/input/Select.cjs.map +1 -1
- package/dist/components/input/Select.mjs +34 -25
- package/dist/components/input/Select.mjs.map +1 -1
- package/dist/components/input/Slider.cjs +1 -1
- package/dist/components/input/Slider.cjs.map +1 -1
- package/dist/components/input/Slider.mjs +77 -54
- package/dist/components/input/Slider.mjs.map +1 -1
- package/dist/components/input/SpinButton.cjs +1 -1
- package/dist/components/input/SpinButton.cjs.map +1 -1
- package/dist/components/input/SpinButton.mjs +198 -96
- package/dist/components/input/SpinButton.mjs.map +1 -1
- package/dist/components/input/SwatchPicker.cjs +1 -1
- package/dist/components/input/SwatchPicker.cjs.map +1 -1
- package/dist/components/input/SwatchPicker.context.cjs +2 -0
- package/dist/components/input/SwatchPicker.context.cjs.map +1 -0
- package/dist/components/input/SwatchPicker.context.mjs +35 -0
- package/dist/components/input/SwatchPicker.context.mjs.map +1 -0
- package/dist/components/input/SwatchPicker.mjs +131 -103
- package/dist/components/input/SwatchPicker.mjs.map +1 -1
- package/dist/components/input/SwatchPicker.swatches.cjs +2 -0
- package/dist/components/input/SwatchPicker.swatches.cjs.map +1 -0
- package/dist/components/input/SwatchPicker.swatches.mjs +207 -0
- package/dist/components/input/SwatchPicker.swatches.mjs.map +1 -0
- package/dist/components/input/Switch.cjs +1 -1
- package/dist/components/input/Switch.cjs.map +1 -1
- package/dist/components/input/Switch.mjs +71 -55
- package/dist/components/input/Switch.mjs.map +1 -1
- package/dist/components/input/TagPicker.cjs +1 -1
- package/dist/components/input/TagPicker.cjs.map +1 -1
- package/dist/components/input/TagPicker.mjs +154 -123
- package/dist/components/input/TagPicker.mjs.map +1 -1
- package/dist/components/input/Textarea.cjs +1 -1
- package/dist/components/input/Textarea.cjs.map +1 -1
- package/dist/components/input/Textarea.mjs +28 -21
- package/dist/components/input/Textarea.mjs.map +1 -1
- package/dist/components/input/TimePicker.cjs +1 -1
- package/dist/components/input/TimePicker.cjs.map +1 -1
- package/dist/components/input/TimePicker.mjs +204 -199
- package/dist/components/input/TimePicker.mjs.map +1 -1
- package/dist/components/input/dateUtils.cjs +1 -1
- package/dist/components/input/dateUtils.cjs.map +1 -1
- package/dist/components/input/dateUtils.mjs +114 -96
- package/dist/components/input/dateUtils.mjs.map +1 -1
- package/dist/components/input/fieldLabel.cjs +2 -0
- package/dist/components/input/fieldLabel.cjs.map +1 -0
- package/dist/components/input/fieldLabel.mjs +17 -0
- package/dist/components/input/fieldLabel.mjs.map +1 -0
- package/dist/components/input/inputLook.cjs +2 -0
- package/dist/components/input/inputLook.cjs.map +1 -0
- package/dist/components/input/inputLook.mjs +28 -0
- package/dist/components/input/inputLook.mjs.map +1 -0
- package/dist/components/input/pickerLabels.cjs +2 -0
- package/dist/components/input/pickerLabels.cjs.map +1 -0
- package/dist/components/input/pickerLabels.mjs +16 -0
- package/dist/components/input/pickerLabels.mjs.map +1 -0
- package/dist/components/input/pickerStyles.cjs +1 -1
- package/dist/components/input/pickerStyles.cjs.map +1 -1
- package/dist/components/input/pickerStyles.mjs +42 -4
- package/dist/components/input/pickerStyles.mjs.map +1 -1
- package/dist/components/input/pickerValues.cjs +2 -0
- package/dist/components/input/pickerValues.cjs.map +1 -0
- package/dist/components/input/pickerValues.mjs +24 -0
- package/dist/components/input/pickerValues.mjs.map +1 -0
- package/dist/components/navigation/Menu.popover.cjs +1 -1
- package/dist/components/navigation/Menu.popover.cjs.map +1 -1
- package/dist/components/navigation/Menu.popover.mjs +10 -10
- package/dist/components/navigation/Menu.root.cjs +1 -1
- package/dist/components/navigation/Menu.root.cjs.map +1 -1
- package/dist/components/navigation/Menu.root.mjs +10 -10
- package/dist/components/overlays/Popover.cjs +1 -1
- package/dist/components/overlays/Popover.cjs.map +1 -1
- package/dist/components/overlays/Popover.mjs +203 -208
- package/dist/components/overlays/Popover.mjs.map +1 -1
- package/dist/components/overlays/Popover.shared.cjs +1 -1
- package/dist/components/overlays/Popover.shared.cjs.map +1 -1
- package/dist/components/overlays/Popover.shared.mjs +82 -77
- package/dist/components/overlays/Popover.shared.mjs.map +1 -1
- package/dist/components/provider/WaveProvider.cjs +1 -1
- package/dist/components/provider/WaveProvider.cjs.map +1 -1
- package/dist/components/provider/WaveProvider.mjs +25 -19
- package/dist/components/provider/WaveProvider.mjs.map +1 -1
- package/dist/components/table/useGridNavigation.cjs +1 -1
- package/dist/components/table/useGridNavigation.cjs.map +1 -1
- package/dist/components/table/useGridNavigation.mjs +67 -70
- package/dist/components/table/useGridNavigation.mjs.map +1 -1
- package/dist/hooks/useActiveDescendant.cjs +2 -0
- package/dist/hooks/useActiveDescendant.cjs.map +1 -0
- package/dist/hooks/useActiveDescendant.mjs +56 -0
- package/dist/hooks/useActiveDescendant.mjs.map +1 -0
- package/dist/hooks/useFieldControl.cjs.map +1 -1
- package/dist/hooks/useFieldControl.mjs.map +1 -1
- package/dist/hooks/useListbox.cjs +1 -1
- package/dist/hooks/useListbox.cjs.map +1 -1
- package/dist/hooks/useListbox.mjs +250 -210
- package/dist/hooks/useListbox.mjs.map +1 -1
- package/dist/hooks/useRovingGrid.cjs +2 -0
- package/dist/hooks/useRovingGrid.cjs.map +1 -0
- package/dist/hooks/useRovingGrid.mjs +69 -0
- package/dist/hooks/useRovingGrid.mjs.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +2901 -203
- package/dist/index.d.ts +2901 -203
- package/dist/index.mjs +107 -99
- package/dist/lib/classNames.cjs +1 -1
- package/dist/lib/classNames.cjs.map +1 -1
- package/dist/lib/classNames.mjs +111 -67
- package/dist/lib/classNames.mjs.map +1 -1
- package/dist/lib/date.cjs +2 -0
- package/dist/lib/date.cjs.map +1 -0
- package/dist/lib/date.mjs +44 -0
- package/dist/lib/date.mjs.map +1 -0
- package/dist/lib/focus.cjs +1 -1
- package/dist/lib/focus.cjs.map +1 -1
- package/dist/lib/focus.mjs +5 -2
- package/dist/lib/focus.mjs.map +1 -1
- package/dist/lib/layers.cjs.map +1 -1
- package/dist/lib/layers.mjs.map +1 -1
- package/dist/lib/styles.cjs +1 -1
- package/dist/lib/styles.cjs.map +1 -1
- package/dist/lib/styles.mjs +21 -4
- package/dist/lib/styles.mjs.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/src/styles/tokens.css +13 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TimePicker.mjs","names":["useId","useId$1","React","OPTION_CLASSES","TimePickerOption","useListboxOption","optionProps","cn","timePickerClassNames","selected","forcedColors","CheckIcon","label","defaultInvalidTimeLabel","format","defaultOutOfRangeLabel","min","max","displayTime","minutes","timeToMinutes","value","minutesToTime","matchesQuery","item","text","startsWithQuery","TimePicker","valueProp","openProp","ariaLabel","ariaLabelledBy","ariaDescribedBy","ariaInvalid","ariaRequired","ariaErrorMessage","ariaDetails","rest","props","onChange","warnDeprecated","selectedValue","setSelectedValue","useControllable","defaultValue","onValueChange","next","stepValid","normalizeTimeStep","step","minMinutes","minTime","maxMinutes","maxTime","boundsValid","allOptions","generateTimeOptions","warnOnce","interactive","disabled","readOnly","openState","setOpen","openControlled","defaultOpen","onOpenChange","isClient","useIsClient","open","draft","setDraft","invalid","setInvalid","wasInteractive","setWasInteractive","seenValue","setSeenValue","query","openList","closeList","filter","exact","minutesToValue","getTypedActiveValue","needle","selectedInList","selectedValues","commitValue","commitDraft","rejectDraft","reason","onInvalidInput","clearDraft","hasMatches","expanded","lb","useListbox","displayLabel","showClear","clearable","statusMessage","labels","rootRef","inputRef","surfaceRef","rootRefs","useMergedRefs","ref","inputRefs","controlRef","useDismiss","usePopupPosition","surfaceRefs","setFloating","field","useFieldContext","validates","required","errorId","showOwnError","fieldProps","useFieldControl","joinIds","invalidLook","isInvalidLook","form","handleInputChange","event","handleKeyDown","composeEventHandlers","onKeyDown","flushSync","handleKeyUp","onKeyUp","handleInputClick","handleBlur","onBlur","handleExpandClick","handleClear","listboxLabelledBy","renderListbox","hidden","showExpand","showsExpandButton","expandIcon","errorMessage","listboxMounted","ListboxContext","className","setReference","inputBase","inputFocus","disabledStyles","pickerEndPadding","DismissIcon","PickerExpandButton","HiddenInput","Portal","floatingProps"],"sources":["../../../src/components/input/TimePicker.tsx"],"sourcesContent":["import * as React from 'react';\nimport { flushSync } from 'react-dom';\nimport { cn } from '../../lib/cn';\nimport { joinIds } from '../../lib/aria';\nimport { timePickerClassNames } from '../../lib/classNames';\nimport { composeEventHandlers } from '../../lib/composeEventHandlers';\nimport { warnDeprecated, warnOnce } from '../../lib/dev';\nimport { CheckIcon, DismissIcon } from '../../lib/icons';\nimport type { Slot } from '../../lib/slot';\nimport {\n disabledStyles,\n focusRing,\n forcedColors,\n inputBase,\n inputFocus,\n inputInvalid,\n} from '../../lib/styles';\nimport { useControllable } from '../../hooks/useControllable';\nimport { useDismiss } from '../../hooks/useDismiss';\nimport { useFieldContext, useFieldControl } from '../../hooks/useFieldControl';\nimport { useFormReset } from '../../hooks/useFormReset';\nimport { useId } from '../../hooks/useId';\nimport { useIsClient } from '../../hooks/useIsClient';\nimport { ListboxContext, useListbox, useListboxOption } from '../../hooks/useListbox';\nimport type { ListboxItem } from '../../hooks/useListbox';\nimport { useMergedRefs } from '../../hooks/useMergedRefs';\nimport { usePopupPosition } from '../../hooks/usePopupPosition';\nimport { HiddenInput } from '../internal/HiddenInput';\nimport { Portal } from '../portal/Portal';\nimport { PickerExpandButton, showsExpandButton } from './Combobox.expand';\nimport {\n DEFAULT_TIME_STEP,\n generateTimeOptions,\n minutesToTime,\n minutesToValue,\n normalizeTimeStep,\n timeToMinutes,\n} from './dateUtils';\nimport { isInvalidLook } from './Input';\nimport { PICKER_ICON_BUTTON_CLASSES, pickerEndPadding } from './pickerStyles';\nimport type { RoutedHandlers } from './routedHandlers';\n\n/* ------------------------------------------------------------------ */\n/* Option */\n/* ------------------------------------------------------------------ */\n\nconst OPTION_CLASSES =\n 'flex cursor-pointer items-center gap-2 px-3 py-1.5 text-body-1 text-foreground hover:bg-subtle-hover data-[active]:bg-subtle-hover data-[active]:outline-2 data-[active]:outline-ring data-[active]:-outline-offset-2 data-[selected]:bg-subtle-selected data-[selected]:font-semibold';\n\ninterface TimePickerOptionProps {\n value: string;\n label: string;\n}\n\n/**\n * One time option. Memoized: it re-renders only when its own active/selected state changes (read\n * from the listbox store), so moving the highlight renders two options, not the whole list.\n */\nconst TimePickerOption = React.memo(function TimePickerOption({\n value,\n label,\n}: TimePickerOptionProps) {\n const { selected, optionProps } = useListboxOption<HTMLLIElement>({ value, label });\n return (\n <li\n {...optionProps}\n className={cn(\n timePickerClassNames.option,\n OPTION_CLASSES,\n selected && forcedColors.selectedContainer,\n )}\n >\n <span\n className={cn(\n timePickerClassNames.checkIcon,\n 'flex h-4 w-4 shrink-0 items-center justify-center',\n )}\n >\n {selected ? <CheckIcon size={12} /> : null}\n </span>\n {label}\n </li>\n );\n});\nTimePickerOption.displayName = 'TimePickerOption';\n\n/* ------------------------------------------------------------------ */\n/* TimePicker */\n/* ------------------------------------------------------------------ */\n\n/** Why typed text was not accepted (see {@link TimePickerProps.onInvalidInput}). */\nexport type TimePickerInvalidReason = 'unparseable' | 'out-of-range';\n\n/**\n * The TimePicker's built-in texts, for localization. Each member is optional and falls back to its\n * English default. The times themselves follow `format`.\n */\nexport interface TimePickerLabels {\n /** Name of the clear button.\n * @default 'Clear time'\n */\n clear?: string;\n /** Name of the expand button at the end of the input.\n * @default 'Show times'\n */\n expand?: string;\n /** Name of the list of times when the picker has no `aria-label`, `aria-labelledby` or Field\n * label to lend it.\n * @default 'Times'\n */\n list?: string;\n /** Status text (announced and shown in the open list) when `minTime`/`maxTime` leave no times.\n * @default 'No times available'\n */\n noTimes?: string;\n /** Status text (announced and shown in the open list) when the typed text matches no time.\n * @default 'No matching times'\n */\n noMatches?: string;\n /**\n * Message for text that is not a time. `format` is the display pattern (`'HH:mm'` or\n * `'h:mm AM'`).\n * @default (format) => `Enter a time in the format ${format}.`\n */\n invalidTime?: (format: string) => string;\n /**\n * Message for a time outside `minTime`/`maxTime`. The bounds are passed as the input shows times\n * (`format`); a bound that is not set is `undefined`.\n * @default (min, max) => `Enter a time between ${min} and ${max}.` (only `min`: `Enter a time on\n * or after ${min}.`; only `max`: `Enter a time on or before ${max}.`)\n */\n outOfRange?: (min: string | undefined, max: string | undefined) => string;\n}\n\nconst defaultInvalidTimeLabel = (format: string) => `Enter a time in the format ${format}.`;\n\nconst defaultOutOfRangeLabel = (min: string | undefined, max: string | undefined) => {\n if (min !== undefined && max !== undefined) return `Enter a time between ${min} and ${max}.`;\n if (min !== undefined) return `Enter a time on or after ${min}.`;\n return `Enter a time on or before ${max}.`;\n};\n\n/** Properties for the TimePicker component. */\nexport interface TimePickerProps extends Omit<\n React.HTMLAttributes<HTMLDivElement>,\n 'onChange' | 'defaultValue' | 'placeholder' | RoutedHandlers\n> {\n /** Controlled selected time in `HH:mm` (24-hour) format; `''` for no time. */\n value?: string;\n /**\n * Initial time for uncontrolled usage (`HH:mm`).\n * @default ''\n */\n defaultValue?: string;\n /** Called with the new `HH:mm` value (`''` when cleared) whenever the selected time changes. */\n onValueChange?: (value: string) => void;\n /**\n * @deprecated Use `onValueChange`. Still called with the new value (warns once in development).\n */\n onChange?: (value: string) => void;\n /**\n * Time display format of the input and the options.\n * @default '12h'\n */\n format?: '12h' | '24h';\n /**\n * Minutes between options. A non-finite or non-positive value falls back to 30 (development\n * warning); fractions are floored.\n * @default 30\n */\n step?: number;\n /**\n * Earliest time (inclusive): `HH:mm`, `HH:mm:ss` or `h:mm AM`.\n * @default '00:00'\n */\n minTime?: string;\n /**\n * Latest time (inclusive): `HH:mm`, `HH:mm:ss` or `h:mm AM`. Invalid or reversed bounds give no\n * options (development warning, \"No times available\", see `labels`).\n * @default '23:59'\n */\n maxTime?: string;\n /**\n * Placeholder text shown when no time is selected.\n * @default 'Select a time'\n */\n placeholder?: string;\n /**\n * Whether the time picker is disabled and non-interactive.\n * @default false\n */\n disabled?: boolean;\n /**\n * Makes the input read-only: the list does not open and the value cannot change. Turning it (or\n * `disabled`) on while the user is typing drops the typed text.\n */\n readOnly?: boolean;\n /**\n * Whether to show a clear button when a time is selected (not shown while `readOnly`; shown\n * disabled while `disabled`). It is a tab stop after the input. Tab from erased text clears the\n * value before focus moves, so focus goes on past the clear button that disappears with it.\n * @default false\n */\n clearable?: boolean;\n /**\n * The glyph of the expand button at the end of the input (default: a chevron). The button\n * opens and closes the list without moving focus out of the input and is not a tab stop\n * (Alt+ArrowDown opens the list from the keyboard). `null` or `undefined` keep the chevron;\n * `false`, or a value that renders nothing, hides the button. Decorative content rendered\n * inside the built-in button: a `<button>` or `Button` passed here is not nested (its children\n * become the glyph, with a development warning).\n */\n expandIcon?: Slot<'span'>;\n /**\n * Controlled open state of the list (never shown while `disabled` or `readOnly`). The list\n * renders only in the browser: an open picker is closed in the server HTML and opens once it has\n * hydrated.\n */\n open?: boolean;\n /**\n * Initial open state of the list for uncontrolled usage. A picker that starts disabled or\n * read-only starts closed. The list renders only in the browser: it is closed in the server HTML\n * and opens once the picker has hydrated.\n * @default false\n */\n defaultOpen?: boolean;\n /** Called when the list opens or closes (only when the state changes). */\n onOpenChange?: (open: boolean) => void;\n /**\n * Called when edited text is not accepted on Enter or blur (once per edit): it is not a time\n * (`'unparseable'`) or lies outside `minTime`/`maxTime` (`'out-of-range'`). The text stays in the\n * input, which is marked `aria-invalid` and described by an error message (left to the\n * surrounding `Field` when it shows an error); Enter and leaving the input also close the list,\n * so it does not cover the message. Enter reports it again when pressed again.\n */\n onInvalidInput?: (text: string, reason: TimePickerInvalidReason) => void;\n /**\n * Names of the clear and expand buttons and of the list, the status texts of an empty list and\n * the error messages of rejected text, for localization. Unset members keep their English\n * defaults.\n */\n labels?: TimePickerLabels;\n /** Form field name: the `HH:mm` value is submitted under it (hidden input). */\n name?: string;\n /** Id of the `<form>` the value belongs to, when the picker is outside it. */\n form?: string;\n /**\n * Blocks form submission while no time is selected; sets `aria-required` on the input. Like a\n * native readonly input, a `readOnly` TimePicker does not block submission.\n */\n required?: boolean;\n /** Native `autocomplete` of the input. @default 'off' */\n autoComplete?: string;\n /** Native `maxlength` of the input. */\n maxLength?: number;\n /** Focus handler of the text input (the root keeps the other handlers). */\n onFocus?: React.FocusEventHandler<HTMLInputElement>;\n /** Blur handler of the text input. */\n onBlur?: React.FocusEventHandler<HTMLInputElement>;\n /** Key handler of the text input; `preventDefault()` skips the picker's own keys. */\n onKeyDown?: React.KeyboardEventHandler<HTMLInputElement>;\n /** Key handler of the text input. */\n onKeyUp?: React.KeyboardEventHandler<HTMLInputElement>;\n /** Ref to the text input (the `role=\"combobox\"` element); `ref` stays on the root. */\n controlRef?: React.Ref<HTMLInputElement>;\n /** Ref to the root element. */\n ref?: React.Ref<HTMLDivElement>;\n}\n\nfunction displayTime(value: string, format: '12h' | '24h'): string {\n const minutes = timeToMinutes(value);\n return Number.isNaN(minutes) ? value : minutesToTime(minutes, format);\n}\n\n/** Whether an option matches the (trimmed, lower-case) typed text: by label or `HH:mm` value. */\nfunction matchesQuery(item: ListboxItem, text: string): boolean {\n return item.label.toLowerCase().includes(text) || item.value.includes(text);\n}\n\n/** Whether an option's label or `HH:mm` value starts with the (trimmed, lower-case) typed text. */\nfunction startsWithQuery(item: ListboxItem, text: string): boolean {\n return item.label.toLowerCase().startsWith(text) || item.value.startsWith(text);\n}\n\n/**\n * A time input with a filterable list of times (APG editable combobox with list autocomplete).\n *\n * - Type to filter (by label or `HH:mm` value). A complete typed time (`2:00 PM`, `14:00`) makes\n * its own option active — not an earlier one that merely contains the text, such as `12:00 PM`\n * — and partial text makes the first option that starts with it active (`2:00 p` -> `2:00 PM`),\n * else the first one that contains it; Enter commits the active option. Enter or leaving the\n * field also commits a complete typed time that is not in the list (`9:15 AM`, `14:45`) when it\n * lies within `minTime`/`maxTime`, and erased text clears the value (as the clear button does);\n * Enter never submits the form with edited text, and both close the list. Other typed text is\n * kept: the input is marked invalid and an error message describes it (`onInvalidInput`,\n * reported once per edit) until the text is edited or replaced (an option, the clear button,\n * Escape, a form reset, a new value from the parent). Escape on a closed list reverts any edit,\n * erased text included, to the selected time.\n * - Click the input or the expand button at its end (a chevron, see `expandIcon`; not a tab stop,\n * and it leaves focus in the input), press ArrowDown/ArrowUp or type to open; the list opens\n * with every option and the selected one (when it is in the list) active and scrolled into\n * view. Without one, no option is active until ArrowDown/ArrowUp or typing, so Enter lets the\n * surrounding form submit. Escape, Tab, an outside press or focus leaving closes it. Typed text\n * kept after Escape closed the list still filters it when it reopens: a click resumes the option\n * typing made active, ArrowDown/ArrowUp start at the first/last match.\n * - `open`/`defaultOpen`/`onOpenChange` control the list. It closes when the picker becomes\n * disabled or read-only (uncontrolled `open`: it stays closed when the picker is enabled again),\n * and the text typed until then is dropped.\n * - Keys of an IME composition (its confirming Enter included) are left to the IME.\n * - `clearable` shows a clear button while a time is selected (not while read-only), a tab stop\n * after the input. Tab from erased text clears the value first, so focus moves on past the\n * picker instead of to the clear button that disappears.\n * - The value is `HH:mm` (24-hour) whatever the display `format`; values off the `step` grid or\n * outside the bounds are still displayed in `format`.\n * - The input (`controlRef`) receives `id`, `aria-label`, `aria-labelledby`, `aria-describedby`,\n * `aria-invalid`, `aria-required`, `aria-errormessage`, `aria-details`, `tabIndex`,\n * `autoFocus`, `onFocus`/`onBlur`/`onKeyDown`/`onKeyUp` and the text input attributes\n * `autoComplete`, `autoCapitalize`, `autoCorrect`, `maxLength`, `inputMode`, `spellCheck` and\n * `enterKeyHint`. `ref`, `className`, `style`, other `aria-*` attributes and the remaining props\n * stay on the root `<div>`. Inside a `Field`, the input is labelled and described by it.\n * - `name`/`required` add a hidden input for native forms (`HH:mm`); the value resets with its form.\n * - The built-in names, status texts and error messages are English; `labels` localizes them.\n * - The open list renders only in the browser: an open list (`defaultOpen`, `open`) is closed in\n * the server HTML and opens once the picker has hydrated.\n */\nexport const TimePicker = (props: TimePickerProps) => {\n const {\n value: valueProp,\n defaultValue,\n onValueChange,\n onChange,\n format = '12h',\n step = DEFAULT_TIME_STEP,\n minTime = '00:00',\n maxTime = '23:59',\n placeholder = 'Select a time',\n disabled = false,\n readOnly,\n clearable = false,\n expandIcon,\n open: openProp,\n defaultOpen,\n onOpenChange,\n onInvalidInput,\n labels,\n name,\n form,\n required,\n autoComplete = 'off',\n autoCapitalize,\n autoCorrect,\n maxLength,\n enterKeyHint,\n inputMode,\n spellCheck,\n autoFocus,\n tabIndex,\n id,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n 'aria-describedby': ariaDescribedBy,\n 'aria-invalid': ariaInvalid,\n 'aria-required': ariaRequired,\n 'aria-errormessage': ariaErrorMessage,\n 'aria-details': ariaDetails,\n onFocus,\n onBlur,\n onKeyDown,\n onKeyUp,\n controlRef,\n className,\n ref,\n ...rest\n } = props;\n\n if (onChange !== undefined) warnDeprecated('TimePicker', 'onChange', 'onValueChange');\n\n const [selectedValue, setSelectedValue] = useControllable<string>(\n valueProp,\n defaultValue ?? '',\n (next) => {\n onValueChange?.(next);\n onChange?.(next);\n },\n );\n\n /* ---- options, step and bounds ---------------------------------- */\n\n const stepValid = normalizeTimeStep(step).valid;\n const minMinutes = timeToMinutes(minTime);\n const maxMinutes = timeToMinutes(maxTime);\n const boundsValid =\n !Number.isNaN(minMinutes) && !Number.isNaN(maxMinutes) && minMinutes <= maxMinutes;\n const allOptions = React.useMemo(\n () => generateTimeOptions(step, minTime, maxTime, format),\n [step, minTime, maxTime, format],\n );\n\n // Once per invalid value and page (C-DEV), not once per instance and effect run.\n React.useEffect(() => {\n if (!stepValid) {\n warnOnce(\n `TimePicker:step:${String(step)}`,\n `TimePicker: \\`step\\` must be a positive number of minutes (received ${String(step)}); ` +\n `using ${DEFAULT_TIME_STEP}.`,\n );\n }\n }, [step, stepValid]);\n\n React.useEffect(() => {\n if (!boundsValid) {\n warnOnce(\n `TimePicker:bounds:${minTime}|${maxTime}`,\n `TimePicker: \\`minTime\\`/\\`maxTime\\` must be times such as \"09:00\", \"09:00:00\" or ` +\n `\"9:00 AM\" with minTime <= maxTime (received \"${minTime}\" / \"${maxTime}\"); no times are available.`,\n );\n }\n }, [minTime, maxTime, boundsValid]);\n\n /* ---- open state, draft text and query -------------------------- */\n\n const interactive = !disabled && !readOnly;\n const [openState, setOpen, openControlled] = useControllable<boolean>(\n openProp,\n // A picker that starts disabled or read-only never shows its list, so it starts closed (no\n // close to report later).\n (defaultOpen ?? false) && interactive,\n onOpenChange,\n );\n // The open list lives in a portal, which renders only in the browser: until then (the server\n // HTML, hydration) the picker shows the closed inline list and reports it closed, so\n // aria-controls and aria-activedescendant never name a missing element.\n const isClient = useIsClient();\n const open = openState && interactive && isClient;\n // A list hidden because the picker became disabled or read-only is closed for good\n // (uncontrolled), so enabling the picker again does not bring it back; onOpenChange(false)\n // reports it (a consumer callback, so from an effect). A controlled `open` stays the parent's.\n React.useEffect(() => {\n if (!interactive && openState && !openControlled) setOpen(false);\n }, [interactive, openState, openControlled, setOpen]);\n /** Typed text; `null` shows the selected time's label (draft model). */\n const [draft, setDraft] = React.useState<string | null>(null);\n /** Why the typed text was rejected (it is kept and flagged); `null` while it is not. */\n const [invalid, setInvalid] = React.useState<TimePickerInvalidReason | null>(null);\n // Locking the picker while the user types drops the typed text and its error, so no later blur\n // commits it (adjusted during render, C-HOOKS).\n const [wasInteractive, setWasInteractive] = React.useState(interactive);\n if (wasInteractive !== interactive) {\n setWasInteractive(interactive);\n if (!interactive) {\n setDraft(null);\n setInvalid(null);\n }\n }\n // A new value from the parent replaces rejected text (the picker's own commits clear it\n // themselves), so the input never shows an error next to a valid value. Text the user is still\n // typing stays.\n const [seenValue, setSeenValue] = React.useState(selectedValue);\n if (seenValue !== selectedValue) {\n setSeenValue(selectedValue);\n if (invalid !== null) {\n setDraft(null);\n setInvalid(null);\n }\n }\n /**\n * Filter text: the typed text only, never the selected time's label, so opening without an edit\n * shows every option. Typed text kept while the list is closed (Escape) keeps filtering it, so\n * a reopened list shows what the input says.\n */\n const query = draft ?? '';\n\n const openList = () => {\n if (!interactive || open) return;\n setOpen(true);\n };\n const closeList = () => setOpen(false);\n\n // A complete time in any spelling the commit accepts (`2:00pm`, `09:00 AM`, `14:00:00`) keeps its\n // own option, so it stays listed and active, as Enter and blur commit it.\n const filter = React.useMemo(() => {\n const text = query.trim().toLowerCase();\n if (!text) return undefined;\n const minutes = timeToMinutes(text);\n const exact = Number.isNaN(minutes) ? null : minutesToValue(minutes);\n return (item: ListboxItem) => item.value === exact || matchesQuery(item, text);\n }, [query]);\n\n /**\n * The option that typing `text` makes active. A complete time (`2:00 PM`, `14:00`) activates its\n * own option, never another one that merely contains the text (`12:00 PM`); when it is not in\n * the list (off-grid or out of bounds) nothing is active, so Enter commits the typed time or\n * nothing. Partial text activates the first option whose label or value starts with it (`2:00 p`\n * -> `2:00 PM`, `2` -> `2:00 AM`, not `12:00 AM`), else the first option that contains it.\n */\n const getTypedActiveValue = (text: string): string | null => {\n const needle = text.trim().toLowerCase();\n if (!needle) return null;\n const minutes = timeToMinutes(text);\n if (!Number.isNaN(minutes)) {\n const exact = minutesToValue(minutes);\n return allOptions.some((item) => item.value === exact) ? exact : null;\n }\n const match =\n allOptions.find((item) => startsWithQuery(item, needle)) ??\n allOptions.find((item) => matchesQuery(item, needle));\n return match?.value ?? null;\n };\n\n // Opening without typing activates the selected option only when it is in the list. An empty,\n // off-grid or out-of-range value activates nothing (ArrowDown/ArrowUp start at the first/last\n // option), so Enter on a freshly opened list commits nothing and the surrounding form submits.\n const selectedInList =\n selectedValue !== '' && allOptions.some((item) => item.value === selectedValue);\n\n const selectedValues = React.useMemo(\n () => (selectedValue ? [selectedValue] : []),\n [selectedValue],\n );\n\n const commitValue = (next: string) => {\n setSelectedValue(next);\n setDraft(null);\n setInvalid(null);\n };\n\n /**\n * Commits the typed text: erased text clears the value, a complete time within the bounds selects\n * it. Returns why any other text is not accepted (it is kept), else `null`.\n */\n const commitDraft = (text: string): TimePickerInvalidReason | null => {\n if (!text.trim()) {\n commitValue('');\n return null;\n }\n const minutes = timeToMinutes(text);\n if (Number.isNaN(minutes)) return 'unparseable';\n if (!boundsValid || minutes < minMinutes || minutes > maxMinutes) return 'out-of-range';\n commitValue(minutesToValue(minutes));\n return null;\n };\n\n /** Keeps rejected text, flags it and reports it. */\n const rejectDraft = (text: string, reason: TimePickerInvalidReason) => {\n setInvalid(reason);\n onInvalidInput?.(text, reason);\n };\n\n const clearDraft = () => {\n setDraft(null);\n setInvalid(null);\n };\n\n // The listbox counts as open only while it shows options: with no match nothing is displayed,\n // so `aria-expanded` is false and Escape reverts the typed text instead of closing an empty list.\n const hasMatches = React.useMemo(\n () => (filter ? allOptions.some(filter) : allOptions.length > 0),\n [allOptions, filter],\n );\n const expanded = open && hasMatches;\n\n const lb = useListbox({\n open: expanded,\n onOpenChange: (next) => (next ? openList() : closeList()),\n mode: 'editable',\n selectedValues,\n onSelect: (next) => commitValue(next),\n items: allOptions,\n filter,\n // Typing sets the active option itself (getTypedActiveValue in handleInputChange), so an exact\n // typed time wins over an earlier substring match; highlightOnFilter would force the first one.\n autoHighlight: draft === null && selectedInList ? 'selected' : false,\n idPrefix: 'timepicker-listbox',\n // Escape on a closed list reverts typed text (erased text: handleKeyDown); without a draft it is\n // left to enclosing layers.\n onClearDraft:\n draft !== null\n ? () => {\n clearDraft();\n closeList();\n }\n : undefined,\n });\n\n const displayLabel = selectedValue\n ? (lb.getItem(selectedValue)?.label ?? displayTime(selectedValue, format))\n : '';\n // Read-only pickers offer no clear action (the value cannot change).\n const showClear = clearable && !readOnly && selectedValue !== '';\n const statusMessage =\n open && !hasMatches\n ? allOptions.length === 0\n ? (labels?.noTimes ?? 'No times available')\n : (labels?.noMatches ?? 'No matching times')\n : '';\n\n /* ---- elements, dismissal, positioning --------------------------- */\n\n const rootRef = React.useRef<HTMLDivElement | null>(null);\n const inputRef = React.useRef<HTMLInputElement | null>(null);\n const surfaceRef = React.useRef<HTMLDivElement | null>(null);\n const rootRefs = useMergedRefs<HTMLDivElement>(ref, rootRef);\n const inputRefs = useMergedRefs<HTMLInputElement>(controlRef, inputRef);\n\n const { layerId } = useDismiss({\n open,\n onDismiss: closeList,\n refs: [surfaceRef, rootRef],\n anchorRef: inputRef,\n kind: 'listbox',\n focusOutside: true,\n });\n\n const { setReference, setFloating, floatingProps } = usePopupPosition({\n open,\n side: 'bottom',\n align: 'start',\n matchReferenceWidth: true,\n fitViewport: true,\n });\n const surfaceRefs = useMergedRefs<HTMLDivElement>(surfaceRef, setFloating);\n\n /* ---- forms and Field ------------------------------------------- */\n\n const field = useFieldContext();\n const isRequired = required ?? field?.required ?? false;\n // Like a native readonly input, a read-only picker is barred from constraint validation (the\n // user could not fix it); its value is still submitted.\n const validates = isRequired && !readOnly;\n const errorId = useId('timepicker-error');\n // A Field that shows an error describes the input itself (its message is not repeated).\n const showOwnError = invalid !== null && !field?.hasErrorMessage;\n const fieldProps = useFieldControl({\n id,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n 'aria-describedby': joinIds(ariaDescribedBy, showOwnError ? errorId : undefined),\n 'aria-invalid': invalid !== null ? true : ariaInvalid,\n // An explicit `required={false}` wins over a required Field, so aria-required matches\n // `isRequired`.\n 'aria-required': ariaRequired ?? required,\n });\n const invalidLook = isInvalidLook(false, fieldProps['aria-invalid']);\n\n useFormReset(\n inputRef,\n () => {\n setSelectedValue(defaultValue ?? '');\n clearDraft();\n closeList();\n },\n form,\n );\n\n /* ---- handlers --------------------------------------------------- */\n\n const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {\n if (!interactive) return;\n const text = event.target.value;\n setDraft(text);\n setInvalid(null);\n if (!open) setOpen(true);\n // Every edit re-ranks the highlight (it opens the list in the same update when closed).\n lb.setActiveValue(getTypedActiveValue(text));\n };\n\n const handleKeyDown = composeEventHandlers(\n onKeyDown,\n (event: React.KeyboardEvent<HTMLInputElement>) => {\n if (!interactive) return;\n // Enter commits the active option: the exact typed time, the first match of partial text,\n // or the option moved to with the arrow keys.\n lb.onKeyDown(event);\n // Keys that belong to an IME composition (its confirming Enter) are left to the IME, as\n // useListbox leaves them.\n if (event.defaultPrevented || event.nativeEvent.isComposing) return;\n if (event.key === 'Escape' && draft === '' && displayLabel !== '') {\n // Erased text: useListbox calls onClearDraft only while the input has text, so Escape on a\n // closed list restores the selected time here (an open list was closed above). With nothing\n // to restore, Escape is left to enclosing layers.\n event.preventDefault();\n clearDraft();\n closeList();\n return;\n }\n if (event.key === 'Enter' && draft !== null) {\n // Edited text is committed (a complete time within the bounds; erased text clears) or kept\n // and flagged (reported on every Enter), never submitted with the form; untouched text\n // lets Enter submit. The list closes either way, so it does not cover the error message.\n event.preventDefault();\n const reason = commitDraft(draft);\n closeList();\n if (reason !== null) rejectDraft(draft, reason);\n }\n if (event.key === 'Tab' && !event.shiftKey && showClear && draft !== null && !draft.trim()) {\n // Erased text clears the value, and with it the clear button, the next tab stop. It is\n // settled before Tab moves focus (the blur would settle it while focus moves to that\n // button), so focus moves on to the tab stop after the picker instead of dropping to\n // <body> with the button (C-DISABLED).\n flushSync(() => commitDraft(draft));\n }\n if (event.key === 'Tab' && open && !expanded) {\n // useListbox closes the list on Tab only while it shows options. The status text shown\n // instead ('No matching times', 'No times available') closes too: the clear button, the\n // next tab stop, is inside the picker, so focus moving there does not dismiss it.\n closeList();\n }\n },\n );\n const handleKeyUp = composeEventHandlers(onKeyUp, lb.onKeyUp);\n\n const handleInputClick = () => {\n if (!interactive || open) return;\n openList();\n // Typed text kept after Escape resumes where typing left it: its matches (the filter) and the\n // option typing made active, so Enter commits the same time as before the list closed.\n if (draft !== null) lb.setActiveValue(getTypedActiveValue(draft));\n };\n\n const handleBlur = composeEventHandlers(onBlur, () => {\n if (!interactive || draft === null) return;\n // Leaving the input settles the edit as Enter does: rejected text is kept (text that was\n // already rejected is not reported again) and the list closes either way, so it does not cover\n // the error message.\n const reason = invalid === null ? commitDraft(draft) : null;\n closeList();\n if (reason !== null) rejectDraft(draft, reason);\n });\n\n // The expand button opens the list as a click in the input does (typed text kept after Escape\n // resumes its matches) and closes it; focus stays in (or returns to) the input.\n const handleExpandClick = () => {\n if (!interactive) return;\n if (open) closeList();\n else handleInputClick();\n inputRef.current?.focus();\n };\n\n const handleClear = () => {\n if (!interactive) return;\n commitValue('');\n inputRef.current?.focus();\n };\n\n /* ---- render ---------------------------------------------------- */\n\n const listboxLabelledBy = joinIds(ariaLabelledBy, field?.labelId);\n const renderListbox = (hidden: boolean) => (\n <ul\n {...lb.getListboxProps()}\n aria-labelledby={listboxLabelledBy}\n aria-label={listboxLabelledBy ? undefined : (ariaLabel ?? labels?.list ?? 'Times')}\n hidden={hidden || undefined}\n className={cn(\n timePickerClassNames.listbox,\n 'max-h-60 min-h-0 overflow-auto py-1 focus:outline-hidden',\n )}\n >\n {lb.items.map((item) => (\n <TimePickerOption key={item.value} value={item.value} label={item.label} />\n ))}\n </ul>\n );\n\n const showExpand = showsExpandButton(expandIcon);\n\n let errorMessage = '';\n if (showOwnError && invalid === 'unparseable') {\n errorMessage = (labels?.invalidTime ?? defaultInvalidTimeLabel)(\n format === '24h' ? 'HH:mm' : 'h:mm AM',\n );\n } else if (showOwnError) {\n // Bounds the consumer did not set are `undefined`, not the defaults.\n errorMessage = (labels?.outOfRange ?? defaultOutOfRangeLabel)(\n props.minTime === undefined ? undefined : displayTime(minTime, format),\n props.maxTime === undefined ? undefined : displayTime(maxTime, format),\n );\n }\n // aria-controls must name a listbox that is actually mounted. The closed list is inline only\n // while options exist; the open list is mounted only while something matches.\n const listboxMounted = open ? hasMatches : allOptions.length > 0;\n\n return (\n <ListboxContext.Provider value={lb.context}>\n <div\n {...rest}\n ref={rootRefs}\n className={cn(timePickerClassNames.root, 'relative inline-flex flex-col', className)}\n >\n <div ref={setReference} className=\"relative flex items-center\">\n <input\n ref={inputRefs}\n type=\"text\"\n {...lb.getComboboxProps()}\n aria-controls={listboxMounted ? lb.listboxId : undefined}\n {...fieldProps}\n aria-errormessage={ariaErrorMessage}\n aria-details={ariaDetails}\n value={draft ?? displayLabel}\n onChange={handleInputChange}\n onClick={handleInputClick}\n onFocus={onFocus}\n onBlur={handleBlur}\n onKeyDown={handleKeyDown}\n onKeyUp={handleKeyUp}\n placeholder={placeholder}\n disabled={disabled}\n readOnly={readOnly}\n autoComplete={autoComplete}\n autoCapitalize={autoCapitalize}\n autoCorrect={autoCorrect}\n maxLength={maxLength}\n enterKeyHint={enterKeyHint}\n inputMode={inputMode}\n spellCheck={spellCheck}\n autoFocus={autoFocus}\n tabIndex={tabIndex}\n className={cn(\n timePickerClassNames.input,\n inputBase,\n 'border-b-stroke-accessible',\n inputFocus,\n disabledStyles,\n invalidLook && inputInvalid,\n pickerEndPadding(Number(showClear) + Number(showExpand)),\n )}\n />\n {showClear && (\n <button\n type=\"button\"\n aria-label={labels?.clear ?? 'Clear time'}\n disabled={disabled}\n // Keeps focus (and an open list) on the input while the pointer clears it.\n onMouseDown={(event) => event.preventDefault()}\n onClick={handleClear}\n className={cn(\n timePickerClassNames.clearButton,\n PICKER_ICON_BUTTON_CLASSES,\n showExpand ? 'end-7' : 'end-1',\n focusRing,\n disabledStyles,\n )}\n >\n <DismissIcon />\n </button>\n )}\n {showExpand && (\n <PickerExpandButton\n component=\"TimePicker\"\n expandIcon={expandIcon}\n label={labels?.expand ?? 'Show times'}\n expanded={expanded}\n listboxId={lb.listboxId}\n disabled={disabled || !!readOnly}\n onToggle={handleExpandClick}\n className={timePickerClassNames.expandButton}\n iconClassName={timePickerClassNames.expandIcon}\n />\n )}\n </div>\n {showOwnError && (\n <p\n id={errorId}\n role=\"alert\"\n className={cn(timePickerClassNames.errorMessage, 'mt-1 text-caption-1 text-error')}\n >\n {errorMessage}\n </p>\n )}\n {!open && allOptions.length > 0 && renderListbox(true)}\n <span role=\"status\" className=\"sr-only\">\n {statusMessage}\n </span>\n <HiddenInput\n name={name}\n form={form}\n disabled={disabled}\n value={selectedValue}\n type=\"text\"\n required={validates}\n onInvalid={() => inputRef.current?.focus()}\n />\n {open && (\n <Portal layerId={layerId}>\n <div\n ref={surfaceRefs}\n {...floatingProps}\n data-state=\"open\"\n className={cn(\n timePickerClassNames.surface,\n 'flex flex-col overflow-hidden rounded border border-border bg-background shadow-8',\n )}\n >\n {hasMatches ? (\n renderListbox(false)\n ) : (\n <div\n aria-hidden=\"true\"\n className={cn(\n timePickerClassNames.emptyMessage,\n 'px-3 py-1.5 text-body-1 text-muted-foreground',\n )}\n >\n {statusMessage}\n </div>\n )}\n </div>\n </Portal>\n )}\n </div>\n </ListboxContext.Provider>\n );\n};\n\nTimePicker.displayName = 'TimePicker';\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"TimePicker.mjs","names":["useId","useId$1","React","OPTION_CLASSES","TimePickerOption","useListboxOption","optionProps","cn","timePickerClassNames","selected","forcedColors","CheckIcon","label","defaultInvalidTimeLabel","format","defaultOutOfRangeLabel","min","max","displayTime","minutes","timeToMinutes","value","minutesToTime","matchesQuery","item","text","startsWithQuery","TimePicker","valueProp","sizeProp","appearanceProp","openProp","ariaLabel","ariaLabelledBy","ariaDescribedBy","ariaInvalid","ariaRequired","ariaErrorMessage","ariaDetails","rest","props","onChange","warnDeprecated","useInputLook","selectedValue","setSelectedValue","useControllable","defaultValue","onValueChange","next","stepValid","normalizeTimeStep","step","minMinutes","minTime","maxMinutes","maxTime","boundsValid","allOptions","generateTimeOptions","warnOnce","interactive","disabled","readOnly","openState","setOpen","openControlled","defaultOpen","onOpenChange","isClient","useIsClient","open","draft","setDraft","invalid","setInvalid","wasInteractive","setWasInteractive","seenValue","setSeenValue","query","openList","closeList","filter","exact","minutesToValue","getTypedActiveValue","needle","selectedInList","selectedValues","commitValue","commitDraft","rejectDraft","reason","onInvalidInput","clearDraft","hasMatches","expanded","lb","useListbox","displayLabel","showClear","clearable","statusMessage","labels","rootRef","inputRef","surfaceRef","rootRefs","useMergedRefs","ref","inputRefs","controlRef","useDismiss","usePopupPosition","surfaceRefs","setFloating","field","useFieldContext","validates","required","errorId","showOwnError","fieldProps","useFieldControl","joinIds","invalidLook","isInvalidLook","form","handleInputChange","event","handleKeyDown","composeEventHandlers","onKeyDown","flushSync","handleKeyUp","onKeyUp","handleInputClick","handleBlur","onBlur","handleExpandClick","handleClear","listboxLabelledBy","renderListbox","hidden","showExpand","showsExpandButton","expandIcon","errorMessage","listboxMounted","ListboxContext","size","appearance","className","setReference","inputHeightClasses","inputPaddingClasses","inputTextClasses","inputAppearanceClasses","inputFocus","disabledStyles","pickerEndPadding","pickerIconButtonClasses","pickerButtonOffset","DismissIcon","pickerGlyphSize","PickerExpandButton","HiddenInput","Portal","floatingProps"],"sources":["../../../src/components/input/TimePicker.tsx"],"sourcesContent":["import * as React from 'react';\nimport { flushSync } from 'react-dom';\nimport { cn } from '../../lib/cn';\nimport { joinIds } from '../../lib/aria';\nimport { timePickerClassNames } from '../../lib/classNames';\nimport { composeEventHandlers } from '../../lib/composeEventHandlers';\nimport { warnDeprecated, warnOnce } from '../../lib/dev';\nimport { CheckIcon, DismissIcon } from '../../lib/icons';\nimport type { Slot } from '../../lib/slot';\nimport {\n disabledStyles,\n focusRing,\n forcedColors,\n inputAppearanceClasses,\n inputFocus,\n inputHeightClasses,\n inputInvalid,\n inputPaddingClasses,\n inputTextClasses,\n} from '../../lib/styles';\nimport type { CoreSize, InputAppearance } from '../../lib/types';\nimport { useControllable } from '../../hooks/useControllable';\nimport { useDismiss } from '../../hooks/useDismiss';\nimport { useFieldContext, useFieldControl } from '../../hooks/useFieldControl';\nimport { useFormReset } from '../../hooks/useFormReset';\nimport { useId } from '../../hooks/useId';\nimport { useIsClient } from '../../hooks/useIsClient';\nimport { ListboxContext, useListbox, useListboxOption } from '../../hooks/useListbox';\nimport type { ListboxItem } from '../../hooks/useListbox';\nimport { useMergedRefs } from '../../hooks/useMergedRefs';\nimport { usePopupPosition } from '../../hooks/usePopupPosition';\nimport { HiddenInput } from '../internal/HiddenInput';\nimport { Portal } from '../portal/Portal';\nimport { PickerExpandButton, showsExpandButton } from './Combobox.expand';\nimport {\n DEFAULT_TIME_STEP,\n generateTimeOptions,\n minutesToTime,\n minutesToValue,\n normalizeTimeStep,\n timeToMinutes,\n} from './dateUtils';\nimport { useInputLook } from './inputLook';\nimport { isInvalidLook } from './Input';\nimport {\n pickerButtonOffset,\n pickerEndPadding,\n pickerGlyphSize,\n pickerIconButtonClasses,\n} from './pickerStyles';\nimport type { RoutedHandlers } from './routedHandlers';\n\n/* ------------------------------------------------------------------ */\n/* Option */\n/* ------------------------------------------------------------------ */\n\nconst OPTION_CLASSES =\n 'flex cursor-pointer items-center gap-2 px-3 py-1.5 text-body-1 text-foreground hover:bg-subtle-hover data-[active]:bg-subtle-hover data-[active]:outline-2 data-[active]:outline-ring data-[active]:-outline-offset-2 data-[selected]:bg-subtle-selected data-[selected]:font-semibold';\n\ninterface TimePickerOptionProps {\n value: string;\n label: string;\n}\n\n/**\n * One time option. Memoized: it re-renders only when its own active/selected state changes (read\n * from the listbox store), so moving the highlight renders two options, not the whole list.\n */\nconst TimePickerOption = React.memo(function TimePickerOption({\n value,\n label,\n}: TimePickerOptionProps) {\n const { selected, optionProps } = useListboxOption<HTMLLIElement>({ value, label });\n return (\n <li\n {...optionProps}\n className={cn(\n timePickerClassNames.option,\n OPTION_CLASSES,\n selected && forcedColors.selectedContainer,\n )}\n >\n <span\n className={cn(\n timePickerClassNames.checkIcon,\n 'flex h-4 w-4 shrink-0 items-center justify-center',\n )}\n >\n {selected ? <CheckIcon size={12} /> : null}\n </span>\n {label}\n </li>\n );\n});\nTimePickerOption.displayName = 'TimePickerOption';\n\n/* ------------------------------------------------------------------ */\n/* TimePicker */\n/* ------------------------------------------------------------------ */\n\n/** Why typed text was not accepted (see {@link TimePickerProps.onInvalidInput}). */\nexport type TimePickerInvalidReason = 'unparseable' | 'out-of-range';\n\n/**\n * The TimePicker's built-in texts, for localization. Each member is optional and falls back to its\n * English default. The times themselves follow `format`.\n */\nexport interface TimePickerLabels {\n /** Name of the clear button.\n * @default 'Clear time'\n */\n clear?: string;\n /** Name of the expand button at the end of the input.\n * @default 'Show times'\n */\n expand?: string;\n /** Name of the list of times when the picker has no `aria-label`, `aria-labelledby` or Field\n * label to lend it.\n * @default 'Times'\n */\n list?: string;\n /** Status text (announced and shown in the open list) when `minTime`/`maxTime` leave no times.\n * @default 'No times available'\n */\n noTimes?: string;\n /** Status text (announced and shown in the open list) when the typed text matches no time.\n * @default 'No matching times'\n */\n noMatches?: string;\n /**\n * Message for text that is not a time. `format` is the display pattern (`'HH:mm'` or\n * `'h:mm AM'`).\n * @default (format) => `Enter a time in the format ${format}.`\n */\n invalidTime?: (format: string) => string;\n /**\n * Message for a time outside `minTime`/`maxTime`. The bounds are passed as the input shows times\n * (`format`); a bound that is not set is `undefined`.\n * @default (min, max) => `Enter a time between ${min} and ${max}.` (only `min`: `Enter a time on\n * or after ${min}.`; only `max`: `Enter a time on or before ${max}.`)\n */\n outOfRange?: (min: string | undefined, max: string | undefined) => string;\n}\n\nconst defaultInvalidTimeLabel = (format: string) => `Enter a time in the format ${format}.`;\n\nconst defaultOutOfRangeLabel = (min: string | undefined, max: string | undefined) => {\n if (min !== undefined && max !== undefined) return `Enter a time between ${min} and ${max}.`;\n if (min !== undefined) return `Enter a time on or after ${min}.`;\n return `Enter a time on or before ${max}.`;\n};\n\n/** Properties for the TimePicker component. */\nexport interface TimePickerProps extends Omit<\n React.HTMLAttributes<HTMLDivElement>,\n 'onChange' | 'defaultValue' | 'placeholder' | RoutedHandlers\n> {\n /** Controlled selected time in `HH:mm` (24-hour) format; `''` for no time. */\n value?: string;\n /**\n * Initial time for uncontrolled usage (`HH:mm`).\n * @default ''\n */\n defaultValue?: string;\n /** Called with the new `HH:mm` value (`''` when cleared) whenever the selected time changes. */\n onValueChange?: (value: string) => void;\n /**\n * @deprecated Use `onValueChange`. Still called with the new value (warns once in development).\n */\n onChange?: (value: string) => void;\n /**\n * Time display format of the input and the options.\n * @default '12h'\n */\n format?: '12h' | '24h';\n /**\n * Minutes between options. A non-finite or non-positive value falls back to 30 (development\n * warning); fractions are floored.\n * @default 30\n */\n step?: number;\n /**\n * Earliest time (inclusive): `HH:mm`, `HH:mm:ss` or `h:mm AM`.\n * @default '00:00'\n */\n minTime?: string;\n /**\n * Latest time (inclusive): `HH:mm`, `HH:mm:ss` or `h:mm AM`. Invalid or reversed bounds give no\n * options (development warning, \"No times available\", see `labels`).\n * @default '23:59'\n */\n maxTime?: string;\n /**\n * Placeholder text shown when no time is selected.\n * @default 'Select a time'\n */\n placeholder?: string;\n /**\n * Whether the time picker is disabled and non-interactive.\n * @default false\n */\n disabled?: boolean;\n /**\n * Size of the field: `small` (24px tall), `medium` (32px) or `large` (40px). Default: the\n * surrounding Field's `size`, else `WaveProvider inputDefaults.size`, else `'medium'`. The time\n * list keeps its size.\n */\n size?: CoreSize;\n /**\n * Look of the field: `outline` (a full border), `underline` (a bottom stroke only),\n * `filled-darker` or `filled-lighter` (a fill without a visible stroke: give the field a visible\n * label). Default: `WaveProvider inputDefaults.appearance`, else `'outline'`.\n */\n appearance?: InputAppearance;\n /**\n * Makes the input read-only: the list does not open and the value cannot change. Turning it (or\n * `disabled`) on while the user is typing drops the typed text.\n */\n readOnly?: boolean;\n /**\n * Whether to show a clear button when a time is selected (not shown while `readOnly`; shown\n * disabled while `disabled`). It is a tab stop after the input. Tab from erased text clears the\n * value before focus moves, so focus goes on past the clear button that disappears with it.\n * @default false\n */\n clearable?: boolean;\n /**\n * The glyph of the expand button at the end of the input (default: a chevron). The button\n * opens and closes the list without moving focus out of the input and is not a tab stop\n * (Alt+ArrowDown opens the list from the keyboard). `null` or `undefined` keep the chevron;\n * `false`, or a value that renders nothing, hides the button. Decorative content rendered\n * inside the built-in button: a `<button>` or `Button` passed here is not nested (its children\n * become the glyph, with a development warning).\n */\n expandIcon?: Slot<'span'>;\n /**\n * Controlled open state of the list (never shown while `disabled` or `readOnly`). The list\n * renders only in the browser: an open picker is closed in the server HTML and opens once it has\n * hydrated.\n */\n open?: boolean;\n /**\n * Initial open state of the list for uncontrolled usage. A picker that starts disabled or\n * read-only starts closed. The list renders only in the browser: it is closed in the server HTML\n * and opens once the picker has hydrated.\n * @default false\n */\n defaultOpen?: boolean;\n /** Called when the list opens or closes (only when the state changes). */\n onOpenChange?: (open: boolean) => void;\n /**\n * Called when edited text is not accepted on Enter or blur (once per edit): it is not a time\n * (`'unparseable'`) or lies outside `minTime`/`maxTime` (`'out-of-range'`). The text stays in the\n * input, which is marked `aria-invalid` and described by an error message (left to the\n * surrounding `Field` when it shows an error); Enter and leaving the input also close the list,\n * so it does not cover the message. Enter reports it again when pressed again.\n */\n onInvalidInput?: (text: string, reason: TimePickerInvalidReason) => void;\n /**\n * Names of the clear and expand buttons and of the list, the status texts of an empty list and\n * the error messages of rejected text, for localization. Unset members keep their English\n * defaults.\n */\n labels?: TimePickerLabels;\n /** Form field name: the `HH:mm` value is submitted under it (hidden input). */\n name?: string;\n /** Id of the `<form>` the value belongs to, when the picker is outside it. */\n form?: string;\n /**\n * Blocks form submission while no time is selected; sets `aria-required` on the input. Like a\n * native readonly input, a `readOnly` TimePicker does not block submission.\n */\n required?: boolean;\n /** Native `autocomplete` of the input. @default 'off' */\n autoComplete?: string;\n /** Native `maxlength` of the input. */\n maxLength?: number;\n /** Focus handler of the text input (the root keeps the other handlers). */\n onFocus?: React.FocusEventHandler<HTMLInputElement>;\n /** Blur handler of the text input. */\n onBlur?: React.FocusEventHandler<HTMLInputElement>;\n /** Key handler of the text input; `preventDefault()` skips the picker's own keys. */\n onKeyDown?: React.KeyboardEventHandler<HTMLInputElement>;\n /** Key handler of the text input. */\n onKeyUp?: React.KeyboardEventHandler<HTMLInputElement>;\n /** Ref to the text input (the `role=\"combobox\"` element); `ref` stays on the root. */\n controlRef?: React.Ref<HTMLInputElement>;\n /** Ref to the root element. */\n ref?: React.Ref<HTMLDivElement>;\n}\n\nfunction displayTime(value: string, format: '12h' | '24h'): string {\n const minutes = timeToMinutes(value);\n return Number.isNaN(minutes) ? value : minutesToTime(minutes, format);\n}\n\n/** Whether an option matches the (trimmed, lower-case) typed text: by label or `HH:mm` value. */\nfunction matchesQuery(item: ListboxItem, text: string): boolean {\n return item.label.toLowerCase().includes(text) || item.value.includes(text);\n}\n\n/** Whether an option's label or `HH:mm` value starts with the (trimmed, lower-case) typed text. */\nfunction startsWithQuery(item: ListboxItem, text: string): boolean {\n return item.label.toLowerCase().startsWith(text) || item.value.startsWith(text);\n}\n\n/**\n * A time input with a filterable list of times (APG editable combobox with list autocomplete).\n *\n * - Type to filter (by label or `HH:mm` value). A complete typed time (`2:00 PM`, `14:00`) makes\n * its own option active — not an earlier one that merely contains the text, such as `12:00 PM`\n * — and partial text makes the first option that starts with it active (`2:00 p` -> `2:00 PM`),\n * else the first one that contains it; Enter commits the active option. Enter or leaving the\n * field also commits a complete typed time that is not in the list (`9:15 AM`, `14:45`) when it\n * lies within `minTime`/`maxTime`, and erased text clears the value (as the clear button does);\n * Enter never submits the form with edited text, and both close the list. Other typed text is\n * kept: the input is marked invalid and an error message describes it (`onInvalidInput`,\n * reported once per edit) until the text is edited or replaced (an option, the clear button,\n * Escape, a form reset, a new value from the parent). Escape on a closed list reverts any edit,\n * erased text included, to the selected time.\n * - Click the input or the expand button at its end (a chevron, see `expandIcon`; not a tab stop,\n * and it leaves focus in the input), press ArrowDown/ArrowUp or type to open; the list opens\n * with every option and the selected one (when it is in the list) active and scrolled into\n * view. Without one, no option is active until ArrowDown/ArrowUp or typing, so Enter lets the\n * surrounding form submit. Escape, Tab, an outside press or focus leaving closes it. Typed text\n * kept after Escape closed the list still filters it when it reopens: a click resumes the option\n * typing made active, ArrowDown/ArrowUp start at the first/last match.\n * - `open`/`defaultOpen`/`onOpenChange` control the list. It closes when the picker becomes\n * disabled or read-only (uncontrolled `open`: it stays closed when the picker is enabled again),\n * and the text typed until then is dropped.\n * - Keys of an IME composition (its confirming Enter included) are left to the IME.\n * - `clearable` shows a clear button while a time is selected (not while read-only), a tab stop\n * after the input. Tab from erased text clears the value first, so focus moves on past the\n * picker instead of to the clear button that disappears.\n * - The value is `HH:mm` (24-hour) whatever the display `format`; values off the `step` grid or\n * outside the bounds are still displayed in `format`.\n * - **Size and appearance**: `size` resolves from its own prop, then the surrounding `Field`'s\n * `size`, then `WaveProvider inputDefaults.size`, else `'medium'`; `appearance` from its own\n * prop, then `WaveProvider inputDefaults.appearance`, else `'outline'`; both render as\n * `data-size` and `data-appearance` on the root `<div>`. The time list keeps its size.\n * - The input (`controlRef`) receives `id`, `aria-label`, `aria-labelledby`, `aria-describedby`,\n * `aria-invalid`, `aria-required`, `aria-errormessage`, `aria-details`, `tabIndex`,\n * `autoFocus`, `onFocus`/`onBlur`/`onKeyDown`/`onKeyUp` and the text input attributes\n * `autoComplete`, `autoCapitalize`, `autoCorrect`, `maxLength`, `inputMode`, `spellCheck` and\n * `enterKeyHint`. `ref`, `className`, `style`, other `aria-*` attributes and the remaining props\n * stay on the root `<div>`. Inside a `Field`, the input is labelled and described by it.\n * - `name`/`required` add a hidden input for native forms (`HH:mm`); the value resets with its form.\n * - The built-in names, status texts and error messages are English; `labels` localizes them.\n * - The open list renders only in the browser: an open list (`defaultOpen`, `open`) is closed in\n * the server HTML and opens once the picker has hydrated.\n */\nexport const TimePicker = (props: TimePickerProps) => {\n const {\n value: valueProp,\n defaultValue,\n onValueChange,\n onChange,\n format = '12h',\n step = DEFAULT_TIME_STEP,\n minTime = '00:00',\n maxTime = '23:59',\n placeholder = 'Select a time',\n disabled = false,\n size: sizeProp,\n appearance: appearanceProp,\n readOnly,\n clearable = false,\n expandIcon,\n open: openProp,\n defaultOpen,\n onOpenChange,\n onInvalidInput,\n labels,\n name,\n form,\n required,\n autoComplete = 'off',\n autoCapitalize,\n autoCorrect,\n maxLength,\n enterKeyHint,\n inputMode,\n spellCheck,\n autoFocus,\n tabIndex,\n id,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n 'aria-describedby': ariaDescribedBy,\n 'aria-invalid': ariaInvalid,\n 'aria-required': ariaRequired,\n 'aria-errormessage': ariaErrorMessage,\n 'aria-details': ariaDetails,\n onFocus,\n onBlur,\n onKeyDown,\n onKeyUp,\n controlRef,\n className,\n ref,\n ...rest\n } = props;\n\n if (onChange !== undefined) warnDeprecated('TimePicker', 'onChange', 'onValueChange');\n\n const { size, appearance } = useInputLook(sizeProp, appearanceProp);\n\n const [selectedValue, setSelectedValue] = useControllable<string>(\n valueProp,\n defaultValue ?? '',\n (next) => {\n onValueChange?.(next);\n onChange?.(next);\n },\n );\n\n /* ---- options, step and bounds ---------------------------------- */\n\n const stepValid = normalizeTimeStep(step).valid;\n const minMinutes = timeToMinutes(minTime);\n const maxMinutes = timeToMinutes(maxTime);\n const boundsValid =\n !Number.isNaN(minMinutes) && !Number.isNaN(maxMinutes) && minMinutes <= maxMinutes;\n const allOptions = React.useMemo(\n () => generateTimeOptions(step, minTime, maxTime, format),\n [step, minTime, maxTime, format],\n );\n\n // Once per invalid value and page (C-DEV), not once per instance and effect run.\n React.useEffect(() => {\n if (!stepValid) {\n warnOnce(\n `TimePicker:step:${String(step)}`,\n `TimePicker: \\`step\\` must be a positive number of minutes (received ${String(step)}); ` +\n `using ${DEFAULT_TIME_STEP}.`,\n );\n }\n }, [step, stepValid]);\n\n React.useEffect(() => {\n if (!boundsValid) {\n warnOnce(\n `TimePicker:bounds:${minTime}|${maxTime}`,\n `TimePicker: \\`minTime\\`/\\`maxTime\\` must be times such as \"09:00\", \"09:00:00\" or ` +\n `\"9:00 AM\" with minTime <= maxTime (received \"${minTime}\" / \"${maxTime}\"); no times are available.`,\n );\n }\n }, [minTime, maxTime, boundsValid]);\n\n /* ---- open state, draft text and query -------------------------- */\n\n const interactive = !disabled && !readOnly;\n const [openState, setOpen, openControlled] = useControllable<boolean>(\n openProp,\n // A picker that starts disabled or read-only never shows its list, so it starts closed (no\n // close to report later).\n (defaultOpen ?? false) && interactive,\n onOpenChange,\n );\n // The open list lives in a portal, which renders only in the browser: until then (the server\n // HTML, hydration) the picker shows the closed inline list and reports it closed, so\n // aria-controls and aria-activedescendant never name a missing element.\n const isClient = useIsClient();\n const open = openState && interactive && isClient;\n // A list hidden because the picker became disabled or read-only is closed for good\n // (uncontrolled), so enabling the picker again does not bring it back; onOpenChange(false)\n // reports it (a consumer callback, so from an effect). A controlled `open` stays the parent's.\n React.useEffect(() => {\n if (!interactive && openState && !openControlled) setOpen(false);\n }, [interactive, openState, openControlled, setOpen]);\n /** Typed text; `null` shows the selected time's label (draft model). */\n const [draft, setDraft] = React.useState<string | null>(null);\n /** Why the typed text was rejected (it is kept and flagged); `null` while it is not. */\n const [invalid, setInvalid] = React.useState<TimePickerInvalidReason | null>(null);\n // Locking the picker while the user types drops the typed text and its error, so no later blur\n // commits it (adjusted during render, C-HOOKS).\n const [wasInteractive, setWasInteractive] = React.useState(interactive);\n if (wasInteractive !== interactive) {\n setWasInteractive(interactive);\n if (!interactive) {\n setDraft(null);\n setInvalid(null);\n }\n }\n // A new value from the parent replaces rejected text (the picker's own commits clear it\n // themselves), so the input never shows an error next to a valid value. Text the user is still\n // typing stays.\n const [seenValue, setSeenValue] = React.useState(selectedValue);\n if (seenValue !== selectedValue) {\n setSeenValue(selectedValue);\n if (invalid !== null) {\n setDraft(null);\n setInvalid(null);\n }\n }\n /**\n * Filter text: the typed text only, never the selected time's label, so opening without an edit\n * shows every option. Typed text kept while the list is closed (Escape) keeps filtering it, so\n * a reopened list shows what the input says.\n */\n const query = draft ?? '';\n\n const openList = () => {\n if (!interactive || open) return;\n setOpen(true);\n };\n const closeList = () => setOpen(false);\n\n // A complete time in any spelling the commit accepts (`2:00pm`, `09:00 AM`, `14:00:00`) keeps its\n // own option, so it stays listed and active, as Enter and blur commit it.\n const filter = React.useMemo(() => {\n const text = query.trim().toLowerCase();\n if (!text) return undefined;\n const minutes = timeToMinutes(text);\n const exact = Number.isNaN(minutes) ? null : minutesToValue(minutes);\n return (item: ListboxItem) => item.value === exact || matchesQuery(item, text);\n }, [query]);\n\n /**\n * The option that typing `text` makes active. A complete time (`2:00 PM`, `14:00`) activates its\n * own option, never another one that merely contains the text (`12:00 PM`); when it is not in\n * the list (off-grid or out of bounds) nothing is active, so Enter commits the typed time or\n * nothing. Partial text activates the first option whose label or value starts with it (`2:00 p`\n * -> `2:00 PM`, `2` -> `2:00 AM`, not `12:00 AM`), else the first option that contains it.\n */\n const getTypedActiveValue = (text: string): string | null => {\n const needle = text.trim().toLowerCase();\n if (!needle) return null;\n const minutes = timeToMinutes(text);\n if (!Number.isNaN(minutes)) {\n const exact = minutesToValue(minutes);\n return allOptions.some((item) => item.value === exact) ? exact : null;\n }\n const match =\n allOptions.find((item) => startsWithQuery(item, needle)) ??\n allOptions.find((item) => matchesQuery(item, needle));\n return match?.value ?? null;\n };\n\n // Opening without typing activates the selected option only when it is in the list. An empty,\n // off-grid or out-of-range value activates nothing (ArrowDown/ArrowUp start at the first/last\n // option), so Enter on a freshly opened list commits nothing and the surrounding form submits.\n const selectedInList =\n selectedValue !== '' && allOptions.some((item) => item.value === selectedValue);\n\n const selectedValues = React.useMemo(\n () => (selectedValue ? [selectedValue] : []),\n [selectedValue],\n );\n\n const commitValue = (next: string) => {\n setSelectedValue(next);\n setDraft(null);\n setInvalid(null);\n };\n\n /**\n * Commits the typed text: erased text clears the value, a complete time within the bounds selects\n * it. Returns why any other text is not accepted (it is kept), else `null`.\n */\n const commitDraft = (text: string): TimePickerInvalidReason | null => {\n if (!text.trim()) {\n commitValue('');\n return null;\n }\n const minutes = timeToMinutes(text);\n if (Number.isNaN(minutes)) return 'unparseable';\n if (!boundsValid || minutes < minMinutes || minutes > maxMinutes) return 'out-of-range';\n commitValue(minutesToValue(minutes));\n return null;\n };\n\n /** Keeps rejected text, flags it and reports it. */\n const rejectDraft = (text: string, reason: TimePickerInvalidReason) => {\n setInvalid(reason);\n onInvalidInput?.(text, reason);\n };\n\n const clearDraft = () => {\n setDraft(null);\n setInvalid(null);\n };\n\n // The listbox counts as open only while it shows options: with no match nothing is displayed,\n // so `aria-expanded` is false and Escape reverts the typed text instead of closing an empty list.\n const hasMatches = React.useMemo(\n () => (filter ? allOptions.some(filter) : allOptions.length > 0),\n [allOptions, filter],\n );\n const expanded = open && hasMatches;\n\n const lb = useListbox({\n open: expanded,\n onOpenChange: (next) => (next ? openList() : closeList()),\n mode: 'editable',\n selectedValues,\n onSelect: (next) => commitValue(next),\n items: allOptions,\n filter,\n // Typing sets the active option itself (getTypedActiveValue in handleInputChange), so an exact\n // typed time wins over an earlier substring match; highlightOnFilter would force the first one.\n autoHighlight: draft === null && selectedInList ? 'selected' : false,\n idPrefix: 'timepicker-listbox',\n // Escape on a closed list reverts typed text (erased text: handleKeyDown); without a draft it is\n // left to enclosing layers.\n onClearDraft:\n draft !== null\n ? () => {\n clearDraft();\n closeList();\n }\n : undefined,\n });\n\n const displayLabel = selectedValue\n ? (lb.getItem(selectedValue)?.label ?? displayTime(selectedValue, format))\n : '';\n // Read-only pickers offer no clear action (the value cannot change).\n const showClear = clearable && !readOnly && selectedValue !== '';\n const statusMessage =\n open && !hasMatches\n ? allOptions.length === 0\n ? (labels?.noTimes ?? 'No times available')\n : (labels?.noMatches ?? 'No matching times')\n : '';\n\n /* ---- elements, dismissal, positioning --------------------------- */\n\n const rootRef = React.useRef<HTMLDivElement | null>(null);\n const inputRef = React.useRef<HTMLInputElement | null>(null);\n const surfaceRef = React.useRef<HTMLDivElement | null>(null);\n const rootRefs = useMergedRefs<HTMLDivElement>(ref, rootRef);\n const inputRefs = useMergedRefs<HTMLInputElement>(controlRef, inputRef);\n\n const { layerId } = useDismiss({\n open,\n onDismiss: closeList,\n refs: [surfaceRef, rootRef],\n anchorRef: inputRef,\n kind: 'listbox',\n focusOutside: true,\n });\n\n const { setReference, setFloating, floatingProps } = usePopupPosition({\n open,\n side: 'bottom',\n align: 'start',\n matchReferenceWidth: true,\n fitViewport: true,\n });\n const surfaceRefs = useMergedRefs<HTMLDivElement>(surfaceRef, setFloating);\n\n /* ---- forms and Field ------------------------------------------- */\n\n const field = useFieldContext();\n const isRequired = required ?? field?.required ?? false;\n // Like a native readonly input, a read-only picker is barred from constraint validation (the\n // user could not fix it); its value is still submitted.\n const validates = isRequired && !readOnly;\n const errorId = useId('timepicker-error');\n // A Field that shows an error describes the input itself (its message is not repeated).\n const showOwnError = invalid !== null && !field?.hasErrorMessage;\n const fieldProps = useFieldControl({\n id,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n 'aria-describedby': joinIds(ariaDescribedBy, showOwnError ? errorId : undefined),\n 'aria-invalid': invalid !== null ? true : ariaInvalid,\n // An explicit `required={false}` wins over a required Field, so aria-required matches\n // `isRequired`.\n 'aria-required': ariaRequired ?? required,\n });\n const invalidLook = isInvalidLook(false, fieldProps['aria-invalid']);\n\n useFormReset(\n inputRef,\n () => {\n setSelectedValue(defaultValue ?? '');\n clearDraft();\n closeList();\n },\n form,\n );\n\n /* ---- handlers --------------------------------------------------- */\n\n const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {\n if (!interactive) return;\n const text = event.target.value;\n setDraft(text);\n setInvalid(null);\n if (!open) setOpen(true);\n // Every edit re-ranks the highlight (it opens the list in the same update when closed).\n lb.setActiveValue(getTypedActiveValue(text));\n };\n\n const handleKeyDown = composeEventHandlers(\n onKeyDown,\n (event: React.KeyboardEvent<HTMLInputElement>) => {\n if (!interactive) return;\n // Enter commits the active option: the exact typed time, the first match of partial text,\n // or the option moved to with the arrow keys.\n lb.onKeyDown(event);\n // Keys that belong to an IME composition (its confirming Enter) are left to the IME, as\n // useListbox leaves them.\n if (event.defaultPrevented || event.nativeEvent.isComposing) return;\n if (event.key === 'Escape' && draft === '' && displayLabel !== '') {\n // Erased text: useListbox calls onClearDraft only while the input has text, so Escape on a\n // closed list restores the selected time here (an open list was closed above). With nothing\n // to restore, Escape is left to enclosing layers.\n event.preventDefault();\n clearDraft();\n closeList();\n return;\n }\n if (event.key === 'Enter' && draft !== null) {\n // Edited text is committed (a complete time within the bounds; erased text clears) or kept\n // and flagged (reported on every Enter), never submitted with the form; untouched text\n // lets Enter submit. The list closes either way, so it does not cover the error message.\n event.preventDefault();\n const reason = commitDraft(draft);\n closeList();\n if (reason !== null) rejectDraft(draft, reason);\n }\n if (event.key === 'Tab' && !event.shiftKey && showClear && draft !== null && !draft.trim()) {\n // Erased text clears the value, and with it the clear button, the next tab stop. It is\n // settled before Tab moves focus (the blur would settle it while focus moves to that\n // button), so focus moves on to the tab stop after the picker instead of dropping to\n // <body> with the button (C-DISABLED).\n flushSync(() => commitDraft(draft));\n }\n if (event.key === 'Tab' && open && !expanded) {\n // useListbox closes the list on Tab only while it shows options. The status text shown\n // instead ('No matching times', 'No times available') closes too: the clear button, the\n // next tab stop, is inside the picker, so focus moving there does not dismiss it.\n closeList();\n }\n },\n );\n const handleKeyUp = composeEventHandlers(onKeyUp, lb.onKeyUp);\n\n const handleInputClick = () => {\n if (!interactive || open) return;\n openList();\n // Typed text kept after Escape resumes where typing left it: its matches (the filter) and the\n // option typing made active, so Enter commits the same time as before the list closed.\n if (draft !== null) lb.setActiveValue(getTypedActiveValue(draft));\n };\n\n const handleBlur = composeEventHandlers(onBlur, () => {\n if (!interactive || draft === null) return;\n // Leaving the input settles the edit as Enter does: rejected text is kept (text that was\n // already rejected is not reported again) and the list closes either way, so it does not cover\n // the error message.\n const reason = invalid === null ? commitDraft(draft) : null;\n closeList();\n if (reason !== null) rejectDraft(draft, reason);\n });\n\n // The expand button opens the list as a click in the input does (typed text kept after Escape\n // resumes its matches) and closes it; focus stays in (or returns to) the input.\n const handleExpandClick = () => {\n if (!interactive) return;\n if (open) closeList();\n else handleInputClick();\n inputRef.current?.focus();\n };\n\n const handleClear = () => {\n if (!interactive) return;\n commitValue('');\n inputRef.current?.focus();\n };\n\n /* ---- render ---------------------------------------------------- */\n\n const listboxLabelledBy = joinIds(ariaLabelledBy, field?.labelId);\n const renderListbox = (hidden: boolean) => (\n <ul\n {...lb.getListboxProps()}\n aria-labelledby={listboxLabelledBy}\n aria-label={listboxLabelledBy ? undefined : (ariaLabel ?? labels?.list ?? 'Times')}\n hidden={hidden || undefined}\n className={cn(\n timePickerClassNames.listbox,\n 'max-h-60 min-h-0 overflow-auto py-1 focus:outline-hidden',\n )}\n >\n {lb.items.map((item) => (\n <TimePickerOption key={item.value} value={item.value} label={item.label} />\n ))}\n </ul>\n );\n\n const showExpand = showsExpandButton(expandIcon);\n\n let errorMessage = '';\n if (showOwnError && invalid === 'unparseable') {\n errorMessage = (labels?.invalidTime ?? defaultInvalidTimeLabel)(\n format === '24h' ? 'HH:mm' : 'h:mm AM',\n );\n } else if (showOwnError) {\n // Bounds the consumer did not set are `undefined`, not the defaults.\n errorMessage = (labels?.outOfRange ?? defaultOutOfRangeLabel)(\n props.minTime === undefined ? undefined : displayTime(minTime, format),\n props.maxTime === undefined ? undefined : displayTime(maxTime, format),\n );\n }\n // aria-controls must name a listbox that is actually mounted. The closed list is inline only\n // while options exist; the open list is mounted only while something matches.\n const listboxMounted = open ? hasMatches : allOptions.length > 0;\n\n return (\n <ListboxContext.Provider value={lb.context}>\n <div\n data-size={size}\n data-appearance={appearance}\n {...rest}\n ref={rootRefs}\n className={cn(timePickerClassNames.root, 'relative inline-flex flex-col', className)}\n >\n <div ref={setReference} className=\"relative flex items-center\">\n <input\n ref={inputRefs}\n type=\"text\"\n {...lb.getComboboxProps()}\n aria-controls={listboxMounted ? lb.listboxId : undefined}\n {...fieldProps}\n aria-errormessage={ariaErrorMessage}\n aria-details={ariaDetails}\n value={draft ?? displayLabel}\n onChange={handleInputChange}\n onClick={handleInputClick}\n onFocus={onFocus}\n onBlur={handleBlur}\n onKeyDown={handleKeyDown}\n onKeyUp={handleKeyUp}\n placeholder={placeholder}\n disabled={disabled}\n readOnly={readOnly}\n autoComplete={autoComplete}\n autoCapitalize={autoCapitalize}\n autoCorrect={autoCorrect}\n maxLength={maxLength}\n enterKeyHint={enterKeyHint}\n inputMode={inputMode}\n spellCheck={spellCheck}\n autoFocus={autoFocus}\n tabIndex={tabIndex}\n className={cn(\n timePickerClassNames.input,\n inputHeightClasses[size],\n 'w-full',\n inputPaddingClasses[size],\n inputTextClasses[size],\n inputAppearanceClasses[appearance],\n 'text-foreground placeholder:text-muted-foreground',\n inputFocus,\n disabledStyles,\n invalidLook && inputInvalid,\n pickerEndPadding(Number(showClear) + Number(showExpand), size),\n )}\n />\n {showClear && (\n <button\n type=\"button\"\n aria-label={labels?.clear ?? 'Clear time'}\n disabled={disabled}\n // Keeps focus (and an open list) on the input while the pointer clears it.\n onMouseDown={(event) => event.preventDefault()}\n onClick={handleClear}\n className={cn(\n timePickerClassNames.clearButton,\n pickerIconButtonClasses(size, appearance),\n pickerButtonOffset(size, showExpand ? 2 : 1),\n focusRing,\n disabledStyles,\n )}\n >\n <DismissIcon size={pickerGlyphSize(size, 'icon')} />\n </button>\n )}\n {showExpand && (\n <PickerExpandButton\n component=\"TimePicker\"\n expandIcon={expandIcon}\n label={labels?.expand ?? 'Show times'}\n expanded={expanded}\n listboxId={lb.listboxId}\n disabled={disabled || !!readOnly}\n onToggle={handleExpandClick}\n size={size}\n appearance={appearance}\n className={timePickerClassNames.expandButton}\n iconClassName={timePickerClassNames.expandIcon}\n />\n )}\n </div>\n {showOwnError && (\n <p\n id={errorId}\n role=\"alert\"\n className={cn(timePickerClassNames.errorMessage, 'mt-1 text-caption-1 text-error')}\n >\n {errorMessage}\n </p>\n )}\n {!open && allOptions.length > 0 && renderListbox(true)}\n <span role=\"status\" className=\"sr-only\">\n {statusMessage}\n </span>\n <HiddenInput\n name={name}\n form={form}\n disabled={disabled}\n value={selectedValue}\n type=\"text\"\n required={validates}\n onInvalid={() => inputRef.current?.focus()}\n />\n {open && (\n <Portal layerId={layerId}>\n <div\n ref={surfaceRefs}\n {...floatingProps}\n data-state=\"open\"\n className={cn(\n timePickerClassNames.surface,\n 'flex flex-col overflow-hidden rounded border border-border bg-background shadow-8',\n )}\n >\n {hasMatches ? (\n renderListbox(false)\n ) : (\n <div\n aria-hidden=\"true\"\n className={cn(\n timePickerClassNames.emptyMessage,\n 'px-3 py-1.5 text-body-1 text-muted-foreground',\n )}\n >\n {statusMessage}\n </div>\n )}\n </div>\n </Portal>\n )}\n </div>\n </ListboxContext.Provider>\n );\n};\n\nTimePicker.displayName = 'TimePicker';\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use client";
|
|
1
|
+
"use client";const e=require("../../lib/date.cjs");function t(e,t){return e.getFullYear()===t.getFullYear()&&e.getMonth()===t.getMonth()}function n(t,n,r){let i=e.startOfDay(t).getTime();return!(n&&i<e.startOfDay(n).getTime()||r&&i>e.startOfDay(r).getTime())}function r(t,n,r){let i=e.startOfDay(t);return n&&i.getTime()<e.startOfDay(n).getTime()?e.startOfDay(n):r&&i.getTime()>e.startOfDay(r).getTime()?e.startOfDay(r):i}function i(e){return Math.floor(e/10)*10}function a(t){return Array.from({length:12},(n,r)=>e.makeDate(t,r,1))}function o(t){return Array.from({length:12},(n,r)=>e.makeDate(t-1+r,0,1))}function s(t,n,r){let i=e.startOfMonth(t),a=e.makeDate(i.getFullYear(),i.getMonth()+1,0);return!!n&&a<e.startOfDay(n)||!!r&&i>e.startOfDay(r)}function c(t,n,r){return!!n&&e.makeDate(t,11,31)<e.startOfDay(n)||!!r&&e.makeDate(t,0,1)>e.startOfDay(r)}function l(t,n){let r=e.startOfMonth(t),i=(r.getDay()-n+7)%7,a=[];for(let t=0;t<42;t++)a.push(e.addDays(r,t-i));return a}function u(t,r,i,a){return!n(t,r,i)||!!a?.(e.startOfDay(t))}function d(t,n,r,i,a){let o=e.startOfDay(t),s;if(n===`day`)return[o];if(n===`month`){let t=e.startOfMonth(o);s=Array.from({length:e.daysInMonth(o.getFullYear(),o.getMonth())},(n,r)=>e.addDays(t,r))}else{let t=e.startOfWeek(o,r);s=Array.from({length:7},(n,r)=>e.addDays(t,r)),n===`work-week`&&(s=s.filter(e=>i.includes(e.getDay())))}return a?s.filter(e=>!a(e)):s}function f(e,t){return String(e).padStart(t,`0`)}function p(e){return`${f(e.getFullYear(),4)}-${f(e.getMonth()+1,2)}-${f(e.getDate(),2)}`}function m(t,n,r){return!Number.isInteger(t)||t<1||t>9999||!Number.isInteger(n)||n<1||n>12||!Number.isInteger(r)||r<1||r>e.daysInMonth(t,n-1)?null:e.makeDate(t,n-1,r)}function h(e){let t=/^(\d{4})-(\d{2})-(\d{2})$/.exec(e.trim());return t?m(Number(t[1]),Number(t[2]),Number(t[3])):null}function g(e){try{return Intl.getCanonicalLocales(e),!0}catch{return!1}}var _={year:`numeric`,month:`2-digit`,day:`2-digit`},v=new Set([`gregory`,`buddhist`]),y=new Map;function b(e){let t=e??``,n=y.get(t);if(n===void 0){let r;try{r=new Intl.DateTimeFormat(e).resolvedOptions().calendar}catch{r=new Intl.DateTimeFormat().resolvedOptions().calendar}n=v.has(r)?r:`gregory`,y.set(t,n)}return n}var x=new Map;function S(e,t){let n=`${e??``}|${JSON.stringify(t)}`,r=x.get(n);if(!r){let i={...t,calendar:b(e)};try{r=new Intl.DateTimeFormat(e,i)}catch{r=new Intl.DateTimeFormat(void 0,i)}x.set(n,r)}return r}var C=/[\u061c\u200b-\u200f\u202a-\u202e\u2066-\u2069]/g,w=new Map,T=e.makeDate(2001,10,23);function E(e,t){let n=``;for(let r of e.replace(C,``))n+=t.get(r)??r;return n}function D(e){let t=e??``,n=w.get(t);if(n)return n;let r=new Map,i;try{i=new Intl.NumberFormat(e,{useGrouping:!1})}catch{i=new Intl.NumberFormat(void 0,{useGrouping:!1})}for(let e=0;e<=9;e++)r.set(i.format(e),String(e));let a=S(e,_).formatToParts(T),o=[],s=new Set,c=``,l=0,u=4;for(let e of a)if(e.type===`day`||e.type===`month`||e.type===`year`){if(o.push(e.type),e.type===`year`){let t=E(e.value,r);u=t.length,l=Number(t)-T.getFullYear()}c+=e.type===`day`?`DD`:e.type===`month`?`MM`:`YYYY`}else{let t=e.value.replace(C,``);c+=t;for(let e of t)s.add(e)}let d={order:o,pattern:c.trim(),literals:s,digits:r,yearOffset:l,yearLength:u};return w.set(t,d),d}function O(e){let{order:t,pattern:n}=D(e);return{order:[...t],pattern:n}}function k(e,t){return S(t,_).format(e)}function A(e,t){let n=E(e.trim(),t.digits),r=n.match(/\d+/g);if(!r)return null;for(let e of n.replace(/\d+/g,``))if(!(/\s/.test(e)||t.literals.has(e)||`/.-`.includes(e)))return null;return r}function j(e,t){if(!e.trim())return null;let n=h(e);if(n)return n;let r=D(t);if(r.order.length!==3)return null;let i=A(e,r);if(!i||i.length!==3)return null;let a={};for(let e=0;e<3;e++){let t=r.order[e];if(t===`year`&&i[e].length<r.yearLength)return null;a[t]=Number(i[e])}let o=m((a.year??NaN)-r.yearOffset,a.month??NaN,a.day??NaN);if(!o)return null;let s=A(k(o,t),r);return!s||s.some((e,t)=>Number(e)!==Number(i[t]))?null:o}function M(t,n=0){let r=S(t,{weekday:`short`}),i=S(t,{weekday:`long`});return Array.from({length:7},(t,a)=>{let o=e.makeDate(2023,0,1+(n+a)%7);return{short:r.format(o),long:i.format(o)}})}function N(e,t){return S(t,{month:`long`,year:`numeric`}).format(e)}function P(e,t){return S(t,{month:`short`}).format(e)}function F(e,t){return S(t,{year:`numeric`}).formatToParts(e).find(e=>e.type===`year`)?.value??String(e.getFullYear())}function I(t,n){return S(n,{year:`numeric`}).formatRange(e.makeDate(t,0,1),e.makeDate(t+9,11,31))}function L(e,t){return S(t,{weekday:`long`,year:`numeric`,month:`long`,day:`numeric`}).format(e)}var R=new Map;function z(e,t){let n=t??``,r=R.get(n);if(!r){try{r=new Intl.NumberFormat(t,{useGrouping:!1})}catch{r=new Intl.NumberFormat(void 0,{useGrouping:!1})}R.set(n,r)}return r.format(e.getDate())}var B=/^(\d{1,2}):(\d{2})(?::(\d{2}))?$/,V=/^(\d{1,2}):(\d{2})(?::(\d{2}))?\s*([ap])\.?\s*m\.?$/i;function H(e){let t=e.trim(),n=V.exec(t),r=n??B.exec(t);if(!r)return NaN;let i=Number(r[1]),a=Number(r[2]),o=r[3]===void 0?0:Number(r[3]);if(a>59||o>59)return NaN;if(n){if(i<1||i>12)return NaN;let e=r[4].toLowerCase()===`p`;i=i%12+(e?12:0)}else if(i>23)return NaN;return i*60+a}function U(e){return`${f(Math.floor(e/60)%24,2)}:${f(e%60,2)}`}function W(e,t){if(t===`24h`)return U(e);let n=Math.floor(e/60)%24,r=n>=12?`PM`:`AM`;return`${n%12==0?12:n%12}:${f(e%60,2)} ${r}`}function G(e){return!Number.isFinite(e)||e<=0?{step:30,valid:!1}:{step:Math.max(1,Math.floor(e)),valid:!0}}function K(e,t,n,r){let i=G(e).step,a=H(t),o=H(n);if(Number.isNaN(a)||Number.isNaN(o)||a>o)return[];let s=[];for(let e=a;e<=o;e+=i)s.push({value:U(e),label:W(e,r)});return s}exports.addDays=e.addDays,exports.addMonths=e.addMonths,exports.clampDate=r,exports.formatDate=k,exports.formatDayLabel=L,exports.formatDayNumber=z,exports.formatDecade=I,exports.formatISODate=p,exports.formatMonthShort=P,exports.formatMonthYear=N,exports.formatYear=F,exports.generateTimeOptions=K,exports.getCalendarDays=l,exports.getDecadeStart=i,exports.getDecadeYears=o,exports.getLocaleDateFormat=O,exports.getSelectionRange=d,exports.getWeekdayNames=M,exports.getYearMonths=a,exports.isDateInRange=n,exports.isDayUnavailable=u,exports.isMonthOutOfRange=s,exports.isSameDay=e.isSameDay,exports.isSameMonth=t,exports.isValidLocaleTag=g,exports.isYearOutOfRange=c,exports.minutesToTime=W,exports.minutesToValue=U,exports.normalizeTimeStep=G,exports.parseDate=j,exports.parseISODate=h,exports.startOfDay=e.startOfDay,exports.startOfMonth=e.startOfMonth,exports.timeToMinutes=H;
|
|
2
2
|
//# sourceMappingURL=dateUtils.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dateUtils.cjs","names":["makeDate","date","year","monthIndex","day","startOfDay","startOfMonth","daysInMonth","addDays","amount","addMonths","target","isSameDay","a","b","isSameMonth","isDateInRange","min","max","clampDate","getCalendarDays","first","viewMonth","offset","firstDayOfWeek","days","i","pad","value","length","formatISODate","fromFields","month","parseISODate","match","text","isValidLocaleTag","locale","DEFAULT_DATE_OPTIONS","GRID_CALENDARS","calendarCache","getCalendar","key","calendar","resolved","formatterCache","getFormatter","options","formatter","withCalendar","BIDI_MARKS","infoCache","SAMPLE_DATE","toAsciiDigits","out","char","digits","getLocaleInfo","cached","numberFormat","parts","order","literals","pattern","yearOffset","yearLength","part","info","getLocaleDateFormat","formatDate","digitGroups","normalized","groups","parseDate","iso","values","field","roundTrip","group","getWeekdayNames","short","long","formatMonthYear","formatDayLabel","numberFormatCache","formatDayNumber","TIME_24H","TIME_12H","timeToMinutes","twelve","hours","minutes","seconds","pm","minutesToValue","minutesToTime","format","period","normalizeTimeStep","step","generateTimeOptions","increment","minTime","maxTime"],"sources":["../../../src/components/input/dateUtils.ts"],"sourcesContent":["/**\n * Date and time helpers shared by `DatePicker` and `TimePicker` (spec §5.6).\n *\n * - Every date these helpers return is a **local-midnight** `Date` (the start of a calendar day in\n * the runtime's time zone). Comparisons are by calendar day, never by time of day.\n * - The default display format is `Intl.DateTimeFormat(locale, { year: 'numeric', month:\n * '2-digit', day: '2-digit' })`, and {@link parseDate} is its exact inverse for the same\n * `locale` (field order from `formatToParts`), so a formatted date always parses back to the\n * same day. ISO `yyyy-mm-dd` text is accepted too.\n * - Every formatter uses the locale's calendar only when it has the Gregorian months and days of\n * the grid (Gregorian, or the Buddhist calendar of `th-TH`, whose years are offset); any other\n * calendar (the Persian default of `fa-IR`, `-u-ca-islamic`, Japanese eras) is replaced by the\n * Gregorian one, so the text, the heading and the day labels name the day the grid shows.\n * - Pass an explicit `locale` when rendering on the server: the runtime default locale of the\n * server and the browser may differ, which would change the formatted text between them.\n *\n * Internal module (not exported from the package).\n */\n\n/* ------------------------------------------------------------------ */\n/* Day arithmetic */\n/* ------------------------------------------------------------------ */\n\n/**\n * Builds a local-midnight date without the `new Date(y, …)` two-digit-year mapping (years 0–99\n * would otherwise become 1900–1999).\n */\nfunction makeDate(year: number, monthIndex: number, day: number): Date {\n const date = new Date(2000, 0, 1);\n date.setFullYear(year, monthIndex, day);\n date.setHours(0, 0, 0, 0);\n return date;\n}\n\n/** The start (local midnight) of `date`'s calendar day, as a new `Date`. */\nexport function startOfDay(date: Date): Date {\n return makeDate(date.getFullYear(), date.getMonth(), date.getDate());\n}\n\n/** The first day (local midnight) of `date`'s month. */\nexport function startOfMonth(date: Date): Date {\n return makeDate(date.getFullYear(), date.getMonth(), 1);\n}\n\n/** The number of days in the given month (`monthIndex` may be out of range; it is normalized). */\nfunction daysInMonth(year: number, monthIndex: number): number {\n return makeDate(year, monthIndex + 1, 0).getDate();\n}\n\n/** `date` plus `amount` calendar days, at local midnight (month/year boundaries included). */\nexport function addDays(date: Date, amount: number): Date {\n return makeDate(date.getFullYear(), date.getMonth(), date.getDate() + amount);\n}\n\n/**\n * `date` plus `amount` months, at local midnight. The day is clamped to the length of the target\n * month, so Jan 31 + 1 month is Feb 28 (29 in a leap year) and Mar 31 − 1 month is Feb 28/29 —\n * never a day of the following month.\n */\nexport function addMonths(date: Date, amount: number): Date {\n const target = makeDate(date.getFullYear(), date.getMonth() + amount, 1);\n const day = Math.min(date.getDate(), daysInMonth(target.getFullYear(), target.getMonth()));\n return makeDate(target.getFullYear(), target.getMonth(), day);\n}\n\n/** Whether both dates are on the same calendar day. `false` when either is missing. */\nexport function isSameDay(a: Date | null | undefined, b: Date | null | undefined): boolean {\n return (\n !!a &&\n !!b &&\n a.getFullYear() === b.getFullYear() &&\n a.getMonth() === b.getMonth() &&\n a.getDate() === b.getDate()\n );\n}\n\n/** Whether both dates are in the same month of the same year. */\nexport function isSameMonth(a: Date, b: Date): boolean {\n return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth();\n}\n\n/**\n * Whether `date`'s day lies within `[min, max]`, comparing calendar days (a `max` with a time of\n * day still includes that whole day). Missing bounds are open.\n */\nexport function isDateInRange(date: Date, min?: Date | null, max?: Date | null): boolean {\n const day = startOfDay(date).getTime();\n if (min && day < startOfDay(min).getTime()) return false;\n if (max && day > startOfDay(max).getTime()) return false;\n return true;\n}\n\n/** `date`'s day clamped into `[min, max]` (calendar days), at local midnight. */\nexport function clampDate(date: Date, min?: Date | null, max?: Date | null): Date {\n const day = startOfDay(date);\n if (min && day.getTime() < startOfDay(min).getTime()) return startOfDay(min);\n if (max && day.getTime() > startOfDay(max).getTime()) return startOfDay(max);\n return day;\n}\n\n/**\n * The 42 days (6 weeks) of the calendar grid showing `viewMonth`'s month: the first row starts on\n * `firstDayOfWeek` (0 = Sunday) on or before the 1st.\n */\nexport function getCalendarDays(viewMonth: Date, firstDayOfWeek: number): Date[] {\n const first = startOfMonth(viewMonth);\n const offset = (first.getDay() - firstDayOfWeek + 7) % 7;\n const days: Date[] = [];\n for (let i = 0; i < 42; i++) days.push(addDays(first, i - offset));\n return days;\n}\n\n/* ------------------------------------------------------------------ */\n/* ISO dates */\n/* ------------------------------------------------------------------ */\n\nfunction pad(value: number, length: number): string {\n return String(value).padStart(length, '0');\n}\n\n/** `yyyy-mm-dd` from the **local** calendar fields (the `HiddenInput` form value). */\nexport function formatISODate(date: Date): string {\n return `${pad(date.getFullYear(), 4)}-${pad(date.getMonth() + 1, 2)}-${pad(date.getDate(), 2)}`;\n}\n\n/** Builds a date from calendar fields; `null` when they do not name a real day. */\nfunction fromFields(year: number, month: number, day: number): Date | null {\n if (!Number.isInteger(year) || year < 1 || year > 9999) return null;\n if (!Number.isInteger(month) || month < 1 || month > 12) return null;\n if (!Number.isInteger(day) || day < 1 || day > daysInMonth(year, month - 1)) return null;\n return makeDate(year, month - 1, day);\n}\n\n/** Parses `yyyy-mm-dd` (surrounding whitespace allowed) to local midnight; `null` otherwise. */\nexport function parseISODate(text: string): Date | null {\n const match = /^(\\d{4})-(\\d{2})-(\\d{2})$/.exec(text.trim());\n if (!match) return null;\n return fromFields(Number(match[1]), Number(match[2]), Number(match[3]));\n}\n\n/* ------------------------------------------------------------------ */\n/* Locale formats */\n/* ------------------------------------------------------------------ */\n\n/** A numeric date field. */\nexport type DateField = 'year' | 'month' | 'day';\n\n/** How a locale writes the default (numeric) date format. */\nexport interface LocaleDateFormat {\n /** The order of the fields, e.g. `['day', 'month', 'year']` for `en-GB`. */\n order: DateField[];\n /** A readable pattern with the locale's own separators, e.g. `DD/MM/YYYY`. */\n pattern: string;\n}\n\n/**\n * Whether `Intl` accepts `locale` as a language tag. Every helper below falls back to the runtime\n * default locale for a tag it rejects (the POSIX `de_DE`, `''`, a typo), so DatePicker warns about\n * one in development.\n */\nexport function isValidLocaleTag(locale: string): boolean {\n try {\n Intl.getCanonicalLocales(locale);\n return true;\n } catch {\n return false;\n }\n}\n\nconst DEFAULT_DATE_OPTIONS: Intl.DateTimeFormatOptions = {\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n};\n\n/**\n * Calendars with the Gregorian months and days, which the grid shows (`getCalendarDays`): a locale\n * whose calendar is one of these keeps it. The Buddhist calendar (default of `th-TH`) only numbers\n * the years differently.\n */\nconst GRID_CALENDARS = new Set(['gregory', 'buddhist']);\n\nconst calendarCache = new Map<string, string>();\n\n/**\n * The calendar every date helper formats `locale` in: the locale's own (its default or a `-u-ca-`\n * extension) when it has the Gregorian months and days, else `'gregory'`. So a Persian default\n * (`fa-IR`, `fa-AF`, `ps-AF`), an explicit `-u-ca-islamic` or the Japanese eras never label the\n * Gregorian grid with other months or years, and the text stays parseable; the locale's digits\n * stay. An invalid locale tag uses the runtime default's.\n */\nfunction getCalendar(locale: string | undefined): string {\n const key = locale ?? '';\n let calendar = calendarCache.get(key);\n if (calendar === undefined) {\n let resolved: string;\n try {\n resolved = new Intl.DateTimeFormat(locale).resolvedOptions().calendar;\n } catch {\n resolved = new Intl.DateTimeFormat().resolvedOptions().calendar;\n }\n calendar = GRID_CALENDARS.has(resolved) ? resolved : 'gregory';\n calendarCache.set(key, calendar);\n }\n return calendar;\n}\n\nconst formatterCache = new Map<string, Intl.DateTimeFormat>();\n\n/**\n * A cached `Intl.DateTimeFormat` in the calendar of {@link getCalendar}; an invalid locale tag falls\n * back to the runtime default.\n */\nfunction getFormatter(\n locale: string | undefined,\n options: Intl.DateTimeFormatOptions,\n): Intl.DateTimeFormat {\n const key = `${locale ?? ''}|${JSON.stringify(options)}`;\n let formatter = formatterCache.get(key);\n if (!formatter) {\n const withCalendar = { ...options, calendar: getCalendar(locale) };\n try {\n formatter = new Intl.DateTimeFormat(locale, withCalendar);\n } catch {\n formatter = new Intl.DateTimeFormat(undefined, withCalendar);\n }\n formatterCache.set(key, formatter);\n }\n return formatter;\n}\n\n/** Bidirectional control characters Intl inserts around fields (Arabic, Hebrew locales). */\nconst BIDI_MARKS = /[\\u061c\\u200b-\\u200f\\u202a-\\u202e\\u2066-\\u2069]/g;\n\ninterface LocaleInfo extends LocaleDateFormat {\n /** Non-digit characters of the formatted date (allowed between the typed fields). */\n literals: Set<string>;\n /** Locale digit → ASCII digit (for non-Latin numbering systems). */\n digits: Map<string, string>;\n /** Formatted year minus the Gregorian year (e.g. 543 for the Buddhist calendar). */\n yearOffset: number;\n /** Digit count of a formatted year: shorter typed years are rejected (`25` is not 2025). */\n yearLength: number;\n}\n\nconst infoCache = new Map<string, LocaleInfo>();\nconst SAMPLE_DATE = makeDate(2001, 10, 23); // 23 Nov 2001: every field distinct\n\nfunction toAsciiDigits(text: string, digits: Map<string, string>): string {\n let out = '';\n for (const char of text.replace(BIDI_MARKS, '')) out += digits.get(char) ?? char;\n return out;\n}\n\nfunction getLocaleInfo(locale: string | undefined): LocaleInfo {\n const key = locale ?? '';\n const cached = infoCache.get(key);\n if (cached) return cached;\n\n const digits = new Map<string, string>();\n let numberFormat: Intl.NumberFormat;\n try {\n numberFormat = new Intl.NumberFormat(locale, { useGrouping: false });\n } catch {\n numberFormat = new Intl.NumberFormat(undefined, { useGrouping: false });\n }\n for (let i = 0; i <= 9; i++) digits.set(numberFormat.format(i), String(i));\n\n const parts = getFormatter(locale, DEFAULT_DATE_OPTIONS).formatToParts(SAMPLE_DATE);\n const order: DateField[] = [];\n const literals = new Set<string>();\n let pattern = '';\n let yearOffset = 0;\n let yearLength = 4;\n for (const part of parts) {\n if (part.type === 'day' || part.type === 'month' || part.type === 'year') {\n order.push(part.type);\n if (part.type === 'year') {\n const year = toAsciiDigits(part.value, digits);\n yearLength = year.length;\n yearOffset = Number(year) - SAMPLE_DATE.getFullYear();\n }\n pattern += part.type === 'day' ? 'DD' : part.type === 'month' ? 'MM' : 'YYYY';\n } else {\n const text = part.value.replace(BIDI_MARKS, '');\n pattern += text;\n for (const char of text) literals.add(char);\n }\n }\n const info: LocaleInfo = {\n order,\n pattern: pattern.trim(),\n literals,\n digits,\n yearOffset,\n yearLength,\n };\n infoCache.set(key, info);\n return info;\n}\n\n/**\n * The field order and a readable pattern (`DD/MM/YYYY`, `MM/DD/YYYY`, `DD.MM.YYYY`, …) of the\n * default date format for `locale` (runtime default when omitted).\n */\nexport function getLocaleDateFormat(locale?: string): LocaleDateFormat {\n const { order, pattern } = getLocaleInfo(locale);\n return { order: [...order], pattern };\n}\n\n/**\n * The default DatePicker display text: `Intl.DateTimeFormat(locale, { year: 'numeric', month:\n * '2-digit', day: '2-digit' })` in the grid-compatible calendar of {@link getCalendar} (`fa-IR`:\n * `۲۰۲۵/۰۶/۱۵`, not the Persian `۱۴۰۴/۰۳/۲۵`). {@link parseDate} with the same `locale` reverses it.\n */\nexport function formatDate(date: Date, locale?: string): string {\n return getFormatter(locale, DEFAULT_DATE_OPTIONS).format(date);\n}\n\n/** The numeric fields of `text` in order, or `null` when anything but separators surrounds them. */\nfunction digitGroups(text: string, info: LocaleInfo): string[] | null {\n const normalized = toAsciiDigits(text.trim(), info.digits);\n const groups = normalized.match(/\\d+/g);\n if (!groups) return null;\n for (const char of normalized.replace(/\\d+/g, '')) {\n if (/\\s/.test(char) || info.literals.has(char) || '/.-'.includes(char)) continue;\n return null;\n }\n return groups;\n}\n\n/**\n * Parses text typed in the default display format of `locale` — the exact inverse of\n * {@link formatDate} for the same locale — to local midnight. Also accepts single-digit day/month\n * fields, `/`, `.` and `-` separators, and ISO `yyyy-mm-dd`. Returns `null` for anything else:\n * impossible dates (31/02), a year shorter than the locale writes it (`25`), letters or extra\n * fields.\n */\nexport function parseDate(text: string, locale?: string): Date | null {\n if (!text.trim()) return null;\n const iso = parseISODate(text);\n if (iso) return iso;\n\n const info = getLocaleInfo(locale);\n if (info.order.length !== 3) return null;\n const groups = digitGroups(text, info);\n if (!groups || groups.length !== 3) return null;\n\n const values: Partial<Record<DateField, number>> = {};\n for (let i = 0; i < 3; i++) {\n const field = info.order[i];\n if (field === 'year' && groups[i].length < info.yearLength) return null;\n values[field] = Number(groups[i]);\n }\n const date = fromFields(\n (values.year ?? NaN) - info.yearOffset,\n values.month ?? NaN,\n values.day ?? NaN,\n );\n if (!date) return null;\n\n // Exact inverse: formatting the result must give back the typed numbers (guards calendars\n // whose months or days differ from the Gregorian ones).\n const roundTrip = digitGroups(formatDate(date, locale), info);\n if (!roundTrip || roundTrip.some((group, i) => Number(group) !== Number(groups[i]))) return null;\n return date;\n}\n\n/** The 12 long month names of `locale` (January … December). */\nexport function getMonthNames(locale?: string): string[] {\n const formatter = getFormatter(locale, { month: 'long' });\n return Array.from({ length: 12 }, (_, month) => formatter.format(makeDate(2001, month, 1)));\n}\n\n/** A weekday name in both lengths. */\nexport interface WeekdayName {\n /** Short name for the column header (`Mon`). */\n short: string;\n /** Full name for assistive technology (`Monday`). */\n long: string;\n}\n\n/** The seven weekday names of `locale`, starting at `firstDayOfWeek` (0 = Sunday). */\nexport function getWeekdayNames(locale: string | undefined, firstDayOfWeek = 0): WeekdayName[] {\n const short = getFormatter(locale, { weekday: 'short' });\n const long = getFormatter(locale, { weekday: 'long' });\n // 1 January 2023 was a Sunday.\n return Array.from({ length: 7 }, (_, i) => {\n const day = makeDate(2023, 0, 1 + ((firstDayOfWeek + i) % 7));\n return { short: short.format(day), long: long.format(day) };\n });\n}\n\n/** The calendar heading, e.g. `May 2025` / `Mai 2025`. */\nexport function formatMonthYear(date: Date, locale?: string): string {\n return getFormatter(locale, { month: 'long', year: 'numeric' }).format(date);\n}\n\n/** The accessible name of a day cell, e.g. `Monday, May 26, 2025`. */\nexport function formatDayLabel(date: Date, locale?: string): string {\n return getFormatter(locale, {\n weekday: 'long',\n year: 'numeric',\n month: 'long',\n day: 'numeric',\n }).format(date);\n}\n\nconst numberFormatCache = new Map<string, Intl.NumberFormat>();\n\n/** The day-of-month number in the locale's digits, without suffixes (`3`, not `3日`). */\nexport function formatDayNumber(date: Date, locale?: string): string {\n const key = locale ?? '';\n let formatter = numberFormatCache.get(key);\n if (!formatter) {\n try {\n formatter = new Intl.NumberFormat(locale, { useGrouping: false });\n } catch {\n formatter = new Intl.NumberFormat(undefined, { useGrouping: false });\n }\n numberFormatCache.set(key, formatter);\n }\n return formatter.format(date.getDate());\n}\n\n/* ------------------------------------------------------------------ */\n/* Time */\n/* ------------------------------------------------------------------ */\n\n/** TimePicker label style. */\nexport type TimeFormat = '12h' | '24h';\n\nconst TIME_24H = /^(\\d{1,2}):(\\d{2})(?::(\\d{2}))?$/;\nconst TIME_12H = /^(\\d{1,2}):(\\d{2})(?::(\\d{2}))?\\s*([ap])\\.?\\s*m\\.?$/i;\n\n/**\n * Minutes since midnight of a time string: `HH:mm`, `H:mm`, `HH:mm:ss` (seconds ignored) or a\n * 12-hour time such as `9:00 AM`, `12:30 pm`, `11:59 p.m.`. `NaN` when invalid.\n */\nexport function timeToMinutes(text: string): number {\n const value = text.trim();\n const twelve = TIME_12H.exec(value);\n const match = twelve ?? TIME_24H.exec(value);\n if (!match) return NaN;\n let hours = Number(match[1]);\n const minutes = Number(match[2]);\n const seconds = match[3] === undefined ? 0 : Number(match[3]);\n if (minutes > 59 || seconds > 59) return NaN;\n if (twelve) {\n if (hours < 1 || hours > 12) return NaN;\n const pm = match[4].toLowerCase() === 'p';\n hours = (hours % 12) + (pm ? 12 : 0);\n } else if (hours > 23) {\n return NaN;\n }\n return hours * 60 + minutes;\n}\n\n/** `HH:mm` (24-hour, zero-padded): the TimePicker value format. */\nexport function minutesToValue(minutes: number): string {\n const hours = Math.floor(minutes / 60) % 24;\n return `${pad(hours, 2)}:${pad(minutes % 60, 2)}`;\n}\n\n/** Display text of a time: `13:00` (`'24h'`) or `1:00 PM` (`'12h'`). */\nexport function minutesToTime(minutes: number, format: TimeFormat): string {\n if (format === '24h') return minutesToValue(minutes);\n const hours = Math.floor(minutes / 60) % 24;\n const period = hours >= 12 ? 'PM' : 'AM';\n const hours12 = hours % 12 === 0 ? 12 : hours % 12;\n return `${hours12}:${pad(minutes % 60, 2)} ${period}`;\n}\n\n/** The TimePicker step fallback for invalid values. */\nexport const DEFAULT_TIME_STEP = 30;\n\n/**\n * Guards a TimePicker `step`: a non-finite or non-positive step falls back to\n * {@link DEFAULT_TIME_STEP} (`valid: false`, so the component can report it); fractions are\n * floored to a whole number of minutes, at least 1.\n */\nexport function normalizeTimeStep(step: number): { step: number; valid: boolean } {\n if (!Number.isFinite(step) || step <= 0) return { step: DEFAULT_TIME_STEP, valid: false };\n return { step: Math.max(1, Math.floor(step)), valid: true };\n}\n\n/** One generated TimePicker option. */\nexport interface TimeOption {\n /** `HH:mm`. */\n value: string;\n /** Display text in the requested format. */\n label: string;\n}\n\n/**\n * The options from `minTime` to `maxTime` (inclusive) every `step` minutes. The step is guarded\n * with {@link normalizeTimeStep} (the loop always terminates); invalid or reversed bounds give\n * `[]`.\n */\nexport function generateTimeOptions(\n step: number,\n minTime: string,\n maxTime: string,\n format: TimeFormat,\n): TimeOption[] {\n const increment = normalizeTimeStep(step).step;\n const min = timeToMinutes(minTime);\n const max = timeToMinutes(maxTime);\n if (Number.isNaN(min) || Number.isNaN(max) || min > max) return [];\n const options: TimeOption[] = [];\n for (let minutes = min; minutes <= max; minutes += increment) {\n options.push({ value: minutesToValue(minutes), label: minutesToTime(minutes, format) });\n }\n return options;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"dateUtils.cjs","names":["startOfDay","makeDate","startOfMonth","addDays","daysInMonth","startOfWeek","require_date","isSameMonth","a","b","isDateInRange","day","date","min","max","clampDate","getDecadeStart","year","getYearMonths","month","getDecadeYears","decadeStart","index","isMonthOutOfRange","first","last","isYearOutOfRange","getCalendarDays","viewMonth","offset","firstDayOfWeek","days","i","isDayUnavailable","disabledDates","getSelectionRange","mode","start","workWeekDays","weekDay","isUnavailable","candidate","pad","value","length","formatISODate","fromFields","parseISODate","match","text","isValidLocaleTag","locale","DEFAULT_DATE_OPTIONS","GRID_CALENDARS","calendarCache","getCalendar","key","calendar","resolved","formatterCache","getFormatter","options","formatter","withCalendar","BIDI_MARKS","infoCache","SAMPLE_DATE","toAsciiDigits","out","char","digits","getLocaleInfo","cached","numberFormat","parts","order","literals","pattern","yearOffset","yearLength","part","info","getLocaleDateFormat","formatDate","digitGroups","normalized","groups","parseDate","iso","values","field","roundTrip","group","getWeekdayNames","short","long","formatMonthYear","formatMonthShort","formatYear","formatDecade","formatDayLabel","numberFormatCache","formatDayNumber","TIME_24H","TIME_12H","timeToMinutes","twelve","hours","minutes","seconds","pm","minutesToValue","minutesToTime","format","period","normalizeTimeStep","step","generateTimeOptions","increment","minTime","maxTime"],"sources":["../../../src/components/input/dateUtils.ts"],"sourcesContent":["/**\n * Date and time helpers shared by `DatePicker` and `TimePicker` (spec §5.6).\n *\n * - Every date these helpers return is a **local-midnight** `Date` (the start of a calendar day in\n * the runtime's time zone). Comparisons are by calendar day, never by time of day.\n * - The default display format is `Intl.DateTimeFormat(locale, { year: 'numeric', month:\n * '2-digit', day: '2-digit' })`, and {@link parseDate} is its exact inverse for the same\n * `locale` (field order from `formatToParts`), so a formatted date always parses back to the\n * same day. ISO `yyyy-mm-dd` text is accepted too.\n * - Every formatter uses the locale's calendar only when it has the Gregorian months and days of\n * the grid (Gregorian, or the Buddhist calendar of `th-TH`, whose years are offset); any other\n * calendar (the Persian default of `fa-IR`, `-u-ca-islamic`, Japanese eras) is replaced by the\n * Gregorian one, so the text, the heading and the day labels name the day the grid shows.\n * - Pass an explicit `locale` when rendering on the server: the runtime default locale of the\n * server and the browser may differ, which would change the formatted text between them.\n *\n * Internal module (not exported from the package).\n */\n\nimport {\n addDays,\n addMonths,\n daysInMonth,\n isSameDay,\n makeDate,\n startOfDay,\n startOfMonth,\n startOfWeek,\n} from '../../lib/date';\nimport type { DayOfWeek } from '../../lib/types';\n\n/* ------------------------------------------------------------------ */\n/* Day arithmetic (the shared helpers live in lib/date) */\n/* ------------------------------------------------------------------ */\n\nexport { addDays, addMonths, isSameDay, startOfDay, startOfMonth };\n\n/** Whether both dates are in the same month of the same year. */\nexport function isSameMonth(a: Date, b: Date): boolean {\n return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth();\n}\n\n/**\n * Whether `date`'s day lies within `[min, max]`, comparing calendar days (a `max` with a time of\n * day still includes that whole day). Missing bounds are open.\n */\nexport function isDateInRange(date: Date, min?: Date | null, max?: Date | null): boolean {\n const day = startOfDay(date).getTime();\n if (min && day < startOfDay(min).getTime()) return false;\n if (max && day > startOfDay(max).getTime()) return false;\n return true;\n}\n\n/** `date`'s day clamped into `[min, max]` (calendar days), at local midnight. */\nexport function clampDate(date: Date, min?: Date | null, max?: Date | null): Date {\n const day = startOfDay(date);\n if (min && day.getTime() < startOfDay(min).getTime()) return startOfDay(min);\n if (max && day.getTime() > startOfDay(max).getTime()) return startOfDay(max);\n return day;\n}\n\n/** The first year of `year`'s decade (2025 gives 2020). */\nexport function getDecadeStart(year: number): number {\n return Math.floor(year / 10) * 10;\n}\n\n/** The 12 months of `year`, as the 1st of each month (the cells of the month view). */\nexport function getYearMonths(year: number): Date[] {\n return Array.from({ length: 12 }, (_, month) => makeDate(year, month, 1));\n}\n\n/**\n * The 12 years of the year view, as January 1 of each: the decade starting at `decadeStart`, with\n * the year before and the year after it.\n */\nexport function getDecadeYears(decadeStart: number): Date[] {\n return Array.from({ length: 12 }, (_, index) => makeDate(decadeStart - 1 + index, 0, 1));\n}\n\n/** Whether every day of `month`'s month lies outside `[min, max]` (calendar days). */\nexport function isMonthOutOfRange(month: Date, min?: Date | null, max?: Date | null): boolean {\n const first = startOfMonth(month);\n const last = makeDate(first.getFullYear(), first.getMonth() + 1, 0);\n return (!!min && last < startOfDay(min)) || (!!max && first > startOfDay(max));\n}\n\n/** Whether every day of `year` lies outside `[min, max]` (calendar days). */\nexport function isYearOutOfRange(year: number, min?: Date | null, max?: Date | null): boolean {\n return (\n (!!min && makeDate(year, 11, 31) < startOfDay(min)) ||\n (!!max && makeDate(year, 0, 1) > startOfDay(max))\n );\n}\n\n/**\n * The 42 days (6 weeks) of the calendar grid showing `viewMonth`'s month: the first row starts on\n * `firstDayOfWeek` (0 = Sunday) on or before the 1st.\n */\nexport function getCalendarDays(viewMonth: Date, firstDayOfWeek: number): Date[] {\n const first = startOfMonth(viewMonth);\n const offset = (first.getDay() - firstDayOfWeek + 7) % 7;\n const days: Date[] = [];\n for (let i = 0; i < 42; i++) days.push(addDays(first, i - offset));\n return days;\n}\n\n/**\n * Whether a day cannot be picked: outside `min`/`max` (whole days) or named by `disabledDates`\n * (called with local midnight). The one rule behind the focusable-but-unavailable look, the\n * drawn range and the emitted range.\n */\nexport function isDayUnavailable(\n date: Date,\n min?: Date | null,\n max?: Date | null,\n disabledDates?: (date: Date) => boolean,\n): boolean {\n return !isDateInRange(date, min, max) || !!disabledDates?.(startOfDay(date));\n}\n\n/** What a pick selects (the Calendar's `selectionRange`). */\nexport type CalendarSelectionRange = 'day' | 'week' | 'work-week' | 'month';\n\n/**\n * The days a pick of `date` selects, in order, at local midnight: the day itself (`'day'`, whatever\n * its availability), its week from `firstDayOfWeek`, the days of that week named by `workWeekDays`\n * (`'work-week'`, in the order of the week), or its whole month. The days of the other modes that\n * `isUnavailable` names are left out.\n */\nexport function getSelectionRange(\n date: Date,\n mode: CalendarSelectionRange,\n firstDayOfWeek: DayOfWeek,\n workWeekDays: readonly DayOfWeek[],\n isUnavailable?: (date: Date) => boolean,\n): Date[] {\n const day = startOfDay(date);\n let days: Date[];\n if (mode === 'day') return [day];\n if (mode === 'month') {\n const first = startOfMonth(day);\n days = Array.from({ length: daysInMonth(day.getFullYear(), day.getMonth()) }, (_, i) =>\n addDays(first, i),\n );\n } else {\n const start = startOfWeek(day, firstDayOfWeek);\n days = Array.from({ length: 7 }, (_, i) => addDays(start, i));\n if (mode === 'work-week') {\n days = days.filter((weekDay) => workWeekDays.includes(weekDay.getDay() as DayOfWeek));\n }\n }\n return isUnavailable ? days.filter((candidate) => !isUnavailable(candidate)) : days;\n}\n\n/* ------------------------------------------------------------------ */\n/* ISO dates */\n/* ------------------------------------------------------------------ */\n\nfunction pad(value: number, length: number): string {\n return String(value).padStart(length, '0');\n}\n\n/** `yyyy-mm-dd` from the **local** calendar fields (the `HiddenInput` form value). */\nexport function formatISODate(date: Date): string {\n return `${pad(date.getFullYear(), 4)}-${pad(date.getMonth() + 1, 2)}-${pad(date.getDate(), 2)}`;\n}\n\n/** Builds a date from calendar fields; `null` when they do not name a real day. */\nfunction fromFields(year: number, month: number, day: number): Date | null {\n if (!Number.isInteger(year) || year < 1 || year > 9999) return null;\n if (!Number.isInteger(month) || month < 1 || month > 12) return null;\n if (!Number.isInteger(day) || day < 1 || day > daysInMonth(year, month - 1)) return null;\n return makeDate(year, month - 1, day);\n}\n\n/** Parses `yyyy-mm-dd` (surrounding whitespace allowed) to local midnight; `null` otherwise. */\nexport function parseISODate(text: string): Date | null {\n const match = /^(\\d{4})-(\\d{2})-(\\d{2})$/.exec(text.trim());\n if (!match) return null;\n return fromFields(Number(match[1]), Number(match[2]), Number(match[3]));\n}\n\n/* ------------------------------------------------------------------ */\n/* Locale formats */\n/* ------------------------------------------------------------------ */\n\n/** A numeric date field. */\nexport type DateField = 'year' | 'month' | 'day';\n\n/** How a locale writes the default (numeric) date format. */\nexport interface LocaleDateFormat {\n /** The order of the fields, e.g. `['day', 'month', 'year']` for `en-GB`. */\n order: DateField[];\n /** A readable pattern with the locale's own separators, e.g. `DD/MM/YYYY`. */\n pattern: string;\n}\n\n/**\n * Whether `Intl` accepts `locale` as a language tag. Every helper below falls back to the runtime\n * default locale for a tag it rejects (the POSIX `de_DE`, `''`, a typo), so DatePicker warns about\n * one in development.\n */\nexport function isValidLocaleTag(locale: string): boolean {\n try {\n Intl.getCanonicalLocales(locale);\n return true;\n } catch {\n return false;\n }\n}\n\nconst DEFAULT_DATE_OPTIONS: Intl.DateTimeFormatOptions = {\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n};\n\n/**\n * Calendars with the Gregorian months and days, which the grid shows (`getCalendarDays`): a locale\n * whose calendar is one of these keeps it. The Buddhist calendar (default of `th-TH`) only numbers\n * the years differently.\n */\nconst GRID_CALENDARS = new Set(['gregory', 'buddhist']);\n\nconst calendarCache = new Map<string, string>();\n\n/**\n * The calendar every date helper formats `locale` in: the locale's own (its default or a `-u-ca-`\n * extension) when it has the Gregorian months and days, else `'gregory'`. So a Persian default\n * (`fa-IR`, `fa-AF`, `ps-AF`), an explicit `-u-ca-islamic` or the Japanese eras never label the\n * Gregorian grid with other months or years, and the text stays parseable; the locale's digits\n * stay. An invalid locale tag uses the runtime default's.\n */\nfunction getCalendar(locale: string | undefined): string {\n const key = locale ?? '';\n let calendar = calendarCache.get(key);\n if (calendar === undefined) {\n let resolved: string;\n try {\n resolved = new Intl.DateTimeFormat(locale).resolvedOptions().calendar;\n } catch {\n resolved = new Intl.DateTimeFormat().resolvedOptions().calendar;\n }\n calendar = GRID_CALENDARS.has(resolved) ? resolved : 'gregory';\n calendarCache.set(key, calendar);\n }\n return calendar;\n}\n\nconst formatterCache = new Map<string, Intl.DateTimeFormat>();\n\n/**\n * A cached `Intl.DateTimeFormat` in the calendar of {@link getCalendar}; an invalid locale tag falls\n * back to the runtime default.\n */\nfunction getFormatter(\n locale: string | undefined,\n options: Intl.DateTimeFormatOptions,\n): Intl.DateTimeFormat {\n const key = `${locale ?? ''}|${JSON.stringify(options)}`;\n let formatter = formatterCache.get(key);\n if (!formatter) {\n const withCalendar = { ...options, calendar: getCalendar(locale) };\n try {\n formatter = new Intl.DateTimeFormat(locale, withCalendar);\n } catch {\n formatter = new Intl.DateTimeFormat(undefined, withCalendar);\n }\n formatterCache.set(key, formatter);\n }\n return formatter;\n}\n\n/** Bidirectional control characters Intl inserts around fields (Arabic, Hebrew locales). */\nconst BIDI_MARKS = /[\\u061c\\u200b-\\u200f\\u202a-\\u202e\\u2066-\\u2069]/g;\n\ninterface LocaleInfo extends LocaleDateFormat {\n /** Non-digit characters of the formatted date (allowed between the typed fields). */\n literals: Set<string>;\n /** Locale digit → ASCII digit (for non-Latin numbering systems). */\n digits: Map<string, string>;\n /** Formatted year minus the Gregorian year (e.g. 543 for the Buddhist calendar). */\n yearOffset: number;\n /** Digit count of a formatted year: shorter typed years are rejected (`25` is not 2025). */\n yearLength: number;\n}\n\nconst infoCache = new Map<string, LocaleInfo>();\nconst SAMPLE_DATE = makeDate(2001, 10, 23); // 23 Nov 2001: every field distinct\n\nfunction toAsciiDigits(text: string, digits: Map<string, string>): string {\n let out = '';\n for (const char of text.replace(BIDI_MARKS, '')) out += digits.get(char) ?? char;\n return out;\n}\n\nfunction getLocaleInfo(locale: string | undefined): LocaleInfo {\n const key = locale ?? '';\n const cached = infoCache.get(key);\n if (cached) return cached;\n\n const digits = new Map<string, string>();\n let numberFormat: Intl.NumberFormat;\n try {\n numberFormat = new Intl.NumberFormat(locale, { useGrouping: false });\n } catch {\n numberFormat = new Intl.NumberFormat(undefined, { useGrouping: false });\n }\n for (let i = 0; i <= 9; i++) digits.set(numberFormat.format(i), String(i));\n\n const parts = getFormatter(locale, DEFAULT_DATE_OPTIONS).formatToParts(SAMPLE_DATE);\n const order: DateField[] = [];\n const literals = new Set<string>();\n let pattern = '';\n let yearOffset = 0;\n let yearLength = 4;\n for (const part of parts) {\n if (part.type === 'day' || part.type === 'month' || part.type === 'year') {\n order.push(part.type);\n if (part.type === 'year') {\n const year = toAsciiDigits(part.value, digits);\n yearLength = year.length;\n yearOffset = Number(year) - SAMPLE_DATE.getFullYear();\n }\n pattern += part.type === 'day' ? 'DD' : part.type === 'month' ? 'MM' : 'YYYY';\n } else {\n const text = part.value.replace(BIDI_MARKS, '');\n pattern += text;\n for (const char of text) literals.add(char);\n }\n }\n const info: LocaleInfo = {\n order,\n pattern: pattern.trim(),\n literals,\n digits,\n yearOffset,\n yearLength,\n };\n infoCache.set(key, info);\n return info;\n}\n\n/**\n * The field order and a readable pattern (`DD/MM/YYYY`, `MM/DD/YYYY`, `DD.MM.YYYY`, …) of the\n * default date format for `locale` (runtime default when omitted).\n */\nexport function getLocaleDateFormat(locale?: string): LocaleDateFormat {\n const { order, pattern } = getLocaleInfo(locale);\n return { order: [...order], pattern };\n}\n\n/**\n * The default DatePicker display text: `Intl.DateTimeFormat(locale, { year: 'numeric', month:\n * '2-digit', day: '2-digit' })` in the grid-compatible calendar of {@link getCalendar} (`fa-IR`:\n * `۲۰۲۵/۰۶/۱۵`, not the Persian `۱۴۰۴/۰۳/۲۵`). {@link parseDate} with the same `locale` reverses it.\n */\nexport function formatDate(date: Date, locale?: string): string {\n return getFormatter(locale, DEFAULT_DATE_OPTIONS).format(date);\n}\n\n/** The numeric fields of `text` in order, or `null` when anything but separators surrounds them. */\nfunction digitGroups(text: string, info: LocaleInfo): string[] | null {\n const normalized = toAsciiDigits(text.trim(), info.digits);\n const groups = normalized.match(/\\d+/g);\n if (!groups) return null;\n for (const char of normalized.replace(/\\d+/g, '')) {\n if (/\\s/.test(char) || info.literals.has(char) || '/.-'.includes(char)) continue;\n return null;\n }\n return groups;\n}\n\n/**\n * Parses text typed in the default display format of `locale` — the exact inverse of\n * {@link formatDate} for the same locale — to local midnight. Also accepts single-digit day/month\n * fields, `/`, `.` and `-` separators, and ISO `yyyy-mm-dd`. Returns `null` for anything else:\n * impossible dates (31/02), a year shorter than the locale writes it (`25`), letters or extra\n * fields.\n */\nexport function parseDate(text: string, locale?: string): Date | null {\n if (!text.trim()) return null;\n const iso = parseISODate(text);\n if (iso) return iso;\n\n const info = getLocaleInfo(locale);\n if (info.order.length !== 3) return null;\n const groups = digitGroups(text, info);\n if (!groups || groups.length !== 3) return null;\n\n const values: Partial<Record<DateField, number>> = {};\n for (let i = 0; i < 3; i++) {\n const field = info.order[i];\n if (field === 'year' && groups[i].length < info.yearLength) return null;\n values[field] = Number(groups[i]);\n }\n const date = fromFields(\n (values.year ?? NaN) - info.yearOffset,\n values.month ?? NaN,\n values.day ?? NaN,\n );\n if (!date) return null;\n\n // Exact inverse: formatting the result must give back the typed numbers (guards calendars\n // whose months or days differ from the Gregorian ones).\n const roundTrip = digitGroups(formatDate(date, locale), info);\n if (!roundTrip || roundTrip.some((group, i) => Number(group) !== Number(groups[i]))) return null;\n return date;\n}\n\n/** The 12 long month names of `locale` (January … December). */\nexport function getMonthNames(locale?: string): string[] {\n const formatter = getFormatter(locale, { month: 'long' });\n return Array.from({ length: 12 }, (_, month) => formatter.format(makeDate(2001, month, 1)));\n}\n\n/** A weekday name in both lengths. */\nexport interface WeekdayName {\n /** Short name for the column header (`Mon`). */\n short: string;\n /** Full name for assistive technology (`Monday`). */\n long: string;\n}\n\n/** The seven weekday names of `locale`, starting at `firstDayOfWeek` (0 = Sunday). */\nexport function getWeekdayNames(locale: string | undefined, firstDayOfWeek = 0): WeekdayName[] {\n const short = getFormatter(locale, { weekday: 'short' });\n const long = getFormatter(locale, { weekday: 'long' });\n // 1 January 2023 was a Sunday.\n return Array.from({ length: 7 }, (_, i) => {\n const day = makeDate(2023, 0, 1 + ((firstDayOfWeek + i) % 7));\n return { short: short.format(day), long: long.format(day) };\n });\n}\n\n/** The calendar heading, e.g. `May 2025` / `Mai 2025`. */\nexport function formatMonthYear(date: Date, locale?: string): string {\n return getFormatter(locale, { month: 'long', year: 'numeric' }).format(date);\n}\n\n/** The short month name of a month cell, e.g. `Mar` / `Mär`. */\nexport function formatMonthShort(date: Date, locale?: string): string {\n return getFormatter(locale, { month: 'short' }).format(date);\n}\n\n/**\n * The year of a year cell and of the month view's heading, in the locale's digits and the\n * calendar of {@link getCalendar}, without an era: `2025`, the Buddhist `2568` of `th-TH`.\n */\nexport function formatYear(date: Date, locale?: string): string {\n const parts = getFormatter(locale, { year: 'numeric' }).formatToParts(date);\n return parts.find((part) => part.type === 'year')?.value ?? String(date.getFullYear());\n}\n\n/**\n * The year view's heading: the decade starting at `decadeStart` as a range of years\n * (`Intl.DateTimeFormat#formatRange`), e.g. `2020 – 2029`.\n */\nexport function formatDecade(decadeStart: number, locale?: string): string {\n return getFormatter(locale, { year: 'numeric' }).formatRange(\n makeDate(decadeStart, 0, 1),\n makeDate(decadeStart + 9, 11, 31),\n );\n}\n\n/** The accessible name of a day cell, e.g. `Monday, May 26, 2025`. */\nexport function formatDayLabel(date: Date, locale?: string): string {\n return getFormatter(locale, {\n weekday: 'long',\n year: 'numeric',\n month: 'long',\n day: 'numeric',\n }).format(date);\n}\n\nconst numberFormatCache = new Map<string, Intl.NumberFormat>();\n\n/** The day-of-month number in the locale's digits, without suffixes (`3`, not `3日`). */\nexport function formatDayNumber(date: Date, locale?: string): string {\n const key = locale ?? '';\n let formatter = numberFormatCache.get(key);\n if (!formatter) {\n try {\n formatter = new Intl.NumberFormat(locale, { useGrouping: false });\n } catch {\n formatter = new Intl.NumberFormat(undefined, { useGrouping: false });\n }\n numberFormatCache.set(key, formatter);\n }\n return formatter.format(date.getDate());\n}\n\n/* ------------------------------------------------------------------ */\n/* Time */\n/* ------------------------------------------------------------------ */\n\n/** TimePicker label style. */\nexport type TimeFormat = '12h' | '24h';\n\nconst TIME_24H = /^(\\d{1,2}):(\\d{2})(?::(\\d{2}))?$/;\nconst TIME_12H = /^(\\d{1,2}):(\\d{2})(?::(\\d{2}))?\\s*([ap])\\.?\\s*m\\.?$/i;\n\n/**\n * Minutes since midnight of a time string: `HH:mm`, `H:mm`, `HH:mm:ss` (seconds ignored) or a\n * 12-hour time such as `9:00 AM`, `12:30 pm`, `11:59 p.m.`. `NaN` when invalid.\n */\nexport function timeToMinutes(text: string): number {\n const value = text.trim();\n const twelve = TIME_12H.exec(value);\n const match = twelve ?? TIME_24H.exec(value);\n if (!match) return NaN;\n let hours = Number(match[1]);\n const minutes = Number(match[2]);\n const seconds = match[3] === undefined ? 0 : Number(match[3]);\n if (minutes > 59 || seconds > 59) return NaN;\n if (twelve) {\n if (hours < 1 || hours > 12) return NaN;\n const pm = match[4].toLowerCase() === 'p';\n hours = (hours % 12) + (pm ? 12 : 0);\n } else if (hours > 23) {\n return NaN;\n }\n return hours * 60 + minutes;\n}\n\n/** `HH:mm` (24-hour, zero-padded): the TimePicker value format. */\nexport function minutesToValue(minutes: number): string {\n const hours = Math.floor(minutes / 60) % 24;\n return `${pad(hours, 2)}:${pad(minutes % 60, 2)}`;\n}\n\n/** Display text of a time: `13:00` (`'24h'`) or `1:00 PM` (`'12h'`). */\nexport function minutesToTime(minutes: number, format: TimeFormat): string {\n if (format === '24h') return minutesToValue(minutes);\n const hours = Math.floor(minutes / 60) % 24;\n const period = hours >= 12 ? 'PM' : 'AM';\n const hours12 = hours % 12 === 0 ? 12 : hours % 12;\n return `${hours12}:${pad(minutes % 60, 2)} ${period}`;\n}\n\n/** The TimePicker step fallback for invalid values. */\nexport const DEFAULT_TIME_STEP = 30;\n\n/**\n * Guards a TimePicker `step`: a non-finite or non-positive step falls back to\n * {@link DEFAULT_TIME_STEP} (`valid: false`, so the component can report it); fractions are\n * floored to a whole number of minutes, at least 1.\n */\nexport function normalizeTimeStep(step: number): { step: number; valid: boolean } {\n if (!Number.isFinite(step) || step <= 0) return { step: DEFAULT_TIME_STEP, valid: false };\n return { step: Math.max(1, Math.floor(step)), valid: true };\n}\n\n/** One generated TimePicker option. */\nexport interface TimeOption {\n /** `HH:mm`. */\n value: string;\n /** Display text in the requested format. */\n label: string;\n}\n\n/**\n * The options from `minTime` to `maxTime` (inclusive) every `step` minutes. The step is guarded\n * with {@link normalizeTimeStep} (the loop always terminates); invalid or reversed bounds give\n * `[]`.\n */\nexport function generateTimeOptions(\n step: number,\n minTime: string,\n maxTime: string,\n format: TimeFormat,\n): TimeOption[] {\n const increment = normalizeTimeStep(step).step;\n const min = timeToMinutes(minTime);\n const max = timeToMinutes(maxTime);\n if (Number.isNaN(min) || Number.isNaN(max) || min > max) return [];\n const options: TimeOption[] = [];\n for (let minutes = min; minutes <= max; minutes += increment) {\n options.push({ value: minutesToValue(minutes), label: minutesToTime(minutes, format) });\n }\n return options;\n}\n"],"mappings":"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"}
|