smarthr-ui 97.0.0 → 98.0.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.
Files changed (208) hide show
  1. package/lib/components/AppHeader/components/common/AppLauncherFeatures.cjs +1 -1
  2. package/lib/components/AppHeader/components/common/AppLauncherFeatures.js +1 -1
  3. package/lib/components/AppHeader/components/common/AppLauncherSortDropdown.cjs +1 -1
  4. package/lib/components/AppHeader/components/common/AppLauncherSortDropdown.js +1 -1
  5. package/lib/components/AppHeader/components/desktop/AppLauncher.cjs +5 -5
  6. package/lib/components/AppHeader/components/desktop/AppLauncher.cjs.map +1 -1
  7. package/lib/components/AppHeader/components/desktop/AppLauncher.js +5 -5
  8. package/lib/components/AppHeader/components/desktop/AppLauncher.js.map +1 -1
  9. package/lib/components/AppHeader/components/desktop/DesktopHeader.cjs +1 -1
  10. package/lib/components/AppHeader/components/desktop/DesktopHeader.js +1 -1
  11. package/lib/components/AppHeader/components/desktop/ReleaseNotesDropdown.cjs +1 -1
  12. package/lib/components/AppHeader/components/desktop/ReleaseNotesDropdown.js +1 -1
  13. package/lib/components/AppHeader/components/desktop/UserInfo.cjs +1 -1
  14. package/lib/components/AppHeader/components/desktop/UserInfo.js +1 -1
  15. package/lib/components/AppHeader/components/mobile/AppLauncher.cjs +1 -1
  16. package/lib/components/AppHeader/components/mobile/AppLauncher.js +1 -1
  17. package/lib/components/AppHeader/components/mobile/AppLauncherFilterDropdown.cjs +1 -1
  18. package/lib/components/AppHeader/components/mobile/AppLauncherFilterDropdown.js +1 -1
  19. package/lib/components/AppHeader/components/mobile/Help.cjs +1 -1
  20. package/lib/components/AppHeader/components/mobile/Help.js +1 -1
  21. package/lib/components/AppHeader/components/mobile/LanguageSelector.cjs +1 -1
  22. package/lib/components/AppHeader/components/mobile/LanguageSelector.js +1 -1
  23. package/lib/components/AppHeader/components/mobile/Menu.cjs +1 -1
  24. package/lib/components/AppHeader/components/mobile/Menu.js +1 -1
  25. package/lib/components/AppHeader/components/mobile/MenuAccordion.cjs +1 -1
  26. package/lib/components/AppHeader/components/mobile/MenuAccordion.js +1 -1
  27. package/lib/components/AppHeader/components/mobile/MenuDialog.cjs +2 -2
  28. package/lib/components/AppHeader/components/mobile/MenuDialog.js +2 -2
  29. package/lib/components/AppHeader/components/mobile/MenuSubHeading.cjs +1 -1
  30. package/lib/components/AppHeader/components/mobile/MenuSubHeading.js +1 -1
  31. package/lib/components/AppHeader/components/mobile/ReleaseNote.cjs +1 -1
  32. package/lib/components/AppHeader/components/mobile/ReleaseNote.js +1 -1
  33. package/lib/components/AppHeader/components/mobile/UserInfo.cjs +2 -2
  34. package/lib/components/AppHeader/components/mobile/UserInfo.js +2 -2
  35. package/lib/components/Browser/Browser.cjs +11 -7
  36. package/lib/components/Browser/Browser.cjs.map +1 -1
  37. package/lib/components/Browser/Browser.js +12 -8
  38. package/lib/components/Browser/Browser.js.map +1 -1
  39. package/lib/components/Calendar/Calendar.cjs +1 -1
  40. package/lib/components/Calendar/Calendar.js +1 -1
  41. package/lib/components/Calendar/CalendarTable.cjs +1 -1
  42. package/lib/components/Calendar/CalendarTable.js +1 -1
  43. package/lib/components/Calendar/YearPicker.cjs +1 -1
  44. package/lib/components/Calendar/YearPicker.js +1 -1
  45. package/lib/components/Combobox/MultiCombobox/MultiCombobox.cjs +122 -134
  46. package/lib/components/Combobox/MultiCombobox/MultiCombobox.cjs.map +1 -1
  47. package/lib/components/Combobox/MultiCombobox/MultiCombobox.js +123 -135
  48. package/lib/components/Combobox/MultiCombobox/MultiCombobox.js.map +1 -1
  49. package/lib/components/Combobox/MultiCombobox/MultiSelectedItem.cjs +1 -1
  50. package/lib/components/Combobox/MultiCombobox/MultiSelectedItem.js +1 -1
  51. package/lib/components/Combobox/SingleCombobox/SingleCombobox.cjs +96 -46
  52. package/lib/components/Combobox/SingleCombobox/SingleCombobox.cjs.map +1 -1
  53. package/lib/components/Combobox/SingleCombobox/SingleCombobox.js +98 -48
  54. package/lib/components/Combobox/SingleCombobox/SingleCombobox.js.map +1 -1
  55. package/lib/components/Combobox/useListbox.cjs +1 -1
  56. package/lib/components/Combobox/useListbox.js +1 -1
  57. package/lib/components/Combobox/useOptions.cjs +3 -1
  58. package/lib/components/Combobox/useOptions.cjs.map +1 -1
  59. package/lib/components/Combobox/useOptions.js +4 -2
  60. package/lib/components/Combobox/useOptions.js.map +1 -1
  61. package/lib/components/Dialog/ControlledActionDialog/ActionDialogContentInner.cjs +3 -3
  62. package/lib/components/Dialog/ControlledActionDialog/ActionDialogContentInner.cjs.map +1 -1
  63. package/lib/components/Dialog/ControlledActionDialog/ActionDialogContentInner.js +3 -3
  64. package/lib/components/Dialog/ControlledActionDialog/ActionDialogContentInner.js.map +1 -1
  65. package/lib/components/Dialog/ControlledFormDialog/FormDialogContentInner.cjs +3 -3
  66. package/lib/components/Dialog/ControlledFormDialog/FormDialogContentInner.cjs.map +1 -1
  67. package/lib/components/Dialog/ControlledFormDialog/FormDialogContentInner.js +3 -3
  68. package/lib/components/Dialog/ControlledFormDialog/FormDialogContentInner.js.map +1 -1
  69. package/lib/components/Dialog/ControlledMessageDialog/MessageDialogContentInner.cjs +1 -1
  70. package/lib/components/Dialog/ControlledMessageDialog/MessageDialogContentInner.js +1 -1
  71. package/lib/components/Dialog/ControlledStepFormDialog/ControlledStepFormDialog.cjs +1 -1
  72. package/lib/components/Dialog/ControlledStepFormDialog/ControlledStepFormDialog.js +1 -1
  73. package/lib/components/Dialog/ControlledStepFormDialog/StepFormDialogContentInner.cjs +2 -2
  74. package/lib/components/Dialog/ControlledStepFormDialog/StepFormDialogContentInner.cjs.map +1 -1
  75. package/lib/components/Dialog/ControlledStepFormDialog/StepFormDialogContentInner.js +2 -2
  76. package/lib/components/Dialog/ControlledStepFormDialog/StepFormDialogContentInner.js.map +1 -1
  77. package/lib/components/Dialog/DialogContentResponseStatusMessage.cjs +3 -8
  78. package/lib/components/Dialog/DialogContentResponseStatusMessage.cjs.map +1 -1
  79. package/lib/components/Dialog/DialogContentResponseStatusMessage.d.ts +1 -1
  80. package/lib/components/Dialog/DialogContentResponseStatusMessage.js +3 -8
  81. package/lib/components/Dialog/DialogContentResponseStatusMessage.js.map +1 -1
  82. package/lib/components/Dialog/FocusTrap.cjs +4 -1
  83. package/lib/components/Dialog/FocusTrap.cjs.map +1 -1
  84. package/lib/components/Dialog/FocusTrap.js +4 -1
  85. package/lib/components/Dialog/FocusTrap.js.map +1 -1
  86. package/lib/components/Dialog/ModelessDialog/ModelessDialog.cjs +1 -1
  87. package/lib/components/Dialog/ModelessDialog/ModelessDialog.js +1 -1
  88. package/lib/components/DropZone/DropZone.cjs +1 -1
  89. package/lib/components/DropZone/DropZone.js +1 -1
  90. package/lib/components/Dropdown/Dropdown.cjs +36 -34
  91. package/lib/components/Dropdown/Dropdown.cjs.map +1 -1
  92. package/lib/components/Dropdown/Dropdown.js +37 -35
  93. package/lib/components/Dropdown/Dropdown.js.map +1 -1
  94. package/lib/components/Dropdown/DropdownMenuButton/DropdownMenuButton.cjs +1 -1
  95. package/lib/components/Dropdown/DropdownMenuButton/DropdownMenuButton.js +1 -1
  96. package/lib/components/Dropdown/FilterDropdown/FilterDropdown.cjs +3 -5
  97. package/lib/components/Dropdown/FilterDropdown/FilterDropdown.cjs.map +1 -1
  98. package/lib/components/Dropdown/FilterDropdown/FilterDropdown.d.ts +1 -1
  99. package/lib/components/Dropdown/FilterDropdown/FilterDropdown.js +3 -5
  100. package/lib/components/Dropdown/FilterDropdown/FilterDropdown.js.map +1 -1
  101. package/lib/components/Dropdown/SortDropdown/useSortDropdown.cjs +1 -1
  102. package/lib/components/Dropdown/SortDropdown/useSortDropdown.js +1 -1
  103. package/lib/components/ErrorScreen/AuthErrorScreen.cjs +1 -1
  104. package/lib/components/ErrorScreen/AuthErrorScreen.js +1 -1
  105. package/lib/components/ErrorScreen/ForbiddenErrorScreen.cjs +1 -1
  106. package/lib/components/ErrorScreen/ForbiddenErrorScreen.js +1 -1
  107. package/lib/components/ErrorScreen/NotFoundErrorScreen.cjs +1 -1
  108. package/lib/components/ErrorScreen/NotFoundErrorScreen.js +1 -1
  109. package/lib/components/ErrorScreen/UnauthorizedErrorScreen.cjs +1 -1
  110. package/lib/components/ErrorScreen/UnauthorizedErrorScreen.js +1 -1
  111. package/lib/components/FormControl/FormControl.cjs +2 -22
  112. package/lib/components/FormControl/FormControl.cjs.map +1 -1
  113. package/lib/components/FormControl/FormControl.js +2 -22
  114. package/lib/components/FormControl/FormControl.js.map +1 -1
  115. package/lib/components/Header/AppLauncher/AppLauncher.cjs +1 -1
  116. package/lib/components/Header/AppLauncher/AppLauncher.js +1 -1
  117. package/lib/components/Header/LanguageSwitcher/LanguageSwitcher.cjs +1 -1
  118. package/lib/components/Header/LanguageSwitcher/LanguageSwitcher.js +1 -1
  119. package/lib/components/Icon/OpenInNewTabIcon.cjs +1 -1
  120. package/lib/components/Icon/OpenInNewTabIcon.js +1 -1
  121. package/lib/components/InformationPanel/InformationPanel.cjs +1 -1
  122. package/lib/components/InformationPanel/InformationPanel.js +1 -1
  123. package/lib/components/Input/SearchInput/SearchInputIcon.cjs +1 -1
  124. package/lib/components/Input/SearchInput/SearchInputIcon.js +1 -1
  125. package/lib/components/InputFile/InputFileMultiplyAppendable.cjs +1 -1
  126. package/lib/components/InputFile/InputFileMultiplyAppendable.js +1 -1
  127. package/lib/components/InputFile/InputFileNative.cjs +1 -1
  128. package/lib/components/InputFile/InputFileNative.js +1 -1
  129. package/lib/components/Layout/Cluster/Cluster.cjs +0 -1
  130. package/lib/components/Layout/Cluster/Cluster.cjs.map +1 -1
  131. package/lib/components/Layout/Cluster/Cluster.js +0 -1
  132. package/lib/components/Layout/Cluster/Cluster.js.map +1 -1
  133. package/lib/components/Layout/Container/Container.cjs +1 -5
  134. package/lib/components/Layout/Container/Container.cjs.map +1 -1
  135. package/lib/components/Layout/Container/Container.js +1 -5
  136. package/lib/components/Layout/Container/Container.js.map +1 -1
  137. package/lib/components/Layout/Stack/Stack.cjs +22 -22
  138. package/lib/components/Layout/Stack/Stack.cjs.map +1 -1
  139. package/lib/components/Layout/Stack/Stack.js +22 -22
  140. package/lib/components/Layout/Stack/Stack.js.map +1 -1
  141. package/lib/components/Loader/LoaderSpinner.cjs +1 -1
  142. package/lib/components/Loader/LoaderSpinner.js +1 -1
  143. package/lib/components/Pagination/Pagination.cjs +1 -1
  144. package/lib/components/Pagination/Pagination.js +1 -1
  145. package/lib/components/Pagination/PaginationItemButton.cjs +1 -1
  146. package/lib/components/Pagination/PaginationItemButton.js +1 -1
  147. package/lib/components/SideNav/SideNav.cjs +3 -22
  148. package/lib/components/SideNav/SideNav.cjs.map +1 -1
  149. package/lib/components/SideNav/SideNav.d.ts +2 -9
  150. package/lib/components/SideNav/SideNav.js +4 -23
  151. package/lib/components/SideNav/SideNav.js.map +1 -1
  152. package/lib/components/SideNav/SideNavContext.cjs +11 -0
  153. package/lib/components/SideNav/SideNavContext.cjs.map +1 -0
  154. package/lib/components/SideNav/SideNavContext.d.ts +7 -0
  155. package/lib/components/SideNav/SideNavContext.js +8 -0
  156. package/lib/components/SideNav/SideNavContext.js.map +1 -0
  157. package/lib/components/SideNav/SideNavItemButton.cjs +12 -7
  158. package/lib/components/SideNav/SideNavItemButton.cjs.map +1 -1
  159. package/lib/components/SideNav/SideNavItemButton.d.ts +5 -11
  160. package/lib/components/SideNav/SideNavItemButton.js +12 -7
  161. package/lib/components/SideNav/SideNavItemButton.js.map +1 -1
  162. package/lib/components/Table/Th.cjs +2 -2
  163. package/lib/components/Table/Th.cjs.map +1 -1
  164. package/lib/components/Table/Th.d.ts +0 -2
  165. package/lib/components/Table/Th.js +2 -2
  166. package/lib/components/Table/Th.js.map +1 -1
  167. package/lib/components/Table/ThCheckbox.cjs +1 -1
  168. package/lib/components/Table/ThCheckbox.js +1 -1
  169. package/lib/components/Table/ThSortButton.cjs +5 -6
  170. package/lib/components/Table/ThSortButton.cjs.map +1 -1
  171. package/lib/components/Table/ThSortButton.d.ts +1 -6
  172. package/lib/components/Table/ThSortButton.js +5 -6
  173. package/lib/components/Table/ThSortButton.js.map +1 -1
  174. package/lib/components/WarekiPicker/WarekiPicker.cjs +1 -1
  175. package/lib/components/WarekiPicker/WarekiPicker.js +1 -1
  176. package/lib/index.cjs +1 -6
  177. package/lib/index.cjs.map +1 -1
  178. package/lib/index.d.ts +0 -1
  179. package/lib/index.js +1 -4
  180. package/lib/index.js.map +1 -1
  181. package/lib/intl/Localizer.cjs +5 -1
  182. package/lib/intl/Localizer.cjs.map +1 -1
  183. package/lib/intl/Localizer.d.ts +1 -1
  184. package/lib/intl/Localizer.js +5 -1
  185. package/lib/intl/Localizer.js.map +1 -1
  186. package/metadata.json +1 -1
  187. package/package.json +1 -1
  188. package/smarthr-ui.css +0 -94
  189. package/lib/hooks/useDecorators.cjs +0 -18
  190. package/lib/hooks/useDecorators.cjs.map +0 -1
  191. package/lib/hooks/useDecorators.d.ts +0 -6
  192. package/lib/hooks/useDecorators.js +0 -16
  193. package/lib/hooks/useDecorators.js.map +0 -1
  194. package/lib/hooks/useDevice/DeviceProvider.cjs +0 -37
  195. package/lib/hooks/useDevice/DeviceProvider.cjs.map +0 -1
  196. package/lib/hooks/useDevice/DeviceProvider.d.ts +0 -4
  197. package/lib/hooks/useDevice/DeviceProvider.js +0 -35
  198. package/lib/hooks/useDevice/DeviceProvider.js.map +0 -1
  199. package/lib/hooks/useDevice/index.cjs +0 -10
  200. package/lib/hooks/useDevice/index.cjs.map +0 -1
  201. package/lib/hooks/useDevice/index.d.ts +0 -2
  202. package/lib/hooks/useDevice/index.js +0 -3
  203. package/lib/hooks/useDevice/index.js.map +0 -1
  204. package/lib/hooks/useDevice/useDevice.cjs +0 -11
  205. package/lib/hooks/useDevice/useDevice.cjs.map +0 -1
  206. package/lib/hooks/useDevice/useDevice.d.ts +0 -4
  207. package/lib/hooks/useDevice/useDevice.js +0 -8
  208. package/lib/hooks/useDevice/useDevice.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"FilterDropdown.cjs","sources":["../../../../src/components/Dropdown/FilterDropdown/FilterDropdown.tsx"],"sourcesContent":["'use client'\n\nimport {\n type ComponentProps,\n type FC,\n type FormEvent,\n type MouseEventHandler,\n type ReactNode,\n isValidElement,\n useMemo,\n} from 'react'\nimport innerText from 'react-innertext'\nimport { tv } from 'tailwind-variants'\n\nimport { type ResponseStatus, useResponseStatus } from '../../../hooks/useResponseStatus'\nimport { useIntl } from '../../../intl'\nimport { Button, type AbstractProps as ButtonProps } from '../../Button'\nimport { FaCircleCheckIcon, FaFilterIcon, FaRotateLeftIcon } from '../../Icon'\nimport { Cluster, Stack } from '../../Layout'\nimport { ResponseMessage } from '../../ResponseMessage'\nimport { Dropdown } from '../Dropdown'\nimport { DropdownCloser } from '../DropdownCloser'\nimport { DropdownContent } from '../DropdownContent'\nimport { DropdownTrigger } from '../DropdownTrigger'\n\ntype ObjectTriggerType = {\n text?: ReactNode\n /** 引き金となるボタンの大きさ */\n size?: ButtonProps['size']\n /** 引き金となるボタンをアイコンのみとするかどうか */\n onlyIcon?: boolean\n}\ntype AbstractProps = {\n /** 引き金となるボタン */\n trigger?: ReactNode | ObjectTriggerType\n applyText?: ReactNode\n cancelText?: ReactNode\n resetText?: ReactNode\n children: ReactNode\n filtered?:\n | boolean\n | {\n iconAlt?: ReactNode\n }\n responseStatus?: ResponseStatus\n onApply: MouseEventHandler<HTMLButtonElement>\n onCancel?: MouseEventHandler<HTMLButtonElement>\n onReset?: MouseEventHandler<HTMLButtonElement>\n onOpen?: () => void\n onClose?: () => void\n}\ntype Props = AbstractProps & Omit<ComponentProps<'button'>, keyof AbstractProps>\n\nconst CONTROL_CLUSTER_GAP: ComponentProps<typeof Cluster>['gap'] = { column: 1, row: 0.5 }\nconst ON_SUBMIT = (e: FormEvent) => {\n e.preventDefault()\n}\n\nconst classNameGenerator = tv({\n slots: {\n iconWrapper: ['smarthr-ui-Icon-extended', 'shr-relative shr-leading-none'],\n filteredIcon: 'shr-absolute shr-bottom-[2px] shr-right-[-4px] shr-h-[0.5em] shr-w-[0.5em]',\n inner: 'shr-p-1.5',\n actionArea: 'shr-border-t-shorthand shr-sticky shr-bottom-0 shr-bg-white shr-px-1.5 shr-py-1',\n resetButtonArea: '-shr-ms-0.5',\n rightButtonArea: 'shr-ms-auto',\n message: 'shr-text-right',\n },\n variants: {\n filtered: {\n true: {\n iconWrapper: 'shr-text-main',\n },\n },\n triggerSize: {\n M: {},\n S: {\n iconWrapper: '-shr-translate-x-0.25',\n },\n },\n },\n})\n\nexport const FilterDropdown: FC<Props> = ({\n trigger: orgTrigger,\n applyText,\n cancelText,\n resetText,\n children,\n filtered,\n responseStatus,\n onApply,\n onCancel,\n onReset,\n onOpen,\n onClose,\n ...rest\n}) => {\n // HINT: ReactNodeとObjectのどちらかを判定\n // typeofはnullの場合もobject判定されてしまうため念の為falsyで判定\n // ReactNodeの一部であるReactElementもobjectとして判定されてしまうためisValidElementで判定\n const trigger: ObjectTriggerType =\n !orgTrigger || typeof orgTrigger !== 'object' || isValidElement(orgTrigger)\n ? {\n text: orgTrigger as ReactNode,\n }\n : (orgTrigger as ObjectTriggerType)\n const { localize } = useIntl()\n\n const decorated = useMemo(\n () => ({\n filteredIconAlt:\n (typeof filtered === 'object' && filtered.iconAlt) ||\n localize({\n id: 'smarthr-ui/FilterDropdown/status',\n defaultText: '適用中',\n }),\n trigger:\n trigger.text ||\n localize({\n id: 'smarthr-ui/FilterDropdown/triggerText',\n defaultText: '絞り込み',\n }),\n applyText:\n applyText ||\n localize({\n id: 'smarthr-ui/FilterDropdown/applyText',\n defaultText: '適用',\n }),\n cancelText:\n cancelText ||\n localize({\n id: 'smarthr-ui/FilterDropdown/cancelText',\n defaultText: 'キャンセル',\n }),\n resetText:\n resetText ||\n localize({\n id: 'smarthr-ui/FilterDropdown/resetText',\n defaultText: '絞り込み条件を解除',\n }),\n }),\n [filtered, trigger.text, applyText, cancelText, resetText, localize],\n )\n\n const filteredIconAlt = useMemo(\n () => innerText(decorated.filteredIconAlt),\n [decorated.filteredIconAlt],\n )\n const calcedResponseStatus = useResponseStatus(responseStatus)\n\n const classNamesMapper = useMemo(() => {\n const {\n iconWrapper,\n filteredIcon,\n inner,\n actionArea,\n resetButtonArea,\n rightButtonArea,\n message,\n } = classNameGenerator()\n\n const commonStyles = {\n filteredIcon: filteredIcon(),\n inner: inner(),\n actionArea: actionArea(),\n resetButtonArea: resetButtonArea(),\n rightButtonArea: rightButtonArea(),\n message: message(),\n }\n\n return {\n filtered: {\n ...commonStyles,\n iconWrapper: iconWrapper({ filtered: true, triggerSize: trigger.size }),\n },\n unfiltered: {\n ...commonStyles,\n iconWrapper: iconWrapper({ filtered: false, triggerSize: trigger.size }),\n },\n }\n }, [trigger.size])\n\n const classNames = classNamesMapper[filtered ? 'filtered' : 'unfiltered']\n\n const { buttonSuffix, buttonContent } = useMemo(() => {\n const FilterIcon = (\n <span className={classNames.iconWrapper}>\n <FaFilterIcon alt={trigger.onlyIcon ? decorated.trigger : undefined} />\n\n {filtered && (\n // HINT: altに揃えたいが、styleが複雑になってしまうためaria-labelを利用している\n <FaCircleCheckIcon aria-label={filteredIconAlt} className={classNames.filteredIcon} />\n )}\n </span>\n )\n\n if (trigger.onlyIcon) {\n return {\n buttonSuffix: undefined,\n buttonContent: FilterIcon,\n }\n }\n\n return {\n buttonSuffix: FilterIcon,\n buttonContent: decorated.trigger,\n }\n }, [filtered, decorated.trigger, trigger.onlyIcon, filteredIconAlt, classNames])\n\n return (\n <Dropdown onOpen={onOpen} onClose={onClose}>\n <DropdownTrigger tooltip={{ show: trigger.onlyIcon, message: decorated.trigger }}>\n <Button {...rest} suffix={buttonSuffix} size={trigger.size}>\n {buttonContent}\n </Button>\n </DropdownTrigger>\n <DropdownContent controllable>\n <form onSubmit={ON_SUBMIT}>\n <div className={classNames.inner}>{children}</div>\n <Stack gap={0.5} className={classNames.actionArea}>\n <Cluster gap={1} align=\"center\" justify=\"space-between\">\n {onReset && (\n <div className={classNames.resetButtonArea}>\n <Button\n variant=\"text\"\n size=\"S\"\n prefix={<FaRotateLeftIcon />}\n onClick={onReset}\n disabled={calcedResponseStatus.isProcessing}\n >\n {decorated.resetText}\n </Button>\n </div>\n )}\n\n <Cluster\n gap={CONTROL_CLUSTER_GAP}\n justify=\"flex-end\"\n className={classNames.rightButtonArea}\n >\n <DropdownCloser>\n <Button onClick={onCancel} disabled={calcedResponseStatus.isProcessing}>\n {decorated.cancelText}\n </Button>\n </DropdownCloser>\n <DropdownCloser>\n <Button\n variant=\"primary\"\n onClick={onApply}\n loading={calcedResponseStatus.isProcessing}\n >\n {decorated.applyText}\n </Button>\n </DropdownCloser>\n </Cluster>\n </Cluster>\n {calcedResponseStatus.message && (\n <div className={classNames.message}>\n <ResponseMessage status={calcedResponseStatus.status} role=\"alert\">\n {calcedResponseStatus.message}\n </ResponseMessage>\n </div>\n )}\n </Stack>\n </form>\n </DropdownContent>\n </Dropdown>\n )\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqDA;AACA;;AAEA;AAEA;AACE;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACD;AACD;AACE;AACE;AACE;AACD;AACF;AACD;AACE;AACA;AACE;AACD;AACF;AACF;AACF;AAEM;;;;AAkBL;AAEI;AACI;AACD;;AAEP;AAEA;;AAIM;AACE;AACA;;;AAIF;AACE;AACA;;AAEJ;AAEE;AACE;AACA;;AAEJ;AAEE;AACE;AACA;;AAEJ;AAEE;AACE;AACA;;AAEL;;AAQH;AAEA;AACE;AAUA;;;;;;;;;AAUE;AACE;AACA;AACD;AACD;AACE;AACA;AACD;;AAEL;AAEA;;AAGE;;AAMM;AAKN;;AAEI;AACA;;;;AAKF;;;AAGJ;;AA6DF;;"}
