@basic-ui/core 0.0.53 → 0.0.54
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 +3 -3
- package/build/cjs/index.js +88 -88
- package/build/cjs/index.js.map +1 -1
- package/build/esm/Accordion/Accordion.d.ts +9 -9
- package/build/esm/Accordion/Accordion.js.map +1 -1
- package/build/esm/Accordion/AccordionBody.d.ts +6 -6
- package/build/esm/Accordion/AccordionBody.js.map +1 -1
- package/build/esm/Accordion/AccordionHeader.d.ts +7 -7
- package/build/esm/Accordion/AccordionHeader.js.map +1 -1
- package/build/esm/Accordion/AccordionItem.d.ts +9 -9
- package/build/esm/Accordion/AccordionItem.js.map +1 -1
- package/build/esm/Accordion/context.d.ts +19 -19
- package/build/esm/Accordion/context.js.map +1 -1
- package/build/esm/Accordion/index.d.ts +4 -4
- package/build/esm/Accordion/index.js.map +1 -1
- package/build/esm/Accordion/scopeQuery.d.ts +2 -2
- package/build/esm/Accordion/scopeQuery.js.map +1 -1
- package/build/esm/CheckBox/CheckBox.d.ts +7 -7
- package/build/esm/CheckBox/CheckBox.js.map +1 -1
- package/build/esm/CheckBox/index.d.ts +1 -1
- package/build/esm/CheckBox/index.js.map +1 -1
- package/build/esm/ComboBox/Combobox.d.ts +18 -18
- package/build/esm/ComboBox/Combobox.js.map +1 -1
- package/build/esm/ComboBox/ComboboxButton.d.ts +9 -9
- package/build/esm/ComboBox/ComboboxButton.js.map +1 -1
- package/build/esm/ComboBox/ComboboxInput.d.ts +17 -17
- package/build/esm/ComboBox/ComboboxInput.js.map +1 -1
- package/build/esm/ComboBox/ComboboxLabel.d.ts +7 -7
- package/build/esm/ComboBox/ComboboxLabel.js.map +1 -1
- package/build/esm/ComboBox/ComboboxList.d.ts +8 -8
- package/build/esm/ComboBox/ComboboxList.js.map +1 -1
- package/build/esm/ComboBox/ComboboxOption.d.ts +11 -11
- package/build/esm/ComboBox/ComboboxOption.js.map +1 -1
- package/build/esm/ComboBox/ComboboxPopover.d.ts +9 -9
- package/build/esm/ComboBox/ComboboxPopover.js.map +1 -1
- package/build/esm/ComboBox/cities.d.ts +5 -5
- package/build/esm/ComboBox/cities.js.map +1 -1
- package/build/esm/ComboBox/context.d.ts +30 -30
- package/build/esm/ComboBox/context.js.map +1 -1
- package/build/esm/ComboBox/hooks.d.ts +37 -37
- package/build/esm/ComboBox/hooks.js.map +1 -1
- package/build/esm/ComboBox/index.d.ts +8 -8
- package/build/esm/ComboBox/index.js.map +1 -1
- package/build/esm/ComboBox/makeHash.d.ts +1 -1
- package/build/esm/ComboBox/makeHash.js.map +1 -1
- package/build/esm/ComboBox/scopeQuery.d.ts +1 -1
- package/build/esm/ComboBox/scopeQuery.js.map +1 -1
- package/build/esm/FocusLock/FocusLock.d.ts +9 -9
- package/build/esm/FocusLock/FocusLock.js.map +1 -1
- package/build/esm/FocusLock/index.d.ts +1 -1
- package/build/esm/FocusLock/index.js.map +1 -1
- package/build/esm/FocusLock/tabUtils.d.ts +3 -3
- package/build/esm/FocusLock/tabUtils.js.map +1 -1
- package/build/esm/FocusLock/useFocusLock.d.ts +7 -7
- package/build/esm/FocusLock/useFocusLock.js.map +1 -1
- package/build/esm/List/List.d.ts +7 -7
- package/build/esm/List/List.js.map +1 -1
- package/build/esm/List/ListItem.d.ts +7 -7
- package/build/esm/List/ListItem.js.map +1 -1
- package/build/esm/List/context.d.ts +4 -4
- package/build/esm/List/context.js.map +1 -1
- package/build/esm/List/index.d.ts +2 -2
- package/build/esm/List/index.js.map +1 -1
- package/build/esm/Menu/ContextMenuTrigger.d.ts +11 -11
- package/build/esm/Menu/ContextMenuTrigger.js.map +1 -1
- package/build/esm/Menu/Menu.d.ts +10 -10
- package/build/esm/Menu/Menu.js.map +1 -1
- package/build/esm/Menu/MenuButton.d.ts +11 -11
- package/build/esm/Menu/MenuButton.js.map +1 -1
- package/build/esm/Menu/MenuItem.d.ts +8 -8
- package/build/esm/Menu/MenuItem.js.map +1 -1
- package/build/esm/Menu/MenuList.d.ts +7 -7
- package/build/esm/Menu/MenuList.js.map +1 -1
- package/build/esm/Menu/MenuPopover.d.ts +8 -8
- package/build/esm/Menu/MenuPopover.js.map +1 -1
- package/build/esm/Menu/context.d.ts +25 -25
- package/build/esm/Menu/context.js.map +1 -1
- package/build/esm/Menu/fixtures/countryList.d.ts +1 -1
- package/build/esm/Menu/fixtures/countryList.js.map +1 -1
- package/build/esm/Menu/index.d.ts +6 -6
- package/build/esm/Menu/index.js.map +1 -1
- package/build/esm/Menu/scope.d.ts +1 -1
- package/build/esm/Menu/scope.js.map +1 -1
- package/build/esm/Modal/Modal.d.ts +9 -9
- package/build/esm/Modal/Modal.js.map +1 -1
- package/build/esm/Modal/ModalBackdrop.d.ts +10 -10
- package/build/esm/Modal/ModalBackdrop.js.map +1 -1
- package/build/esm/Modal/index.d.ts +2 -2
- package/build/esm/Modal/index.js.map +1 -1
- package/build/esm/Popper/Popper.d.ts +35 -35
- package/build/esm/Popper/Popper.js +1 -2
- package/build/esm/Popper/Popper.js.map +1 -1
- package/build/esm/Popper/PopperArrow.d.ts +6 -6
- package/build/esm/Popper/PopperArrow.js.map +1 -1
- package/build/esm/Popper/context.d.ts +6 -6
- package/build/esm/Popper/context.js.map +1 -1
- package/build/esm/Popper/index.d.ts +3 -3
- package/build/esm/Popper/index.js.map +1 -1
- package/build/esm/Portal/Portal.d.ts +7 -7
- package/build/esm/Portal/PortalSelectorProvider.d.ts +8 -8
- package/build/esm/Portal/index.d.ts +2 -2
- package/build/esm/RadioButton/RadioButton.d.ts +10 -10
- package/build/esm/RadioButton/RadioButton.js.map +1 -1
- package/build/esm/RadioButton/RadioGroup.d.ts +12 -12
- package/build/esm/RadioButton/RadioGroup.js.map +1 -1
- package/build/esm/RadioButton/context.d.ts +9 -9
- package/build/esm/RadioButton/context.js.map +1 -1
- package/build/esm/RadioButton/index.d.ts +2 -2
- package/build/esm/RadioButton/index.js.map +1 -1
- package/build/esm/SkipNav/SkipNav.d.ts +7 -7
- package/build/esm/SkipNav/SkipNav.js.map +1 -1
- package/build/esm/SkipNav/index.d.ts +1 -1
- package/build/esm/SkipNav/index.js.map +1 -1
- package/build/esm/Slider/Slider.d.ts +197 -197
- package/build/esm/Slider/Slider.js +82 -82
- package/build/esm/Slider/Slider.js.map +1 -1
- package/build/esm/Slider/index.d.ts +1 -1
- package/build/esm/Slider/index.js.map +1 -1
- package/build/esm/Spinner/Spinner.d.ts +12 -12
- package/build/esm/Spinner/Spinner.js.map +1 -1
- package/build/esm/Spinner/SpinnerButton.d.ts +8 -8
- package/build/esm/Spinner/SpinnerButton.js.map +1 -1
- package/build/esm/Spinner/context.d.ts +12 -12
- package/build/esm/Spinner/context.js.map +1 -1
- package/build/esm/Spinner/index.d.ts +2 -2
- package/build/esm/Spinner/index.js.map +1 -1
- package/build/esm/Tabs/Tab.d.ts +7 -7
- package/build/esm/Tabs/Tab.js.map +1 -1
- package/build/esm/Tabs/TabList.d.ts +9 -9
- package/build/esm/Tabs/TabList.js.map +1 -1
- package/build/esm/Tabs/TabPanel.d.ts +8 -8
- package/build/esm/Tabs/TabPanel.js.map +1 -1
- package/build/esm/Tabs/TabPanels.d.ts +8 -8
- package/build/esm/Tabs/TabPanels.js.map +1 -1
- package/build/esm/Tabs/Tabs.d.ts +10 -10
- package/build/esm/Tabs/Tabs.js.map +1 -1
- package/build/esm/Tabs/context.d.ts +17 -17
- package/build/esm/Tabs/context.js.map +1 -1
- package/build/esm/Tabs/index.d.ts +5 -5
- package/build/esm/Tabs/index.js.map +1 -1
- package/build/esm/Tabs/scopeQuery.d.ts +1 -1
- package/build/esm/Tabs/scopeQuery.js.map +1 -1
- package/build/esm/Tooltip/Tooltip.d.ts +10 -10
- package/build/esm/Tooltip/Tooltip.js.map +1 -1
- package/build/esm/Tooltip/index.d.ts +1 -1
- package/build/esm/Tooltip/index.js.map +1 -1
- package/build/esm/Tooltip/stateMachine.d.ts +28 -28
- package/build/esm/Tooltip/stateMachine.js.map +1 -1
- package/build/esm/Tooltip/useTooltip.d.ts +10 -10
- package/build/esm/Tooltip/useTooltip.js.map +1 -1
- package/build/esm/hooks/index.d.ts +13 -13
- package/build/esm/hooks/index.js.map +1 -1
- package/build/esm/hooks/useAutoFocus.d.ts +2 -2
- package/build/esm/hooks/useAutoFocus.js.map +1 -1
- package/build/esm/hooks/useChildrenCounter.d.ts +7 -7
- package/build/esm/hooks/useChildrenCounter.js.map +1 -1
- package/build/esm/hooks/useControlledState.d.ts +3 -3
- package/build/esm/hooks/useFocusReturn.d.ts +2 -2
- package/build/esm/hooks/useFocusReturn.js.map +1 -1
- package/build/esm/hooks/useFocusState.d.ts +11 -11
- package/build/esm/hooks/useFocusState.js.map +1 -1
- package/build/esm/hooks/useGestureHandlers.d.ts +52 -52
- package/build/esm/hooks/useGestureHandlers.js.map +1 -1
- package/build/esm/hooks/useMeasure.d.ts +7 -7
- package/build/esm/hooks/useMeasure.js.map +1 -1
- package/build/esm/hooks/useOnClickOutside.d.ts +2 -2
- package/build/esm/hooks/useOnClickOutside.js.map +1 -1
- package/build/esm/hooks/useOnKeyDown.d.ts +1 -1
- package/build/esm/hooks/useOnKeyDown.js.map +1 -1
- package/build/esm/hooks/useReducerMachine.d.ts +24 -24
- package/build/esm/hooks/useReducerMachine.js.map +1 -1
- package/build/esm/hooks/useRemoveBodyScroll.d.ts +2 -2
- package/build/esm/hooks/useRemoveBodyScroll.js.map +1 -1
- package/build/esm/hooks/useScope.d.ts +11 -11
- package/build/esm/hooks/useScope.js.map +1 -1
- package/build/esm/hooks/useThrottle.d.ts +1 -1
- package/build/esm/hooks/useThrottle.js.map +1 -1
- package/build/esm/index.d.ts +15 -15
- package/build/esm/index.js.map +1 -1
- package/build/esm/utils/assign-ref.d.ts +3 -3
- package/build/esm/utils/assign-ref.js.map +1 -1
- package/build/esm/utils/can-use-dom.d.ts +1 -1
- package/build/esm/utils/can-use-dom.js.map +1 -1
- package/build/esm/utils/clamp.d.ts +1 -1
- package/build/esm/utils/clamp.js.map +1 -1
- package/build/esm/utils/context.d.ts +7 -7
- package/build/esm/utils/context.js.map +1 -1
- package/build/esm/utils/create-subscription.d.ts +4 -4
- package/build/esm/utils/create-subscription.js.map +1 -1
- package/build/esm/utils/get-circular-index.d.ts +1 -1
- package/build/esm/utils/get-circular-index.js.map +1 -1
- package/build/esm/utils/index.d.ts +10 -10
- package/build/esm/utils/index.js.map +1 -1
- package/build/esm/utils/is-right-click.d.ts +6 -6
- package/build/esm/utils/is-right-click.js +4 -4
- package/build/esm/utils/is-right-click.js.map +1 -1
- package/build/esm/utils/owner-document.d.ts +7 -7
- package/build/esm/utils/owner-document.js +5 -5
- package/build/esm/utils/owner-document.js.map +1 -1
- package/build/esm/utils/polymorphic.d.ts +39 -39
- package/build/esm/utils/polymorphic.js.map +1 -1
- package/build/esm/utils/rubber-band-clamp.d.ts +2 -2
- package/build/esm/utils/rubber-band-clamp.js.map +1 -1
- package/build/esm/utils/use-stable-callback.d.ts +16 -16
- package/build/esm/utils/use-stable-callback.js +16 -16
- package/build/esm/utils/use-stable-callback.js.map +1 -1
- package/build/esm/utils/wrap-event.d.ts +3 -3
- package/build/esm/utils/wrap-event.js.map +1 -1
- package/build/tsconfig-build.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/Accordion/Accordion.story.tsx +74 -74
- package/src/Accordion/Accordion.tsx +59 -59
- package/src/Accordion/AccordionBody.tsx +52 -52
- package/src/Accordion/AccordionHeader.tsx +167 -167
- package/src/Accordion/AccordionItem.tsx +50 -50
- package/src/Accordion/context.ts +37 -37
- package/src/Accordion/index.ts +4 -4
- package/src/Accordion/scopeQuery.ts +7 -7
- package/src/Accordion/styles.css +21 -21
- package/src/CheckBox/CheckBox.tsx +41 -41
- package/src/CheckBox/index.ts +1 -1
- package/src/ComboBox/ComboBox.story.tsx +120 -120
- package/src/ComboBox/Combobox.tsx +148 -148
- package/src/ComboBox/ComboboxButton.tsx +61 -61
- package/src/ComboBox/ComboboxInput.tsx +187 -187
- package/src/ComboBox/ComboboxLabel.tsx +33 -33
- package/src/ComboBox/ComboboxList.tsx +47 -47
- package/src/ComboBox/ComboboxOption.tsx +111 -111
- package/src/ComboBox/ComboboxPopover.tsx +64 -64
- package/src/ComboBox/cities.ts +23194 -23194
- package/src/ComboBox/context.ts +35 -35
- package/src/ComboBox/hooks.tsx +451 -451
- package/src/ComboBox/index.ts +8 -8
- package/src/ComboBox/makeHash.ts +19 -19
- package/src/ComboBox/scopeQuery.ts +6 -6
- package/src/ComboBox/styles.css +32 -32
- package/src/FocusLock/FocusLock.tsx +66 -66
- package/src/FocusLock/index.ts +1 -1
- package/src/FocusLock/tabUtils.ts +40 -40
- package/src/FocusLock/useFocusLock.ts +56 -56
- package/src/List/List.story.tsx +18 -18
- package/src/List/List.tsx +17 -17
- package/src/List/ListItem.tsx +23 -23
- package/src/List/context.ts +19 -19
- package/src/List/index.ts +2 -2
- package/src/Menu/ContextMenu.story.tsx +73 -73
- package/src/Menu/ContextMenuTrigger.tsx +76 -76
- package/src/Menu/Menu.story.tsx +160 -160
- package/src/Menu/Menu.tsx +83 -83
- package/src/Menu/MenuButton.tsx +83 -83
- package/src/Menu/MenuComplex.story.tsx +58 -58
- package/src/Menu/MenuItem.tsx +88 -88
- package/src/Menu/MenuList.tsx +254 -254
- package/src/Menu/MenuPopover.tsx +35 -35
- package/src/Menu/context.ts +44 -44
- package/src/Menu/fixtures/countryList.ts +198 -198
- package/src/Menu/index.ts +6 -6
- package/src/Menu/scope.ts +7 -7
- package/src/Menu/styles.css +42 -42
- package/src/Modal/Modal.story.tsx +258 -258
- package/src/Modal/Modal.tsx +48 -48
- package/src/Modal/ModalBackdrop.tsx +78 -78
- package/src/Modal/NavDrawer.story.tsx +158 -158
- package/src/Modal/index.ts +2 -2
- package/src/Modal/styles.css +46 -46
- package/src/Popper/Popper.story.tsx +263 -263
- package/src/Popper/Popper.tsx +1 -1
- package/src/Popper/PopperArrow.tsx +35 -35
- package/src/Popper/context.ts +10 -10
- package/src/Popper/index.ts +3 -3
- package/src/Popper/styles.css +60 -60
- package/src/RadioButton/RadioButton.story.tsx +77 -77
- package/src/RadioButton/RadioButton.tsx +55 -55
- package/src/RadioButton/RadioGroup.tsx +60 -60
- package/src/RadioButton/context.ts +17 -17
- package/src/RadioButton/index.ts +2 -2
- package/src/SkipNav/SkipNav.tsx +16 -16
- package/src/SkipNav/index.tsx +1 -1
- package/src/Slider/Slider.story.tsx +45 -45
- package/src/Slider/Slider.tsx +1120 -1120
- package/src/Slider/index.ts +1 -1
- package/src/Slider/styles.css +131 -131
- package/src/Spinner/Spinner.story.tsx +31 -31
- package/src/Spinner/Spinner.tsx +117 -117
- package/src/Spinner/SpinnerButton.tsx +54 -54
- package/src/Spinner/context.ts +20 -20
- package/src/Spinner/index.ts +2 -2
- package/src/Spinner/styles.css +23 -23
- package/src/Tabs/Tab.story.tsx +80 -80
- package/src/Tabs/Tab.tsx +136 -136
- package/src/Tabs/TabList.tsx +71 -71
- package/src/Tabs/TabPanel.tsx +53 -53
- package/src/Tabs/TabPanels.tsx +30 -30
- package/src/Tabs/Tabs.tsx +46 -46
- package/src/Tabs/context.ts +30 -30
- package/src/Tabs/index.tsx +5 -5
- package/src/Tabs/scopeQuery.ts +6 -6
- package/src/Tooltip/Tooltip.story.tsx +61 -61
- package/src/Tooltip/Tooltip.tsx +50 -50
- package/src/Tooltip/index.ts +1 -1
- package/src/Tooltip/stateMachine.ts +192 -192
- package/src/Tooltip/styles.css +17 -17
- package/src/Tooltip/useTooltip.ts +136 -136
- package/src/hooks/index.ts +13 -13
- package/src/hooks/useAutoFocus.ts +22 -22
- package/src/hooks/useChildrenCounter.ts +51 -51
- package/src/hooks/useFocusReturn.ts +43 -43
- package/src/hooks/useFocusState.ts +30 -30
- package/src/hooks/useGestureHandlers.ts +286 -286
- package/src/hooks/useMeasure.ts +33 -33
- package/src/hooks/useOnClickOutside.ts +32 -32
- package/src/hooks/useOnKeyDown.ts +19 -19
- package/src/hooks/useReducerMachine.ts +60 -60
- package/src/hooks/useRemoveBodyScroll.ts +39 -39
- package/src/hooks/useScope.ts +52 -52
- package/src/hooks/useThrottle.ts +19 -19
- package/src/index.ts +20 -20
- package/src/utils/assign-ref.ts +27 -27
- package/src/utils/can-use-dom.ts +7 -7
- package/src/utils/clamp.ts +3 -3
- package/src/utils/context.tsx +48 -48
- package/src/utils/create-subscription.ts +16 -16
- package/src/utils/get-circular-index.ts +7 -7
- package/src/utils/index.ts +10 -10
- package/src/utils/is-right-click.ts +14 -14
- package/src/utils/owner-document.ts +13 -13
- package/src/utils/polymorphic.ts +78 -78
- package/src/utils/rubber-band-clamp.ts +25 -25
- package/src/utils/use-stable-callback.ts +58 -58
- package/src/utils/wrap-event.ts +22 -22
- package/build/esm/Carousel/Carousel.d.ts +0 -9
- package/build/esm/Carousel/Carousel.js +0 -38
- package/build/esm/Carousel/Carousel.js.map +0 -1
- package/build/esm/Carousel/Fader.d.ts +0 -14
- package/build/esm/Carousel/Fader.js +0 -76
- package/build/esm/Carousel/Fader.js.map +0 -1
- package/build/esm/Carousel/FaderItem.d.ts +0 -5
- package/build/esm/Carousel/FaderItem.js +0 -16
- package/build/esm/Carousel/FaderItem.js.map +0 -1
- package/build/esm/Carousel/Preloader.d.ts +0 -7
- package/build/esm/Carousel/Preloader.js +0 -70
- package/build/esm/Carousel/Preloader.js.map +0 -1
- package/build/esm/Carousel/Slider.d.ts +0 -14
- package/build/esm/Carousel/Slider.js +0 -212
- package/build/esm/Carousel/Slider.js.map +0 -1
- package/build/esm/Carousel/SliderItem.d.ts +0 -12
- package/build/esm/Carousel/SliderItem.js +0 -41
- package/build/esm/Carousel/SliderItem.js.map +0 -1
- package/build/esm/Carousel/context.d.ts +0 -10
- package/build/esm/Carousel/context.js +0 -8
- package/build/esm/Carousel/context.js.map +0 -1
- package/build/esm/Carousel/getSliderParams.d.ts +0 -9
- package/build/esm/Carousel/getSliderParams.js +0 -85
- package/build/esm/Carousel/getSliderParams.js.map +0 -1
- package/build/esm/Carousel/index.d.ts +0 -7
- package/build/esm/Carousel/index.js +0 -8
- package/build/esm/Carousel/index.js.map +0 -1
- package/build/esm/Carousel/useCarouselGestures.d.ts +0 -30
- package/build/esm/Carousel/useCarouselGestures.js +0 -33
- package/build/esm/Carousel/useCarouselGestures.js.map +0 -1
- package/build/esm/DatePicker/DatePicker.d.ts +0 -24
- package/build/esm/DatePicker/DatePicker.js +0 -101
- package/build/esm/DatePicker/DatePicker.js.map +0 -1
- package/build/esm/DatePicker/DatePickerSelect.d.ts +0 -8
- package/build/esm/DatePicker/DatePickerSelect.js +0 -201
- package/build/esm/DatePicker/DatePickerSelect.js.map +0 -1
- package/build/esm/DatePicker/RangeDatePicker.d.ts +0 -28
- package/build/esm/DatePicker/RangeDatePicker.js +0 -94
- package/build/esm/DatePicker/RangeDatePicker.js.map +0 -1
- package/build/esm/DatePicker/adjustDates.d.ts +0 -4
- package/build/esm/DatePicker/adjustDates.js +0 -18
- package/build/esm/DatePicker/adjustDates.js.map +0 -1
- package/build/esm/DatePicker/contexts.d.ts +0 -31
- package/build/esm/DatePicker/contexts.js +0 -15
- package/build/esm/DatePicker/contexts.js.map +0 -1
- package/build/esm/DatePicker/dateTypes.d.ts +0 -2
- package/build/esm/DatePicker/dateTypes.js +0 -2
- package/build/esm/DatePicker/dateTypes.js.map +0 -1
- package/build/esm/DatePicker/hooks.d.ts +0 -36
- package/build/esm/DatePicker/hooks.js +0 -98
- package/build/esm/DatePicker/hooks.js.map +0 -1
- package/build/esm/DatePicker/index.d.ts +0 -5
- package/build/esm/DatePicker/index.js +0 -6
- package/build/esm/DatePicker/index.js.map +0 -1
- package/build/esm/hooks/useId.d.ts +0 -1
- package/build/esm/hooks/useId.js +0 -25
- package/build/esm/hooks/useId.js.map +0 -1
- package/build/esm/utils/assignRef.d.ts +0 -3
- package/build/esm/utils/assignRef.js +0 -25
- package/build/esm/utils/assignRef.js.map +0 -1
- package/build/esm/utils/getCircularIndex.d.ts +0 -1
- package/build/esm/utils/getCircularIndex.js +0 -8
- package/build/esm/utils/getCircularIndex.js.map +0 -1
- package/build/esm/utils/rubberBandClamp.d.ts +0 -2
- package/build/esm/utils/rubberBandClamp.js +0 -20
- package/build/esm/utils/rubberBandClamp.js.map +0 -1
- package/build/esm/utils/wrapEvent.d.ts +0 -3
- package/build/esm/utils/wrapEvent.js +0 -16
- package/build/esm/utils/wrapEvent.js.map +0 -1
- package/build/tsconfig.tsbuildinfo +0 -7270
package/src/ComboBox/hooks.tsx
CHANGED
|
@@ -1,451 +1,451 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-use-before-define */
|
|
2
|
-
/* eslint-disable default-case */
|
|
3
|
-
import type { KeyboardEvent, MutableRefObject } from 'react';
|
|
4
|
-
import { useEffect } from 'react';
|
|
5
|
-
|
|
6
|
-
import type {
|
|
7
|
-
StateChart as GenericStateChart,
|
|
8
|
-
StateMachineState,
|
|
9
|
-
} from '../hooks/useReducerMachine';
|
|
10
|
-
import { getCircularIndex } from '../utils/get-circular-index';
|
|
11
|
-
import { useComboBoxContext } from './context';
|
|
12
|
-
import { scopeQuery } from './scopeQuery';
|
|
13
|
-
|
|
14
|
-
////////////////////////////////////////////////////////////////////////////////
|
|
15
|
-
// States
|
|
16
|
-
|
|
17
|
-
// Nothing going on, waiting for the user to type or use the arrow keys
|
|
18
|
-
export const IDLE = 'IDLE';
|
|
19
|
-
|
|
20
|
-
// The component is suggesting options as the user types
|
|
21
|
-
const SUGGESTING = 'SUGGESTING';
|
|
22
|
-
|
|
23
|
-
// The user is using the keyboard to navigate the list, not typing
|
|
24
|
-
export const NAVIGATING = 'NAVIGATING';
|
|
25
|
-
|
|
26
|
-
export type StateTypes = typeof IDLE | typeof SUGGESTING | typeof NAVIGATING;
|
|
27
|
-
|
|
28
|
-
////////////////////////////////////////////////////////////////////////////////
|
|
29
|
-
// Actions:
|
|
30
|
-
|
|
31
|
-
// Used to sync the state with controlled state, right after mounting
|
|
32
|
-
export const INIT = 'INIT';
|
|
33
|
-
|
|
34
|
-
// User cleared the value w/ backspace, but input still has focus
|
|
35
|
-
export const CLEAR = 'CLEAR';
|
|
36
|
-
|
|
37
|
-
// User cleared the value w/ backspace, but input still has focus
|
|
38
|
-
export const CLEAR_SELECTION = 'CLEAR_SELECTION';
|
|
39
|
-
|
|
40
|
-
// User is typing
|
|
41
|
-
export const CHANGE = 'CHANGE';
|
|
42
|
-
|
|
43
|
-
// User is navigating w/ the keyboard
|
|
44
|
-
export const NAVIGATE = 'NAVIGATE';
|
|
45
|
-
|
|
46
|
-
// User can be navigating with keyboard and then click instead, we want the
|
|
47
|
-
// value from the click, not the current nav item
|
|
48
|
-
const SELECT_WITH_KEYBOARD = 'SELECT_WITH_KEYBOARD';
|
|
49
|
-
export const SELECT_WITH_CLICK = 'SELECT_WITH_CLICK';
|
|
50
|
-
|
|
51
|
-
// Pretty self-explanatory, user can hit escape or blur to close the popover
|
|
52
|
-
const ESCAPE = 'ESCAPE';
|
|
53
|
-
const BLUR = 'BLUR';
|
|
54
|
-
|
|
55
|
-
export const FOCUS = 'FOCUS';
|
|
56
|
-
|
|
57
|
-
export const OPEN_WITH_BUTTON = 'OPEN_WITH_BUTTON';
|
|
58
|
-
|
|
59
|
-
export const CLOSE_WITH_BUTTON = 'CLOSE_WITH_BUTTON';
|
|
60
|
-
|
|
61
|
-
export type ActionTypes =
|
|
62
|
-
| typeof CLEAR
|
|
63
|
-
| typeof CLEAR_SELECTION
|
|
64
|
-
| typeof CHANGE
|
|
65
|
-
| typeof INIT
|
|
66
|
-
| typeof NAVIGATE
|
|
67
|
-
| typeof SELECT_WITH_KEYBOARD
|
|
68
|
-
| typeof SELECT_WITH_CLICK
|
|
69
|
-
| typeof ESCAPE
|
|
70
|
-
| typeof BLUR
|
|
71
|
-
| typeof FOCUS
|
|
72
|
-
| typeof OPEN_WITH_BUTTON
|
|
73
|
-
| typeof CLOSE_WITH_BUTTON;
|
|
74
|
-
|
|
75
|
-
////////////////////////////////////////////////////////////////////////////////
|
|
76
|
-
export const stateChart: GenericStateChart<StateTypes, ActionTypes> = {
|
|
77
|
-
initial: IDLE,
|
|
78
|
-
states: {
|
|
79
|
-
[IDLE]: {
|
|
80
|
-
on: {
|
|
81
|
-
[BLUR]: IDLE,
|
|
82
|
-
[CLEAR]: IDLE,
|
|
83
|
-
[INIT]: IDLE,
|
|
84
|
-
[CLEAR_SELECTION]: IDLE,
|
|
85
|
-
[CHANGE]: SUGGESTING,
|
|
86
|
-
[FOCUS]: SUGGESTING,
|
|
87
|
-
[NAVIGATE]: NAVIGATING,
|
|
88
|
-
[OPEN_WITH_BUTTON]: SUGGESTING,
|
|
89
|
-
},
|
|
90
|
-
},
|
|
91
|
-
[SUGGESTING]: {
|
|
92
|
-
on: {
|
|
93
|
-
[CHANGE]: SUGGESTING,
|
|
94
|
-
[FOCUS]: SUGGESTING,
|
|
95
|
-
[INIT]: SUGGESTING,
|
|
96
|
-
[NAVIGATE]: NAVIGATING,
|
|
97
|
-
[CLEAR]: IDLE,
|
|
98
|
-
[CLEAR_SELECTION]: SUGGESTING,
|
|
99
|
-
[ESCAPE]: IDLE,
|
|
100
|
-
[BLUR]: IDLE,
|
|
101
|
-
[SELECT_WITH_CLICK]: IDLE,
|
|
102
|
-
[CLOSE_WITH_BUTTON]: IDLE,
|
|
103
|
-
},
|
|
104
|
-
},
|
|
105
|
-
[NAVIGATING]: {
|
|
106
|
-
on: {
|
|
107
|
-
[CHANGE]: SUGGESTING,
|
|
108
|
-
[FOCUS]: SUGGESTING,
|
|
109
|
-
[INIT]: NAVIGATING,
|
|
110
|
-
[CLEAR]: IDLE,
|
|
111
|
-
[CLEAR_SELECTION]: NAVIGATING,
|
|
112
|
-
[BLUR]: IDLE,
|
|
113
|
-
[ESCAPE]: IDLE,
|
|
114
|
-
[NAVIGATE]: NAVIGATING,
|
|
115
|
-
[SELECT_WITH_KEYBOARD]: IDLE,
|
|
116
|
-
[SELECT_WITH_CLICK]: IDLE,
|
|
117
|
-
[CLOSE_WITH_BUTTON]: IDLE,
|
|
118
|
-
},
|
|
119
|
-
},
|
|
120
|
-
},
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
export interface ReducerState
|
|
124
|
-
extends StateMachineState<StateTypes, ActionTypes> {
|
|
125
|
-
item: string;
|
|
126
|
-
navigationItem: string;
|
|
127
|
-
text: string;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
interface ActionObject {
|
|
131
|
-
type: ActionTypes;
|
|
132
|
-
state: StateTypes;
|
|
133
|
-
nextState: StateTypes;
|
|
134
|
-
[rest: string]: any;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
export function comboboxReducer(
|
|
138
|
-
data: Readonly<ReducerState>,
|
|
139
|
-
action: ActionObject
|
|
140
|
-
): ReducerState {
|
|
141
|
-
const nextState = {
|
|
142
|
-
...data,
|
|
143
|
-
state: action.nextState,
|
|
144
|
-
lastActionType: action.type,
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
switch (action.type) {
|
|
148
|
-
case INIT:
|
|
149
|
-
case CHANGE:
|
|
150
|
-
return {
|
|
151
|
-
...nextState,
|
|
152
|
-
text: action.text,
|
|
153
|
-
navigationItem: '',
|
|
154
|
-
item: '',
|
|
155
|
-
};
|
|
156
|
-
case NAVIGATE:
|
|
157
|
-
case OPEN_WITH_BUTTON:
|
|
158
|
-
if (action.persistSelection) {
|
|
159
|
-
return {
|
|
160
|
-
...nextState,
|
|
161
|
-
navigationItem: data.item,
|
|
162
|
-
};
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
return {
|
|
166
|
-
...nextState,
|
|
167
|
-
navigationItem: action.item,
|
|
168
|
-
};
|
|
169
|
-
case CLEAR_SELECTION:
|
|
170
|
-
return {
|
|
171
|
-
...nextState,
|
|
172
|
-
navigationItem: '',
|
|
173
|
-
};
|
|
174
|
-
case CLEAR:
|
|
175
|
-
return {
|
|
176
|
-
...nextState,
|
|
177
|
-
text: '',
|
|
178
|
-
navigationItem: '',
|
|
179
|
-
item: '',
|
|
180
|
-
};
|
|
181
|
-
case BLUR:
|
|
182
|
-
return {
|
|
183
|
-
...nextState,
|
|
184
|
-
text: action.text,
|
|
185
|
-
navigationItem: '',
|
|
186
|
-
item: action.item,
|
|
187
|
-
};
|
|
188
|
-
case CLOSE_WITH_BUTTON:
|
|
189
|
-
case ESCAPE:
|
|
190
|
-
return {
|
|
191
|
-
...nextState,
|
|
192
|
-
navigationItem: '',
|
|
193
|
-
item: '',
|
|
194
|
-
};
|
|
195
|
-
case SELECT_WITH_CLICK:
|
|
196
|
-
case SELECT_WITH_KEYBOARD:
|
|
197
|
-
return {
|
|
198
|
-
...nextState,
|
|
199
|
-
text: action.text,
|
|
200
|
-
item: action.item,
|
|
201
|
-
navigationItem: '',
|
|
202
|
-
};
|
|
203
|
-
case FOCUS:
|
|
204
|
-
return {
|
|
205
|
-
...nextState,
|
|
206
|
-
navigationItem: action.item,
|
|
207
|
-
};
|
|
208
|
-
|
|
209
|
-
default:
|
|
210
|
-
throw new Error(`Unknown action ${action.type}`);
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
const visibleStates = [SUGGESTING, NAVIGATING];
|
|
215
|
-
export const isVisible = (state: any) => visibleStates.indexOf(state) >= 0;
|
|
216
|
-
|
|
217
|
-
////////////////////////////////////////////////////////////////////////////////
|
|
218
|
-
// The rest is all implementation details
|
|
219
|
-
|
|
220
|
-
// Move focus back to the input if we start navigating w/ the
|
|
221
|
-
// keyboard after focus has moved to any focusable content in
|
|
222
|
-
// the popup.
|
|
223
|
-
export function useFocusManagement(
|
|
224
|
-
lastActionType: ActionTypes,
|
|
225
|
-
inputRef: MutableRefObject<HTMLInputElement | null>
|
|
226
|
-
) {
|
|
227
|
-
// useEffect so that the cursor goes to the end of the input instead
|
|
228
|
-
// of awkwardly at the beginning, unclear to me why ...
|
|
229
|
-
useEffect(() => {
|
|
230
|
-
if (
|
|
231
|
-
lastActionType === NAVIGATE ||
|
|
232
|
-
lastActionType === ESCAPE ||
|
|
233
|
-
lastActionType === SELECT_WITH_CLICK ||
|
|
234
|
-
lastActionType === OPEN_WITH_BUTTON
|
|
235
|
-
) {
|
|
236
|
-
inputRef.current && inputRef.current.focus();
|
|
237
|
-
}
|
|
238
|
-
});
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
function getNextItem(
|
|
242
|
-
currentItem: string,
|
|
243
|
-
key: 'ArrowDown' | 'ArrowUp' | 'PageDown' | 'PageUp' | 'Home' | 'End',
|
|
244
|
-
optionsItems: HTMLElement[],
|
|
245
|
-
autocomplete: boolean
|
|
246
|
-
): HTMLElement | null {
|
|
247
|
-
const jumpToStartOrEnd = key === 'Home' || key === 'End';
|
|
248
|
-
let incr = 1;
|
|
249
|
-
switch (key) {
|
|
250
|
-
case 'PageUp':
|
|
251
|
-
incr = -10;
|
|
252
|
-
break;
|
|
253
|
-
case 'PageDown':
|
|
254
|
-
incr = 10;
|
|
255
|
-
break;
|
|
256
|
-
case 'End':
|
|
257
|
-
case 'ArrowUp':
|
|
258
|
-
incr = -1;
|
|
259
|
-
break;
|
|
260
|
-
case 'Home':
|
|
261
|
-
case 'ArrowDown':
|
|
262
|
-
incr = 1;
|
|
263
|
-
break;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
const index =
|
|
267
|
-
currentItem === ''
|
|
268
|
-
? -1
|
|
269
|
-
: optionsItems.findIndex((n) => String(n.id) === currentItem);
|
|
270
|
-
|
|
271
|
-
const optionsLen = optionsItems.length;
|
|
272
|
-
|
|
273
|
-
// Nothing selected, either go to start, or end
|
|
274
|
-
if (index < 0 || jumpToStartOrEnd) {
|
|
275
|
-
if (incr > 0) {
|
|
276
|
-
// Go to start
|
|
277
|
-
return optionsItems[0];
|
|
278
|
-
} else {
|
|
279
|
-
// Go to end
|
|
280
|
-
return optionsItems[optionsLen - 1];
|
|
281
|
-
}
|
|
282
|
-
} else if (autocomplete) {
|
|
283
|
-
const nextIndex = index + incr;
|
|
284
|
-
|
|
285
|
-
if (nextIndex < 0 || nextIndex >= optionsLen) {
|
|
286
|
-
// Next is outside the bounds of list, return nothing selected
|
|
287
|
-
return null;
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
// I'm sure it won't be null, we already check optionsLen above
|
|
292
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
293
|
-
return optionsItems[getCircularIndex(index + incr, optionsLen)!];
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
// We want the same events when the input or the popup have focus (HOW COOL ARE
|
|
297
|
-
// HOOKS BTW?) This is probably the hairiest piece but it's not bad.
|
|
298
|
-
export function useKeyDown() {
|
|
299
|
-
const {
|
|
300
|
-
data: { text, navigationItem },
|
|
301
|
-
onSelect,
|
|
302
|
-
optionsRef,
|
|
303
|
-
inputRef,
|
|
304
|
-
state,
|
|
305
|
-
transition,
|
|
306
|
-
autocompletePropRef,
|
|
307
|
-
clearOnEscapeRef,
|
|
308
|
-
persistSelectionRef,
|
|
309
|
-
listScope,
|
|
310
|
-
} = useComboBoxContext();
|
|
311
|
-
|
|
312
|
-
return function handleKeyDown(event: KeyboardEvent<any>) {
|
|
313
|
-
const optionNodes = listScope.current.queryAllNodes(scopeQuery);
|
|
314
|
-
|
|
315
|
-
switch (event.key) {
|
|
316
|
-
case 'Home':
|
|
317
|
-
case 'End':
|
|
318
|
-
case 'PageUp':
|
|
319
|
-
case 'PageDown':
|
|
320
|
-
case 'ArrowUp':
|
|
321
|
-
case 'ArrowDown': {
|
|
322
|
-
// Don't scroll the page
|
|
323
|
-
event.preventDefault();
|
|
324
|
-
|
|
325
|
-
const optionsLen = optionNodes.length;
|
|
326
|
-
|
|
327
|
-
// If the developer didn't render any options, there's no point in
|
|
328
|
-
// trying to navigate--but seriously what the heck? Give us some
|
|
329
|
-
// options fam.
|
|
330
|
-
if (optionsLen === 0) {
|
|
331
|
-
return;
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
if (state === IDLE) {
|
|
335
|
-
// Opening a closed list
|
|
336
|
-
transition(NAVIGATE, {
|
|
337
|
-
persistSelection: persistSelectionRef.current,
|
|
338
|
-
});
|
|
339
|
-
} else {
|
|
340
|
-
// When autocompletting, we'll not cycle through the list directly
|
|
341
|
-
const autocomplete = autocompletePropRef.current;
|
|
342
|
-
|
|
343
|
-
// Get next selected item
|
|
344
|
-
const nextItem = getNextItem(
|
|
345
|
-
navigationItem,
|
|
346
|
-
event.key,
|
|
347
|
-
optionNodes,
|
|
348
|
-
autocomplete
|
|
349
|
-
);
|
|
350
|
-
|
|
351
|
-
if (nextItem !== null) {
|
|
352
|
-
nextItem.scrollIntoView({ behavior: 'auto', block: 'nearest' });
|
|
353
|
-
transition(NAVIGATE, {
|
|
354
|
-
value: optionsRef.current[nextItem.id].text,
|
|
355
|
-
item: nextItem.id,
|
|
356
|
-
});
|
|
357
|
-
} else {
|
|
358
|
-
transition(NAVIGATE, { value: null, item: '' });
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
break;
|
|
362
|
-
}
|
|
363
|
-
case 'Escape': {
|
|
364
|
-
if (state !== IDLE) {
|
|
365
|
-
transition(ESCAPE);
|
|
366
|
-
} else if (state === IDLE && text !== '') {
|
|
367
|
-
if (!inputRef.current || !clearOnEscapeRef.current) {
|
|
368
|
-
break;
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
// emulate a inputRef change event, might not work in future versions of React
|
|
372
|
-
const lastValue = inputRef.current.value;
|
|
373
|
-
inputRef.current.value = '';
|
|
374
|
-
|
|
375
|
-
const tracker = (inputRef.current as any)._valueTracker;
|
|
376
|
-
if (tracker) {
|
|
377
|
-
tracker.setValue(lastValue);
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
const event = new Event('change', { bubbles: true });
|
|
381
|
-
inputRef.current.dispatchEvent(event);
|
|
382
|
-
}
|
|
383
|
-
break;
|
|
384
|
-
}
|
|
385
|
-
case 'Enter': {
|
|
386
|
-
if (state === NAVIGATING && navigationItem !== '') {
|
|
387
|
-
const { value: navigationValue, text: navigationText } =
|
|
388
|
-
optionsRef.current[navigationItem];
|
|
389
|
-
|
|
390
|
-
// don't want to submit forms
|
|
391
|
-
event.preventDefault();
|
|
392
|
-
onSelect && onSelect(navigationText, navigationItem, navigationValue);
|
|
393
|
-
transition(SELECT_WITH_KEYBOARD, {
|
|
394
|
-
text: navigationText,
|
|
395
|
-
item: navigationItem,
|
|
396
|
-
});
|
|
397
|
-
}
|
|
398
|
-
break;
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
};
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
export function useBlur() {
|
|
405
|
-
const {
|
|
406
|
-
data: { navigationItem, text: stateText },
|
|
407
|
-
transition,
|
|
408
|
-
optionsRef,
|
|
409
|
-
popoverRef,
|
|
410
|
-
inputRef,
|
|
411
|
-
buttonRef,
|
|
412
|
-
onSelect,
|
|
413
|
-
selectOnBlur, // not implemented yet
|
|
414
|
-
} = useComboBoxContext();
|
|
415
|
-
|
|
416
|
-
return function handleBlur() {
|
|
417
|
-
requestAnimationFrame(() => {
|
|
418
|
-
// we on want to close only if focus rests outside the combobox
|
|
419
|
-
if (
|
|
420
|
-
document.activeElement !== inputRef.current &&
|
|
421
|
-
document.activeElement !== buttonRef.current &&
|
|
422
|
-
popoverRef.current
|
|
423
|
-
) {
|
|
424
|
-
if (popoverRef.current.contains(document.activeElement)) {
|
|
425
|
-
// focus landed inside the combobox, keep it open
|
|
426
|
-
// in the future, we can make it not close, select, or anything
|
|
427
|
-
// this way we can have like... checkboxes available in the
|
|
428
|
-
// menu item, etc.
|
|
429
|
-
} else {
|
|
430
|
-
// focus landed outside the combobox, close it.
|
|
431
|
-
if (!selectOnBlur || navigationItem === '') {
|
|
432
|
-
// we don't wanna select on blur, or navigationIndex is invalid
|
|
433
|
-
transition(BLUR, { text: stateText, item: '' });
|
|
434
|
-
} else {
|
|
435
|
-
// select the currently selected item
|
|
436
|
-
const { value: navigationValue, text: navigationText } =
|
|
437
|
-
optionsRef.current[navigationItem];
|
|
438
|
-
|
|
439
|
-
onSelect &&
|
|
440
|
-
onSelect(navigationText, navigationItem, navigationValue);
|
|
441
|
-
|
|
442
|
-
transition(BLUR, {
|
|
443
|
-
text: navigationText,
|
|
444
|
-
item: navigationItem,
|
|
445
|
-
});
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
});
|
|
450
|
-
};
|
|
451
|
-
}
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-use-before-define */
|
|
2
|
+
/* eslint-disable default-case */
|
|
3
|
+
import type { KeyboardEvent, MutableRefObject } from 'react';
|
|
4
|
+
import { useEffect } from 'react';
|
|
5
|
+
|
|
6
|
+
import type {
|
|
7
|
+
StateChart as GenericStateChart,
|
|
8
|
+
StateMachineState,
|
|
9
|
+
} from '../hooks/useReducerMachine';
|
|
10
|
+
import { getCircularIndex } from '../utils/get-circular-index';
|
|
11
|
+
import { useComboBoxContext } from './context';
|
|
12
|
+
import { scopeQuery } from './scopeQuery';
|
|
13
|
+
|
|
14
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
15
|
+
// States
|
|
16
|
+
|
|
17
|
+
// Nothing going on, waiting for the user to type or use the arrow keys
|
|
18
|
+
export const IDLE = 'IDLE';
|
|
19
|
+
|
|
20
|
+
// The component is suggesting options as the user types
|
|
21
|
+
const SUGGESTING = 'SUGGESTING';
|
|
22
|
+
|
|
23
|
+
// The user is using the keyboard to navigate the list, not typing
|
|
24
|
+
export const NAVIGATING = 'NAVIGATING';
|
|
25
|
+
|
|
26
|
+
export type StateTypes = typeof IDLE | typeof SUGGESTING | typeof NAVIGATING;
|
|
27
|
+
|
|
28
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
29
|
+
// Actions:
|
|
30
|
+
|
|
31
|
+
// Used to sync the state with controlled state, right after mounting
|
|
32
|
+
export const INIT = 'INIT';
|
|
33
|
+
|
|
34
|
+
// User cleared the value w/ backspace, but input still has focus
|
|
35
|
+
export const CLEAR = 'CLEAR';
|
|
36
|
+
|
|
37
|
+
// User cleared the value w/ backspace, but input still has focus
|
|
38
|
+
export const CLEAR_SELECTION = 'CLEAR_SELECTION';
|
|
39
|
+
|
|
40
|
+
// User is typing
|
|
41
|
+
export const CHANGE = 'CHANGE';
|
|
42
|
+
|
|
43
|
+
// User is navigating w/ the keyboard
|
|
44
|
+
export const NAVIGATE = 'NAVIGATE';
|
|
45
|
+
|
|
46
|
+
// User can be navigating with keyboard and then click instead, we want the
|
|
47
|
+
// value from the click, not the current nav item
|
|
48
|
+
const SELECT_WITH_KEYBOARD = 'SELECT_WITH_KEYBOARD';
|
|
49
|
+
export const SELECT_WITH_CLICK = 'SELECT_WITH_CLICK';
|
|
50
|
+
|
|
51
|
+
// Pretty self-explanatory, user can hit escape or blur to close the popover
|
|
52
|
+
const ESCAPE = 'ESCAPE';
|
|
53
|
+
const BLUR = 'BLUR';
|
|
54
|
+
|
|
55
|
+
export const FOCUS = 'FOCUS';
|
|
56
|
+
|
|
57
|
+
export const OPEN_WITH_BUTTON = 'OPEN_WITH_BUTTON';
|
|
58
|
+
|
|
59
|
+
export const CLOSE_WITH_BUTTON = 'CLOSE_WITH_BUTTON';
|
|
60
|
+
|
|
61
|
+
export type ActionTypes =
|
|
62
|
+
| typeof CLEAR
|
|
63
|
+
| typeof CLEAR_SELECTION
|
|
64
|
+
| typeof CHANGE
|
|
65
|
+
| typeof INIT
|
|
66
|
+
| typeof NAVIGATE
|
|
67
|
+
| typeof SELECT_WITH_KEYBOARD
|
|
68
|
+
| typeof SELECT_WITH_CLICK
|
|
69
|
+
| typeof ESCAPE
|
|
70
|
+
| typeof BLUR
|
|
71
|
+
| typeof FOCUS
|
|
72
|
+
| typeof OPEN_WITH_BUTTON
|
|
73
|
+
| typeof CLOSE_WITH_BUTTON;
|
|
74
|
+
|
|
75
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
76
|
+
export const stateChart: GenericStateChart<StateTypes, ActionTypes> = {
|
|
77
|
+
initial: IDLE,
|
|
78
|
+
states: {
|
|
79
|
+
[IDLE]: {
|
|
80
|
+
on: {
|
|
81
|
+
[BLUR]: IDLE,
|
|
82
|
+
[CLEAR]: IDLE,
|
|
83
|
+
[INIT]: IDLE,
|
|
84
|
+
[CLEAR_SELECTION]: IDLE,
|
|
85
|
+
[CHANGE]: SUGGESTING,
|
|
86
|
+
[FOCUS]: SUGGESTING,
|
|
87
|
+
[NAVIGATE]: NAVIGATING,
|
|
88
|
+
[OPEN_WITH_BUTTON]: SUGGESTING,
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
[SUGGESTING]: {
|
|
92
|
+
on: {
|
|
93
|
+
[CHANGE]: SUGGESTING,
|
|
94
|
+
[FOCUS]: SUGGESTING,
|
|
95
|
+
[INIT]: SUGGESTING,
|
|
96
|
+
[NAVIGATE]: NAVIGATING,
|
|
97
|
+
[CLEAR]: IDLE,
|
|
98
|
+
[CLEAR_SELECTION]: SUGGESTING,
|
|
99
|
+
[ESCAPE]: IDLE,
|
|
100
|
+
[BLUR]: IDLE,
|
|
101
|
+
[SELECT_WITH_CLICK]: IDLE,
|
|
102
|
+
[CLOSE_WITH_BUTTON]: IDLE,
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
[NAVIGATING]: {
|
|
106
|
+
on: {
|
|
107
|
+
[CHANGE]: SUGGESTING,
|
|
108
|
+
[FOCUS]: SUGGESTING,
|
|
109
|
+
[INIT]: NAVIGATING,
|
|
110
|
+
[CLEAR]: IDLE,
|
|
111
|
+
[CLEAR_SELECTION]: NAVIGATING,
|
|
112
|
+
[BLUR]: IDLE,
|
|
113
|
+
[ESCAPE]: IDLE,
|
|
114
|
+
[NAVIGATE]: NAVIGATING,
|
|
115
|
+
[SELECT_WITH_KEYBOARD]: IDLE,
|
|
116
|
+
[SELECT_WITH_CLICK]: IDLE,
|
|
117
|
+
[CLOSE_WITH_BUTTON]: IDLE,
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
export interface ReducerState
|
|
124
|
+
extends StateMachineState<StateTypes, ActionTypes> {
|
|
125
|
+
item: string;
|
|
126
|
+
navigationItem: string;
|
|
127
|
+
text: string;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
interface ActionObject {
|
|
131
|
+
type: ActionTypes;
|
|
132
|
+
state: StateTypes;
|
|
133
|
+
nextState: StateTypes;
|
|
134
|
+
[rest: string]: any;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export function comboboxReducer(
|
|
138
|
+
data: Readonly<ReducerState>,
|
|
139
|
+
action: ActionObject
|
|
140
|
+
): ReducerState {
|
|
141
|
+
const nextState = {
|
|
142
|
+
...data,
|
|
143
|
+
state: action.nextState,
|
|
144
|
+
lastActionType: action.type,
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
switch (action.type) {
|
|
148
|
+
case INIT:
|
|
149
|
+
case CHANGE:
|
|
150
|
+
return {
|
|
151
|
+
...nextState,
|
|
152
|
+
text: action.text,
|
|
153
|
+
navigationItem: '',
|
|
154
|
+
item: '',
|
|
155
|
+
};
|
|
156
|
+
case NAVIGATE:
|
|
157
|
+
case OPEN_WITH_BUTTON:
|
|
158
|
+
if (action.persistSelection) {
|
|
159
|
+
return {
|
|
160
|
+
...nextState,
|
|
161
|
+
navigationItem: data.item,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return {
|
|
166
|
+
...nextState,
|
|
167
|
+
navigationItem: action.item,
|
|
168
|
+
};
|
|
169
|
+
case CLEAR_SELECTION:
|
|
170
|
+
return {
|
|
171
|
+
...nextState,
|
|
172
|
+
navigationItem: '',
|
|
173
|
+
};
|
|
174
|
+
case CLEAR:
|
|
175
|
+
return {
|
|
176
|
+
...nextState,
|
|
177
|
+
text: '',
|
|
178
|
+
navigationItem: '',
|
|
179
|
+
item: '',
|
|
180
|
+
};
|
|
181
|
+
case BLUR:
|
|
182
|
+
return {
|
|
183
|
+
...nextState,
|
|
184
|
+
text: action.text,
|
|
185
|
+
navigationItem: '',
|
|
186
|
+
item: action.item,
|
|
187
|
+
};
|
|
188
|
+
case CLOSE_WITH_BUTTON:
|
|
189
|
+
case ESCAPE:
|
|
190
|
+
return {
|
|
191
|
+
...nextState,
|
|
192
|
+
navigationItem: '',
|
|
193
|
+
item: '',
|
|
194
|
+
};
|
|
195
|
+
case SELECT_WITH_CLICK:
|
|
196
|
+
case SELECT_WITH_KEYBOARD:
|
|
197
|
+
return {
|
|
198
|
+
...nextState,
|
|
199
|
+
text: action.text,
|
|
200
|
+
item: action.item,
|
|
201
|
+
navigationItem: '',
|
|
202
|
+
};
|
|
203
|
+
case FOCUS:
|
|
204
|
+
return {
|
|
205
|
+
...nextState,
|
|
206
|
+
navigationItem: action.item,
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
default:
|
|
210
|
+
throw new Error(`Unknown action ${action.type}`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const visibleStates = [SUGGESTING, NAVIGATING];
|
|
215
|
+
export const isVisible = (state: any) => visibleStates.indexOf(state) >= 0;
|
|
216
|
+
|
|
217
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
218
|
+
// The rest is all implementation details
|
|
219
|
+
|
|
220
|
+
// Move focus back to the input if we start navigating w/ the
|
|
221
|
+
// keyboard after focus has moved to any focusable content in
|
|
222
|
+
// the popup.
|
|
223
|
+
export function useFocusManagement(
|
|
224
|
+
lastActionType: ActionTypes,
|
|
225
|
+
inputRef: MutableRefObject<HTMLInputElement | null>
|
|
226
|
+
) {
|
|
227
|
+
// useEffect so that the cursor goes to the end of the input instead
|
|
228
|
+
// of awkwardly at the beginning, unclear to me why ...
|
|
229
|
+
useEffect(() => {
|
|
230
|
+
if (
|
|
231
|
+
lastActionType === NAVIGATE ||
|
|
232
|
+
lastActionType === ESCAPE ||
|
|
233
|
+
lastActionType === SELECT_WITH_CLICK ||
|
|
234
|
+
lastActionType === OPEN_WITH_BUTTON
|
|
235
|
+
) {
|
|
236
|
+
inputRef.current && inputRef.current.focus();
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function getNextItem(
|
|
242
|
+
currentItem: string,
|
|
243
|
+
key: 'ArrowDown' | 'ArrowUp' | 'PageDown' | 'PageUp' | 'Home' | 'End',
|
|
244
|
+
optionsItems: HTMLElement[],
|
|
245
|
+
autocomplete: boolean
|
|
246
|
+
): HTMLElement | null {
|
|
247
|
+
const jumpToStartOrEnd = key === 'Home' || key === 'End';
|
|
248
|
+
let incr = 1;
|
|
249
|
+
switch (key) {
|
|
250
|
+
case 'PageUp':
|
|
251
|
+
incr = -10;
|
|
252
|
+
break;
|
|
253
|
+
case 'PageDown':
|
|
254
|
+
incr = 10;
|
|
255
|
+
break;
|
|
256
|
+
case 'End':
|
|
257
|
+
case 'ArrowUp':
|
|
258
|
+
incr = -1;
|
|
259
|
+
break;
|
|
260
|
+
case 'Home':
|
|
261
|
+
case 'ArrowDown':
|
|
262
|
+
incr = 1;
|
|
263
|
+
break;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const index =
|
|
267
|
+
currentItem === ''
|
|
268
|
+
? -1
|
|
269
|
+
: optionsItems.findIndex((n) => String(n.id) === currentItem);
|
|
270
|
+
|
|
271
|
+
const optionsLen = optionsItems.length;
|
|
272
|
+
|
|
273
|
+
// Nothing selected, either go to start, or end
|
|
274
|
+
if (index < 0 || jumpToStartOrEnd) {
|
|
275
|
+
if (incr > 0) {
|
|
276
|
+
// Go to start
|
|
277
|
+
return optionsItems[0];
|
|
278
|
+
} else {
|
|
279
|
+
// Go to end
|
|
280
|
+
return optionsItems[optionsLen - 1];
|
|
281
|
+
}
|
|
282
|
+
} else if (autocomplete) {
|
|
283
|
+
const nextIndex = index + incr;
|
|
284
|
+
|
|
285
|
+
if (nextIndex < 0 || nextIndex >= optionsLen) {
|
|
286
|
+
// Next is outside the bounds of list, return nothing selected
|
|
287
|
+
return null;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// I'm sure it won't be null, we already check optionsLen above
|
|
292
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
293
|
+
return optionsItems[getCircularIndex(index + incr, optionsLen)!];
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// We want the same events when the input or the popup have focus (HOW COOL ARE
|
|
297
|
+
// HOOKS BTW?) This is probably the hairiest piece but it's not bad.
|
|
298
|
+
export function useKeyDown() {
|
|
299
|
+
const {
|
|
300
|
+
data: { text, navigationItem },
|
|
301
|
+
onSelect,
|
|
302
|
+
optionsRef,
|
|
303
|
+
inputRef,
|
|
304
|
+
state,
|
|
305
|
+
transition,
|
|
306
|
+
autocompletePropRef,
|
|
307
|
+
clearOnEscapeRef,
|
|
308
|
+
persistSelectionRef,
|
|
309
|
+
listScope,
|
|
310
|
+
} = useComboBoxContext();
|
|
311
|
+
|
|
312
|
+
return function handleKeyDown(event: KeyboardEvent<any>) {
|
|
313
|
+
const optionNodes = listScope.current.queryAllNodes(scopeQuery);
|
|
314
|
+
|
|
315
|
+
switch (event.key) {
|
|
316
|
+
case 'Home':
|
|
317
|
+
case 'End':
|
|
318
|
+
case 'PageUp':
|
|
319
|
+
case 'PageDown':
|
|
320
|
+
case 'ArrowUp':
|
|
321
|
+
case 'ArrowDown': {
|
|
322
|
+
// Don't scroll the page
|
|
323
|
+
event.preventDefault();
|
|
324
|
+
|
|
325
|
+
const optionsLen = optionNodes.length;
|
|
326
|
+
|
|
327
|
+
// If the developer didn't render any options, there's no point in
|
|
328
|
+
// trying to navigate--but seriously what the heck? Give us some
|
|
329
|
+
// options fam.
|
|
330
|
+
if (optionsLen === 0) {
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
if (state === IDLE) {
|
|
335
|
+
// Opening a closed list
|
|
336
|
+
transition(NAVIGATE, {
|
|
337
|
+
persistSelection: persistSelectionRef.current,
|
|
338
|
+
});
|
|
339
|
+
} else {
|
|
340
|
+
// When autocompletting, we'll not cycle through the list directly
|
|
341
|
+
const autocomplete = autocompletePropRef.current;
|
|
342
|
+
|
|
343
|
+
// Get next selected item
|
|
344
|
+
const nextItem = getNextItem(
|
|
345
|
+
navigationItem,
|
|
346
|
+
event.key,
|
|
347
|
+
optionNodes,
|
|
348
|
+
autocomplete
|
|
349
|
+
);
|
|
350
|
+
|
|
351
|
+
if (nextItem !== null) {
|
|
352
|
+
nextItem.scrollIntoView({ behavior: 'auto', block: 'nearest' });
|
|
353
|
+
transition(NAVIGATE, {
|
|
354
|
+
value: optionsRef.current[nextItem.id].text,
|
|
355
|
+
item: nextItem.id,
|
|
356
|
+
});
|
|
357
|
+
} else {
|
|
358
|
+
transition(NAVIGATE, { value: null, item: '' });
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
break;
|
|
362
|
+
}
|
|
363
|
+
case 'Escape': {
|
|
364
|
+
if (state !== IDLE) {
|
|
365
|
+
transition(ESCAPE);
|
|
366
|
+
} else if (state === IDLE && text !== '') {
|
|
367
|
+
if (!inputRef.current || !clearOnEscapeRef.current) {
|
|
368
|
+
break;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// emulate a inputRef change event, might not work in future versions of React
|
|
372
|
+
const lastValue = inputRef.current.value;
|
|
373
|
+
inputRef.current.value = '';
|
|
374
|
+
|
|
375
|
+
const tracker = (inputRef.current as any)._valueTracker;
|
|
376
|
+
if (tracker) {
|
|
377
|
+
tracker.setValue(lastValue);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
const event = new Event('change', { bubbles: true });
|
|
381
|
+
inputRef.current.dispatchEvent(event);
|
|
382
|
+
}
|
|
383
|
+
break;
|
|
384
|
+
}
|
|
385
|
+
case 'Enter': {
|
|
386
|
+
if (state === NAVIGATING && navigationItem !== '') {
|
|
387
|
+
const { value: navigationValue, text: navigationText } =
|
|
388
|
+
optionsRef.current[navigationItem];
|
|
389
|
+
|
|
390
|
+
// don't want to submit forms
|
|
391
|
+
event.preventDefault();
|
|
392
|
+
onSelect && onSelect(navigationText, navigationItem, navigationValue);
|
|
393
|
+
transition(SELECT_WITH_KEYBOARD, {
|
|
394
|
+
text: navigationText,
|
|
395
|
+
item: navigationItem,
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
break;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
export function useBlur() {
|
|
405
|
+
const {
|
|
406
|
+
data: { navigationItem, text: stateText },
|
|
407
|
+
transition,
|
|
408
|
+
optionsRef,
|
|
409
|
+
popoverRef,
|
|
410
|
+
inputRef,
|
|
411
|
+
buttonRef,
|
|
412
|
+
onSelect,
|
|
413
|
+
selectOnBlur, // not implemented yet
|
|
414
|
+
} = useComboBoxContext();
|
|
415
|
+
|
|
416
|
+
return function handleBlur() {
|
|
417
|
+
requestAnimationFrame(() => {
|
|
418
|
+
// we on want to close only if focus rests outside the combobox
|
|
419
|
+
if (
|
|
420
|
+
document.activeElement !== inputRef.current &&
|
|
421
|
+
document.activeElement !== buttonRef.current &&
|
|
422
|
+
popoverRef.current
|
|
423
|
+
) {
|
|
424
|
+
if (popoverRef.current.contains(document.activeElement)) {
|
|
425
|
+
// focus landed inside the combobox, keep it open
|
|
426
|
+
// in the future, we can make it not close, select, or anything
|
|
427
|
+
// this way we can have like... checkboxes available in the
|
|
428
|
+
// menu item, etc.
|
|
429
|
+
} else {
|
|
430
|
+
// focus landed outside the combobox, close it.
|
|
431
|
+
if (!selectOnBlur || navigationItem === '') {
|
|
432
|
+
// we don't wanna select on blur, or navigationIndex is invalid
|
|
433
|
+
transition(BLUR, { text: stateText, item: '' });
|
|
434
|
+
} else {
|
|
435
|
+
// select the currently selected item
|
|
436
|
+
const { value: navigationValue, text: navigationText } =
|
|
437
|
+
optionsRef.current[navigationItem];
|
|
438
|
+
|
|
439
|
+
onSelect &&
|
|
440
|
+
onSelect(navigationText, navigationItem, navigationValue);
|
|
441
|
+
|
|
442
|
+
transition(BLUR, {
|
|
443
|
+
text: navigationText,
|
|
444
|
+
item: navigationItem,
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
});
|
|
450
|
+
};
|
|
451
|
+
}
|