smarthr-ui 97.0.0 → 98.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/components/AppHeader/components/common/AppLauncherFeatures.cjs +1 -1
- package/lib/components/AppHeader/components/common/AppLauncherFeatures.js +1 -1
- package/lib/components/AppHeader/components/common/AppLauncherSortDropdown.cjs +1 -1
- package/lib/components/AppHeader/components/common/AppLauncherSortDropdown.js +1 -1
- package/lib/components/AppHeader/components/desktop/AppLauncher.cjs +5 -5
- package/lib/components/AppHeader/components/desktop/AppLauncher.cjs.map +1 -1
- package/lib/components/AppHeader/components/desktop/AppLauncher.js +5 -5
- package/lib/components/AppHeader/components/desktop/AppLauncher.js.map +1 -1
- package/lib/components/AppHeader/components/desktop/DesktopHeader.cjs +1 -1
- package/lib/components/AppHeader/components/desktop/DesktopHeader.js +1 -1
- package/lib/components/AppHeader/components/desktop/ReleaseNotesDropdown.cjs +1 -1
- package/lib/components/AppHeader/components/desktop/ReleaseNotesDropdown.js +1 -1
- package/lib/components/AppHeader/components/desktop/UserInfo.cjs +1 -1
- package/lib/components/AppHeader/components/desktop/UserInfo.js +1 -1
- package/lib/components/AppHeader/components/mobile/AppLauncher.cjs +1 -1
- package/lib/components/AppHeader/components/mobile/AppLauncher.js +1 -1
- package/lib/components/AppHeader/components/mobile/AppLauncherFilterDropdown.cjs +1 -1
- package/lib/components/AppHeader/components/mobile/AppLauncherFilterDropdown.js +1 -1
- package/lib/components/AppHeader/components/mobile/Help.cjs +1 -1
- package/lib/components/AppHeader/components/mobile/Help.js +1 -1
- package/lib/components/AppHeader/components/mobile/LanguageSelector.cjs +1 -1
- package/lib/components/AppHeader/components/mobile/LanguageSelector.js +1 -1
- package/lib/components/AppHeader/components/mobile/Menu.cjs +1 -1
- package/lib/components/AppHeader/components/mobile/Menu.js +1 -1
- package/lib/components/AppHeader/components/mobile/MenuAccordion.cjs +1 -1
- package/lib/components/AppHeader/components/mobile/MenuAccordion.js +1 -1
- package/lib/components/AppHeader/components/mobile/MenuDialog.cjs +2 -2
- package/lib/components/AppHeader/components/mobile/MenuDialog.js +2 -2
- package/lib/components/AppHeader/components/mobile/MenuSubHeading.cjs +1 -1
- package/lib/components/AppHeader/components/mobile/MenuSubHeading.js +1 -1
- package/lib/components/AppHeader/components/mobile/ReleaseNote.cjs +1 -1
- package/lib/components/AppHeader/components/mobile/ReleaseNote.js +1 -1
- package/lib/components/AppHeader/components/mobile/UserInfo.cjs +2 -2
- package/lib/components/AppHeader/components/mobile/UserInfo.js +2 -2
- package/lib/components/Browser/Browser.cjs +11 -7
- package/lib/components/Browser/Browser.cjs.map +1 -1
- package/lib/components/Browser/Browser.js +12 -8
- package/lib/components/Browser/Browser.js.map +1 -1
- package/lib/components/Calendar/Calendar.cjs +1 -1
- package/lib/components/Calendar/Calendar.js +1 -1
- package/lib/components/Calendar/CalendarTable.cjs +1 -1
- package/lib/components/Calendar/CalendarTable.js +1 -1
- package/lib/components/Calendar/YearPicker.cjs +1 -1
- package/lib/components/Calendar/YearPicker.js +1 -1
- package/lib/components/Combobox/ItemButton.cjs +27 -26
- package/lib/components/Combobox/ItemButton.cjs.map +1 -1
- package/lib/components/Combobox/ItemButton.js +28 -27
- package/lib/components/Combobox/ItemButton.js.map +1 -1
- package/lib/components/Combobox/MultiCombobox/MultiCombobox.cjs +122 -134
- package/lib/components/Combobox/MultiCombobox/MultiCombobox.cjs.map +1 -1
- package/lib/components/Combobox/MultiCombobox/MultiCombobox.js +123 -135
- package/lib/components/Combobox/MultiCombobox/MultiCombobox.js.map +1 -1
- package/lib/components/Combobox/MultiCombobox/MultiSelectedItem.cjs +1 -1
- package/lib/components/Combobox/MultiCombobox/MultiSelectedItem.js +1 -1
- package/lib/components/Combobox/SingleCombobox/SingleCombobox.cjs +79 -46
- package/lib/components/Combobox/SingleCombobox/SingleCombobox.cjs.map +1 -1
- package/lib/components/Combobox/SingleCombobox/SingleCombobox.js +81 -48
- package/lib/components/Combobox/SingleCombobox/SingleCombobox.js.map +1 -1
- package/lib/components/Combobox/useListbox.cjs +1 -1
- package/lib/components/Combobox/useListbox.js +1 -1
- package/lib/components/Combobox/useOptions.cjs +3 -1
- package/lib/components/Combobox/useOptions.cjs.map +1 -1
- package/lib/components/Combobox/useOptions.js +4 -2
- package/lib/components/Combobox/useOptions.js.map +1 -1
- package/lib/components/Dialog/ControlledActionDialog/ActionDialogContentInner.cjs +3 -3
- package/lib/components/Dialog/ControlledActionDialog/ActionDialogContentInner.cjs.map +1 -1
- package/lib/components/Dialog/ControlledActionDialog/ActionDialogContentInner.js +3 -3
- package/lib/components/Dialog/ControlledActionDialog/ActionDialogContentInner.js.map +1 -1
- package/lib/components/Dialog/ControlledFormDialog/FormDialogContentInner.cjs +3 -3
- package/lib/components/Dialog/ControlledFormDialog/FormDialogContentInner.cjs.map +1 -1
- package/lib/components/Dialog/ControlledFormDialog/FormDialogContentInner.js +3 -3
- package/lib/components/Dialog/ControlledFormDialog/FormDialogContentInner.js.map +1 -1
- package/lib/components/Dialog/ControlledMessageDialog/MessageDialogContentInner.cjs +1 -1
- package/lib/components/Dialog/ControlledMessageDialog/MessageDialogContentInner.js +1 -1
- package/lib/components/Dialog/ControlledStepFormDialog/ControlledStepFormDialog.cjs +1 -1
- package/lib/components/Dialog/ControlledStepFormDialog/ControlledStepFormDialog.js +1 -1
- package/lib/components/Dialog/ControlledStepFormDialog/StepFormDialogContentInner.cjs +2 -2
- package/lib/components/Dialog/ControlledStepFormDialog/StepFormDialogContentInner.cjs.map +1 -1
- package/lib/components/Dialog/ControlledStepFormDialog/StepFormDialogContentInner.js +2 -2
- package/lib/components/Dialog/ControlledStepFormDialog/StepFormDialogContentInner.js.map +1 -1
- package/lib/components/Dialog/DialogContentResponseStatusMessage.cjs +3 -8
- package/lib/components/Dialog/DialogContentResponseStatusMessage.cjs.map +1 -1
- package/lib/components/Dialog/DialogContentResponseStatusMessage.d.ts +1 -1
- package/lib/components/Dialog/DialogContentResponseStatusMessage.js +3 -8
- package/lib/components/Dialog/DialogContentResponseStatusMessage.js.map +1 -1
- package/lib/components/Dialog/FocusTrap.cjs +4 -1
- package/lib/components/Dialog/FocusTrap.cjs.map +1 -1
- package/lib/components/Dialog/FocusTrap.js +4 -1
- package/lib/components/Dialog/FocusTrap.js.map +1 -1
- package/lib/components/Dialog/ModelessDialog/ModelessDialog.cjs +1 -1
- package/lib/components/Dialog/ModelessDialog/ModelessDialog.js +1 -1
- package/lib/components/Dialog/RemoteDialogTrigger/RemoteDialogTrigger.cjs +9 -3
- package/lib/components/Dialog/RemoteDialogTrigger/RemoteDialogTrigger.cjs.map +1 -1
- package/lib/components/Dialog/RemoteDialogTrigger/RemoteDialogTrigger.js +9 -3
- package/lib/components/Dialog/RemoteDialogTrigger/RemoteDialogTrigger.js.map +1 -1
- package/lib/components/Disclosure/DisclosureTrigger.cjs +8 -4
- package/lib/components/Disclosure/DisclosureTrigger.cjs.map +1 -1
- package/lib/components/Disclosure/DisclosureTrigger.js +8 -4
- package/lib/components/Disclosure/DisclosureTrigger.js.map +1 -1
- package/lib/components/DropZone/DropZone.cjs +1 -1
- package/lib/components/DropZone/DropZone.js +1 -1
- package/lib/components/Dropdown/Dropdown.cjs +36 -34
- package/lib/components/Dropdown/Dropdown.cjs.map +1 -1
- package/lib/components/Dropdown/Dropdown.js +37 -35
- package/lib/components/Dropdown/Dropdown.js.map +1 -1
- package/lib/components/Dropdown/DropdownMenuButton/DropdownMenuButton.cjs +1 -1
- package/lib/components/Dropdown/DropdownMenuButton/DropdownMenuButton.js +1 -1
- package/lib/components/Dropdown/FilterDropdown/FilterDropdown.cjs +3 -5
- package/lib/components/Dropdown/FilterDropdown/FilterDropdown.cjs.map +1 -1
- package/lib/components/Dropdown/FilterDropdown/FilterDropdown.d.ts +1 -1
- package/lib/components/Dropdown/FilterDropdown/FilterDropdown.js +3 -5
- package/lib/components/Dropdown/FilterDropdown/FilterDropdown.js.map +1 -1
- package/lib/components/Dropdown/SortDropdown/useSortDropdown.cjs +1 -1
- package/lib/components/Dropdown/SortDropdown/useSortDropdown.js +1 -1
- package/lib/components/ErrorScreen/AuthErrorScreen.cjs +1 -1
- package/lib/components/ErrorScreen/AuthErrorScreen.js +1 -1
- package/lib/components/ErrorScreen/ForbiddenErrorScreen.cjs +1 -1
- package/lib/components/ErrorScreen/ForbiddenErrorScreen.js +1 -1
- package/lib/components/ErrorScreen/NotFoundErrorScreen.cjs +1 -1
- package/lib/components/ErrorScreen/NotFoundErrorScreen.js +1 -1
- package/lib/components/ErrorScreen/UnauthorizedErrorScreen.cjs +1 -1
- package/lib/components/ErrorScreen/UnauthorizedErrorScreen.js +1 -1
- package/lib/components/FileViewer/FileViewer.cjs +35 -2
- package/lib/components/FileViewer/FileViewer.cjs.map +1 -1
- package/lib/components/FileViewer/FileViewer.js +37 -4
- package/lib/components/FileViewer/FileViewer.js.map +1 -1
- package/lib/components/FileViewer/PDFViewer.cjs +59 -2
- package/lib/components/FileViewer/PDFViewer.cjs.map +1 -1
- package/lib/components/FileViewer/PDFViewer.d.ts +6 -1
- package/lib/components/FileViewer/PDFViewer.js +60 -3
- package/lib/components/FileViewer/PDFViewer.js.map +1 -1
- package/lib/components/FileViewer/SearchController.cjs +105 -0
- package/lib/components/FileViewer/SearchController.cjs.map +1 -0
- package/lib/components/FileViewer/SearchController.d.ts +7 -0
- package/lib/components/FileViewer/SearchController.js +103 -0
- package/lib/components/FileViewer/SearchController.js.map +1 -0
- package/lib/components/FileViewer/buildCustomTextRenderer.cjs +55 -0
- package/lib/components/FileViewer/buildCustomTextRenderer.cjs.map +1 -0
- package/lib/components/FileViewer/buildCustomTextRenderer.d.ts +9 -0
- package/lib/components/FileViewer/buildCustomTextRenderer.js +50 -0
- package/lib/components/FileViewer/buildCustomTextRenderer.js.map +1 -0
- package/lib/components/FileViewer/types.d.ts +7 -0
- package/lib/components/FileViewer/usePDFSearch.cjs +148 -0
- package/lib/components/FileViewer/usePDFSearch.cjs.map +1 -0
- package/lib/components/FileViewer/usePDFSearch.d.ts +19 -0
- package/lib/components/FileViewer/usePDFSearch.js +143 -0
- package/lib/components/FileViewer/usePDFSearch.js.map +1 -0
- package/lib/components/FormControl/FormControl.cjs +2 -22
- package/lib/components/FormControl/FormControl.cjs.map +1 -1
- package/lib/components/FormControl/FormControl.js +2 -22
- package/lib/components/FormControl/FormControl.js.map +1 -1
- package/lib/components/Header/AppLauncher/AppLauncher.cjs +1 -1
- package/lib/components/Header/AppLauncher/AppLauncher.js +1 -1
- package/lib/components/Header/LanguageSwitcher/LanguageSwitcher.cjs +1 -1
- package/lib/components/Header/LanguageSwitcher/LanguageSwitcher.js +1 -1
- package/lib/components/Icon/FaIcon.js +1 -1
- package/lib/components/Icon/OpenInNewTabIcon.cjs +1 -1
- package/lib/components/Icon/OpenInNewTabIcon.js +1 -1
- package/lib/components/InformationPanel/InformationPanel.cjs +1 -1
- package/lib/components/InformationPanel/InformationPanel.js +1 -1
- package/lib/components/Input/SearchInput/SearchInputIcon.cjs +1 -1
- package/lib/components/Input/SearchInput/SearchInputIcon.js +1 -1
- package/lib/components/InputFile/InputFileMultiplyAppendable.cjs +1 -1
- package/lib/components/InputFile/InputFileMultiplyAppendable.js +1 -1
- package/lib/components/InputFile/InputFileNative.cjs +1 -1
- package/lib/components/InputFile/InputFileNative.js +1 -1
- package/lib/components/Layout/Cluster/Cluster.cjs +0 -1
- package/lib/components/Layout/Cluster/Cluster.cjs.map +1 -1
- package/lib/components/Layout/Cluster/Cluster.js +0 -1
- package/lib/components/Layout/Cluster/Cluster.js.map +1 -1
- package/lib/components/Layout/Container/Container.cjs +1 -5
- package/lib/components/Layout/Container/Container.cjs.map +1 -1
- package/lib/components/Layout/Container/Container.js +1 -5
- package/lib/components/Layout/Container/Container.js.map +1 -1
- package/lib/components/Layout/Stack/Stack.cjs +22 -22
- package/lib/components/Layout/Stack/Stack.cjs.map +1 -1
- package/lib/components/Layout/Stack/Stack.js +22 -22
- package/lib/components/Layout/Stack/Stack.js.map +1 -1
- package/lib/components/Loader/LoaderSpinner.cjs +1 -1
- package/lib/components/Loader/LoaderSpinner.js +1 -1
- package/lib/components/Pagination/Pagination.cjs +1 -1
- package/lib/components/Pagination/Pagination.js +1 -1
- package/lib/components/Pagination/PaginationItemButton.cjs +1 -1
- package/lib/components/Pagination/PaginationItemButton.js +1 -1
- package/lib/components/SideNav/SideNav.cjs +3 -22
- package/lib/components/SideNav/SideNav.cjs.map +1 -1
- package/lib/components/SideNav/SideNav.d.ts +2 -9
- package/lib/components/SideNav/SideNav.js +4 -23
- package/lib/components/SideNav/SideNav.js.map +1 -1
- package/lib/components/SideNav/SideNavContext.cjs +11 -0
- package/lib/components/SideNav/SideNavContext.cjs.map +1 -0
- package/lib/components/SideNav/SideNavContext.d.ts +7 -0
- package/lib/components/SideNav/SideNavContext.js +8 -0
- package/lib/components/SideNav/SideNavContext.js.map +1 -0
- package/lib/components/SideNav/SideNavItemButton.cjs +12 -7
- package/lib/components/SideNav/SideNavItemButton.cjs.map +1 -1
- package/lib/components/SideNav/SideNavItemButton.d.ts +5 -11
- package/lib/components/SideNav/SideNavItemButton.js +12 -7
- package/lib/components/SideNav/SideNavItemButton.js.map +1 -1
- package/lib/components/Table/Th.cjs +2 -2
- package/lib/components/Table/Th.cjs.map +1 -1
- package/lib/components/Table/Th.d.ts +0 -2
- package/lib/components/Table/Th.js +2 -2
- package/lib/components/Table/Th.js.map +1 -1
- package/lib/components/Table/ThCheckbox.cjs +1 -1
- package/lib/components/Table/ThCheckbox.js +1 -1
- package/lib/components/Table/ThSortButton.cjs +5 -6
- package/lib/components/Table/ThSortButton.cjs.map +1 -1
- package/lib/components/Table/ThSortButton.d.ts +1 -6
- package/lib/components/Table/ThSortButton.js +5 -6
- package/lib/components/Table/ThSortButton.js.map +1 -1
- package/lib/components/WarekiPicker/WarekiPicker.cjs +1 -1
- package/lib/components/WarekiPicker/WarekiPicker.js +1 -1
- package/lib/hooks/useLatest.cjs +33 -0
- package/lib/hooks/useLatest.cjs.map +1 -0
- package/lib/hooks/useLatest.d.ts +19 -0
- package/lib/hooks/useLatest.js +31 -0
- package/lib/hooks/useLatest.js.map +1 -0
- package/lib/index.cjs +1 -6
- package/lib/index.cjs.map +1 -1
- package/lib/index.d.ts +0 -1
- package/lib/index.js +1 -4
- package/lib/index.js.map +1 -1
- package/lib/intl/Localizer.cjs +5 -1
- package/lib/intl/Localizer.cjs.map +1 -1
- package/lib/intl/Localizer.d.ts +1 -1
- package/lib/intl/Localizer.js +5 -1
- package/lib/intl/Localizer.js.map +1 -1
- package/lib/intl/locales/en_us.json.cjs +3 -0
- package/lib/intl/locales/en_us.json.cjs.map +1 -1
- package/lib/intl/locales/en_us.json.js +3 -0
- package/lib/intl/locales/en_us.json.js.map +1 -1
- package/lib/intl/locales/id_id.json.cjs +3 -0
- package/lib/intl/locales/id_id.json.cjs.map +1 -1
- package/lib/intl/locales/id_id.json.js +3 -0
- package/lib/intl/locales/id_id.json.js.map +1 -1
- package/lib/intl/locales/index.d.ts +27 -0
- package/lib/intl/locales/ja.cjs +3 -0
- package/lib/intl/locales/ja.cjs.map +1 -1
- package/lib/intl/locales/ja.d.ts +3 -0
- package/lib/intl/locales/ja.js +3 -0
- package/lib/intl/locales/ja.js.map +1 -1
- package/lib/intl/locales/ja.json.cjs +3 -0
- package/lib/intl/locales/ja.json.cjs.map +1 -1
- package/lib/intl/locales/ja.json.js +3 -0
- package/lib/intl/locales/ja.json.js.map +1 -1
- package/lib/intl/locales/ja_easy.json.cjs +3 -0
- package/lib/intl/locales/ja_easy.json.cjs.map +1 -1
- package/lib/intl/locales/ja_easy.json.js +3 -0
- package/lib/intl/locales/ja_easy.json.js.map +1 -1
- package/lib/intl/locales/ko_kr.json.cjs +3 -0
- package/lib/intl/locales/ko_kr.json.cjs.map +1 -1
- package/lib/intl/locales/ko_kr.json.js +3 -0
- package/lib/intl/locales/ko_kr.json.js.map +1 -1
- package/lib/intl/locales/pt_br.json.cjs +3 -0
- package/lib/intl/locales/pt_br.json.cjs.map +1 -1
- package/lib/intl/locales/pt_br.json.js +3 -0
- package/lib/intl/locales/pt_br.json.js.map +1 -1
- package/lib/intl/locales/vi_vn.json.cjs +3 -0
- package/lib/intl/locales/vi_vn.json.cjs.map +1 -1
- package/lib/intl/locales/vi_vn.json.js +3 -0
- package/lib/intl/locales/vi_vn.json.js.map +1 -1
- package/lib/intl/locales/zh_hans_cn.json.cjs +3 -0
- package/lib/intl/locales/zh_hans_cn.json.cjs.map +1 -1
- package/lib/intl/locales/zh_hans_cn.json.js +3 -0
- package/lib/intl/locales/zh_hans_cn.json.js.map +1 -1
- package/lib/intl/locales/zh_hant_tw.json.cjs +3 -0
- package/lib/intl/locales/zh_hant_tw.json.cjs.map +1 -1
- package/lib/intl/locales/zh_hant_tw.json.js +3 -0
- package/lib/intl/locales/zh_hant_tw.json.js.map +1 -1
- package/metadata.json +1 -1
- package/package.json +1 -1
- package/smarthr-ui.css +35 -98
- package/lib/hooks/useDecorators.cjs +0 -18
- package/lib/hooks/useDecorators.cjs.map +0 -1
- package/lib/hooks/useDecorators.d.ts +0 -6
- package/lib/hooks/useDecorators.js +0 -16
- package/lib/hooks/useDecorators.js.map +0 -1
- package/lib/hooks/useDevice/DeviceProvider.cjs +0 -37
- package/lib/hooks/useDevice/DeviceProvider.cjs.map +0 -1
- package/lib/hooks/useDevice/DeviceProvider.d.ts +0 -4
- package/lib/hooks/useDevice/DeviceProvider.js +0 -35
- package/lib/hooks/useDevice/DeviceProvider.js.map +0 -1
- package/lib/hooks/useDevice/index.cjs +0 -10
- package/lib/hooks/useDevice/index.cjs.map +0 -1
- package/lib/hooks/useDevice/index.d.ts +0 -2
- package/lib/hooks/useDevice/index.js +0 -3
- package/lib/hooks/useDevice/index.js.map +0 -1
- package/lib/hooks/useDevice/useDevice.cjs +0 -11
- package/lib/hooks/useDevice/useDevice.cjs.map +0 -1
- package/lib/hooks/useDevice/useDevice.d.ts +0 -4
- package/lib/hooks/useDevice/useDevice.js +0 -8
- package/lib/hooks/useDevice/useDevice.js.map +0 -1
|
@@ -6,10 +6,11 @@ var React = require('react');
|
|
|
6
6
|
var index$1 = require('../../../_virtual/index3.cjs');
|
|
7
7
|
var index = require('./../../../vendor/.pnpm/tailwind-variants@0.3.1_tailwindcss@3.4.19_ts-node@10.9.2_@swc_core@1.15.17_@types_node@20.19.35_typescript@5.9.3__/vendor/tailwind-variants/dist/index.cjs');
|
|
8
8
|
var hooks_useClick = require('../../../hooks/useClick.cjs');
|
|
9
|
+
var hooks_useLatest = require('../../../hooks/useLatest.cjs');
|
|
9
10
|
var hooks_useTheme = require('../../../hooks/useTheme.cjs');
|
|
10
11
|
require('../../../intl/IntlProvider.cjs');
|
|
11
12
|
var intl_useIntl = require('../../../intl/useIntl.cjs');
|
|
12
|
-
require('
|
|
13
|
+
require('../../../intl/Localizer.cjs');
|
|
13
14
|
require('../../../_virtual/dayjs.min.cjs');
|
|
14
15
|
require('../../../intl/TimeFormatter.cjs');
|
|
15
16
|
var libs_util = require('../../../libs/util.cjs');
|
|
@@ -66,6 +67,7 @@ const classNameGenerator = index.tv({
|
|
|
66
67
|
},
|
|
67
68
|
},
|
|
68
69
|
});
|
|
70
|
+
const SuffixButtons = React.memo(({ onClickClear, clearButtonRef, onClickIcon: onDelegateClickIcon, caretIconColor, destroyButtonIconAlt, classNames, }) => (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(components_Button_UnstyledButton.UnstyledButton, { onClick: onClickClear, ref: clearButtonRef, className: classNames.clearButton, children: jsxRuntime.jsx(components_Icon_FaIcon.FaCircleXmarkIcon, { color: "TEXT_BLACK", alt: destroyButtonIconAlt, className: classNames.clearButtonIcon }) }), jsxRuntime.jsx("span", { role: "presentation", onClick: onDelegateClickIcon, className: classNames.caretDownLayout, children: jsxRuntime.jsx(components_Icon_FaIcon.FaCaretDownIcon, { color: caretIconColor, className: classNames.caretDownIcon }) })] })));
|
|
69
71
|
const ActualSingleCombobox = ({ items, selectedItem, defaultItem, name, disabled, readOnly, required, prefix, error, creatable, placeholder, autoComplete, dropdownHelpMessage, isLoading, width, dropdownWidth = 'auto', className, onChange, onChangeInput, onAdd, onSelect, onClear, onClearClick, onChangeSelected, onFocus, onBlur, onKeyPress, noResultText, style, ...rest }, ref) => {
|
|
70
72
|
const theme = hooks_useTheme.useTheme();
|
|
71
73
|
const { localize } = intl_useIntl.useIntl();
|
|
@@ -77,6 +79,24 @@ const ActualSingleCombobox = ({ items, selectedItem, defaultItem, name, disabled
|
|
|
77
79
|
const [inputValue, setInputValue] = React.useState('');
|
|
78
80
|
const [isComposing, setIsComposing] = React.useState(false);
|
|
79
81
|
const [isEditing, setIsEditing] = React.useState(false);
|
|
82
|
+
const latest = hooks_useLatest.useLatest({
|
|
83
|
+
onChange,
|
|
84
|
+
onChangeInput,
|
|
85
|
+
onAdd,
|
|
86
|
+
onSelect,
|
|
87
|
+
onClear,
|
|
88
|
+
onClearClick,
|
|
89
|
+
onChangeSelected,
|
|
90
|
+
onFocus,
|
|
91
|
+
onBlur,
|
|
92
|
+
onKeyPress,
|
|
93
|
+
defaultItem,
|
|
94
|
+
selectedItem,
|
|
95
|
+
isFocused,
|
|
96
|
+
isExpanded,
|
|
97
|
+
isComposing,
|
|
98
|
+
isEditing,
|
|
99
|
+
});
|
|
80
100
|
React.useImperativeHandle(ref, () => inputRef.current);
|
|
81
101
|
const { options } = components_Combobox_useOptions.useSingleOptions({
|
|
82
102
|
items,
|
|
@@ -91,49 +111,58 @@ const ActualSingleCombobox = ({ items, selectedItem, defaultItem, name, disabled
|
|
|
91
111
|
dropdownWidth,
|
|
92
112
|
onAdd,
|
|
93
113
|
onSelect: React.useCallback((selected) => {
|
|
94
|
-
onSelect?.(selected);
|
|
95
|
-
onChangeSelected?.(selected);
|
|
96
|
-
// 制御コンポーネントの場合に親側でinputValueを更新できるように、選択時にonChangeInputを空文字で発火する
|
|
97
|
-
onChangeInput?.(EMPTY_INPUT_CHANGE_EVENT);
|
|
114
|
+
latest.onSelect?.(selected);
|
|
115
|
+
latest.onChangeSelected?.(selected);
|
|
98
116
|
// HINT: Dropdown系コンポーネント内でComboboxを使うと、選択肢がportalで表現されている関係上Dropdownが閉じてしまう
|
|
99
117
|
// requestAnimationFrameを追加、処理を遅延させることで正常に閉じる/閉じないの判定を行えるようにする
|
|
100
118
|
requestAnimationFrame(() => {
|
|
101
119
|
setIsExpanded(false);
|
|
120
|
+
// HINT:
|
|
121
|
+
// - 制御コンポーネントの場合に親側でinputValueを更新できるように、選択時にonChangeInputを空文字で発火する
|
|
122
|
+
// - 対応するdropdownを閉じて以降にonChangeInputを発火する必要がある
|
|
123
|
+
// - 先にclearしてしまうと意図せずこの要素のドロップダウンを閉じる前に他要素の再レンダリングを引き起こす可能性がある
|
|
124
|
+
// - 例えばFilterDropdownなどで当comboboxを使っている場合、レイアウト上comboboxのdropdown以下の要素がクリックされた扱いになってしまい
|
|
125
|
+
// FilterDropdownを意図せず閉じてしまうなどの挙動のバグを引き起こす可能性がある
|
|
126
|
+
latest.onChangeInput?.(EMPTY_INPUT_CHANGE_EVENT);
|
|
102
127
|
});
|
|
103
128
|
setIsEditing(false);
|
|
104
|
-
}, [
|
|
129
|
+
}, [latest]),
|
|
105
130
|
isExpanded,
|
|
106
131
|
isLoading,
|
|
107
132
|
triggerRef: outerRef,
|
|
108
133
|
noResultText,
|
|
109
134
|
});
|
|
110
|
-
const selectDefaultItem = React.
|
|
135
|
+
const selectDefaultItem = React.useCallback(() => {
|
|
136
|
+
if (latest.onSelect && latest.defaultItem) {
|
|
137
|
+
latest.onSelect(latest.defaultItem);
|
|
138
|
+
}
|
|
139
|
+
}, [latest]);
|
|
111
140
|
const focus = React.useCallback(() => {
|
|
112
|
-
onFocus?.();
|
|
141
|
+
latest.onFocus?.();
|
|
113
142
|
inputRef.current?.focus();
|
|
114
143
|
setIsFocused(true);
|
|
115
|
-
if (!isFocused) {
|
|
144
|
+
if (!latest.isFocused) {
|
|
116
145
|
setIsExpanded(true);
|
|
117
146
|
}
|
|
118
|
-
}, [
|
|
147
|
+
}, [latest]);
|
|
119
148
|
const unfocus = React.useCallback(() => {
|
|
120
|
-
if (!isFocused)
|
|
149
|
+
if (!latest.isFocused)
|
|
121
150
|
return;
|
|
122
|
-
onBlur?.();
|
|
151
|
+
latest.onBlur?.();
|
|
123
152
|
setIsFocused(false);
|
|
124
153
|
setIsExpanded(false);
|
|
125
154
|
setIsEditing(false);
|
|
126
|
-
if (selectedItem) {
|
|
127
|
-
setInputValue(index$1(selectedItem.label));
|
|
155
|
+
if (latest.selectedItem) {
|
|
156
|
+
setInputValue(index$1(latest.selectedItem.label));
|
|
128
157
|
}
|
|
129
158
|
else {
|
|
130
159
|
selectDefaultItem();
|
|
131
160
|
}
|
|
132
|
-
}, [
|
|
161
|
+
}, [selectDefaultItem, latest]);
|
|
133
162
|
const onClickClear = React.useCallback((e) => {
|
|
134
163
|
e.stopPropagation();
|
|
135
164
|
let isExecutedPreventDefault = false;
|
|
136
|
-
onClearClick?.({
|
|
165
|
+
latest.onClearClick?.({
|
|
137
166
|
...e,
|
|
138
167
|
preventDefault: () => {
|
|
139
168
|
e.preventDefault();
|
|
@@ -141,44 +170,43 @@ const ActualSingleCombobox = ({ items, selectedItem, defaultItem, name, disabled
|
|
|
141
170
|
},
|
|
142
171
|
});
|
|
143
172
|
if (!isExecutedPreventDefault) {
|
|
144
|
-
onClear?.();
|
|
145
|
-
onChangeSelected?.(null);
|
|
173
|
+
latest.onClear?.();
|
|
174
|
+
latest.onChangeSelected?.(null);
|
|
146
175
|
inputRef.current?.focus();
|
|
147
176
|
setIsFocused(true);
|
|
148
177
|
setIsExpanded(true);
|
|
149
178
|
}
|
|
150
|
-
}, [
|
|
179
|
+
}, [latest]);
|
|
151
180
|
const onClickInput = React.useCallback((e) => {
|
|
152
181
|
if (disabled || readOnly) {
|
|
153
182
|
e.stopPropagation();
|
|
154
183
|
return;
|
|
155
184
|
}
|
|
156
185
|
inputRef.current?.focus();
|
|
157
|
-
if (!isExpanded) {
|
|
186
|
+
if (!latest.isExpanded) {
|
|
158
187
|
setIsExpanded(true);
|
|
159
188
|
}
|
|
160
|
-
}, [disabled, readOnly,
|
|
161
|
-
const onDelegateClickIcon = onClickInput;
|
|
189
|
+
}, [disabled, readOnly, latest]);
|
|
162
190
|
const actualOnChangeInput = React.useCallback((e) => {
|
|
163
|
-
onChange?.(e);
|
|
164
|
-
onChangeInput?.(e);
|
|
165
|
-
if (!isEditing)
|
|
191
|
+
latest.onChange?.(e);
|
|
192
|
+
latest.onChangeInput?.(e);
|
|
193
|
+
if (!latest.isEditing)
|
|
166
194
|
setIsEditing(true);
|
|
167
195
|
const { value } = e.currentTarget;
|
|
168
196
|
setInputValue(value);
|
|
169
197
|
if (value === '') {
|
|
170
|
-
onClear?.();
|
|
171
|
-
onChangeSelected?.(null);
|
|
198
|
+
latest.onClear?.();
|
|
199
|
+
latest.onChangeSelected?.(null);
|
|
172
200
|
}
|
|
173
|
-
}, [
|
|
201
|
+
}, [latest]);
|
|
174
202
|
const onCompositionStart = React.useCallback(() => setIsComposing(true), []);
|
|
175
203
|
const onCompositionEnd = React.useCallback(() => setIsComposing(false), []);
|
|
176
204
|
const onKeyDownInput = React.useCallback((e) => {
|
|
177
|
-
if (isComposing) {
|
|
205
|
+
if (latest.isComposing) {
|
|
178
206
|
return;
|
|
179
207
|
}
|
|
180
208
|
if (ESCAPE_KEY_REGEX.test(e.key)) {
|
|
181
|
-
if (isExpanded) {
|
|
209
|
+
if (latest.isExpanded) {
|
|
182
210
|
e.stopPropagation();
|
|
183
211
|
setIsExpanded(false);
|
|
184
212
|
}
|
|
@@ -191,28 +219,26 @@ const ActualSingleCombobox = ({ items, selectedItem, defaultItem, name, disabled
|
|
|
191
219
|
e.preventDefault();
|
|
192
220
|
}
|
|
193
221
|
inputRef.current?.focus();
|
|
194
|
-
if (!isExpanded) {
|
|
222
|
+
if (!latest.isExpanded) {
|
|
195
223
|
setIsExpanded(true);
|
|
196
224
|
}
|
|
197
225
|
}
|
|
198
226
|
onKeyDownListBox(e);
|
|
199
|
-
}, [
|
|
227
|
+
}, [unfocus, onKeyDownListBox, latest]);
|
|
200
228
|
// HINT: form内にcomboboxを設置 & 検索inputにfocusした状態で
|
|
201
229
|
// アイテムをキーボードで選択し、Enterを押すとinput上でEnterを押したことになるため、
|
|
202
230
|
// submitイベントが発生し、formが送信される場合がある
|
|
203
231
|
const handleKeyPress = React.useCallback((e) => {
|
|
204
232
|
if (e.key === 'Enter')
|
|
205
233
|
e.preventDefault();
|
|
206
|
-
onKeyPress?.(e);
|
|
207
|
-
}, [
|
|
208
|
-
const caretIconColor =
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
}, [disabled, readOnly, isFocused, theme.textColor]);
|
|
215
|
-
hooks_useClick.useClick(React.useMemo(() => [outerRef, listBoxRef, clearButtonRef], [outerRef, listBoxRef, clearButtonRef]), isFocused || selectedItem ? NOOP : selectDefaultItem, unfocus);
|
|
234
|
+
latest.onKeyPress?.(e);
|
|
235
|
+
}, [latest]);
|
|
236
|
+
const caretIconColor = isFocused
|
|
237
|
+
? theme.textColor.black
|
|
238
|
+
: disabled || readOnly
|
|
239
|
+
? theme.textColor.disabled
|
|
240
|
+
: theme.textColor.grey;
|
|
241
|
+
hooks_useClick.useClick(React.useMemo(() => [outerRef, listBoxRef, clearButtonRef], [listBoxRef]), isFocused || selectedItem ? NOOP : selectDefaultItem, unfocus);
|
|
216
242
|
// selectedItem.label はプリミティブ値でないデータ型の可能性があり、そのまま useEffect の依存配列に入れると意図せぬエフェクトの実行を引き起こしてしまう可能性があるので、プリミティブ値である string 型に変換したものを依存配列に入れています。
|
|
217
243
|
const selectedItemLabelText = index$1(selectedItem?.label);
|
|
218
244
|
React.useEffect(() => {
|
|
@@ -222,7 +248,7 @@ const ActualSingleCombobox = ({ items, selectedItem, defaultItem, name, disabled
|
|
|
222
248
|
...style,
|
|
223
249
|
width: typeof width === 'number' ? `${width}px` : width,
|
|
224
250
|
}), [style, width]);
|
|
225
|
-
const
|
|
251
|
+
const hasSelectedItem = selectedItem !== null;
|
|
226
252
|
const classNames = React.useMemo(() => {
|
|
227
253
|
const { wrapper, input, caretDownLayout, caretDownIcon, clearButton, clearButtonIcon } = classNameGenerator();
|
|
228
254
|
return {
|
|
@@ -230,17 +256,24 @@ const ActualSingleCombobox = ({ items, selectedItem, defaultItem, name, disabled
|
|
|
230
256
|
input: input(),
|
|
231
257
|
caretDownLayout: caretDownLayout(),
|
|
232
258
|
caretDownIcon: caretDownIcon(),
|
|
233
|
-
clearButton: clearButton({
|
|
259
|
+
clearButton: clearButton({
|
|
260
|
+
hidden: !hasSelectedItem || disabled || readOnly,
|
|
261
|
+
}),
|
|
234
262
|
clearButtonIcon: clearButtonIcon(),
|
|
235
263
|
};
|
|
236
|
-
}, [
|
|
264
|
+
}, [hasSelectedItem, disabled, readOnly, className]);
|
|
237
265
|
const destroyButtonIconAlt = React.useMemo(() => localize({
|
|
238
266
|
id: 'smarthr-ui/SingleCombobox/destroyButtonIconAlt',
|
|
239
267
|
defaultText: 'クリア',
|
|
240
268
|
}), [localize]);
|
|
241
269
|
return (jsxRuntime.jsxs("div", { role: "group", className: classNames.wrapper, style: wrapperStyle, ref: outerRef, children: [jsxRuntime.jsx(components_Input_Input.Input, { ...rest, ref: inputRef, type: "text", role: "combobox", name: name, value: inputValue, disabled: disabled, readOnly: readOnly, required: required, autoComplete: autoComplete ?? 'off', "aria-haspopup": "listbox", "aria-controls": listBoxId, "aria-expanded": isFocused, "aria-activedescendant": activeOption?.id, "aria-autocomplete": "list",
|
|
242
270
|
/* eslint-disable-next-line smarthr/a11y-prohibit-input-placeholder */
|
|
243
|
-
placeholder: placeholder, onClick: onClickInput, onChange: actualOnChangeInput, onFocus: isFocused ? undefined : focus, onCompositionStart: onCompositionStart, onCompositionEnd: onCompositionEnd, onKeyDown: onKeyDownInput, onKeyPress: handleKeyPress, error: error, prefix: prefix, suffix: jsxRuntime.
|
|
271
|
+
placeholder: placeholder, onClick: onClickInput, onChange: actualOnChangeInput, onFocus: isFocused ? undefined : focus, onCompositionStart: onCompositionStart, onCompositionEnd: onCompositionEnd, onKeyDown: onKeyDownInput, onKeyPress: handleKeyPress, error: error, prefix: prefix, suffix: jsxRuntime.jsx(SuffixButtons, { onClickClear: onClickClear, clearButtonRef: clearButtonRef, onClickIcon: onClickInput, caretIconColor: caretIconColor, destroyButtonIconAlt: destroyButtonIconAlt, classNames: {
|
|
272
|
+
clearButton: classNames.clearButton,
|
|
273
|
+
clearButtonIcon: classNames.clearButtonIcon,
|
|
274
|
+
caretDownLayout: classNames.caretDownLayout,
|
|
275
|
+
caretDownIcon: classNames.caretDownIcon,
|
|
276
|
+
} }), className: classNames.input, "data-smarthr-ui-input": "true" }), !readOnly && renderListBox()] }));
|
|
244
277
|
};
|
|
245
278
|
const SingleCombobox = libs_util.genericsForwardRef(ActualSingleCombobox);
|
|
246
279
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SingleCombobox.cjs","sources":["../../../../src/components/Combobox/SingleCombobox/SingleCombobox.tsx"],"sourcesContent":["'use client'\n\nimport {\n type ChangeEvent,\n type ComponentPropsWithoutRef,\n type KeyboardEvent,\n type MouseEvent,\n type ReactNode,\n type Ref,\n useCallback,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n} from 'react'\nimport innerText from 'react-innertext'\nimport { tv } from 'tailwind-variants'\n\nimport { useClick } from '../../../hooks/useClick'\nimport { useTheme } from '../../../hooks/useTheme'\nimport { useIntl } from '../../../intl'\nimport { genericsForwardRef } from '../../../libs/util'\nimport { UnstyledButton } from '../../Button'\nimport { FaCaretDownIcon, FaCircleXmarkIcon } from '../../Icon'\nimport { Input } from '../../Input'\nimport { useListbox } from '../useListbox'\nimport { useSingleOptions } from '../useOptions'\n\nimport type { ComboboxItem, AbstractProps as ComboboxProps } from '../types'\n\ntype AbstractProps<T> = ComboboxProps<T> & {\n /**\n * 選択されているアイテム\n */\n selectedItem: ComboboxItem<T> | null\n /**\n * デフォルトで選択されるアイテム\n */\n defaultItem?: ComboboxItem<T>\n /**\n * コンポーネント内の先頭に表示する内容\n */\n prefix?: ReactNode\n /**\n * 選択されているアイテムがクリアされた時に発火するコールバック関数\n */\n onClear?: () => void\n /**\n * 選択されているアイテムがクリアされた時に発火するコールバック関数\n * 指定している場合、クリア時にonClickを実行せずにonClearClickのみ実行する\n */\n onClearClick?: (e: MouseEvent) => void\n /**\n * 選択されているアイテムのリストが変わった時に発火するコールバック関数\n */\n onChangeSelected?: (selectedItem: ComboboxItem<T> | null) => void\n /**\n * コンポーネントがフォーカスされたときに発火するコールバック関数\n */\n onFocus?: () => void\n /**\n * コンポーネントからフォーカスが外れた時に発火するコールバック関数\n */\n onBlur?: () => void\n /**\n * 検索結果が0件の時に表示するコンテンツ\n */\n noResultText?: ReactNode\n}\ntype Props<T> = AbstractProps<T> &\n Omit<ComponentPropsWithoutRef<'input'>, keyof AbstractProps<unknown>>\n\nconst NOOP = () => undefined\n\nconst ESCAPE_KEY_REGEX = /^Esc(ape)?$/\nconst ARROW_UP_DOWN_REGEX = /^(Arrow)?(Up|Down)$/\n\nconst EMPTY_INPUT_CHANGE_EVENT = {\n currentTarget: { value: '' },\n target: { value: '' },\n} as ChangeEvent<HTMLInputElement>\n\nconst classNameGenerator = tv({\n slots: {\n wrapper: 'smarthr-ui-SingleCombobox shr-inline-block',\n input: 'smarthr-ui-SingleCombobox-input shr-w-full',\n caretDownLayout: [\n 'shr-relative -shr-me-0.5 shr-p-0.5',\n 'before:shr-border-0',\n 'before:shr-absolute before:shr-inset-x-0 before:shr-inset-y-0.25 before:shr-w-0 before:shr-border-l before:shr-border-solid before:shr-border-default before:shr-content-[\"\"]',\n ],\n caretDownIcon: 'shr-block',\n clearButton: [\n 'smarthr-ui-SingleCombobox-clearButton',\n 'shr-group/clearButton',\n 'shr-me-0.5',\n 'focus-visible:shr-shadow-none',\n ],\n clearButtonIcon: [\n 'shr-block',\n 'group-focus-visible/clearButton:shr-focus-indicator group-focus-visible/clearButton:shr-rounded-full',\n ],\n },\n variants: {\n disabled: {\n true: {\n wrapper: 'shr-cursor-not-allowed',\n },\n },\n hidden: {\n true: {\n clearButton: 'shr-hidden',\n },\n },\n },\n})\n\nconst ActualSingleCombobox = <T,>(\n {\n items,\n selectedItem,\n defaultItem,\n name,\n disabled,\n readOnly,\n required,\n prefix,\n error,\n creatable,\n placeholder,\n autoComplete,\n dropdownHelpMessage,\n isLoading,\n width,\n dropdownWidth = 'auto',\n className,\n onChange,\n onChangeInput,\n onAdd,\n onSelect,\n onClear,\n onClearClick,\n onChangeSelected,\n onFocus,\n onBlur,\n onKeyPress,\n noResultText,\n style,\n ...rest\n }: Props<T>,\n ref: Ref<HTMLInputElement>,\n) => {\n const theme = useTheme()\n const { localize } = useIntl()\n const outerRef = useRef<HTMLDivElement>(null)\n const inputRef = useRef<HTMLInputElement>(null)\n const clearButtonRef = useRef<HTMLButtonElement>(null)\n const [isFocused, setIsFocused] = useState(false)\n const [isExpanded, setIsExpanded] = useState(false)\n const [inputValue, setInputValue] = useState('')\n const [isComposing, setIsComposing] = useState(false)\n const [isEditing, setIsEditing] = useState(false)\n\n useImperativeHandle<HTMLInputElement | null, HTMLInputElement | null>(ref, () => inputRef.current)\n\n const { options } = useSingleOptions({\n items,\n selected: selectedItem,\n creatable,\n inputValue,\n isFilteringDisabled: !isEditing,\n })\n\n const { renderListBox, activeOption, onKeyDownListBox, listBoxId, listBoxRef } = useListbox<T>({\n options,\n dropdownHelpMessage,\n dropdownWidth,\n onAdd,\n onSelect: useCallback(\n (selected: ComboboxItem<T>) => {\n onSelect?.(selected)\n onChangeSelected?.(selected)\n\n // 制御コンポーネントの場合に親側でinputValueを更新できるように、選択時にonChangeInputを空文字で発火する\n onChangeInput?.(EMPTY_INPUT_CHANGE_EVENT)\n\n // HINT: Dropdown系コンポーネント内でComboboxを使うと、選択肢がportalで表現されている関係上Dropdownが閉じてしまう\n // requestAnimationFrameを追加、処理を遅延させることで正常に閉じる/閉じないの判定を行えるようにする\n requestAnimationFrame(() => {\n setIsExpanded(false)\n })\n\n setIsEditing(false)\n },\n [onChangeSelected, onSelect, onChangeInput],\n ),\n isExpanded,\n isLoading,\n triggerRef: outerRef,\n noResultText,\n })\n\n const selectDefaultItem = useMemo(\n () => (onSelect && defaultItem ? () => onSelect(defaultItem) : NOOP),\n [onSelect, defaultItem],\n )\n\n const focus = useCallback(() => {\n onFocus?.()\n inputRef.current?.focus()\n setIsFocused(true)\n\n if (!isFocused) {\n setIsExpanded(true)\n }\n }, [onFocus, isFocused])\n const unfocus = useCallback(() => {\n if (!isFocused) return\n\n onBlur?.()\n\n setIsFocused(false)\n setIsExpanded(false)\n setIsEditing(false)\n\n if (selectedItem) {\n setInputValue(innerText(selectedItem.label))\n } else {\n selectDefaultItem()\n }\n }, [isFocused, onBlur, selectedItem, selectDefaultItem])\n const onClickClear = useCallback(\n (e: MouseEvent) => {\n e.stopPropagation()\n\n let isExecutedPreventDefault = false\n\n onClearClick?.({\n ...e,\n preventDefault: () => {\n e.preventDefault()\n isExecutedPreventDefault = true\n },\n })\n\n if (!isExecutedPreventDefault) {\n onClear?.()\n onChangeSelected?.(null)\n\n inputRef.current?.focus()\n\n setIsFocused(true)\n setIsExpanded(true)\n }\n },\n [onClearClick, onClear, onChangeSelected],\n )\n const onClickInput = useCallback(\n (e: MouseEvent) => {\n if (disabled || readOnly) {\n e.stopPropagation()\n\n return\n }\n\n inputRef.current?.focus()\n\n if (!isExpanded) {\n setIsExpanded(true)\n }\n },\n [disabled, readOnly, inputRef, isExpanded],\n )\n const onDelegateClickIcon = onClickInput\n const actualOnChangeInput = useCallback(\n (e: ChangeEvent<HTMLInputElement>) => {\n onChange?.(e)\n onChangeInput?.(e)\n\n if (!isEditing) setIsEditing(true)\n\n const { value } = e.currentTarget\n\n setInputValue(value)\n\n if (value === '') {\n onClear?.()\n onChangeSelected?.(null)\n }\n },\n [isEditing, onChange, onChangeInput, onClear, onChangeSelected],\n )\n const onCompositionStart = useCallback(() => setIsComposing(true), [])\n const onCompositionEnd = useCallback(() => setIsComposing(false), [])\n const onKeyDownInput = useCallback(\n (e: KeyboardEvent<HTMLInputElement>) => {\n if (isComposing) {\n return\n }\n\n if (ESCAPE_KEY_REGEX.test(e.key)) {\n if (isExpanded) {\n e.stopPropagation()\n setIsExpanded(false)\n }\n } else if (e.key === 'Tab') {\n unfocus()\n } else {\n if (ARROW_UP_DOWN_REGEX.test(e.key)) {\n e.preventDefault()\n }\n\n inputRef.current?.focus()\n\n if (!isExpanded) {\n setIsExpanded(true)\n }\n }\n onKeyDownListBox(e)\n },\n [isComposing, isExpanded, unfocus, onKeyDownListBox],\n )\n\n // HINT: form内にcomboboxを設置 & 検索inputにfocusした状態で\n // アイテムをキーボードで選択し、Enterを押すとinput上でEnterを押したことになるため、\n // submitイベントが発生し、formが送信される場合がある\n const handleKeyPress = useCallback(\n (e: KeyboardEvent<HTMLInputElement>) => {\n if (e.key === 'Enter') e.preventDefault()\n\n onKeyPress?.(e)\n },\n [onKeyPress],\n )\n\n const caretIconColor = useMemo(() => {\n if (isFocused) return theme.textColor.black\n if (disabled || readOnly) return theme.textColor.disabled\n\n return theme.textColor.grey\n }, [disabled, readOnly, isFocused, theme.textColor])\n\n useClick(\n useMemo(() => [outerRef, listBoxRef, clearButtonRef], [outerRef, listBoxRef, clearButtonRef]),\n isFocused || selectedItem ? NOOP : selectDefaultItem,\n unfocus,\n )\n\n // selectedItem.label はプリミティブ値でないデータ型の可能性があり、そのまま useEffect の依存配列に入れると意図せぬエフェクトの実行を引き起こしてしまう可能性があるので、プリミティブ値である string 型に変換したものを依存配列に入れています。\n const selectedItemLabelText = innerText(selectedItem?.label)\n useEffect(() => {\n setInputValue(selectedItemLabelText)\n }, [selectedItemLabelText])\n\n const wrapperStyle = useMemo(\n () => ({\n ...style,\n width: typeof width === 'number' ? `${width}px` : width,\n }),\n [style, width],\n )\n\n const notSelected = selectedItem === null\n\n const classNames = useMemo(() => {\n const { wrapper, input, caretDownLayout, caretDownIcon, clearButton, clearButtonIcon } =\n classNameGenerator()\n\n return {\n wrapper: wrapper({ disabled, className }),\n input: input(),\n caretDownLayout: caretDownLayout(),\n caretDownIcon: caretDownIcon(),\n clearButton: clearButton({ hidden: notSelected || disabled || readOnly }),\n clearButtonIcon: clearButtonIcon(),\n }\n }, [notSelected, disabled, readOnly, className])\n\n const destroyButtonIconAlt = useMemo(\n () =>\n localize({\n id: 'smarthr-ui/SingleCombobox/destroyButtonIconAlt',\n defaultText: 'クリア',\n }),\n [localize],\n )\n\n return (\n <div role=\"group\" className={classNames.wrapper} style={wrapperStyle} ref={outerRef}>\n <Input\n {...rest}\n ref={inputRef}\n type=\"text\"\n role=\"combobox\"\n name={name}\n value={inputValue}\n disabled={disabled}\n readOnly={readOnly}\n required={required}\n autoComplete={autoComplete ?? 'off'}\n aria-haspopup=\"listbox\"\n aria-controls={listBoxId}\n aria-expanded={isFocused}\n aria-activedescendant={activeOption?.id}\n aria-autocomplete=\"list\"\n /* eslint-disable-next-line smarthr/a11y-prohibit-input-placeholder */\n placeholder={placeholder}\n onClick={onClickInput}\n onChange={actualOnChangeInput}\n onFocus={isFocused ? undefined : focus}\n onCompositionStart={onCompositionStart}\n onCompositionEnd={onCompositionEnd}\n onKeyDown={onKeyDownInput}\n onKeyPress={handleKeyPress}\n error={error}\n prefix={prefix}\n suffix={\n <>\n <UnstyledButton\n onClick={onClickClear}\n ref={clearButtonRef}\n className={classNames.clearButton}\n >\n <FaCircleXmarkIcon\n color=\"TEXT_BLACK\"\n alt={destroyButtonIconAlt}\n className={classNames.clearButtonIcon}\n />\n </UnstyledButton>\n <span\n role=\"presentation\"\n onClick={onDelegateClickIcon}\n className={classNames.caretDownLayout}\n >\n <FaCaretDownIcon color={caretIconColor} className={classNames.caretDownIcon} />\n </span>\n </>\n }\n className={classNames.input}\n data-smarthr-ui-input=\"true\"\n />\n {!readOnly && renderListBox()}\n </div>\n )\n}\n\nexport const SingleCombobox = genericsForwardRef(ActualSingleCombobox)\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAyEA;AAEA;AACA;AAEA;AACE;AACA;;AAGF;AACE;AACE;AACA;AACA;;;;AAIC;AACD;AACA;;;;;AAKC;AACD;;;AAGC;AACF;AACD;AACE;AACE;AACE;AACD;AACF;AACD;AACE;AACE;AACD;AACF;AACF;AACF;AAED;AAmCE;AACA;AACA;AACA;AACA;;;;;;;AASA;;AAEE;;;;AAID;AAED;;;;;AAKE;AAEI;AACA;;AAGA;;;;;AAMA;;;;;AAQJ;;AAED;AAED;AAKA;;AAEE;;;;;AAMF;AACA;AACE;;;;;;;;;;AAWE;;;AAGJ;;;AAMI;AACE;;;;;AAKD;;;AAIC;AAEA;;;;;AAQN;AAEI;;;;AAMA;;;;;;AASJ;AAEI;AACA;AAEA;;AAEA;;AAIA;;AAEE;;AAEJ;AAGF;AACA;AACA;;;;;;;;;;AAWW;AACL;;;;;;AAMA;;;;;;;;;;AAcN;AAEI;;AAEA;AACF;AAIF;AACE;AAAe;;AACW;AAE1B;AACF;AAEA;;;;;AAUA;AAEA;AAEI;AACA;AACD;AAIH;AAEA;AACE;;;;;;AAQE;;;;;AAQE;AACA;AACD;AAIL;;AAmBM;AAsCR;;;"}
|
|
1
|
+
{"version":3,"file":"SingleCombobox.cjs","sources":["../../../../src/components/Combobox/SingleCombobox/SingleCombobox.tsx"],"sourcesContent":["'use client'\n\nimport {\n type ChangeEvent,\n type ComponentPropsWithoutRef,\n type KeyboardEvent,\n type MouseEvent,\n type ReactNode,\n type Ref,\n type RefObject,\n memo,\n useCallback,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n} from 'react'\nimport innerText from 'react-innertext'\nimport { tv } from 'tailwind-variants'\n\nimport { useClick } from '../../../hooks/useClick'\nimport { useLatest } from '../../../hooks/useLatest'\nimport { useTheme } from '../../../hooks/useTheme'\nimport { useIntl } from '../../../intl'\nimport { genericsForwardRef } from '../../../libs/util'\nimport { UnstyledButton } from '../../Button'\nimport { FaCaretDownIcon, FaCircleXmarkIcon } from '../../Icon'\nimport { Input } from '../../Input'\nimport { useListbox } from '../useListbox'\nimport { useSingleOptions } from '../useOptions'\n\nimport type { ComboboxItem, AbstractProps as ComboboxProps } from '../types'\n\ntype AbstractProps<T> = ComboboxProps<T> & {\n /**\n * 選択されているアイテム\n */\n selectedItem: ComboboxItem<T> | null\n /**\n * デフォルトで選択されるアイテム\n */\n defaultItem?: ComboboxItem<T>\n /**\n * コンポーネント内の先頭に表示する内容\n */\n prefix?: ReactNode\n /**\n * 選択されているアイテムがクリアされた時に発火するコールバック関数\n */\n onClear?: () => void\n /**\n * 選択されているアイテムがクリアされた時に発火するコールバック関数\n * 指定している場合、クリア時にonClickを実行せずにonClearClickのみ実行する\n */\n onClearClick?: (e: MouseEvent) => void\n /**\n * 選択されているアイテムのリストが変わった時に発火するコールバック関数\n */\n onChangeSelected?: (selectedItem: ComboboxItem<T> | null) => void\n /**\n * コンポーネントがフォーカスされたときに発火するコールバック関数\n */\n onFocus?: () => void\n /**\n * コンポーネントからフォーカスが外れた時に発火するコールバック関数\n */\n onBlur?: () => void\n /**\n * 検索結果が0件の時に表示するコンテンツ\n */\n noResultText?: ReactNode\n}\ntype Props<T> = AbstractProps<T> &\n Omit<ComponentPropsWithoutRef<'input'>, keyof AbstractProps<unknown>>\n\nconst NOOP = () => undefined\n\nconst ESCAPE_KEY_REGEX = /^Esc(ape)?$/\nconst ARROW_UP_DOWN_REGEX = /^(Arrow)?(Up|Down)$/\n\nconst EMPTY_INPUT_CHANGE_EVENT = {\n currentTarget: { value: '' },\n target: { value: '' },\n} as ChangeEvent<HTMLInputElement>\n\nconst classNameGenerator = tv({\n slots: {\n wrapper: 'smarthr-ui-SingleCombobox shr-inline-block',\n input: 'smarthr-ui-SingleCombobox-input shr-w-full',\n caretDownLayout: [\n 'shr-relative -shr-me-0.5 shr-p-0.5',\n 'before:shr-border-0',\n 'before:shr-absolute before:shr-inset-x-0 before:shr-inset-y-0.25 before:shr-w-0 before:shr-border-l before:shr-border-solid before:shr-border-default before:shr-content-[\"\"]',\n ],\n caretDownIcon: 'shr-block',\n clearButton: [\n 'smarthr-ui-SingleCombobox-clearButton',\n 'shr-group/clearButton',\n 'shr-me-0.5',\n 'focus-visible:shr-shadow-none',\n ],\n clearButtonIcon: [\n 'shr-block',\n 'group-focus-visible/clearButton:shr-focus-indicator group-focus-visible/clearButton:shr-rounded-full',\n ],\n },\n variants: {\n disabled: {\n true: {\n wrapper: 'shr-cursor-not-allowed',\n },\n },\n hidden: {\n true: {\n clearButton: 'shr-hidden',\n },\n },\n },\n})\n\ntype SuffixButtonsProps = {\n onClickClear: (e: MouseEvent) => void\n clearButtonRef: RefObject<HTMLButtonElement>\n onClickIcon: (e: MouseEvent) => void\n caretIconColor: string\n destroyButtonIconAlt: string\n classNames: {\n clearButton: string\n clearButtonIcon: string\n caretDownLayout: string\n caretDownIcon: string\n }\n}\n\nconst SuffixButtons = memo<SuffixButtonsProps>(\n ({\n onClickClear,\n clearButtonRef,\n onClickIcon: onDelegateClickIcon,\n caretIconColor,\n destroyButtonIconAlt,\n classNames,\n }) => (\n <>\n <UnstyledButton\n onClick={onClickClear}\n ref={clearButtonRef}\n className={classNames.clearButton}\n >\n <FaCircleXmarkIcon\n color=\"TEXT_BLACK\"\n alt={destroyButtonIconAlt}\n className={classNames.clearButtonIcon}\n />\n </UnstyledButton>\n <span\n role=\"presentation\"\n onClick={onDelegateClickIcon}\n className={classNames.caretDownLayout}\n >\n <FaCaretDownIcon color={caretIconColor} className={classNames.caretDownIcon} />\n </span>\n </>\n ),\n)\n\nconst ActualSingleCombobox = <T,>(\n {\n items,\n selectedItem,\n defaultItem,\n name,\n disabled,\n readOnly,\n required,\n prefix,\n error,\n creatable,\n placeholder,\n autoComplete,\n dropdownHelpMessage,\n isLoading,\n width,\n dropdownWidth = 'auto',\n className,\n onChange,\n onChangeInput,\n onAdd,\n onSelect,\n onClear,\n onClearClick,\n onChangeSelected,\n onFocus,\n onBlur,\n onKeyPress,\n noResultText,\n style,\n ...rest\n }: Props<T>,\n ref: Ref<HTMLInputElement>,\n) => {\n const theme = useTheme()\n const { localize } = useIntl()\n const outerRef = useRef<HTMLDivElement>(null)\n const inputRef = useRef<HTMLInputElement>(null)\n const clearButtonRef = useRef<HTMLButtonElement>(null)\n const [isFocused, setIsFocused] = useState(false)\n const [isExpanded, setIsExpanded] = useState(false)\n const [inputValue, setInputValue] = useState('')\n const [isComposing, setIsComposing] = useState(false)\n const [isEditing, setIsEditing] = useState(false)\n\n const latest = useLatest({\n onChange,\n onChangeInput,\n onAdd,\n onSelect,\n onClear,\n onClearClick,\n onChangeSelected,\n onFocus,\n onBlur,\n onKeyPress,\n defaultItem,\n selectedItem,\n isFocused,\n isExpanded,\n isComposing,\n isEditing,\n })\n\n useImperativeHandle<HTMLInputElement | null, HTMLInputElement | null>(ref, () => inputRef.current)\n\n const { options } = useSingleOptions({\n items,\n selected: selectedItem,\n creatable,\n inputValue,\n isFilteringDisabled: !isEditing,\n })\n\n const { renderListBox, activeOption, onKeyDownListBox, listBoxId, listBoxRef } = useListbox<T>({\n options,\n dropdownHelpMessage,\n dropdownWidth,\n onAdd,\n onSelect: useCallback(\n (selected: ComboboxItem<T>) => {\n latest.onSelect?.(selected)\n latest.onChangeSelected?.(selected)\n\n // HINT: Dropdown系コンポーネント内でComboboxを使うと、選択肢がportalで表現されている関係上Dropdownが閉じてしまう\n // requestAnimationFrameを追加、処理を遅延させることで正常に閉じる/閉じないの判定を行えるようにする\n requestAnimationFrame(() => {\n setIsExpanded(false)\n // HINT:\n // - 制御コンポーネントの場合に親側でinputValueを更新できるように、選択時にonChangeInputを空文字で発火する\n // - 対応するdropdownを閉じて以降にonChangeInputを発火する必要がある\n // - 先にclearしてしまうと意図せずこの要素のドロップダウンを閉じる前に他要素の再レンダリングを引き起こす可能性がある\n // - 例えばFilterDropdownなどで当comboboxを使っている場合、レイアウト上comboboxのdropdown以下の要素がクリックされた扱いになってしまい\n // FilterDropdownを意図せず閉じてしまうなどの挙動のバグを引き起こす可能性がある\n latest.onChangeInput?.(EMPTY_INPUT_CHANGE_EVENT)\n })\n\n setIsEditing(false)\n },\n [latest],\n ),\n isExpanded,\n isLoading,\n triggerRef: outerRef,\n noResultText,\n })\n\n const selectDefaultItem = useCallback(() => {\n if (latest.onSelect && latest.defaultItem) {\n latest.onSelect(latest.defaultItem)\n }\n }, [latest])\n\n const focus = useCallback(() => {\n latest.onFocus?.()\n inputRef.current?.focus()\n setIsFocused(true)\n\n if (!latest.isFocused) {\n setIsExpanded(true)\n }\n }, [latest])\n const unfocus = useCallback(() => {\n if (!latest.isFocused) return\n\n latest.onBlur?.()\n\n setIsFocused(false)\n setIsExpanded(false)\n setIsEditing(false)\n\n if (latest.selectedItem) {\n setInputValue(innerText(latest.selectedItem.label))\n } else {\n selectDefaultItem()\n }\n }, [selectDefaultItem, latest])\n const onClickClear = useCallback(\n (e: MouseEvent) => {\n e.stopPropagation()\n\n let isExecutedPreventDefault = false\n\n latest.onClearClick?.({\n ...e,\n preventDefault: () => {\n e.preventDefault()\n isExecutedPreventDefault = true\n },\n })\n\n if (!isExecutedPreventDefault) {\n latest.onClear?.()\n latest.onChangeSelected?.(null)\n\n inputRef.current?.focus()\n\n setIsFocused(true)\n setIsExpanded(true)\n }\n },\n [latest],\n )\n const onClickInput = useCallback(\n (e: MouseEvent) => {\n if (disabled || readOnly) {\n e.stopPropagation()\n\n return\n }\n\n inputRef.current?.focus()\n\n if (!latest.isExpanded) {\n setIsExpanded(true)\n }\n },\n [disabled, readOnly, latest],\n )\n const actualOnChangeInput = useCallback(\n (e: ChangeEvent<HTMLInputElement>) => {\n latest.onChange?.(e)\n latest.onChangeInput?.(e)\n\n if (!latest.isEditing) setIsEditing(true)\n\n const { value } = e.currentTarget\n\n setInputValue(value)\n\n if (value === '') {\n latest.onClear?.()\n latest.onChangeSelected?.(null)\n }\n },\n [latest],\n )\n const onCompositionStart = useCallback(() => setIsComposing(true), [])\n const onCompositionEnd = useCallback(() => setIsComposing(false), [])\n const onKeyDownInput = useCallback(\n (e: KeyboardEvent<HTMLInputElement>) => {\n if (latest.isComposing) {\n return\n }\n\n if (ESCAPE_KEY_REGEX.test(e.key)) {\n if (latest.isExpanded) {\n e.stopPropagation()\n setIsExpanded(false)\n }\n } else if (e.key === 'Tab') {\n unfocus()\n } else {\n if (ARROW_UP_DOWN_REGEX.test(e.key)) {\n e.preventDefault()\n }\n\n inputRef.current?.focus()\n\n if (!latest.isExpanded) {\n setIsExpanded(true)\n }\n }\n onKeyDownListBox(e)\n },\n [unfocus, onKeyDownListBox, latest],\n )\n\n // HINT: form内にcomboboxを設置 & 検索inputにfocusした状態で\n // アイテムをキーボードで選択し、Enterを押すとinput上でEnterを押したことになるため、\n // submitイベントが発生し、formが送信される場合がある\n const handleKeyPress = useCallback(\n (e: KeyboardEvent<HTMLInputElement>) => {\n if (e.key === 'Enter') e.preventDefault()\n\n latest.onKeyPress?.(e)\n },\n [latest],\n )\n\n const caretIconColor = isFocused\n ? theme.textColor.black\n : disabled || readOnly\n ? theme.textColor.disabled\n : theme.textColor.grey\n\n useClick(\n useMemo(() => [outerRef, listBoxRef, clearButtonRef], [listBoxRef]),\n isFocused || selectedItem ? NOOP : selectDefaultItem,\n unfocus,\n )\n\n // selectedItem.label はプリミティブ値でないデータ型の可能性があり、そのまま useEffect の依存配列に入れると意図せぬエフェクトの実行を引き起こしてしまう可能性があるので、プリミティブ値である string 型に変換したものを依存配列に入れています。\n const selectedItemLabelText = innerText(selectedItem?.label)\n useEffect(() => {\n setInputValue(selectedItemLabelText)\n }, [selectedItemLabelText])\n\n const wrapperStyle = useMemo(\n () => ({\n ...style,\n width: typeof width === 'number' ? `${width}px` : width,\n }),\n [style, width],\n )\n\n const hasSelectedItem = selectedItem !== null\n\n const classNames = useMemo(() => {\n const { wrapper, input, caretDownLayout, caretDownIcon, clearButton, clearButtonIcon } =\n classNameGenerator()\n\n return {\n wrapper: wrapper({ disabled, className }),\n input: input(),\n caretDownLayout: caretDownLayout(),\n caretDownIcon: caretDownIcon(),\n clearButton: clearButton({\n hidden: !hasSelectedItem || disabled || readOnly,\n }),\n clearButtonIcon: clearButtonIcon(),\n }\n }, [hasSelectedItem, disabled, readOnly, className])\n\n const destroyButtonIconAlt = useMemo(\n () =>\n localize({\n id: 'smarthr-ui/SingleCombobox/destroyButtonIconAlt',\n defaultText: 'クリア',\n }),\n [localize],\n )\n\n return (\n <div role=\"group\" className={classNames.wrapper} style={wrapperStyle} ref={outerRef}>\n <Input\n {...rest}\n ref={inputRef}\n type=\"text\"\n role=\"combobox\"\n name={name}\n value={inputValue}\n disabled={disabled}\n readOnly={readOnly}\n required={required}\n autoComplete={autoComplete ?? 'off'}\n aria-haspopup=\"listbox\"\n aria-controls={listBoxId}\n aria-expanded={isFocused}\n aria-activedescendant={activeOption?.id}\n aria-autocomplete=\"list\"\n /* eslint-disable-next-line smarthr/a11y-prohibit-input-placeholder */\n placeholder={placeholder}\n onClick={onClickInput}\n onChange={actualOnChangeInput}\n onFocus={isFocused ? undefined : focus}\n onCompositionStart={onCompositionStart}\n onCompositionEnd={onCompositionEnd}\n onKeyDown={onKeyDownInput}\n onKeyPress={handleKeyPress}\n error={error}\n prefix={prefix}\n suffix={\n <SuffixButtons\n onClickClear={onClickClear}\n clearButtonRef={clearButtonRef}\n onClickIcon={onClickInput}\n caretIconColor={caretIconColor}\n destroyButtonIconAlt={destroyButtonIconAlt}\n classNames={{\n clearButton: classNames.clearButton,\n clearButtonIcon: classNames.clearButtonIcon,\n caretDownLayout: classNames.caretDownLayout,\n caretDownIcon: classNames.caretDownIcon,\n }}\n />\n }\n className={classNames.input}\n data-smarthr-ui-input=\"true\"\n />\n {!readOnly && renderListBox()}\n </div>\n )\n}\n\nexport const SingleCombobox = genericsForwardRef(ActualSingleCombobox)\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4EA;AAEA;AACA;AAEA;AACE;AACA;;AAGF;AACE;AACE;AACA;AACA;;;;AAIC;AACD;AACA;;;;;AAKC;AACD;;;AAGC;AACF;AACD;AACE;AACE;AACE;AACD;AACF;AACD;AACE;AACE;AACD;AACF;AACF;AACF;AAgBD;AAgCA;AAmCE;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;;;;;;;AAwBC;;AAID;;AAEE;;;;AAID;AAED;;;;;AAKE;AAEI;AACA;;;;;;;;;;;AAYE;AACF;;AAGF;;;AAKF;;AAED;AAED;;AAEI;;AAEJ;AAEA;AACE;AACA;;AAGA;;;AAGF;AACA;;;AAGE;;;;AAMA;;;;AAGE;;AAEJ;AACA;;;;AAOM;;;;;AAKD;;AAGC;AACA;AAEA;;;;AAKJ;AAGF;AAEI;;;;AAMA;AAEA;;;;AAMJ;AAEI;AACA;;;AAIA;;AAIA;AACE;AACA;;AAEJ;AAGF;AACA;AACA;AAEI;;;;AAKE;;;;;AAIK;AACL;;;;;;AAMA;AAEA;;;;;;;;;AAYN;AAEI;;AAEA;AACF;;AAKA;;AAEE;AACA;AAEJ;;;;;AAUA;AAEA;AAEI;AACA;AACD;AAIH;AAEA;AACE;;;;;;;AASI;;;;;;AASA;AACA;AACD;AAIL;;AAmBM;;;;;AAsBK;AASb;;;"}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsxs,
|
|
3
|
-
import { useRef, useState, useImperativeHandle, useCallback, useMemo, useEffect } from 'react';
|
|
2
|
+
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
3
|
+
import { memo, useRef, useState, useImperativeHandle, useCallback, useMemo, useEffect } from 'react';
|
|
4
4
|
import innerText from '../../../_virtual/index3.js';
|
|
5
5
|
import { tv as ce } from './../../../vendor/.pnpm/tailwind-variants@0.3.1_tailwindcss@3.4.19_ts-node@10.9.2_@swc_core@1.15.17_@types_node@20.19.35_typescript@5.9.3__/vendor/tailwind-variants/dist/index.js';
|
|
6
6
|
import { useClick } from '../../../hooks/useClick.js';
|
|
7
|
+
import { useLatest } from '../../../hooks/useLatest.js';
|
|
7
8
|
import { useTheme } from '../../../hooks/useTheme.js';
|
|
8
9
|
import '../../../intl/IntlProvider.js';
|
|
9
10
|
import { useIntl } from '../../../intl/useIntl.js';
|
|
10
|
-
import '
|
|
11
|
+
import '../../../intl/Localizer.js';
|
|
11
12
|
import '../../../_virtual/dayjs.min.js';
|
|
12
13
|
import '../../../intl/TimeFormatter.js';
|
|
13
14
|
import { genericsForwardRef } from '../../../libs/util.js';
|
|
@@ -64,6 +65,7 @@ const classNameGenerator = ce({
|
|
|
64
65
|
},
|
|
65
66
|
},
|
|
66
67
|
});
|
|
68
|
+
const SuffixButtons = memo(({ onClickClear, clearButtonRef, onClickIcon: onDelegateClickIcon, caretIconColor, destroyButtonIconAlt, classNames, }) => (jsxs(Fragment, { children: [jsx(UnstyledButton, { onClick: onClickClear, ref: clearButtonRef, className: classNames.clearButton, children: jsx(FaCircleXmarkIcon, { color: "TEXT_BLACK", alt: destroyButtonIconAlt, className: classNames.clearButtonIcon }) }), jsx("span", { role: "presentation", onClick: onDelegateClickIcon, className: classNames.caretDownLayout, children: jsx(FaCaretDownIcon, { color: caretIconColor, className: classNames.caretDownIcon }) })] })));
|
|
67
69
|
const ActualSingleCombobox = ({ items, selectedItem, defaultItem, name, disabled, readOnly, required, prefix, error, creatable, placeholder, autoComplete, dropdownHelpMessage, isLoading, width, dropdownWidth = 'auto', className, onChange, onChangeInput, onAdd, onSelect, onClear, onClearClick, onChangeSelected, onFocus, onBlur, onKeyPress, noResultText, style, ...rest }, ref) => {
|
|
68
70
|
const theme = useTheme();
|
|
69
71
|
const { localize } = useIntl();
|
|
@@ -75,6 +77,24 @@ const ActualSingleCombobox = ({ items, selectedItem, defaultItem, name, disabled
|
|
|
75
77
|
const [inputValue, setInputValue] = useState('');
|
|
76
78
|
const [isComposing, setIsComposing] = useState(false);
|
|
77
79
|
const [isEditing, setIsEditing] = useState(false);
|
|
80
|
+
const latest = useLatest({
|
|
81
|
+
onChange,
|
|
82
|
+
onChangeInput,
|
|
83
|
+
onAdd,
|
|
84
|
+
onSelect,
|
|
85
|
+
onClear,
|
|
86
|
+
onClearClick,
|
|
87
|
+
onChangeSelected,
|
|
88
|
+
onFocus,
|
|
89
|
+
onBlur,
|
|
90
|
+
onKeyPress,
|
|
91
|
+
defaultItem,
|
|
92
|
+
selectedItem,
|
|
93
|
+
isFocused,
|
|
94
|
+
isExpanded,
|
|
95
|
+
isComposing,
|
|
96
|
+
isEditing,
|
|
97
|
+
});
|
|
78
98
|
useImperativeHandle(ref, () => inputRef.current);
|
|
79
99
|
const { options } = useSingleOptions({
|
|
80
100
|
items,
|
|
@@ -89,49 +109,58 @@ const ActualSingleCombobox = ({ items, selectedItem, defaultItem, name, disabled
|
|
|
89
109
|
dropdownWidth,
|
|
90
110
|
onAdd,
|
|
91
111
|
onSelect: useCallback((selected) => {
|
|
92
|
-
onSelect?.(selected);
|
|
93
|
-
onChangeSelected?.(selected);
|
|
94
|
-
// 制御コンポーネントの場合に親側でinputValueを更新できるように、選択時にonChangeInputを空文字で発火する
|
|
95
|
-
onChangeInput?.(EMPTY_INPUT_CHANGE_EVENT);
|
|
112
|
+
latest.onSelect?.(selected);
|
|
113
|
+
latest.onChangeSelected?.(selected);
|
|
96
114
|
// HINT: Dropdown系コンポーネント内でComboboxを使うと、選択肢がportalで表現されている関係上Dropdownが閉じてしまう
|
|
97
115
|
// requestAnimationFrameを追加、処理を遅延させることで正常に閉じる/閉じないの判定を行えるようにする
|
|
98
116
|
requestAnimationFrame(() => {
|
|
99
117
|
setIsExpanded(false);
|
|
118
|
+
// HINT:
|
|
119
|
+
// - 制御コンポーネントの場合に親側でinputValueを更新できるように、選択時にonChangeInputを空文字で発火する
|
|
120
|
+
// - 対応するdropdownを閉じて以降にonChangeInputを発火する必要がある
|
|
121
|
+
// - 先にclearしてしまうと意図せずこの要素のドロップダウンを閉じる前に他要素の再レンダリングを引き起こす可能性がある
|
|
122
|
+
// - 例えばFilterDropdownなどで当comboboxを使っている場合、レイアウト上comboboxのdropdown以下の要素がクリックされた扱いになってしまい
|
|
123
|
+
// FilterDropdownを意図せず閉じてしまうなどの挙動のバグを引き起こす可能性がある
|
|
124
|
+
latest.onChangeInput?.(EMPTY_INPUT_CHANGE_EVENT);
|
|
100
125
|
});
|
|
101
126
|
setIsEditing(false);
|
|
102
|
-
}, [
|
|
127
|
+
}, [latest]),
|
|
103
128
|
isExpanded,
|
|
104
129
|
isLoading,
|
|
105
130
|
triggerRef: outerRef,
|
|
106
131
|
noResultText,
|
|
107
132
|
});
|
|
108
|
-
const selectDefaultItem =
|
|
133
|
+
const selectDefaultItem = useCallback(() => {
|
|
134
|
+
if (latest.onSelect && latest.defaultItem) {
|
|
135
|
+
latest.onSelect(latest.defaultItem);
|
|
136
|
+
}
|
|
137
|
+
}, [latest]);
|
|
109
138
|
const focus = useCallback(() => {
|
|
110
|
-
onFocus?.();
|
|
139
|
+
latest.onFocus?.();
|
|
111
140
|
inputRef.current?.focus();
|
|
112
141
|
setIsFocused(true);
|
|
113
|
-
if (!isFocused) {
|
|
142
|
+
if (!latest.isFocused) {
|
|
114
143
|
setIsExpanded(true);
|
|
115
144
|
}
|
|
116
|
-
}, [
|
|
145
|
+
}, [latest]);
|
|
117
146
|
const unfocus = useCallback(() => {
|
|
118
|
-
if (!isFocused)
|
|
147
|
+
if (!latest.isFocused)
|
|
119
148
|
return;
|
|
120
|
-
onBlur?.();
|
|
149
|
+
latest.onBlur?.();
|
|
121
150
|
setIsFocused(false);
|
|
122
151
|
setIsExpanded(false);
|
|
123
152
|
setIsEditing(false);
|
|
124
|
-
if (selectedItem) {
|
|
125
|
-
setInputValue(innerText(selectedItem.label));
|
|
153
|
+
if (latest.selectedItem) {
|
|
154
|
+
setInputValue(innerText(latest.selectedItem.label));
|
|
126
155
|
}
|
|
127
156
|
else {
|
|
128
157
|
selectDefaultItem();
|
|
129
158
|
}
|
|
130
|
-
}, [
|
|
159
|
+
}, [selectDefaultItem, latest]);
|
|
131
160
|
const onClickClear = useCallback((e) => {
|
|
132
161
|
e.stopPropagation();
|
|
133
162
|
let isExecutedPreventDefault = false;
|
|
134
|
-
onClearClick?.({
|
|
163
|
+
latest.onClearClick?.({
|
|
135
164
|
...e,
|
|
136
165
|
preventDefault: () => {
|
|
137
166
|
e.preventDefault();
|
|
@@ -139,44 +168,43 @@ const ActualSingleCombobox = ({ items, selectedItem, defaultItem, name, disabled
|
|
|
139
168
|
},
|
|
140
169
|
});
|
|
141
170
|
if (!isExecutedPreventDefault) {
|
|
142
|
-
onClear?.();
|
|
143
|
-
onChangeSelected?.(null);
|
|
171
|
+
latest.onClear?.();
|
|
172
|
+
latest.onChangeSelected?.(null);
|
|
144
173
|
inputRef.current?.focus();
|
|
145
174
|
setIsFocused(true);
|
|
146
175
|
setIsExpanded(true);
|
|
147
176
|
}
|
|
148
|
-
}, [
|
|
177
|
+
}, [latest]);
|
|
149
178
|
const onClickInput = useCallback((e) => {
|
|
150
179
|
if (disabled || readOnly) {
|
|
151
180
|
e.stopPropagation();
|
|
152
181
|
return;
|
|
153
182
|
}
|
|
154
183
|
inputRef.current?.focus();
|
|
155
|
-
if (!isExpanded) {
|
|
184
|
+
if (!latest.isExpanded) {
|
|
156
185
|
setIsExpanded(true);
|
|
157
186
|
}
|
|
158
|
-
}, [disabled, readOnly,
|
|
159
|
-
const onDelegateClickIcon = onClickInput;
|
|
187
|
+
}, [disabled, readOnly, latest]);
|
|
160
188
|
const actualOnChangeInput = useCallback((e) => {
|
|
161
|
-
onChange?.(e);
|
|
162
|
-
onChangeInput?.(e);
|
|
163
|
-
if (!isEditing)
|
|
189
|
+
latest.onChange?.(e);
|
|
190
|
+
latest.onChangeInput?.(e);
|
|
191
|
+
if (!latest.isEditing)
|
|
164
192
|
setIsEditing(true);
|
|
165
193
|
const { value } = e.currentTarget;
|
|
166
194
|
setInputValue(value);
|
|
167
195
|
if (value === '') {
|
|
168
|
-
onClear?.();
|
|
169
|
-
onChangeSelected?.(null);
|
|
196
|
+
latest.onClear?.();
|
|
197
|
+
latest.onChangeSelected?.(null);
|
|
170
198
|
}
|
|
171
|
-
}, [
|
|
199
|
+
}, [latest]);
|
|
172
200
|
const onCompositionStart = useCallback(() => setIsComposing(true), []);
|
|
173
201
|
const onCompositionEnd = useCallback(() => setIsComposing(false), []);
|
|
174
202
|
const onKeyDownInput = useCallback((e) => {
|
|
175
|
-
if (isComposing) {
|
|
203
|
+
if (latest.isComposing) {
|
|
176
204
|
return;
|
|
177
205
|
}
|
|
178
206
|
if (ESCAPE_KEY_REGEX.test(e.key)) {
|
|
179
|
-
if (isExpanded) {
|
|
207
|
+
if (latest.isExpanded) {
|
|
180
208
|
e.stopPropagation();
|
|
181
209
|
setIsExpanded(false);
|
|
182
210
|
}
|
|
@@ -189,28 +217,26 @@ const ActualSingleCombobox = ({ items, selectedItem, defaultItem, name, disabled
|
|
|
189
217
|
e.preventDefault();
|
|
190
218
|
}
|
|
191
219
|
inputRef.current?.focus();
|
|
192
|
-
if (!isExpanded) {
|
|
220
|
+
if (!latest.isExpanded) {
|
|
193
221
|
setIsExpanded(true);
|
|
194
222
|
}
|
|
195
223
|
}
|
|
196
224
|
onKeyDownListBox(e);
|
|
197
|
-
}, [
|
|
225
|
+
}, [unfocus, onKeyDownListBox, latest]);
|
|
198
226
|
// HINT: form内にcomboboxを設置 & 検索inputにfocusした状態で
|
|
199
227
|
// アイテムをキーボードで選択し、Enterを押すとinput上でEnterを押したことになるため、
|
|
200
228
|
// submitイベントが発生し、formが送信される場合がある
|
|
201
229
|
const handleKeyPress = useCallback((e) => {
|
|
202
230
|
if (e.key === 'Enter')
|
|
203
231
|
e.preventDefault();
|
|
204
|
-
onKeyPress?.(e);
|
|
205
|
-
}, [
|
|
206
|
-
const caretIconColor =
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
}, [disabled, readOnly, isFocused, theme.textColor]);
|
|
213
|
-
useClick(useMemo(() => [outerRef, listBoxRef, clearButtonRef], [outerRef, listBoxRef, clearButtonRef]), isFocused || selectedItem ? NOOP : selectDefaultItem, unfocus);
|
|
232
|
+
latest.onKeyPress?.(e);
|
|
233
|
+
}, [latest]);
|
|
234
|
+
const caretIconColor = isFocused
|
|
235
|
+
? theme.textColor.black
|
|
236
|
+
: disabled || readOnly
|
|
237
|
+
? theme.textColor.disabled
|
|
238
|
+
: theme.textColor.grey;
|
|
239
|
+
useClick(useMemo(() => [outerRef, listBoxRef, clearButtonRef], [listBoxRef]), isFocused || selectedItem ? NOOP : selectDefaultItem, unfocus);
|
|
214
240
|
// selectedItem.label はプリミティブ値でないデータ型の可能性があり、そのまま useEffect の依存配列に入れると意図せぬエフェクトの実行を引き起こしてしまう可能性があるので、プリミティブ値である string 型に変換したものを依存配列に入れています。
|
|
215
241
|
const selectedItemLabelText = innerText(selectedItem?.label);
|
|
216
242
|
useEffect(() => {
|
|
@@ -220,7 +246,7 @@ const ActualSingleCombobox = ({ items, selectedItem, defaultItem, name, disabled
|
|
|
220
246
|
...style,
|
|
221
247
|
width: typeof width === 'number' ? `${width}px` : width,
|
|
222
248
|
}), [style, width]);
|
|
223
|
-
const
|
|
249
|
+
const hasSelectedItem = selectedItem !== null;
|
|
224
250
|
const classNames = useMemo(() => {
|
|
225
251
|
const { wrapper, input, caretDownLayout, caretDownIcon, clearButton, clearButtonIcon } = classNameGenerator();
|
|
226
252
|
return {
|
|
@@ -228,17 +254,24 @@ const ActualSingleCombobox = ({ items, selectedItem, defaultItem, name, disabled
|
|
|
228
254
|
input: input(),
|
|
229
255
|
caretDownLayout: caretDownLayout(),
|
|
230
256
|
caretDownIcon: caretDownIcon(),
|
|
231
|
-
clearButton: clearButton({
|
|
257
|
+
clearButton: clearButton({
|
|
258
|
+
hidden: !hasSelectedItem || disabled || readOnly,
|
|
259
|
+
}),
|
|
232
260
|
clearButtonIcon: clearButtonIcon(),
|
|
233
261
|
};
|
|
234
|
-
}, [
|
|
262
|
+
}, [hasSelectedItem, disabled, readOnly, className]);
|
|
235
263
|
const destroyButtonIconAlt = useMemo(() => localize({
|
|
236
264
|
id: 'smarthr-ui/SingleCombobox/destroyButtonIconAlt',
|
|
237
265
|
defaultText: 'クリア',
|
|
238
266
|
}), [localize]);
|
|
239
267
|
return (jsxs("div", { role: "group", className: classNames.wrapper, style: wrapperStyle, ref: outerRef, children: [jsx(Input, { ...rest, ref: inputRef, type: "text", role: "combobox", name: name, value: inputValue, disabled: disabled, readOnly: readOnly, required: required, autoComplete: autoComplete ?? 'off', "aria-haspopup": "listbox", "aria-controls": listBoxId, "aria-expanded": isFocused, "aria-activedescendant": activeOption?.id, "aria-autocomplete": "list",
|
|
240
268
|
/* eslint-disable-next-line smarthr/a11y-prohibit-input-placeholder */
|
|
241
|
-
placeholder: placeholder, onClick: onClickInput, onChange: actualOnChangeInput, onFocus: isFocused ? undefined : focus, onCompositionStart: onCompositionStart, onCompositionEnd: onCompositionEnd, onKeyDown: onKeyDownInput, onKeyPress: handleKeyPress, error: error, prefix: prefix, suffix:
|
|
269
|
+
placeholder: placeholder, onClick: onClickInput, onChange: actualOnChangeInput, onFocus: isFocused ? undefined : focus, onCompositionStart: onCompositionStart, onCompositionEnd: onCompositionEnd, onKeyDown: onKeyDownInput, onKeyPress: handleKeyPress, error: error, prefix: prefix, suffix: jsx(SuffixButtons, { onClickClear: onClickClear, clearButtonRef: clearButtonRef, onClickIcon: onClickInput, caretIconColor: caretIconColor, destroyButtonIconAlt: destroyButtonIconAlt, classNames: {
|
|
270
|
+
clearButton: classNames.clearButton,
|
|
271
|
+
clearButtonIcon: classNames.clearButtonIcon,
|
|
272
|
+
caretDownLayout: classNames.caretDownLayout,
|
|
273
|
+
caretDownIcon: classNames.caretDownIcon,
|
|
274
|
+
} }), className: classNames.input, "data-smarthr-ui-input": "true" }), !readOnly && renderListBox()] }));
|
|
242
275
|
};
|
|
243
276
|
const SingleCombobox = genericsForwardRef(ActualSingleCombobox);
|
|
244
277
|
|