1
+ {"version":3,"file":"FilterDropdown.cjs","sources":["../../../../src/components/Dropdown/FilterDropdown/FilterDropdown.tsx"],"sourcesContent":["'use client'\n\nimport {\n type ComponentProps,\n type FC,\n type FormEvent,\n type MouseEventHandler,\n type ReactNode,\n isValidElement,\n useMemo,\n} from 'react'\nimport { tv } from 'tailwind-variants'\n\nimport { type ResponseStatus, useResponseStatus } from '../../../hooks/useResponseStatus'\nimport { useIntl } from '../../../intl'\nimport { Button, type AbstractProps as ButtonProps } from '../../Button'\nimport { FaCircleCheckIcon, FaFilterIcon, FaRotateLeftIcon } from '../../Icon'\nimport { Cluster, Stack } from '../../Layout'\nimport { ResponseMessage } from '../../ResponseMessage'\nimport { Dropdown } from '../Dropdown'\nimport { DropdownCloser } from '../DropdownCloser'\nimport { DropdownContent } from '../DropdownContent'\nimport { DropdownTrigger } from '../DropdownTrigger'\n\ntype ObjectTriggerType = {\n text?: ReactNode\n /** 引き金となるボタンの大きさ */\n size?: ButtonProps['size']\n /** 引き金となるボタンをアイコンのみとするかどうか */\n onlyIcon?: boolean\n}\ntype AbstractProps = {\n /** 引き金となるボタン */\n trigger?: ReactNode | ObjectTriggerType\n applyText?: ReactNode\n cancelText?: ReactNode\n resetText?: ReactNode\n children: ReactNode\n filtered?:\n | boolean\n | {\n iconAlt?: string\n }\n responseStatus?: ResponseStatus\n onApply: MouseEventHandler<HTMLButtonElement>\n onCancel?: MouseEventHandler<HTMLButtonElement>\n onReset?: MouseEventHandler<HTMLButtonElement>\n onOpen?: () => void\n onClose?: () => void\n}\ntype Props = AbstractProps & Omit<ComponentProps<'button'>, keyof AbstractProps>\n\nconst CONTROL_CLUSTER_GAP: ComponentProps<typeof Cluster>['gap'] = { column: 1, row: 0.5 }\nconst ON_SUBMIT = (e: FormEvent) => {\n e.preventDefault()\n}\n\nconst classNameGenerator = tv({\n slots: {\n iconWrapper: ['smarthr-ui-Icon-extended', 'shr-relative shr-leading-none'],\n filteredIcon: 'shr-absolute shr-bottom-[2px] shr-right-[-4px] shr-h-[0.5em] shr-w-[0.5em]',\n inner: 'shr-p-1.5',\n actionArea: 'shr-border-t-shorthand shr-sticky shr-bottom-0 shr-bg-white shr-px-1.5 shr-py-1',\n resetButtonArea: '-shr-ms-0.5',\n rightButtonArea: 'shr-ms-auto',\n message: 'shr-text-right',\n },\n variants: {\n filtered: {\n true: {\n iconWrapper: 'shr-text-main',\n },\n },\n triggerSize: {\n M: {},\n S: {\n iconWrapper: '-shr-translate-x-0.25',\n },\n },\n },\n})\n\nexport const FilterDropdown: FC<Props> = ({\n trigger: orgTrigger,\n applyText,\n cancelText,\n resetText,\n children,\n filtered,\n responseStatus,\n onApply,\n onCancel,\n onReset,\n onOpen,\n onClose,\n ...rest\n}) => {\n // HINT: ReactNodeとObjectのどちらかを判定\n // typeofはnullの場合もobject判定されてしまうため念の為falsyで判定\n // ReactNodeの一部であるReactElementもobjectとして判定されてしまうためisValidElementで判定\n const trigger: ObjectTriggerType =\n !orgTrigger || typeof orgTrigger !== 'object' || isValidElement(orgTrigger)\n ? {\n text: orgTrigger as ReactNode,\n }\n : (orgTrigger as ObjectTriggerType)\n const { localize } = useIntl()\n\n const decorated = useMemo(\n () => ({\n filteredIconAlt:\n (typeof filtered === 'object' && filtered.iconAlt) ||\n localize({\n id: 'smarthr-ui/FilterDropdown/status',\n defaultText: '適用中',\n }),\n trigger:\n trigger.text ||\n localize({\n id: 'smarthr-ui/FilterDropdown/triggerText',\n defaultText: '絞り込み',\n }),\n applyText:\n applyText ||\n localize({\n id: 'smarthr-ui/FilterDropdown/applyText',\n defaultText: '適用',\n }),\n cancelText:\n cancelText ||\n localize({\n id: 'smarthr-ui/FilterDropdown/cancelText',\n defaultText: 'キャンセル',\n }),\n resetText:\n resetText ||\n localize({\n id: 'smarthr-ui/FilterDropdown/resetText',\n defaultText: '絞り込み条件を解除',\n }),\n }),\n [filtered, trigger.text, applyText, cancelText, resetText, localize],\n )\n\n const calcedResponseStatus = useResponseStatus(responseStatus)\n\n const classNamesMapper = useMemo(() => {\n const {\n iconWrapper,\n filteredIcon,\n inner,\n actionArea,\n resetButtonArea,\n rightButtonArea,\n message,\n } = classNameGenerator()\n\n const commonStyles = {\n filteredIcon: filteredIcon(),\n inner: inner(),\n actionArea: actionArea(),\n resetButtonArea: resetButtonArea(),\n rightButtonArea: rightButtonArea(),\n message: message(),\n }\n\n return {\n filtered: {\n ...commonStyles,\n iconWrapper: iconWrapper({ filtered: true, triggerSize: trigger.size }),\n },\n unfiltered: {\n ...commonStyles,\n iconWrapper: iconWrapper({ filtered: false, triggerSize: trigger.size }),\n },\n }\n }, [trigger.size])\n\n const classNames = classNamesMapper[filtered ? 'filtered' : 'unfiltered']\n\n const { buttonSuffix, buttonContent } = useMemo(() => {\n const FilterIcon = (\n <span className={classNames.iconWrapper}>\n <FaFilterIcon alt={trigger.onlyIcon ? decorated.trigger : undefined} />\n\n {filtered && (\n // HINT: altに揃えたいが、styleが複雑になってしまうためaria-labelを利用している\n <FaCircleCheckIcon\n aria-label={decorated.filteredIconAlt}\n className={classNames.filteredIcon}\n />\n )}\n </span>\n )\n\n if (trigger.onlyIcon) {\n return {\n buttonSuffix: undefined,\n buttonContent: FilterIcon,\n }\n }\n\n return {\n buttonSuffix: FilterIcon,\n buttonContent: decorated.trigger,\n }\n }, [filtered, decorated.trigger, decorated.filteredIconAlt, trigger.onlyIcon, classNames])\n\n return (\n <Dropdown onOpen={onOpen} onClose={onClose}>\n <DropdownTrigger tooltip={{ show: trigger.onlyIcon, message: decorated.trigger }}>\n <Button {...rest} suffix={buttonSuffix} size={trigger.size}>\n {buttonContent}\n </Button>\n </DropdownTrigger>\n <DropdownContent controllable>\n <form onSubmit={ON_SUBMIT}>\n <div className={classNames.inner}>{children}</div>\n <Stack gap={0.5} className={classNames.actionArea}>\n <Cluster gap={1} align=\"center\" justify=\"space-between\">\n {onReset && (\n <div className={classNames.resetButtonArea}>\n <Button\n variant=\"text\"\n size=\"S\"\n prefix={<FaRotateLeftIcon />}\n onClick={onReset}\n disabled={calcedResponseStatus.isProcessing}\n >\n {decorated.resetText}\n </Button>\n </div>\n )}\n\n <Cluster\n gap={CONTROL_CLUSTER_GAP}\n justify=\"flex-end\"\n className={classNames.rightButtonArea}\n >\n <DropdownCloser>\n <Button onClick={onCancel} disabled={calcedResponseStatus.isProcessing}>\n {decorated.cancelText}\n </Button>\n </DropdownCloser>\n <DropdownCloser>\n <Button\n variant=\"primary\"\n onClick={onApply}\n loading={calcedResponseStatus.isProcessing}\n >\n {decorated.applyText}\n </Button>\n </DropdownCloser>\n </Cluster>\n </Cluster>\n {calcedResponseStatus.message && (\n <div className={classNames.message}>\n <ResponseMessage status={calcedResponseStatus.status} role=\"alert\">\n {calcedResponseStatus.message}\n </ResponseMessage>\n </div>\n )}\n </Stack>\n </form>\n </DropdownContent>\n </Dropdown>\n )\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoDA;AACA;;AAEA;AAEA;AACE;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACD;AACD;AACE;AACE;AACE;AACD;AACF;AACD;AACE;AACA;AACE;AACD;AACF;AACF;AACF;AAEM;;;;AAkBL;AAEI;AACI;AACD;;AAEP;AAEA;;AAIM;AACE;AACA;;;AAIF;AACE;AACA;;AAEJ;AAEE;AACE;AACA;;AAEJ;AAEE;AACE;AACA;;AAEJ;AAEE;AACE;AACA;;AAEL;AAIH;AAEA;AACE;AAUA;;;;;;;;;AAUE;AACE;AACA;AACD;AACD;AACE;AACA;AACD;;AAEL;AAEA;;AAGE;;AAMM;AAQN;;AAEI;AACA;;;;AAKF;;;AAGJ;;AA6DF;;"}
@@ -16,7 +16,7 @@ type AbstractProps = {
16
16
  resetText?: ReactNode;
17
17
  children: ReactNode;
18
18
  filtered?: boolean | {
19
- iconAlt?: ReactNode;
19
+ iconAlt?: string;
20
20
  };
21
21
  responseStatus?: ResponseStatus;
22
22
  onApply: MouseEventHandler<HTMLButtonElement>;
@@ -1,12 +1,11 @@
1
1
  "use client";
2
2
  import { jsxs, jsx } from 'react/jsx-runtime';
3
3
  import { isValidElement, useMemo } from 'react';
4
- import innerText from '../../../_virtual/index3.js';
5
4
  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
5
  import { useResponseStatus } from '../../../hooks/useResponseStatus.js';
7
6
  import '../../../intl/IntlProvider.js';
8
7
  import { useIntl } from '../../../intl/useIntl.js';
9
- import 'react-intl';
8
+ import '../../../intl/Localizer.js';
10
9
  import '../../../_virtual/dayjs.min.js';
11
10
  import '../../../intl/TimeFormatter.js';
12
11
  import { Button } from '../../Button/Button.js';
@@ -92,7 +91,6 @@ const FilterDropdown = ({ trigger: orgTrigger, applyText, cancelText, resetText,
92
91
  defaultText: '絞り込み条件を解除',
93
92
  }),
94
93
  }), [filtered, trigger.text, applyText, cancelText, resetText, localize]);
95
- const filteredIconAlt = useMemo(() => innerText(decorated.filteredIconAlt), [decorated.filteredIconAlt]);
96
94
  const calcedResponseStatus = useResponseStatus(responseStatus);
