@octanejs/aria 0.0.1 → 0.0.3
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/package.json +6 -3
- package/src/breadcrumbs/useBreadcrumbItem.ts +95 -0
- package/src/breadcrumbs/useBreadcrumbs.ts +51 -0
- package/src/button/useButton.ts +273 -0
- package/src/button/useToggleButton.ts +131 -0
- package/src/button/useToggleButtonGroup.ts +131 -0
- package/src/checkbox/useCheckbox.ts +114 -0
- package/src/checkbox/useCheckboxGroup.ts +107 -0
- package/src/checkbox/useCheckboxGroupItem.ts +132 -0
- package/src/checkbox/utils.ts +15 -0
- package/src/combobox/useComboBox.ts +602 -0
- package/src/dialog/useDialog.ts +147 -0
- package/src/disclosure/useDisclosure.ts +180 -0
- package/src/focus/FocusRing.ts +69 -0
- package/src/focus/FocusScope.ts +1205 -0
- package/src/focus/useFocusRing.ts +125 -0
- package/src/focus/useHasTabbableChild.ts +82 -0
- package/src/focus/virtualFocus.ts +38 -0
- package/src/form/useFormValidation.ts +205 -0
- package/src/grid/useGridSelectionAnnouncement.ts +177 -0
- package/src/grid/useGridSelectionCheckbox.ts +86 -0
- package/src/grid/useHighlightSelectionDescription.ts +81 -0
- package/src/gridlist/useGridList.ts +236 -0
- package/src/gridlist/useGridListItem.ts +512 -0
- package/src/gridlist/useGridListSection.ts +78 -0
- package/src/gridlist/useGridListSelectionCheckbox.ts +47 -0
- package/src/gridlist/utils.ts +36 -0
- package/src/i18n/I18nProvider.ts +97 -0
- package/src/i18n/server.ts +115 -0
- package/src/i18n/useCollator.ts +42 -0
- package/src/i18n/useDateFormatter.ts +64 -0
- package/src/i18n/useDefaultLocale.ts +93 -0
- package/src/i18n/useFilter.ts +107 -0
- package/src/i18n/useListFormatter.ts +33 -0
- package/src/i18n/useLocalizedStringFormatter.ts +90 -0
- package/src/i18n/useNumberFormatter.ts +33 -0
- package/src/i18n/utils.ts +67 -0
- package/src/index.ts +268 -0
- package/src/intl/breadcrumbs/ar-AE.json +3 -0
- package/src/intl/breadcrumbs/bg-BG.json +3 -0
- package/src/intl/breadcrumbs/cs-CZ.json +3 -0
- package/src/intl/breadcrumbs/da-DK.json +3 -0
- package/src/intl/breadcrumbs/de-DE.json +3 -0
- package/src/intl/breadcrumbs/el-GR.json +3 -0
- package/src/intl/breadcrumbs/en-US.json +3 -0
- package/src/intl/breadcrumbs/es-ES.json +3 -0
- package/src/intl/breadcrumbs/et-EE.json +3 -0
- package/src/intl/breadcrumbs/fi-FI.json +3 -0
- package/src/intl/breadcrumbs/fr-FR.json +3 -0
- package/src/intl/breadcrumbs/he-IL.json +3 -0
- package/src/intl/breadcrumbs/hr-HR.json +3 -0
- package/src/intl/breadcrumbs/hu-HU.json +3 -0
- package/src/intl/breadcrumbs/index.ts +75 -0
- package/src/intl/breadcrumbs/it-IT.json +3 -0
- package/src/intl/breadcrumbs/ja-JP.json +3 -0
- package/src/intl/breadcrumbs/ko-KR.json +3 -0
- package/src/intl/breadcrumbs/lt-LT.json +3 -0
- package/src/intl/breadcrumbs/lv-LV.json +3 -0
- package/src/intl/breadcrumbs/nb-NO.json +3 -0
- package/src/intl/breadcrumbs/nl-NL.json +3 -0
- package/src/intl/breadcrumbs/pl-PL.json +3 -0
- package/src/intl/breadcrumbs/pt-BR.json +3 -0
- package/src/intl/breadcrumbs/pt-PT.json +3 -0
- package/src/intl/breadcrumbs/ro-RO.json +3 -0
- package/src/intl/breadcrumbs/ru-RU.json +3 -0
- package/src/intl/breadcrumbs/sk-SK.json +3 -0
- package/src/intl/breadcrumbs/sl-SI.json +3 -0
- package/src/intl/breadcrumbs/sr-SP.json +3 -0
- package/src/intl/breadcrumbs/sv-SE.json +3 -0
- package/src/intl/breadcrumbs/tr-TR.json +3 -0
- package/src/intl/breadcrumbs/uk-UA.json +3 -0
- package/src/intl/breadcrumbs/zh-CN.json +3 -0
- package/src/intl/breadcrumbs/zh-TW.json +3 -0
- package/src/intl/combobox/ar-AE.json +7 -0
- package/src/intl/combobox/bg-BG.json +7 -0
- package/src/intl/combobox/cs-CZ.json +7 -0
- package/src/intl/combobox/da-DK.json +7 -0
- package/src/intl/combobox/de-DE.json +7 -0
- package/src/intl/combobox/el-GR.json +7 -0
- package/src/intl/combobox/en-US.json +7 -0
- package/src/intl/combobox/es-ES.json +7 -0
- package/src/intl/combobox/et-EE.json +7 -0
- package/src/intl/combobox/fi-FI.json +7 -0
- package/src/intl/combobox/fr-FR.json +7 -0
- package/src/intl/combobox/he-IL.json +7 -0
- package/src/intl/combobox/hr-HR.json +7 -0
- package/src/intl/combobox/hu-HU.json +7 -0
- package/src/intl/combobox/index.ts +75 -0
- package/src/intl/combobox/it-IT.json +7 -0
- package/src/intl/combobox/ja-JP.json +7 -0
- package/src/intl/combobox/ko-KR.json +7 -0
- package/src/intl/combobox/lt-LT.json +7 -0
- package/src/intl/combobox/lv-LV.json +7 -0
- package/src/intl/combobox/nb-NO.json +7 -0
- package/src/intl/combobox/nl-NL.json +7 -0
- package/src/intl/combobox/pl-PL.json +7 -0
- package/src/intl/combobox/pt-BR.json +7 -0
- package/src/intl/combobox/pt-PT.json +7 -0
- package/src/intl/combobox/ro-RO.json +7 -0
- package/src/intl/combobox/ru-RU.json +7 -0
- package/src/intl/combobox/sk-SK.json +7 -0
- package/src/intl/combobox/sl-SI.json +7 -0
- package/src/intl/combobox/sr-SP.json +7 -0
- package/src/intl/combobox/sv-SE.json +7 -0
- package/src/intl/combobox/tr-TR.json +7 -0
- package/src/intl/combobox/uk-UA.json +7 -0
- package/src/intl/combobox/zh-CN.json +7 -0
- package/src/intl/combobox/zh-TW.json +7 -0
- package/src/intl/grid/ar-AE.json +8 -0
- package/src/intl/grid/bg-BG.json +8 -0
- package/src/intl/grid/cs-CZ.json +8 -0
- package/src/intl/grid/da-DK.json +8 -0
- package/src/intl/grid/de-DE.json +8 -0
- package/src/intl/grid/el-GR.json +8 -0
- package/src/intl/grid/en-US.json +8 -0
- package/src/intl/grid/es-ES.json +8 -0
- package/src/intl/grid/et-EE.json +8 -0
- package/src/intl/grid/fi-FI.json +8 -0
- package/src/intl/grid/fr-FR.json +8 -0
- package/src/intl/grid/he-IL.json +8 -0
- package/src/intl/grid/hr-HR.json +8 -0
- package/src/intl/grid/hu-HU.json +8 -0
- package/src/intl/grid/index.ts +75 -0
- package/src/intl/grid/it-IT.json +8 -0
- package/src/intl/grid/ja-JP.json +8 -0
- package/src/intl/grid/ko-KR.json +8 -0
- package/src/intl/grid/lt-LT.json +8 -0
- package/src/intl/grid/lv-LV.json +8 -0
- package/src/intl/grid/nb-NO.json +8 -0
- package/src/intl/grid/nl-NL.json +8 -0
- package/src/intl/grid/pl-PL.json +8 -0
- package/src/intl/grid/pt-BR.json +8 -0
- package/src/intl/grid/pt-PT.json +8 -0
- package/src/intl/grid/ro-RO.json +8 -0
- package/src/intl/grid/ru-RU.json +8 -0
- package/src/intl/grid/sk-SK.json +8 -0
- package/src/intl/grid/sl-SI.json +8 -0
- package/src/intl/grid/sr-SP.json +8 -0
- package/src/intl/grid/sv-SE.json +8 -0
- package/src/intl/grid/tr-TR.json +8 -0
- package/src/intl/grid/uk-UA.json +8 -0
- package/src/intl/grid/zh-CN.json +8 -0
- package/src/intl/grid/zh-TW.json +8 -0
- package/src/intl/gridlist/ar-AE.json +4 -0
- package/src/intl/gridlist/bg-BG.json +4 -0
- package/src/intl/gridlist/cs-CZ.json +4 -0
- package/src/intl/gridlist/da-DK.json +4 -0
- package/src/intl/gridlist/de-DE.json +4 -0
- package/src/intl/gridlist/el-GR.json +4 -0
- package/src/intl/gridlist/en-US.json +4 -0
- package/src/intl/gridlist/es-ES.json +4 -0
- package/src/intl/gridlist/et-EE.json +4 -0
- package/src/intl/gridlist/fi-FI.json +4 -0
- package/src/intl/gridlist/fr-FR.json +4 -0
- package/src/intl/gridlist/he-IL.json +4 -0
- package/src/intl/gridlist/hr-HR.json +4 -0
- package/src/intl/gridlist/hu-HU.json +4 -0
- package/src/intl/gridlist/index.ts +75 -0
- package/src/intl/gridlist/it-IT.json +4 -0
- package/src/intl/gridlist/ja-JP.json +4 -0
- package/src/intl/gridlist/ko-KR.json +4 -0
- package/src/intl/gridlist/lt-LT.json +4 -0
- package/src/intl/gridlist/lv-LV.json +4 -0
- package/src/intl/gridlist/nb-NO.json +4 -0
- package/src/intl/gridlist/nl-NL.json +4 -0
- package/src/intl/gridlist/pl-PL.json +4 -0
- package/src/intl/gridlist/pt-BR.json +4 -0
- package/src/intl/gridlist/pt-PT.json +4 -0
- package/src/intl/gridlist/ro-RO.json +4 -0
- package/src/intl/gridlist/ru-RU.json +4 -0
- package/src/intl/gridlist/sk-SK.json +4 -0
- package/src/intl/gridlist/sl-SI.json +4 -0
- package/src/intl/gridlist/sr-SP.json +4 -0
- package/src/intl/gridlist/sv-SE.json +4 -0
- package/src/intl/gridlist/tr-TR.json +4 -0
- package/src/intl/gridlist/uk-UA.json +4 -0
- package/src/intl/gridlist/zh-CN.json +4 -0
- package/src/intl/gridlist/zh-TW.json +4 -0
- package/src/intl/menu/ar-AE.json +3 -0
- package/src/intl/menu/bg-BG.json +3 -0
- package/src/intl/menu/cs-CZ.json +3 -0
- package/src/intl/menu/da-DK.json +3 -0
- package/src/intl/menu/de-DE.json +3 -0
- package/src/intl/menu/el-GR.json +3 -0
- package/src/intl/menu/en-US.json +3 -0
- package/src/intl/menu/es-ES.json +3 -0
- package/src/intl/menu/et-EE.json +3 -0
- package/src/intl/menu/fi-FI.json +3 -0
- package/src/intl/menu/fr-FR.json +3 -0
- package/src/intl/menu/he-IL.json +3 -0
- package/src/intl/menu/hr-HR.json +3 -0
- package/src/intl/menu/hu-HU.json +3 -0
- package/src/intl/menu/index.ts +75 -0
- package/src/intl/menu/it-IT.json +3 -0
- package/src/intl/menu/ja-JP.json +3 -0
- package/src/intl/menu/ko-KR.json +3 -0
- package/src/intl/menu/lt-LT.json +3 -0
- package/src/intl/menu/lv-LV.json +3 -0
- package/src/intl/menu/nb-NO.json +3 -0
- package/src/intl/menu/nl-NL.json +3 -0
- package/src/intl/menu/pl-PL.json +3 -0
- package/src/intl/menu/pt-BR.json +3 -0
- package/src/intl/menu/pt-PT.json +3 -0
- package/src/intl/menu/ro-RO.json +3 -0
- package/src/intl/menu/ru-RU.json +3 -0
- package/src/intl/menu/sk-SK.json +3 -0
- package/src/intl/menu/sl-SI.json +3 -0
- package/src/intl/menu/sr-SP.json +3 -0
- package/src/intl/menu/sv-SE.json +3 -0
- package/src/intl/menu/tr-TR.json +3 -0
- package/src/intl/menu/uk-UA.json +3 -0
- package/src/intl/menu/zh-CN.json +3 -0
- package/src/intl/menu/zh-TW.json +3 -0
- package/src/intl/numberfield/ar-AE.json +5 -0
- package/src/intl/numberfield/bg-BG.json +5 -0
- package/src/intl/numberfield/cs-CZ.json +5 -0
- package/src/intl/numberfield/da-DK.json +5 -0
- package/src/intl/numberfield/de-DE.json +5 -0
- package/src/intl/numberfield/el-GR.json +5 -0
- package/src/intl/numberfield/en-US.json +5 -0
- package/src/intl/numberfield/es-ES.json +5 -0
- package/src/intl/numberfield/et-EE.json +5 -0
- package/src/intl/numberfield/fi-FI.json +5 -0
- package/src/intl/numberfield/fr-FR.json +5 -0
- package/src/intl/numberfield/he-IL.json +5 -0
- package/src/intl/numberfield/hr-HR.json +5 -0
- package/src/intl/numberfield/hu-HU.json +5 -0
- package/src/intl/numberfield/index.ts +75 -0
- package/src/intl/numberfield/it-IT.json +5 -0
- package/src/intl/numberfield/ja-JP.json +5 -0
- package/src/intl/numberfield/ko-KR.json +5 -0
- package/src/intl/numberfield/lt-LT.json +5 -0
- package/src/intl/numberfield/lv-LV.json +5 -0
- package/src/intl/numberfield/nb-NO.json +5 -0
- package/src/intl/numberfield/nl-NL.json +5 -0
- package/src/intl/numberfield/pl-PL.json +5 -0
- package/src/intl/numberfield/pt-BR.json +5 -0
- package/src/intl/numberfield/pt-PT.json +5 -0
- package/src/intl/numberfield/ro-RO.json +5 -0
- package/src/intl/numberfield/ru-RU.json +5 -0
- package/src/intl/numberfield/sk-SK.json +5 -0
- package/src/intl/numberfield/sl-SI.json +5 -0
- package/src/intl/numberfield/sr-SP.json +5 -0
- package/src/intl/numberfield/sv-SE.json +5 -0
- package/src/intl/numberfield/tr-TR.json +5 -0
- package/src/intl/numberfield/uk-UA.json +5 -0
- package/src/intl/numberfield/zh-CN.json +5 -0
- package/src/intl/numberfield/zh-TW.json +5 -0
- package/src/intl/overlays/ar-AE.json +3 -0
- package/src/intl/overlays/bg-BG.json +3 -0
- package/src/intl/overlays/cs-CZ.json +3 -0
- package/src/intl/overlays/da-DK.json +3 -0
- package/src/intl/overlays/de-DE.json +3 -0
- package/src/intl/overlays/el-GR.json +3 -0
- package/src/intl/overlays/en-US.json +3 -0
- package/src/intl/overlays/es-ES.json +3 -0
- package/src/intl/overlays/et-EE.json +3 -0
- package/src/intl/overlays/fi-FI.json +3 -0
- package/src/intl/overlays/fr-FR.json +3 -0
- package/src/intl/overlays/he-IL.json +3 -0
- package/src/intl/overlays/hr-HR.json +3 -0
- package/src/intl/overlays/hu-HU.json +3 -0
- package/src/intl/overlays/index.ts +75 -0
- package/src/intl/overlays/it-IT.json +3 -0
- package/src/intl/overlays/ja-JP.json +3 -0
- package/src/intl/overlays/ko-KR.json +3 -0
- package/src/intl/overlays/lt-LT.json +3 -0
- package/src/intl/overlays/lv-LV.json +3 -0
- package/src/intl/overlays/nb-NO.json +3 -0
- package/src/intl/overlays/nl-NL.json +3 -0
- package/src/intl/overlays/pl-PL.json +3 -0
- package/src/intl/overlays/pt-BR.json +3 -0
- package/src/intl/overlays/pt-PT.json +3 -0
- package/src/intl/overlays/ro-RO.json +3 -0
- package/src/intl/overlays/ru-RU.json +3 -0
- package/src/intl/overlays/sk-SK.json +3 -0
- package/src/intl/overlays/sl-SI.json +3 -0
- package/src/intl/overlays/sr-SP.json +3 -0
- package/src/intl/overlays/sv-SE.json +3 -0
- package/src/intl/overlays/tr-TR.json +3 -0
- package/src/intl/overlays/uk-UA.json +3 -0
- package/src/intl/overlays/zh-CN.json +3 -0
- package/src/intl/overlays/zh-TW.json +3 -0
- package/src/intl/searchfield/ar-AE.json +3 -0
- package/src/intl/searchfield/bg-BG.json +3 -0
- package/src/intl/searchfield/cs-CZ.json +3 -0
- package/src/intl/searchfield/da-DK.json +3 -0
- package/src/intl/searchfield/de-DE.json +3 -0
- package/src/intl/searchfield/el-GR.json +3 -0
- package/src/intl/searchfield/en-US.json +3 -0
- package/src/intl/searchfield/es-ES.json +3 -0
- package/src/intl/searchfield/et-EE.json +3 -0
- package/src/intl/searchfield/fi-FI.json +3 -0
- package/src/intl/searchfield/fr-FR.json +3 -0
- package/src/intl/searchfield/he-IL.json +3 -0
- package/src/intl/searchfield/hr-HR.json +3 -0
- package/src/intl/searchfield/hu-HU.json +3 -0
- package/src/intl/searchfield/index.ts +75 -0
- package/src/intl/searchfield/it-IT.json +3 -0
- package/src/intl/searchfield/ja-JP.json +3 -0
- package/src/intl/searchfield/ko-KR.json +3 -0
- package/src/intl/searchfield/lt-LT.json +3 -0
- package/src/intl/searchfield/lv-LV.json +3 -0
- package/src/intl/searchfield/nb-NO.json +3 -0
- package/src/intl/searchfield/nl-NL.json +3 -0
- package/src/intl/searchfield/pl-PL.json +3 -0
- package/src/intl/searchfield/pt-BR.json +3 -0
- package/src/intl/searchfield/pt-PT.json +3 -0
- package/src/intl/searchfield/ro-RO.json +3 -0
- package/src/intl/searchfield/ru-RU.json +3 -0
- package/src/intl/searchfield/sk-SK.json +3 -0
- package/src/intl/searchfield/sl-SI.json +3 -0
- package/src/intl/searchfield/sr-SP.json +3 -0
- package/src/intl/searchfield/sv-SE.json +3 -0
- package/src/intl/searchfield/tr-TR.json +3 -0
- package/src/intl/searchfield/uk-UA.json +3 -0
- package/src/intl/searchfield/zh-CN.json +3 -0
- package/src/intl/searchfield/zh-TW.json +3 -0
- package/src/intl/spinbutton/ar-AE.json +3 -0
- package/src/intl/spinbutton/bg-BG.json +3 -0
- package/src/intl/spinbutton/cs-CZ.json +3 -0
- package/src/intl/spinbutton/da-DK.json +3 -0
- package/src/intl/spinbutton/de-DE.json +3 -0
- package/src/intl/spinbutton/el-GR.json +3 -0
- package/src/intl/spinbutton/en-US.json +3 -0
- package/src/intl/spinbutton/es-ES.json +3 -0
- package/src/intl/spinbutton/et-EE.json +3 -0
- package/src/intl/spinbutton/fi-FI.json +3 -0
- package/src/intl/spinbutton/fr-FR.json +3 -0
- package/src/intl/spinbutton/he-IL.json +3 -0
- package/src/intl/spinbutton/hr-HR.json +3 -0
- package/src/intl/spinbutton/hu-HU.json +3 -0
- package/src/intl/spinbutton/index.ts +75 -0
- package/src/intl/spinbutton/it-IT.json +3 -0
- package/src/intl/spinbutton/ja-JP.json +3 -0
- package/src/intl/spinbutton/ko-KR.json +3 -0
- package/src/intl/spinbutton/lt-LT.json +3 -0
- package/src/intl/spinbutton/lv-LV.json +3 -0
- package/src/intl/spinbutton/nb-NO.json +3 -0
- package/src/intl/spinbutton/nl-NL.json +3 -0
- package/src/intl/spinbutton/pl-PL.json +3 -0
- package/src/intl/spinbutton/pt-BR.json +3 -0
- package/src/intl/spinbutton/pt-PT.json +3 -0
- package/src/intl/spinbutton/ro-RO.json +3 -0
- package/src/intl/spinbutton/ru-RU.json +3 -0
- package/src/intl/spinbutton/sk-SK.json +3 -0
- package/src/intl/spinbutton/sl-SI.json +3 -0
- package/src/intl/spinbutton/sr-SP.json +3 -0
- package/src/intl/spinbutton/sv-SE.json +3 -0
- package/src/intl/spinbutton/tr-TR.json +3 -0
- package/src/intl/spinbutton/uk-UA.json +3 -0
- package/src/intl/spinbutton/zh-CN.json +3 -0
- package/src/intl/spinbutton/zh-TW.json +3 -0
- package/src/intl/tag/ar-AE.json +4 -0
- package/src/intl/tag/bg-BG.json +4 -0
- package/src/intl/tag/cs-CZ.json +4 -0
- package/src/intl/tag/da-DK.json +4 -0
- package/src/intl/tag/de-DE.json +4 -0
- package/src/intl/tag/el-GR.json +4 -0
- package/src/intl/tag/en-US.json +4 -0
- package/src/intl/tag/es-ES.json +4 -0
- package/src/intl/tag/et-EE.json +4 -0
- package/src/intl/tag/fi-FI.json +4 -0
- package/src/intl/tag/fr-FR.json +4 -0
- package/src/intl/tag/he-IL.json +4 -0
- package/src/intl/tag/hr-HR.json +4 -0
- package/src/intl/tag/hu-HU.json +4 -0
- package/src/intl/tag/index.ts +75 -0
- package/src/intl/tag/it-IT.json +4 -0
- package/src/intl/tag/ja-JP.json +4 -0
- package/src/intl/tag/ko-KR.json +4 -0
- package/src/intl/tag/lt-LT.json +4 -0
- package/src/intl/tag/lv-LV.json +4 -0
- package/src/intl/tag/nb-NO.json +4 -0
- package/src/intl/tag/nl-NL.json +4 -0
- package/src/intl/tag/pl-PL.json +4 -0
- package/src/intl/tag/pt-BR.json +4 -0
- package/src/intl/tag/pt-PT.json +4 -0
- package/src/intl/tag/ro-RO.json +4 -0
- package/src/intl/tag/ru-RU.json +4 -0
- package/src/intl/tag/sk-SK.json +4 -0
- package/src/intl/tag/sl-SI.json +4 -0
- package/src/intl/tag/sr-SP.json +4 -0
- package/src/intl/tag/sv-SE.json +4 -0
- package/src/intl/tag/tr-TR.json +4 -0
- package/src/intl/tag/uk-UA.json +4 -0
- package/src/intl/tag/zh-CN.json +4 -0
- package/src/intl/tag/zh-TW.json +4 -0
- package/src/label/useField.ts +67 -0
- package/src/label/useLabel.ts +78 -0
- package/src/link/useLink.ts +108 -0
- package/src/listbox/useListBox.ts +224 -0
- package/src/listbox/useListBoxSection.ts +74 -0
- package/src/listbox/useOption.ts +212 -0
- package/src/listbox/utils.ts +38 -0
- package/src/live-announcer/LiveAnnouncer.ts +172 -0
- package/src/menu/useMenu.ts +139 -0
- package/src/menu/useMenuItem.ts +453 -0
- package/src/menu/useMenuSection.ts +70 -0
- package/src/menu/useMenuTrigger.ts +189 -0
- package/src/menu/useSafelyMouseToSubmenu.ts +236 -0
- package/src/menu/useSubmenuTrigger.ts +357 -0
- package/src/menu/utils.ts +16 -0
- package/src/meter/useMeter.ts +43 -0
- package/src/numberfield/useNumberField.ts +572 -0
- package/src/overlays/DismissButton.ts +50 -0
- package/src/overlays/Overlay.ts +106 -0
- package/src/overlays/PortalProvider.ts +37 -0
- package/src/overlays/ariaHideOutside.ts +299 -0
- package/src/overlays/calculatePosition.ts +856 -0
- package/src/overlays/useCloseOnScroll.ts +75 -0
- package/src/overlays/useModal.ts +224 -0
- package/src/overlays/useModalOverlay.ts +103 -0
- package/src/overlays/useOverlay.ts +199 -0
- package/src/overlays/useOverlayPosition.ts +462 -0
- package/src/overlays/useOverlayTrigger.ts +94 -0
- package/src/overlays/usePopover.ts +170 -0
- package/src/overlays/usePreventScroll.ts +316 -0
- package/src/progress/useProgressBar.ts +115 -0
- package/src/radio/useRadio.ts +219 -0
- package/src/radio/useRadioGroup.ts +184 -0
- package/src/radio/utils.ts +15 -0
- package/src/searchfield/useSearchField.ts +156 -0
- package/src/select/HiddenSelect.ts +261 -0
- package/src/select/useSelect.ts +332 -0
- package/src/selection/DOMLayoutDelegate.ts +51 -0
- package/src/selection/ListKeyboardDelegate.ts +392 -0
- package/src/selection/useSelectableCollection.ts +750 -0
- package/src/selection/useSelectableItem.ts +499 -0
- package/src/selection/useSelectableList.ts +132 -0
- package/src/selection/useTypeSelect.ts +192 -0
- package/src/selection/utils.ts +50 -0
- package/src/separator/useSeparator.ts +41 -0
- package/src/slider/useSlider.ts +277 -0
- package/src/slider/useSliderThumb.ts +375 -0
- package/src/slider/utils.ts +20 -0
- package/src/spinbutton/useSpinButton.ts +380 -0
- package/src/stately/checkbox/useCheckboxGroupState.ts +177 -0
- package/src/stately/collections/CollectionBuilder.ts +332 -0
- package/src/stately/collections/Item.ts +78 -0
- package/src/stately/collections/Section.ts +52 -0
- package/src/stately/collections/getChildNodes.ts +94 -0
- package/src/stately/collections/getItemCount.ts +29 -0
- package/src/stately/collections/types.ts +20 -0
- package/src/stately/collections/useCollection.ts +54 -0
- package/src/stately/combobox/useComboBoxState.ts +763 -0
- package/src/stately/disclosure/useDisclosureGroupState.ts +108 -0
- package/src/stately/disclosure/useDisclosureState.ts +84 -0
- package/src/stately/form/useFormValidationState.ts +331 -0
- package/src/stately/index.ts +111 -0
- package/src/stately/list/ListCollection.ts +104 -0
- package/src/stately/list/useListState.ts +178 -0
- package/src/stately/list/useSingleSelectListState.ts +101 -0
- package/src/stately/menu/useMenuTriggerState.ts +120 -0
- package/src/stately/menu/useSubmenuTriggerState.ts +116 -0
- package/src/stately/numberfield/useNumberFieldState.ts +430 -0
- package/src/stately/overlays/useOverlayTriggerState.ts +85 -0
- package/src/stately/radio/useRadioGroupState.ts +150 -0
- package/src/stately/searchfield/useSearchFieldState.ts +79 -0
- package/src/stately/select/useSelectState.ts +322 -0
- package/src/stately/selection/Selection.ts +23 -0
- package/src/stately/selection/SelectionManager.ts +541 -0
- package/src/stately/selection/types.ts +117 -0
- package/src/stately/selection/useMultipleSelectionState.ts +178 -0
- package/src/stately/slider/useSliderState.ts +458 -0
- package/src/stately/tabs/useTabListState.ts +149 -0
- package/src/stately/toggle/useToggleGroupState.ts +109 -0
- package/src/stately/toggle/useToggleState.ts +100 -0
- package/src/stately/tooltip/useTooltipTriggerState.ts +218 -0
- package/src/stately/tree/TreeCollection.ts +92 -0
- package/src/stately/tree/useTreeState.ts +121 -0
- package/src/stately/utils/useControlledState.ts +7 -0
- package/src/switch/useSwitch.ts +77 -0
- package/src/tabs/TabsKeyboardDelegate.ts +99 -0
- package/src/tabs/useTab.ts +117 -0
- package/src/tabs/useTabList.ts +105 -0
- package/src/tabs/useTabPanel.ts +75 -0
- package/src/tabs/utils.ts +32 -0
- package/src/tag/useTag.ts +162 -0
- package/src/tag/useTagGroup.ts +192 -0
- package/src/textfield/useFormattedTextField.ts +203 -0
- package/src/textfield/useTextField.ts +248 -0
- package/src/toggle/useToggle.ts +265 -0
- package/src/toolbar/useToolbar.ts +147 -0
- package/src/tooltip/useTooltip.ts +64 -0
- package/src/tooltip/useTooltipTrigger.ts +187 -0
- package/src/utils/filterDOMProps.ts +121 -0
- package/src/utils/getScrollParent.ts +16 -0
- package/src/utils/getScrollParents.ts +20 -0
- package/src/utils/keyboard.ts +38 -0
- package/src/utils/scrollIntoView.ts +178 -0
- package/src/utils/useDeepMemo.ts +30 -0
- package/src/utils/useFormReset.ts +54 -0
- package/src/utils/useId.ts +5 -0
- package/src/utils/useLabels.ts +53 -0
- package/src/utils/useResizeObserver.ts +70 -0
- package/src/utils/useSlot.ts +78 -0
- package/src/utils/useUpdateEffect.ts +54 -0
- package/src/utils/useUpdateLayoutEffect.ts +58 -0
- package/src/visually-hidden/VisuallyHidden.ts +100 -0
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
// Ported from react-aria (source: .react-spectrum/packages/react-aria/src/textfield/useTextField.ts).
|
|
2
|
+
// octane adaptations:
|
|
3
|
+
// - onChange→onInput DOM wiring: the per-keystroke state update rides octane's native
|
|
4
|
+
// `input` event (identical timing to React's synthetic onChange for text inputs). The
|
|
5
|
+
// user's own `onInput` prop still runs first (matching React's plugin order, where
|
|
6
|
+
// onInput fires before onChange), chained ahead of the setter.
|
|
7
|
+
// - The React intrinsic-element generic machinery (JSX.IntrinsicElements lookup types)
|
|
8
|
+
// collapses to a plain 'input' | 'textarea' parameter with structural prop bags.
|
|
9
|
+
// - `FocusableProps` is the ported native-event version; `enterKeyHint` is always the
|
|
10
|
+
// camelCase prop (the React-version probe collapses).
|
|
11
|
+
// - Public-hook slot threading.
|
|
12
|
+
import type {
|
|
13
|
+
AriaLabelingProps,
|
|
14
|
+
AriaValidationProps,
|
|
15
|
+
FocusableDOMProps,
|
|
16
|
+
HelpTextProps,
|
|
17
|
+
InputBase,
|
|
18
|
+
LabelableProps,
|
|
19
|
+
TextInputBase,
|
|
20
|
+
Validation,
|
|
21
|
+
ValidationResult,
|
|
22
|
+
ValueBase,
|
|
23
|
+
RefObject,
|
|
24
|
+
} from '@react-types/shared';
|
|
25
|
+
import { useState } from 'octane';
|
|
26
|
+
|
|
27
|
+
import type { FocusableProps } from '../interactions/useFocusable';
|
|
28
|
+
import { S, splitSlot, subSlot } from '../internal';
|
|
29
|
+
import { chain } from '../utils/chain';
|
|
30
|
+
import { filterDOMProps } from '../utils/filterDOMProps';
|
|
31
|
+
import { getEventTarget } from '../utils/shadowdom/DOMFunctions';
|
|
32
|
+
import { mergeProps } from '../utils/mergeProps';
|
|
33
|
+
import { useControlledState } from '../stately/utils/useControlledState';
|
|
34
|
+
import { useField } from '../label/useField';
|
|
35
|
+
import { useFocusable } from '../interactions/useFocusable';
|
|
36
|
+
import { useFormReset } from '../utils/useFormReset';
|
|
37
|
+
import { useFormValidation } from '../form/useFormValidation';
|
|
38
|
+
import { useFormValidationState } from '../stately/form/useFormValidationState';
|
|
39
|
+
|
|
40
|
+
type DOMAttributes = Record<string, any>;
|
|
41
|
+
type TextFieldIntrinsicElements = 'input' | 'textarea';
|
|
42
|
+
|
|
43
|
+
export interface TextFieldProps
|
|
44
|
+
extends
|
|
45
|
+
InputBase,
|
|
46
|
+
Validation<string>,
|
|
47
|
+
HelpTextProps,
|
|
48
|
+
FocusableProps,
|
|
49
|
+
TextInputBase,
|
|
50
|
+
ValueBase<string>,
|
|
51
|
+
LabelableProps {}
|
|
52
|
+
|
|
53
|
+
export interface AriaTextFieldProps
|
|
54
|
+
extends TextFieldProps, AriaLabelingProps, FocusableDOMProps, AriaValidationProps {
|
|
55
|
+
// https://www.w3.org/TR/wai-aria-1.2/#textbox
|
|
56
|
+
/**
|
|
57
|
+
* Identifies the currently active element when DOM focus is on a composite widget, textbox,
|
|
58
|
+
* group, or application.
|
|
59
|
+
*/
|
|
60
|
+
'aria-activedescendant'?: string;
|
|
61
|
+
/**
|
|
62
|
+
* Indicates whether inputting text could trigger display of one or more predictions of the user's
|
|
63
|
+
* intended value for an input and specifies how predictions would be presented if they are made.
|
|
64
|
+
*/
|
|
65
|
+
'aria-autocomplete'?: 'none' | 'inline' | 'list' | 'both';
|
|
66
|
+
/**
|
|
67
|
+
* Indicates the availability and type of interactive popup element, such as menu or dialog, that
|
|
68
|
+
* can be triggered by an element.
|
|
69
|
+
*/
|
|
70
|
+
'aria-haspopup'?: boolean | 'false' | 'true' | 'menu' | 'listbox' | 'tree' | 'grid' | 'dialog';
|
|
71
|
+
/**
|
|
72
|
+
* Identifies the element (or elements) whose contents or presence are controlled by the current
|
|
73
|
+
* element.
|
|
74
|
+
*/
|
|
75
|
+
'aria-controls'?: string;
|
|
76
|
+
/**
|
|
77
|
+
* An enumerated attribute that defines what action label or icon to preset for the enter key on
|
|
78
|
+
* virtual keyboards. See
|
|
79
|
+
* [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/enterkeyhint).
|
|
80
|
+
*/
|
|
81
|
+
enterKeyHint?: 'enter' | 'done' | 'go' | 'next' | 'previous' | 'search' | 'send';
|
|
82
|
+
// octane adaptation: the text-input DOM props are structural (upstream's
|
|
83
|
+
// TextInputDOMProps drags React handler types).
|
|
84
|
+
type?: string;
|
|
85
|
+
pattern?: string;
|
|
86
|
+
autoComplete?: string;
|
|
87
|
+
autoCapitalize?: 'off' | 'none' | 'on' | 'sentences' | 'words' | 'characters';
|
|
88
|
+
maxLength?: number;
|
|
89
|
+
minLength?: number;
|
|
90
|
+
name?: string;
|
|
91
|
+
form?: string;
|
|
92
|
+
placeholder?: string;
|
|
93
|
+
inputMode?: string;
|
|
94
|
+
autoCorrect?: string;
|
|
95
|
+
spellCheck?: string | boolean;
|
|
96
|
+
onCopy?: (e: ClipboardEvent) => void;
|
|
97
|
+
onCut?: (e: ClipboardEvent) => void;
|
|
98
|
+
onPaste?: (e: ClipboardEvent) => void;
|
|
99
|
+
onCompositionEnd?: (e: CompositionEvent) => void;
|
|
100
|
+
onCompositionStart?: (e: CompositionEvent) => void;
|
|
101
|
+
onCompositionUpdate?: (e: CompositionEvent) => void;
|
|
102
|
+
onSelect?: (e: Event) => void;
|
|
103
|
+
onBeforeInput?: (e: InputEvent) => void;
|
|
104
|
+
onInput?: (e: Event) => void;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export interface AriaTextFieldOptions<
|
|
108
|
+
T extends TextFieldIntrinsicElements = 'input',
|
|
109
|
+
> extends AriaTextFieldProps {
|
|
110
|
+
/**
|
|
111
|
+
* The HTML element used to render the input, e.g. 'input', or 'textarea'. It determines whether
|
|
112
|
+
* certain HTML attributes will be included in `inputProps`. For example,
|
|
113
|
+
* [`type`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-type).
|
|
114
|
+
*
|
|
115
|
+
* @default 'input'
|
|
116
|
+
*/
|
|
117
|
+
inputElementType?: T;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export interface TextFieldAria extends ValidationResult {
|
|
121
|
+
/** Props for the input element. */
|
|
122
|
+
inputProps: DOMAttributes;
|
|
123
|
+
/** Props for the text field's visible label element, if any. */
|
|
124
|
+
labelProps: DOMAttributes;
|
|
125
|
+
/** Props for the text field's description element, if any. */
|
|
126
|
+
descriptionProps: DOMAttributes;
|
|
127
|
+
/** Props for the text field's error message element, if any. */
|
|
128
|
+
errorMessageProps: DOMAttributes;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Provides the behavior and accessibility implementation for a text field.
|
|
133
|
+
*/
|
|
134
|
+
export function useTextField<T extends TextFieldIntrinsicElements = 'input'>(
|
|
135
|
+
props: AriaTextFieldOptions<T>,
|
|
136
|
+
ref: RefObject<HTMLInputElement | HTMLTextAreaElement | null>,
|
|
137
|
+
): TextFieldAria;
|
|
138
|
+
// Slot-threading form: sibling ported hooks pass their derived sub-slot as the trailing arg.
|
|
139
|
+
export function useTextField<T extends TextFieldIntrinsicElements = 'input'>(
|
|
140
|
+
props: AriaTextFieldOptions<T>,
|
|
141
|
+
ref: RefObject<HTMLInputElement | HTMLTextAreaElement | null>,
|
|
142
|
+
slot: symbol | undefined,
|
|
143
|
+
): TextFieldAria;
|
|
144
|
+
export function useTextField(...args: any[]): TextFieldAria {
|
|
145
|
+
const [user, slotArg] = splitSlot(args);
|
|
146
|
+
const slot = slotArg ?? S('useTextField');
|
|
147
|
+
const props = user[0] as AriaTextFieldOptions<any>;
|
|
148
|
+
const ref = user[1] as RefObject<HTMLInputElement | HTMLTextAreaElement | null>;
|
|
149
|
+
|
|
150
|
+
let {
|
|
151
|
+
inputElementType = 'input',
|
|
152
|
+
isDisabled = false,
|
|
153
|
+
isRequired = false,
|
|
154
|
+
isReadOnly = false,
|
|
155
|
+
type = 'text',
|
|
156
|
+
validationBehavior = 'aria',
|
|
157
|
+
} = props as any;
|
|
158
|
+
let [value, setValue] = useControlledState<string>(
|
|
159
|
+
props.value,
|
|
160
|
+
props.defaultValue || '',
|
|
161
|
+
props.onChange,
|
|
162
|
+
subSlot(slot, 'value'),
|
|
163
|
+
);
|
|
164
|
+
let { focusableProps } = useFocusable(props, ref, subSlot(slot, 'focusable'));
|
|
165
|
+
let validationState = useFormValidationState(
|
|
166
|
+
{
|
|
167
|
+
...props,
|
|
168
|
+
value,
|
|
169
|
+
},
|
|
170
|
+
subSlot(slot, 'validationState'),
|
|
171
|
+
);
|
|
172
|
+
let { isInvalid, validationErrors, validationDetails } = validationState.displayValidation;
|
|
173
|
+
let { labelProps, fieldProps, descriptionProps, errorMessageProps } = useField(
|
|
174
|
+
{
|
|
175
|
+
...props,
|
|
176
|
+
isInvalid,
|
|
177
|
+
errorMessage: props.errorMessage || validationErrors,
|
|
178
|
+
},
|
|
179
|
+
subSlot(slot, 'field'),
|
|
180
|
+
);
|
|
181
|
+
let domProps = filterDOMProps(props, { labelable: true });
|
|
182
|
+
|
|
183
|
+
const inputOnlyProps = {
|
|
184
|
+
type,
|
|
185
|
+
pattern: props.pattern,
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
let [initialValue] = useState(value, subSlot(slot, 'initialValue'));
|
|
189
|
+
useFormReset(ref, props.defaultValue ?? initialValue, setValue, subSlot(slot, 'reset'));
|
|
190
|
+
useFormValidation(props, validationState, ref, subSlot(slot, 'validation'));
|
|
191
|
+
|
|
192
|
+
return {
|
|
193
|
+
labelProps,
|
|
194
|
+
inputProps: mergeProps(domProps, inputElementType === 'input' ? inputOnlyProps : undefined, {
|
|
195
|
+
disabled: isDisabled,
|
|
196
|
+
readOnly: isReadOnly,
|
|
197
|
+
required: isRequired && validationBehavior === 'native',
|
|
198
|
+
'aria-required': (isRequired && validationBehavior === 'aria') || undefined,
|
|
199
|
+
'aria-invalid': isInvalid || undefined,
|
|
200
|
+
'aria-errormessage': props['aria-errormessage'],
|
|
201
|
+
'aria-activedescendant': props['aria-activedescendant'],
|
|
202
|
+
'aria-autocomplete': props['aria-autocomplete'],
|
|
203
|
+
'aria-haspopup': props['aria-haspopup'],
|
|
204
|
+
'aria-controls': props['aria-controls'],
|
|
205
|
+
value,
|
|
206
|
+
// octane adaptation: the state update rides the native `input` event (no
|
|
207
|
+
// synthetic onChange); the user's own onInput runs first, matching React's
|
|
208
|
+
// dispatch order for the same native event.
|
|
209
|
+
onInput: chain(props.onInput, (e: Event) =>
|
|
210
|
+
setValue((getEventTarget(e) as HTMLInputElement).value),
|
|
211
|
+
),
|
|
212
|
+
autoComplete: props.autoComplete,
|
|
213
|
+
autoCapitalize: props.autoCapitalize,
|
|
214
|
+
maxLength: props.maxLength,
|
|
215
|
+
minLength: props.minLength,
|
|
216
|
+
name: props.name,
|
|
217
|
+
form: props.form,
|
|
218
|
+
placeholder: props.placeholder,
|
|
219
|
+
inputMode: props.inputMode,
|
|
220
|
+
autoCorrect: props.autoCorrect,
|
|
221
|
+
spellCheck: props.spellCheck,
|
|
222
|
+
enterKeyHint: props.enterKeyHint,
|
|
223
|
+
|
|
224
|
+
// Clipboard events
|
|
225
|
+
onCopy: props.onCopy,
|
|
226
|
+
onCut: props.onCut,
|
|
227
|
+
onPaste: props.onPaste,
|
|
228
|
+
|
|
229
|
+
// Composition events
|
|
230
|
+
onCompositionEnd: props.onCompositionEnd,
|
|
231
|
+
onCompositionStart: props.onCompositionStart,
|
|
232
|
+
onCompositionUpdate: props.onCompositionUpdate,
|
|
233
|
+
|
|
234
|
+
// Selection events
|
|
235
|
+
onSelect: props.onSelect,
|
|
236
|
+
|
|
237
|
+
// Input events
|
|
238
|
+
onBeforeInput: props.onBeforeInput,
|
|
239
|
+
...focusableProps,
|
|
240
|
+
...fieldProps,
|
|
241
|
+
}),
|
|
242
|
+
descriptionProps,
|
|
243
|
+
errorMessageProps,
|
|
244
|
+
isInvalid,
|
|
245
|
+
validationErrors,
|
|
246
|
+
validationDetails,
|
|
247
|
+
};
|
|
248
|
+
}
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
// Ported from react-aria (source: .react-spectrum/packages/react-aria/src/toggle/useToggle.ts).
|
|
2
|
+
// octane adaptations:
|
|
3
|
+
// - `react-stately/*` imports → the ported `../stately/*` modules.
|
|
4
|
+
// - DOM wiring uses octane's native `onInput` prop instead of React's synthetic `onChange`
|
|
5
|
+
// (for checkbox-type inputs the native `input` event fires on every toggle, matching the
|
|
6
|
+
// upstream timing); `React.ChangeEvent` reads become the native event via
|
|
7
|
+
// `getEventTarget`. Public value-level `onChange` callbacks are unchanged.
|
|
8
|
+
// - React's `LabelHTMLAttributes`/`InputHTMLAttributes`/`DOMAttributesWithRef` prop-bag
|
|
9
|
+
// types → a local structural `DOMAttributes` (octane native event props).
|
|
10
|
+
// - The dev-only "you must specify an aria-label" console.warn (and the locals that only
|
|
11
|
+
// fed it) is not ported (repo policy: no dev-only console warnings).
|
|
12
|
+
// - Public-hook slot threading (splitSlot/subSlot) per the binding convention.
|
|
13
|
+
import type {
|
|
14
|
+
AriaLabelingProps,
|
|
15
|
+
AriaValidationProps,
|
|
16
|
+
FocusableDOMProps,
|
|
17
|
+
InputDOMProps,
|
|
18
|
+
PressEvents,
|
|
19
|
+
RefObject,
|
|
20
|
+
ValidationResult,
|
|
21
|
+
} from '@react-types/shared';
|
|
22
|
+
import { useState } from 'octane';
|
|
23
|
+
|
|
24
|
+
import { S, splitSlot, subSlot } from '../internal';
|
|
25
|
+
import { filterDOMProps } from '../utils/filterDOMProps';
|
|
26
|
+
import { getEventTarget } from '../utils/shadowdom/DOMFunctions';
|
|
27
|
+
import { mergeProps } from '../utils/mergeProps';
|
|
28
|
+
import {
|
|
29
|
+
privateValidationStateProp,
|
|
30
|
+
useFormValidationState,
|
|
31
|
+
} from '../stately/form/useFormValidationState';
|
|
32
|
+
import type { ToggleProps, ToggleState } from '../stately/toggle/useToggleState';
|
|
33
|
+
import { useFocusable } from '../interactions/useFocusable';
|
|
34
|
+
import { useFormReset } from '../utils/useFormReset';
|
|
35
|
+
import { useFormValidation } from '../form/useFormValidation';
|
|
36
|
+
import { usePress } from '../interactions/usePress';
|
|
37
|
+
import { useSlotId2 } from '../utils/useSlot';
|
|
38
|
+
|
|
39
|
+
// octane adaptation: minimal structural prop bag (upstream's drags React attribute types).
|
|
40
|
+
type DOMAttributes = Record<string, any>;
|
|
41
|
+
|
|
42
|
+
export interface AriaToggleProps
|
|
43
|
+
extends
|
|
44
|
+
ToggleProps,
|
|
45
|
+
FocusableDOMProps,
|
|
46
|
+
AriaLabelingProps,
|
|
47
|
+
AriaValidationProps,
|
|
48
|
+
InputDOMProps,
|
|
49
|
+
Omit<PressEvents, 'onClick'> {
|
|
50
|
+
/**
|
|
51
|
+
* **Not recommended – use `onPress` instead.** octane adaptation: native MouseEvent
|
|
52
|
+
* (upstream's `PressEvents.onClick` is typed over React's synthetic event).
|
|
53
|
+
*/
|
|
54
|
+
onClick?: (e: MouseEvent) => void;
|
|
55
|
+
/**
|
|
56
|
+
* Identifies the element (or elements) whose contents or presence are controlled by the current
|
|
57
|
+
* element.
|
|
58
|
+
*/
|
|
59
|
+
'aria-controls'?: string;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface ToggleAria extends ValidationResult {
|
|
63
|
+
/** Props to be spread on the label element. */
|
|
64
|
+
labelProps: DOMAttributes;
|
|
65
|
+
/** Props to be spread on the input element. */
|
|
66
|
+
inputProps: DOMAttributes;
|
|
67
|
+
/** Props for the checkbox description element, if any. */
|
|
68
|
+
descriptionProps: DOMAttributes;
|
|
69
|
+
/** Props for the checkbox error message element, if any. */
|
|
70
|
+
errorMessageProps: DOMAttributes;
|
|
71
|
+
/** Whether the toggle is selected. */
|
|
72
|
+
isSelected: boolean;
|
|
73
|
+
/** Whether the toggle is in a pressed state. */
|
|
74
|
+
isPressed: boolean;
|
|
75
|
+
/** Whether the toggle is disabled. */
|
|
76
|
+
isDisabled: boolean;
|
|
77
|
+
/** Whether the toggle is read only. */
|
|
78
|
+
isReadOnly: boolean;
|
|
79
|
+
/** Whether the toggle is invalid. */
|
|
80
|
+
isInvalid: boolean;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Handles interactions for toggle elements, e.g. Checkboxes and Switches.
|
|
85
|
+
*/
|
|
86
|
+
export function useToggle(
|
|
87
|
+
props: AriaToggleProps,
|
|
88
|
+
state: ToggleState,
|
|
89
|
+
ref: RefObject<HTMLInputElement | null>,
|
|
90
|
+
): ToggleAria;
|
|
91
|
+
// Slot-threading form: sibling ported hooks pass their derived sub-slot as the trailing arg.
|
|
92
|
+
export function useToggle(
|
|
93
|
+
props: AriaToggleProps,
|
|
94
|
+
state: ToggleState,
|
|
95
|
+
ref: RefObject<HTMLInputElement | null>,
|
|
96
|
+
slot: symbol | undefined,
|
|
97
|
+
): ToggleAria;
|
|
98
|
+
export function useToggle(...args: any[]): ToggleAria {
|
|
99
|
+
const [user, slotArg] = splitSlot(args);
|
|
100
|
+
const slot = slotArg ?? S('useToggle');
|
|
101
|
+
const props = user[0] as AriaToggleProps;
|
|
102
|
+
const state = user[1] as ToggleState;
|
|
103
|
+
const ref = user[2] as RefObject<HTMLInputElement | null>;
|
|
104
|
+
|
|
105
|
+
let {
|
|
106
|
+
isDisabled = false,
|
|
107
|
+
isReadOnly = false,
|
|
108
|
+
value,
|
|
109
|
+
name,
|
|
110
|
+
form,
|
|
111
|
+
isRequired,
|
|
112
|
+
validationBehavior = 'aria',
|
|
113
|
+
'aria-describedby': ariaDescribedby,
|
|
114
|
+
onPressStart,
|
|
115
|
+
onPressEnd,
|
|
116
|
+
onPressChange,
|
|
117
|
+
onPress,
|
|
118
|
+
onPressUp,
|
|
119
|
+
onClick,
|
|
120
|
+
} = props;
|
|
121
|
+
|
|
122
|
+
// Create validation state here because it doesn't make sense to add to general useToggleState.
|
|
123
|
+
let validationState = useFormValidationState(
|
|
124
|
+
{ ...props, value: state.isSelected },
|
|
125
|
+
subSlot(slot, 'validation'),
|
|
126
|
+
);
|
|
127
|
+
let { isInvalid, validationErrors, validationDetails } = validationState.displayValidation;
|
|
128
|
+
|
|
129
|
+
useFormValidation(props, validationState, ref, subSlot(slot, 'formValidation'));
|
|
130
|
+
|
|
131
|
+
let onInput = (e: Event) => {
|
|
132
|
+
// since we spread props on label, onInput will end up there as well as in here.
|
|
133
|
+
// so we have to stop propagation at the lowest level that we care about
|
|
134
|
+
e.stopPropagation();
|
|
135
|
+
state.setSelected((getEventTarget(e) as HTMLInputElement).checked);
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
// Handle press state for keyboard interactions and cases where labelProps is not used.
|
|
139
|
+
let { pressProps, isPressed } = usePress(
|
|
140
|
+
{
|
|
141
|
+
onPressStart,
|
|
142
|
+
onPressEnd,
|
|
143
|
+
onPressChange,
|
|
144
|
+
onPress,
|
|
145
|
+
onPressUp,
|
|
146
|
+
onClick,
|
|
147
|
+
isDisabled,
|
|
148
|
+
},
|
|
149
|
+
subSlot(slot, 'press'),
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
// Handle press state on the label.
|
|
153
|
+
let [isLabelPressed, setLabelPressed] = useState(false, subSlot(slot, 'labelPressed'));
|
|
154
|
+
let { pressProps: labelProps } = usePress(
|
|
155
|
+
{
|
|
156
|
+
onPressStart(e) {
|
|
157
|
+
// Keyboard interactions are handled directly on the input.
|
|
158
|
+
if (e.pointerType === 'keyboard' || e.pointerType === 'virtual') {
|
|
159
|
+
e.continuePropagation();
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
onPressStart?.(e);
|
|
164
|
+
onPressChange?.(true);
|
|
165
|
+
setLabelPressed(true);
|
|
166
|
+
},
|
|
167
|
+
onPressEnd(e) {
|
|
168
|
+
// Keyboard interactions are handled directly on the input.
|
|
169
|
+
if (e.pointerType === 'keyboard' || e.pointerType === 'virtual') {
|
|
170
|
+
e.continuePropagation();
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
onPressEnd?.(e);
|
|
175
|
+
onPressChange?.(false);
|
|
176
|
+
setLabelPressed(false);
|
|
177
|
+
},
|
|
178
|
+
onPressUp(e) {
|
|
179
|
+
if (e.pointerType === 'keyboard' || e.pointerType === 'virtual') {
|
|
180
|
+
e.continuePropagation();
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
onPressUp?.(e);
|
|
185
|
+
},
|
|
186
|
+
onClick,
|
|
187
|
+
onPress(e) {
|
|
188
|
+
if (e.pointerType === 'keyboard' || e.pointerType === 'virtual') {
|
|
189
|
+
e.continuePropagation();
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
onPress?.(e);
|
|
194
|
+
state.toggle();
|
|
195
|
+
ref.current?.focus();
|
|
196
|
+
|
|
197
|
+
let groupValidationState = (props as any)[privateValidationStateProp];
|
|
198
|
+
|
|
199
|
+
let { commitValidation } = groupValidationState ? groupValidationState : validationState;
|
|
200
|
+
|
|
201
|
+
commitValidation();
|
|
202
|
+
},
|
|
203
|
+
isDisabled: isDisabled || isReadOnly,
|
|
204
|
+
},
|
|
205
|
+
subSlot(slot, 'labelPress'),
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
let { focusableProps } = useFocusable(props, ref, subSlot(slot, 'focusable'));
|
|
209
|
+
let interactions = mergeProps(pressProps, focusableProps);
|
|
210
|
+
let domProps = filterDOMProps(props, { labelable: true });
|
|
211
|
+
|
|
212
|
+
useFormReset(ref, state.defaultSelected, state.setSelected, subSlot(slot, 'formReset'));
|
|
213
|
+
|
|
214
|
+
// Copied from useField because we don't want the label behavior that provides.
|
|
215
|
+
let descriptionProps = useSlotId2(undefined, subSlot(slot, 'description'));
|
|
216
|
+
let errorMessageProps = useSlotId2(undefined, subSlot(slot, 'errorMessage'));
|
|
217
|
+
|
|
218
|
+
return {
|
|
219
|
+
labelProps: mergeProps(labelProps, {
|
|
220
|
+
// octane adaptation: upstream preventDefaults ALL label clicks to stop the label's
|
|
221
|
+
// built-in click-forwarding (a second, doubled activation) — React's synthetic
|
|
222
|
+
// onChange still fires either way, and the controlled `checked` prop papers over
|
|
223
|
+
// the cancelled DOM activation. With native events, an unconditional preventDefault
|
|
224
|
+
// would ALSO cancel the input's own activation (no `input` event → no toggle) for
|
|
225
|
+
// virtual clicks (element.click(), assistive tech). Scope it to clicks that do NOT
|
|
226
|
+
// target the input: label-body clicks still can't double-forward, input-targeted
|
|
227
|
+
// clicks activate natively. Real pointer gestures toggle via the label press path
|
|
228
|
+
// on both sides; virtual label-body clicks toggle on NEITHER side (React's
|
|
229
|
+
// onChange only fires for clicks targeting the input).
|
|
230
|
+
onClick: (e: MouseEvent) => {
|
|
231
|
+
if (getEventTarget(e) !== ref.current) {
|
|
232
|
+
e.preventDefault();
|
|
233
|
+
}
|
|
234
|
+
},
|
|
235
|
+
}),
|
|
236
|
+
inputProps: mergeProps(domProps, {
|
|
237
|
+
checked: state.isSelected,
|
|
238
|
+
'aria-required': (isRequired && validationBehavior === 'aria') || undefined,
|
|
239
|
+
required: isRequired && validationBehavior === 'native',
|
|
240
|
+
'aria-invalid': isInvalid || props.validationState === 'invalid' || undefined,
|
|
241
|
+
'aria-errormessage': props['aria-errormessage'],
|
|
242
|
+
'aria-controls': props['aria-controls'],
|
|
243
|
+
'aria-readonly': isReadOnly || undefined,
|
|
244
|
+
'aria-describedby':
|
|
245
|
+
[descriptionProps.id, errorMessageProps.id, ariaDescribedby].filter(Boolean).join(' ') ||
|
|
246
|
+
undefined,
|
|
247
|
+
onInput,
|
|
248
|
+
disabled: isDisabled,
|
|
249
|
+
...(value == null ? {} : { value }),
|
|
250
|
+
name,
|
|
251
|
+
form,
|
|
252
|
+
type: 'checkbox',
|
|
253
|
+
...interactions,
|
|
254
|
+
}),
|
|
255
|
+
descriptionProps,
|
|
256
|
+
errorMessageProps,
|
|
257
|
+
isSelected: state.isSelected,
|
|
258
|
+
isPressed: isPressed || isLabelPressed,
|
|
259
|
+
isDisabled,
|
|
260
|
+
isReadOnly,
|
|
261
|
+
isInvalid: isInvalid || props.validationState === 'invalid',
|
|
262
|
+
validationErrors,
|
|
263
|
+
validationDetails,
|
|
264
|
+
};
|
|
265
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
// Ported from react-aria (source: .react-spectrum/packages/react-aria/src/toolbar/useToolbar.ts).
|
|
2
|
+
// octane adaptations: React's FocusEventHandler/KeyboardEventHandler → native event
|
|
3
|
+
// handlers (octane delegates the *Capture props natively); public-hook slot threading.
|
|
4
|
+
import type { AriaLabelingProps, Orientation, RefObject } from '@react-types/shared';
|
|
5
|
+
import { useRef, useState } from 'octane';
|
|
6
|
+
|
|
7
|
+
import { S, splitSlot, subSlot } from '../internal';
|
|
8
|
+
import { createFocusManager } from '../focus/FocusScope';
|
|
9
|
+
import { filterDOMProps } from '../utils/filterDOMProps';
|
|
10
|
+
import { getActiveElement, getEventTarget, nodeContains } from '../utils/shadowdom/DOMFunctions';
|
|
11
|
+
import { useLayoutEffect } from '../utils/useLayoutEffect';
|
|
12
|
+
import { useLocale } from '../i18n/I18nProvider';
|
|
13
|
+
|
|
14
|
+
export interface AriaToolbarProps extends AriaLabelingProps {
|
|
15
|
+
/**
|
|
16
|
+
* The orientation of the entire toolbar.
|
|
17
|
+
*
|
|
18
|
+
* @default 'horizontal'
|
|
19
|
+
*/
|
|
20
|
+
orientation?: Orientation;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface ToolbarAria {
|
|
24
|
+
/**
|
|
25
|
+
* Props for the toolbar container.
|
|
26
|
+
*/
|
|
27
|
+
toolbarProps: Record<string, any>;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function useToolbar(
|
|
31
|
+
props: AriaToolbarProps,
|
|
32
|
+
ref: RefObject<HTMLElement | null>,
|
|
33
|
+
): ToolbarAria;
|
|
34
|
+
// Slot-threading form: sibling ported hooks pass their derived sub-slot as the trailing arg.
|
|
35
|
+
export function useToolbar(
|
|
36
|
+
props: AriaToolbarProps,
|
|
37
|
+
ref: RefObject<HTMLElement | null>,
|
|
38
|
+
slot: symbol | undefined,
|
|
39
|
+
): ToolbarAria;
|
|
40
|
+
export function useToolbar(...args: any[]): ToolbarAria {
|
|
41
|
+
const [user, slotArg] = splitSlot(args);
|
|
42
|
+
const slot = slotArg ?? S('useToolbar');
|
|
43
|
+
const props = user[0] as AriaToolbarProps;
|
|
44
|
+
const ref = user[1] as RefObject<HTMLElement | null>;
|
|
45
|
+
|
|
46
|
+
const {
|
|
47
|
+
'aria-label': ariaLabel,
|
|
48
|
+
'aria-labelledby': ariaLabelledBy,
|
|
49
|
+
orientation = 'horizontal',
|
|
50
|
+
} = props;
|
|
51
|
+
let [isInToolbar, setInToolbar] = useState(false, subSlot(slot, 'inToolbar'));
|
|
52
|
+
// should be safe because re-calling set state with the same value it already has is a no-op
|
|
53
|
+
// this will allow us to react should a parent re-render and change its role though
|
|
54
|
+
useLayoutEffect(
|
|
55
|
+
() => {
|
|
56
|
+
setInToolbar(!!(ref.current && ref.current.parentElement?.closest('[role="toolbar"]')));
|
|
57
|
+
},
|
|
58
|
+
null,
|
|
59
|
+
subSlot(slot, 'detect'),
|
|
60
|
+
);
|
|
61
|
+
const { direction } = useLocale(subSlot(slot, 'locale'));
|
|
62
|
+
const shouldReverse = direction === 'rtl' && orientation === 'horizontal';
|
|
63
|
+
let focusManager = createFocusManager(ref);
|
|
64
|
+
|
|
65
|
+
const onKeyDown = (e: KeyboardEvent) => {
|
|
66
|
+
// don't handle portalled events
|
|
67
|
+
if (!nodeContains(e.currentTarget as Element, getEventTarget(e) as HTMLElement)) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (
|
|
71
|
+
(orientation === 'horizontal' && e.key === 'ArrowRight') ||
|
|
72
|
+
(orientation === 'vertical' && e.key === 'ArrowDown')
|
|
73
|
+
) {
|
|
74
|
+
if (shouldReverse) {
|
|
75
|
+
focusManager.focusPrevious();
|
|
76
|
+
} else {
|
|
77
|
+
focusManager.focusNext();
|
|
78
|
+
}
|
|
79
|
+
} else if (
|
|
80
|
+
(orientation === 'horizontal' && e.key === 'ArrowLeft') ||
|
|
81
|
+
(orientation === 'vertical' && e.key === 'ArrowUp')
|
|
82
|
+
) {
|
|
83
|
+
if (shouldReverse) {
|
|
84
|
+
focusManager.focusNext();
|
|
85
|
+
} else {
|
|
86
|
+
focusManager.focusPrevious();
|
|
87
|
+
}
|
|
88
|
+
} else if (e.key === 'Tab') {
|
|
89
|
+
// When the tab key is pressed, we want to move focus
|
|
90
|
+
// out of the entire toolbar. To do this, move focus
|
|
91
|
+
// to the first or last focusable child, and let the
|
|
92
|
+
// browser handle the Tab key as usual from there.
|
|
93
|
+
lastFocused.current = getActiveElement() as HTMLElement;
|
|
94
|
+
if (e.shiftKey) {
|
|
95
|
+
focusManager.focusFirst();
|
|
96
|
+
} else {
|
|
97
|
+
focusManager.focusLast();
|
|
98
|
+
}
|
|
99
|
+
return;
|
|
100
|
+
} else {
|
|
101
|
+
// if we didn't handle anything, return early so we don't preventDefault
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Prevent arrow keys from being handled by nested action groups.
|
|
106
|
+
e.stopPropagation();
|
|
107
|
+
e.preventDefault();
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
// Record the last focused child when focus moves out of the toolbar.
|
|
111
|
+
const lastFocused = useRef<HTMLElement | null>(null, subSlot(slot, 'lastFocused'));
|
|
112
|
+
const onBlur = (e: FocusEvent) => {
|
|
113
|
+
if (
|
|
114
|
+
!nodeContains(e.currentTarget as Element, e.relatedTarget as Element) &&
|
|
115
|
+
!lastFocused.current
|
|
116
|
+
) {
|
|
117
|
+
lastFocused.current = getEventTarget(e) as HTMLElement;
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
// Restore focus to the last focused child when focus returns into the toolbar.
|
|
122
|
+
// If the element was removed, do nothing, either the first item in the first group,
|
|
123
|
+
// or the last item in the last group will be focused, depending on direction.
|
|
124
|
+
const onFocus = (e: FocusEvent) => {
|
|
125
|
+
if (
|
|
126
|
+
lastFocused.current &&
|
|
127
|
+
!nodeContains(e.currentTarget as Element, e.relatedTarget as Element) &&
|
|
128
|
+
nodeContains(ref.current, getEventTarget(e) as Element)
|
|
129
|
+
) {
|
|
130
|
+
lastFocused.current?.focus();
|
|
131
|
+
lastFocused.current = null;
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
return {
|
|
136
|
+
toolbarProps: {
|
|
137
|
+
...filterDOMProps(props, { labelable: true }),
|
|
138
|
+
role: !isInToolbar ? 'toolbar' : 'group',
|
|
139
|
+
'aria-orientation': orientation,
|
|
140
|
+
'aria-label': ariaLabel,
|
|
141
|
+
'aria-labelledby': ariaLabel == null ? ariaLabelledBy : undefined,
|
|
142
|
+
onKeyDownCapture: !isInToolbar ? onKeyDown : undefined,
|
|
143
|
+
onFocusCapture: !isInToolbar ? onFocus : undefined,
|
|
144
|
+
onBlurCapture: !isInToolbar ? onBlur : undefined,
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
}
|