smarthr-ui 98.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/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/SingleCombobox/SingleCombobox.cjs +35 -52
- package/lib/components/Combobox/SingleCombobox/SingleCombobox.cjs.map +1 -1
- package/lib/components/Combobox/SingleCombobox/SingleCombobox.js +35 -52
- package/lib/components/Combobox/SingleCombobox/SingleCombobox.js.map +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/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/Icon/FaIcon.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/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 -4
|
@@ -29,43 +29,44 @@ const classNameGenerator = index.tv({
|
|
|
29
29
|
},
|
|
30
30
|
});
|
|
31
31
|
const ItemButton = ({ option, onAdd, onSelect, onMouseOver, activeRef }) => {
|
|
32
|
+
const unstableRef = React.useRef({ onAdd, onSelect, onMouseOver, option });
|
|
33
|
+
unstableRef.current = { onAdd, onSelect, onMouseOver, option };
|
|
32
34
|
const handleMouseOver = React.useCallback(() => {
|
|
33
|
-
onMouseOver(option);
|
|
34
|
-
}, [
|
|
35
|
-
const
|
|
36
|
-
option
|
|
37
|
-
|
|
35
|
+
unstableRef.current.onMouseOver(unstableRef.current.option);
|
|
36
|
+
}, []);
|
|
37
|
+
const handleAddClick = React.useCallback(() => {
|
|
38
|
+
unstableRef.current.onAdd?.(unstableRef.current.option);
|
|
39
|
+
}, []);
|
|
40
|
+
const handleSelectClick = React.useCallback(() => {
|
|
41
|
+
unstableRef.current.onSelect(unstableRef.current.option);
|
|
42
|
+
}, []);
|
|
43
|
+
const commonAttrs = {
|
|
44
|
+
id: option.id,
|
|
45
|
+
label: option.item.label,
|
|
38
46
|
activeRef,
|
|
47
|
+
onMouseOver: handleMouseOver,
|
|
39
48
|
};
|
|
40
|
-
return option.isNew ? (jsxRuntime.jsx(AddButton, { ...
|
|
49
|
+
return option.isNew ? (jsxRuntime.jsx(AddButton, { ...commonAttrs, onClick: handleAddClick })) : (jsxRuntime.jsx(SelectButton, { ...commonAttrs, disabled: option.item.disabled, selected: option.selected, onClick: handleSelectClick }));
|
|
41
50
|
};
|
|
42
51
|
const typedMemo = React.memo;
|
|
43
52
|
const Memoized = typedMemo(ItemButton);
|
|
44
|
-
const AddButton = ({
|
|
45
|
-
const className =
|
|
53
|
+
const AddButton = React.memo(({ id, label, activeRef, onClick, onMouseOver }) => {
|
|
54
|
+
const className = classNameGenerator({
|
|
46
55
|
new: true,
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
? () => {
|
|
50
|
-
onAdd(option);
|
|
51
|
-
}
|
|
52
|
-
: undefined, [option, onAdd]);
|
|
53
|
-
return (jsxRuntime.jsx("button", { ref: activeRef, type: "button", role: "option", "aria-selected": false, id: option.id, "data-active": !!activeRef, onClick: onClick,
|
|
56
|
+
});
|
|
57
|
+
return (jsxRuntime.jsx("button", { ref: activeRef, type: "button", role: "option", "aria-selected": false, id: id, "data-active": !!activeRef, onClick: onClick,
|
|
54
58
|
// eslint-disable-next-line jsx-a11y/mouse-events-have-key-events
|
|
55
|
-
onMouseOver: onMouseOver, className: className, children: jsxRuntime.jsx(MemoizedNewIconWithText, { label:
|
|
56
|
-
};
|
|
59
|
+
onMouseOver: onMouseOver, className: className, children: jsxRuntime.jsx(MemoizedNewIconWithText, { label: label }) }));
|
|
60
|
+
});
|
|
57
61
|
const MemoizedNewIconWithText = React.memo(({ label }) => (jsxRuntime.jsx(components_Text_Text.Text, { color: "TEXT_LINK", icon: jsxRuntime.jsx(components_Icon_FaIcon.FaCirclePlusIcon, { color: "TEXT_LINK" }), children: jsxRuntime.jsx(intl_Localizer.Localizer, { id: "smarthr-ui/Combobox/addItemButtonLabel", defaultText: "\u300C{name}\u300D\u3092\u8FFD\u52A0", values: { name: label } }) })));
|
|
58
|
-
const SelectButton = ({
|
|
59
|
-
const className =
|
|
62
|
+
const SelectButton = React.memo(({ id, label, disabled, selected, activeRef, onClick, onMouseOver }) => {
|
|
63
|
+
const className = classNameGenerator({
|
|
60
64
|
new: false,
|
|
61
|
-
})
|
|
62
|
-
|
|
63
|
-
onSelect(option);
|
|
64
|
-
}, [onSelect, option]);
|
|
65
|
-
return (jsxRuntime.jsx("button", { ref: activeRef, type: "button", role: "option", id: option.id, disabled: option.item.disabled, "aria-selected": option.selected, "data-active": !!activeRef, onClick: handleSelect,
|
|
65
|
+
});
|
|
66
|
+
return (jsxRuntime.jsx("button", { ref: activeRef, type: "button", role: "option", id: id, disabled: disabled, "aria-selected": selected, "data-active": !!activeRef, onClick: onClick,
|
|
66
67
|
// eslint-disable-next-line jsx-a11y/mouse-events-have-key-events
|
|
67
|
-
onMouseOver: onMouseOver, className: className, children:
|
|
68
|
-
};
|
|
68
|
+
onMouseOver: onMouseOver, className: className, children: label }));
|
|
69
|
+
});
|
|
69
70
|
|
|
70
71
|
exports.ItemButton = Memoized;
|
|
71
72
|
//# sourceMappingURL=ItemButton.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ItemButton.cjs","sources":["../../../src/components/Combobox/ItemButton.tsx"],"sourcesContent":["import { type ReactNode, type RefObject, memo, useCallback,
|
|
1
|
+
{"version":3,"file":"ItemButton.cjs","sources":["../../../src/components/Combobox/ItemButton.tsx"],"sourcesContent":["import { type ReactNode, type RefObject, memo, useCallback, useRef } from 'react'\nimport { tv } from 'tailwind-variants'\n\nimport { Localizer } from '../../intl'\nimport { FaCirclePlusIcon } from '../Icon'\nimport { Text } from '../Text'\n\nimport type { ComboboxOption } from './types'\n\ntype Props<T> = {\n option: ComboboxOption<T>\n onAdd?: (option: ComboboxOption<T>) => void\n onSelect: (option: ComboboxOption<T>) => void\n onMouseOver: (option: ComboboxOption<T>) => void\n activeRef: RefObject<HTMLButtonElement> | undefined\n}\n\nconst classNameGenerator = tv({\n base: [\n 'shr-block shr-min-w-full shr-cursor-pointer shr-border-none shr-px-1 shr-py-0.5 shr-text-left shr-text-base shr-leading-tight',\n 'aria-selected:shr-text-white',\n 'disabled:shr-cursor-not-allowed disabled:shr-text-disabled',\n 'data-[active=true]:shr-bg-white-darken data-[active=true]:aria-selected:shr-bg-main-darken',\n 'data-[active=false]:shr-bg-white data-[active=false]:aria-selected:shr-bg-main',\n ],\n variants: {\n new: {\n true: 'smarthr-ui-Combobox-addButton shr-flex shr-items-center',\n false: 'smarthr-ui-Combobox-selectButton',\n },\n },\n})\n\nconst ItemButton = <T,>({ option, onAdd, onSelect, onMouseOver, activeRef }: Props<T>) => {\n const unstableRef = useRef({ onAdd, onSelect, onMouseOver, option })\n unstableRef.current = { onAdd, onSelect, onMouseOver, option }\n\n const handleMouseOver = useCallback(() => {\n unstableRef.current.onMouseOver(unstableRef.current.option)\n }, [])\n\n const handleAddClick = useCallback(() => {\n unstableRef.current.onAdd?.(unstableRef.current.option)\n }, [])\n\n const handleSelectClick = useCallback(() => {\n unstableRef.current.onSelect(unstableRef.current.option)\n }, [])\n\n const commonAttrs = {\n id: option.id,\n label: option.item.label,\n activeRef,\n onMouseOver: handleMouseOver,\n }\n\n return option.isNew ? (\n <AddButton {...commonAttrs} onClick={handleAddClick} />\n ) : (\n <SelectButton\n {...commonAttrs}\n disabled={option.item.disabled}\n selected={option.selected}\n onClick={handleSelectClick}\n />\n )\n}\nconst typedMemo: <T>(c: T) => T = memo\nconst Memoized = typedMemo(ItemButton)\nexport { Memoized as ItemButton }\n\nconst AddButton = memo<{\n id: string\n label: ReactNode\n activeRef: RefObject<HTMLButtonElement> | undefined\n onClick?: () => void\n onMouseOver: () => void\n}>(({ id, label, activeRef, onClick, onMouseOver }) => {\n const className = classNameGenerator({\n new: true,\n })\n\n return (\n <button\n ref={activeRef}\n type=\"button\"\n role=\"option\"\n aria-selected={false}\n id={id}\n data-active={!!activeRef}\n onClick={onClick}\n // eslint-disable-next-line jsx-a11y/mouse-events-have-key-events\n onMouseOver={onMouseOver}\n className={className}\n >\n <MemoizedNewIconWithText label={label} />\n </button>\n )\n})\n\nconst MemoizedNewIconWithText = memo<{ label: ReactNode }>(({ label }) => (\n <Text color=\"TEXT_LINK\" icon={<FaCirclePlusIcon color=\"TEXT_LINK\" />}>\n <Localizer\n id=\"smarthr-ui/Combobox/addItemButtonLabel\"\n defaultText=\"「{name}」を追加\"\n values={{ name: label }}\n />\n </Text>\n))\n\nconst SelectButton = memo<{\n id: string\n label: ReactNode\n disabled?: boolean\n selected: boolean\n activeRef: RefObject<HTMLButtonElement> | undefined\n onClick: () => void\n onMouseOver: () => void\n}>(({ id, label, disabled, selected, activeRef, onClick, onMouseOver }) => {\n const className = classNameGenerator({\n new: false,\n })\n\n return (\n <button\n ref={activeRef}\n type=\"button\"\n role=\"option\"\n id={id}\n disabled={disabled}\n aria-selected={selected}\n data-active={!!activeRef}\n onClick={onClick}\n // eslint-disable-next-line jsx-a11y/mouse-events-have-key-events\n onMouseOver={onMouseOver}\n className={className}\n >\n {label}\n </button>\n )\n})\n"],"names":["tv","useRef","useCallback","_jsx","memo","Text","FaCirclePlusIcon","Localizer"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx } from 'react/jsx-runtime';
|
|
2
|
-
import { memo,
|
|
2
|
+
import { memo, useRef, useCallback } from 'react';
|
|
3
3
|
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';
|
|
4
4
|
import '../../intl/IntlProvider.js';
|
|
5
5
|
import 'react-intl';
|
|
@@ -27,43 +27,44 @@ const classNameGenerator = ce({
|
|
|
27
27
|
},
|
|
28
28
|
});
|
|
29
29
|
const ItemButton = ({ option, onAdd, onSelect, onMouseOver, activeRef }) => {
|
|
30
|
+
const unstableRef = useRef({ onAdd, onSelect, onMouseOver, option });
|
|
31
|
+
unstableRef.current = { onAdd, onSelect, onMouseOver, option };
|
|
30
32
|
const handleMouseOver = useCallback(() => {
|
|
31
|
-
onMouseOver(option);
|
|
32
|
-
}, [
|
|
33
|
-
const
|
|
34
|
-
option
|
|
35
|
-
|
|
33
|
+
unstableRef.current.onMouseOver(unstableRef.current.option);
|
|
34
|
+
}, []);
|
|
35
|
+
const handleAddClick = useCallback(() => {
|
|
36
|
+
unstableRef.current.onAdd?.(unstableRef.current.option);
|
|
37
|
+
}, []);
|
|
38
|
+
const handleSelectClick = useCallback(() => {
|
|
39
|
+
unstableRef.current.onSelect(unstableRef.current.option);
|
|
40
|
+
}, []);
|
|
41
|
+
const commonAttrs = {
|
|
42
|
+
id: option.id,
|
|
43
|
+
label: option.item.label,
|
|
36
44
|
activeRef,
|
|
45
|
+
onMouseOver: handleMouseOver,
|
|
37
46
|
};
|
|
38
|
-
return option.isNew ? (jsx(AddButton, { ...
|
|
47
|
+
return option.isNew ? (jsx(AddButton, { ...commonAttrs, onClick: handleAddClick })) : (jsx(SelectButton, { ...commonAttrs, disabled: option.item.disabled, selected: option.selected, onClick: handleSelectClick }));
|
|
39
48
|
};
|
|
40
49
|
const typedMemo = memo;
|
|
41
50
|
const Memoized = typedMemo(ItemButton);
|
|
42
|
-
const AddButton = ({
|
|
43
|
-
const className =
|
|
51
|
+
const AddButton = memo(({ id, label, activeRef, onClick, onMouseOver }) => {
|
|
52
|
+
const className = classNameGenerator({
|
|
44
53
|
new: true,
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
? () => {
|
|
48
|
-
onAdd(option);
|
|
49
|
-
}
|
|
50
|
-
: undefined, [option, onAdd]);
|
|
51
|
-
return (jsx("button", { ref: activeRef, type: "button", role: "option", "aria-selected": false, id: option.id, "data-active": !!activeRef, onClick: onClick,
|
|
54
|
+
});
|
|
55
|
+
return (jsx("button", { ref: activeRef, type: "button", role: "option", "aria-selected": false, id: id, "data-active": !!activeRef, onClick: onClick,
|
|
52
56
|
// eslint-disable-next-line jsx-a11y/mouse-events-have-key-events
|
|
53
|
-
onMouseOver: onMouseOver, className: className, children: jsx(MemoizedNewIconWithText, { label:
|
|
54
|
-
};
|
|
57
|
+
onMouseOver: onMouseOver, className: className, children: jsx(MemoizedNewIconWithText, { label: label }) }));
|
|
58
|
+
});
|
|
55
59
|
const MemoizedNewIconWithText = memo(({ label }) => (jsx(Text, { color: "TEXT_LINK", icon: jsx(FaCirclePlusIcon, { color: "TEXT_LINK" }), children: jsx(Localizer, { id: "smarthr-ui/Combobox/addItemButtonLabel", defaultText: "\u300C{name}\u300D\u3092\u8FFD\u52A0", values: { name: label } }) })));
|
|
56
|
-
const SelectButton = ({
|
|
57
|
-
const className =
|
|
60
|
+
const SelectButton = memo(({ id, label, disabled, selected, activeRef, onClick, onMouseOver }) => {
|
|
61
|
+
const className = classNameGenerator({
|
|
58
62
|
new: false,
|
|
59
|
-
})
|
|
60
|
-
|
|
61
|
-
onSelect(option);
|
|
62
|
-
}, [onSelect, option]);
|
|
63
|
-
return (jsx("button", { ref: activeRef, type: "button", role: "option", id: option.id, disabled: option.item.disabled, "aria-selected": option.selected, "data-active": !!activeRef, onClick: handleSelect,
|
|
63
|
+
});
|
|
64
|
+
return (jsx("button", { ref: activeRef, type: "button", role: "option", id: id, disabled: disabled, "aria-selected": selected, "data-active": !!activeRef, onClick: onClick,
|
|
64
65
|
// eslint-disable-next-line jsx-a11y/mouse-events-have-key-events
|
|
65
|
-
onMouseOver: onMouseOver, className: className, children:
|
|
66
|
-
};
|
|
66
|
+
onMouseOver: onMouseOver, className: className, children: label }));
|
|
67
|
+
});
|
|
67
68
|
|
|
68
69
|
export { Memoized as ItemButton };
|
|
69
70
|
//# sourceMappingURL=ItemButton.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ItemButton.js","sources":["../../../src/components/Combobox/ItemButton.tsx"],"sourcesContent":["import { type ReactNode, type RefObject, memo, useCallback,
|
|
1
|
+
{"version":3,"file":"ItemButton.js","sources":["../../../src/components/Combobox/ItemButton.tsx"],"sourcesContent":["import { type ReactNode, type RefObject, memo, useCallback, useRef } from 'react'\nimport { tv } from 'tailwind-variants'\n\nimport { Localizer } from '../../intl'\nimport { FaCirclePlusIcon } from '../Icon'\nimport { Text } from '../Text'\n\nimport type { ComboboxOption } from './types'\n\ntype Props<T> = {\n option: ComboboxOption<T>\n onAdd?: (option: ComboboxOption<T>) => void\n onSelect: (option: ComboboxOption<T>) => void\n onMouseOver: (option: ComboboxOption<T>) => void\n activeRef: RefObject<HTMLButtonElement> | undefined\n}\n\nconst classNameGenerator = tv({\n base: [\n 'shr-block shr-min-w-full shr-cursor-pointer shr-border-none shr-px-1 shr-py-0.5 shr-text-left shr-text-base shr-leading-tight',\n 'aria-selected:shr-text-white',\n 'disabled:shr-cursor-not-allowed disabled:shr-text-disabled',\n 'data-[active=true]:shr-bg-white-darken data-[active=true]:aria-selected:shr-bg-main-darken',\n 'data-[active=false]:shr-bg-white data-[active=false]:aria-selected:shr-bg-main',\n ],\n variants: {\n new: {\n true: 'smarthr-ui-Combobox-addButton shr-flex shr-items-center',\n false: 'smarthr-ui-Combobox-selectButton',\n },\n },\n})\n\nconst ItemButton = <T,>({ option, onAdd, onSelect, onMouseOver, activeRef }: Props<T>) => {\n const unstableRef = useRef({ onAdd, onSelect, onMouseOver, option })\n unstableRef.current = { onAdd, onSelect, onMouseOver, option }\n\n const handleMouseOver = useCallback(() => {\n unstableRef.current.onMouseOver(unstableRef.current.option)\n }, [])\n\n const handleAddClick = useCallback(() => {\n unstableRef.current.onAdd?.(unstableRef.current.option)\n }, [])\n\n const handleSelectClick = useCallback(() => {\n unstableRef.current.onSelect(unstableRef.current.option)\n }, [])\n\n const commonAttrs = {\n id: option.id,\n label: option.item.label,\n activeRef,\n onMouseOver: handleMouseOver,\n }\n\n return option.isNew ? (\n <AddButton {...commonAttrs} onClick={handleAddClick} />\n ) : (\n <SelectButton\n {...commonAttrs}\n disabled={option.item.disabled}\n selected={option.selected}\n onClick={handleSelectClick}\n />\n )\n}\nconst typedMemo: <T>(c: T) => T = memo\nconst Memoized = typedMemo(ItemButton)\nexport { Memoized as ItemButton }\n\nconst AddButton = memo<{\n id: string\n label: ReactNode\n activeRef: RefObject<HTMLButtonElement> | undefined\n onClick?: () => void\n onMouseOver: () => void\n}>(({ id, label, activeRef, onClick, onMouseOver }) => {\n const className = classNameGenerator({\n new: true,\n })\n\n return (\n <button\n ref={activeRef}\n type=\"button\"\n role=\"option\"\n aria-selected={false}\n id={id}\n data-active={!!activeRef}\n onClick={onClick}\n // eslint-disable-next-line jsx-a11y/mouse-events-have-key-events\n onMouseOver={onMouseOver}\n className={className}\n >\n <MemoizedNewIconWithText label={label} />\n </button>\n )\n})\n\nconst MemoizedNewIconWithText = memo<{ label: ReactNode }>(({ label }) => (\n <Text color=\"TEXT_LINK\" icon={<FaCirclePlusIcon color=\"TEXT_LINK\" />}>\n <Localizer\n id=\"smarthr-ui/Combobox/addItemButtonLabel\"\n defaultText=\"「{name}」を追加\"\n values={{ name: label }}\n />\n </Text>\n))\n\nconst SelectButton = memo<{\n id: string\n label: ReactNode\n disabled?: boolean\n selected: boolean\n activeRef: RefObject<HTMLButtonElement> | undefined\n onClick: () => void\n onMouseOver: () => void\n}>(({ id, label, disabled, selected, activeRef, onClick, onMouseOver }) => {\n const className = classNameGenerator({\n new: false,\n })\n\n return (\n <button\n ref={activeRef}\n type=\"button\"\n role=\"option\"\n id={id}\n disabled={disabled}\n aria-selected={selected}\n data-active={!!activeRef}\n onClick={onClick}\n // eslint-disable-next-line jsx-a11y/mouse-events-have-key-events\n onMouseOver={onMouseOver}\n className={className}\n >\n {label}\n </button>\n )\n})\n"],"names":["tv","_jsx"],"mappings":"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"}
|
|
@@ -6,6 +6,7 @@ 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');
|
|
@@ -78,7 +79,7 @@ const ActualSingleCombobox = ({ items, selectedItem, defaultItem, name, disabled
|
|
|
78
79
|
const [inputValue, setInputValue] = React.useState('');
|
|
79
80
|
const [isComposing, setIsComposing] = React.useState(false);
|
|
80
81
|
const [isEditing, setIsEditing] = React.useState(false);
|
|
81
|
-
const
|
|
82
|
+
const latest = hooks_useLatest.useLatest({
|
|
82
83
|
onChange,
|
|
83
84
|
onChangeInput,
|
|
84
85
|
onAdd,
|
|
@@ -96,24 +97,6 @@ const ActualSingleCombobox = ({ items, selectedItem, defaultItem, name, disabled
|
|
|
96
97
|
isComposing,
|
|
97
98
|
isEditing,
|
|
98
99
|
});
|
|
99
|
-
unstableRef.current = {
|
|
100
|
-
onChange,
|
|
101
|
-
onChangeInput,
|
|
102
|
-
onAdd,
|
|
103
|
-
onSelect,
|
|
104
|
-
onClear,
|
|
105
|
-
onClearClick,
|
|
106
|
-
onChangeSelected,
|
|
107
|
-
onFocus,
|
|
108
|
-
onBlur,
|
|
109
|
-
onKeyPress,
|
|
110
|
-
defaultItem,
|
|
111
|
-
selectedItem,
|
|
112
|
-
isFocused,
|
|
113
|
-
isExpanded,
|
|
114
|
-
isComposing,
|
|
115
|
-
isEditing,
|
|
116
|
-
};
|
|
117
100
|
React.useImperativeHandle(ref, () => inputRef.current);
|
|
118
101
|
const { options } = components_Combobox_useOptions.useSingleOptions({
|
|
119
102
|
items,
|
|
@@ -128,8 +111,8 @@ const ActualSingleCombobox = ({ items, selectedItem, defaultItem, name, disabled
|
|
|
128
111
|
dropdownWidth,
|
|
129
112
|
onAdd,
|
|
130
113
|
onSelect: React.useCallback((selected) => {
|
|
131
|
-
|
|
132
|
-
|
|
114
|
+
latest.onSelect?.(selected);
|
|
115
|
+
latest.onChangeSelected?.(selected);
|
|
133
116
|
// HINT: Dropdown系コンポーネント内でComboboxを使うと、選択肢がportalで表現されている関係上Dropdownが閉じてしまう
|
|
134
117
|
// requestAnimationFrameを追加、処理を遅延させることで正常に閉じる/閉じないの判定を行えるようにする
|
|
135
118
|
requestAnimationFrame(() => {
|
|
@@ -140,46 +123,46 @@ const ActualSingleCombobox = ({ items, selectedItem, defaultItem, name, disabled
|
|
|
140
123
|
// - 先にclearしてしまうと意図せずこの要素のドロップダウンを閉じる前に他要素の再レンダリングを引き起こす可能性がある
|
|
141
124
|
// - 例えばFilterDropdownなどで当comboboxを使っている場合、レイアウト上comboboxのdropdown以下の要素がクリックされた扱いになってしまい
|
|
142
125
|
// FilterDropdownを意図せず閉じてしまうなどの挙動のバグを引き起こす可能性がある
|
|
143
|
-
|
|
126
|
+
latest.onChangeInput?.(EMPTY_INPUT_CHANGE_EVENT);
|
|
144
127
|
});
|
|
145
128
|
setIsEditing(false);
|
|
146
|
-
}, []),
|
|
129
|
+
}, [latest]),
|
|
147
130
|
isExpanded,
|
|
148
131
|
isLoading,
|
|
149
132
|
triggerRef: outerRef,
|
|
150
133
|
noResultText,
|
|
151
134
|
});
|
|
152
135
|
const selectDefaultItem = React.useCallback(() => {
|
|
153
|
-
if (
|
|
154
|
-
|
|
136
|
+
if (latest.onSelect && latest.defaultItem) {
|
|
137
|
+
latest.onSelect(latest.defaultItem);
|
|
155
138
|
}
|
|
156
|
-
}, []);
|
|
139
|
+
}, [latest]);
|
|
157
140
|
const focus = React.useCallback(() => {
|
|
158
|
-
|
|
141
|
+
latest.onFocus?.();
|
|
159
142
|
inputRef.current?.focus();
|
|
160
143
|
setIsFocused(true);
|
|
161
|
-
if (!
|
|
144
|
+
if (!latest.isFocused) {
|
|
162
145
|
setIsExpanded(true);
|
|
163
146
|
}
|
|
164
|
-
}, []);
|
|
147
|
+
}, [latest]);
|
|
165
148
|
const unfocus = React.useCallback(() => {
|
|
166
|
-
if (!
|
|
149
|
+
if (!latest.isFocused)
|
|
167
150
|
return;
|
|
168
|
-
|
|
151
|
+
latest.onBlur?.();
|
|
169
152
|
setIsFocused(false);
|
|
170
153
|
setIsExpanded(false);
|
|
171
154
|
setIsEditing(false);
|
|
172
|
-
if (
|
|
173
|
-
setInputValue(index$1(
|
|
155
|
+
if (latest.selectedItem) {
|
|
156
|
+
setInputValue(index$1(latest.selectedItem.label));
|
|
174
157
|
}
|
|
175
158
|
else {
|
|
176
159
|
selectDefaultItem();
|
|
177
160
|
}
|
|
178
|
-
}, [selectDefaultItem]);
|
|
161
|
+
}, [selectDefaultItem, latest]);
|
|
179
162
|
const onClickClear = React.useCallback((e) => {
|
|
180
163
|
e.stopPropagation();
|
|
181
164
|
let isExecutedPreventDefault = false;
|
|
182
|
-
|
|
165
|
+
latest.onClearClick?.({
|
|
183
166
|
...e,
|
|
184
167
|
preventDefault: () => {
|
|
185
168
|
e.preventDefault();
|
|
@@ -187,43 +170,43 @@ const ActualSingleCombobox = ({ items, selectedItem, defaultItem, name, disabled
|
|
|
187
170
|
},
|
|
188
171
|
});
|
|
189
172
|
if (!isExecutedPreventDefault) {
|
|
190
|
-
|
|
191
|
-
|
|
173
|
+
latest.onClear?.();
|
|
174
|
+
latest.onChangeSelected?.(null);
|
|
192
175
|
inputRef.current?.focus();
|
|
193
176
|
setIsFocused(true);
|
|
194
177
|
setIsExpanded(true);
|
|
195
178
|
}
|
|
196
|
-
}, []);
|
|
179
|
+
}, [latest]);
|
|
197
180
|
const onClickInput = React.useCallback((e) => {
|
|
198
181
|
if (disabled || readOnly) {
|
|
199
182
|
e.stopPropagation();
|
|
200
183
|
return;
|
|
201
184
|
}
|
|
202
185
|
inputRef.current?.focus();
|
|
203
|
-
if (!
|
|
186
|
+
if (!latest.isExpanded) {
|
|
204
187
|
setIsExpanded(true);
|
|
205
188
|
}
|
|
206
|
-
}, [disabled, readOnly]);
|
|
189
|
+
}, [disabled, readOnly, latest]);
|
|
207
190
|
const actualOnChangeInput = React.useCallback((e) => {
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
if (!
|
|
191
|
+
latest.onChange?.(e);
|
|
192
|
+
latest.onChangeInput?.(e);
|
|
193
|
+
if (!latest.isEditing)
|
|
211
194
|
setIsEditing(true);
|
|
212
195
|
const { value } = e.currentTarget;
|
|
213
196
|
setInputValue(value);
|
|
214
197
|
if (value === '') {
|
|
215
|
-
|
|
216
|
-
|
|
198
|
+
latest.onClear?.();
|
|
199
|
+
latest.onChangeSelected?.(null);
|
|
217
200
|
}
|
|
218
|
-
}, []);
|
|
201
|
+
}, [latest]);
|
|
219
202
|
const onCompositionStart = React.useCallback(() => setIsComposing(true), []);
|
|
220
203
|
const onCompositionEnd = React.useCallback(() => setIsComposing(false), []);
|
|
221
204
|
const onKeyDownInput = React.useCallback((e) => {
|
|
222
|
-
if (
|
|
205
|
+
if (latest.isComposing) {
|
|
223
206
|
return;
|
|
224
207
|
}
|
|
225
208
|
if (ESCAPE_KEY_REGEX.test(e.key)) {
|
|
226
|
-
if (
|
|
209
|
+
if (latest.isExpanded) {
|
|
227
210
|
e.stopPropagation();
|
|
228
211
|
setIsExpanded(false);
|
|
229
212
|
}
|
|
@@ -236,20 +219,20 @@ const ActualSingleCombobox = ({ items, selectedItem, defaultItem, name, disabled
|
|
|
236
219
|
e.preventDefault();
|
|
237
220
|
}
|
|
238
221
|
inputRef.current?.focus();
|
|
239
|
-
if (!
|
|
222
|
+
if (!latest.isExpanded) {
|
|
240
223
|
setIsExpanded(true);
|
|
241
224
|
}
|
|
242
225
|
}
|
|
243
226
|
onKeyDownListBox(e);
|
|
244
|
-
}, [unfocus, onKeyDownListBox]);
|
|
227
|
+
}, [unfocus, onKeyDownListBox, latest]);
|
|
245
228
|
// HINT: form内にcomboboxを設置 & 検索inputにfocusした状態で
|
|
246
229
|
// アイテムをキーボードで選択し、Enterを押すとinput上でEnterを押したことになるため、
|
|
247
230
|
// submitイベントが発生し、formが送信される場合がある
|
|
248
231
|
const handleKeyPress = React.useCallback((e) => {
|
|
249
232
|
if (e.key === 'Enter')
|
|
250
233
|
e.preventDefault();
|
|
251
|
-
|
|
252
|
-
}, []);
|
|
234
|
+
latest.onKeyPress?.(e);
|
|
235
|
+
}, [latest]);
|
|
253
236
|
const caretIconColor = isFocused
|
|
254
237
|
? theme.textColor.black
|
|
255
238
|
: disabled || readOnly
|
|
@@ -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 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 { 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 unstableRef = useRef({\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 unstableRef.current = {\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((selected: ComboboxItem<T>) => {\n unstableRef.current.onSelect?.(selected)\n unstableRef.current.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 unstableRef.current.onChangeInput?.(EMPTY_INPUT_CHANGE_EVENT)\n })\n\n setIsEditing(false)\n }, []),\n isExpanded,\n isLoading,\n triggerRef: outerRef,\n noResultText,\n })\n\n const selectDefaultItem = useCallback(() => {\n if (unstableRef.current.onSelect && unstableRef.current.defaultItem) {\n unstableRef.current.onSelect(unstableRef.current.defaultItem)\n }\n }, [])\n\n const focus = useCallback(() => {\n unstableRef.current.onFocus?.()\n inputRef.current?.focus()\n setIsFocused(true)\n\n if (!unstableRef.current.isFocused) {\n setIsExpanded(true)\n }\n }, [])\n const unfocus = useCallback(() => {\n if (!unstableRef.current.isFocused) return\n\n unstableRef.current.onBlur?.()\n\n setIsFocused(false)\n setIsExpanded(false)\n setIsEditing(false)\n\n if (unstableRef.current.selectedItem) {\n setInputValue(innerText(unstableRef.current.selectedItem.label))\n } else {\n selectDefaultItem()\n }\n }, [selectDefaultItem])\n const onClickClear = useCallback((e: MouseEvent) => {\n e.stopPropagation()\n\n let isExecutedPreventDefault = false\n\n unstableRef.current.onClearClick?.({\n ...e,\n preventDefault: () => {\n e.preventDefault()\n isExecutedPreventDefault = true\n },\n })\n\n if (!isExecutedPreventDefault) {\n unstableRef.current.onClear?.()\n unstableRef.current.onChangeSelected?.(null)\n\n inputRef.current?.focus()\n\n setIsFocused(true)\n setIsExpanded(true)\n }\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 (!unstableRef.current.isExpanded) {\n setIsExpanded(true)\n }\n },\n [disabled, readOnly],\n )\n const actualOnChangeInput = useCallback((e: ChangeEvent<HTMLInputElement>) => {\n unstableRef.current.onChange?.(e)\n unstableRef.current.onChangeInput?.(e)\n\n if (!unstableRef.current.isEditing) setIsEditing(true)\n\n const { value } = e.currentTarget\n\n setInputValue(value)\n\n if (value === '') {\n unstableRef.current.onClear?.()\n unstableRef.current.onChangeSelected?.(null)\n }\n }, [])\n const onCompositionStart = useCallback(() => setIsComposing(true), [])\n const onCompositionEnd = useCallback(() => setIsComposing(false), [])\n const onKeyDownInput = useCallback(\n (e: KeyboardEvent<HTMLInputElement>) => {\n if (unstableRef.current.isComposing) {\n return\n }\n\n if (ESCAPE_KEY_REGEX.test(e.key)) {\n if (unstableRef.current.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 (!unstableRef.current.isExpanded) {\n setIsExpanded(true)\n }\n }\n onKeyDownListBox(e)\n },\n [unfocus, onKeyDownListBox],\n )\n\n // HINT: form内にcomboboxを設置 & 検索inputにfocusした状態で\n // アイテムをキーボードで選択し、Enterを押すとinput上でEnterを押したことになるため、\n // submitイベントが発生し、formが送信される場合がある\n const handleKeyPress = useCallback((e: KeyboardEvent<HTMLInputElement>) => {\n if (e.key === 'Enter') e.preventDefault()\n\n unstableRef.current.onKeyPress?.(e)\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":";;;;;;;;;;;;;;;;;;;;;;;;;;;AA2EA;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;;;;;;;;;;;;;;;;;;;;AAsBD;;AAEE;;;;AAID;AAED;;;;;AAKE;;;;;;;;;;;;;;AAeE;;;;;AAMF;;AAED;AAED;AACE;;;;AAKF;AACE;AACA;;AAGA;;;;AAIF;AACE;;AAEA;;;;AAMA;AACE;;;AAEA;;AAEJ;AACA;;;AAKE;AACE;;;;;AAKD;;AAGC;;AAGA;;;;;AAMJ;AAEI;;;;AAMA;AAEA;;;AAGF;AAGF;;;AAIE;;AAEA;;AAIA;AACE;;;;AAIJ;AACA;AACA;AAEI;;;;AAKE;;;;;AAIK;AACL;;;;;;AAMA;AAEA;;;;;AAKJ;;;;AAOF;AACE;;;;;AAMA;;AAEE;AACA;AAEJ;;;;;AAUA;AAEA;AAEI;AACA;AACD;AAIH;AAEA;AACE;;;;;;;AASI;;;;;;AASA;AACA;AACD;AAIL;;AAmBM;;;;;AAsBK;AASb;;;"}
|
|
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;;;"}
|