97
95
  const classNamesMapper = useMemo(() => {
98
96
  const { iconWrapper, filteredIcon, inner, actionArea, resetButtonArea, rightButtonArea, message, } = classNameGenerator();
@@ -119,7 +117,7 @@ const FilterDropdown = ({ trigger: orgTrigger, applyText, cancelText, resetText,
119
117
  const { buttonSuffix, buttonContent } = useMemo(() => {
120
118
  const FilterIcon = (jsxs("span", { className: classNames.iconWrapper, children: [jsx(FaFilterIcon, { alt: trigger.onlyIcon ? decorated.trigger : undefined }), filtered && (
121
119
  // HINT: altに揃えたいが、styleが複雑になってしまうためaria-labelを利用している
122
- jsx(FaCircleCheckIcon, { "aria-label": filteredIconAlt, className: classNames.filteredIcon }))] }));
120
+ jsx(FaCircleCheckIcon, { "aria-label": decorated.filteredIconAlt, className: classNames.filteredIcon }))] }));
123
121
  if (trigger.onlyIcon) {
124
122
  return {
125
123
  buttonSuffix: undefined,
@@ -130,7 +128,7 @@ const FilterDropdown = ({ trigger: orgTrigger, applyText, cancelText, resetText,
130
128
  buttonSuffix: FilterIcon,
131
129
  buttonContent: decorated.trigger,
132
130
  };
133
- }, [filtered, decorated.trigger, trigger.onlyIcon, filteredIconAlt, classNames]);
131
+ }, [filtered, decorated.trigger, decorated.filteredIconAlt, trigger.onlyIcon, classNames]);
134
132
  return (jsxs(Dropdown, { onOpen: onOpen, onClose: onClose, children: [jsx(DropdownTrigger, { tooltip: { show: trigger.onlyIcon, message: decorated.trigger }, children: jsx(Button, { ...rest, suffix: buttonSuffix, size: trigger.size, children: buttonContent }) }), jsx(DropdownContent, { controllable: true, children: jsxs("form", { onSubmit: ON_SUBMIT, children: [jsx("div", { className: classNames.inner, children: children }), jsxs(Stack, { gap: 0.5, className: classNames.actionArea, children: [jsxs(Cluster, { gap: 1, align: "center", justify: "space-between", children: [onReset && (jsx("div", { className: classNames.resetButtonArea, children: jsx(Button, { variant: "text", size: "S", prefix: jsx(FaRotateLeftIcon, {}), onClick: onReset, disabled: calcedResponseStatus.isProcessing, children: decorated.resetText }) })), jsxs(Cluster, { gap: CONTROL_CLUSTER_GAP, justify: "flex-end", className: classNames.rightButtonArea, children: [jsx(DropdownCloser, { children: jsx(Button, { onClick: onCancel, disabled: calcedResponseStatus.isProcessing, children: decorated.cancelText }) }), jsx(DropdownCloser, { children: jsx(Button, { variant: "primary", onClick: onApply, loading: calcedResponseStatus.isProcessing, children: decorated.applyText }) })] })] }), calcedResponseStatus.message && (jsx("div", { className: classNames.message, children: jsx(ResponseMessage, { status: calcedResponseStatus.status, role: "alert", children: calcedResponseStatus.message }) }))] })] }) })] }));
135
133
  };
136
134
 
@@ -1 +1 @@
1
- {"version":3,"file":"FilterDropdown.js","sources":["../../../../src/components/Dropdown/FilterDropdown/FilterDropdown.tsx"],"sourcesContent":["'use client'\n\nimport {\n type ComponentProps,\n type FC,\n type FormEvent,\n type MouseEventHandler,\n type ReactNode,\n isValidElement,\n useMemo,\n} from 'react'\nimport innerText from 'react-innertext'\nimport { tv } from 'tailwind-variants'\n\nimport { type ResponseStatus, useResponseStatus } from '../../../hooks/useResponseStatus'\nimport { useIntl } from '../../../intl'\nimport { Button, type AbstractProps as ButtonProps } from '../../Button'\nimport { FaCircleCheckIcon, FaFilterIcon, FaRotateLeftIcon } from '../../Icon'\nimport { Cluster, Stack } from '../../Layout'\nimport { ResponseMessage } from '../../ResponseMessage'\nimport { Dropdown } from '../Dropdown'\nimport { DropdownCloser } from '../DropdownCloser'\nimport { DropdownContent } from '../DropdownContent'\nimport { DropdownTrigger } from '../DropdownTrigger'\n\ntype ObjectTriggerType = {\n text?: ReactNode\n /** 引き金となるボタンの大きさ */\n size?: ButtonProps['size']\n /** 引き金となるボタンをアイコンのみとするかどうか */\n onlyIcon?: boolean\n}\ntype AbstractProps = {\n /** 引き金となるボタン */\n trigger?: ReactNode | ObjectTriggerType\n applyText?: ReactNode\n cancelText?: ReactNode\n resetText?: ReactNode\n children: ReactNode\n filtered?:\n | boolean\n | {\n iconAlt?: ReactNode\n }\n responseStatus?: ResponseStatus\n onApply: MouseEventHandler<HTMLButtonElement>\n onCancel?: MouseEventHandler<HTMLButtonElement>\n onReset?: MouseEventHandler<HTMLButtonElement>\n onOpen?: () => void\n onClose?: () => void\n}\ntype Props = AbstractProps & Omit<ComponentProps<'button'>, keyof AbstractProps>\n\nconst CONTROL_CLUSTER_GAP: ComponentProps<typeof Cluster>['gap'] = { column: 1, row: 0.5 }\nconst ON_SUBMIT = (e: FormEvent) => {\n e.preventDefault()\n}\n\nconst classNameGenerator = tv({\n slots: {\n iconWrapper: ['smarthr-ui-Icon-extended', 'shr-relative shr-leading-none'],\n filteredIcon: 'shr-absolute shr-bottom-[2px] shr-right-[-4px] shr-h-[0.5em] shr-w-[0.5em]',\n inner: 'shr-p-1.5',\n actionArea: 'shr-border-t-shorthand shr-sticky shr-bottom-0 shr-bg-white shr-px-1.5 shr-py-1',\n resetButtonArea: '-shr-ms-0.5',\n rightButtonArea: 'shr-ms-auto',\n message: 'shr-text-right',\n },\n variants: {\n filtered: {\n true: {\n iconWrapper: 'shr-text-main',\n },\n },\n triggerSize: {\n M: {},\n S: {\n iconWrapper: '-shr-translate-x-0.25',\n },\n },\n },\n})\n\nexport const FilterDropdown: FC<Props> = ({\n trigger: orgTrigger,\n applyText,\n cancelText,\n resetText,\n children,\n filtered,\n responseStatus,\n onApply,\n onCancel,\n onReset,\n onOpen,\n onClose,\n ...rest\n}) => {\n // HINT: ReactNodeとObjectのどちらかを判定\n // typeofはnullの場合もobject判定されてしまうため念の為falsyで判定\n // ReactNodeの一部であるReactElementもobjectとして判定されてしまうためisValidElementで判定\n const trigger: ObjectTriggerType =\n !orgTrigger || typeof orgTrigger !== 'object' || isValidElement(orgTrigger)\n ? {\n text: orgTrigger as ReactNode,\n }\n : (orgTrigger as ObjectTriggerType)\n const { localize } = useIntl()\n\n const decorated = useMemo(\n () => ({\n filteredIconAlt:\n (typeof filtered === 'object' && filtered.iconAlt) ||\n localize({\n id: 'smarthr-ui/FilterDropdown/status',\n defaultText: '適用中',\n }),\n trigger:\n trigger.text ||\n localize({\n id: 'smarthr-ui/FilterDropdown/triggerText',\n defaultText: '絞り込み',\n }),\n applyText:\n applyText ||\n localize({\n id: 'smarthr-ui/FilterDropdown/applyText',\n defaultText: '適用',\n }),\n cancelText:\n cancelText ||\n localize({\n id: 'smarthr-ui/FilterDropdown/cancelText',\n defaultText: 'キャンセル',\n }),\n resetText:\n resetText ||\n localize({\n id: 'smarthr-ui/FilterDropdown/resetText',\n defaultText: '絞り込み条件を解除',\n }),\n }),\n [filtered, trigger.text, applyText, cancelText, resetText, localize],\n )\n\n const filteredIconAlt = useMemo(\n () => innerText(decorated.filteredIconAlt),\n [decorated.filteredIconAlt],\n )\n const calcedResponseStatus = useResponseStatus(responseStatus)\n\n const classNamesMapper = useMemo(() => {\n const {\n iconWrapper,\n filteredIcon,\n inner,\n actionArea,\n resetButtonArea,\n rightButtonArea,\n message,\n } = classNameGenerator()\n\n const commonStyles = {\n filteredIcon: filteredIcon(),\n inner: inner(),\n actionArea: actionArea(),\n resetButtonArea: resetButtonArea(),\n rightButtonArea: rightButtonArea(),\n message: message(),\n }\n\n return {\n filtered: {\n ...commonStyles,\n iconWrapper: iconWrapper({ filtered: true, triggerSize: trigger.size }),\n },\n unfiltered: {\n ...commonStyles,\n iconWrapper: iconWrapper({ filtered: false, triggerSize: trigger.size }),\n },\n }\n }, [trigger.size])\n\n const classNames = classNamesMapper[filtered ? 'filtered' : 'unfiltered']\n\n const { buttonSuffix, buttonContent } = useMemo(() => {\n const FilterIcon = (\n <span className={classNames.iconWrapper}>\n <FaFilterIcon alt={trigger.onlyIcon ? decorated.trigger : undefined} />\n\n {filtered && (\n // HINT: altに揃えたいが、styleが複雑になってしまうためaria-labelを利用している\n <FaCircleCheckIcon aria-label={filteredIconAlt} className={classNames.filteredIcon} />\n )}\n </span>\n )\n\n if (trigger.onlyIcon) {\n return {\n buttonSuffix: undefined,\n buttonContent: FilterIcon,\n }\n }\n\n return {\n buttonSuffix: FilterIcon,\n buttonContent: decorated.trigger,\n }\n }, [filtered, decorated.trigger, trigger.onlyIcon, filteredIconAlt, classNames])\n\n return (\n <Dropdown onOpen={onOpen} onClose={onClose}>\n <DropdownTrigger tooltip={{ show: trigger.onlyIcon, message: decorated.trigger }}>\n <Button {...rest} suffix={buttonSuffix} size={trigger.size}>\n {buttonContent}\n </Button>\n </DropdownTrigger>\n <DropdownContent controllable>\n <form onSubmit={ON_SUBMIT}>\n <div className={classNames.inner}>{children}</div>\n <Stack gap={0.5} className={classNames.actionArea}>\n <Cluster gap={1} align=\"center\" justify=\"space-between\">\n {onReset && (\n <div className={classNames.resetButtonArea}>\n <Button\n variant=\"text\"\n size=\"S\"\n prefix={<FaRotateLeftIcon />}\n onClick={onReset}\n disabled={calcedResponseStatus.isProcessing}\n >\n {decorated.resetText}\n </Button>\n </div>\n )}\n\n <Cluster\n gap={CONTROL_CLUSTER_GAP}\n justify=\"flex-end\"\n className={classNames.rightButtonArea}\n >\n <DropdownCloser>\n <Button onClick={onCancel} disabled={calcedResponseStatus.isProcessing}>\n {decorated.cancelText}\n </Button>\n </DropdownCloser>\n <DropdownCloser>\n <Button\n variant=\"primary\"\n onClick={onApply}\n loading={calcedResponseStatus.isProcessing}\n >\n {decorated.applyText}\n </Button>\n </DropdownCloser>\n </Cluster>\n </Cluster>\n {calcedResponseStatus.message && (\n <div className={classNames.message}>\n <ResponseMessage status={calcedResponseStatus.status} role=\"alert\">\n {calcedResponseStatus.message}\n </ResponseMessage>\n </div>\n )}\n </Stack>\n </form>\n </DropdownContent>\n </Dropdown>\n )\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqDA;AACA;;AAEA;AAEA;AACE;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACD;AACD;AACE;AACE;AACE;AACD;AACF;AACD;AACE;AACA;AACE;AACD;AACF;AACF;AACF;AAEM;;;;AAkBL;AAEI;AACI;AACD;;AAEP;AAEA;;AAIM;AACE;AACA;;;AAIF;AACE;AACA;;AAEJ;AAEE;AACE;AACA;;AAEJ;AAEE;AACE;AACA;;AAEJ;AAEE;AACE;AACA;;AAEL;;AAQH;AAEA;AACE;AAUA;;;;;;;;;AAUE;AACE;AACA;AACD;AACD;AACE;AACA;AACD;;AAEL;AAEA;;AAGE;;AAMM;AAKN;;AAEI;AACA;;;;AAKF;;;AAGJ;;AA6DF;;"}
1
+ {"version":3,"file":"FilterDropdown.js","sources":["../../../../src/components/Dropdown/FilterDropdown/FilterDropdown.tsx"],"sourcesContent":["'use client'\n\nimport {\n type ComponentProps,\n type FC,\n type FormEvent,\n type MouseEventHandler,\n type ReactNode,\n isValidElement,\n useMemo,\n} from 'react'\nimport { tv } from 'tailwind-variants'\n\nimport { type ResponseStatus, useResponseStatus } from '../../../hooks/useResponseStatus'\nimport { useIntl } from '../../../intl'\nimport { Button, type AbstractProps as ButtonProps } from '../../Button'\nimport { FaCircleCheckIcon, FaFilterIcon, FaRotateLeftIcon } from '../../Icon'\nimport { Cluster, Stack } from '../../Layout'\nimport { ResponseMessage } from '../../ResponseMessage'\nimport { Dropdown } from '../Dropdown'\nimport { DropdownCloser } from '../DropdownCloser'\nimport { DropdownContent } from '../DropdownContent'\nimport { DropdownTrigger } from '../DropdownTrigger'\n\ntype ObjectTriggerType = {\n text?: ReactNode\n /** 引き金となるボタンの大きさ */\n size?: ButtonProps['size']\n /** 引き金となるボタンをアイコンのみとするかどうか */\n onlyIcon?: boolean\n}\ntype AbstractProps = {\n /** 引き金となるボタン */\n trigger?: ReactNode | ObjectTriggerType\n applyText?: ReactNode\n cancelText?: ReactNode\n resetText?: ReactNode\n children: ReactNode\n filtered?:\n | boolean\n | {\n iconAlt?: string\n }\n responseStatus?: ResponseStatus\n onApply: MouseEventHandler<HTMLButtonElement>\n onCancel?: MouseEventHandler<HTMLButtonElement>\n onReset?: MouseEventHandler<HTMLButtonElement>\n onOpen?: () => void\n onClose?: () => void\n}\ntype Props = AbstractProps & Omit<ComponentProps<'button'>, keyof AbstractProps>\n\nconst CONTROL_CLUSTER_GAP: ComponentProps<typeof Cluster>['gap'] = { column: 1, row: 0.5 }\nconst ON_SUBMIT = (e: FormEvent) => {\n e.preventDefault()\n}\n\nconst classNameGenerator = tv({\n slots: {\n iconWrapper: ['smarthr-ui-Icon-extended', 'shr-relative shr-leading-none'],\n filteredIcon: 'shr-absolute shr-bottom-[2px] shr-right-[-4px] shr-h-[0.5em] shr-w-[0.5em]',\n inner: 'shr-p-1.5',\n actionArea: 'shr-border-t-shorthand shr-sticky shr-bottom-0 shr-bg-white shr-px-1.5 shr-py-1',\n resetButtonArea: '-shr-ms-0.5',\n rightButtonArea: 'shr-ms-auto',\n message: 'shr-text-right',\n },\n variants: {\n filtered: {\n true: {\n iconWrapper: 'shr-text-main',\n },\n },\n triggerSize: {\n M: {},\n S: {\n iconWrapper: '-shr-translate-x-0.25',\n },\n },\n },\n})\n\nexport const FilterDropdown: FC<Props> = ({\n trigger: orgTrigger,\n applyText,\n cancelText,\n resetText,\n children,\n filtered,\n responseStatus,\n onApply,\n onCancel,\n onReset,\n onOpen,\n onClose,\n ...rest\n}) => {\n // HINT: ReactNodeとObjectのどちらかを判定\n // typeofはnullの場合もobject判定されてしまうため念の為falsyで判定\n // ReactNodeの一部であるReactElementもobjectとして判定されてしまうためisValidElementで判定\n const trigger: ObjectTriggerType =\n !orgTrigger || typeof orgTrigger !== 'object' || isValidElement(orgTrigger)\n ? {\n text: orgTrigger as ReactNode,\n }\n : (orgTrigger as ObjectTriggerType)\n const { localize } = useIntl()\n\n const decorated = useMemo(\n () => ({\n filteredIconAlt:\n (typeof filtered === 'object' && filtered.iconAlt) ||\n localize({\n id: 'smarthr-ui/FilterDropdown/status',\n defaultText: '適用中',\n }),\n trigger:\n trigger.text ||\n localize({\n id: 'smarthr-ui/FilterDropdown/triggerText',\n defaultText: '絞り込み',\n }),\n applyText:\n applyText ||\n localize({\n id: 'smarthr-ui/FilterDropdown/applyText',\n defaultText: '適用',\n }),\n cancelText:\n cancelText ||\n localize({\n id: 'smarthr-ui/FilterDropdown/cancelText',\n defaultText: 'キャンセル',\n }),\n resetText:\n resetText ||\n localize({\n id: 'smarthr-ui/FilterDropdown/resetText',\n defaultText: '絞り込み条件を解除',\n }),\n }),\n [filtered, trigger.text, applyText, cancelText, resetText, localize],\n )\n\n const calcedResponseStatus = useResponseStatus(responseStatus)\n\n const classNamesMapper = useMemo(() => {\n const {\n iconWrapper,\n filteredIcon,\n inner,\n actionArea,\n resetButtonArea,\n rightButtonArea,\n message,\n } = classNameGenerator()\n\n const commonStyles = {\n filteredIcon: filteredIcon(),\n inner: inner(),\n actionArea: actionArea(),\n resetButtonArea: resetButtonArea(),\n rightButtonArea: rightButtonArea(),\n message: message(),\n }\n\n return {\n filtered: {\n ...commonStyles,\n iconWrapper: iconWrapper({ filtered: true, triggerSize: trigger.size }),\n },\n unfiltered: {\n ...commonStyles,\n iconWrapper: iconWrapper({ filtered: false, triggerSize: trigger.size }),\n },\n }\n }, [trigger.size])\n\n const classNames = classNamesMapper[filtered ? 'filtered' : 'unfiltered']\n\n const { buttonSuffix, buttonContent } = useMemo(() => {\n const FilterIcon = (\n <span className={classNames.iconWrapper}>\n <FaFilterIcon alt={trigger.onlyIcon ? decorated.trigger : undefined} />\n\n {filtered && (\n // HINT: altに揃えたいが、styleが複雑になってしまうためaria-labelを利用している\n <FaCircleCheckIcon\n aria-label={decorated.filteredIconAlt}\n className={classNames.filteredIcon}\n />\n )}\n </span>\n )\n\n if (trigger.onlyIcon) {\n return {\n buttonSuffix: undefined,\n buttonContent: FilterIcon,\n }\n }\n\n return {\n buttonSuffix: FilterIcon,\n buttonContent: decorated.trigger,\n }\n }, [filtered, decorated.trigger, decorated.filteredIconAlt, trigger.onlyIcon, classNames])\n\n return (\n <Dropdown onOpen={onOpen} onClose={onClose}>\n <DropdownTrigger tooltip={{ show: trigger.onlyIcon, message: decorated.trigger }}>\n <Button {...rest} suffix={buttonSuffix} size={trigger.size}>\n {buttonContent}\n </Button>\n </DropdownTrigger>\n <DropdownContent controllable>\n <form onSubmit={ON_SUBMIT}>\n <div className={classNames.inner}>{children}</div>\n <Stack gap={0.5} className={classNames.actionArea}>\n <Cluster gap={1} align=\"center\" justify=\"space-between\">\n {onReset && (\n <div className={classNames.resetButtonArea}>\n <Button\n variant=\"text\"\n size=\"S\"\n prefix={<FaRotateLeftIcon />}\n onClick={onReset}\n disabled={calcedResponseStatus.isProcessing}\n >\n {decorated.resetText}\n </Button>\n </div>\n )}\n\n <Cluster\n gap={CONTROL_CLUSTER_GAP}\n justify=\"flex-end\"\n className={classNames.rightButtonArea}\n >\n <DropdownCloser>\n <Button onClick={onCancel} disabled={calcedResponseStatus.isProcessing}>\n {decorated.cancelText}\n </Button>\n </DropdownCloser>\n <DropdownCloser>\n <Button\n variant=\"primary\"\n onClick={onApply}\n loading={calcedResponseStatus.isProcessing}\n >\n {decorated.applyText}\n </Button>\n </DropdownCloser>\n </Cluster>\n </Cluster>\n {calcedResponseStatus.message && (\n <div className={classNames.message}>\n <ResponseMessage status={calcedResponseStatus.status} role=\"alert\">\n {calcedResponseStatus.message}\n </ResponseMessage>\n </div>\n )}\n </Stack>\n </form>\n </DropdownContent>\n </Dropdown>\n )\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoDA;AACA;;AAEA;AAEA;AACE;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACD;AACD;AACE;AACE;AACE;AACD;AACF;AACD;AACE;AACA;AACE;AACD;AACF;AACF;AACF;AAEM;;;;AAkBL;AAEI;AACI;AACD;;AAEP;AAEA;;AAIM;AACE;AACA;;;AAIF;AACE;AACA;;AAEJ;AAEE;AACE;AACA;;AAEJ;AAEE;AACE;AACA;;AAEJ;AAEE;AACE;AACA;;AAEL;AAIH;AAEA;AACE;AAUA;;;;;;;;;AAUE;AACE;AACA;AACD;AACD;AACE;AACA;AACD;;AAEL;AAEA;;AAGE;;AAMM;AAQN;;AAEI;AACA;;;;AAKF;;;AAGJ;;AA6DF;;"}
@@ -4,8 +4,8 @@ var React = require('react');
4
4
  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');
5
5
  require('../../../intl/IntlProvider.cjs');
6
6
  var intl_useIntl = require('../../../intl/useIntl.cjs');
7
+ require('../../../intl/Localizer.cjs');
7
8
  require('react/jsx-runtime');
8
- require('react-intl');
9
9
  require('../../../_virtual/dayjs.min.cjs');
10
10
  require('../../../intl/TimeFormatter.cjs');
11
11
  var components_Icon_FaIcon = require('../../Icon/FaIcon.cjs');
@@ -2,8 +2,8 @@ import { useMemo, useState, useEffect, useCallback } from 'react';
2
2
  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';
3
3
  import '../../../intl/IntlProvider.js';
4
4
  import { useIntl } from '../../../intl/useIntl.js';
5
+ import '../../../intl/Localizer.js';
5
6
  import 'react/jsx-runtime';
6
- import 'react-intl';
7
7
  import '../../../_virtual/dayjs.min.js';
8
8
  import '../../../intl/TimeFormatter.js';
9
9
  import { FaArrowUpWideShortIcon, FaArrowDownWideShortIcon } from '../../Icon/FaIcon.js';
@@ -4,7 +4,7 @@
4
4
  var jsxRuntime = require('react/jsx-runtime');
5
5
  require('../../intl/IntlProvider.cjs');
6
6
  var intl_useIntl = require('../../intl/useIntl.cjs');
7
- require('react-intl');
7
+ require('../../intl/Localizer.cjs');
8
8
  require('../../_virtual/dayjs.min.cjs');
9
9
  require('../../intl/TimeFormatter.cjs');
10
10
  var components_ErrorScreen_ErrorScreen = require('./ErrorScreen.cjs');
@@ -2,7 +2,7 @@
2
2
  import { jsx } from 'react/jsx-runtime';
3
3
  import '../../intl/IntlProvider.js';
4
4
  import { useIntl } from '../../intl/useIntl.js';
5
- import 'react-intl';
5
+ import '../../intl/Localizer.js';
6
6
  import '../../_virtual/dayjs.min.js';
7
7
  import '../../intl/TimeFormatter.js';
8
8
  import { ErrorScreen } from './ErrorScreen.js';
@@ -4,7 +4,7 @@
4
4
  var jsxRuntime = require('react/jsx-runtime');
5
5
  require('../../intl/IntlProvider.cjs');
6
6
  var intl_useIntl = require('../../intl/useIntl.cjs');
7
- require('react-intl');
7
+ require('../../intl/Localizer.cjs');
8
8
  require('../../_virtual/dayjs.min.cjs');
9
9
  require('../../intl/TimeFormatter.cjs');
10
10
  var components_ErrorScreen_ErrorScreen = require('./ErrorScreen.cjs');
@@ -2,7 +2,7 @@
2
2
  import { jsx } from 'react/jsx-runtime';
3
3
  import '../../intl/IntlProvider.js';
4
4
  import { useIntl } from '../../intl/useIntl.js';
5
- import 'react-intl';
5
+ import '../../intl/Localizer.js';
6
6
  import '../../_virtual/dayjs.min.js';
7
7
  import '../../intl/TimeFormatter.js';
8
8
  import { ErrorScreen } from './ErrorScreen.js';
@@ -4,7 +4,7 @@
4
4
  var jsxRuntime = require('react/jsx-runtime');
5
5
  require('../../intl/IntlProvider.cjs');
6
6
  var intl_useIntl = require('../../intl/useIntl.cjs');
7
- require('react-intl');
7
+ require('../../intl/Localizer.cjs');
8
8
  require('../../_virtual/dayjs.min.cjs');
9
9
  require('../../intl/TimeFormatter.cjs');
10
10
  var components_ErrorScreen_ErrorScreen = require('./ErrorScreen.cjs');
@@ -2,7 +2,7 @@
2
2
  import { jsx } from 'react/jsx-runtime';
3
3
  import '../../intl/IntlProvider.js';
4
4
  import { useIntl } from '../../intl/useIntl.js';
5
- import 'react-intl';
5
+ import '../../intl/Localizer.js';
6
6
  import '../../_virtual/dayjs.min.js';
7
7
  import '../../intl/TimeFormatter.js';
8
8
  import { ErrorScreen } from './ErrorScreen.js';
@@ -4,7 +4,7 @@
4
4
  var jsxRuntime = require('react/jsx-runtime');
5
5
  require('../../intl/IntlProvider.cjs');
6
6
  var intl_useIntl = require('../../intl/useIntl.cjs');
7
- require('react-intl');
7
+ require('../../intl/Localizer.cjs');
8
8
  require('../../_virtual/dayjs.min.cjs');
9
9
  require('../../intl/TimeFormatter.cjs');
10
10
  var components_Button_Button = require('../Button/Button.cjs');
@@ -2,7 +2,7 @@
2
2
  import { jsxs, jsx } from 'react/jsx-runtime';
3
3
  import '../../intl/IntlProvider.js';
4
4
  import { useIntl } from '../../intl/useIntl.js';
5
- import 'react-intl';
5
+ import '../../intl/Localizer.js';
6
6
  import '../../_virtual/dayjs.min.js';
7
7
  import '../../intl/TimeFormatter.js';
8
8
  import { Button } from '../Button/Button.js';
@@ -35,7 +35,6 @@ const classNameGenerator = index.tv({
35
35
  errorList: ['shr-list-none'],
36
36
  errorIcon: ['smarthr-ui-FormControl-errorMessage-Icon', 'shr-text-danger'],
37
37
  errorMessage: ['smarthr-ui-FormControl-errorMessage'],
38
- underLabelStack: ['[&&&]:shr-mt-0'],
39
38
  childrenWrapper: [],
40
39
  },
41
40
  variants: {
@@ -76,13 +75,6 @@ const classNameGenerator = index.tv({
76
75
  {
77
76
  innerMargin: undefined,
78
77
  isFieldset: true,
79
- class: {
80
- childrenWrapper: '[:not([hidden])_~_&&&]:shr-mt-1',
81
- },
82
- },
83
- {
84
- innerMargin: undefined,
85
- isFieldset: false,
86
78
  class: {
87
79
  childrenWrapper: '[:not([hidden])_~_&&&]:shr-mt-0.5',
88
80
  },
@@ -122,7 +114,6 @@ const ActualFormControl = ({ label: orgLabel, subActionArea, innerMargin, status
122
114
  }
123
115
  return Array.isArray(errorMessages) ? errorMessages : [errorMessages];
124
116
  }, [errorMessages]);
125
- const actualInnerMargin = React.useMemo(() => innerMargin ?? 0.5, [innerMargin]);
126
117
  const classNames = React.useMemo(() => {
127
118
  const generators = classNameGenerator({ innerMargin, isFieldset });
128
119
  return {
@@ -133,7 +124,6 @@ const ActualFormControl = ({ label: orgLabel, subActionArea, innerMargin, status
133
124
  errorList: generators.errorList(),
134
125
  errorIcon: generators.errorIcon(),
135
126
  errorMessage: generators.errorMessage(),
136
- underLabelStack: generators.underLabelStack(),
137
127
  childrenWrapper: generators.childrenWrapper(),
138
128
  };
139
129
  }, [innerMargin, isFieldset, label.unrecommendedHide, className]);
@@ -217,14 +207,7 @@ const ActualFormControl = ({ label: orgLabel, subActionArea, innerMargin, status
217
207
  }
218
208
  });
219
209
  }, [isFieldset, label.text]);
220
- let body = (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(HelpMessageParagraph, { helpMessage: helpMessage, managedHtmlFor: managedHtmlFor }), jsxRuntime.jsx(ExampleMessageText, { exampleMessage: exampleMessage, managedHtmlFor: managedHtmlFor }), jsxRuntime.jsx(ErrorMessageList, { errorMessages: actualErrorMessages, managedHtmlFor: managedHtmlFor, classNames: classNames }), jsxRuntime.jsx("div", { className: classNames.childrenWrapper, ref: inputWrapperRef, children: children }), jsxRuntime.jsx(SupplementaryMessageText, { supplementaryMessage: supplementaryMessage, managedHtmlFor: managedHtmlFor })] }));
221
- // HINT: label.unrecommendedHideの場合、body以下の余白の計算を簡略化するため
222
- // Stackをネストし、そのStackに対してmargin-top: 0を指定する
223
- // こうすることでinner Stack以下の要素は擬似的にStackの最初の要素になる
224
- if (label.unrecommendedHide) {
225
- body = (jsxRuntime.jsx(components_Layout_Stack_Stack.Stack, { gap: actualInnerMargin, className: classNames.underLabelStack, children: body }));
226
- }
227
- return (jsxRuntime.jsxs(components_Layout_Stack_Stack.Stack, { ...rest, as: as, gap: actualInnerMargin, "aria-describedby": isFieldset && describedbyIds ? describedbyIds : undefined, className: classNames.wrapper, children: [jsxRuntime.jsx(LabelCluster, { isFieldset: isFieldset, managedHtmlFor: managedHtmlFor, managedLabelId: managedLabelId, unrecommendedHideLabel: label.unrecommendedHide, labelType: label.styleType, label: label.text, labelIcon: label.icon, statusLabels: actualStatusLabels, subActionArea: subActionArea, labelClassName: classNames.label }), body] }));
210
+ return (jsxRuntime.jsxs(components_Layout_Stack_Stack.Stack, { ...rest, as: as, gap: innerMargin ?? 0.5, "aria-describedby": isFieldset && describedbyIds ? describedbyIds : undefined, className: classNames.wrapper, children: [jsxRuntime.jsx(LabelCluster, { isFieldset: isFieldset, managedHtmlFor: managedHtmlFor, managedLabelId: managedLabelId, unrecommendedHideLabel: label.unrecommendedHide, labelType: label.styleType, label: label.text, labelIcon: label.icon, statusLabels: actualStatusLabels, subActionArea: subActionArea, labelClassName: classNames.label }), jsxRuntime.jsx(HelpMessageParagraph, { helpMessage: helpMessage, managedHtmlFor: managedHtmlFor }), jsxRuntime.jsx(ExampleMessageText, { exampleMessage: exampleMessage, managedHtmlFor: managedHtmlFor }), jsxRuntime.jsx(ErrorMessageList, { errorMessages: actualErrorMessages, managedHtmlFor: managedHtmlFor, classNames: classNames }), jsxRuntime.jsx("div", { className: classNames.childrenWrapper, ref: inputWrapperRef, children: children }), jsxRuntime.jsx(SupplementaryMessageText, { supplementaryMessage: supplementaryMessage, managedHtmlFor: managedHtmlFor })] }));
228
211
  };
229
212
  const LabelCluster = React.memo(({ isFieldset, managedHtmlFor, managedLabelId, unrecommendedHideLabel, labelType = 'blockTitle', label, labelIcon, subActionArea, labelClassName, statusLabels, }) => {
230
213
  const body = (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(components_Text_Text.Text, { styleType: labelType, icon: labelIcon, children: label }), jsxRuntime.jsx(StatusLabelCluster, { statusLabels: statusLabels })] }));
