@allxsmith/bestax-bulma 5.27.0 → 5.27.2
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/dist/bestax.css +1 -1
- package/dist/bestax.css.map +1 -1
- package/dist/esm/columns/Column.js +48 -0
- package/dist/esm/columns/Column.js.map +1 -0
- package/dist/esm/columns/Columns.js +53 -0
- package/dist/esm/columns/Columns.js.map +1 -0
- package/dist/esm/components/Avatar.js +299 -0
- package/dist/esm/components/Avatar.js.map +1 -0
- package/dist/esm/components/Avatars.js +81 -0
- package/dist/esm/components/Avatars.js.map +1 -0
- package/dist/esm/components/Badge.js +105 -0
- package/dist/esm/components/Badge.js.map +1 -0
- package/dist/esm/components/Breadcrumb.js +36 -0
- package/dist/esm/components/Breadcrumb.js.map +1 -0
- package/dist/esm/components/Card.js +252 -0
- package/dist/esm/components/Card.js.map +1 -0
- package/dist/esm/components/Carousel.js +351 -0
- package/dist/esm/components/Carousel.js.map +1 -0
- package/dist/esm/components/Collapse.js +193 -0
- package/dist/esm/components/Collapse.js.map +1 -0
- package/dist/esm/components/Collapses.js +99 -0
- package/dist/esm/components/Collapses.js.map +1 -0
- package/dist/esm/components/Dialog.js +230 -0
- package/dist/esm/components/Dialog.js.map +1 -0
- package/dist/esm/components/Dropdown.js +362 -0
- package/dist/esm/components/Dropdown.js.map +1 -0
- package/dist/esm/components/Loading.js +87 -0
- package/dist/esm/components/Loading.js.map +1 -0
- package/dist/esm/components/Menu.js +117 -0
- package/dist/esm/components/Menu.js.map +1 -0
- package/dist/esm/components/Message.js +56 -0
- package/dist/esm/components/Message.js.map +1 -0
- package/dist/esm/components/Modal.js +354 -0
- package/dist/esm/components/Modal.js.map +1 -0
- package/dist/esm/components/Navbar.js +287 -0
- package/dist/esm/components/Navbar.js.map +1 -0
- package/dist/esm/components/Pagination.js +191 -0
- package/dist/esm/components/Pagination.js.map +1 -0
- package/dist/esm/components/Panel.js +100 -0
- package/dist/esm/components/Panel.js.map +1 -0
- package/dist/esm/components/Popover.js +298 -0
- package/dist/esm/components/Popover.js.map +1 -0
- package/dist/esm/components/Reveal.js +157 -0
- package/dist/esm/components/Reveal.js.map +1 -0
- package/dist/esm/components/Sidebar.js +209 -0
- package/dist/esm/components/Sidebar.js.map +1 -0
- package/dist/esm/components/Steps.js +128 -0
- package/dist/esm/components/Steps.js.map +1 -0
- package/dist/esm/components/Tabs.js +335 -0
- package/dist/esm/components/Tabs.js.map +1 -0
- package/dist/esm/components/Toast.js +322 -0
- package/dist/esm/components/Toast.js.map +1 -0
- package/dist/esm/components/Tooltip.js +202 -0
- package/dist/esm/components/Tooltip.js.map +1 -0
- package/dist/esm/components/collapsesContext.js +12 -0
- package/dist/esm/components/collapsesContext.js.map +1 -0
- package/dist/esm/elements/Block.js +29 -0
- package/dist/esm/elements/Block.js.map +1 -0
- package/dist/esm/elements/Box.js +32 -0
- package/dist/esm/elements/Box.js.map +1 -0
- package/dist/esm/elements/Button.js +139 -0
- package/dist/esm/elements/Button.js.map +1 -0
- package/dist/esm/elements/Buttons.js +42 -0
- package/dist/esm/elements/Buttons.js.map +1 -0
- package/dist/esm/elements/Code.js +25 -0
- package/dist/esm/elements/Code.js.map +1 -0
- package/dist/esm/elements/Content.js +33 -0
- package/dist/esm/elements/Content.js.map +1 -0
- package/dist/esm/elements/Delete.js +35 -0
- package/dist/esm/elements/Delete.js.map +1 -0
- package/dist/esm/elements/Divider.js +27 -0
- package/dist/esm/elements/Divider.js.map +1 -0
- package/dist/esm/elements/Emphasis.js +25 -0
- package/dist/esm/elements/Emphasis.js.map +1 -0
- package/dist/esm/elements/Figure.js +58 -0
- package/dist/esm/elements/Figure.js.map +1 -0
- package/dist/esm/elements/Icon.js +256 -0
- package/dist/esm/elements/Icon.js.map +1 -0
- package/dist/esm/elements/IconText.js +36 -0
- package/dist/esm/elements/IconText.js.map +1 -0
- package/dist/esm/elements/Image.js +46 -0
- package/dist/esm/elements/Image.js.map +1 -0
- package/dist/esm/elements/Link.js +36 -0
- package/dist/esm/elements/Link.js.map +1 -0
- package/dist/esm/elements/LinkButton.js +37 -0
- package/dist/esm/elements/LinkButton.js.map +1 -0
- package/dist/esm/elements/ListItem.js +28 -0
- package/dist/esm/elements/ListItem.js.map +1 -0
- package/dist/esm/elements/Loader.js +37 -0
- package/dist/esm/elements/Loader.js.map +1 -0
- package/dist/esm/elements/Notification.js +316 -0
- package/dist/esm/elements/Notification.js.map +1 -0
- package/dist/esm/elements/OrderedList.js +31 -0
- package/dist/esm/elements/OrderedList.js.map +1 -0
- package/dist/esm/elements/Paragraph.js +28 -0
- package/dist/esm/elements/Paragraph.js.map +1 -0
- package/dist/esm/elements/Pre.js +25 -0
- package/dist/esm/elements/Pre.js.map +1 -0
- package/dist/esm/elements/Progress.js +33 -0
- package/dist/esm/elements/Progress.js.map +1 -0
- package/dist/esm/elements/Skeleton.js +30 -0
- package/dist/esm/elements/Skeleton.js.map +1 -0
- package/dist/esm/elements/Span.js +28 -0
- package/dist/esm/elements/Span.js.map +1 -0
- package/dist/esm/elements/Strong.js +25 -0
- package/dist/esm/elements/Strong.js.map +1 -0
- package/dist/esm/elements/SubTitle.js +51 -0
- package/dist/esm/elements/SubTitle.js.map +1 -0
- package/dist/esm/elements/Table.js +51 -0
- package/dist/esm/elements/Table.js.map +1 -0
- package/dist/esm/elements/Tag.js +49 -0
- package/dist/esm/elements/Tag.js.map +1 -0
- package/dist/esm/elements/Tags.js +34 -0
- package/dist/esm/elements/Tags.js.map +1 -0
- package/dist/esm/elements/Tbody.js +26 -0
- package/dist/esm/elements/Tbody.js.map +1 -0
- package/dist/esm/elements/Td.js +61 -0
- package/dist/esm/elements/Td.js.map +1 -0
- package/dist/esm/elements/Tfoot.js +26 -0
- package/dist/esm/elements/Tfoot.js.map +1 -0
- package/dist/esm/elements/Th.js +34 -0
- package/dist/esm/elements/Th.js.map +1 -0
- package/dist/esm/elements/Thead.js +26 -0
- package/dist/esm/elements/Thead.js.map +1 -0
- package/dist/esm/elements/Title.js +44 -0
- package/dist/esm/elements/Title.js.map +1 -0
- package/dist/esm/elements/Tr.js +31 -0
- package/dist/esm/elements/Tr.js.map +1 -0
- package/dist/esm/elements/UnorderedList.js +31 -0
- package/dist/esm/elements/UnorderedList.js.map +1 -0
- package/dist/esm/elements/iconProps.js +49 -0
- package/dist/esm/elements/iconProps.js.map +1 -0
- package/dist/esm/form/Autocomplete.js +279 -0
- package/dist/esm/form/Autocomplete.js.map +1 -0
- package/dist/esm/form/Checkbox.js +111 -0
- package/dist/esm/form/Checkbox.js.map +1 -0
- package/dist/esm/form/Checkboxes.js +90 -0
- package/dist/esm/form/Checkboxes.js.map +1 -0
- package/dist/esm/form/Control.js +79 -0
- package/dist/esm/form/Control.js.map +1 -0
- package/dist/esm/form/DateInput.js +88 -0
- package/dist/esm/form/DateInput.js.map +1 -0
- package/dist/esm/form/DateInputBase.js +263 -0
- package/dist/esm/form/DateInputBase.js.map +1 -0
- package/dist/esm/form/DateRangeInput.js +77 -0
- package/dist/esm/form/DateRangeInput.js.map +1 -0
- package/dist/esm/form/DateRangeInputBase.js +301 -0
- package/dist/esm/form/DateRangeInputBase.js.map +1 -0
- package/dist/esm/form/DateTimeInput.js +88 -0
- package/dist/esm/form/DateTimeInput.js.map +1 -0
- package/dist/esm/form/DateTimeInputBase.js +333 -0
- package/dist/esm/form/DateTimeInputBase.js.map +1 -0
- package/dist/esm/form/Field.js +167 -0
- package/dist/esm/form/Field.js.map +1 -0
- package/dist/esm/form/File.js +112 -0
- package/dist/esm/form/File.js.map +1 -0
- package/dist/esm/form/FormContext.js +194 -0
- package/dist/esm/form/FormContext.js.map +1 -0
- package/dist/esm/form/Input.js +88 -0
- package/dist/esm/form/Input.js.map +1 -0
- package/dist/esm/form/InputBase.js +40 -0
- package/dist/esm/form/InputBase.js.map +1 -0
- package/dist/esm/form/Numberinput.js +201 -0
- package/dist/esm/form/Numberinput.js.map +1 -0
- package/dist/esm/form/Radio.js +108 -0
- package/dist/esm/form/Radio.js.map +1 -0
- package/dist/esm/form/Radios.js +92 -0
- package/dist/esm/form/Radios.js.map +1 -0
- package/dist/esm/form/Rate.js +280 -0
- package/dist/esm/form/Rate.js.map +1 -0
- package/dist/esm/form/Select.js +81 -0
- package/dist/esm/form/Select.js.map +1 -0
- package/dist/esm/form/SelectBase.js +54 -0
- package/dist/esm/form/SelectBase.js.map +1 -0
- package/dist/esm/form/Slider.js +461 -0
- package/dist/esm/form/Slider.js.map +1 -0
- package/dist/esm/form/Switch.js +96 -0
- package/dist/esm/form/Switch.js.map +1 -0
- package/dist/esm/form/Taginput.js +351 -0
- package/dist/esm/form/Taginput.js.map +1 -0
- package/dist/esm/form/TextArea.js +77 -0
- package/dist/esm/form/TextArea.js.map +1 -0
- package/dist/esm/form/TextAreaBase.js +45 -0
- package/dist/esm/form/TextAreaBase.js.map +1 -0
- package/dist/esm/form/TimeInput.js +84 -0
- package/dist/esm/form/TimeInput.js.map +1 -0
- package/dist/esm/form/TimeInputBase.js +246 -0
- package/dist/esm/form/TimeInputBase.js.map +1 -0
- package/dist/esm/form/_pickerInternals/Calendar.js +760 -0
- package/dist/esm/form/_pickerInternals/Calendar.js.map +1 -0
- package/dist/esm/form/_pickerInternals/PickerPopover.js +95 -0
- package/dist/esm/form/_pickerInternals/PickerPopover.js.map +1 -0
- package/dist/esm/form/_pickerInternals/TimeWheels.js +721 -0
- package/dist/esm/form/_pickerInternals/TimeWheels.js.map +1 -0
- package/dist/esm/form/_pickerInternals/audioTick.js +109 -0
- package/dist/esm/form/_pickerInternals/audioTick.js.map +1 -0
- package/dist/esm/form/_pickerInternals/dateUtils.js +267 -0
- package/dist/esm/form/_pickerInternals/dateUtils.js.map +1 -0
- package/dist/esm/form/_pickerInternals/formatters.js +322 -0
- package/dist/esm/form/_pickerInternals/formatters.js.map +1 -0
- package/dist/esm/form/_pickerInternals/haptics.js +38 -0
- package/dist/esm/form/_pickerInternals/haptics.js.map +1 -0
- package/dist/esm/form/_pickerInternals/nativeInputSupport.js +25 -0
- package/dist/esm/form/_pickerInternals/nativeInputSupport.js.map +1 -0
- package/dist/esm/form/_pickerInternals/pickerTypes.js +50 -0
- package/dist/esm/form/_pickerInternals/pickerTypes.js.map +1 -0
- package/dist/esm/form/_pickerInternals/segmentMap.js +315 -0
- package/dist/esm/form/_pickerInternals/segmentMap.js.map +1 -0
- package/dist/esm/form/_pickerInternals/useNativeMobilePicker.js +49 -0
- package/dist/esm/form/_pickerInternals/useNativeMobilePicker.js.map +1 -0
- package/dist/esm/form/_pickerInternals/useSegmentedEntry.js +427 -0
- package/dist/esm/form/_pickerInternals/useSegmentedEntry.js.map +1 -0
- package/dist/esm/form/controlLoading.js +18 -0
- package/dist/esm/form/controlLoading.js.map +1 -0
- package/dist/esm/form/useAutoLabelId.js +88 -0
- package/dist/esm/form/useAutoLabelId.js.map +1 -0
- package/dist/esm/grid/Cell.js +35 -0
- package/dist/esm/grid/Cell.js.map +1 -0
- package/dist/esm/grid/Grid.js +51 -0
- package/dist/esm/grid/Grid.js.map +1 -0
- package/dist/esm/helpers/ClientOnly.js +33 -0
- package/dist/esm/helpers/ClientOnly.js.map +1 -0
- package/dist/esm/helpers/Config.js +60 -0
- package/dist/esm/helpers/Config.js.map +1 -0
- package/dist/esm/helpers/Theme.js +947 -0
- package/dist/esm/helpers/Theme.js.map +1 -0
- package/dist/esm/helpers/anchorAttrs.js +72 -0
- package/dist/esm/helpers/anchorAttrs.js.map +1 -0
- package/dist/esm/helpers/bulmaClassHelpers.js +415 -0
- package/dist/esm/helpers/bulmaClassHelpers.js.map +1 -0
- package/dist/esm/helpers/buttonType.js +24 -0
- package/dist/esm/helpers/buttonType.js.map +1 -0
- package/dist/esm/helpers/classNames.js +137 -0
- package/dist/esm/helpers/classNames.js.map +1 -0
- package/dist/esm/helpers/colorDeprecations.js +50 -0
- package/dist/esm/helpers/colorDeprecations.js.map +1 -0
- package/dist/esm/helpers/devWarnings.js +27 -0
- package/dist/esm/helpers/devWarnings.js.map +1 -0
- package/dist/esm/helpers/inertProps.js +27 -0
- package/dist/esm/helpers/inertProps.js.map +1 -0
- package/dist/esm/helpers/mergeBulmaStyles.js +20 -0
- package/dist/esm/helpers/mergeBulmaStyles.js.map +1 -0
- package/dist/esm/helpers/polymorphic.js +18 -0
- package/dist/esm/helpers/polymorphic.js.map +1 -0
- package/dist/esm/helpers/portal.js +76 -0
- package/dist/esm/helpers/portal.js.map +1 -0
- package/dist/esm/helpers/positionStacks.js +45 -0
- package/dist/esm/helpers/positionStacks.js.map +1 -0
- package/dist/esm/helpers/scrollLock.js +38 -0
- package/dist/esm/helpers/scrollLock.js.map +1 -0
- package/dist/esm/helpers/shadowDom.js +71 -0
- package/dist/esm/helpers/shadowDom.js.map +1 -0
- package/dist/esm/helpers/statusRegion.js +282 -0
- package/dist/esm/helpers/statusRegion.js.map +1 -0
- package/dist/esm/helpers/useAnchoredPosition.js +86 -0
- package/dist/esm/helpers/useAnchoredPosition.js.map +1 -0
- package/dist/esm/helpers/useBulmaClasses.js +158 -0
- package/dist/esm/helpers/useBulmaClasses.js.map +1 -0
- package/dist/esm/helpers/useClientLayoutEffect.js +13 -0
- package/dist/esm/helpers/useClientLayoutEffect.js.map +1 -0
- package/dist/esm/helpers/useColorClasses.js +84 -0
- package/dist/esm/helpers/useColorClasses.js.map +1 -0
- package/dist/esm/helpers/useFlexboxClasses.js +92 -0
- package/dist/esm/helpers/useFlexboxClasses.js.map +1 -0
- package/dist/esm/helpers/useFocusTrap.js +470 -0
- package/dist/esm/helpers/useFocusTrap.js.map +1 -0
- package/dist/esm/helpers/useIsHydrated.js +31 -0
- package/dist/esm/helpers/useIsHydrated.js.map +1 -0
- package/dist/esm/helpers/useOtherClasses.js +115 -0
- package/dist/esm/helpers/useOtherClasses.js.map +1 -0
- package/dist/esm/helpers/useSpacingClasses.js +82 -0
- package/dist/esm/helpers/useSpacingClasses.js.map +1 -0
- package/dist/esm/helpers/useTypographyClasses.js +93 -0
- package/dist/esm/helpers/useTypographyClasses.js.map +1 -0
- package/dist/esm/helpers/useVisibilityClasses.js +134 -0
- package/dist/esm/helpers/useVisibilityClasses.js.map +1 -0
- package/dist/esm/helpers/withSubComponents.js +24 -0
- package/dist/esm/helpers/withSubComponents.js.map +1 -0
- package/dist/esm/layout/Container.js +48 -0
- package/dist/esm/layout/Container.js.map +1 -0
- package/dist/esm/layout/Footer.js +33 -0
- package/dist/esm/layout/Footer.js.map +1 -0
- package/dist/esm/layout/Hero.js +90 -0
- package/dist/esm/layout/Hero.js.map +1 -0
- package/dist/esm/layout/Level.js +110 -0
- package/dist/esm/layout/Level.js.map +1 -0
- package/dist/esm/layout/Media.js +86 -0
- package/dist/esm/layout/Media.js.map +1 -0
- package/dist/esm/layout/Section.js +30 -0
- package/dist/esm/layout/Section.js.map +1 -0
- package/dist/extras.css +1 -1
- package/dist/extras.css.map +1 -1
- package/dist/index.cjs +187 -57
- package/dist/index.cjs.map +1 -1
- package/dist/index.esm.js +115 -17516
- package/dist/index.esm.js.map +1 -1
- package/dist/types/elements/Icon.d.ts +10 -7
- package/dist/types/form/Autocomplete.d.ts +1 -1
- package/dist/types/form/Checkboxes.d.ts +1 -1
- package/dist/types/form/DateInput.d.ts +1 -1
- package/dist/types/form/DateInputBase.d.ts +3 -4
- package/dist/types/form/DateRangeInput.d.ts +1 -1
- package/dist/types/form/DateTimeInput.d.ts +1 -1
- package/dist/types/form/DateTimeInputBase.d.ts +3 -4
- package/dist/types/form/Field.d.ts +2 -2
- package/dist/types/form/File.d.ts +4 -4
- package/dist/types/form/FormContext.d.ts +24 -2
- package/dist/types/form/Numberinput.d.ts +1 -1
- package/dist/types/form/Radios.d.ts +1 -1
- package/dist/types/form/Rate.d.ts +1 -1
- package/dist/types/form/Slider.d.ts +1 -1
- package/dist/types/form/Taginput.d.ts +1 -1
- package/dist/types/form/TimeInput.d.ts +1 -1
- package/dist/types/form/useAutoLabelId.d.ts +25 -4
- package/dist/types/helpers/Config.d.ts +3 -4
- package/dist/types/helpers/Theme.d.ts +44 -40
- package/dist/types-cjs/elements/Icon.d.ts +10 -7
- package/dist/types-cjs/form/Autocomplete.d.ts +1 -1
- package/dist/types-cjs/form/Checkboxes.d.ts +1 -1
- package/dist/types-cjs/form/DateInput.d.ts +1 -1
- package/dist/types-cjs/form/DateInputBase.d.ts +3 -4
- package/dist/types-cjs/form/DateRangeInput.d.ts +1 -1
- package/dist/types-cjs/form/DateTimeInput.d.ts +1 -1
- package/dist/types-cjs/form/DateTimeInputBase.d.ts +3 -4
- package/dist/types-cjs/form/Field.d.ts +2 -2
- package/dist/types-cjs/form/File.d.ts +4 -4
- package/dist/types-cjs/form/FormContext.d.ts +24 -2
- package/dist/types-cjs/form/Numberinput.d.ts +1 -1
- package/dist/types-cjs/form/Radios.d.ts +1 -1
- package/dist/types-cjs/form/Rate.d.ts +1 -1
- package/dist/types-cjs/form/Slider.d.ts +1 -1
- package/dist/types-cjs/form/Taginput.d.ts +1 -1
- package/dist/types-cjs/form/TimeInput.d.ts +1 -1
- package/dist/types-cjs/form/useAutoLabelId.d.ts +25 -4
- package/dist/types-cjs/helpers/Config.d.ts +3 -4
- package/dist/types-cjs/helpers/Theme.d.ts +44 -40
- package/dist/versions/bestax-no-dark-mode.css +1 -1
- package/dist/versions/bestax-no-dark-mode.css.map +1 -1
- package/dist/versions/bestax-no-helpers-prefixed.css +1 -1
- package/dist/versions/bestax-no-helpers-prefixed.css.map +1 -1
- package/dist/versions/bestax-no-helpers.css +1 -1
- package/dist/versions/bestax-no-helpers.css.map +1 -1
- package/dist/versions/bestax-prefixed.css +1 -1
- package/dist/versions/bestax-prefixed.css.map +1 -1
- package/package.json +1 -1
- package/src/scss/form/_dateinput.scss +37 -6
- package/src/scss/form/_datetimeinput.scss +7 -2
- package/src/scss/form/_picker-popover.scss +1 -4
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
/* @allxsmith/bestax-bulma — AI agents: see AGENTS.md in the package root, or https://bestax.io/llms.txt */
|
|
2
|
+
import { addYears, makeDate } from './dateUtils.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Segment-map utility for manual keyboard entry on a date / time picker input.
|
|
6
|
+
* Given a token-format string like `'YYYY-MM-DD'`, `'HH:mm:ss'`, `'hh:mm A'`,
|
|
7
|
+
* or a combined `'YYYY-MM-DD HH:mm'`, we compute the character ranges of each
|
|
8
|
+
* editable segment in the formatted output, plus arithmetic helpers
|
|
9
|
+
* (increment / digit-set) for each kind.
|
|
10
|
+
*
|
|
11
|
+
* Variable-width tokens (`Y`, `YYY`, `M`, `D`, `H`, `h`, `m`, `s`) cause the
|
|
12
|
+
* renderer to emit output of unpredictable width depending on value, which
|
|
13
|
+
* would shift segment boundaries between renders and break the selection.
|
|
14
|
+
* `buildSegmentMap` returns `null` for any format containing those, signalling
|
|
15
|
+
* the caller to fall back to free-form text entry.
|
|
16
|
+
*/
|
|
17
|
+
// Match longest tokens first so `HH` doesn't collapse to two `H`s and `YYYY`
|
|
18
|
+
// doesn't collapse to `YY` + `YY`. The single-char and odd-width variants
|
|
19
|
+
// (`Y`, `YYY`, `M`, `D`, `H`, `h`, `m`, `s`) are matched only so they can be
|
|
20
|
+
// explicitly rejected below — otherwise a format like `'YYY-MM'` would
|
|
21
|
+
// silently mis-tokenize as `YY` + a stray `Y`.
|
|
22
|
+
const TOKEN_REGEX = /YYYY|YYY|YY|Y|MM|M|DD|D|HH|hh|mm|ss|H|h|m|s|A|a/g;
|
|
23
|
+
// Fixed-width tokens render the same number of chars on every value. All are 2
|
|
24
|
+
// wide except the 4-digit year.
|
|
25
|
+
const tokenWidth = (token) => (token === 'YYYY' ? 4 : 2);
|
|
26
|
+
const KIND_BY_TOKEN = {
|
|
27
|
+
YYYY: 'year',
|
|
28
|
+
YY: 'year',
|
|
29
|
+
MM: 'month',
|
|
30
|
+
DD: 'day',
|
|
31
|
+
HH: 'hours',
|
|
32
|
+
hh: 'hours',
|
|
33
|
+
mm: 'minutes',
|
|
34
|
+
ss: 'seconds',
|
|
35
|
+
A: 'ampm',
|
|
36
|
+
a: 'ampm',
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Walk a token-format string and produce a {@link SegmentMap}, or `null` if
|
|
40
|
+
* the format contains any variable-width tokens (`H`, `h`, `m`, `s`) — the
|
|
41
|
+
* caller should treat that case as "segment mode unsupported, use the input's
|
|
42
|
+
* free-form text-entry fallback".
|
|
43
|
+
*/
|
|
44
|
+
function buildSegmentMap(format) {
|
|
45
|
+
const segments = [];
|
|
46
|
+
let cursor = 0;
|
|
47
|
+
let renderedPos = 0;
|
|
48
|
+
// Reset the regex's lastIndex from any previous use (it's a /g regex).
|
|
49
|
+
TOKEN_REGEX.lastIndex = 0;
|
|
50
|
+
let match;
|
|
51
|
+
while ((match = TOKEN_REGEX.exec(format)) !== null) {
|
|
52
|
+
const token = match[0];
|
|
53
|
+
// Reject variable / odd-width tokens — they render an unpredictable number
|
|
54
|
+
// of characters, which would break fixed segment boundaries.
|
|
55
|
+
if (token === 'H' ||
|
|
56
|
+
token === 'h' ||
|
|
57
|
+
token === 'm' ||
|
|
58
|
+
token === 's' ||
|
|
59
|
+
token === 'M' ||
|
|
60
|
+
token === 'D' ||
|
|
61
|
+
token === 'Y' ||
|
|
62
|
+
token === 'YYY') {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
// Capture any literal text between the previous cursor and this match.
|
|
66
|
+
if (match.index > cursor) {
|
|
67
|
+
const literal = format.slice(cursor, match.index);
|
|
68
|
+
segments.push({
|
|
69
|
+
kind: 'literal',
|
|
70
|
+
token: literal,
|
|
71
|
+
start: renderedPos,
|
|
72
|
+
end: renderedPos + literal.length,
|
|
73
|
+
});
|
|
74
|
+
renderedPos += literal.length;
|
|
75
|
+
}
|
|
76
|
+
const kind = KIND_BY_TOKEN[token];
|
|
77
|
+
const width = tokenWidth(token);
|
|
78
|
+
const seg = {
|
|
79
|
+
kind,
|
|
80
|
+
token,
|
|
81
|
+
start: renderedPos,
|
|
82
|
+
end: renderedPos + width,
|
|
83
|
+
};
|
|
84
|
+
if (kind === 'hours')
|
|
85
|
+
seg.hourFormat = token === 'hh' ? '12' : '24';
|
|
86
|
+
segments.push(seg);
|
|
87
|
+
renderedPos += width;
|
|
88
|
+
cursor = match.index + token.length;
|
|
89
|
+
}
|
|
90
|
+
// Trailing literal after the final token.
|
|
91
|
+
if (cursor < format.length) {
|
|
92
|
+
const literal = format.slice(cursor);
|
|
93
|
+
segments.push({
|
|
94
|
+
kind: 'literal',
|
|
95
|
+
token: literal,
|
|
96
|
+
start: renderedPos,
|
|
97
|
+
end: renderedPos + literal.length,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
const editable = [];
|
|
101
|
+
for (let i = 0; i < segments.length; i++) {
|
|
102
|
+
if (segments[i].kind !== 'literal')
|
|
103
|
+
editable.push(i);
|
|
104
|
+
}
|
|
105
|
+
// Defensive: a format with no editable token is not segment-editable.
|
|
106
|
+
if (editable.length === 0)
|
|
107
|
+
return null;
|
|
108
|
+
return { segments, editable };
|
|
109
|
+
}
|
|
110
|
+
// ---------------------------------------------------------------------------
|
|
111
|
+
// Arithmetic helpers
|
|
112
|
+
// ---------------------------------------------------------------------------
|
|
113
|
+
const wrap$1 = (n, max) => ((n % max) + max) % max;
|
|
114
|
+
const clamp = (n, min, max) => Math.max(min, Math.min(max, n));
|
|
115
|
+
/**
|
|
116
|
+
* Number of days in the month of `d` (handles leap Februarys). Built on
|
|
117
|
+
* `makeDate`, so a year below 100 keeps its own leap rule.
|
|
118
|
+
*/
|
|
119
|
+
const daysInMonth = (d) => makeDate(d.getFullYear(), d.getMonth() + 1, 0).getDate();
|
|
120
|
+
/**
|
|
121
|
+
* Returns a new Date with the given segment incremented (`delta` = +1 or -1).
|
|
122
|
+
* Wraps at segment boundaries: year is unbounded, month 0↔11 (in place, no
|
|
123
|
+
* year roll), day within the current month's length, hours-24 0↔23, hours-12
|
|
124
|
+
* 1↔12, minutes/seconds 0↔59, am/pm toggles. Day is re-clamped after a
|
|
125
|
+
* month/year change (Jan 31 → Feb 28/29). `isPm` is the *current* meridiem
|
|
126
|
+
* when editing a 12h hour segment so we preserve it across the increment;
|
|
127
|
+
* date segments ignore it.
|
|
128
|
+
*/
|
|
129
|
+
function incrementSegmentValue(segment, currentDate, delta, isPm) {
|
|
130
|
+
const next = new Date(currentDate);
|
|
131
|
+
switch (segment.kind) {
|
|
132
|
+
case 'year':
|
|
133
|
+
// Unbounded ±1; addYears clamps Feb 29 → Feb 28 on non-leap targets.
|
|
134
|
+
return addYears(next, delta);
|
|
135
|
+
case 'month': {
|
|
136
|
+
// In-place wrap Jan↔Dec without rolling the year, then clamp the day to
|
|
137
|
+
// the new month's length (e.g. Jan 31 → Feb → day 28/29).
|
|
138
|
+
const day = next.getDate();
|
|
139
|
+
const m0 = wrap$1(next.getMonth() + delta, 12);
|
|
140
|
+
next.setDate(1);
|
|
141
|
+
next.setMonth(m0);
|
|
142
|
+
next.setDate(Math.min(day, daysInMonth(next)));
|
|
143
|
+
return next;
|
|
144
|
+
}
|
|
145
|
+
case 'day': {
|
|
146
|
+
// Wrap within the current month's day count.
|
|
147
|
+
const dim = daysInMonth(next);
|
|
148
|
+
next.setDate(wrap$1(next.getDate() - 1 + delta, dim) + 1);
|
|
149
|
+
return next;
|
|
150
|
+
}
|
|
151
|
+
case 'hours': {
|
|
152
|
+
if (segment.hourFormat === '12') {
|
|
153
|
+
const h = next.getHours();
|
|
154
|
+
const displayed = h % 12 === 0 ? 12 : h % 12; // 1..12
|
|
155
|
+
// Move within 1..12 and preserve the current AM/PM.
|
|
156
|
+
const nextDisplayed = wrap$1(displayed - 1 + delta, 12) + 1;
|
|
157
|
+
const next24 = isPm ? (nextDisplayed % 12) + 12 : nextDisplayed % 12;
|
|
158
|
+
next.setHours(next24);
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
next.setHours(wrap$1(next.getHours() + delta, 24));
|
|
162
|
+
}
|
|
163
|
+
return next;
|
|
164
|
+
}
|
|
165
|
+
case 'minutes':
|
|
166
|
+
next.setMinutes(wrap$1(next.getMinutes() + delta, 60));
|
|
167
|
+
return next;
|
|
168
|
+
case 'seconds':
|
|
169
|
+
next.setSeconds(wrap$1(next.getSeconds() + delta, 60));
|
|
170
|
+
return next;
|
|
171
|
+
case 'ampm':
|
|
172
|
+
// Toggle by shifting 12 hours in either direction.
|
|
173
|
+
next.setHours((next.getHours() + 12) % 24);
|
|
174
|
+
return next;
|
|
175
|
+
default:
|
|
176
|
+
return next;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Apply a buffered string of typed digits to the active segment. Returns the
|
|
181
|
+
* updated Date plus an `advance` flag — `true` when the buffer is full (its
|
|
182
|
+
* token width) or when the first digit alone forecloses any valid multi-digit
|
|
183
|
+
* completion (e.g., first digit `3` for hours-24 since 30+ is out of range, or
|
|
184
|
+
* `2` for month since there is no month 20+).
|
|
185
|
+
*
|
|
186
|
+
* Special cases: a leading `'0'` is held without committing for hours-12,
|
|
187
|
+
* month, and day (00 isn't a valid display value for any of them) — the buffer
|
|
188
|
+
* must reach `'0X'` first. Month/day writes clamp to range and re-clamp the
|
|
189
|
+
* day to the month's length; year writes re-clamp the day across leap-year
|
|
190
|
+
* boundaries.
|
|
191
|
+
*/
|
|
192
|
+
function setSegmentValue(segment, currentDate, rawDigits, isPm) {
|
|
193
|
+
if (rawDigits.length === 0)
|
|
194
|
+
return { date: currentDate, advance: false };
|
|
195
|
+
const value = parseInt(rawDigits, 10);
|
|
196
|
+
if (Number.isNaN(value))
|
|
197
|
+
return { date: currentDate, advance: false };
|
|
198
|
+
const next = new Date(currentDate);
|
|
199
|
+
switch (segment.kind) {
|
|
200
|
+
case 'year': {
|
|
201
|
+
// YYYY buffers 4 digits; YY buffers 2 (mapped into the 2000s).
|
|
202
|
+
const full = segment.token === 'YYYY';
|
|
203
|
+
const maxLen = full ? 4 : 2;
|
|
204
|
+
const yr = full ? value : 2000 + (value % 100);
|
|
205
|
+
// Set day to 1 before changing the year so a Feb-29 source doesn't roll
|
|
206
|
+
// into March on a non-leap target; then restore the clamped day.
|
|
207
|
+
const day = next.getDate();
|
|
208
|
+
next.setDate(1);
|
|
209
|
+
next.setFullYear(yr);
|
|
210
|
+
next.setDate(Math.min(day, daysInMonth(next)));
|
|
211
|
+
return { date: next, advance: rawDigits.length >= maxLen };
|
|
212
|
+
}
|
|
213
|
+
case 'month': {
|
|
214
|
+
if (rawDigits === '0') {
|
|
215
|
+
// Hold: 00 is invalid; wait for the second digit (01..09).
|
|
216
|
+
return { date: currentDate, advance: false };
|
|
217
|
+
}
|
|
218
|
+
const clamped = clamp(value, 1, 12);
|
|
219
|
+
const day = next.getDate();
|
|
220
|
+
next.setDate(1);
|
|
221
|
+
next.setMonth(clamped - 1);
|
|
222
|
+
next.setDate(Math.min(day, daysInMonth(next)));
|
|
223
|
+
// Advance when buffer is full, or first digit >=2 (no month 20+). First
|
|
224
|
+
// digit 1 still waits for 10/11/12.
|
|
225
|
+
const advance = rawDigits.length >= 2 || value >= 2;
|
|
226
|
+
return { date: next, advance };
|
|
227
|
+
}
|
|
228
|
+
case 'day': {
|
|
229
|
+
if (rawDigits === '0') {
|
|
230
|
+
// Hold: 00 is invalid; wait for the second digit (01..09).
|
|
231
|
+
return { date: currentDate, advance: false };
|
|
232
|
+
}
|
|
233
|
+
const clamped = clamp(value, 1, daysInMonth(next));
|
|
234
|
+
next.setDate(clamped);
|
|
235
|
+
// Advance when buffer is full, or first digit >=4 (no day 40+). First
|
|
236
|
+
// digit 3 still waits for 30/31.
|
|
237
|
+
const advance = rawDigits.length >= 2 || value >= 4;
|
|
238
|
+
return { date: next, advance };
|
|
239
|
+
}
|
|
240
|
+
case 'hours': {
|
|
241
|
+
if (segment.hourFormat === '12') {
|
|
242
|
+
if (rawDigits === '0') {
|
|
243
|
+
// Don't commit yet; wait for the second digit (01..09 are valid).
|
|
244
|
+
return { date: currentDate, advance: false };
|
|
245
|
+
}
|
|
246
|
+
const clamped = clamp(value, 1, 12);
|
|
247
|
+
const h24 = isPm ? (clamped % 12) + 12 : clamped % 12;
|
|
248
|
+
next.setHours(h24);
|
|
249
|
+
// Advance when the buffer is full, or when no valid 2-digit
|
|
250
|
+
// completion starting with this digit exists. For 12h: only 0 and 1
|
|
251
|
+
// can be the first of a 2-digit value (giving 01..09 and 10..12);
|
|
252
|
+
// any other first digit completes the segment.
|
|
253
|
+
const advance = rawDigits.length >= 2 || value >= 2;
|
|
254
|
+
return { date: next, advance };
|
|
255
|
+
}
|
|
256
|
+
// 24h: valid range 0..23.
|
|
257
|
+
const clamped = clamp(value, 0, 23);
|
|
258
|
+
next.setHours(clamped);
|
|
259
|
+
// Advance when buffer is full, or when first digit is 3..9 (since
|
|
260
|
+
// 30..99 are all out of range). First digits 0, 1, 2 can still accept
|
|
261
|
+
// a second digit.
|
|
262
|
+
const advance = rawDigits.length >= 2 || value >= 3;
|
|
263
|
+
return { date: next, advance };
|
|
264
|
+
}
|
|
265
|
+
case 'minutes': {
|
|
266
|
+
const clamped = clamp(value, 0, 59);
|
|
267
|
+
next.setMinutes(clamped);
|
|
268
|
+
// Advance when buffer full, or first digit 6..9 (can't form 60..99).
|
|
269
|
+
const advance = rawDigits.length >= 2 || value >= 6;
|
|
270
|
+
return { date: next, advance };
|
|
271
|
+
}
|
|
272
|
+
case 'seconds': {
|
|
273
|
+
const clamped = clamp(value, 0, 59);
|
|
274
|
+
next.setSeconds(clamped);
|
|
275
|
+
const advance = rawDigits.length >= 2 || value >= 6;
|
|
276
|
+
return { date: next, advance };
|
|
277
|
+
}
|
|
278
|
+
default:
|
|
279
|
+
return { date: currentDate, advance: false };
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
/** Force the meridiem of `currentDate` to AM (isPm=false) or PM (true). */
|
|
283
|
+
function setAmPm(currentDate, isPm) {
|
|
284
|
+
const next = new Date(currentDate);
|
|
285
|
+
const h = next.getHours();
|
|
286
|
+
if (isPm && h < 12)
|
|
287
|
+
next.setHours(h + 12);
|
|
288
|
+
if (!isPm && h >= 12)
|
|
289
|
+
next.setHours(h - 12);
|
|
290
|
+
return next;
|
|
291
|
+
}
|
|
292
|
+
/** Map a caret position into the index of the segment that contains it. */
|
|
293
|
+
function segmentIndexAtCaret(map, caret) {
|
|
294
|
+
// Prefer an editable segment that strictly contains the caret; otherwise
|
|
295
|
+
// the nearest editable segment by char distance.
|
|
296
|
+
for (const idx of map.editable) {
|
|
297
|
+
const s = map.segments[idx];
|
|
298
|
+
if (caret >= s.start && caret <= s.end)
|
|
299
|
+
return idx;
|
|
300
|
+
}
|
|
301
|
+
let bestIdx = map.editable[0] ?? null;
|
|
302
|
+
let bestDist = Infinity;
|
|
303
|
+
for (const idx of map.editable) {
|
|
304
|
+
const s = map.segments[idx];
|
|
305
|
+
const dist = Math.min(Math.abs(caret - s.start), Math.abs(caret - s.end));
|
|
306
|
+
if (dist < bestDist) {
|
|
307
|
+
bestDist = dist;
|
|
308
|
+
bestIdx = idx;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
return bestIdx;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
export { buildSegmentMap, incrementSegmentValue, segmentIndexAtCaret, setAmPm, setSegmentValue };
|
|
315
|
+
//# sourceMappingURL=segmentMap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"segmentMap.js","sources":["../../../../src/form/_pickerInternals/segmentMap.ts"],"sourcesContent":["/**\n * Segment-map utility for manual keyboard entry on a date / time picker input.\n * Given a token-format string like `'YYYY-MM-DD'`, `'HH:mm:ss'`, `'hh:mm A'`,\n * or a combined `'YYYY-MM-DD HH:mm'`, we compute the character ranges of each\n * editable segment in the formatted output, plus arithmetic helpers\n * (increment / digit-set) for each kind.\n *\n * Variable-width tokens (`Y`, `YYY`, `M`, `D`, `H`, `h`, `m`, `s`) cause the\n * renderer to emit output of unpredictable width depending on value, which\n * would shift segment boundaries between renders and break the selection.\n * `buildSegmentMap` returns `null` for any format containing those, signalling\n * the caller to fall back to free-form text entry.\n */\n\nimport { addYears, makeDate } from './dateUtils';\n\nexport type SegmentKind =\n | 'year'\n | 'month'\n | 'day'\n | 'hours'\n | 'minutes'\n | 'seconds'\n | 'ampm'\n | 'literal';\n\nexport interface Segment {\n kind: SegmentKind;\n /** Source token (e.g., `'HH'`) for editable segments; literal text for `'literal'`. */\n token: string;\n /** Inclusive char index into the formatted-string output. */\n start: number;\n /** Exclusive char index. */\n end: number;\n /** Populated only when `kind === 'hours'`. */\n hourFormat?: '12' | '24';\n}\n\nexport interface SegmentMap {\n segments: Segment[];\n /** Indices of non-literal segments, left to right. */\n editable: number[];\n}\n\n// Match longest tokens first so `HH` doesn't collapse to two `H`s and `YYYY`\n// doesn't collapse to `YY` + `YY`. The single-char and odd-width variants\n// (`Y`, `YYY`, `M`, `D`, `H`, `h`, `m`, `s`) are matched only so they can be\n// explicitly rejected below — otherwise a format like `'YYY-MM'` would\n// silently mis-tokenize as `YY` + a stray `Y`.\nconst TOKEN_REGEX = /YYYY|YYY|YY|Y|MM|M|DD|D|HH|hh|mm|ss|H|h|m|s|A|a/g;\n\n// Fixed-width tokens render the same number of chars on every value. All are 2\n// wide except the 4-digit year.\nconst tokenWidth = (token: string): number => (token === 'YYYY' ? 4 : 2);\n\nconst KIND_BY_TOKEN: Record<string, SegmentKind> = {\n YYYY: 'year',\n YY: 'year',\n MM: 'month',\n DD: 'day',\n HH: 'hours',\n hh: 'hours',\n mm: 'minutes',\n ss: 'seconds',\n A: 'ampm',\n a: 'ampm',\n};\n\n/**\n * Walk a token-format string and produce a {@link SegmentMap}, or `null` if\n * the format contains any variable-width tokens (`H`, `h`, `m`, `s`) — the\n * caller should treat that case as \"segment mode unsupported, use the input's\n * free-form text-entry fallback\".\n */\nexport function buildSegmentMap(format: string): SegmentMap | null {\n const segments: Segment[] = [];\n let cursor = 0;\n let renderedPos = 0;\n // Reset the regex's lastIndex from any previous use (it's a /g regex).\n TOKEN_REGEX.lastIndex = 0;\n let match: RegExpExecArray | null;\n while ((match = TOKEN_REGEX.exec(format)) !== null) {\n const token = match[0];\n\n // Reject variable / odd-width tokens — they render an unpredictable number\n // of characters, which would break fixed segment boundaries.\n if (\n token === 'H' ||\n token === 'h' ||\n token === 'm' ||\n token === 's' ||\n token === 'M' ||\n token === 'D' ||\n token === 'Y' ||\n token === 'YYY'\n ) {\n return null;\n }\n\n // Capture any literal text between the previous cursor and this match.\n if (match.index > cursor) {\n const literal = format.slice(cursor, match.index);\n segments.push({\n kind: 'literal',\n token: literal,\n start: renderedPos,\n end: renderedPos + literal.length,\n });\n renderedPos += literal.length;\n }\n\n const kind = KIND_BY_TOKEN[token];\n const width = tokenWidth(token);\n const seg: Segment = {\n kind,\n token,\n start: renderedPos,\n end: renderedPos + width,\n };\n if (kind === 'hours') seg.hourFormat = token === 'hh' ? '12' : '24';\n segments.push(seg);\n\n renderedPos += width;\n cursor = match.index + token.length;\n }\n\n // Trailing literal after the final token.\n if (cursor < format.length) {\n const literal = format.slice(cursor);\n segments.push({\n kind: 'literal',\n token: literal,\n start: renderedPos,\n end: renderedPos + literal.length,\n });\n }\n\n const editable: number[] = [];\n for (let i = 0; i < segments.length; i++) {\n if (segments[i].kind !== 'literal') editable.push(i);\n }\n // Defensive: a format with no editable token is not segment-editable.\n if (editable.length === 0) return null;\n return { segments, editable };\n}\n\n// ---------------------------------------------------------------------------\n// Arithmetic helpers\n// ---------------------------------------------------------------------------\n\nconst wrap = (n: number, max: number): number => ((n % max) + max) % max;\nconst clamp = (n: number, min: number, max: number): number =>\n Math.max(min, Math.min(max, n));\n\n/**\n * Number of days in the month of `d` (handles leap Februarys). Built on\n * `makeDate`, so a year below 100 keeps its own leap rule.\n */\nconst daysInMonth = (d: Date): number =>\n makeDate(d.getFullYear(), d.getMonth() + 1, 0).getDate();\n\n/**\n * Returns a new Date with the given segment incremented (`delta` = +1 or -1).\n * Wraps at segment boundaries: year is unbounded, month 0↔11 (in place, no\n * year roll), day within the current month's length, hours-24 0↔23, hours-12\n * 1↔12, minutes/seconds 0↔59, am/pm toggles. Day is re-clamped after a\n * month/year change (Jan 31 → Feb 28/29). `isPm` is the *current* meridiem\n * when editing a 12h hour segment so we preserve it across the increment;\n * date segments ignore it.\n */\nexport function incrementSegmentValue(\n segment: Segment,\n currentDate: Date,\n delta: number,\n isPm: boolean\n): Date {\n const next = new Date(currentDate);\n switch (segment.kind) {\n case 'year':\n // Unbounded ±1; addYears clamps Feb 29 → Feb 28 on non-leap targets.\n return addYears(next, delta);\n case 'month': {\n // In-place wrap Jan↔Dec without rolling the year, then clamp the day to\n // the new month's length (e.g. Jan 31 → Feb → day 28/29).\n const day = next.getDate();\n const m0 = wrap(next.getMonth() + delta, 12);\n next.setDate(1);\n next.setMonth(m0);\n next.setDate(Math.min(day, daysInMonth(next)));\n return next;\n }\n case 'day': {\n // Wrap within the current month's day count.\n const dim = daysInMonth(next);\n next.setDate(wrap(next.getDate() - 1 + delta, dim) + 1);\n return next;\n }\n case 'hours': {\n if (segment.hourFormat === '12') {\n const h = next.getHours();\n const displayed = h % 12 === 0 ? 12 : h % 12; // 1..12\n // Move within 1..12 and preserve the current AM/PM.\n const nextDisplayed = wrap(displayed - 1 + delta, 12) + 1;\n const next24 = isPm ? (nextDisplayed % 12) + 12 : nextDisplayed % 12;\n next.setHours(next24);\n } else {\n next.setHours(wrap(next.getHours() + delta, 24));\n }\n return next;\n }\n case 'minutes':\n next.setMinutes(wrap(next.getMinutes() + delta, 60));\n return next;\n case 'seconds':\n next.setSeconds(wrap(next.getSeconds() + delta, 60));\n return next;\n case 'ampm':\n // Toggle by shifting 12 hours in either direction.\n next.setHours((next.getHours() + 12) % 24);\n return next;\n default:\n return next;\n }\n}\n\n/**\n * Apply a buffered string of typed digits to the active segment. Returns the\n * updated Date plus an `advance` flag — `true` when the buffer is full (its\n * token width) or when the first digit alone forecloses any valid multi-digit\n * completion (e.g., first digit `3` for hours-24 since 30+ is out of range, or\n * `2` for month since there is no month 20+).\n *\n * Special cases: a leading `'0'` is held without committing for hours-12,\n * month, and day (00 isn't a valid display value for any of them) — the buffer\n * must reach `'0X'` first. Month/day writes clamp to range and re-clamp the\n * day to the month's length; year writes re-clamp the day across leap-year\n * boundaries.\n */\nexport function setSegmentValue(\n segment: Segment,\n currentDate: Date,\n rawDigits: string,\n isPm: boolean\n): { date: Date; advance: boolean } {\n if (rawDigits.length === 0) return { date: currentDate, advance: false };\n const value = parseInt(rawDigits, 10);\n if (Number.isNaN(value)) return { date: currentDate, advance: false };\n const next = new Date(currentDate);\n switch (segment.kind) {\n case 'year': {\n // YYYY buffers 4 digits; YY buffers 2 (mapped into the 2000s).\n const full = segment.token === 'YYYY';\n const maxLen = full ? 4 : 2;\n const yr = full ? value : 2000 + (value % 100);\n // Set day to 1 before changing the year so a Feb-29 source doesn't roll\n // into March on a non-leap target; then restore the clamped day.\n const day = next.getDate();\n next.setDate(1);\n next.setFullYear(yr);\n next.setDate(Math.min(day, daysInMonth(next)));\n return { date: next, advance: rawDigits.length >= maxLen };\n }\n case 'month': {\n if (rawDigits === '0') {\n // Hold: 00 is invalid; wait for the second digit (01..09).\n return { date: currentDate, advance: false };\n }\n const clamped = clamp(value, 1, 12);\n const day = next.getDate();\n next.setDate(1);\n next.setMonth(clamped - 1);\n next.setDate(Math.min(day, daysInMonth(next)));\n // Advance when buffer is full, or first digit >=2 (no month 20+). First\n // digit 1 still waits for 10/11/12.\n const advance = rawDigits.length >= 2 || value >= 2;\n return { date: next, advance };\n }\n case 'day': {\n if (rawDigits === '0') {\n // Hold: 00 is invalid; wait for the second digit (01..09).\n return { date: currentDate, advance: false };\n }\n const clamped = clamp(value, 1, daysInMonth(next));\n next.setDate(clamped);\n // Advance when buffer is full, or first digit >=4 (no day 40+). First\n // digit 3 still waits for 30/31.\n const advance = rawDigits.length >= 2 || value >= 4;\n return { date: next, advance };\n }\n case 'hours': {\n if (segment.hourFormat === '12') {\n if (rawDigits === '0') {\n // Don't commit yet; wait for the second digit (01..09 are valid).\n return { date: currentDate, advance: false };\n }\n const clamped = clamp(value, 1, 12);\n const h24 = isPm ? (clamped % 12) + 12 : clamped % 12;\n next.setHours(h24);\n // Advance when the buffer is full, or when no valid 2-digit\n // completion starting with this digit exists. For 12h: only 0 and 1\n // can be the first of a 2-digit value (giving 01..09 and 10..12);\n // any other first digit completes the segment.\n const advance = rawDigits.length >= 2 || value >= 2;\n return { date: next, advance };\n }\n // 24h: valid range 0..23.\n const clamped = clamp(value, 0, 23);\n next.setHours(clamped);\n // Advance when buffer is full, or when first digit is 3..9 (since\n // 30..99 are all out of range). First digits 0, 1, 2 can still accept\n // a second digit.\n const advance = rawDigits.length >= 2 || value >= 3;\n return { date: next, advance };\n }\n case 'minutes': {\n const clamped = clamp(value, 0, 59);\n next.setMinutes(clamped);\n // Advance when buffer full, or first digit 6..9 (can't form 60..99).\n const advance = rawDigits.length >= 2 || value >= 6;\n return { date: next, advance };\n }\n case 'seconds': {\n const clamped = clamp(value, 0, 59);\n next.setSeconds(clamped);\n const advance = rawDigits.length >= 2 || value >= 6;\n return { date: next, advance };\n }\n default:\n return { date: currentDate, advance: false };\n }\n}\n\n/** Force the meridiem of `currentDate` to AM (isPm=false) or PM (true). */\nexport function setAmPm(currentDate: Date, isPm: boolean): Date {\n const next = new Date(currentDate);\n const h = next.getHours();\n if (isPm && h < 12) next.setHours(h + 12);\n if (!isPm && h >= 12) next.setHours(h - 12);\n return next;\n}\n\n/** Map a caret position into the index of the segment that contains it. */\nexport function segmentIndexAtCaret(\n map: SegmentMap,\n caret: number\n): number | null {\n // Prefer an editable segment that strictly contains the caret; otherwise\n // the nearest editable segment by char distance.\n for (const idx of map.editable) {\n const s = map.segments[idx];\n if (caret >= s.start && caret <= s.end) return idx;\n }\n let bestIdx = map.editable[0] ?? null;\n let bestDist = Infinity;\n for (const idx of map.editable) {\n const s = map.segments[idx];\n const dist = Math.min(Math.abs(caret - s.start), Math.abs(caret - s.end));\n if (dist < bestDist) {\n bestDist = dist;\n bestIdx = idx;\n }\n }\n return bestIdx;\n}\n"],"names":["wrap"],"mappings":"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"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/* @allxsmith/bestax-bulma — AI agents: see AGENTS.md in the package root, or https://bestax.io/llms.txt */
|
|
2
|
+
import { useState, useEffect } from 'react';
|
|
3
|
+
|
|
4
|
+
function useNativeMobilePicker(options = {}) {
|
|
5
|
+
const { smallViewportMaxPx = 768, force } = options;
|
|
6
|
+
const [state, setState] = useState({
|
|
7
|
+
isCoarsePointer: false,
|
|
8
|
+
isSmallViewport: false,
|
|
9
|
+
shouldUseNative: force === true,
|
|
10
|
+
});
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
const isBrowser = typeof window !== 'undefined' && typeof window.matchMedia === 'function';
|
|
13
|
+
if (!isBrowser)
|
|
14
|
+
return undefined;
|
|
15
|
+
const coarse = window.matchMedia('(pointer: coarse)');
|
|
16
|
+
const small = window.matchMedia(`(max-width: ${smallViewportMaxPx}px)`);
|
|
17
|
+
const update = () => {
|
|
18
|
+
const isCoarsePointer = coarse.matches;
|
|
19
|
+
const isSmallViewport = small.matches;
|
|
20
|
+
const shouldUseNative = force === true
|
|
21
|
+
? true
|
|
22
|
+
: force === false
|
|
23
|
+
? false
|
|
24
|
+
: isCoarsePointer && isSmallViewport;
|
|
25
|
+
setState({ isCoarsePointer, isSmallViewport, shouldUseNative });
|
|
26
|
+
};
|
|
27
|
+
update();
|
|
28
|
+
const subscribe = (mql, fn) => {
|
|
29
|
+
if (mql.addEventListener) {
|
|
30
|
+
// eslint-disable-next-line @eslint-react/web-api-no-leaked-event-listener -- cleanup returned below and run in the effect teardown
|
|
31
|
+
mql.addEventListener('change', fn);
|
|
32
|
+
return () => mql.removeEventListener('change', fn);
|
|
33
|
+
}
|
|
34
|
+
// Older Safari fallback
|
|
35
|
+
mql.addListener(fn);
|
|
36
|
+
return () => mql.removeListener(fn);
|
|
37
|
+
};
|
|
38
|
+
const unsubCoarse = subscribe(coarse, update);
|
|
39
|
+
const unsubSmall = subscribe(small, update);
|
|
40
|
+
return () => {
|
|
41
|
+
unsubCoarse();
|
|
42
|
+
unsubSmall();
|
|
43
|
+
};
|
|
44
|
+
}, [smallViewportMaxPx, force]);
|
|
45
|
+
return state;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export { useNativeMobilePicker };
|
|
49
|
+
//# sourceMappingURL=useNativeMobilePicker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useNativeMobilePicker.js","sources":["../../../../src/form/_pickerInternals/useNativeMobilePicker.ts"],"sourcesContent":["import { useEffect, useState } from 'react';\n\nexport interface NativeMobileDetect {\n isCoarsePointer: boolean;\n isSmallViewport: boolean;\n shouldUseNative: boolean;\n}\n\nexport interface UseNativeMobilePickerOptions {\n smallViewportMaxPx?: number;\n /** When set, overrides detection. */\n force?: boolean;\n}\n\nexport function useNativeMobilePicker(\n options: UseNativeMobilePickerOptions = {}\n): NativeMobileDetect {\n const { smallViewportMaxPx = 768, force } = options;\n const [state, setState] = useState<NativeMobileDetect>({\n isCoarsePointer: false,\n isSmallViewport: false,\n shouldUseNative: force === true,\n });\n\n useEffect(() => {\n const isBrowser =\n typeof window !== 'undefined' && typeof window.matchMedia === 'function';\n if (!isBrowser) return undefined;\n const coarse = window.matchMedia('(pointer: coarse)');\n const small = window.matchMedia(`(max-width: ${smallViewportMaxPx}px)`);\n\n const update = () => {\n const isCoarsePointer = coarse.matches;\n const isSmallViewport = small.matches;\n const shouldUseNative =\n force === true\n ? true\n : force === false\n ? false\n : isCoarsePointer && isSmallViewport;\n setState({ isCoarsePointer, isSmallViewport, shouldUseNative });\n };\n\n update();\n const subscribe = (mql: MediaQueryList, fn: () => void) => {\n if (mql.addEventListener) {\n // eslint-disable-next-line @eslint-react/web-api-no-leaked-event-listener -- cleanup returned below and run in the effect teardown\n mql.addEventListener('change', fn);\n return () => mql.removeEventListener('change', fn);\n }\n // Older Safari fallback\n mql.addListener(fn);\n return () => mql.removeListener(fn);\n };\n const unsubCoarse = subscribe(coarse, update);\n const unsubSmall = subscribe(small, update);\n return () => {\n unsubCoarse();\n unsubSmall();\n };\n }, [smallViewportMaxPx, force]);\n\n return state;\n}\n"],"names":[],"mappings":";;;AAcM,SAAU,qBAAqB,CACnC,OAAA,GAAwC,EAAE,EAAA;IAE1C,MAAM,EAAE,kBAAkB,GAAG,GAAG,EAAE,KAAK,EAAE,GAAG,OAAO;AACnD,IAAA,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAqB;AACrD,QAAA,eAAe,EAAE,KAAK;AACtB,QAAA,eAAe,EAAE,KAAK;QACtB,eAAe,EAAE,KAAK,KAAK,IAAI;AAChC,KAAA,CAAC;IAEF,SAAS,CAAC,MAAK;AACb,QAAA,MAAM,SAAS,GACb,OAAO,MAAM,KAAK,WAAW,IAAI,OAAO,MAAM,CAAC,UAAU,KAAK,UAAU;AAC1E,QAAA,IAAI,CAAC,SAAS;AAAE,YAAA,OAAO,SAAS;QAChC,MAAM,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC,mBAAmB,CAAC;QACrD,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,CAAC,CAAA,YAAA,EAAe,kBAAkB,CAAA,GAAA,CAAK,CAAC;QAEvE,MAAM,MAAM,GAAG,MAAK;AAClB,YAAA,MAAM,eAAe,GAAG,MAAM,CAAC,OAAO;AACtC,YAAA,MAAM,eAAe,GAAG,KAAK,CAAC,OAAO;AACrC,YAAA,MAAM,eAAe,GACnB,KAAK,KAAK;AACR,kBAAE;kBACA,KAAK,KAAK;AACV,sBAAE;AACF,sBAAE,eAAe,IAAI,eAAe;YAC1C,QAAQ,CAAC,EAAE,eAAe,EAAE,eAAe,EAAE,eAAe,EAAE,CAAC;AACjE,QAAA,CAAC;AAED,QAAA,MAAM,EAAE;AACR,QAAA,MAAM,SAAS,GAAG,CAAC,GAAmB,EAAE,EAAc,KAAI;AACxD,YAAA,IAAI,GAAG,CAAC,gBAAgB,EAAE;;AAExB,gBAAA,GAAG,CAAC,gBAAgB,CAAC,QAAQ,EAAE,EAAE,CAAC;gBAClC,OAAO,MAAM,GAAG,CAAC,mBAAmB,CAAC,QAAQ,EAAE,EAAE,CAAC;YACpD;;AAEA,YAAA,GAAG,CAAC,WAAW,CAAC,EAAE,CAAC;YACnB,OAAO,MAAM,GAAG,CAAC,cAAc,CAAC,EAAE,CAAC;AACrC,QAAA,CAAC;QACD,MAAM,WAAW,GAAG,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC;QAC7C,MAAM,UAAU,GAAG,SAAS,CAAC,KAAK,EAAE,MAAM,CAAC;AAC3C,QAAA,OAAO,MAAK;AACV,YAAA,WAAW,EAAE;AACb,YAAA,UAAU,EAAE;AACd,QAAA,CAAC;AACH,IAAA,CAAC,EAAE,CAAC,kBAAkB,EAAE,KAAK,CAAC,CAAC;AAE/B,IAAA,OAAO,KAAK;AACd;;"}
|