@@ -261,10 +244,7 @@ const LabelCluster = React.memo(({ isFieldset, managedHtmlFor, managedLabelId, u
261
244
  return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [attrs.visuallyHidden && (jsxRuntime.jsx(components_VisuallyHiddenText_VisuallyHiddenText.VisuallyHiddenText, { ...attrs.visuallyHidden, children:
262
245
  // HINT: innerTextでは正しく文字が取得できない場合がある
263
246
  // 安全策としてinnerTextが空を取得してきたらbody自体を埋めこみます
264
- index$1(body) || body })), attrs.label && (jsxRuntime.jsxs(components_Layout_Cluster_Cluster.Cluster, { justify: "space-between",
265
- // HINT: UI上、常にトップの要素になるため、Stackの計算が狂わないよう、
266
- // 常にmargin-topを0にする
267
- className: "[&&&]:shr--mt-0", children: [jsxRuntime.jsx(components_Layout_Cluster_Cluster.Cluster, { ...attrs.label, align: "center", className: labelClassName, children: body }), subActionArea && jsxRuntime.jsx("div", { className: "shr-grow", children: subActionArea })] }))] }));
247
+ index$1(body) || body })), attrs.label && (jsxRuntime.jsxs(components_Layout_Cluster_Cluster.Cluster, { justify: "space-between", children: [jsxRuntime.jsx(components_Layout_Cluster_Cluster.Cluster, { ...attrs.label, align: "center", className: labelClassName, children: body }), subActionArea && jsxRuntime.jsx("div", { className: "shr-grow", children: subActionArea })] }))] }));
268
248
  });
269
249
  const StatusLabelCluster = React.memo(({ statusLabels }) => statusLabels.length === 0 ? null : (jsxRuntime.jsx(components_Layout_Cluster_Cluster.Cluster, { gap: 0.25, as: "span", children: statusLabels })));
270
250
  const HelpMessageParagraph = React.memo(({ helpMessage, managedHtmlFor }) => helpMessage ? (jsxRuntime.jsx("p", { className: "smarthr-ui-FormControl-helpMessage", id: `${managedHtmlFor}_helpMessage`, children: helpMessage })) : null);
@@ -1 +1 @@
1
- {"version":3,"file":"FormControl.cjs","sources":["../../../src/components/FormControl/FormControl.tsx"],"sourcesContent":["'use client'\n\nimport {\n type ComponentProps,\n type ComponentPropsWithoutRef,\n type ComponentType,\n type FC,\n type FunctionComponentElement,\n type PropsWithChildren,\n type ReactNode,\n memo,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react'\nimport { useId } from 'react'\nimport innerText from 'react-innertext'\nimport { tv } from 'tailwind-variants'\n\nimport { useObjectAttributes } from '../../hooks/useObjectAttributes'\nimport { FaCircleExclamationIcon } from '../Icon'\nimport { Cluster, Stack } from '../Layout'\nimport { Text, type TextProps } from '../Text'\nimport { VisuallyHiddenText, visuallyHiddenTextClassName } from '../VisuallyHiddenText'\n\nimport type { Gap } from '../../types'\nimport type { StatusLabel } from '../StatusLabel'\n\ntype StatusLabelType = FunctionComponentElement<ComponentProps<typeof StatusLabel>>\ntype IconType = ComponentProps<typeof Text>['icon']\n\ntype ObjectLabelType = {\n text: ReactNode\n /** ラベルの表示タイプ */\n styleType?: TextProps['styleType']\n /** ラベル左に設置するアイコン */\n icon?: IconType\n /** ラベルを視覚的に隠すかどうか */\n unrecommendedHide?: boolean\n /** ラベルを紐づける入力要素のID属性と同じ値 */\n htmlFor?: string\n /** ラベルに適用する `id` 値 */\n id?: string\n}\ntype AbstractProps = PropsWithChildren<{\n /** グループのラベル名 */\n label: ReactNode | ObjectLabelType\n /** タイトル右の領域 */\n subActionArea?: ReactNode\n /** タイトル群と子要素の間の間隔調整用(基本的には不要) */\n innerMargin?: Gap\n /** タイトルの隣に表示する `StatusLabel` の配列 */\n statusLabels?: StatusLabelType | StatusLabelType[]\n /** タイトルの下に表示するヘルプメッセージ */\n helpMessage?: ReactNode\n /** タイトルの下に表示する入力例 */\n exampleMessage?: ReactNode\n /** タイトルの下に表示するエラーメッセージ */\n errorMessages?: ReactNode | ReactNode[]\n /** エラーがある場合に自動的に入力要素を error にするかどうか */\n autoBindErrorInput?: boolean\n /** フォームコントロールの下に表示する補足メッセージ */\n supplementaryMessage?: ReactNode\n /** `true` のとき、文字色を `TEXT_DISABLED` にする */\n disabled?: boolean\n as?: string | ComponentType<any>\n}>\ntype Props = AbstractProps &\n Omit<ComponentPropsWithoutRef<'div'>, keyof AbstractProps | 'aria-labelledby'>\n\nconst labelObjectConverter = (label: ReactNode) => ({ text: label })\n\nconst classNameGenerator = tv({\n slots: {\n wrapper: [\n 'smarthr-ui-FormControl',\n 'shr-mx-[unset] shr-border-none shr-p-[unset]',\n 'disabled:shr-text-disabled',\n '[&:disabled_.smarthr-ui-FormControl-label_>_span]:shr-text-disabled',\n '[&:disabled_.smarthr-ui-FormControl-exampleMessage]:shr-text-color-inherit',\n '[&:disabled_.smarthr-ui-FormControl-errorMessage-Icon]:shr-text-color-inherit',\n '[&:disabled_.smarthr-ui-FormControl-supplementaryMessage]:shr-text-color-inherit',\n '[&:disabled_.smarthr-ui-Input]:shr-border-default/50 [&:disabled_.smarthr-ui-Input]:shr-bg-white-darken',\n ],\n label: ['smarthr-ui-FormControl-label'],\n errorList: ['shr-list-none'],\n errorIcon: ['smarthr-ui-FormControl-errorMessage-Icon', 'shr-text-danger'],\n errorMessage: ['smarthr-ui-FormControl-errorMessage'],\n underLabelStack: ['[&&&]:shr-mt-0'],\n childrenWrapper: [],\n },\n variants: {\n innerMargin: {\n 0: {},\n 0.25: {},\n 0.5: {},\n 0.75: {},\n 1: {},\n 1.25: {},\n 1.5: {},\n 2: {},\n 2.5: {},\n 3: {},\n 3.5: {},\n 4: {},\n 8: {},\n X3S: {},\n XXS: {},\n XS: {},\n S: {},\n M: {},\n L: {},\n XL: {},\n XXL: {},\n X3L: {},\n } as { [key in Gap]: string },\n isFieldset: {\n true: {},\n false: {},\n },\n },\n compoundVariants: [\n // TODO: innerMarginが未指定、初期値の場合、かつFieldsetの場合、childrenの上部の余白を広げることで\n // FormControltとの差をわかりやすくしている\n // 微妙な方法ではあるので、必要に応じてinnerMarginではない属性を用意する\n // https://kufuinc.slack.com/archives/CGC58MW01/p1737944965871159?thread_ts=1737541173.404369&cid=CGC58MW01\n {\n innerMargin: undefined,\n isFieldset: true,\n class: {\n childrenWrapper: '[:not([hidden])_~_&&&]:shr-mt-1',\n },\n },\n {\n innerMargin: undefined,\n isFieldset: false,\n class: {\n childrenWrapper: '[:not([hidden])_~_&&&]:shr-mt-0.5',\n },\n },\n ],\n})\n\nconst SMARTHR_UI_INPUT_SELECTOR = '[data-smarthr-ui-input=\"true\"]'\n\nexport const ActualFormControl: FC<Props> = ({\n label: orgLabel,\n subActionArea,\n innerMargin,\n statusLabels,\n helpMessage,\n exampleMessage,\n errorMessages,\n autoBindErrorInput = true,\n supplementaryMessage,\n as = 'div',\n className,\n children,\n ...rest\n}) => {\n const label = useObjectAttributes<ReactNode | ObjectLabelType, ObjectLabelType>(\n orgLabel,\n labelObjectConverter,\n )\n const defaultHtmlFor = useId()\n const defaultLabelId = useId()\n const [childInputId, setChildInputId] = useState<string>('')\n const managedHtmlFor = label.htmlFor || childInputId || defaultHtmlFor\n const managedLabelId = label.id || defaultLabelId\n const inputWrapperRef = useRef<HTMLDivElement>(null)\n const isFieldset = as === 'fieldset'\n\n const describedbyIds = useMemo(() => {\n const temp = []\n\n if (helpMessage) {\n temp.push(`${managedHtmlFor}_helpMessage`)\n }\n if (exampleMessage) {\n temp.push(`${managedHtmlFor}_exampleMessage`)\n }\n if (supplementaryMessage) {\n temp.push(`${managedHtmlFor}_supplementaryMessage`)\n }\n if (errorMessages) {\n temp.push(`${managedHtmlFor}_errorMessages`)\n }\n\n return temp.join(' ')\n }, [helpMessage, exampleMessage, supplementaryMessage, errorMessages, managedHtmlFor])\n\n const actualStatusLabels = useMemo(\n () => (statusLabels ? (Array.isArray(statusLabels) ? statusLabels : [statusLabels]) : []),\n [statusLabels],\n )\n\n const actualErrorMessages = useMemo(() => {\n if (!errorMessages) {\n return []\n }\n\n return Array.isArray(errorMessages) ? errorMessages : [errorMessages]\n }, [errorMessages])\n\n const actualInnerMargin = useMemo(() => innerMargin ?? 0.5, [innerMargin])\n\n const classNames = useMemo(() => {\n const generators = classNameGenerator({ innerMargin, isFieldset })\n\n return {\n wrapper: generators.wrapper({ className }),\n label: generators.label({\n className: label.unrecommendedHide ? visuallyHiddenTextClassName : '',\n }),\n errorList: generators.errorList(),\n errorIcon: generators.errorIcon(),\n errorMessage: generators.errorMessage(),\n underLabelStack: generators.underLabelStack(),\n childrenWrapper: generators.childrenWrapper(),\n }\n }, [innerMargin, isFieldset, label.unrecommendedHide, className])\n\n useEffect(() => {\n if (\n isFieldset ||\n !inputWrapperRef?.current ||\n // HINT: 対象idを持つ要素が既に存在する場合、何もしない\n document.getElementById(managedHtmlFor)\n ) {\n return\n }\n\n const input = inputWrapperRef.current.querySelector(SMARTHR_UI_INPUT_SELECTOR)\n\n if (!input) {\n return\n }\n\n const inputId = input.getAttribute('id')\n\n if (inputId) {\n setChildInputId(inputId)\n } else {\n input.setAttribute('id', managedHtmlFor)\n }\n\n if (input instanceof HTMLInputElement && input.type === 'file') {\n const attrName = 'aria-labelledby'\n const inputLabelledByIds = input.getAttribute(attrName)\n\n if (inputLabelledByIds) {\n // InputFileの場合はlabel要素の可視ラベルをアクセシブルネームに含める\n input.setAttribute(attrName, `${inputLabelledByIds} ${managedLabelId}`)\n }\n }\n }, [managedHtmlFor, isFieldset, managedLabelId])\n\n useEffect(() => {\n if (!describedbyIds || !inputWrapperRef?.current) {\n return\n }\n\n const inputWrapper = inputWrapperRef.current\n const attrName = 'aria-describedby'\n\n if (inputWrapper.querySelector(`[${attrName}=\"${describedbyIds}\"]`)) {\n return\n }\n\n const input = inputWrapper.querySelector(SMARTHR_UI_INPUT_SELECTOR)\n\n if (input) {\n const attribute = input.getAttribute(attrName)\n\n input.setAttribute(attrName, attribute ? `${attribute} ${describedbyIds}` : describedbyIds)\n }\n }, [describedbyIds])\n\n useEffect(() => {\n if (!autoBindErrorInput || !inputWrapperRef?.current) {\n return\n }\n\n const input = inputWrapperRef.current.querySelector(SMARTHR_UI_INPUT_SELECTOR)\n\n if (input) {\n const attrName = 'aria-invalid'\n\n if (actualErrorMessages.length > 0) {\n input.setAttribute(attrName, 'true')\n } else {\n input.removeAttribute(attrName)\n }\n }\n }, [actualErrorMessages.length, autoBindErrorInput])\n\n // HINT: Fieldset内の可視ラベルが無いinputに、legend文言をアクセシブルネームに追加する\n // https://waic.jp/translations/WCAG21/Understanding/label-in-name.html\n useEffect(() => {\n if (!isFieldset || !inputWrapperRef.current) return\n\n const inputs =\n inputWrapperRef.current.querySelectorAll<HTMLInputElement>(SMARTHR_UI_INPUT_SELECTOR)\n\n if (!inputs.length) return\n\n const legendText = innerText(label.text)\n\n if (!legendText) return\n\n inputs.forEach((input: HTMLInputElement) => {\n const accessibleName =\n input.getAttribute('aria-label') ||\n (input.labels?.[0]?.classList.contains('smarthr-ui-VisuallyHiddenText')\n ? input.labels![0].textContent\n : '')\n\n if (\n accessibleName &&\n !accessibleName.includes(legendText) &&\n !legendText.includes(accessibleName)\n ) {\n input.setAttribute('aria-label', `${accessibleName} ${legendText}`)\n }\n })\n }, [isFieldset, label.text])\n\n let body = (\n <>\n <HelpMessageParagraph helpMessage={helpMessage} managedHtmlFor={managedHtmlFor} />\n <ExampleMessageText exampleMessage={exampleMessage} managedHtmlFor={managedHtmlFor} />\n <ErrorMessageList\n errorMessages={actualErrorMessages}\n managedHtmlFor={managedHtmlFor}\n classNames={classNames}\n />\n <div className={classNames.childrenWrapper} ref={inputWrapperRef}>\n {children}\n </div>\n <SupplementaryMessageText\n supplementaryMessage={supplementaryMessage}\n managedHtmlFor={managedHtmlFor}\n />\n </>\n )\n\n // HINT: label.unrecommendedHideの場合、body以下の余白の計算を簡略化するため\n // Stackをネストし、そのStackに対してmargin-top: 0を指定する\n // こうすることでinner Stack以下の要素は擬似的にStackの最初の要素になる\n if (label.unrecommendedHide) {\n body = (\n <Stack gap={actualInnerMargin} className={classNames.underLabelStack}>\n {body}\n </Stack>\n )\n }\n\n return (\n <Stack\n {...rest}\n as={as}\n gap={actualInnerMargin}\n aria-describedby={isFieldset && describedbyIds ? describedbyIds : undefined}\n className={classNames.wrapper}\n >\n <LabelCluster\n isFieldset={isFieldset}\n managedHtmlFor={managedHtmlFor}\n managedLabelId={managedLabelId}\n unrecommendedHideLabel={label.unrecommendedHide}\n labelType={label.styleType}\n label={label.text}\n labelIcon={label.icon}\n statusLabels={actualStatusLabels}\n subActionArea={subActionArea}\n labelClassName={classNames.label}\n />\n {body}\n </Stack>\n )\n}\n\nconst LabelCluster = memo<\n Pick<Props, 'subActionArea'> & {\n label: ReactNode\n labelType: TextProps['styleType']\n labelIcon?: IconType\n unrecommendedHideLabel?: boolean\n isFieldset: boolean\n managedHtmlFor: string\n managedLabelId: string\n labelClassName: string\n statusLabels: StatusLabelType[]\n }\n>(\n ({\n isFieldset,\n managedHtmlFor,\n managedLabelId,\n unrecommendedHideLabel,\n labelType = 'blockTitle',\n label,\n labelIcon,\n subActionArea,\n labelClassName,\n statusLabels,\n }) => {\n const body = (\n <>\n <Text styleType={labelType} icon={labelIcon}>\n {label}\n </Text>\n <StatusLabelCluster statusLabels={statusLabels} />\n </>\n )\n\n const attrs = useMemo(() => {\n if (unrecommendedHideLabel) {\n return {\n label: null,\n visuallyHidden: isFieldset\n ? {\n as: 'legend',\n }\n : {\n as: 'label',\n htmlFor: managedHtmlFor,\n id: managedLabelId,\n },\n }\n }\n\n if (isFieldset) {\n return {\n label: { 'aria-hidden': 'true' } as const,\n visuallyHidden: { as: 'legend' },\n }\n }\n\n return {\n label: {\n as: 'label' as const,\n htmlFor: managedHtmlFor,\n id: managedLabelId,\n },\n visuallyHidden: null,\n }\n }, [managedLabelId, managedHtmlFor, unrecommendedHideLabel, isFieldset])\n\n return (\n <>\n {attrs.visuallyHidden && (\n <VisuallyHiddenText {...attrs.visuallyHidden}>\n {\n // HINT: innerTextでは正しく文字が取得できない場合がある\n // 安全策としてinnerTextが空を取得してきたらbody自体を埋めこみます\n innerText(body) || body\n }\n </VisuallyHiddenText>\n )}\n {attrs.label && (\n <Cluster\n justify=\"space-between\"\n // HINT: UI上、常にトップの要素になるため、Stackの計算が狂わないよう、\n // 常にmargin-topを0にする\n className=\"[&&&]:shr--mt-0\"\n >\n <Cluster {...attrs.label} align=\"center\" className={labelClassName}>\n {body}\n </Cluster>\n {subActionArea && <div className=\"shr-grow\">{subActionArea}</div>}\n </Cluster>\n )}\n </>\n )\n },\n)\n\nconst StatusLabelCluster = memo<{ statusLabels: StatusLabelType[] }>(({ statusLabels }) =>\n statusLabels.length === 0 ? null : (\n <Cluster gap={0.25} as=\"span\">\n {statusLabels}\n </Cluster>\n ),\n)\n\nconst HelpMessageParagraph = memo<Pick<Props, 'helpMessage'> & { managedHtmlFor: string }>(\n ({ helpMessage, managedHtmlFor }) =>\n helpMessage ? (\n <p className=\"smarthr-ui-FormControl-helpMessage\" id={`${managedHtmlFor}_helpMessage`}>\n {helpMessage}\n </p>\n ) : null,\n)\n\nconst ExampleMessageText = memo<Pick<Props, 'exampleMessage'> & { managedHtmlFor: string }>(\n ({ exampleMessage, managedHtmlFor }) =>\n exampleMessage ? (\n <Text\n as=\"p\"\n color=\"TEXT_GREY\"\n italic\n id={`${managedHtmlFor}_exampleMessage`}\n className=\"smarthr-ui-FormControl-exampleMessage\"\n >\n {exampleMessage}\n </Text>\n ) : null,\n)\n\nconst ErrorMessageList = memo<{\n errorMessages: ReactNode[]\n managedHtmlFor: string\n classNames: {\n errorList: string\n errorIcon: string\n errorMessage: string\n }\n}>(({ errorMessages, managedHtmlFor, classNames }) =>\n errorMessages.length > 0 ? (\n <div id={`${managedHtmlFor}_errorMessages`} className={classNames.errorList} role=\"alert\">\n {errorMessages.map((message, index) => (\n <p key={index}>\n <Text\n className={classNames.errorMessage}\n icon={<FaCircleExclamationIcon className={classNames.errorIcon} />}\n >\n {message}\n </Text>\n </p>\n ))}\n </div>\n ) : null,\n)\n\nconst SupplementaryMessageText = memo<\n Pick<Props, 'supplementaryMessage'> & { managedHtmlFor: string }\n>(({ supplementaryMessage, managedHtmlFor }) =>\n supplementaryMessage ? (\n <Text\n as=\"p\"\n size=\"S\"\n color=\"TEXT_GREY\"\n id={`${managedHtmlFor}_supplementaryMessage`}\n className=\"smarthr-ui-FormControl-supplementaryMessage\"\n >\n {supplementaryMessage}\n </Text>\n ) : null,\n)\n\nexport const FormControl: FC<Omit<Props, 'as' | 'disabled'>> = ActualFormControl\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;AAuEA;AAEA;AACE;AACE;;;;;;;;;AASC;;;AAGD;;;AAGA;AACD;AACD;AACE;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAC2B;AAC7B;AACE;AACA;AACD;AACF;AACD;;;;;AAKE;AACE;AACA;AACA;AACE;AACD;AACF;AACD;AACE;AACA;AACA;AACE;AACD;AACF;AACF;AACF;AAED;AAEO;;AAmBL;AACA;;;AAGA;AACA;AACA;AAEA;;;AAII;;;AAGA;;;AAGA;;;AAGA;;AAGF;AACF;AAEA;AAKA;;AAEI;;AAGF;AACF;AAEA;AAEA;;;;AAKI;;;AAGA;AACA;AACA;AACA;AACA;;AAEJ;;AAGE;;;AAIE;;;;;;;;;;;;AAgBA;;;;;;;;;;;;;;;AAmBF;;;;;;;;AAYE;;AAEJ;;;;;;;;AAYI;AACE;;;AAEA;;;;;;;AAQJ;;;;;;AASA;;AAEA;AACE;AAEE;;;AAIF;AAEE;AACA;;;AAIJ;;AAGF;;;;AAsBA;AACE;;AAOF;AAuBF;AAEA;;AAkCI;;;AAGM;AACA;AACE;AACI;AACD;AACH;AACI;AACA;AACA;AACD;;;;;AAML;AACA;;;;AAKF;AACE;AACA;AACA;AACD;AACD;;;AAIJ;;;AAOU;;;AASF;AAUV;AAGF;AAQA;AASA;AAeA;AAyBA;AAgBO;;;"}
1
+ {"version":3,"file":"FormControl.cjs","sources":["../../../src/components/FormControl/FormControl.tsx"],"sourcesContent":["'use client'\n\nimport {\n type ComponentProps,\n type ComponentPropsWithoutRef,\n type ComponentType,\n type FC,\n type FunctionComponentElement,\n type PropsWithChildren,\n type ReactNode,\n memo,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react'\nimport { useId } from 'react'\nimport innerText from 'react-innertext'\nimport { tv } from 'tailwind-variants'\n\nimport { useObjectAttributes } from '../../hooks/useObjectAttributes'\nimport { FaCircleExclamationIcon } from '../Icon'\nimport { Cluster, Stack } from '../Layout'\nimport { Text, type TextProps } from '../Text'\nimport { VisuallyHiddenText, visuallyHiddenTextClassName } from '../VisuallyHiddenText'\n\nimport type { Gap } from '../../types'\nimport type { StatusLabel } from '../StatusLabel'\n\ntype StatusLabelType = FunctionComponentElement<ComponentProps<typeof StatusLabel>>\ntype IconType = ComponentProps<typeof Text>['icon']\n\ntype ObjectLabelType = {\n text: ReactNode\n /** ラベルの表示タイプ */\n styleType?: TextProps['styleType']\n /** ラベル左に設置するアイコン */\n icon?: IconType\n /** ラベルを視覚的に隠すかどうか */\n unrecommendedHide?: boolean\n /** ラベルを紐づける入力要素のID属性と同じ値 */\n htmlFor?: string\n /** ラベルに適用する `id` 値 */\n id?: string\n}\ntype AbstractProps = PropsWithChildren<{\n /** グループのラベル名 */\n label: ReactNode | ObjectLabelType\n /** タイトル右の領域 */\n subActionArea?: ReactNode\n /** タイトル群と子要素の間の間隔調整用(基本的には不要) */\n innerMargin?: Gap\n /** タイトルの隣に表示する `StatusLabel` の配列 */\n statusLabels?: StatusLabelType | StatusLabelType[]\n /** タイトルの下に表示するヘルプメッセージ */\n helpMessage?: ReactNode\n /** タイトルの下に表示する入力例 */\n exampleMessage?: ReactNode\n /** タイトルの下に表示するエラーメッセージ */\n errorMessages?: ReactNode | ReactNode[]\n /** エラーがある場合に自動的に入力要素を error にするかどうか */\n autoBindErrorInput?: boolean\n /** フォームコントロールの下に表示する補足メッセージ */\n supplementaryMessage?: ReactNode\n /** `true` のとき、文字色を `TEXT_DISABLED` にする */\n disabled?: boolean\n as?: string | ComponentType<any>\n}>\ntype Props = AbstractProps &\n Omit<ComponentPropsWithoutRef<'div'>, keyof AbstractProps | 'aria-labelledby'>\n\nconst labelObjectConverter = (label: ReactNode) => ({ text: label })\n\nconst classNameGenerator = tv({\n slots: {\n wrapper: [\n 'smarthr-ui-FormControl',\n 'shr-mx-[unset] shr-border-none shr-p-[unset]',\n 'disabled:shr-text-disabled',\n '[&:disabled_.smarthr-ui-FormControl-label_>_span]:shr-text-disabled',\n '[&:disabled_.smarthr-ui-FormControl-exampleMessage]:shr-text-color-inherit',\n '[&:disabled_.smarthr-ui-FormControl-errorMessage-Icon]:shr-text-color-inherit',\n '[&:disabled_.smarthr-ui-FormControl-supplementaryMessage]:shr-text-color-inherit',\n '[&:disabled_.smarthr-ui-Input]:shr-border-default/50 [&:disabled_.smarthr-ui-Input]:shr-bg-white-darken',\n ],\n label: ['smarthr-ui-FormControl-label'],\n errorList: ['shr-list-none'],\n errorIcon: ['smarthr-ui-FormControl-errorMessage-Icon', 'shr-text-danger'],\n errorMessage: ['smarthr-ui-FormControl-errorMessage'],\n childrenWrapper: [],\n },\n variants: {\n innerMargin: {\n 0: {},\n 0.25: {},\n 0.5: {},\n 0.75: {},\n 1: {},\n 1.25: {},\n 1.5: {},\n 2: {},\n 2.5: {},\n 3: {},\n 3.5: {},\n 4: {},\n 8: {},\n X3S: {},\n XXS: {},\n XS: {},\n S: {},\n M: {},\n L: {},\n XL: {},\n XXL: {},\n X3L: {},\n } as { [key in Gap]: string },\n isFieldset: {\n true: {},\n false: {},\n },\n },\n compoundVariants: [\n // TODO: innerMarginが未指定、初期値の場合、かつFieldsetの場合、childrenの上部の余白を広げることで\n // FormControltとの差をわかりやすくしている\n // 微妙な方法ではあるので、必要に応じてinnerMarginではない属性を用意する\n // https://kufuinc.slack.com/archives/CGC58MW01/p1737944965871159?thread_ts=1737541173.404369&cid=CGC58MW01\n {\n innerMargin: undefined,\n isFieldset: true,\n class: {\n childrenWrapper: '[:not([hidden])_~_&&&]:shr-mt-0.5',\n },\n },\n ],\n})\n\nconst SMARTHR_UI_INPUT_SELECTOR = '[data-smarthr-ui-input=\"true\"]'\n\nexport const ActualFormControl: FC<Props> = ({\n label: orgLabel,\n subActionArea,\n innerMargin,\n statusLabels,\n helpMessage,\n exampleMessage,\n errorMessages,\n autoBindErrorInput = true,\n supplementaryMessage,\n as = 'div',\n className,\n children,\n ...rest\n}) => {\n const label = useObjectAttributes<ReactNode | ObjectLabelType, ObjectLabelType>(\n orgLabel,\n labelObjectConverter,\n )\n const defaultHtmlFor = useId()\n const defaultLabelId = useId()\n const [childInputId, setChildInputId] = useState<string>('')\n const managedHtmlFor = label.htmlFor || childInputId || defaultHtmlFor\n const managedLabelId = label.id || defaultLabelId\n const inputWrapperRef = useRef<HTMLDivElement>(null)\n const isFieldset = as === 'fieldset'\n\n const describedbyIds = useMemo(() => {\n const temp = []\n\n if (helpMessage) {\n temp.push(`${managedHtmlFor}_helpMessage`)\n }\n if (exampleMessage) {\n temp.push(`${managedHtmlFor}_exampleMessage`)\n }\n if (supplementaryMessage) {\n temp.push(`${managedHtmlFor}_supplementaryMessage`)\n }\n if (errorMessages) {\n temp.push(`${managedHtmlFor}_errorMessages`)\n }\n\n return temp.join(' ')\n }, [helpMessage, exampleMessage, supplementaryMessage, errorMessages, managedHtmlFor])\n\n const actualStatusLabels = useMemo(\n () => (statusLabels ? (Array.isArray(statusLabels) ? statusLabels : [statusLabels]) : []),\n [statusLabels],\n )\n\n const actualErrorMessages = useMemo(() => {\n if (!errorMessages) {\n return []\n }\n\n return Array.isArray(errorMessages) ? errorMessages : [errorMessages]\n }, [errorMessages])\n\n const classNames = useMemo(() => {\n const generators = classNameGenerator({ innerMargin, isFieldset })\n\n return {\n wrapper: generators.wrapper({ className }),\n label: generators.label({\n className: label.unrecommendedHide ? visuallyHiddenTextClassName : '',\n }),\n errorList: generators.errorList(),\n errorIcon: generators.errorIcon(),\n errorMessage: generators.errorMessage(),\n childrenWrapper: generators.childrenWrapper(),\n }\n }, [innerMargin, isFieldset, label.unrecommendedHide, className])\n\n useEffect(() => {\n if (\n isFieldset ||\n !inputWrapperRef?.current ||\n // HINT: 対象idを持つ要素が既に存在する場合、何もしない\n document.getElementById(managedHtmlFor)\n ) {\n return\n }\n\n const input = inputWrapperRef.current.querySelector(SMARTHR_UI_INPUT_SELECTOR)\n\n if (!input) {\n return\n }\n\n const inputId = input.getAttribute('id')\n\n if (inputId) {\n setChildInputId(inputId)\n } else {\n input.setAttribute('id', managedHtmlFor)\n }\n\n if (input instanceof HTMLInputElement && input.type === 'file') {\n const attrName = 'aria-labelledby'\n const inputLabelledByIds = input.getAttribute(attrName)\n\n if (inputLabelledByIds) {\n // InputFileの場合はlabel要素の可視ラベルをアクセシブルネームに含める\n input.setAttribute(attrName, `${inputLabelledByIds} ${managedLabelId}`)\n }\n }\n }, [managedHtmlFor, isFieldset, managedLabelId])\n\n useEffect(() => {\n if (!describedbyIds || !inputWrapperRef?.current) {\n return\n }\n\n const inputWrapper = inputWrapperRef.current\n const attrName = 'aria-describedby'\n\n if (inputWrapper.querySelector(`[${attrName}=\"${describedbyIds}\"]`)) {\n return\n }\n\n const input = inputWrapper.querySelector(SMARTHR_UI_INPUT_SELECTOR)\n\n if (input) {\n const attribute = input.getAttribute(attrName)\n\n input.setAttribute(attrName, attribute ? `${attribute} ${describedbyIds}` : describedbyIds)\n }\n }, [describedbyIds])\n\n useEffect(() => {\n if (!autoBindErrorInput || !inputWrapperRef?.current) {\n return\n }\n\n const input = inputWrapperRef.current.querySelector(SMARTHR_UI_INPUT_SELECTOR)\n\n if (input) {\n const attrName = 'aria-invalid'\n\n if (actualErrorMessages.length > 0) {\n input.setAttribute(attrName, 'true')\n } else {\n input.removeAttribute(attrName)\n }\n }\n }, [actualErrorMessages.length, autoBindErrorInput])\n\n // HINT: Fieldset内の可視ラベルが無いinputに、legend文言をアクセシブルネームに追加する\n // https://waic.jp/translations/WCAG21/Understanding/label-in-name.html\n useEffect(() => {\n if (!isFieldset || !inputWrapperRef.current) return\n\n const inputs =\n inputWrapperRef.current.querySelectorAll<HTMLInputElement>(SMARTHR_UI_INPUT_SELECTOR)\n\n if (!inputs.length) return\n\n const legendText = innerText(label.text)\n\n if (!legendText) return\n\n inputs.forEach((input: HTMLInputElement) => {\n const accessibleName =\n input.getAttribute('aria-label') ||\n (input.labels?.[0]?.classList.contains('smarthr-ui-VisuallyHiddenText')\n ? input.labels![0].textContent\n : '')\n\n if (\n accessibleName &&\n !accessibleName.includes(legendText) &&\n !legendText.includes(accessibleName)\n ) {\n input.setAttribute('aria-label', `${accessibleName} ${legendText}`)\n }\n })\n }, [isFieldset, label.text])\n\n return (\n <Stack\n {...rest}\n as={as}\n gap={innerMargin ?? 0.5}\n aria-describedby={isFieldset && describedbyIds ? describedbyIds : undefined}\n className={classNames.wrapper}\n >\n <LabelCluster\n isFieldset={isFieldset}\n managedHtmlFor={managedHtmlFor}\n managedLabelId={managedLabelId}\n unrecommendedHideLabel={label.unrecommendedHide}\n labelType={label.styleType}\n label={label.text}\n labelIcon={label.icon}\n statusLabels={actualStatusLabels}\n subActionArea={subActionArea}\n labelClassName={classNames.label}\n />\n <HelpMessageParagraph helpMessage={helpMessage} managedHtmlFor={managedHtmlFor} />\n <ExampleMessageText exampleMessage={exampleMessage} managedHtmlFor={managedHtmlFor} />\n <ErrorMessageList\n errorMessages={actualErrorMessages}\n managedHtmlFor={managedHtmlFor}\n classNames={classNames}\n />\n <div className={classNames.childrenWrapper} ref={inputWrapperRef}>\n {children}\n </div>\n <SupplementaryMessageText\n supplementaryMessage={supplementaryMessage}\n managedHtmlFor={managedHtmlFor}\n />\n </Stack>\n )\n}\n\nconst LabelCluster = memo<\n Pick<Props, 'subActionArea'> & {\n label: ReactNode\n labelType: TextProps['styleType']\n labelIcon?: IconType\n unrecommendedHideLabel?: boolean\n isFieldset: boolean\n managedHtmlFor: string\n managedLabelId: string\n labelClassName: string\n statusLabels: StatusLabelType[]\n }\n>(\n ({\n isFieldset,\n managedHtmlFor,\n managedLabelId,\n unrecommendedHideLabel,\n labelType = 'blockTitle',\n label,\n labelIcon,\n subActionArea,\n labelClassName,\n statusLabels,\n }) => {\n const body = (\n <>\n <Text styleType={labelType} icon={labelIcon}>\n {label}\n </Text>\n <StatusLabelCluster statusLabels={statusLabels} />\n </>\n )\n\n const attrs = useMemo(() => {\n if (unrecommendedHideLabel) {\n return {\n label: null,\n visuallyHidden: isFieldset\n ? {\n as: 'legend',\n }\n : {\n as: 'label',\n htmlFor: managedHtmlFor,\n id: managedLabelId,\n },\n }\n }\n\n if (isFieldset) {\n return {\n label: { 'aria-hidden': 'true' } as const,\n visuallyHidden: { as: 'legend' },\n }\n }\n\n return {\n label: {\n as: 'label' as const,\n htmlFor: managedHtmlFor,\n id: managedLabelId,\n },\n visuallyHidden: null,\n }\n }, [managedLabelId, managedHtmlFor, unrecommendedHideLabel, isFieldset])\n\n return (\n <>\n {attrs.visuallyHidden && (\n <VisuallyHiddenText {...attrs.visuallyHidden}>\n {\n // HINT: innerTextでは正しく文字が取得できない場合がある\n // 安全策としてinnerTextが空を取得してきたらbody自体を埋めこみます\n innerText(body) || body\n }\n </VisuallyHiddenText>\n )}\n {attrs.label && (\n <Cluster justify=\"space-between\">\n <Cluster {...attrs.label} align=\"center\" className={labelClassName}>\n {body}\n </Cluster>\n {subActionArea && <div className=\"shr-grow\">{subActionArea}</div>}\n </Cluster>\n )}\n </>\n )\n },\n)\n\nconst StatusLabelCluster = memo<{ statusLabels: StatusLabelType[] }>(({ statusLabels }) =>\n statusLabels.length === 0 ? null : (\n <Cluster gap={0.25} as=\"span\">\n {statusLabels}\n </Cluster>\n ),\n)\n\nconst HelpMessageParagraph = memo<Pick<Props, 'helpMessage'> & { managedHtmlFor: string }>(\n ({ helpMessage, managedHtmlFor }) =>\n helpMessage ? (\n <p className=\"smarthr-ui-FormControl-helpMessage\" id={`${managedHtmlFor}_helpMessage`}>\n {helpMessage}\n </p>\n ) : null,\n)\n\nconst ExampleMessageText = memo<Pick<Props, 'exampleMessage'> & { managedHtmlFor: string }>(\n ({ exampleMessage, managedHtmlFor }) =>\n exampleMessage ? (\n <Text\n as=\"p\"\n color=\"TEXT_GREY\"\n italic\n id={`${managedHtmlFor}_exampleMessage`}\n className=\"smarthr-ui-FormControl-exampleMessage\"\n >\n {exampleMessage}\n </Text>\n ) : null,\n)\n\nconst ErrorMessageList = memo<{\n errorMessages: ReactNode[]\n managedHtmlFor: string\n classNames: {\n errorList: string\n errorIcon: string\n errorMessage: string\n }\n}>(({ errorMessages, managedHtmlFor, classNames }) =>\n errorMessages.length > 0 ? (\n <div id={`${managedHtmlFor}_errorMessages`} className={classNames.errorList} role=\"alert\">\n {errorMessages.map((message, index) => (\n <p key={index}>\n <Text\n className={classNames.errorMessage}\n icon={<FaCircleExclamationIcon className={classNames.errorIcon} />}\n >\n {message}\n </Text>\n </p>\n ))}\n </div>\n ) : null,\n)\n\nconst SupplementaryMessageText = memo<\n Pick<Props, 'supplementaryMessage'> & { managedHtmlFor: string }\n>(({ supplementaryMessage, managedHtmlFor }) =>\n supplementaryMessage ? (\n <Text\n as=\"p\"\n size=\"S\"\n color=\"TEXT_GREY\"\n id={`${managedHtmlFor}_supplementaryMessage`}\n className=\"smarthr-ui-FormControl-supplementaryMessage\"\n >\n {supplementaryMessage}\n </Text>\n ) : null,\n)\n\nexport const FormControl: FC<Omit<Props, 'as' | 'disabled'>> = ActualFormControl\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;AAuEA;AAEA;AACE;AACE;;;;;;;;;AASC;;;AAGD;;AAEA;AACD;AACD;AACE;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAC2B;AAC7B;AACE;AACA;AACD;AACF;AACD;;;;;AAKE;AACE;AACA;AACA;AACE;AACD;AACF;AACF;AACF;AAED;AAEO;;AAmBL;AACA;;;AAGA;AACA;AACA;AAEA;;;AAII;;;AAGA;;;AAGA;;;AAGA;;AAGF;AACF;AAEA;AAKA;;AAEI;;AAGF;AACF;AAEA;;;;AAKI;;;AAGA;AACA;AACA;AACA;;AAEJ;;AAGE;;;AAIE;;;;;;;;;;;;AAgBA;;;;;;;;;;;;;;;AAmBF;;;;;;;;AAYE;;AAEJ;;;;;;;;AAYI;AACE;;;AAEA;;;;;;;AAQJ;;;;;;AASA;;AAEA;AACE;AAEE;;;AAIF;AAEE;AACA;;;AAIJ;;AAGF;AAoCF;AAEA;;AAkCI;;;AAGM;AACA;AACE;AACI;AACD;AACH;AACI;AACA;AACA;AACD;;;;;AAML;AACA;;;;AAKF;AACE;AACA;AACA;AACD;AACD;;;AAIJ;;;;AAqBF;AAGF;AAQA;AASA;AAeA;AAyBA;AAgBO;;;"}
@@ -33,7 +33,6 @@ const classNameGenerator = ce({
33
33
  errorList: ['shr-list-none'],
34
34
  errorIcon: ['smarthr-ui-FormControl-errorMessage-Icon', 'shr-text-danger'],
35
35
  errorMessage: ['smarthr-ui-FormControl-errorMessage'],
36
- underLabelStack: ['[&&&]:shr-mt-0'],
37
36
  childrenWrapper: [],
38
37
  },
39
38
  variants: {
@@ -74,13 +73,6 @@ const classNameGenerator = ce({
74
73
  {
75
74
  innerMargin: undefined,
76
75
  isFieldset: true,
77
- class: {
78
- childrenWrapper: '[:not([hidden])_~_&&&]:shr-mt-1',
79
- },
80
- },
81
- {
82
- innerMargin: undefined,
83
- isFieldset: false,
84
76
  class: {
85
77
  childrenWrapper: '[:not([hidden])_~_&&&]:shr-mt-0.5',
86
78
  },
@@ -120,7 +112,6 @@ const ActualFormControl = ({ label: orgLabel, subActionArea, innerMargin, status
120
112
  }
121
113
  return Array.isArray(errorMessages) ? errorMessages : [errorMessages];
122
114
  }, [errorMessages]);
123
- const actualInnerMargin = useMemo(() => innerMargin ?? 0.5, [innerMargin]);
124
115
  const classNames = useMemo(() => {
125
116
  const generators = classNameGenerator({ innerMargin, isFieldset });
126
117
  return {
@@ -131,7 +122,6 @@ const ActualFormControl = ({ label: orgLabel, subActionArea, innerMargin, status
131
122
  errorList: generators.errorList(),
132
123
  errorIcon: generators.errorIcon(),
133
124
  errorMessage: generators.errorMessage(),
134
- underLabelStack: generators.underLabelStack(),
135
125
  childrenWrapper: generators.childrenWrapper(),
136
126
  };
137
127
  }, [innerMargin, isFieldset, label.unrecommendedHide, className]);
@@ -215,14 +205,7 @@ const ActualFormControl = ({ label: orgLabel, subActionArea, innerMargin, status
215
205
  }
216
206
  });
217
207
  }, [isFieldset, label.text]);
218
- let body = (jsxs(Fragment, { children: [jsx(HelpMessageParagraph, { helpMessage: helpMessage, managedHtmlFor: managedHtmlFor }), jsx(ExampleMessageText, { exampleMessage: exampleMessage, managedHtmlFor: managedHtmlFor }), jsx(ErrorMessageList, { errorMessages: actualErrorMessages, managedHtmlFor: managedHtmlFor, classNames: classNames }), jsx("div", { className: classNames.childrenWrapper, ref: inputWrapperRef, children: children }), jsx(SupplementaryMessageText, { supplementaryMessage: supplementaryMessage, managedHtmlFor: managedHtmlFor })] }));
219
- // HINT: label.unrecommendedHideの場合、body以下の余白の計算を簡略化するため
220
- // Stackをネストし、そのStackに対してmargin-top: 0を指定する
221
- // こうすることでinner Stack以下の要素は擬似的にStackの最初の要素になる
222
- if (label.unrecommendedHide) {
223
- body = (jsx(Stack, { gap: actualInnerMargin, className: classNames.underLabelStack, children: body }));
224
- }
225
- return (jsxs(Stack, { ...rest, as: as, gap: actualInnerMargin, "aria-describedby": isFieldset && describedbyIds ? describedbyIds : undefined, className: classNames.wrapper, children: [jsx(LabelCluster, { isFieldset: isFieldset, managedHtmlFor: managedHtmlFor, managedLabelId: managedLabelId, unrecommendedHideLabel: label.unrecommendedHide, labelType: label.styleType, label: label.text, labelIcon: label.icon, statusLabels: actualStatusLabels, subActionArea: subActionArea, labelClassName: classNames.label }), body] }));
208
+ return (jsxs(Stack, { ...rest, as: as, gap: innerMargin ?? 0.5, "aria-describedby": isFieldset && describedbyIds ? describedbyIds : undefined, className: classNames.wrapper, children: [jsx(LabelCluster, { isFieldset: isFieldset, managedHtmlFor: managedHtmlFor, managedLabelId: managedLabelId, unrecommendedHideLabel: label.unrecommendedHide, labelType: label.styleType, label: label.text, labelIcon: label.icon, statusLabels: actualStatusLabels, subActionArea: subActionArea, labelClassName: classNames.label }), jsx(HelpMessageParagraph, { helpMessage: helpMessage, managedHtmlFor: managedHtmlFor }), jsx(ExampleMessageText, { exampleMessage: exampleMessage, managedHtmlFor: managedHtmlFor }), jsx(ErrorMessageList, { errorMessages: actualErrorMessages, managedHtmlFor: managedHtmlFor, classNames: classNames }), jsx("div", { className: classNames.childrenWrapper, ref: inputWrapperRef, children: children }), jsx(SupplementaryMessageText, { supplementaryMessage: supplementaryMessage, managedHtmlFor: managedHtmlFor })] }));
226
209
  };
227
210
  const LabelCluster = memo(({ isFieldset, managedHtmlFor, managedLabelId, unrecommendedHideLabel, labelType = 'blockTitle', label, labelIcon, subActionArea, labelClassName, statusLabels, }) => {
228
211
  const body = (jsxs(Fragment, { children: [jsx(Text, { styleType: labelType, icon: labelIcon, children: label }), jsx(StatusLabelCluster, { statusLabels: statusLabels })] }));
@@ -259,10 +242,7 @@ const LabelCluster = memo(({ isFieldset, managedHtmlFor, managedLabelId, unrecom
259
242
  return (jsxs(Fragment, { children: [attrs.visuallyHidden && (jsx(VisuallyHiddenText, { ...attrs.visuallyHidden, children:
260
243
  // HINT: innerTextでは正しく文字が取得できない場合がある
261
244
  // 安全策としてinnerTextが空を取得してきたらbody自体を埋めこみます
262
- innerText(body) || body })), attrs.label && (jsxs(Cluster, { justify: "space-between",
263
- // HINT: UI上、常にトップの要素になるため、Stackの計算が狂わないよう、
264
- // 常にmargin-topを0にする
265
- className: "[&&&]:shr--mt-0", children: [jsx(Cluster, { ...attrs.label, align: "center", className: labelClassName, children: body }), subActionArea && jsx("div", { className: "shr-grow", children: subActionArea })] }))] }));
245
+ innerText(body) || body })), attrs.label && (jsxs(Cluster, { justify: "space-between", children: [jsx(Cluster, { ...attrs.label, align: "center", className: labelClassName, children: body }), subActionArea && jsx("div", { className: "shr-grow", children: subActionArea })] }))] }));
266
246
  });
267
247
  const StatusLabelCluster = memo(({ statusLabels }) => statusLabels.length === 0 ? null : (jsx(Cluster, { gap: 0.25, as: "span", children: statusLabels })));
268
248
  const HelpMessageParagraph = memo(({ helpMessage, managedHtmlFor }) => helpMessage ? (jsx("p", { className: "smarthr-ui-FormControl-helpMessage", id: `${managedHtmlFor}_helpMessage`, children: helpMessage })) : null);
@@ -1 +1 @@
1
- {"version":3,"file":"FormControl.js","sources":["../../../src/components/FormControl/FormControl.tsx"],"sourcesContent":["'use client'\n\nimport {\n type ComponentProps,\n type ComponentPropsWithoutRef,\n type ComponentType,\n type FC,\n type FunctionComponentElement,\n type PropsWithChildren,\n type ReactNode,\n memo,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react'\nimport { useId } from 'react'\nimport innerText from 'react-innertext'\nimport { tv } from 'tailwind-variants'\n\nimport { useObjectAttributes } from '../../hooks/useObjectAttributes'\nimport { FaCircleExclamationIcon } from '../Icon'\nimport { Cluster, Stack } from '../Layout'\nimport { Text, type TextProps } from '../Text'\nimport { VisuallyHiddenText, visuallyHiddenTextClassName } from '../VisuallyHiddenText'\n\nimport type { Gap } from '../../types'\nimport type { StatusLabel } from '../StatusLabel'\n\ntype StatusLabelType = FunctionComponentElement<ComponentProps<typeof StatusLabel>>\ntype IconType = ComponentProps<typeof Text>['icon']\n\ntype ObjectLabelType = {\n text: ReactNode\n /** ラベルの表示タイプ */\n styleType?: TextProps['styleType']\n /** ラベル左に設置するアイコン */\n icon?: IconType\n /** ラベルを視覚的に隠すかどうか */\n unrecommendedHide?: boolean\n /** ラベルを紐づける入力要素のID属性と同じ値 */\n htmlFor?: string\n /** ラベルに適用する `id` 値 */\n id?: string\n}\ntype AbstractProps = PropsWithChildren<{\n /** グループのラベル名 */\n label: ReactNode | ObjectLabelType\n /** タイトル右の領域 */\n subActionArea?: ReactNode\n /** タイトル群と子要素の間の間隔調整用(基本的には不要) */\n innerMargin?: Gap\n /** タイトルの隣に表示する `StatusLabel` の配列 */\n statusLabels?: StatusLabelType | StatusLabelType[]\n /** タイトルの下に表示するヘルプメッセージ */\n helpMessage?: ReactNode\n /** タイトルの下に表示する入力例 */\n exampleMessage?: ReactNode\n /** タイトルの下に表示するエラーメッセージ */\n errorMessages?: ReactNode | ReactNode[]\n /** エラーがある場合に自動的に入力要素を error にするかどうか */\n autoBindErrorInput?: boolean\n /** フォームコントロールの下に表示する補足メッセージ */\n supplementaryMessage?: ReactNode\n /** `true` のとき、文字色を `TEXT_DISABLED` にする */\n disabled?: boolean\n as?: string | ComponentType<any>\n}>\ntype Props = AbstractProps &\n Omit<ComponentPropsWithoutRef<'div'>, keyof AbstractProps | 'aria-labelledby'>\n\nconst labelObjectConverter = (label: ReactNode) => ({ text: label })\n\nconst classNameGenerator = tv({\n slots: {\n wrapper: [\n 'smarthr-ui-FormControl',\n 'shr-mx-[unset] shr-border-none shr-p-[unset]',\n 'disabled:shr-text-disabled',\n '[&:disabled_.smarthr-ui-FormControl-label_>_span]:shr-text-disabled',\n '[&:disabled_.smarthr-ui-FormControl-exampleMessage]:shr-text-color-inherit',\n '[&:disabled_.smarthr-ui-FormControl-errorMessage-Icon]:shr-text-color-inherit',\n '[&:disabled_.smarthr-ui-FormControl-supplementaryMessage]:shr-text-color-inherit',\n '[&:disabled_.smarthr-ui-Input]:shr-border-default/50 [&:disabled_.smarthr-ui-Input]:shr-bg-white-darken',\n ],\n label: ['smarthr-ui-FormControl-label'],\n errorList: ['shr-list-none'],\n errorIcon: ['smarthr-ui-FormControl-errorMessage-Icon', 'shr-text-danger'],\n errorMessage: ['smarthr-ui-FormControl-errorMessage'],\n underLabelStack: ['[&&&]:shr-mt-0'],\n childrenWrapper: [],\n },\n variants: {\n innerMargin: {\n 0: {},\n 0.25: {},\n 0.5: {},\n 0.75: {},\n 1: {},\n 1.25: {},\n 1.5: {},\n 2: {},\n 2.5: {},\n 3: {},\n 3.5: {},\n 4: {},\n 8: {},\n X3S: {},\n XXS: {},\n XS: {},\n S: {},\n M: {},\n L: {},\n XL: {},\n XXL: {},\n X3L: {},\n } as { [key in Gap]: string },\n isFieldset: {\n true: {},\n false: {},\n },\n },\n compoundVariants: [\n // TODO: innerMarginが未指定、初期値の場合、かつFieldsetの場合、childrenの上部の余白を広げることで\n // FormControltとの差をわかりやすくしている\n // 微妙な方法ではあるので、必要に応じてinnerMarginではない属性を用意する\n // https://kufuinc.slack.com/archives/CGC58MW01/p1737944965871159?thread_ts=1737541173.404369&cid=CGC58MW01\n {\n innerMargin: undefined,\n isFieldset: true,\n class: {\n childrenWrapper: '[:not([hidden])_~_&&&]:shr-mt-1',\n },\n },\n {\n innerMargin: undefined,\n isFieldset: false,\n class: {\n childrenWrapper: '[:not([hidden])_~_&&&]:shr-mt-0.5',\n },\n },\n ],\n})\n\nconst SMARTHR_UI_INPUT_SELECTOR = '[data-smarthr-ui-input=\"true\"]'\n\nexport const ActualFormControl: FC<Props> = ({\n label: orgLabel,\n subActionArea,\n innerMargin,\n statusLabels,\n helpMessage,\n exampleMessage,\n errorMessages,\n autoBindErrorInput = true,\n supplementaryMessage,\n as = 'div',\n className,\n children,\n ...rest\n}) => {\n const label = useObjectAttributes<ReactNode | ObjectLabelType, ObjectLabelType>(\n orgLabel,\n labelObjectConverter,\n )\n const defaultHtmlFor = useId()\n const defaultLabelId = useId()\n const [childInputId, setChildInputId] = useState<string>('')\n const managedHtmlFor = label.htmlFor || childInputId || defaultHtmlFor\n const managedLabelId = label.id || defaultLabelId\n const inputWrapperRef = useRef<HTMLDivElement>(null)\n const isFieldset = as === 'fieldset'\n\n const describedbyIds = useMemo(() => {\n const temp = []\n\n if (helpMessage) {\n temp.push(`${managedHtmlFor}_helpMessage`)\n }\n if (exampleMessage) {\n temp.push(`${managedHtmlFor}_exampleMessage`)\n }\n if (supplementaryMessage) {\n temp.push(`${managedHtmlFor}_supplementaryMessage`)\n }\n if (errorMessages) {\n temp.push(`${managedHtmlFor}_errorMessages`)\n }\n\n return temp.join(' ')\n }, [helpMessage, exampleMessage, supplementaryMessage, errorMessages, managedHtmlFor])\n\n const actualStatusLabels = useMemo(\n () => (statusLabels ? (Array.isArray(statusLabels) ? statusLabels : [statusLabels]) : []),\n [statusLabels],\n )\n\n const actualErrorMessages = useMemo(() => {\n if (!errorMessages) {\n return []\n }\n\n return Array.isArray(errorMessages) ? errorMessages : [errorMessages]\n }, [errorMessages])\n\n const actualInnerMargin = useMemo(() => innerMargin ?? 0.5, [innerMargin])\n\n const classNames = useMemo(() => {\n const generators = classNameGenerator({ innerMargin, isFieldset })\n\n return {\n wrapper: generators.wrapper({ className }),\n label: generators.label({\n className: label.unrecommendedHide ? visuallyHiddenTextClassName : '',\n }),\n errorList: generators.errorList(),\n errorIcon: generators.errorIcon(),\n errorMessage: generators.errorMessage(),\n underLabelStack: generators.underLabelStack(),\n childrenWrapper: generators.childrenWrapper(),\n }\n }, [innerMargin, isFieldset, label.unrecommendedHide, className])\n\n useEffect(() => {\n if (\n isFieldset ||\n !inputWrapperRef?.current ||\n // HINT: 対象idを持つ要素が既に存在する場合、何もしない\n document.getElementById(managedHtmlFor)\n ) {\n return\n }\n\n const input = inputWrapperRef.current.querySelector(SMARTHR_UI_INPUT_SELECTOR)\n\n if (!input) {\n return\n }\n\n const inputId = input.getAttribute('id')\n\n if (inputId) {\n setChildInputId(inputId)\n } else {\n input.setAttribute('id', managedHtmlFor)\n }\n\n if (input instanceof HTMLInputElement && input.type === 'file') {\n const attrName = 'aria-labelledby'\n const inputLabelledByIds = input.getAttribute(attrName)\n\n if (inputLabelledByIds) {\n // InputFileの場合はlabel要素の可視ラベルをアクセシブルネームに含める\n input.setAttribute(attrName, `${inputLabelledByIds} ${managedLabelId}`)\n }\n }\n }, [managedHtmlFor, isFieldset, managedLabelId])\n\n useEffect(() => {\n if (!describedbyIds || !inputWrapperRef?.current) {\n return\n }\n\n const inputWrapper = inputWrapperRef.current\n const attrName = 'aria-describedby'\n\n if (inputWrapper.querySelector(`[${attrName}=\"${describedbyIds}\"]`)) {\n return\n }\n\n const input = inputWrapper.querySelector(SMARTHR_UI_INPUT_SELECTOR)\n\n if (input) {\n const attribute = input.getAttribute(attrName)\n\n input.setAttribute(attrName, attribute ? `${attribute} ${describedbyIds}` : describedbyIds)\n }\n }, [describedbyIds])\n\n useEffect(() => {\n if (!autoBindErrorInput || !inputWrapperRef?.current) {\n return\n }\n\n const input = inputWrapperRef.current.querySelector(SMARTHR_UI_INPUT_SELECTOR)\n\n if (input) {\n const attrName = 'aria-invalid'\n\n if (actualErrorMessages.length > 0) {\n input.setAttribute(attrName, 'true')\n } else {\n input.removeAttribute(attrName)\n }\n }\n }, [actualErrorMessages.length, autoBindErrorInput])\n\n // HINT: Fieldset内の可視ラベルが無いinputに、legend文言をアクセシブルネームに追加する\n // https://waic.jp/translations/WCAG21/Understanding/label-in-name.html\n useEffect(() => {\n if (!isFieldset || !inputWrapperRef.current) return\n\n const inputs =\n inputWrapperRef.current.querySelectorAll<HTMLInputElement>(SMARTHR_UI_INPUT_SELECTOR)\n\n if (!inputs.length) return\n\n const legendText = innerText(label.text)\n\n if (!legendText) return\n\n inputs.forEach((input: HTMLInputElement) => {\n const accessibleName =\n input.getAttribute('aria-label') ||\n (input.labels?.[0]?.classList.contains('smarthr-ui-VisuallyHiddenText')\n ? input.labels![0].textContent\n : '')\n\n if (\n accessibleName &&\n !accessibleName.includes(legendText) &&\n !legendText.includes(accessibleName)\n ) {\n input.setAttribute('aria-label', `${accessibleName} ${legendText}`)\n }\n })\n }, [isFieldset, label.text])\n\n let body = (\n <>\n <HelpMessageParagraph helpMessage={helpMessage} managedHtmlFor={managedHtmlFor} />\n <ExampleMessageText exampleMessage={exampleMessage} managedHtmlFor={managedHtmlFor} />\n <ErrorMessageList\n errorMessages={actualErrorMessages}\n managedHtmlFor={managedHtmlFor}\n classNames={classNames}\n />\n <div className={classNames.childrenWrapper} ref={inputWrapperRef}>\n {children}\n </div>\n <SupplementaryMessageText\n supplementaryMessage={supplementaryMessage}\n managedHtmlFor={managedHtmlFor}\n />\n </>\n )\n\n // HINT: label.unrecommendedHideの場合、body以下の余白の計算を簡略化するため\n // Stackをネストし、そのStackに対してmargin-top: 0を指定する\n // こうすることでinner Stack以下の要素は擬似的にStackの最初の要素になる\n if (label.unrecommendedHide) {\n body = (\n <Stack gap={actualInnerMargin} className={classNames.underLabelStack}>\n {body}\n </Stack>\n )\n }\n\n return (\n <Stack\n {...rest}\n as={as}\n gap={actualInnerMargin}\n aria-describedby={isFieldset && describedbyIds ? describedbyIds : undefined}\n className={classNames.wrapper}\n >\n <LabelCluster\n isFieldset={isFieldset}\n managedHtmlFor={managedHtmlFor}\n managedLabelId={managedLabelId}\n unrecommendedHideLabel={label.unrecommendedHide}\n labelType={label.styleType}\n label={label.text}\n labelIcon={label.icon}\n statusLabels={actualStatusLabels}\n subActionArea={subActionArea}\n labelClassName={classNames.label}\n />\n {body}\n </Stack>\n )\n}\n\nconst LabelCluster = memo<\n Pick<Props, 'subActionArea'> & {\n label: ReactNode\n labelType: TextProps['styleType']\n labelIcon?: IconType\n unrecommendedHideLabel?: boolean\n isFieldset: boolean\n managedHtmlFor: string\n managedLabelId: string\n labelClassName: string\n statusLabels: StatusLabelType[]\n }\n>(\n ({\n isFieldset,\n managedHtmlFor,\n managedLabelId,\n unrecommendedHideLabel,\n labelType = 'blockTitle',\n label,\n labelIcon,\n subActionArea,\n labelClassName,\n statusLabels,\n }) => {\n const body = (\n <>\n <Text styleType={labelType} icon={labelIcon}>\n {label}\n </Text>\n <StatusLabelCluster statusLabels={statusLabels} />\n </>\n )\n\n const attrs = useMemo(() => {\n if (unrecommendedHideLabel) {\n return {\n label: null,\n visuallyHidden: isFieldset\n ? {\n as: 'legend',\n }\n : {\n as: 'label',\n htmlFor: managedHtmlFor,\n id: managedLabelId,\n },\n }\n }\n\n if (isFieldset) {\n return {\n label: { 'aria-hidden': 'true' } as const,\n visuallyHidden: { as: 'legend' },\n }\n }\n\n return {\n label: {\n as: 'label' as const,\n htmlFor: managedHtmlFor,\n id: managedLabelId,\n },\n visuallyHidden: null,\n }\n }, [managedLabelId, managedHtmlFor, unrecommendedHideLabel, isFieldset])\n\n return (\n <>\n {attrs.visuallyHidden && (\n <VisuallyHiddenText {...attrs.visuallyHidden}>\n {\n // HINT: innerTextでは正しく文字が取得できない場合がある\n // 安全策としてinnerTextが空を取得してきたらbody自体を埋めこみます\n innerText(body) || body\n }\n </VisuallyHiddenText>\n )}\n {attrs.label && (\n <Cluster\n justify=\"space-between\"\n // HINT: UI上、常にトップの要素になるため、Stackの計算が狂わないよう、\n // 常にmargin-topを0にする\n className=\"[&&&]:shr--mt-0\"\n >\n <Cluster {...attrs.label} align=\"center\" className={labelClassName}>\n {body}\n </Cluster>\n {subActionArea && <div className=\"shr-grow\">{subActionArea}</div>}\n </Cluster>\n )}\n </>\n )\n },\n)\n\nconst StatusLabelCluster = memo<{ statusLabels: StatusLabelType[] }>(({ statusLabels }) =>\n statusLabels.length === 0 ? null : (\n <Cluster gap={0.25} as=\"span\">\n {statusLabels}\n </Cluster>\n ),\n)\n\nconst HelpMessageParagraph = memo<Pick<Props, 'helpMessage'> & { managedHtmlFor: string }>(\n ({ helpMessage, managedHtmlFor }) =>\n helpMessage ? (\n <p className=\"smarthr-ui-FormControl-helpMessage\" id={`${managedHtmlFor}_helpMessage`}>\n {helpMessage}\n </p>\n ) : null,\n)\n\nconst ExampleMessageText = memo<Pick<Props, 'exampleMessage'> & { managedHtmlFor: string }>(\n ({ exampleMessage, managedHtmlFor }) =>\n exampleMessage ? (\n <Text\n as=\"p\"\n color=\"TEXT_GREY\"\n italic\n id={`${managedHtmlFor}_exampleMessage`}\n className=\"smarthr-ui-FormControl-exampleMessage\"\n >\n {exampleMessage}\n </Text>\n ) : null,\n)\n\nconst ErrorMessageList = memo<{\n errorMessages: ReactNode[]\n managedHtmlFor: string\n classNames: {\n errorList: string\n errorIcon: string\n errorMessage: string\n }\n}>(({ errorMessages, managedHtmlFor, classNames }) =>\n errorMessages.length > 0 ? (\n <div id={`${managedHtmlFor}_errorMessages`} className={classNames.errorList} role=\"alert\">\n {errorMessages.map((message, index) => (\n <p key={index}>\n <Text\n className={classNames.errorMessage}\n icon={<FaCircleExclamationIcon className={classNames.errorIcon} />}\n >\n {message}\n </Text>\n </p>\n ))}\n </div>\n ) : null,\n)\n\nconst SupplementaryMessageText = memo<\n Pick<Props, 'supplementaryMessage'> & { managedHtmlFor: string }\n>(({ supplementaryMessage, managedHtmlFor }) =>\n supplementaryMessage ? (\n <Text\n as=\"p\"\n size=\"S\"\n color=\"TEXT_GREY\"\n id={`${managedHtmlFor}_supplementaryMessage`}\n className=\"smarthr-ui-FormControl-supplementaryMessage\"\n >\n {supplementaryMessage}\n </Text>\n ) : null,\n)\n\nexport const FormControl: FC<Omit<Props, 'as' | 'disabled'>> = ActualFormControl\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AAuEA;AAEA;AACE;AACE;;;;;;;;;AASC;;;AAGD;;;AAGA;AACD;AACD;AACE;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAC2B;AAC7B;AACE;AACA;AACD;AACF;AACD;;;;;AAKE;AACE;AACA;AACA;AACE;AACD;AACF;AACD;AACE;AACA;AACA;AACE;AACD;AACF;AACF;AACF;AAED;AAEO;;AAmBL;AACA;;;AAGA;AACA;AACA;AAEA;;;AAII;;;AAGA;;;AAGA;;;AAGA;;AAGF;AACF;AAEA;AAKA;;AAEI;;AAGF;AACF;AAEA;AAEA;;;;AAKI;;;AAGA;AACA;AACA;AACA;AACA;;AAEJ;;AAGE;;;AAIE;;;;;;;;;;;;AAgBA;;;;;;;;;;;;;;;AAmBF;;;;;;;;AAYE;;AAEJ;;;;;;;;AAYI;AACE;;;AAEA;;;;;;;AAQJ;;;;;;AASA;;AAEA;AACE;AAEE;;;AAIF;AAEE;AACA;;;AAIJ;;AAGF;;;;AAsBA;AACE;;AAOF;AAuBF;AAEA;;AAkCI;;;AAGM;AACA;AACE;AACI;AACD;AACH;AACI;AACA;AACA;AACD;;;;;AAML;AACA;;;;AAKF;AACE;AACA;AACA;AACD;AACD;;;AAIJ;;;AAOU;;;AASF;AAUV;AAGF;AAQA;AASA;AAeA;AAyBA;AAgBO;;"}
1
+ {"version":3,"file":"FormControl.js","sources":["../../../src/components/FormControl/FormControl.tsx"],"sourcesContent":["'use client'\n\nimport {\n type ComponentProps,\n type ComponentPropsWithoutRef,\n type ComponentType,\n type FC,\n type FunctionComponentElement,\n type PropsWithChildren,\n type ReactNode,\n memo,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react'\nimport { useId } from 'react'\nimport innerText from 'react-innertext'\nimport { tv } from 'tailwind-variants'\n\nimport { useObjectAttributes } from '../../hooks/useObjectAttributes'\nimport { FaCircleExclamationIcon } from '../Icon'\nimport { Cluster, Stack } from '../Layout'\nimport { Text, type TextProps } from '../Text'\nimport { VisuallyHiddenText, visuallyHiddenTextClassName } from '../VisuallyHiddenText'\n\nimport type { Gap } from '../../types'\nimport type { StatusLabel } from '../StatusLabel'\n\ntype StatusLabelType = FunctionComponentElement<ComponentProps<typeof StatusLabel>>\ntype IconType = ComponentProps<typeof Text>['icon']\n\ntype ObjectLabelType = {\n text: ReactNode\n /** ラベルの表示タイプ */\n styleType?: TextProps['styleType']\n /** ラベル左に設置するアイコン */\n icon?: IconType\n /** ラベルを視覚的に隠すかどうか */\n unrecommendedHide?: boolean\n /** ラベルを紐づける入力要素のID属性と同じ値 */\n htmlFor?: string\n /** ラベルに適用する `id` 値 */\n id?: string\n}\ntype AbstractProps = PropsWithChildren<{\n /** グループのラベル名 */\n label: ReactNode | ObjectLabelType\n /** タイトル右の領域 */\n subActionArea?: ReactNode\n /** タイトル群と子要素の間の間隔調整用(基本的には不要) */\n innerMargin?: Gap\n /** タイトルの隣に表示する `StatusLabel` の配列 */\n statusLabels?: StatusLabelType | StatusLabelType[]\n /** タイトルの下に表示するヘルプメッセージ */\n helpMessage?: ReactNode\n /** タイトルの下に表示する入力例 */\n exampleMessage?: ReactNode\n /** タイトルの下に表示するエラーメッセージ */\n errorMessages?: ReactNode | ReactNode[]\n /** エラーがある場合に自動的に入力要素を error にするかどうか */\n autoBindErrorInput?: boolean\n /** フォームコントロールの下に表示する補足メッセージ */\n supplementaryMessage?: ReactNode\n /** `true` のとき、文字色を `TEXT_DISABLED` にする */\n disabled?: boolean\n as?: string | ComponentType<any>\n}>\ntype Props = AbstractProps &\n Omit<ComponentPropsWithoutRef<'div'>, keyof AbstractProps | 'aria-labelledby'>\n\nconst labelObjectConverter = (label: ReactNode) => ({ text: label })\n\nconst classNameGenerator = tv({\n slots: {\n wrapper: [\n 'smarthr-ui-FormControl',\n 'shr-mx-[unset] shr-border-none shr-p-[unset]',\n 'disabled:shr-text-disabled',\n '[&:disabled_.smarthr-ui-FormControl-label_>_span]:shr-text-disabled',\n '[&:disabled_.smarthr-ui-FormControl-exampleMessage]:shr-text-color-inherit',\n '[&:disabled_.smarthr-ui-FormControl-errorMessage-Icon]:shr-text-color-inherit',\n '[&:disabled_.smarthr-ui-FormControl-supplementaryMessage]:shr-text-color-inherit',\n '[&:disabled_.smarthr-ui-Input]:shr-border-default/50 [&:disabled_.smarthr-ui-Input]:shr-bg-white-darken',\n ],\n label: ['smarthr-ui-FormControl-label'],\n errorList: ['shr-list-none'],\n errorIcon: ['smarthr-ui-FormControl-errorMessage-Icon', 'shr-text-danger'],\n errorMessage: ['smarthr-ui-FormControl-errorMessage'],\n childrenWrapper: [],\n },\n variants: {\n innerMargin: {\n 0: {},\n 0.25: {},\n 0.5: {},\n 0.75: {},\n 1: {},\n 1.25: {},\n 1.5: {},\n 2: {},\n 2.5: {},\n 3: {},\n 3.5: {},\n 4: {},\n 8: {},\n X3S: {},\n XXS: {},\n XS: {},\n S: {},\n M: {},\n L: {},\n XL: {},\n XXL: {},\n X3L: {},\n } as { [key in Gap]: string },\n isFieldset: {\n true: {},\n false: {},\n },\n },\n compoundVariants: [\n // TODO: innerMarginが未指定、初期値の場合、かつFieldsetの場合、childrenの上部の余白を広げることで\n // FormControltとの差をわかりやすくしている\n // 微妙な方法ではあるので、必要に応じてinnerMarginではない属性を用意する\n // https://kufuinc.slack.com/archives/CGC58MW01/p1737944965871159?thread_ts=1737541173.404369&cid=CGC58MW01\n {\n innerMargin: undefined,\n isFieldset: true,\n class: {\n childrenWrapper: '[:not([hidden])_~_&&&]:shr-mt-0.5',\n },\n },\n ],\n})\n\nconst SMARTHR_UI_INPUT_SELECTOR = '[data-smarthr-ui-input=\"true\"]'\n\nexport const ActualFormControl: FC<Props> = ({\n label: orgLabel,\n subActionArea,\n innerMargin,\n statusLabels,\n helpMessage,\n exampleMessage,\n errorMessages,\n autoBindErrorInput = true,\n supplementaryMessage,\n as = 'div',\n className,\n children,\n ...rest\n}) => {\n const label = useObjectAttributes<ReactNode | ObjectLabelType, ObjectLabelType>(\n orgLabel,\n labelObjectConverter,\n )\n const defaultHtmlFor = useId()\n const defaultLabelId = useId()\n const [childInputId, setChildInputId] = useState<string>('')\n const managedHtmlFor = label.htmlFor || childInputId || defaultHtmlFor\n const managedLabelId = label.id || defaultLabelId\n const inputWrapperRef = useRef<HTMLDivElement>(null)\n const isFieldset = as === 'fieldset'\n\n const describedbyIds = useMemo(() => {\n const temp = []\n\n if (helpMessage) {\n temp.push(`${managedHtmlFor}_helpMessage`)\n }\n if (exampleMessage) {\n temp.push(`${managedHtmlFor}_exampleMessage`)\n }\n if (supplementaryMessage) {\n temp.push(`${managedHtmlFor}_supplementaryMessage`)\n }\n if (errorMessages) {\n temp.push(`${managedHtmlFor}_errorMessages`)\n }\n\n return temp.join(' ')\n }, [helpMessage, exampleMessage, supplementaryMessage, errorMessages, managedHtmlFor])\n\n const actualStatusLabels = useMemo(\n () => (statusLabels ? (Array.isArray(statusLabels) ? statusLabels : [statusLabels]) : []),\n [statusLabels],\n )\n\n const actualErrorMessages = useMemo(() => {\n if (!errorMessages) {\n return []\n }\n\n return Array.isArray(errorMessages) ? errorMessages : [errorMessages]\n }, [errorMessages])\n\n const classNames = useMemo(() => {\n const generators = classNameGenerator({ innerMargin, isFieldset })\n\n return {\n wrapper: generators.wrapper({ className }),\n label: generators.label({\n className: label.unrecommendedHide ? visuallyHiddenTextClassName : '',\n }),\n errorList: generators.errorList(),\n errorIcon: generators.errorIcon(),\n errorMessage: generators.errorMessage(),\n childrenWrapper: generators.childrenWrapper(),\n }\n }, [innerMargin, isFieldset, label.unrecommendedHide, className])\n\n useEffect(() => {\n if (\n isFieldset ||\n !inputWrapperRef?.current ||\n // HINT: 対象idを持つ要素が既に存在する場合、何もしない\n document.getElementById(managedHtmlFor)\n ) {\n return\n }\n\n const input = inputWrapperRef.current.querySelector(SMARTHR_UI_INPUT_SELECTOR)\n\n if (!input) {\n return\n }\n\n const inputId = input.getAttribute('id')\n\n if (inputId) {\n setChildInputId(inputId)\n } else {\n input.setAttribute('id', managedHtmlFor)\n }\n\n if (input instanceof HTMLInputElement && input.type === 'file') {\n const attrName = 'aria-labelledby'\n const inputLabelledByIds = input.getAttribute(attrName)\n\n if (inputLabelledByIds) {\n // InputFileの場合はlabel要素の可視ラベルをアクセシブルネームに含める\n input.setAttribute(attrName, `${inputLabelledByIds} ${managedLabelId}`)\n }\n }\n }, [managedHtmlFor, isFieldset, managedLabelId])\n\n useEffect(() => {\n if (!describedbyIds || !inputWrapperRef?.current) {\n return\n }\n\n const inputWrapper = inputWrapperRef.current\n const attrName = 'aria-describedby'\n\n if (inputWrapper.querySelector(`[${attrName}=\"${describedbyIds}\"]`)) {\n return\n }\n\n const input = inputWrapper.querySelector(SMARTHR_UI_INPUT_SELECTOR)\n\n if (input) {\n const attribute = input.getAttribute(attrName)\n\n input.setAttribute(attrName, attribute ? `${attribute} ${describedbyIds}` : describedbyIds)\n }\n }, [describedbyIds])\n\n useEffect(() => {\n if (!autoBindErrorInput || !inputWrapperRef?.current) {\n return\n }\n\n const input = inputWrapperRef.current.querySelector(SMARTHR_UI_INPUT_SELECTOR)\n\n if (input) {\n const attrName = 'aria-invalid'\n\n if (actualErrorMessages.length > 0) {\n input.setAttribute(attrName, 'true')\n } else {\n input.removeAttribute(attrName)\n }\n }\n }, [actualErrorMessages.length, autoBindErrorInput])\n\n // HINT: Fieldset内の可視ラベルが無いinputに、legend文言をアクセシブルネームに追加する\n // https://waic.jp/translations/WCAG21/Understanding/label-in-name.html\n useEffect(() => {\n if (!isFieldset || !inputWrapperRef.current) return\n\n const inputs =\n inputWrapperRef.current.querySelectorAll<HTMLInputElement>(SMARTHR_UI_INPUT_SELECTOR)\n\n if (!inputs.length) return\n\n const legendText = innerText(label.text)\n\n if (!legendText) return\n\n inputs.forEach((input: HTMLInputElement) => {\n const accessibleName =\n input.getAttribute('aria-label') ||\n (input.labels?.[0]?.classList.contains('smarthr-ui-VisuallyHiddenText')\n ? input.labels![0].textContent\n : '')\n\n if (\n accessibleName &&\n !accessibleName.includes(legendText) &&\n !legendText.includes(accessibleName)\n ) {\n input.setAttribute('aria-label', `${accessibleName} ${legendText}`)\n }\n })\n }, [isFieldset, label.text])\n\n return (\n <Stack\n {...rest}\n as={as}\n gap={innerMargin ?? 0.5}\n aria-describedby={isFieldset && describedbyIds ? describedbyIds : undefined}\n className={classNames.wrapper}\n >\n <LabelCluster\n isFieldset={isFieldset}\n managedHtmlFor={managedHtmlFor}\n managedLabelId={managedLabelId}\n unrecommendedHideLabel={label.unrecommendedHide}\n labelType={label.styleType}\n label={label.text}\n labelIcon={label.icon}\n statusLabels={actualStatusLabels}\n subActionArea={subActionArea}\n labelClassName={classNames.label}\n />\n <HelpMessageParagraph helpMessage={helpMessage} managedHtmlFor={managedHtmlFor} />\n <ExampleMessageText exampleMessage={exampleMessage} managedHtmlFor={managedHtmlFor} />\n <ErrorMessageList\n errorMessages={actualErrorMessages}\n managedHtmlFor={managedHtmlFor}\n classNames={classNames}\n />\n <div className={classNames.childrenWrapper} ref={inputWrapperRef}>\n {children}\n </div>\n <SupplementaryMessageText\n supplementaryMessage={supplementaryMessage}\n managedHtmlFor={managedHtmlFor}\n />\n </Stack>\n )\n}\n\nconst LabelCluster = memo<\n Pick<Props, 'subActionArea'> & {\n label: ReactNode\n labelType: TextProps['styleType']\n labelIcon?: IconType\n unrecommendedHideLabel?: boolean\n isFieldset: boolean\n managedHtmlFor: string\n managedLabelId: string\n labelClassName: string\n statusLabels: StatusLabelType[]\n }\n>(\n ({\n isFieldset,\n managedHtmlFor,\n managedLabelId,\n unrecommendedHideLabel,\n labelType = 'blockTitle',\n label,\n labelIcon,\n subActionArea,\n labelClassName,\n statusLabels,\n }) => {\n const body = (\n <>\n <Text styleType={labelType} icon={labelIcon}>\n {label}\n </Text>\n <StatusLabelCluster statusLabels={statusLabels} />\n </>\n )\n\n const attrs = useMemo(() => {\n if (unrecommendedHideLabel) {\n return {\n label: null,\n visuallyHidden: isFieldset\n ? {\n as: 'legend',\n }\n : {\n as: 'label',\n htmlFor: managedHtmlFor,\n id: managedLabelId,\n },\n }\n }\n\n if (isFieldset) {\n return {\n label: { 'aria-hidden': 'true' } as const,\n visuallyHidden: { as: 'legend' },\n }\n }\n\n return {\n label: {\n as: 'label' as const,\n htmlFor: managedHtmlFor,\n id: managedLabelId,\n },\n visuallyHidden: null,\n }\n }, [managedLabelId, managedHtmlFor, unrecommendedHideLabel, isFieldset])\n\n return (\n <>\n {attrs.visuallyHidden && (\n <VisuallyHiddenText {...attrs.visuallyHidden}>\n {\n // HINT: innerTextでは正しく文字が取得できない場合がある\n // 安全策としてinnerTextが空を取得してきたらbody自体を埋めこみます\n innerText(body) || body\n }\n </VisuallyHiddenText>\n )}\n {attrs.label && (\n <Cluster justify=\"space-between\">\n <Cluster {...attrs.label} align=\"center\" className={labelClassName}>\n {body}\n </Cluster>\n {subActionArea && <div className=\"shr-grow\">{subActionArea}</div>}\n </Cluster>\n )}\n </>\n )\n },\n)\n\nconst StatusLabelCluster = memo<{ statusLabels: StatusLabelType[] }>(({ statusLabels }) =>\n statusLabels.length === 0 ? null : (\n <Cluster gap={0.25} as=\"span\">\n {statusLabels}\n </Cluster>\n ),\n)\n\nconst HelpMessageParagraph = memo<Pick<Props, 'helpMessage'> & { managedHtmlFor: string }>(\n ({ helpMessage, managedHtmlFor }) =>\n helpMessage ? (\n <p className=\"smarthr-ui-FormControl-helpMessage\" id={`${managedHtmlFor}_helpMessage`}>\n {helpMessage}\n </p>\n ) : null,\n)\n\nconst ExampleMessageText = memo<Pick<Props, 'exampleMessage'> & { managedHtmlFor: string }>(\n ({ exampleMessage, managedHtmlFor }) =>\n exampleMessage ? (\n <Text\n as=\"p\"\n color=\"TEXT_GREY\"\n italic\n id={`${managedHtmlFor}_exampleMessage`}\n className=\"smarthr-ui-FormControl-exampleMessage\"\n >\n {exampleMessage}\n </Text>\n ) : null,\n)\n\nconst ErrorMessageList = memo<{\n errorMessages: ReactNode[]\n managedHtmlFor: string\n classNames: {\n errorList: string\n errorIcon: string\n errorMessage: string\n }\n}>(({ errorMessages, managedHtmlFor, classNames }) =>\n errorMessages.length > 0 ? (\n <div id={`${managedHtmlFor}_errorMessages`} className={classNames.errorList} role=\"alert\">\n {errorMessages.map((message, index) => (\n <p key={index}>\n <Text\n className={classNames.errorMessage}\n icon={<FaCircleExclamationIcon className={classNames.errorIcon} />}\n >\n {message}\n </Text>\n </p>\n ))}\n </div>\n ) : null,\n)\n\nconst SupplementaryMessageText = memo<\n Pick<Props, 'supplementaryMessage'> & { managedHtmlFor: string }\n>(({ supplementaryMessage, managedHtmlFor }) =>\n supplementaryMessage ? (\n <Text\n as=\"p\"\n size=\"S\"\n color=\"TEXT_GREY\"\n id={`${managedHtmlFor}_supplementaryMessage`}\n className=\"smarthr-ui-FormControl-supplementaryMessage\"\n >\n {supplementaryMessage}\n </Text>\n ) : null,\n)\n\nexport const FormControl: FC<Omit<Props, 'as' | 'disabled'>> = ActualFormControl\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AAuEA;AAEA;AACE;AACE;;;;;;;;;AASC;;;AAGD;;AAEA;AACD;AACD;AACE;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAC2B;AAC7B;AACE;AACA;AACD;AACF;AACD;;;;;AAKE;AACE;AACA;AACA;AACE;AACD;AACF;AACF;AACF;AAED;AAEO;;AAmBL;AACA;;;AAGA;AACA;AACA;AAEA;;;AAII;;;AAGA;;;AAGA;;;AAGA;;AAGF;AACF;AAEA;AAKA;;AAEI;;AAGF;AACF;AAEA;;;;AAKI;;;AAGA;AACA;AACA;AACA;;AAEJ;;AAGE;;;AAIE;;;;;;;;;;;;AAgBA;;;;;;;;;;;;;;;AAmBF;;;;;;;;AAYE;;AAEJ;;;;;;;;AAYI;AACE;;;AAEA;;;;;;;AAQJ;;;;;;AASA;;AAEA;AACE;AAEE;;;AAIF;AAEE;AACA;;;AAIJ;;AAGF;AAoCF;AAEA;;AAkCI;;;AAGM;AACA;AACE;AACI;AACD;AACH;AACI;AACA;AACA;AACD;;;;;AAML;AACA;;;;AAKF;AACE;AACA;AACA;AACD;AACD;;;AAIJ;;;;AAqBF;AAGF;AAQA;AASA;AAeA;AAyBA;AAgBO;;"}
@@ -6,7 +6,7 @@ var React = require('react');
6
6
  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');
7
7
  require('../../../intl/IntlProvider.cjs');
8
8
  var intl_useIntl = require('../../../intl/useIntl.cjs');
9
- require('react-intl');
9
+ require('../../../intl/Localizer.cjs');
10
10
  require('../../../_virtual/dayjs.min.cjs');
11
11
  require('../../../intl/TimeFormatter.cjs');
12
12
  var components_Button_Button = require('../../Button/Button.cjs');
@@ -4,7 +4,7 @@ import { memo, useMemo } from 'react';
4
4
  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';
5
5
  import '../../../intl/IntlProvider.js';
6
6
  import { useIntl } from '../../../intl/useIntl.js';
7
- import 'react-intl';
7
+ import '../../../intl/Localizer.js';
8
8
  import '../../../_virtual/dayjs.min.js';
9
9
  import '../../../intl/TimeFormatter.js';
10
10
  import { Button } from '../../Button/Button.js';