smarthr-ui 99.3.0 → 99.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/_virtual/index.cjs +2 -2
- package/lib/_virtual/index.js +2 -2
- package/lib/_virtual/index2.cjs +2 -2
- package/lib/_virtual/index2.js +2 -2
- package/lib/components/Checkbox/Checkbox.cjs +1 -1
- package/lib/components/Checkbox/Checkbox.cjs.map +1 -1
- package/lib/components/Checkbox/Checkbox.js +1 -1
- package/lib/components/Checkbox/Checkbox.js.map +1 -1
- package/lib/components/Combobox/ItemButton.cjs +11 -2
- package/lib/components/Combobox/ItemButton.cjs.map +1 -1
- package/lib/components/Combobox/ItemButton.js +11 -2
- package/lib/components/Combobox/ItemButton.js.map +1 -1
- package/lib/components/Combobox/MultiCombobox/MultiCombobox.cjs +2 -2
- package/lib/components/Combobox/MultiCombobox/MultiCombobox.cjs.map +1 -1
- package/lib/components/Combobox/MultiCombobox/MultiCombobox.js +2 -2
- package/lib/components/Combobox/MultiCombobox/MultiCombobox.js.map +1 -1
- package/lib/components/Combobox/SingleCombobox/SingleCombobox.cjs +2 -2
- package/lib/components/Combobox/SingleCombobox/SingleCombobox.cjs.map +1 -1
- package/lib/components/Combobox/SingleCombobox/SingleCombobox.js +2 -2
- package/lib/components/Combobox/SingleCombobox/SingleCombobox.js.map +1 -1
- package/lib/components/DatePicker/DatePicker.cjs +2 -2
- package/lib/components/DatePicker/DatePicker.cjs.map +1 -1
- package/lib/components/DatePicker/DatePicker.js +2 -2
- package/lib/components/DatePicker/DatePicker.js.map +1 -1
- package/lib/components/DatePicker/Portal.cjs +1 -1
- package/lib/components/DatePicker/Portal.cjs.map +1 -1
- package/lib/components/DatePicker/Portal.js +1 -1
- package/lib/components/DatePicker/Portal.js.map +1 -1
- package/lib/components/Dialog/FocusTrap.cjs +8 -8
- package/lib/components/Dialog/FocusTrap.cjs.map +1 -1
- package/lib/components/Dialog/FocusTrap.js +9 -9
- package/lib/components/Dialog/FocusTrap.js.map +1 -1
- package/lib/components/Dialog/ModelessDialog/ModelessDialog.cjs +2 -1
- package/lib/components/Dialog/ModelessDialog/ModelessDialog.cjs.map +1 -1
- package/lib/components/Dialog/ModelessDialog/ModelessDialog.js +2 -1
- package/lib/components/Dialog/ModelessDialog/ModelessDialog.js.map +1 -1
- package/lib/components/DropZone/DropZone.cjs +1 -1
- package/lib/components/DropZone/DropZone.cjs.map +1 -1
- package/lib/components/DropZone/DropZone.js +1 -1
- package/lib/components/DropZone/DropZone.js.map +1 -1
- package/lib/components/Heading/PageHeading/PageHeading.cjs +1 -1
- package/lib/components/Heading/PageHeading/PageHeading.cjs.map +1 -1
- package/lib/components/Heading/PageHeading/PageHeading.js +1 -1
- package/lib/components/Heading/PageHeading/PageHeading.js.map +1 -1
- package/lib/components/Input/CurrencyInput/CurrencyInput.cjs +3 -3
- package/lib/components/Input/CurrencyInput/CurrencyInput.cjs.map +1 -1
- package/lib/components/Input/CurrencyInput/CurrencyInput.js +3 -3
- package/lib/components/Input/CurrencyInput/CurrencyInput.js.map +1 -1
- package/lib/components/Input/Input.cjs +15 -17
- package/lib/components/Input/Input.cjs.map +1 -1
- package/lib/components/Input/Input.js +15 -17
- package/lib/components/Input/Input.js.map +1 -1
- package/lib/components/InputFile/InputFileMultiplyAppendable.cjs +8 -7
- package/lib/components/InputFile/InputFileMultiplyAppendable.cjs.map +1 -1
- package/lib/components/InputFile/InputFileMultiplyAppendable.js +8 -7
- package/lib/components/InputFile/InputFileMultiplyAppendable.js.map +1 -1
- package/lib/components/InputFile/InputFileNative.cjs +6 -5
- package/lib/components/InputFile/InputFileNative.cjs.map +1 -1
- package/lib/components/InputFile/InputFileNative.js +6 -5
- package/lib/components/InputFile/InputFileNative.js.map +1 -1
- package/lib/components/Layout/Container/Container.cjs.map +1 -1
- package/lib/components/Layout/Container/Container.js.map +1 -1
- package/lib/components/Pagination/Pagination.cjs +3 -2
- package/lib/components/Pagination/Pagination.cjs.map +1 -1
- package/lib/components/Pagination/Pagination.js +2 -1
- package/lib/components/Pagination/Pagination.js.map +1 -1
- package/lib/components/Panel/Groupbox/Groupbox.cjs +1 -0
- package/lib/components/Panel/Groupbox/Groupbox.cjs.map +1 -1
- package/lib/components/Panel/Groupbox/Groupbox.js +1 -0
- package/lib/components/Panel/Groupbox/Groupbox.js.map +1 -1
- package/lib/components/Scroller/Scroller.cjs +3 -1
- package/lib/components/Scroller/Scroller.cjs.map +1 -1
- package/lib/components/Scroller/Scroller.js +3 -1
- package/lib/components/Scroller/Scroller.js.map +1 -1
- package/lib/components/Select/Select.cjs +12 -14
- package/lib/components/Select/Select.cjs.map +1 -1
- package/lib/components/Select/Select.js +12 -14
- package/lib/components/Select/Select.js.map +1 -1
- package/lib/components/Table/TableReel.cjs +67 -3
- package/lib/components/Table/TableReel.cjs.map +1 -1
- package/lib/components/Table/TableReel.js +68 -4
- package/lib/components/Table/TableReel.js.map +1 -1
- package/lib/components/Table/TableScroller.cjs +13 -15
- package/lib/components/Table/TableScroller.cjs.map +1 -1
- package/lib/components/Table/TableScroller.js +14 -16
- package/lib/components/Table/TableScroller.js.map +1 -1
- package/lib/components/Textarea/Textarea.cjs +18 -12
- package/lib/components/Textarea/Textarea.cjs.map +1 -1
- package/lib/components/Textarea/Textarea.js +19 -13
- package/lib/components/Textarea/Textarea.js.map +1 -1
- package/lib/components/Tooltip/TooltipPortal.cjs +1 -0
- package/lib/components/Tooltip/TooltipPortal.cjs.map +1 -1
- package/lib/components/Tooltip/TooltipPortal.js +1 -0
- package/lib/components/Tooltip/TooltipPortal.js.map +1 -1
- package/lib/hooks/useClick.cjs +7 -4
- package/lib/hooks/useClick.cjs.map +1 -1
- package/lib/hooks/useClick.js +7 -4
- package/lib/hooks/useClick.js.map +1 -1
- package/lib/hooks/useOuterClick.cjs +5 -6
- package/lib/hooks/useOuterClick.cjs.map +1 -1
- package/lib/hooks/useOuterClick.js +6 -7
- package/lib/hooks/useOuterClick.js.map +1 -1
- package/lib/hooks/usePortal.cjs +24 -17
- package/lib/hooks/usePortal.cjs.map +1 -1
- package/lib/hooks/usePortal.d.ts +1 -1
- package/lib/hooks/usePortal.js +25 -18
- package/lib/hooks/usePortal.js.map +1 -1
- package/lib/hooks/useResponseStatus.cjs +20 -23
- package/lib/hooks/useResponseStatus.cjs.map +1 -1
- package/lib/hooks/useResponseStatus.js +20 -23
- package/lib/hooks/useResponseStatus.js.map +1 -1
- package/lib/intl/locales/en_us.json.cjs +1 -0
- package/lib/intl/locales/en_us.json.cjs.map +1 -1
- package/lib/intl/locales/en_us.json.js +1 -0
- package/lib/intl/locales/en_us.json.js.map +1 -1
- package/lib/intl/locales/id_id.json.cjs +0 -8
- package/lib/intl/locales/id_id.json.cjs.map +1 -1
- package/lib/intl/locales/id_id.json.js +0 -8
- package/lib/intl/locales/id_id.json.js.map +1 -1
- package/lib/intl/locales/index.d.ts +6 -16
- package/lib/intl/locales/ja_easy.json.cjs +1 -0
- package/lib/intl/locales/ja_easy.json.cjs.map +1 -1
- package/lib/intl/locales/ja_easy.json.js +1 -0
- package/lib/intl/locales/ja_easy.json.js.map +1 -1
- package/lib/intl/locales/ko_kr.json.cjs +1 -0
- package/lib/intl/locales/ko_kr.json.cjs.map +1 -1
- package/lib/intl/locales/ko_kr.json.js +1 -0
- package/lib/intl/locales/ko_kr.json.js.map +1 -1
- package/lib/intl/locales/pt_br.json.cjs +0 -8
- package/lib/intl/locales/pt_br.json.cjs.map +1 -1
- package/lib/intl/locales/pt_br.json.js +0 -8
- package/lib/intl/locales/pt_br.json.js.map +1 -1
- package/lib/intl/locales/vi_vn.json.cjs +1 -0
- package/lib/intl/locales/vi_vn.json.cjs.map +1 -1
- package/lib/intl/locales/vi_vn.json.js +1 -0
- package/lib/intl/locales/vi_vn.json.js.map +1 -1
- package/lib/intl/locales/zh_hans_cn.json.cjs +1 -0
- package/lib/intl/locales/zh_hans_cn.json.cjs.map +1 -1
- package/lib/intl/locales/zh_hans_cn.json.js +1 -0
- package/lib/intl/locales/zh_hans_cn.json.js.map +1 -1
- package/lib/intl/locales/zh_hant_tw.json.cjs +1 -0
- package/lib/intl/locales/zh_hant_tw.json.cjs.map +1 -1
- package/lib/intl/locales/zh_hant_tw.json.js +1 -0
- package/lib/intl/locales/zh_hant_tw.json.js.map +1 -1
- package/lib/intl/useDateFormat.cjs +1 -2
- package/lib/intl/useDateFormat.cjs.map +1 -1
- package/lib/intl/useDateFormat.js +1 -2
- package/lib/intl/useDateFormat.js.map +1 -1
- package/lib/intl/useIntl.cjs +2 -4
- package/lib/intl/useIntl.cjs.map +1 -1
- package/lib/intl/useIntl.js +2 -4
- package/lib/intl/useIntl.js.map +1 -1
- package/lib/libs/debounce.cjs +10 -2
- package/lib/libs/debounce.cjs.map +1 -1
- package/lib/libs/debounce.d.ts +4 -2
- package/lib/libs/debounce.js +10 -2
- package/lib/libs/debounce.js.map +1 -1
- package/lib/libs/lodash.cjs +3 -4
- package/lib/libs/lodash.cjs.map +1 -1
- package/lib/libs/lodash.d.ts +2 -11
- package/lib/libs/lodash.js +2 -7
- package/lib/libs/lodash.js.map +1 -1
- package/lib/libs/map.cjs +1 -5
- package/lib/libs/map.cjs.map +1 -1
- package/lib/libs/map.js +1 -5
- package/lib/libs/map.js.map +1 -1
- package/lib/themes/createBackgroundColor.cjs +3 -2
- package/lib/themes/createBackgroundColor.cjs.map +1 -1
- package/lib/themes/createBackgroundColor.js +2 -1
- package/lib/themes/createBackgroundColor.js.map +1 -1
- package/lib/themes/createBorder.cjs +3 -2
- package/lib/themes/createBorder.cjs.map +1 -1
- package/lib/themes/createBorder.js +2 -1
- package/lib/themes/createBorder.js.map +1 -1
- package/lib/themes/createBreakpoint.cjs +3 -2
- package/lib/themes/createBreakpoint.cjs.map +1 -1
- package/lib/themes/createBreakpoint.js +2 -1
- package/lib/themes/createBreakpoint.js.map +1 -1
- package/lib/themes/createColor.cjs +3 -2
- package/lib/themes/createColor.cjs.map +1 -1
- package/lib/themes/createColor.js +2 -1
- package/lib/themes/createColor.js.map +1 -1
- package/lib/themes/createFontSize.cjs +3 -2
- package/lib/themes/createFontSize.cjs.map +1 -1
- package/lib/themes/createFontSize.js +2 -1
- package/lib/themes/createFontSize.js.map +1 -1
- package/lib/themes/createInteraction.cjs +3 -2
- package/lib/themes/createInteraction.cjs.map +1 -1
- package/lib/themes/createInteraction.js +2 -1
- package/lib/themes/createInteraction.js.map +1 -1
- package/lib/themes/createLeading.cjs +3 -2
- package/lib/themes/createLeading.cjs.map +1 -1
- package/lib/themes/createLeading.js +2 -1
- package/lib/themes/createLeading.js.map +1 -1
- package/lib/themes/createRadius.cjs +3 -2
- package/lib/themes/createRadius.cjs.map +1 -1
- package/lib/themes/createRadius.js +2 -1
- package/lib/themes/createRadius.js.map +1 -1
- package/lib/themes/createShadow/createShadow.cjs +3 -2
- package/lib/themes/createShadow/createShadow.cjs.map +1 -1
- package/lib/themes/createShadow/createShadow.js +2 -1
- package/lib/themes/createShadow/createShadow.js.map +1 -1
- package/lib/themes/createTextColor.cjs +3 -2
- package/lib/themes/createTextColor.cjs.map +1 -1
- package/lib/themes/createTextColor.js +2 -1
- package/lib/themes/createTextColor.js.map +1 -1
- package/lib/themes/createZIndex.cjs +3 -2
- package/lib/themes/createZIndex.cjs.map +1 -1
- package/lib/themes/createZIndex.js +2 -1
- package/lib/themes/createZIndex.js.map +1 -1
- package/metadata.json +1 -1
- package/package.json +1 -1
- package/smarthr-ui.css +27 -7
- package/lib/components/Table/useReelCells.cjs +0 -83
- package/lib/components/Table/useReelCells.cjs.map +0 -1
- package/lib/components/Table/useReelCells.d.ts +0 -3
- package/lib/components/Table/useReelCells.js +0 -81
- package/lib/components/Table/useReelCells.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ModelessDialog.js","sources":["../../../../src/components/Dialog/ModelessDialog/ModelessDialog.tsx"],"sourcesContent":["'use client'\n\nimport {\n type FC,\n type KeyboardEvent,\n type MouseEvent,\n type PropsWithChildren,\n type ReactNode,\n type RefObject,\n memo,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n} from 'react'\nimport Draggable from 'react-draggable'\nimport { type VariantProps, tv } from 'tailwind-variants'\n\nimport { useHandleEscape } from '../../../hooks/useHandleEscape'\nimport { useLatest } from '../../../hooks/useLatest'\nimport { Localizer, useIntl } from '../../../intl'\nimport { debounce } from '../../../libs/debounce'\nimport { dialogSize } from '../../../tailwind'\nimport { Button } from '../../Button'\nimport { Heading } from '../../Heading'\nimport { FaGripIcon, FaXmarkIcon } from '../../Icon'\nimport { Panel, type PanelElementProps } from '../../Panel'\nimport { DialogBody, type Props as DialogBodyProps } from '../DialogBody'\nimport { DialogOverlap } from '../DialogOverlap'\nimport { useDialogPortal } from '../useDialogPortal'\n\nimport type { DialogSize } from '../types'\n\ntype BaseProps = PropsWithChildren<{\n /**\n * ダイアログのタイトルの内容\n */\n heading: ReactNode\n /**\n * ダイアログのフッタ部分の内容\n */\n footer?: ReactNode\n /**\n * ダイアログが開かれているかどうかの真偽値\n */\n isOpen: boolean\n /**\n * 閉じるボタンを押下したときのハンドラ\n */\n onClickClose?: (e: MouseEvent<HTMLButtonElement>) => void\n /**\n * ダイアログが開いている状態で Escape キーを押下したときのハンドラ\n */\n onPressEscape?: () => void\n /**\n * @deprecated ダイアログの幅を指定する場合は、`width` ではなく `size` を使用してください。\n * ダイアログの幅\n */\n width?: string | number\n /**\n * ダイアログの大きさ\n */\n size?: DialogSize\n /**\n * ダイアログの高さ\n */\n height?: string | number\n /**\n * ダイアログを開いたときの初期 top 位置\n */\n top?: string | number\n /**\n * ダイアログを開いたときの初期 left 位置\n */\n left?: string | number\n /**\n * ダイアログを開いたときの初期 right 位置\n */\n right?: string | number\n /**\n * ダイアログを開いたときの初期 bottom 位置\n */\n bottom?: string | number\n /**\n * ポータルの container となる DOM 要素を追加する親要素\n */\n portalParent?: HTMLElement | RefObject<HTMLElement>\n}>\ntype Props = BaseProps &\n Omit<DialogBodyProps, keyof BaseProps> &\n Omit<PanelElementProps, keyof BaseProps> &\n Omit<VariantProps<typeof classNameGenerator>, keyof BaseProps>\n\nconst classNameGenerator = tv({\n slots: {\n overlap: 'shr-inset-[unset]',\n wrapper:\n 'smarthr-ui-ModelessDialog shr-fixed shr-flex shr-max-h-[calc(100svh-theme(spacing[0.5]))] shr-max-w-[calc(100vw-theme(spacing[0.5]))] shr-flex-col',\n headerEl: [\n 'smarthr-ui-ModelessDialog-header shr-border-b-shorthand shr-relative shr-flex shr-cursor-move shr-items-center shr-rounded-tl-l shr-rounded-tr-l shr-pe-1 shr-ps-1.5',\n 'hover:shr-bg-white-darken',\n /* DialogHandlerにフォーカスが当たっているときは、headerもフォーカス状態のスタイルにする。 */\n 'has-[.smarthr-ui-ModelessDialog-handle:focus-visible]:shr-focus-indicator has-[.smarthr-ui-ModelessDialog-handle:focus-visible]:shr-bg-white-darken has-[.smarthr-ui-ModelessDialog-handle:focus-visible]:shr-transition-colors has-[.smarthr-ui-ModelessDialog-handle:focus-visible]:shr-duration-100 has-[.smarthr-ui-ModelessDialog-handle:focus-visible]:shr-ease-in-out',\n ],\n dialogHandler: [\n 'smarthr-ui-ModelessDialog-handle shr-absolute shr-inset-x-0 shr-bottom-0 shr-top-[2px] shr-m-auto shr-flex shr-justify-center shr-rounded-tl-s shr-rounded-tr-s shr-border-none shr-text-grey shr-transition-colors shr-duration-100 shr-ease-in-out',\n 'focus-visible:shr-focus-indicator--none shr-cursor-[inherit] shr-bg-[unset]',\n ],\n headingEl: ['shr-my-1 shr-me-1'],\n closeButtonLayout: [\n 'shr-relative' /* DialogHandlerの上に出すためにスタッキングコンテキストを生成 */,\n 'shr-ml-auto shr-shrink-0',\n ],\n footerEl: 'smarthr-ui-ModelessDialog-footer shr-border-t-shorthand',\n },\n variants: {\n size: {\n XS: { wrapper: dialogSize.XS },\n S: { wrapper: dialogSize.S },\n M: { wrapper: dialogSize.M },\n L: { wrapper: dialogSize.L },\n XL: { wrapper: dialogSize.XL },\n XXL: { wrapper: dialogSize.XXL },\n FULL: { wrapper: dialogSize.FULL },\n },\n resizable: {\n true: {\n wrapper: 'shr-resize shr-overflow-auto',\n },\n false: {},\n },\n },\n})\n\nexport const ModelessDialog: FC<Props> = ({\n heading,\n children,\n contentBgColor,\n contentPadding,\n footer,\n isOpen,\n onPressEscape,\n resizable = false,\n width,\n size,\n height,\n top,\n left,\n right,\n bottom,\n portalParent,\n className,\n id,\n onClickClose,\n ...rest\n}) => {\n const labelId = useId()\n const lastFocusElementRef = useRef<HTMLElement | null>(null)\n const { createPortal } = useDialogPortal(portalParent, id)\n const { localize } = useIntl()\n\n const classNames = useMemo(() => {\n const { overlap, wrapper, headerEl, headingEl, dialogHandler, closeButtonLayout, footerEl } =\n classNameGenerator()\n\n return {\n overlap: overlap({ className }),\n wrapper: wrapper({ size, resizable }),\n header: headerEl(),\n heading: headingEl(),\n dialogHandler: dialogHandler(),\n closeButtonLayout: closeButtonLayout(),\n footer: footerEl(),\n }\n }, [className, size, resizable])\n\n const wrapperRef = useRef<HTMLDivElement>(null)\n const focusTargetRef = useRef<HTMLDivElement>(null)\n\n const [wrapperPosition, setWrapperPosition] = useState<DOMRect | undefined>(undefined)\n const [debouncedLiveRegionText, setDebouncedLiveRegionText] = useState<string>('')\n const [centering, setCentering] = useState<{\n top?: number\n left?: number\n }>({})\n const [position, setPosition] = useState<{ x: number; y: number }>({\n x: 0,\n y: 0,\n })\n const [draggableBounds, setDraggableBounds] = useState(Draggable.defaultProps.bounds)\n\n const positionStyle = useMemo(\n () => ({\n top: centering.top ?? top,\n left: centering.left ?? left,\n right,\n bottom,\n width: size ? undefined : width,\n height,\n }),\n [centering, top, left, right, bottom, width, height, size],\n )\n\n const latest = useLatest({ isOpen, onClickClose, onPressEscape })\n\n const functions = useMemo(\n () => ({\n debounceLiveRegionText: debounce(setDebouncedLiveRegionText, 600),\n handleArrowKeyDown: (e: KeyboardEvent) => {\n if (!latest.isOpen || document.activeElement !== e.currentTarget) {\n return\n }\n\n const movingDistance = 20\n\n switch (e.key) {\n case 'ArrowUp':\n setPosition((prev) => ({\n x: prev.x,\n y: prev.y - movingDistance,\n }))\n e.preventDefault()\n break\n case 'ArrowDown':\n setPosition((prev) => ({\n x: prev.x,\n y: prev.y + movingDistance,\n }))\n e.preventDefault()\n break\n case 'ArrowLeft':\n setPosition((prev) => ({\n x: prev.x - movingDistance,\n y: prev.y,\n }))\n e.preventDefault()\n break\n case 'ArrowRight':\n setPosition((prev) => ({\n x: prev.x + movingDistance,\n y: prev.y,\n }))\n e.preventDefault()\n break\n }\n },\n handleClickClose: (e: MouseEvent<HTMLButtonElement>) => {\n lastFocusElementRef.current?.focus()\n latest.onClickClose?.(e)\n },\n handlePressEscape: () => {\n lastFocusElementRef.current?.focus()\n latest.onPressEscape?.()\n },\n handleDragStart: (_: any, data: { x: number; y: number }) => setPosition(data),\n handleDrag: (_: any, data: { deltaX: number; deltaY: number }) => {\n setPosition((prev) => ({\n x: prev.x + data.deltaX,\n y: prev.y + data.deltaY,\n }))\n },\n }),\n [latest],\n )\n\n useEffect(() => {\n if (!wrapperPosition) {\n setDebouncedLiveRegionText('')\n return\n }\n\n const txt = localize(\n {\n id: 'smarthr-ui/ModelessDialog/dialogHandlerLiveRegionText',\n defaultText: '上から{top}px、左から{left}px',\n },\n {\n top: Math.trunc(wrapperPosition.top).toString(),\n left: Math.trunc(wrapperPosition.left).toString(),\n },\n )\n\n functions.debounceLiveRegionText(txt)\n }, [localize, wrapperPosition, functions])\n\n useEffect(() => {\n if (wrapperRef.current instanceof Element) {\n setWrapperPosition(wrapperRef.current.getBoundingClientRect())\n }\n }, [position])\n\n useEffect(() => {\n // 中央寄せの座標計算を行う\n if (!wrapperRef.current || !isOpen) {\n return\n }\n\n const isXCenter = left === undefined && right === undefined\n const isYCenter = top === undefined && bottom === undefined\n\n if (isXCenter || isYCenter) {\n const rect = wrapperRef.current.getBoundingClientRect()\n\n setCentering({\n top: isYCenter ? Math.max(0, window.innerHeight / 2 - rect.height / 2) : undefined,\n left: isXCenter ? Math.max(0, window.innerWidth / 2 - rect.width / 2) : undefined,\n })\n }\n }, [bottom, isOpen, left, right, top])\n\n useEffect(() => {\n if (!isOpen) return\n\n if (centering.top) {\n setDraggableBounds({ top: centering.top * -1 })\n\n return\n }\n\n if (wrapperRef.current) {\n const rect = wrapperRef.current.getBoundingClientRect()\n\n setDraggableBounds({ top: rect.top * -1 })\n }\n }, [isOpen, centering.top])\n\n useEffect(() => {\n if (isOpen) {\n setPosition({ x: 0, y: 0 })\n focusTargetRef.current?.focus()\n }\n }, [isOpen])\n\n useHandleEscape(isOpen ? functions.handlePressEscape : undefined)\n\n useEffect(() => {\n const focusHandler = (e: FocusEvent) => {\n // e.target(現在フォーカスがあたっている要素)がModeless dialog外の要素であれば、lastFocusElementRefに代入する\n if (e.target instanceof HTMLElement && !wrapperRef?.current?.contains(e.target)) {\n lastFocusElementRef.current = e.target\n }\n }\n\n document.addEventListener('focus', focusHandler, true)\n\n return () => document.removeEventListener('focus', focusHandler, true)\n }, [])\n\n return createPortal(\n <DialogOverlap isOpen={isOpen} className={classNames.overlap} as=\"section\">\n <Draggable\n {...Draggable.defaultProps}\n handle=\".smarthr-ui-ModelessDialog-handle\"\n onStart={functions.handleDragStart}\n onDrag={functions.handleDrag}\n position={position}\n bounds={draggableBounds ?? false}\n nodeRef={wrapperRef}\n >\n <Panel\n {...rest}\n ref={wrapperRef}\n role=\"dialog\"\n aria-labelledby={labelId}\n radius=\"m\"\n layer={3}\n overflow=\"auto\"\n className={classNames.wrapper}\n style={positionStyle}\n >\n {/* eslint-disable-next-line smarthr/a11y-scroller-has-tabindex -- dummy element for focus management. */}\n <div tabIndex={-1} ref={focusTargetRef} />\n <div className={classNames.header}>\n <Handler\n handleArrowKeyDown={functions.handleArrowKeyDown}\n className={classNames.dialogHandler}\n />\n <div id={labelId} className={classNames.heading}>\n {/* eslint-disable-next-line smarthr/a11y-heading-in-sectioning-content */}\n <Heading>{heading}</Heading>\n </div>\n <CloseButton\n handleClick={functions.handleClickClose}\n className={classNames.closeButtonLayout}\n />\n </div>\n <DialogBody\n contentBgColor={contentBgColor}\n contentPadding={contentPadding}\n className=\"smarthr-ui-ModelessDialog-content shr-overscroll-contain\"\n >\n {children}\n </DialogBody>\n {footer && <div className={classNames.footer}>{footer}</div>}\n <LiveRegion regionText={debouncedLiveRegionText} />\n </Panel>\n </Draggable>\n </DialogOverlap>,\n )\n}\n\nconst Handler = memo<{\n className: string\n handleArrowKeyDown: (e: KeyboardEvent) => void\n}>(({ handleArrowKeyDown, ...rest }) => {\n const { localize } = useIntl()\n\n return (\n <>\n <button\n {...rest}\n type=\"button\"\n aria-label={localize({\n id: 'smarthr-ui/ModelessDialog/dialogHandlerAriaLabel',\n defaultText: 'ダイアログの位置',\n })}\n aria-roledescription={localize({\n id: 'smarthr-ui/ModelessDialog/dialogHandlerAriaRoleDescription',\n defaultText: 'ドラッグ可能',\n })}\n aria-describedby=\"handler-description\"\n onKeyDown={handleArrowKeyDown}\n >\n <FaGripIcon />\n </button>\n <div className=\"shr-hidden\" id=\"handler-description\">\n {localize({\n id: 'smarthr-ui/ModelessDialog/dialogHandlerDescription',\n defaultText: '矢印キーを押して上下左右に移動できます',\n })}\n </div>\n </>\n )\n})\n\nconst LiveRegion = ({ regionText }: { regionText: string | undefined }) => (\n <div\n role=\"status\"\n className=\"shr-fixed -shr-m-px shr-h-px shr-w-px shr-overflow-hidden shr-whitespace-nowrap\"\n >\n {regionText}\n </div>\n)\n\nconst CloseButton = memo<{\n className: string\n handleClick: (e: MouseEvent<HTMLButtonElement>) => void\n}>(({ handleClick, className }) => (\n <div className={className}>\n <Button\n type=\"button\"\n size=\"S\"\n onClick={handleClick}\n className=\"smarthr-ui-ModelessDialog-closeButton\"\n >\n <FaXmarkIcon\n alt={<Localizer id=\"smarthr-ui/ModelessDialog/closeButtonIconAlt\" defaultText=\"閉じる\" />}\n />\n </Button>\n </div>\n))\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8FA;AACE;AACE;AACA;AAEA;;;;;AAKC;AACD;;;AAGC;;AAED;AACE;;AAED;AACD;AACD;AACD;AACE;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACD;AACD;AACE;AACE;AACD;AACD;AACD;AACF;AACF;;AAwBC;AACA;;AAEA;AAEA;AACE;;AAIE;;;;;;;;;AAUJ;AACA;;;;AAQA;AACE;AACA;AACD;AACD;AAEA;AAEI;AACA;;;;;AAKD;AAIH;AAEA;AAEI;AACA;AACE;;;;AAMA;AACE;AACE;;AAEE;AACD;;;AAGH;AACE;;AAEE;AACD;;;AAGH;AACE;AACE;;AAED;;;AAGH;AACE;AACE;;AAED;;;;;AAKP;AACE;AACA;;;AAGA;AACA;;;AAGF;AACE;AACE;AACA;AACD;;AAEJ;;;;;;;AAYG;AACA;;;;AAKD;AAGH;;;AAIA;;;AAGF;;;;;;;;AAWE;;AAGE;;;AAGC;;AAEL;;AAGE;;AAEA;AACE;;;AAKF;;AAGE;;;;;;AAOA;;AAEJ;AAEA;;AAGE;;AAEE;AACE;;AAEJ;;AAIA;;;AAsDJ;AAEA;AAIE;;AAQQ;AACA;;AAGA;AACA;;AASA;AACA;;AAKV;AAEA;AASA;;"}
|
|
1
|
+
{"version":3,"file":"ModelessDialog.js","sources":["../../../../src/components/Dialog/ModelessDialog/ModelessDialog.tsx"],"sourcesContent":["'use client'\n\nimport {\n type FC,\n type KeyboardEvent,\n type MouseEvent,\n type PropsWithChildren,\n type ReactNode,\n type RefObject,\n memo,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n} from 'react'\nimport Draggable from 'react-draggable'\nimport { type VariantProps, tv } from 'tailwind-variants'\n\nimport { useHandleEscape } from '../../../hooks/useHandleEscape'\nimport { useLatest } from '../../../hooks/useLatest'\nimport { Localizer, useIntl } from '../../../intl'\nimport { debounce } from '../../../libs/debounce'\nimport { dialogSize } from '../../../tailwind'\nimport { Button } from '../../Button'\nimport { Heading } from '../../Heading'\nimport { FaGripIcon, FaXmarkIcon } from '../../Icon'\nimport { Panel, type PanelElementProps } from '../../Panel'\nimport { DialogBody, type Props as DialogBodyProps } from '../DialogBody'\nimport { DialogOverlap } from '../DialogOverlap'\nimport { useDialogPortal } from '../useDialogPortal'\n\nimport type { DialogSize } from '../types'\n\ntype BaseProps = PropsWithChildren<{\n /**\n * ダイアログのタイトルの内容\n */\n heading: ReactNode\n /**\n * ダイアログのフッタ部分の内容\n */\n footer?: ReactNode\n /**\n * ダイアログが開かれているかどうかの真偽値\n */\n isOpen: boolean\n /**\n * 閉じるボタンを押下したときのハンドラ\n */\n onClickClose?: (e: MouseEvent<HTMLButtonElement>) => void\n /**\n * ダイアログが開いている状態で Escape キーを押下したときのハンドラ\n */\n onPressEscape?: () => void\n /**\n * @deprecated ダイアログの幅を指定する場合は、`width` ではなく `size` を使用してください。\n * ダイアログの幅\n */\n width?: string | number\n /**\n * ダイアログの大きさ\n */\n size?: DialogSize\n /**\n * ダイアログの高さ\n */\n height?: string | number\n /**\n * ダイアログを開いたときの初期 top 位置\n */\n top?: string | number\n /**\n * ダイアログを開いたときの初期 left 位置\n */\n left?: string | number\n /**\n * ダイアログを開いたときの初期 right 位置\n */\n right?: string | number\n /**\n * ダイアログを開いたときの初期 bottom 位置\n */\n bottom?: string | number\n /**\n * ポータルの container となる DOM 要素を追加する親要素\n */\n portalParent?: HTMLElement | RefObject<HTMLElement>\n}>\ntype Props = BaseProps &\n Omit<DialogBodyProps, keyof BaseProps> &\n Omit<PanelElementProps, keyof BaseProps> &\n Omit<VariantProps<typeof classNameGenerator>, keyof BaseProps>\n\nconst classNameGenerator = tv({\n slots: {\n overlap: 'shr-inset-[unset]',\n wrapper:\n 'smarthr-ui-ModelessDialog shr-fixed shr-flex shr-max-h-[calc(100svh-theme(spacing[0.5]))] shr-max-w-[calc(100vw-theme(spacing[0.5]))] shr-flex-col',\n headerEl: [\n 'smarthr-ui-ModelessDialog-header shr-border-b-shorthand shr-relative shr-flex shr-cursor-move shr-items-center shr-rounded-tl-l shr-rounded-tr-l shr-pe-1 shr-ps-1.5',\n 'hover:shr-bg-white-darken',\n /* DialogHandlerにフォーカスが当たっているときは、headerもフォーカス状態のスタイルにする。 */\n 'has-[.smarthr-ui-ModelessDialog-handle:focus-visible]:shr-focus-indicator has-[.smarthr-ui-ModelessDialog-handle:focus-visible]:shr-bg-white-darken has-[.smarthr-ui-ModelessDialog-handle:focus-visible]:shr-transition-colors has-[.smarthr-ui-ModelessDialog-handle:focus-visible]:shr-duration-100 has-[.smarthr-ui-ModelessDialog-handle:focus-visible]:shr-ease-in-out',\n ],\n dialogHandler: [\n 'smarthr-ui-ModelessDialog-handle shr-absolute shr-inset-x-0 shr-bottom-0 shr-top-[2px] shr-m-auto shr-flex shr-justify-center shr-rounded-tl-s shr-rounded-tr-s shr-border-none shr-text-grey shr-transition-colors shr-duration-100 shr-ease-in-out',\n 'focus-visible:shr-focus-indicator--none shr-cursor-[inherit] shr-bg-[unset]',\n ],\n headingEl: ['shr-my-1 shr-me-1'],\n closeButtonLayout: [\n 'shr-relative' /* DialogHandlerの上に出すためにスタッキングコンテキストを生成 */,\n 'shr-ml-auto shr-shrink-0',\n ],\n footerEl: 'smarthr-ui-ModelessDialog-footer shr-border-t-shorthand',\n },\n variants: {\n size: {\n XS: { wrapper: dialogSize.XS },\n S: { wrapper: dialogSize.S },\n M: { wrapper: dialogSize.M },\n L: { wrapper: dialogSize.L },\n XL: { wrapper: dialogSize.XL },\n XXL: { wrapper: dialogSize.XXL },\n FULL: { wrapper: dialogSize.FULL },\n },\n resizable: {\n true: {\n wrapper: 'shr-resize shr-overflow-auto',\n },\n false: {},\n },\n },\n})\n\nexport const ModelessDialog: FC<Props> = ({\n heading,\n children,\n contentBgColor,\n contentPadding,\n footer,\n isOpen,\n onPressEscape,\n resizable = false,\n width,\n size,\n height,\n top,\n left,\n right,\n bottom,\n portalParent,\n className,\n id,\n onClickClose,\n ...rest\n}) => {\n const labelId = useId()\n const lastFocusElementRef = useRef<HTMLElement | null>(null)\n const { createPortal } = useDialogPortal(portalParent, id)\n const { localize } = useIntl()\n\n const classNames = useMemo(() => {\n const { overlap, wrapper, headerEl, headingEl, dialogHandler, closeButtonLayout, footerEl } =\n classNameGenerator()\n\n return {\n overlap: overlap({ className }),\n wrapper: wrapper({ size, resizable }),\n header: headerEl(),\n heading: headingEl(),\n dialogHandler: dialogHandler(),\n closeButtonLayout: closeButtonLayout(),\n footer: footerEl(),\n }\n }, [className, size, resizable])\n\n const wrapperRef = useRef<HTMLDivElement>(null)\n const focusTargetRef = useRef<HTMLDivElement>(null)\n\n const [wrapperPosition, setWrapperPosition] = useState<DOMRect | undefined>(undefined)\n const [debouncedLiveRegionText, setDebouncedLiveRegionText] = useState<string>('')\n const [centering, setCentering] = useState<{\n top?: number\n left?: number\n }>({})\n const [position, setPosition] = useState<{ x: number; y: number }>({\n x: 0,\n y: 0,\n })\n const [draggableBounds, setDraggableBounds] = useState(Draggable.defaultProps.bounds)\n\n const positionStyle = useMemo(\n () => ({\n top: centering.top ?? top,\n left: centering.left ?? left,\n right,\n bottom,\n width: size ? undefined : width,\n height,\n }),\n [centering, top, left, right, bottom, width, height, size],\n )\n\n const latest = useLatest({ isOpen, onClickClose, onPressEscape })\n\n const functions = useMemo(\n () => ({\n debounceLiveRegionText: debounce(setDebouncedLiveRegionText, 600),\n handleArrowKeyDown: (e: KeyboardEvent) => {\n if (!latest.isOpen || document.activeElement !== e.currentTarget) {\n return\n }\n\n const movingDistance = 20\n\n switch (e.key) {\n case 'ArrowUp':\n setPosition((prev) => ({\n x: prev.x,\n y: prev.y - movingDistance,\n }))\n e.preventDefault()\n break\n case 'ArrowDown':\n setPosition((prev) => ({\n x: prev.x,\n y: prev.y + movingDistance,\n }))\n e.preventDefault()\n break\n case 'ArrowLeft':\n setPosition((prev) => ({\n x: prev.x - movingDistance,\n y: prev.y,\n }))\n e.preventDefault()\n break\n case 'ArrowRight':\n setPosition((prev) => ({\n x: prev.x + movingDistance,\n y: prev.y,\n }))\n e.preventDefault()\n break\n }\n },\n handleClickClose: (e: MouseEvent<HTMLButtonElement>) => {\n lastFocusElementRef.current?.focus()\n latest.onClickClose?.(e)\n },\n handlePressEscape: () => {\n lastFocusElementRef.current?.focus()\n latest.onPressEscape?.()\n },\n handleDragStart: (_: any, data: { x: number; y: number }) => setPosition(data),\n handleDrag: (_: any, data: { deltaX: number; deltaY: number }) => {\n setPosition((prev) => ({\n x: prev.x + data.deltaX,\n y: prev.y + data.deltaY,\n }))\n },\n }),\n [latest],\n )\n\n useEffect(() => {\n if (!wrapperPosition) {\n setDebouncedLiveRegionText('')\n return functions.debounceLiveRegionText.cancel\n }\n\n const txt = localize(\n {\n id: 'smarthr-ui/ModelessDialog/dialogHandlerLiveRegionText',\n defaultText: '上から{top}px、左から{left}px',\n },\n {\n top: Math.trunc(wrapperPosition.top).toString(),\n left: Math.trunc(wrapperPosition.left).toString(),\n },\n )\n\n functions.debounceLiveRegionText(txt)\n\n return functions.debounceLiveRegionText.cancel\n }, [localize, wrapperPosition, functions])\n\n useEffect(() => {\n if (wrapperRef.current instanceof Element) {\n setWrapperPosition(wrapperRef.current.getBoundingClientRect())\n }\n }, [position])\n\n useEffect(() => {\n // 中央寄せの座標計算を行う\n if (!wrapperRef.current || !isOpen) {\n return\n }\n\n const isXCenter = left === undefined && right === undefined\n const isYCenter = top === undefined && bottom === undefined\n\n if (isXCenter || isYCenter) {\n const rect = wrapperRef.current.getBoundingClientRect()\n\n setCentering({\n top: isYCenter ? Math.max(0, window.innerHeight / 2 - rect.height / 2) : undefined,\n left: isXCenter ? Math.max(0, window.innerWidth / 2 - rect.width / 2) : undefined,\n })\n }\n }, [bottom, isOpen, left, right, top])\n\n useEffect(() => {\n if (!isOpen) return\n\n if (centering.top) {\n setDraggableBounds({ top: centering.top * -1 })\n\n return\n }\n\n if (wrapperRef.current) {\n const rect = wrapperRef.current.getBoundingClientRect()\n\n setDraggableBounds({ top: rect.top * -1 })\n }\n }, [isOpen, centering.top])\n\n useEffect(() => {\n if (isOpen) {\n setPosition({ x: 0, y: 0 })\n focusTargetRef.current?.focus()\n }\n }, [isOpen])\n\n useHandleEscape(isOpen ? functions.handlePressEscape : undefined)\n\n useEffect(() => {\n const focusHandler = (e: FocusEvent) => {\n // e.target(現在フォーカスがあたっている要素)がModeless dialog外の要素であれば、lastFocusElementRefに代入する\n if (e.target instanceof HTMLElement && !wrapperRef?.current?.contains(e.target)) {\n lastFocusElementRef.current = e.target\n }\n }\n\n document.addEventListener('focus', focusHandler, true)\n\n return () => document.removeEventListener('focus', focusHandler, true)\n }, [])\n\n return createPortal(\n <DialogOverlap isOpen={isOpen} className={classNames.overlap} as=\"section\">\n <Draggable\n {...Draggable.defaultProps}\n handle=\".smarthr-ui-ModelessDialog-handle\"\n onStart={functions.handleDragStart}\n onDrag={functions.handleDrag}\n position={position}\n bounds={draggableBounds ?? false}\n nodeRef={wrapperRef}\n >\n <Panel\n {...rest}\n ref={wrapperRef}\n role=\"dialog\"\n aria-labelledby={labelId}\n radius=\"m\"\n layer={3}\n overflow=\"auto\"\n className={classNames.wrapper}\n style={positionStyle}\n >\n {/* eslint-disable-next-line smarthr/a11y-scroller-has-tabindex -- dummy element for focus management. */}\n <div tabIndex={-1} ref={focusTargetRef} />\n <div className={classNames.header}>\n <Handler\n handleArrowKeyDown={functions.handleArrowKeyDown}\n className={classNames.dialogHandler}\n />\n <div id={labelId} className={classNames.heading}>\n {/* eslint-disable-next-line smarthr/a11y-heading-in-sectioning-content */}\n <Heading>{heading}</Heading>\n </div>\n <CloseButton\n handleClick={functions.handleClickClose}\n className={classNames.closeButtonLayout}\n />\n </div>\n <DialogBody\n contentBgColor={contentBgColor}\n contentPadding={contentPadding}\n className=\"smarthr-ui-ModelessDialog-content shr-overscroll-contain\"\n >\n {children}\n </DialogBody>\n {footer && <div className={classNames.footer}>{footer}</div>}\n <LiveRegion regionText={debouncedLiveRegionText} />\n </Panel>\n </Draggable>\n </DialogOverlap>,\n )\n}\n\nconst Handler = memo<{\n className: string\n handleArrowKeyDown: (e: KeyboardEvent) => void\n}>(({ handleArrowKeyDown, ...rest }) => {\n const { localize } = useIntl()\n\n return (\n <>\n <button\n {...rest}\n type=\"button\"\n aria-label={localize({\n id: 'smarthr-ui/ModelessDialog/dialogHandlerAriaLabel',\n defaultText: 'ダイアログの位置',\n })}\n aria-roledescription={localize({\n id: 'smarthr-ui/ModelessDialog/dialogHandlerAriaRoleDescription',\n defaultText: 'ドラッグ可能',\n })}\n aria-describedby=\"handler-description\"\n onKeyDown={handleArrowKeyDown}\n >\n <FaGripIcon />\n </button>\n <div className=\"shr-hidden\" id=\"handler-description\">\n {localize({\n id: 'smarthr-ui/ModelessDialog/dialogHandlerDescription',\n defaultText: '矢印キーを押して上下左右に移動できます',\n })}\n </div>\n </>\n )\n})\n\nconst LiveRegion = ({ regionText }: { regionText: string | undefined }) => (\n <div\n role=\"status\"\n className=\"shr-fixed -shr-m-px shr-h-px shr-w-px shr-overflow-hidden shr-whitespace-nowrap\"\n >\n {regionText}\n </div>\n)\n\nconst CloseButton = memo<{\n className: string\n handleClick: (e: MouseEvent<HTMLButtonElement>) => void\n}>(({ handleClick, className }) => (\n <div className={className}>\n <Button\n type=\"button\"\n size=\"S\"\n onClick={handleClick}\n className=\"smarthr-ui-ModelessDialog-closeButton\"\n >\n <FaXmarkIcon\n alt={<Localizer id=\"smarthr-ui/ModelessDialog/closeButtonIconAlt\" defaultText=\"閉じる\" />}\n />\n </Button>\n </div>\n))\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8FA;AACE;AACE;AACA;AAEA;;;;;AAKC;AACD;;;AAGC;;AAED;AACE;;AAED;AACD;AACD;AACD;AACE;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACD;AACD;AACE;AACE;AACD;AACD;AACD;AACF;AACF;;AAwBC;AACA;;AAEA;AAEA;AACE;;AAIE;;;;;;;;;AAUJ;AACA;;;;AAQA;AACE;AACA;AACD;AACD;AAEA;AAEI;AACA;;;;;AAKD;AAIH;AAEA;AAEI;AACA;AACE;;;;AAMA;AACE;AACE;;AAEE;AACD;;;AAGH;AACE;;AAEE;AACD;;;AAGH;AACE;AACE;;AAED;;;AAGH;AACE;AACE;;AAED;;;;;AAKP;AACE;AACA;;;AAGA;AACA;;;AAGF;AACE;AACE;AACA;AACD;;AAEJ;;;;AAOC;;;AAKE;AACA;;;;AAKD;AAGH;AAEA;;;AAIA;;;AAGF;;;;;;;;AAWE;;AAGE;;;AAGC;;AAEL;;AAGE;;AAEA;AACE;;;AAKF;;AAGE;;;;;;AAOA;;AAEJ;AAEA;;AAGE;;AAEE;AACE;;AAEJ;;AAIA;;;AAsDJ;AAEA;AAIE;;AAQQ;AACA;;AAGA;AACA;;AASA;AACA;;AAKV;AAEA;AASA;;"}
|
|
@@ -71,7 +71,7 @@ const DropZone = React.forwardRef(({ children, onSelectFiles, multiple = true, d
|
|
|
71
71
|
fileRef.current.click();
|
|
72
72
|
},
|
|
73
73
|
}), [latest]);
|
|
74
|
-
React.useImperativeHandle(ref, () => fileRef.current);
|
|
74
|
+
React.useImperativeHandle(ref, () => fileRef.current, []);
|
|
75
75
|
return (
|
|
76
76
|
// eslint-disable-next-line jsx-a11y/no-static-element-interactions
|
|
77
77
|
jsxRuntime.jsxs("div", { onDrop: functions.handleDrop, onDragOver: functions.handleDragOver, onDragLeave: functions.handleDragLeave, className: classNames.wrapper, "data-files-dragged-over": filesDraggedOver || undefined, children: [children, jsxRuntime.jsx(SelectButton, { label: selectButtonLabel, disabled: disabled, error: error, handleClick: functions.handleClickButton, className: classNames.button }), jsxRuntime.jsx(components_VisuallyHiddenText_VisuallyHiddenText.VisuallyHiddenText, { children: jsxRuntime.jsx("input", { ...rest, ref: fileRef, type: "file", multiple: multiple, disabled: disabled, tabIndex: -1, "aria-invalid": error || undefined, onChange: functions.handleChange, "data-smarthr-ui-input": "true" }) })] }));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DropZone.cjs","sources":["../../../src/components/DropZone/DropZone.tsx"],"sourcesContent":["'use client'\n\nimport {\n type ChangeEvent,\n type ComponentPropsWithRef,\n type DragEvent,\n type PropsWithChildren,\n forwardRef,\n memo,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n} from 'react'\nimport { tv } from 'tailwind-variants'\n\nimport { useLatest } from '../../hooks/useLatest'\nimport { Localizer } from '../../intl'\nimport { Button } from '../Button'\nimport { FaFolderOpenIcon } from '../Icon'\nimport { VisuallyHiddenText } from '../VisuallyHiddenText'\n\nconst classNameGenerator = tv({\n slots: {\n wrapper: [\n 'smarthr-ui-DropZone',\n 'shr-relative',\n 'shr-border-shorthand shr-flex shr-flex-col shr-items-center shr-justify-center shr-bg-column shr-p-2.5',\n 'has-[.smarthr-ui-DropZone-Button[aria-disabled]]:shr-cursor-not-allowed',\n '[&:not([data-files-dragged-over])]:shr-border-dashed',\n 'data-[files-dragged-over]:shr-border-main',\n ],\n button: ['smarthr-ui-DropZone-Button', 'data-[error]:shr-border-danger'],\n },\n})\n\ntype BaseProps = PropsWithChildren<{\n /**\n * ボタンまたはドラッグ&ドロップでファイルが追加された時に発火するコールバック関数\n */\n onSelectFiles: (\n e: DragEvent<HTMLElement> | ChangeEvent<HTMLInputElement>,\n files: FileList | null,\n ) => void\n /**\n * 許可するファイル型を表す1つ以上の固有ファイル型指定子\n * <b>(ドラッグ&ドロップの挙動には影響しません)</b>\n */\n accept?: string\n /** 複数ファイルを選択できるかどうか */\n multiple?: boolean\n name?: string\n disabled?: boolean\n /** フォームにエラーがあるかどうか */\n error?: boolean\n /** ファイル選択ボタンのラベル */\n selectButtonLabel?: string\n}>\ntype Props = BaseProps & Omit<ComponentPropsWithRef<'div'>, keyof BaseProps>\n\nconst overrideEventDefault = (e: DragEvent<HTMLElement>) => {\n e.preventDefault()\n e.stopPropagation()\n}\n\nexport const DropZone = forwardRef<HTMLInputElement, Props>(\n (\n {\n children,\n onSelectFiles,\n multiple = true,\n disabled,\n error,\n selectButtonLabel,\n className,\n ...rest\n },\n ref,\n ) => {\n const fileRef = useRef<HTMLInputElement>(null)\n const [filesDraggedOver, setFilesDraggedOver] = useState(false)\n\n const classNames = useMemo(() => {\n const { wrapper, button } = classNameGenerator()\n return {\n wrapper: wrapper({ className }),\n button: button(),\n }\n }, [className])\n\n const latest = useLatest({ onSelectFiles })\n\n const functions = useMemo(\n () => ({\n handleDrop: (e: DragEvent<HTMLElement>) => {\n overrideEventDefault(e)\n setFilesDraggedOver(false)\n\n if (e.dataTransfer.types.includes('Files')) {\n if (fileRef.current) {\n fileRef.current.files = e.dataTransfer.files\n }\n latest.onSelectFiles(e, e.dataTransfer.files)\n }\n },\n handleDragOver: (e: DragEvent<HTMLElement>) => {\n overrideEventDefault(e)\n setFilesDraggedOver(true)\n },\n handleDragLeave: () => {\n setFilesDraggedOver(false)\n },\n handleChange: (e: ChangeEvent<HTMLInputElement>) => {\n latest.onSelectFiles(e, e.target.files)\n },\n handleClickButton: () => {\n fileRef.current!.click()\n },\n }),\n [latest],\n )\n\n useImperativeHandle<HTMLInputElement | null, HTMLInputElement | null>(\n ref,\n () => fileRef.current,\n )\n\n return (\n // eslint-disable-next-line jsx-a11y/no-static-element-interactions\n <div\n onDrop={functions.handleDrop}\n onDragOver={functions.handleDragOver}\n onDragLeave={functions.handleDragLeave}\n className={classNames.wrapper}\n data-files-dragged-over={filesDraggedOver || undefined}\n >\n {children}\n <SelectButton\n label={selectButtonLabel}\n disabled={disabled}\n error={error}\n handleClick={functions.handleClickButton}\n className={classNames.button}\n />\n <VisuallyHiddenText>\n {/* TODO: この input にアクセシブルネームが設定されていない。VisuallyHiddenText で視覚的に隠されているが aria-hidden ではないためアクセシビリティツリーに残る。aria-label 等で適切なラベルを付与する必要がある */}\n {/* eslint-disable-next-line smarthr/a11y-input-in-form-control */}\n <input\n {...rest}\n ref={fileRef}\n type=\"file\"\n multiple={multiple}\n disabled={disabled}\n tabIndex={-1}\n aria-invalid={error || undefined}\n onChange={functions.handleChange}\n data-smarthr-ui-input=\"true\"\n />\n </VisuallyHiddenText>\n </div>\n )\n },\n)\n\nconst SelectButton = memo<{\n label?: string\n disabled?: boolean\n error?: boolean\n handleClick: () => void\n className: string\n}>(({ label, disabled, error, handleClick, className }) => (\n <Button\n prefix={<FaFolderOpenIcon />}\n disabled={disabled}\n data-error={error || undefined}\n onClick={handleClick}\n className={className}\n >\n {label || <Localizer id=\"smarthr-ui/DropZone/selectButtonLabel\" defaultText=\"ファイルを選択\" />}\n </Button>\n))\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;AAsBA;AACE;AACE;;;;;;;AAOC;AACD;AACD;AACF;AA0BD;;;AAGA;AAEO;AAcH;;AAGA;;;AAGI;;;AAGJ;;AAIA;AAEI;;;;AAKI;;;;;;AAMJ;;;;;;;AAOA;;;;AAIE;;AAEH
|
|
1
|
+
{"version":3,"file":"DropZone.cjs","sources":["../../../src/components/DropZone/DropZone.tsx"],"sourcesContent":["'use client'\n\nimport {\n type ChangeEvent,\n type ComponentPropsWithRef,\n type DragEvent,\n type PropsWithChildren,\n forwardRef,\n memo,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n} from 'react'\nimport { tv } from 'tailwind-variants'\n\nimport { useLatest } from '../../hooks/useLatest'\nimport { Localizer } from '../../intl'\nimport { Button } from '../Button'\nimport { FaFolderOpenIcon } from '../Icon'\nimport { VisuallyHiddenText } from '../VisuallyHiddenText'\n\nconst classNameGenerator = tv({\n slots: {\n wrapper: [\n 'smarthr-ui-DropZone',\n 'shr-relative',\n 'shr-border-shorthand shr-flex shr-flex-col shr-items-center shr-justify-center shr-bg-column shr-p-2.5',\n 'has-[.smarthr-ui-DropZone-Button[aria-disabled]]:shr-cursor-not-allowed',\n '[&:not([data-files-dragged-over])]:shr-border-dashed',\n 'data-[files-dragged-over]:shr-border-main',\n ],\n button: ['smarthr-ui-DropZone-Button', 'data-[error]:shr-border-danger'],\n },\n})\n\ntype BaseProps = PropsWithChildren<{\n /**\n * ボタンまたはドラッグ&ドロップでファイルが追加された時に発火するコールバック関数\n */\n onSelectFiles: (\n e: DragEvent<HTMLElement> | ChangeEvent<HTMLInputElement>,\n files: FileList | null,\n ) => void\n /**\n * 許可するファイル型を表す1つ以上の固有ファイル型指定子\n * <b>(ドラッグ&ドロップの挙動には影響しません)</b>\n */\n accept?: string\n /** 複数ファイルを選択できるかどうか */\n multiple?: boolean\n name?: string\n disabled?: boolean\n /** フォームにエラーがあるかどうか */\n error?: boolean\n /** ファイル選択ボタンのラベル */\n selectButtonLabel?: string\n}>\ntype Props = BaseProps & Omit<ComponentPropsWithRef<'div'>, keyof BaseProps>\n\nconst overrideEventDefault = (e: DragEvent<HTMLElement>) => {\n e.preventDefault()\n e.stopPropagation()\n}\n\nexport const DropZone = forwardRef<HTMLInputElement, Props>(\n (\n {\n children,\n onSelectFiles,\n multiple = true,\n disabled,\n error,\n selectButtonLabel,\n className,\n ...rest\n },\n ref,\n ) => {\n const fileRef = useRef<HTMLInputElement>(null)\n const [filesDraggedOver, setFilesDraggedOver] = useState(false)\n\n const classNames = useMemo(() => {\n const { wrapper, button } = classNameGenerator()\n return {\n wrapper: wrapper({ className }),\n button: button(),\n }\n }, [className])\n\n const latest = useLatest({ onSelectFiles })\n\n const functions = useMemo(\n () => ({\n handleDrop: (e: DragEvent<HTMLElement>) => {\n overrideEventDefault(e)\n setFilesDraggedOver(false)\n\n if (e.dataTransfer.types.includes('Files')) {\n if (fileRef.current) {\n fileRef.current.files = e.dataTransfer.files\n }\n latest.onSelectFiles(e, e.dataTransfer.files)\n }\n },\n handleDragOver: (e: DragEvent<HTMLElement>) => {\n overrideEventDefault(e)\n setFilesDraggedOver(true)\n },\n handleDragLeave: () => {\n setFilesDraggedOver(false)\n },\n handleChange: (e: ChangeEvent<HTMLInputElement>) => {\n latest.onSelectFiles(e, e.target.files)\n },\n handleClickButton: () => {\n fileRef.current!.click()\n },\n }),\n [latest],\n )\n\n useImperativeHandle<HTMLInputElement | null, HTMLInputElement | null>(\n ref,\n () => fileRef.current,\n [],\n )\n\n return (\n // eslint-disable-next-line jsx-a11y/no-static-element-interactions\n <div\n onDrop={functions.handleDrop}\n onDragOver={functions.handleDragOver}\n onDragLeave={functions.handleDragLeave}\n className={classNames.wrapper}\n data-files-dragged-over={filesDraggedOver || undefined}\n >\n {children}\n <SelectButton\n label={selectButtonLabel}\n disabled={disabled}\n error={error}\n handleClick={functions.handleClickButton}\n className={classNames.button}\n />\n <VisuallyHiddenText>\n {/* TODO: この input にアクセシブルネームが設定されていない。VisuallyHiddenText で視覚的に隠されているが aria-hidden ではないためアクセシビリティツリーに残る。aria-label 等で適切なラベルを付与する必要がある */}\n {/* eslint-disable-next-line smarthr/a11y-input-in-form-control */}\n <input\n {...rest}\n ref={fileRef}\n type=\"file\"\n multiple={multiple}\n disabled={disabled}\n tabIndex={-1}\n aria-invalid={error || undefined}\n onChange={functions.handleChange}\n data-smarthr-ui-input=\"true\"\n />\n </VisuallyHiddenText>\n </div>\n )\n },\n)\n\nconst SelectButton = memo<{\n label?: string\n disabled?: boolean\n error?: boolean\n handleClick: () => void\n className: string\n}>(({ label, disabled, error, handleClick, className }) => (\n <Button\n prefix={<FaFolderOpenIcon />}\n disabled={disabled}\n data-error={error || undefined}\n onClick={handleClick}\n className={className}\n >\n {label || <Localizer id=\"smarthr-ui/DropZone/selectButtonLabel\" defaultText=\"ファイルを選択\" />}\n </Button>\n))\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;AAsBA;AACE;AACE;;;;;;;AAOC;AACD;AACD;AACF;AA0BD;;;AAGA;AAEO;AAcH;;AAGA;;;AAGI;;;AAGJ;;AAIA;AAEI;;;;AAKI;;;;;;AAMJ;;;;;;;AAOA;;;;AAIE;;AAEH;AAIH;;;;AAwCF;AAGF;;"}
|
|
@@ -69,7 +69,7 @@ const DropZone = forwardRef(({ children, onSelectFiles, multiple = true, disable
|
|
|
69
69
|
fileRef.current.click();
|
|
70
70
|
},
|
|
71
71
|
}), [latest]);
|
|
72
|
-
useImperativeHandle(ref, () => fileRef.current);
|
|
72
|
+
useImperativeHandle(ref, () => fileRef.current, []);
|
|
73
73
|
return (
|
|
74
74
|
// eslint-disable-next-line jsx-a11y/no-static-element-interactions
|
|
75
75
|
jsxs("div", { onDrop: functions.handleDrop, onDragOver: functions.handleDragOver, onDragLeave: functions.handleDragLeave, className: classNames.wrapper, "data-files-dragged-over": filesDraggedOver || undefined, children: [children, jsx(SelectButton, { label: selectButtonLabel, disabled: disabled, error: error, handleClick: functions.handleClickButton, className: classNames.button }), jsx(VisuallyHiddenText, { children: jsx("input", { ...rest, ref: fileRef, type: "file", multiple: multiple, disabled: disabled, tabIndex: -1, "aria-invalid": error || undefined, onChange: functions.handleChange, "data-smarthr-ui-input": "true" }) })] }));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DropZone.js","sources":["../../../src/components/DropZone/DropZone.tsx"],"sourcesContent":["'use client'\n\nimport {\n type ChangeEvent,\n type ComponentPropsWithRef,\n type DragEvent,\n type PropsWithChildren,\n forwardRef,\n memo,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n} from 'react'\nimport { tv } from 'tailwind-variants'\n\nimport { useLatest } from '../../hooks/useLatest'\nimport { Localizer } from '../../intl'\nimport { Button } from '../Button'\nimport { FaFolderOpenIcon } from '../Icon'\nimport { VisuallyHiddenText } from '../VisuallyHiddenText'\n\nconst classNameGenerator = tv({\n slots: {\n wrapper: [\n 'smarthr-ui-DropZone',\n 'shr-relative',\n 'shr-border-shorthand shr-flex shr-flex-col shr-items-center shr-justify-center shr-bg-column shr-p-2.5',\n 'has-[.smarthr-ui-DropZone-Button[aria-disabled]]:shr-cursor-not-allowed',\n '[&:not([data-files-dragged-over])]:shr-border-dashed',\n 'data-[files-dragged-over]:shr-border-main',\n ],\n button: ['smarthr-ui-DropZone-Button', 'data-[error]:shr-border-danger'],\n },\n})\n\ntype BaseProps = PropsWithChildren<{\n /**\n * ボタンまたはドラッグ&ドロップでファイルが追加された時に発火するコールバック関数\n */\n onSelectFiles: (\n e: DragEvent<HTMLElement> | ChangeEvent<HTMLInputElement>,\n files: FileList | null,\n ) => void\n /**\n * 許可するファイル型を表す1つ以上の固有ファイル型指定子\n * <b>(ドラッグ&ドロップの挙動には影響しません)</b>\n */\n accept?: string\n /** 複数ファイルを選択できるかどうか */\n multiple?: boolean\n name?: string\n disabled?: boolean\n /** フォームにエラーがあるかどうか */\n error?: boolean\n /** ファイル選択ボタンのラベル */\n selectButtonLabel?: string\n}>\ntype Props = BaseProps & Omit<ComponentPropsWithRef<'div'>, keyof BaseProps>\n\nconst overrideEventDefault = (e: DragEvent<HTMLElement>) => {\n e.preventDefault()\n e.stopPropagation()\n}\n\nexport const DropZone = forwardRef<HTMLInputElement, Props>(\n (\n {\n children,\n onSelectFiles,\n multiple = true,\n disabled,\n error,\n selectButtonLabel,\n className,\n ...rest\n },\n ref,\n ) => {\n const fileRef = useRef<HTMLInputElement>(null)\n const [filesDraggedOver, setFilesDraggedOver] = useState(false)\n\n const classNames = useMemo(() => {\n const { wrapper, button } = classNameGenerator()\n return {\n wrapper: wrapper({ className }),\n button: button(),\n }\n }, [className])\n\n const latest = useLatest({ onSelectFiles })\n\n const functions = useMemo(\n () => ({\n handleDrop: (e: DragEvent<HTMLElement>) => {\n overrideEventDefault(e)\n setFilesDraggedOver(false)\n\n if (e.dataTransfer.types.includes('Files')) {\n if (fileRef.current) {\n fileRef.current.files = e.dataTransfer.files\n }\n latest.onSelectFiles(e, e.dataTransfer.files)\n }\n },\n handleDragOver: (e: DragEvent<HTMLElement>) => {\n overrideEventDefault(e)\n setFilesDraggedOver(true)\n },\n handleDragLeave: () => {\n setFilesDraggedOver(false)\n },\n handleChange: (e: ChangeEvent<HTMLInputElement>) => {\n latest.onSelectFiles(e, e.target.files)\n },\n handleClickButton: () => {\n fileRef.current!.click()\n },\n }),\n [latest],\n )\n\n useImperativeHandle<HTMLInputElement | null, HTMLInputElement | null>(\n ref,\n () => fileRef.current,\n )\n\n return (\n // eslint-disable-next-line jsx-a11y/no-static-element-interactions\n <div\n onDrop={functions.handleDrop}\n onDragOver={functions.handleDragOver}\n onDragLeave={functions.handleDragLeave}\n className={classNames.wrapper}\n data-files-dragged-over={filesDraggedOver || undefined}\n >\n {children}\n <SelectButton\n label={selectButtonLabel}\n disabled={disabled}\n error={error}\n handleClick={functions.handleClickButton}\n className={classNames.button}\n />\n <VisuallyHiddenText>\n {/* TODO: この input にアクセシブルネームが設定されていない。VisuallyHiddenText で視覚的に隠されているが aria-hidden ではないためアクセシビリティツリーに残る。aria-label 等で適切なラベルを付与する必要がある */}\n {/* eslint-disable-next-line smarthr/a11y-input-in-form-control */}\n <input\n {...rest}\n ref={fileRef}\n type=\"file\"\n multiple={multiple}\n disabled={disabled}\n tabIndex={-1}\n aria-invalid={error || undefined}\n onChange={functions.handleChange}\n data-smarthr-ui-input=\"true\"\n />\n </VisuallyHiddenText>\n </div>\n )\n },\n)\n\nconst SelectButton = memo<{\n label?: string\n disabled?: boolean\n error?: boolean\n handleClick: () => void\n className: string\n}>(({ label, disabled, error, handleClick, className }) => (\n <Button\n prefix={<FaFolderOpenIcon />}\n disabled={disabled}\n data-error={error || undefined}\n onClick={handleClick}\n className={className}\n >\n {label || <Localizer id=\"smarthr-ui/DropZone/selectButtonLabel\" defaultText=\"ファイルを選択\" />}\n </Button>\n))\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AAsBA;AACE;AACE;;;;;;;AAOC;AACD;AACD;AACF;AA0BD;;;AAGA;AAEO;AAcH;;AAGA;;;AAGI;;;AAGJ;;AAIA;AAEI;;;;AAKI;;;;;;AAMJ;;;;;;;AAOA;;;;AAIE;;AAEH
|
|
1
|
+
{"version":3,"file":"DropZone.js","sources":["../../../src/components/DropZone/DropZone.tsx"],"sourcesContent":["'use client'\n\nimport {\n type ChangeEvent,\n type ComponentPropsWithRef,\n type DragEvent,\n type PropsWithChildren,\n forwardRef,\n memo,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n} from 'react'\nimport { tv } from 'tailwind-variants'\n\nimport { useLatest } from '../../hooks/useLatest'\nimport { Localizer } from '../../intl'\nimport { Button } from '../Button'\nimport { FaFolderOpenIcon } from '../Icon'\nimport { VisuallyHiddenText } from '../VisuallyHiddenText'\n\nconst classNameGenerator = tv({\n slots: {\n wrapper: [\n 'smarthr-ui-DropZone',\n 'shr-relative',\n 'shr-border-shorthand shr-flex shr-flex-col shr-items-center shr-justify-center shr-bg-column shr-p-2.5',\n 'has-[.smarthr-ui-DropZone-Button[aria-disabled]]:shr-cursor-not-allowed',\n '[&:not([data-files-dragged-over])]:shr-border-dashed',\n 'data-[files-dragged-over]:shr-border-main',\n ],\n button: ['smarthr-ui-DropZone-Button', 'data-[error]:shr-border-danger'],\n },\n})\n\ntype BaseProps = PropsWithChildren<{\n /**\n * ボタンまたはドラッグ&ドロップでファイルが追加された時に発火するコールバック関数\n */\n onSelectFiles: (\n e: DragEvent<HTMLElement> | ChangeEvent<HTMLInputElement>,\n files: FileList | null,\n ) => void\n /**\n * 許可するファイル型を表す1つ以上の固有ファイル型指定子\n * <b>(ドラッグ&ドロップの挙動には影響しません)</b>\n */\n accept?: string\n /** 複数ファイルを選択できるかどうか */\n multiple?: boolean\n name?: string\n disabled?: boolean\n /** フォームにエラーがあるかどうか */\n error?: boolean\n /** ファイル選択ボタンのラベル */\n selectButtonLabel?: string\n}>\ntype Props = BaseProps & Omit<ComponentPropsWithRef<'div'>, keyof BaseProps>\n\nconst overrideEventDefault = (e: DragEvent<HTMLElement>) => {\n e.preventDefault()\n e.stopPropagation()\n}\n\nexport const DropZone = forwardRef<HTMLInputElement, Props>(\n (\n {\n children,\n onSelectFiles,\n multiple = true,\n disabled,\n error,\n selectButtonLabel,\n className,\n ...rest\n },\n ref,\n ) => {\n const fileRef = useRef<HTMLInputElement>(null)\n const [filesDraggedOver, setFilesDraggedOver] = useState(false)\n\n const classNames = useMemo(() => {\n const { wrapper, button } = classNameGenerator()\n return {\n wrapper: wrapper({ className }),\n button: button(),\n }\n }, [className])\n\n const latest = useLatest({ onSelectFiles })\n\n const functions = useMemo(\n () => ({\n handleDrop: (e: DragEvent<HTMLElement>) => {\n overrideEventDefault(e)\n setFilesDraggedOver(false)\n\n if (e.dataTransfer.types.includes('Files')) {\n if (fileRef.current) {\n fileRef.current.files = e.dataTransfer.files\n }\n latest.onSelectFiles(e, e.dataTransfer.files)\n }\n },\n handleDragOver: (e: DragEvent<HTMLElement>) => {\n overrideEventDefault(e)\n setFilesDraggedOver(true)\n },\n handleDragLeave: () => {\n setFilesDraggedOver(false)\n },\n handleChange: (e: ChangeEvent<HTMLInputElement>) => {\n latest.onSelectFiles(e, e.target.files)\n },\n handleClickButton: () => {\n fileRef.current!.click()\n },\n }),\n [latest],\n )\n\n useImperativeHandle<HTMLInputElement | null, HTMLInputElement | null>(\n ref,\n () => fileRef.current,\n [],\n )\n\n return (\n // eslint-disable-next-line jsx-a11y/no-static-element-interactions\n <div\n onDrop={functions.handleDrop}\n onDragOver={functions.handleDragOver}\n onDragLeave={functions.handleDragLeave}\n className={classNames.wrapper}\n data-files-dragged-over={filesDraggedOver || undefined}\n >\n {children}\n <SelectButton\n label={selectButtonLabel}\n disabled={disabled}\n error={error}\n handleClick={functions.handleClickButton}\n className={classNames.button}\n />\n <VisuallyHiddenText>\n {/* TODO: この input にアクセシブルネームが設定されていない。VisuallyHiddenText で視覚的に隠されているが aria-hidden ではないためアクセシビリティツリーに残る。aria-label 等で適切なラベルを付与する必要がある */}\n {/* eslint-disable-next-line smarthr/a11y-input-in-form-control */}\n <input\n {...rest}\n ref={fileRef}\n type=\"file\"\n multiple={multiple}\n disabled={disabled}\n tabIndex={-1}\n aria-invalid={error || undefined}\n onChange={functions.handleChange}\n data-smarthr-ui-input=\"true\"\n />\n </VisuallyHiddenText>\n </div>\n )\n },\n)\n\nconst SelectButton = memo<{\n label?: string\n disabled?: boolean\n error?: boolean\n handleClick: () => void\n className: string\n}>(({ label, disabled, error, handleClick, className }) => (\n <Button\n prefix={<FaFolderOpenIcon />}\n disabled={disabled}\n data-error={error || undefined}\n onClick={handleClick}\n className={className}\n >\n {label || <Localizer id=\"smarthr-ui/DropZone/selectButtonLabel\" defaultText=\"ファイルを選択\" />}\n </Button>\n))\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AAsBA;AACE;AACE;;;;;;;AAOC;AACD;AACD;AACF;AA0BD;;;AAGA;AAEO;AAcH;;AAGA;;;AAGI;;;AAGJ;;AAIA;AAEI;;;;AAKI;;;;;;AAMJ;;;;;;;AAOA;;;;AAIE;;AAEH;AAIH;;;;AAwCF;AAGF;;"}
|
|
@@ -24,7 +24,7 @@ const AutoPageTitleHeading = ({ pageTitleSuffix, pageTitle, outerRef, children,
|
|
|
24
24
|
const pseudoTitleId = React.useId();
|
|
25
25
|
// HINT: h1のテキストをMutationObserverで監視するために内部でrefを保持しつつ、利用者のrefにも要素を渡す
|
|
26
26
|
const innerRef = React.useRef(null);
|
|
27
|
-
React.useImperativeHandle(outerRef, () => innerRef.current);
|
|
27
|
+
React.useImperativeHandle(outerRef, () => innerRef.current, []);
|
|
28
28
|
React.useEffect(() => {
|
|
29
29
|
const h1 = innerRef.current;
|
|
30
30
|
if (!h1)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PageHeading.cjs","sources":["../../../../src/components/Heading/PageHeading/PageHeading.tsx"],"sourcesContent":["'use client'\n\nimport {\n type FC,\n type ForwardedRef,\n type PropsWithChildren,\n type ReactNode,\n type Ref,\n forwardRef,\n memo,\n useEffect,\n useId,\n useImperativeHandle,\n useMemo,\n useRef,\n} from 'react'\nimport { tv } from 'tailwind-variants'\n\nimport { IS_NEXT_JS } from '../../../libs/nextjs'\nimport { STYLE_TYPE_MAP, Text, type TextProps } from '../../Text'\nimport { VisuallyHiddenText, visuallyHiddenTextClassName } from '../../VisuallyHiddenText'\n\nimport type { ElementProps } from '../Heading'\n\nexport type BaseProps = PropsWithChildren<{\n /**\n * テキストのサイズ\n *\n * @default 'XL'\n */\n size?: Extract<TextProps['size'], 'XXL' | 'XL' | 'L'>\n /** 視覚的に非表示にするフラグ */\n visuallyHidden?: boolean\n /**\n * title要素の自動生成フラグ\n *\n * Next.js 環境ではこの値にかかわらずtitleは自動生成されません。metadataなどの方法を利用してください。\n */\n autoPageTitle?: boolean\n /** title要素のprefix */\n pageTitle?: string\n /** title要素のsuffix */\n pageTitleSuffix?: string\n}>\ntype Props = BaseProps & Omit<ElementProps, keyof BaseProps>\n\nconst classNameGenerator = tv({\n base: 'smarthr-ui-Heading smarthr-ui-PageHeading',\n variants: {\n visuallyHidden: {\n false: 'shr-m-[unset]',\n },\n },\n defaultVariants: {\n visuallyHidden: false,\n },\n})\n\nexport const PageHeading = memo(\n forwardRef<HTMLHeadingElement, Props>(\n (\n {\n autoPageTitle = true,\n pageTitleSuffix = 'SmartHR(スマートHR)',\n pageTitle,\n size = 'XL',\n children,\n ...rest\n },\n ref,\n ) =>\n !IS_NEXT_JS && autoPageTitle ? (\n <AutoPageTitleHeading\n {...rest}\n size={size}\n pageTitleSuffix={pageTitleSuffix}\n pageTitle={pageTitle}\n outerRef={ref}\n >\n {children}\n </AutoPageTitleHeading>\n ) : (\n <ActualHeading {...rest} size={size} headingRef={ref}>\n {children}\n </ActualHeading>\n ),\n ),\n)\n\nconst AutoPageTitleHeading: FC<\n Omit<Props, 'size' | 'autoPageTitle' | 'pageTitleSuffix' | 'ref'> & {\n size: TextProps['size']\n pageTitleSuffix: string\n outerRef?: ForwardedRef<HTMLHeadingElement>\n }\n> = ({ pageTitleSuffix, pageTitle, outerRef, children, ...rest }) => {\n const pseudoTitleId = useId()\n // HINT: h1のテキストをMutationObserverで監視するために内部でrefを保持しつつ、利用者のrefにも要素を渡す\n const innerRef = useRef<HTMLHeadingElement | null>(null)\n\n useImperativeHandle<HTMLHeadingElement | null, HTMLHeadingElement | null>(\n outerRef,\n () => innerRef.current,\n )\n\n useEffect(() => {\n const h1 = innerRef.current\n if (!h1) return\n\n const updateTitle = () => {\n const title = pageTitle || h1.textContent || ''\n document.title = pageTitleSuffix ? `${title}|${pageTitleSuffix}` : title\n\n // HINT: SPAで遷移する場合などの対策としてbody直下にaria-liveを仕込む\n // head内はスクリーンリーダーの変更検知のチェック対象外のため、title要素にaria-liveは設定しない\n const pseudoTitle: HTMLDivElement = (document.getElementById(pseudoTitleId) ||\n document.createElement('div')) as HTMLDivElement\n\n pseudoTitle.setAttribute('id', pseudoTitleId)\n pseudoTitle.setAttribute('class', visuallyHiddenTextClassName)\n pseudoTitle.setAttribute('aria-live', 'polite')\n document.body.prepend(pseudoTitle)\n\n requestAnimationFrame(() => {\n pseudoTitle.textContent = document.title\n })\n }\n\n updateTitle()\n\n const observer = new MutationObserver(updateTitle)\n observer.observe(h1, {\n characterData: true,\n childList: true,\n subtree: true,\n })\n\n return () => {\n observer.disconnect()\n const pseudoTitle = document.getElementById(pseudoTitleId)\n if (pseudoTitle) {\n pseudoTitle.remove()\n }\n }\n }, [pageTitle, pageTitleSuffix, pseudoTitleId])\n\n return (\n <ActualHeading {...rest} headingRef={innerRef}>\n {children}\n </ActualHeading>\n )\n}\n\ntype ActualHeadingProps = {\n visuallyHidden?: boolean\n size: TextProps['size']\n className?: string\n children: ReactNode\n headingRef?: Ref<HTMLHeadingElement>\n} & Omit<ElementProps, 'size' | 'className' | 'visuallyHidden' | 'children' | 'ref'>\n\nconst ActualHeading: FC<ActualHeadingProps> = ({\n visuallyHidden,\n size,\n className,\n children,\n headingRef,\n ...rest\n}) => {\n const actualClassName = useMemo(\n () => classNameGenerator({ visuallyHidden, className }),\n [className, visuallyHidden],\n )\n\n const Component = visuallyHidden ? VisuallyHiddenText : Text\n return (\n <Component\n {...rest}\n {...STYLE_TYPE_MAP.screenTitle}\n size={size || STYLE_TYPE_MAP.screenTitle.size}\n as=\"h1\"\n className={actualClassName}\n ref={headingRef}\n >\n {children}\n </Component>\n )\n}\n"],"names":[],"mappings":";;;;;;;;;;AA8CA;AACE;AACA;AACE;AACE;AACD;AACF;AACD;AACE;AACD;AACF;AAEM;AA+BP;AAOE;;AAEA
|
|
1
|
+
{"version":3,"file":"PageHeading.cjs","sources":["../../../../src/components/Heading/PageHeading/PageHeading.tsx"],"sourcesContent":["'use client'\n\nimport {\n type FC,\n type ForwardedRef,\n type PropsWithChildren,\n type ReactNode,\n type Ref,\n forwardRef,\n memo,\n useEffect,\n useId,\n useImperativeHandle,\n useMemo,\n useRef,\n} from 'react'\nimport { tv } from 'tailwind-variants'\n\nimport { IS_NEXT_JS } from '../../../libs/nextjs'\nimport { STYLE_TYPE_MAP, Text, type TextProps } from '../../Text'\nimport { VisuallyHiddenText, visuallyHiddenTextClassName } from '../../VisuallyHiddenText'\n\nimport type { ElementProps } from '../Heading'\n\nexport type BaseProps = PropsWithChildren<{\n /**\n * テキストのサイズ\n *\n * @default 'XL'\n */\n size?: Extract<TextProps['size'], 'XXL' | 'XL' | 'L'>\n /** 視覚的に非表示にするフラグ */\n visuallyHidden?: boolean\n /**\n * title要素の自動生成フラグ\n *\n * Next.js 環境ではこの値にかかわらずtitleは自動生成されません。metadataなどの方法を利用してください。\n */\n autoPageTitle?: boolean\n /** title要素のprefix */\n pageTitle?: string\n /** title要素のsuffix */\n pageTitleSuffix?: string\n}>\ntype Props = BaseProps & Omit<ElementProps, keyof BaseProps>\n\nconst classNameGenerator = tv({\n base: 'smarthr-ui-Heading smarthr-ui-PageHeading',\n variants: {\n visuallyHidden: {\n false: 'shr-m-[unset]',\n },\n },\n defaultVariants: {\n visuallyHidden: false,\n },\n})\n\nexport const PageHeading = memo(\n forwardRef<HTMLHeadingElement, Props>(\n (\n {\n autoPageTitle = true,\n pageTitleSuffix = 'SmartHR(スマートHR)',\n pageTitle,\n size = 'XL',\n children,\n ...rest\n },\n ref,\n ) =>\n !IS_NEXT_JS && autoPageTitle ? (\n <AutoPageTitleHeading\n {...rest}\n size={size}\n pageTitleSuffix={pageTitleSuffix}\n pageTitle={pageTitle}\n outerRef={ref}\n >\n {children}\n </AutoPageTitleHeading>\n ) : (\n <ActualHeading {...rest} size={size} headingRef={ref}>\n {children}\n </ActualHeading>\n ),\n ),\n)\n\nconst AutoPageTitleHeading: FC<\n Omit<Props, 'size' | 'autoPageTitle' | 'pageTitleSuffix' | 'ref'> & {\n size: TextProps['size']\n pageTitleSuffix: string\n outerRef?: ForwardedRef<HTMLHeadingElement>\n }\n> = ({ pageTitleSuffix, pageTitle, outerRef, children, ...rest }) => {\n const pseudoTitleId = useId()\n // HINT: h1のテキストをMutationObserverで監視するために内部でrefを保持しつつ、利用者のrefにも要素を渡す\n const innerRef = useRef<HTMLHeadingElement | null>(null)\n\n useImperativeHandle<HTMLHeadingElement | null, HTMLHeadingElement | null>(\n outerRef,\n () => innerRef.current,\n [],\n )\n\n useEffect(() => {\n const h1 = innerRef.current\n if (!h1) return\n\n const updateTitle = () => {\n const title = pageTitle || h1.textContent || ''\n document.title = pageTitleSuffix ? `${title}|${pageTitleSuffix}` : title\n\n // HINT: SPAで遷移する場合などの対策としてbody直下にaria-liveを仕込む\n // head内はスクリーンリーダーの変更検知のチェック対象外のため、title要素にaria-liveは設定しない\n const pseudoTitle: HTMLDivElement = (document.getElementById(pseudoTitleId) ||\n document.createElement('div')) as HTMLDivElement\n\n pseudoTitle.setAttribute('id', pseudoTitleId)\n pseudoTitle.setAttribute('class', visuallyHiddenTextClassName)\n pseudoTitle.setAttribute('aria-live', 'polite')\n document.body.prepend(pseudoTitle)\n\n requestAnimationFrame(() => {\n pseudoTitle.textContent = document.title\n })\n }\n\n updateTitle()\n\n const observer = new MutationObserver(updateTitle)\n observer.observe(h1, {\n characterData: true,\n childList: true,\n subtree: true,\n })\n\n return () => {\n observer.disconnect()\n const pseudoTitle = document.getElementById(pseudoTitleId)\n if (pseudoTitle) {\n pseudoTitle.remove()\n }\n }\n }, [pageTitle, pageTitleSuffix, pseudoTitleId])\n\n return (\n <ActualHeading {...rest} headingRef={innerRef}>\n {children}\n </ActualHeading>\n )\n}\n\ntype ActualHeadingProps = {\n visuallyHidden?: boolean\n size: TextProps['size']\n className?: string\n children: ReactNode\n headingRef?: Ref<HTMLHeadingElement>\n} & Omit<ElementProps, 'size' | 'className' | 'visuallyHidden' | 'children' | 'ref'>\n\nconst ActualHeading: FC<ActualHeadingProps> = ({\n visuallyHidden,\n size,\n className,\n children,\n headingRef,\n ...rest\n}) => {\n const actualClassName = useMemo(\n () => classNameGenerator({ visuallyHidden, className }),\n [className, visuallyHidden],\n )\n\n const Component = visuallyHidden ? VisuallyHiddenText : Text\n return (\n <Component\n {...rest}\n {...STYLE_TYPE_MAP.screenTitle}\n size={size || STYLE_TYPE_MAP.screenTitle.size}\n as=\"h1\"\n className={actualClassName}\n ref={headingRef}\n >\n {children}\n </Component>\n )\n}\n"],"names":[],"mappings":";;;;;;;;;;AA8CA;AACE;AACA;AACE;AACE;AACD;AACF;AACD;AACE;AACD;AACF;AAEM;AA+BP;AAOE;;AAEA;AAEA;;AAOE;AACA;;;;AAIE;;;;AAKE;AAEF;AACA;AACA;AACA;;AAGE;AACF;AACF;AAEA;AAEA;AACA;AACE;AACA;AACA;AACD;AAED;;;;;;AAMA;;AAGF;AAKF;AAUA;;;AAcE;AAYF;;"}
|
|
@@ -22,7 +22,7 @@ const AutoPageTitleHeading = ({ pageTitleSuffix, pageTitle, outerRef, children,
|
|
|
22
22
|
const pseudoTitleId = useId();
|
|
23
23
|
// HINT: h1のテキストをMutationObserverで監視するために内部でrefを保持しつつ、利用者のrefにも要素を渡す
|
|
24
24
|
const innerRef = useRef(null);
|
|
25
|
-
useImperativeHandle(outerRef, () => innerRef.current);
|
|
25
|
+
useImperativeHandle(outerRef, () => innerRef.current, []);
|
|
26
26
|
useEffect(() => {
|
|
27
27
|
const h1 = innerRef.current;
|
|
28
28
|
if (!h1)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PageHeading.js","sources":["../../../../src/components/Heading/PageHeading/PageHeading.tsx"],"sourcesContent":["'use client'\n\nimport {\n type FC,\n type ForwardedRef,\n type PropsWithChildren,\n type ReactNode,\n type Ref,\n forwardRef,\n memo,\n useEffect,\n useId,\n useImperativeHandle,\n useMemo,\n useRef,\n} from 'react'\nimport { tv } from 'tailwind-variants'\n\nimport { IS_NEXT_JS } from '../../../libs/nextjs'\nimport { STYLE_TYPE_MAP, Text, type TextProps } from '../../Text'\nimport { VisuallyHiddenText, visuallyHiddenTextClassName } from '../../VisuallyHiddenText'\n\nimport type { ElementProps } from '../Heading'\n\nexport type BaseProps = PropsWithChildren<{\n /**\n * テキストのサイズ\n *\n * @default 'XL'\n */\n size?: Extract<TextProps['size'], 'XXL' | 'XL' | 'L'>\n /** 視覚的に非表示にするフラグ */\n visuallyHidden?: boolean\n /**\n * title要素の自動生成フラグ\n *\n * Next.js 環境ではこの値にかかわらずtitleは自動生成されません。metadataなどの方法を利用してください。\n */\n autoPageTitle?: boolean\n /** title要素のprefix */\n pageTitle?: string\n /** title要素のsuffix */\n pageTitleSuffix?: string\n}>\ntype Props = BaseProps & Omit<ElementProps, keyof BaseProps>\n\nconst classNameGenerator = tv({\n base: 'smarthr-ui-Heading smarthr-ui-PageHeading',\n variants: {\n visuallyHidden: {\n false: 'shr-m-[unset]',\n },\n },\n defaultVariants: {\n visuallyHidden: false,\n },\n})\n\nexport const PageHeading = memo(\n forwardRef<HTMLHeadingElement, Props>(\n (\n {\n autoPageTitle = true,\n pageTitleSuffix = 'SmartHR(スマートHR)',\n pageTitle,\n size = 'XL',\n children,\n ...rest\n },\n ref,\n ) =>\n !IS_NEXT_JS && autoPageTitle ? (\n <AutoPageTitleHeading\n {...rest}\n size={size}\n pageTitleSuffix={pageTitleSuffix}\n pageTitle={pageTitle}\n outerRef={ref}\n >\n {children}\n </AutoPageTitleHeading>\n ) : (\n <ActualHeading {...rest} size={size} headingRef={ref}>\n {children}\n </ActualHeading>\n ),\n ),\n)\n\nconst AutoPageTitleHeading: FC<\n Omit<Props, 'size' | 'autoPageTitle' | 'pageTitleSuffix' | 'ref'> & {\n size: TextProps['size']\n pageTitleSuffix: string\n outerRef?: ForwardedRef<HTMLHeadingElement>\n }\n> = ({ pageTitleSuffix, pageTitle, outerRef, children, ...rest }) => {\n const pseudoTitleId = useId()\n // HINT: h1のテキストをMutationObserverで監視するために内部でrefを保持しつつ、利用者のrefにも要素を渡す\n const innerRef = useRef<HTMLHeadingElement | null>(null)\n\n useImperativeHandle<HTMLHeadingElement | null, HTMLHeadingElement | null>(\n outerRef,\n () => innerRef.current,\n )\n\n useEffect(() => {\n const h1 = innerRef.current\n if (!h1) return\n\n const updateTitle = () => {\n const title = pageTitle || h1.textContent || ''\n document.title = pageTitleSuffix ? `${title}|${pageTitleSuffix}` : title\n\n // HINT: SPAで遷移する場合などの対策としてbody直下にaria-liveを仕込む\n // head内はスクリーンリーダーの変更検知のチェック対象外のため、title要素にaria-liveは設定しない\n const pseudoTitle: HTMLDivElement = (document.getElementById(pseudoTitleId) ||\n document.createElement('div')) as HTMLDivElement\n\n pseudoTitle.setAttribute('id', pseudoTitleId)\n pseudoTitle.setAttribute('class', visuallyHiddenTextClassName)\n pseudoTitle.setAttribute('aria-live', 'polite')\n document.body.prepend(pseudoTitle)\n\n requestAnimationFrame(() => {\n pseudoTitle.textContent = document.title\n })\n }\n\n updateTitle()\n\n const observer = new MutationObserver(updateTitle)\n observer.observe(h1, {\n characterData: true,\n childList: true,\n subtree: true,\n })\n\n return () => {\n observer.disconnect()\n const pseudoTitle = document.getElementById(pseudoTitleId)\n if (pseudoTitle) {\n pseudoTitle.remove()\n }\n }\n }, [pageTitle, pageTitleSuffix, pseudoTitleId])\n\n return (\n <ActualHeading {...rest} headingRef={innerRef}>\n {children}\n </ActualHeading>\n )\n}\n\ntype ActualHeadingProps = {\n visuallyHidden?: boolean\n size: TextProps['size']\n className?: string\n children: ReactNode\n headingRef?: Ref<HTMLHeadingElement>\n} & Omit<ElementProps, 'size' | 'className' | 'visuallyHidden' | 'children' | 'ref'>\n\nconst ActualHeading: FC<ActualHeadingProps> = ({\n visuallyHidden,\n size,\n className,\n children,\n headingRef,\n ...rest\n}) => {\n const actualClassName = useMemo(\n () => classNameGenerator({ visuallyHidden, className }),\n [className, visuallyHidden],\n )\n\n const Component = visuallyHidden ? VisuallyHiddenText : Text\n return (\n <Component\n {...rest}\n {...STYLE_TYPE_MAP.screenTitle}\n size={size || STYLE_TYPE_MAP.screenTitle.size}\n as=\"h1\"\n className={actualClassName}\n ref={headingRef}\n >\n {children}\n </Component>\n )\n}\n"],"names":[],"mappings":";;;;;;;;AA8CA;AACE;AACA;AACE;AACE;AACD;AACF;AACD;AACE;AACD;AACF;AAEM;AA+BP;AAOE;;AAEA
|
|
1
|
+
{"version":3,"file":"PageHeading.js","sources":["../../../../src/components/Heading/PageHeading/PageHeading.tsx"],"sourcesContent":["'use client'\n\nimport {\n type FC,\n type ForwardedRef,\n type PropsWithChildren,\n type ReactNode,\n type Ref,\n forwardRef,\n memo,\n useEffect,\n useId,\n useImperativeHandle,\n useMemo,\n useRef,\n} from 'react'\nimport { tv } from 'tailwind-variants'\n\nimport { IS_NEXT_JS } from '../../../libs/nextjs'\nimport { STYLE_TYPE_MAP, Text, type TextProps } from '../../Text'\nimport { VisuallyHiddenText, visuallyHiddenTextClassName } from '../../VisuallyHiddenText'\n\nimport type { ElementProps } from '../Heading'\n\nexport type BaseProps = PropsWithChildren<{\n /**\n * テキストのサイズ\n *\n * @default 'XL'\n */\n size?: Extract<TextProps['size'], 'XXL' | 'XL' | 'L'>\n /** 視覚的に非表示にするフラグ */\n visuallyHidden?: boolean\n /**\n * title要素の自動生成フラグ\n *\n * Next.js 環境ではこの値にかかわらずtitleは自動生成されません。metadataなどの方法を利用してください。\n */\n autoPageTitle?: boolean\n /** title要素のprefix */\n pageTitle?: string\n /** title要素のsuffix */\n pageTitleSuffix?: string\n}>\ntype Props = BaseProps & Omit<ElementProps, keyof BaseProps>\n\nconst classNameGenerator = tv({\n base: 'smarthr-ui-Heading smarthr-ui-PageHeading',\n variants: {\n visuallyHidden: {\n false: 'shr-m-[unset]',\n },\n },\n defaultVariants: {\n visuallyHidden: false,\n },\n})\n\nexport const PageHeading = memo(\n forwardRef<HTMLHeadingElement, Props>(\n (\n {\n autoPageTitle = true,\n pageTitleSuffix = 'SmartHR(スマートHR)',\n pageTitle,\n size = 'XL',\n children,\n ...rest\n },\n ref,\n ) =>\n !IS_NEXT_JS && autoPageTitle ? (\n <AutoPageTitleHeading\n {...rest}\n size={size}\n pageTitleSuffix={pageTitleSuffix}\n pageTitle={pageTitle}\n outerRef={ref}\n >\n {children}\n </AutoPageTitleHeading>\n ) : (\n <ActualHeading {...rest} size={size} headingRef={ref}>\n {children}\n </ActualHeading>\n ),\n ),\n)\n\nconst AutoPageTitleHeading: FC<\n Omit<Props, 'size' | 'autoPageTitle' | 'pageTitleSuffix' | 'ref'> & {\n size: TextProps['size']\n pageTitleSuffix: string\n outerRef?: ForwardedRef<HTMLHeadingElement>\n }\n> = ({ pageTitleSuffix, pageTitle, outerRef, children, ...rest }) => {\n const pseudoTitleId = useId()\n // HINT: h1のテキストをMutationObserverで監視するために内部でrefを保持しつつ、利用者のrefにも要素を渡す\n const innerRef = useRef<HTMLHeadingElement | null>(null)\n\n useImperativeHandle<HTMLHeadingElement | null, HTMLHeadingElement | null>(\n outerRef,\n () => innerRef.current,\n [],\n )\n\n useEffect(() => {\n const h1 = innerRef.current\n if (!h1) return\n\n const updateTitle = () => {\n const title = pageTitle || h1.textContent || ''\n document.title = pageTitleSuffix ? `${title}|${pageTitleSuffix}` : title\n\n // HINT: SPAで遷移する場合などの対策としてbody直下にaria-liveを仕込む\n // head内はスクリーンリーダーの変更検知のチェック対象外のため、title要素にaria-liveは設定しない\n const pseudoTitle: HTMLDivElement = (document.getElementById(pseudoTitleId) ||\n document.createElement('div')) as HTMLDivElement\n\n pseudoTitle.setAttribute('id', pseudoTitleId)\n pseudoTitle.setAttribute('class', visuallyHiddenTextClassName)\n pseudoTitle.setAttribute('aria-live', 'polite')\n document.body.prepend(pseudoTitle)\n\n requestAnimationFrame(() => {\n pseudoTitle.textContent = document.title\n })\n }\n\n updateTitle()\n\n const observer = new MutationObserver(updateTitle)\n observer.observe(h1, {\n characterData: true,\n childList: true,\n subtree: true,\n })\n\n return () => {\n observer.disconnect()\n const pseudoTitle = document.getElementById(pseudoTitleId)\n if (pseudoTitle) {\n pseudoTitle.remove()\n }\n }\n }, [pageTitle, pageTitleSuffix, pseudoTitleId])\n\n return (\n <ActualHeading {...rest} headingRef={innerRef}>\n {children}\n </ActualHeading>\n )\n}\n\ntype ActualHeadingProps = {\n visuallyHidden?: boolean\n size: TextProps['size']\n className?: string\n children: ReactNode\n headingRef?: Ref<HTMLHeadingElement>\n} & Omit<ElementProps, 'size' | 'className' | 'visuallyHidden' | 'children' | 'ref'>\n\nconst ActualHeading: FC<ActualHeadingProps> = ({\n visuallyHidden,\n size,\n className,\n children,\n headingRef,\n ...rest\n}) => {\n const actualClassName = useMemo(\n () => classNameGenerator({ visuallyHidden, className }),\n [className, visuallyHidden],\n )\n\n const Component = visuallyHidden ? VisuallyHiddenText : Text\n return (\n <Component\n {...rest}\n {...STYLE_TYPE_MAP.screenTitle}\n size={size || STYLE_TYPE_MAP.screenTitle.size}\n as=\"h1\"\n className={actualClassName}\n ref={headingRef}\n >\n {children}\n </Component>\n )\n}\n"],"names":[],"mappings":";;;;;;;;AA8CA;AACE;AACA;AACE;AACE;AACD;AACF;AACD;AACE;AACD;AACF;AAEM;AA+BP;AAOE;;AAEA;AAEA;;AAOE;AACA;;;;AAIE;;;;AAKE;AAEF;AACA;AACA;AACA;;AAGE;AACF;AACF;AAEA;AAEA;AACA;AACE;AACA;AACA;AACD;AAED;;;;;;AAMA;;AAGF;AAKF;AAUA;;;AAcE;AAYF;;"}
|
|
@@ -7,7 +7,7 @@ var hooks_useLatest = require('../../../hooks/useLatest.cjs');
|
|
|
7
7
|
var components_Input_Input = require('../Input.cjs');
|
|
8
8
|
var components_Input_CurrencyInput_currencyInputHelper = require('./currencyInputHelper.cjs');
|
|
9
9
|
|
|
10
|
-
const CurrencyInput = React.forwardRef(({ onFormatValue, onFocus, onBlur, value, defaultValue, className
|
|
10
|
+
const CurrencyInput = React.forwardRef(({ onFormatValue, onFocus, onBlur, value, defaultValue, className, ...rest }, ref) => {
|
|
11
11
|
const innerRef = React.useRef(null);
|
|
12
12
|
const [isFocused, setIsFocused] = React.useState(false);
|
|
13
13
|
const latest = hooks_useLatest.useLatest({
|
|
@@ -40,7 +40,7 @@ const CurrencyInput = React.forwardRef(({ onFormatValue, onFocus, onBlur, value,
|
|
|
40
40
|
},
|
|
41
41
|
};
|
|
42
42
|
}, [latest]);
|
|
43
|
-
React.useImperativeHandle(ref, () => innerRef.current);
|
|
43
|
+
React.useImperativeHandle(ref, () => innerRef.current, []);
|
|
44
44
|
React.useEffect(() => {
|
|
45
45
|
if (value === undefined && defaultValue !== undefined) {
|
|
46
46
|
functions.formatCurrencyValue(defaultValue);
|
|
@@ -60,7 +60,7 @@ const CurrencyInput = React.forwardRef(({ onFormatValue, onFocus, onBlur, value,
|
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
}, [isFocused, value, functions]);
|
|
63
|
-
return (jsxRuntime.jsx(components_Input_Input.Input, { ...rest, type: "text", value: value, defaultValue: defaultValue, onFocus: functions.handleFocus, onBlur: functions.handleBlur,
|
|
63
|
+
return (jsxRuntime.jsx(components_Input_Input.Input, { ...rest, ref: innerRef, type: "text", value: value, defaultValue: defaultValue, onFocus: functions.handleFocus, onBlur: functions.handleBlur, className: `smarthr-ui-CurrencyInput${className ? ` ${className}` : ''}` }));
|
|
64
64
|
});
|
|
65
65
|
|
|
66
66
|
exports.CurrencyInput = CurrencyInput;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CurrencyInput.cjs","sources":["../../../../src/components/Input/CurrencyInput/CurrencyInput.tsx"],"sourcesContent":["'use client'\n\nimport {\n type ComponentProps,\n type FocusEvent,\n forwardRef,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n} from 'react'\n\nimport { useLatest } from '../../../hooks/useLatest'\nimport { Input } from '../Input'\n\nimport { formatCurrency } from './currencyInputHelper'\n\ntype Props = Omit<ComponentProps<typeof Input>, 'type' | 'value' | 'defaultValue'> & {\n /** 通貨の値 */\n value?: string\n /** デフォルトで表示する通貨の値 */\n defaultValue?: string\n /** 入力値がフォーマットされたときに発火するコールバック関数 */\n onFormatValue?: (value: string) => void\n}\n\nexport const CurrencyInput = forwardRef<HTMLInputElement, Props>(\n ({ onFormatValue, onFocus, onBlur, value, defaultValue, className
|
|
1
|
+
{"version":3,"file":"CurrencyInput.cjs","sources":["../../../../src/components/Input/CurrencyInput/CurrencyInput.tsx"],"sourcesContent":["'use client'\n\nimport {\n type ComponentProps,\n type FocusEvent,\n forwardRef,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n} from 'react'\n\nimport { useLatest } from '../../../hooks/useLatest'\nimport { Input } from '../Input'\n\nimport { formatCurrency } from './currencyInputHelper'\n\ntype Props = Omit<ComponentProps<typeof Input>, 'type' | 'value' | 'defaultValue'> & {\n /** 通貨の値 */\n value?: string\n /** デフォルトで表示する通貨の値 */\n defaultValue?: string\n /** 入力値がフォーマットされたときに発火するコールバック関数 */\n onFormatValue?: (value: string) => void\n}\n\nexport const CurrencyInput = forwardRef<HTMLInputElement, Props>(\n ({ onFormatValue, onFocus, onBlur, value, defaultValue, className, ...rest }, ref) => {\n const innerRef = useRef<HTMLInputElement>(null)\n const [isFocused, setIsFocused] = useState(false)\n\n const latest = useLatest({\n onFocus,\n onBlur,\n onFormatValue,\n })\n\n const functions = useMemo(() => {\n const formatValue = (formatted = '') => {\n if (innerRef.current && formatted !== innerRef.current.value) {\n innerRef.current.value = formatted\n latest.onFormatValue?.(formatted)\n }\n }\n\n return {\n formatCurrencyValue: (raw = '') => {\n formatValue(formatCurrency(raw))\n },\n handleFocus: (e: FocusEvent<HTMLInputElement>) => {\n setIsFocused(true)\n\n if (innerRef.current) {\n const commaExcluded = innerRef.current.value.replace(/,/g, '')\n formatValue(commaExcluded)\n }\n\n latest.onFocus?.(e)\n },\n handleBlur: (e: FocusEvent<HTMLInputElement>) => {\n setIsFocused(false)\n\n latest.onBlur?.(e)\n },\n }\n }, [latest])\n\n useImperativeHandle<HTMLInputElement | null, HTMLInputElement | null>(\n ref,\n () => innerRef.current,\n [],\n )\n\n useEffect(() => {\n if (value === undefined && defaultValue !== undefined) {\n functions.formatCurrencyValue(defaultValue)\n }\n // when component did mount\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n useEffect(() => {\n if (!isFocused) {\n if (value !== undefined) {\n // for controlled component\n functions.formatCurrencyValue(value)\n } else if (innerRef.current) {\n // for uncontrolled component\n functions.formatCurrencyValue(innerRef.current.value)\n }\n }\n }, [isFocused, value, functions])\n\n return (\n <Input\n {...rest}\n ref={innerRef}\n type=\"text\"\n value={value}\n defaultValue={defaultValue}\n onFocus={functions.handleFocus}\n onBlur={functions.handleBlur}\n className={`smarthr-ui-CurrencyInput${className ? ` ${className}` : ''}`}\n />\n )\n },\n)\n"],"names":[],"mappings":";;;;;;;;;AA2BO;AAEH;;;;;;AAOC;AAED;AACE;AACE;AACE;AACA;;AAEJ;;AAGE;AACE;;AAEF;;AAGE;AACE;;;AAIF;;AAEF;;AAGE;;;AAGN;AAEA;;;AAQI;;;;;;;AAQA;;AAEE;;AACK;;;;;;;AAmBb;;"}
|
|
@@ -5,7 +5,7 @@ import { useLatest } from '../../../hooks/useLatest.js';
|
|
|
5
5
|
import { Input } from '../Input.js';
|
|
6
6
|
import { formatCurrency } from './currencyInputHelper.js';
|
|
7
7
|
|
|
8
|
-
const CurrencyInput = forwardRef(({ onFormatValue, onFocus, onBlur, value, defaultValue, className
|
|
8
|
+
const CurrencyInput = forwardRef(({ onFormatValue, onFocus, onBlur, value, defaultValue, className, ...rest }, ref) => {
|
|
9
9
|
const innerRef = useRef(null);
|
|
10
10
|
const [isFocused, setIsFocused] = useState(false);
|
|
11
11
|
const latest = useLatest({
|
|
@@ -38,7 +38,7 @@ const CurrencyInput = forwardRef(({ onFormatValue, onFocus, onBlur, value, defau
|
|
|
38
38
|
},
|
|
39
39
|
};
|
|
40
40
|
}, [latest]);
|
|
41
|
-
useImperativeHandle(ref, () => innerRef.current);
|
|
41
|
+
useImperativeHandle(ref, () => innerRef.current, []);
|
|
42
42
|
useEffect(() => {
|
|
43
43
|
if (value === undefined && defaultValue !== undefined) {
|
|
44
44
|
functions.formatCurrencyValue(defaultValue);
|
|
@@ -58,7 +58,7 @@ const CurrencyInput = forwardRef(({ onFormatValue, onFocus, onBlur, value, defau
|
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
60
|
}, [isFocused, value, functions]);
|
|
61
|
-
return (jsx(Input, { ...rest, type: "text", value: value, defaultValue: defaultValue, onFocus: functions.handleFocus, onBlur: functions.handleBlur,
|
|
61
|
+
return (jsx(Input, { ...rest, ref: innerRef, type: "text", value: value, defaultValue: defaultValue, onFocus: functions.handleFocus, onBlur: functions.handleBlur, className: `smarthr-ui-CurrencyInput${className ? ` ${className}` : ''}` }));
|
|
62
62
|
});
|
|
63
63
|
|
|
64
64
|
export { CurrencyInput };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CurrencyInput.js","sources":["../../../../src/components/Input/CurrencyInput/CurrencyInput.tsx"],"sourcesContent":["'use client'\n\nimport {\n type ComponentProps,\n type FocusEvent,\n forwardRef,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n} from 'react'\n\nimport { useLatest } from '../../../hooks/useLatest'\nimport { Input } from '../Input'\n\nimport { formatCurrency } from './currencyInputHelper'\n\ntype Props = Omit<ComponentProps<typeof Input>, 'type' | 'value' | 'defaultValue'> & {\n /** 通貨の値 */\n value?: string\n /** デフォルトで表示する通貨の値 */\n defaultValue?: string\n /** 入力値がフォーマットされたときに発火するコールバック関数 */\n onFormatValue?: (value: string) => void\n}\n\nexport const CurrencyInput = forwardRef<HTMLInputElement, Props>(\n ({ onFormatValue, onFocus, onBlur, value, defaultValue, className
|
|
1
|
+
{"version":3,"file":"CurrencyInput.js","sources":["../../../../src/components/Input/CurrencyInput/CurrencyInput.tsx"],"sourcesContent":["'use client'\n\nimport {\n type ComponentProps,\n type FocusEvent,\n forwardRef,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n} from 'react'\n\nimport { useLatest } from '../../../hooks/useLatest'\nimport { Input } from '../Input'\n\nimport { formatCurrency } from './currencyInputHelper'\n\ntype Props = Omit<ComponentProps<typeof Input>, 'type' | 'value' | 'defaultValue'> & {\n /** 通貨の値 */\n value?: string\n /** デフォルトで表示する通貨の値 */\n defaultValue?: string\n /** 入力値がフォーマットされたときに発火するコールバック関数 */\n onFormatValue?: (value: string) => void\n}\n\nexport const CurrencyInput = forwardRef<HTMLInputElement, Props>(\n ({ onFormatValue, onFocus, onBlur, value, defaultValue, className, ...rest }, ref) => {\n const innerRef = useRef<HTMLInputElement>(null)\n const [isFocused, setIsFocused] = useState(false)\n\n const latest = useLatest({\n onFocus,\n onBlur,\n onFormatValue,\n })\n\n const functions = useMemo(() => {\n const formatValue = (formatted = '') => {\n if (innerRef.current && formatted !== innerRef.current.value) {\n innerRef.current.value = formatted\n latest.onFormatValue?.(formatted)\n }\n }\n\n return {\n formatCurrencyValue: (raw = '') => {\n formatValue(formatCurrency(raw))\n },\n handleFocus: (e: FocusEvent<HTMLInputElement>) => {\n setIsFocused(true)\n\n if (innerRef.current) {\n const commaExcluded = innerRef.current.value.replace(/,/g, '')\n formatValue(commaExcluded)\n }\n\n latest.onFocus?.(e)\n },\n handleBlur: (e: FocusEvent<HTMLInputElement>) => {\n setIsFocused(false)\n\n latest.onBlur?.(e)\n },\n }\n }, [latest])\n\n useImperativeHandle<HTMLInputElement | null, HTMLInputElement | null>(\n ref,\n () => innerRef.current,\n [],\n )\n\n useEffect(() => {\n if (value === undefined && defaultValue !== undefined) {\n functions.formatCurrencyValue(defaultValue)\n }\n // when component did mount\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n useEffect(() => {\n if (!isFocused) {\n if (value !== undefined) {\n // for controlled component\n functions.formatCurrencyValue(value)\n } else if (innerRef.current) {\n // for uncontrolled component\n functions.formatCurrencyValue(innerRef.current.value)\n }\n }\n }, [isFocused, value, functions])\n\n return (\n <Input\n {...rest}\n ref={innerRef}\n type=\"text\"\n value={value}\n defaultValue={defaultValue}\n onFocus={functions.handleFocus}\n onBlur={functions.handleBlur}\n className={`smarthr-ui-CurrencyInput${className ? ` ${className}` : ''}`}\n />\n )\n },\n)\n"],"names":[],"mappings":";;;;;;;AA2BO;AAEH;;;;;;AAOC;AAED;AACE;AACE;AACE;AACA;;AAEJ;;AAGE;AACE;;AAEF;;AAGE;AACE;;;AAIF;;AAEF;;AAGE;;;AAGN;AAEA;;;AAQI;;;;;;;AAQA;;AAEE;;AACK;;;;;;;AAmBb;;"}
|
|
@@ -16,20 +16,18 @@ const backgroundColor = {
|
|
|
16
16
|
BORDER: 'border',
|
|
17
17
|
ACTION_BACKGROUND: 'action-background',
|
|
18
18
|
};
|
|
19
|
-
const
|
|
20
|
-
base: [
|
|
21
|
-
'smarthr-ui-Input',
|
|
22
|
-
'shr-border-shorthand shr-box-border shr-inline-flex shr-cursor-text shr-items-center shr-gap-0.5 shr-rounded-m shr-bg-white shr-px-0.5',
|
|
23
|
-
'contrast-more:shr-border-high-contrast',
|
|
24
|
-
'focus-within:shr-focus-indicator',
|
|
25
|
-
'has-[[aria-invalid]]:shr-border-danger',
|
|
26
|
-
'has-[:disabled]:[&&&]:shr-border-default/50',
|
|
27
|
-
'has-[:disabled]:shr-pointer-events-none has-[:disabled]:shr-bg-white-darken',
|
|
28
|
-
'has-[[readonly]:not(:disabled)]:[&&&]:shr-border-[theme(backgroundColor.column)] has-[[readonly]:not(:disabled)]:[&&&]:shr-bg-column',
|
|
29
|
-
],
|
|
30
|
-
});
|
|
31
|
-
const innerClassNameGenerator = index.tv({
|
|
19
|
+
const classNameGenerator = index.tv({
|
|
32
20
|
slots: {
|
|
21
|
+
wrapper: [
|
|
22
|
+
'smarthr-ui-Input',
|
|
23
|
+
'shr-border-shorthand shr-box-border shr-inline-flex shr-cursor-text shr-items-center shr-gap-0.5 shr-rounded-m shr-bg-white shr-px-0.5',
|
|
24
|
+
'contrast-more:shr-border-high-contrast',
|
|
25
|
+
'focus-within:shr-focus-indicator',
|
|
26
|
+
'has-[[aria-invalid]]:shr-border-danger',
|
|
27
|
+
'has-[:disabled]:[&&&]:shr-border-default/50',
|
|
28
|
+
'has-[:disabled]:shr-pointer-events-none has-[:disabled]:shr-bg-white-darken',
|
|
29
|
+
'has-[[readonly]:not(:disabled)]:[&&&]:shr-border-[theme(backgroundColor.column)] has-[[readonly]:not(:disabled)]:[&&&]:shr-bg-column',
|
|
30
|
+
],
|
|
33
31
|
input: [
|
|
34
32
|
'smarthr-ui-Input-input',
|
|
35
33
|
'shr-inline-block shr-w-full shr-grow shr-border-none shr-bg-transparent shr-py-0.75 shr-text-base shr-leading-none shr-text-black shr-outline-none shr-outline-0',
|
|
@@ -56,7 +54,7 @@ const DEFAULT_MAX_ATTR = {
|
|
|
56
54
|
const Input = React.forwardRef(({ onFocus, onBlur, autoFocus, prefix, suffix, className, width, disabled, error, readOnly, bgColor, type, max, ...rest }, ref) => {
|
|
57
55
|
const theme = hooks_useTheme.useTheme();
|
|
58
56
|
const innerRef = React.useRef(null);
|
|
59
|
-
React.useImperativeHandle(ref, () => innerRef.current);
|
|
57
|
+
React.useImperativeHandle(ref, () => innerRef.current, []);
|
|
60
58
|
const latest = hooks_useLatest.useLatest({ autoFocus });
|
|
61
59
|
const functions = React.useMemo(() => ({
|
|
62
60
|
handleInnerRef: (node) => {
|
|
@@ -68,9 +66,9 @@ const Input = React.forwardRef(({ onFocus, onBlur, autoFocus, prefix, suffix, cl
|
|
|
68
66
|
handleDelegateClick: () => innerRef.current?.focus(),
|
|
69
67
|
}), [latest]);
|
|
70
68
|
const classNames = React.useMemo(() => {
|
|
71
|
-
const { input, affix } =
|
|
69
|
+
const { wrapper, input, affix } = classNameGenerator();
|
|
72
70
|
return {
|
|
73
|
-
wrapper:
|
|
71
|
+
wrapper: wrapper({ className }),
|
|
74
72
|
input: input(),
|
|
75
73
|
prefix: affix({ className: 'smarthr-ui-Input-prefix' }),
|
|
76
74
|
suffix: affix({ className: 'smarthr-ui-Input-suffix' }),
|
|
@@ -83,7 +81,7 @@ const Input = React.forwardRef(({ onFocus, onBlur, autoFocus, prefix, suffix, cl
|
|
|
83
81
|
backgroundColor: styleColor,
|
|
84
82
|
maxWidth: styleMaxWidth,
|
|
85
83
|
width: styleMaxWidth ? '100%' : undefined,
|
|
86
|
-
}, children: [prefix && jsxRuntime.jsx("span", { className: classNames.prefix, children: prefix }), jsxRuntime.jsx("input", { ...rest, type: type,
|
|
84
|
+
}, children: [prefix && jsxRuntime.jsx("span", { className: classNames.prefix, children: prefix }), jsxRuntime.jsx("input", { ...rest, ref: functions.handleInnerRef, type: type, disabled: disabled, readOnly: readOnly, max: max || (type && DEFAULT_MAX_ATTR[type]) || undefined, "aria-invalid": error || undefined, "data-smarthr-ui-input": "true", onWheel: type === 'number' ? disableWheel : undefined, onFocus: onFocus, onBlur: onBlur, className: classNames.input }), suffix && jsxRuntime.jsx("span", { className: classNames.suffix, children: suffix })] }));
|
|
87
85
|
});
|
|
88
86
|
const disableWheel = (e) => {
|
|
89
87
|
// wheel イベントに preventDefault はないため
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Input.cjs","sources":["../../../src/components/Input/Input.tsx"],"sourcesContent":["'use client'\n\nimport {\n type ComponentPropsWithRef,\n type MutableRefObject,\n type ReactNode,\n type WheelEvent,\n forwardRef,\n useImperativeHandle,\n useMemo,\n useRef,\n} from 'react'\nimport { tv } from 'tailwind-variants'\n\nimport { useLatest } from '../../hooks/useLatest'\nimport { useTheme } from '../../hooks/useTheme'\n\ntype BaseProps = {\n /** input 要素の `type` 値 */\n type?: HTMLInputElement['type']\n /** フォームにエラーがあるかどうか */\n error?: boolean\n /** コンポーネントの幅 */\n width?: number | string\n /** オートフォーカスを行うかどうか */\n autoFocus?: boolean\n /** コンポーネント内の先頭に表示する内容 */\n prefix?: ReactNode\n /** コンポーネント内の末尾に表示する内容 */\n suffix?: ReactNode\n /** 背景色。readOnly を下地の上に載せる場合に使う */\n bgColor?: keyof typeof backgroundColor\n /**\n * @deprecated placeholder属性は非推奨です。別途ヒント用要素を設置するか、それらの領域を確保出来ない場合はTooltipコンポーネントの利用を検討してください。\n */\n placeholder?: string\n}\ntype Props = BaseProps & Omit<ComponentPropsWithRef<'input'>, keyof BaseProps | 'onWheel'>\n\nexport const backgroundColor = {\n BACKGROUND: 'background',\n COLUMN: 'column',\n BASE_GREY: 'base-grey',\n OVER_BACKGROUND: 'over-background',\n HEAD: 'head',\n BORDER: 'border',\n ACTION_BACKGROUND: 'action-background',\n} as const\n\nconst
|
|
1
|
+
{"version":3,"file":"Input.cjs","sources":["../../../src/components/Input/Input.tsx"],"sourcesContent":["'use client'\n\nimport {\n type ComponentPropsWithRef,\n type MutableRefObject,\n type ReactNode,\n type WheelEvent,\n forwardRef,\n useImperativeHandle,\n useMemo,\n useRef,\n} from 'react'\nimport { tv } from 'tailwind-variants'\n\nimport { useLatest } from '../../hooks/useLatest'\nimport { useTheme } from '../../hooks/useTheme'\n\ntype BaseProps = {\n /** input 要素の `type` 値 */\n type?: HTMLInputElement['type']\n /** フォームにエラーがあるかどうか */\n error?: boolean\n /** コンポーネントの幅 */\n width?: number | string\n /** オートフォーカスを行うかどうか */\n autoFocus?: boolean\n /** コンポーネント内の先頭に表示する内容 */\n prefix?: ReactNode\n /** コンポーネント内の末尾に表示する内容 */\n suffix?: ReactNode\n /** 背景色。readOnly を下地の上に載せる場合に使う */\n bgColor?: keyof typeof backgroundColor\n /**\n * @deprecated placeholder属性は非推奨です。別途ヒント用要素を設置するか、それらの領域を確保出来ない場合はTooltipコンポーネントの利用を検討してください。\n */\n placeholder?: string\n}\ntype Props = BaseProps & Omit<ComponentPropsWithRef<'input'>, keyof BaseProps | 'onWheel'>\n\nexport const backgroundColor = {\n BACKGROUND: 'background',\n COLUMN: 'column',\n BASE_GREY: 'base-grey',\n OVER_BACKGROUND: 'over-background',\n HEAD: 'head',\n BORDER: 'border',\n ACTION_BACKGROUND: 'action-background',\n} as const\n\nconst classNameGenerator = tv({\n slots: {\n wrapper: [\n 'smarthr-ui-Input',\n 'shr-border-shorthand shr-box-border shr-inline-flex shr-cursor-text shr-items-center shr-gap-0.5 shr-rounded-m shr-bg-white shr-px-0.5',\n 'contrast-more:shr-border-high-contrast',\n 'focus-within:shr-focus-indicator',\n 'has-[[aria-invalid]]:shr-border-danger',\n 'has-[:disabled]:[&&&]:shr-border-default/50',\n 'has-[:disabled]:shr-pointer-events-none has-[:disabled]:shr-bg-white-darken',\n 'has-[[readonly]:not(:disabled)]:[&&&]:shr-border-[theme(backgroundColor.column)] has-[[readonly]:not(:disabled)]:[&&&]:shr-bg-column',\n ],\n input: [\n 'smarthr-ui-Input-input',\n 'shr-inline-block shr-w-full shr-grow shr-border-none shr-bg-transparent shr-py-0.75 shr-text-base shr-leading-none shr-text-black shr-outline-none shr-outline-0',\n 'placeholder:shr-text-grey',\n 'disabled:shr-text-disabled disabled:shr-opacity-100',\n 'shr-h-[theme(fontSize.base)]',\n // HINT: 日付系inputがsafariなどで対応されていないため、input要素内が空白になりフォームが潰れる場合がある\n // マジックナンバーになるが、ほかに適切なプロパティがないため、min-widthで最低幅を指定することで防ぐ\n '[&[type=\"datetime-local\"]]:shr-min-w-[11em] [&[type=\"month\"]]:shr-min-w-[8em] [&[type=\"time\"]]:shr-min-w-[5em]',\n ],\n affix: [\n 'shr-flex shr-shrink-0 shr-items-center shr-text-grey',\n '[.smarthr-ui-Input:has(:disabled)_&]:shr-text-disabled [.smarthr-ui-Input:has(:disabled)_&]:shr-opacity-100',\n ],\n },\n})\n\n// HINT: PC版ブラウザで年が6桁入力できる場合、コピー&ペーストが正常に動作しないなど、UI上の問題が発生する場合がある\n// 回避のためmaxで年四桁を指定する\nconst DEFAULT_MAX_ATTR = {\n date: '9999-12-31',\n 'datetime-local': '9999-12-31T23:59',\n month: '9999-12',\n}\n\nexport const Input = forwardRef<HTMLInputElement, Props>(\n (\n {\n onFocus,\n onBlur,\n autoFocus,\n prefix,\n suffix,\n className,\n width,\n disabled,\n error,\n readOnly,\n bgColor,\n type,\n max,\n ...rest\n },\n ref,\n ) => {\n const theme = useTheme()\n const innerRef: MutableRefObject<HTMLInputElement | null> = useRef(null)\n\n useImperativeHandle<HTMLInputElement | null, HTMLInputElement | null>(\n ref,\n () => innerRef.current,\n [],\n )\n\n const latest = useLatest({ autoFocus })\n\n const functions = useMemo(\n () => ({\n handleInnerRef: (node: HTMLInputElement | null) => {\n innerRef.current = node\n\n if (latest.autoFocus && node) {\n node.focus()\n }\n },\n handleDelegateClick: () => innerRef.current?.focus(),\n }),\n [latest],\n )\n\n const classNames = useMemo(() => {\n const { wrapper, input, affix } = classNameGenerator()\n\n return {\n wrapper: wrapper({ className }),\n input: input(),\n prefix: affix({ className: 'smarthr-ui-Input-prefix' }),\n suffix: affix({ className: 'smarthr-ui-Input-suffix' }),\n }\n }, [className])\n\n const styleColor = bgColor ? theme.backgroundColor[backgroundColor[bgColor]] : undefined\n const styleMaxWidth = typeof width === 'number' ? `${width}px` : width\n\n return (\n <span\n role=\"presentation\"\n onClick={functions.handleDelegateClick}\n className={classNames.wrapper}\n style={{\n borderColor: styleColor,\n backgroundColor: styleColor,\n maxWidth: styleMaxWidth,\n width: styleMaxWidth ? '100%' : undefined,\n }}\n >\n {prefix && <span className={classNames.prefix}>{prefix}</span>}\n <input\n {...rest}\n ref={functions.handleInnerRef}\n type={type}\n disabled={disabled}\n readOnly={readOnly}\n max={\n max || (type && DEFAULT_MAX_ATTR[type as keyof typeof DEFAULT_MAX_ATTR]) || undefined\n }\n aria-invalid={error || undefined}\n data-smarthr-ui-input=\"true\"\n onWheel={type === 'number' ? disableWheel : undefined}\n onFocus={onFocus}\n onBlur={onBlur}\n className={classNames.input}\n />\n {suffix && <span className={classNames.suffix}>{suffix}</span>}\n </span>\n )\n },\n)\n\nconst disableWheel = (e: WheelEvent) => {\n // wheel イベントに preventDefault はないため\n if (e.target) {\n ;(e.target as HTMLInputElement).blur()\n }\n}\n"],"names":[],"mappings":";;;;;;;;;AAuCO;AACL;AACA;AACA;AACA;AACA;AACA;AACA;;AAGF;AACE;AACE;;;;;;;;;AASC;AACD;;;;;;;;;AASC;AACD;;;AAGC;AACF;AACF;AAED;AACA;AACA;AACE;AACA;AACA;;AAGK;AAoBH;AACA;AAEA;;AAQA;AAEI;AACE;AAEA;;;;;AAKH;AAIH;;;AAII;;;;;AAKJ;AAEA;AACA;AAEA;AAMM;AACA;AACA;;;AAwBR;AAGF;;AAEE;AACI;;AAEN;;;"}
|
|
@@ -14,20 +14,18 @@ const backgroundColor = {
|
|
|
14
14
|
BORDER: 'border',
|
|
15
15
|
ACTION_BACKGROUND: 'action-background',
|
|
16
16
|
};
|
|
17
|
-
const
|
|
18
|
-
base: [
|
|
19
|
-
'smarthr-ui-Input',
|
|
20
|
-
'shr-border-shorthand shr-box-border shr-inline-flex shr-cursor-text shr-items-center shr-gap-0.5 shr-rounded-m shr-bg-white shr-px-0.5',
|
|
21
|
-
'contrast-more:shr-border-high-contrast',
|
|
22
|
-
'focus-within:shr-focus-indicator',
|
|
23
|
-
'has-[[aria-invalid]]:shr-border-danger',
|
|
24
|
-
'has-[:disabled]:[&&&]:shr-border-default/50',
|
|
25
|
-
'has-[:disabled]:shr-pointer-events-none has-[:disabled]:shr-bg-white-darken',
|
|
26
|
-
'has-[[readonly]:not(:disabled)]:[&&&]:shr-border-[theme(backgroundColor.column)] has-[[readonly]:not(:disabled)]:[&&&]:shr-bg-column',
|
|
27
|
-
],
|
|
28
|
-
});
|
|
29
|
-
const innerClassNameGenerator = ce({
|
|
17
|
+
const classNameGenerator = ce({
|
|
30
18
|
slots: {
|
|
19
|
+
wrapper: [
|
|
20
|
+
'smarthr-ui-Input',
|
|
21
|
+
'shr-border-shorthand shr-box-border shr-inline-flex shr-cursor-text shr-items-center shr-gap-0.5 shr-rounded-m shr-bg-white shr-px-0.5',
|
|
22
|
+
'contrast-more:shr-border-high-contrast',
|
|
23
|
+
'focus-within:shr-focus-indicator',
|
|
24
|
+
'has-[[aria-invalid]]:shr-border-danger',
|
|
25
|
+
'has-[:disabled]:[&&&]:shr-border-default/50',
|
|
26
|
+
'has-[:disabled]:shr-pointer-events-none has-[:disabled]:shr-bg-white-darken',
|
|
27
|
+
'has-[[readonly]:not(:disabled)]:[&&&]:shr-border-[theme(backgroundColor.column)] has-[[readonly]:not(:disabled)]:[&&&]:shr-bg-column',
|
|
28
|
+
],
|
|
31
29
|
input: [
|
|
32
30
|
'smarthr-ui-Input-input',
|
|
33
31
|
'shr-inline-block shr-w-full shr-grow shr-border-none shr-bg-transparent shr-py-0.75 shr-text-base shr-leading-none shr-text-black shr-outline-none shr-outline-0',
|
|
@@ -54,7 +52,7 @@ const DEFAULT_MAX_ATTR = {
|
|
|
54
52
|
const Input = forwardRef(({ onFocus, onBlur, autoFocus, prefix, suffix, className, width, disabled, error, readOnly, bgColor, type, max, ...rest }, ref) => {
|
|
55
53
|
const theme = useTheme();
|
|
56
54
|
const innerRef = useRef(null);
|
|
57
|
-
useImperativeHandle(ref, () => innerRef.current);
|
|
55
|
+
useImperativeHandle(ref, () => innerRef.current, []);
|
|
58
56
|
const latest = useLatest({ autoFocus });
|
|
59
57
|
const functions = useMemo(() => ({
|
|
60
58
|
handleInnerRef: (node) => {
|
|
@@ -66,9 +64,9 @@ const Input = forwardRef(({ onFocus, onBlur, autoFocus, prefix, suffix, classNam
|
|
|
66
64
|
handleDelegateClick: () => innerRef.current?.focus(),
|
|
67
65
|
}), [latest]);
|
|
68
66
|
const classNames = useMemo(() => {
|
|
69
|
-
const { input, affix } =
|
|
67
|
+
const { wrapper, input, affix } = classNameGenerator();
|
|
70
68
|
return {
|
|
71
|
-
wrapper:
|
|
69
|
+
wrapper: wrapper({ className }),
|
|
72
70
|
input: input(),
|
|
73
71
|
prefix: affix({ className: 'smarthr-ui-Input-prefix' }),
|
|
74
72
|
suffix: affix({ className: 'smarthr-ui-Input-suffix' }),
|
|
@@ -81,7 +79,7 @@ const Input = forwardRef(({ onFocus, onBlur, autoFocus, prefix, suffix, classNam
|
|
|
81
79
|
backgroundColor: styleColor,
|
|
82
80
|
maxWidth: styleMaxWidth,
|
|
83
81
|
width: styleMaxWidth ? '100%' : undefined,
|
|
84
|
-
}, children: [prefix && jsx("span", { className: classNames.prefix, children: prefix }), jsx("input", { ...rest, type: type,
|
|
82
|
+
}, children: [prefix && jsx("span", { className: classNames.prefix, children: prefix }), jsx("input", { ...rest, ref: functions.handleInnerRef, type: type, disabled: disabled, readOnly: readOnly, max: max || (type && DEFAULT_MAX_ATTR[type]) || undefined, "aria-invalid": error || undefined, "data-smarthr-ui-input": "true", onWheel: type === 'number' ? disableWheel : undefined, onFocus: onFocus, onBlur: onBlur, className: classNames.input }), suffix && jsx("span", { className: classNames.suffix, children: suffix })] }));
|
|
85
83
|
});
|
|
86
84
|
const disableWheel = (e) => {
|
|
87
85
|
// wheel イベントに preventDefault はないため
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Input.js","sources":["../../../src/components/Input/Input.tsx"],"sourcesContent":["'use client'\n\nimport {\n type ComponentPropsWithRef,\n type MutableRefObject,\n type ReactNode,\n type WheelEvent,\n forwardRef,\n useImperativeHandle,\n useMemo,\n useRef,\n} from 'react'\nimport { tv } from 'tailwind-variants'\n\nimport { useLatest } from '../../hooks/useLatest'\nimport { useTheme } from '../../hooks/useTheme'\n\ntype BaseProps = {\n /** input 要素の `type` 値 */\n type?: HTMLInputElement['type']\n /** フォームにエラーがあるかどうか */\n error?: boolean\n /** コンポーネントの幅 */\n width?: number | string\n /** オートフォーカスを行うかどうか */\n autoFocus?: boolean\n /** コンポーネント内の先頭に表示する内容 */\n prefix?: ReactNode\n /** コンポーネント内の末尾に表示する内容 */\n suffix?: ReactNode\n /** 背景色。readOnly を下地の上に載せる場合に使う */\n bgColor?: keyof typeof backgroundColor\n /**\n * @deprecated placeholder属性は非推奨です。別途ヒント用要素を設置するか、それらの領域を確保出来ない場合はTooltipコンポーネントの利用を検討してください。\n */\n placeholder?: string\n}\ntype Props = BaseProps & Omit<ComponentPropsWithRef<'input'>, keyof BaseProps | 'onWheel'>\n\nexport const backgroundColor = {\n BACKGROUND: 'background',\n COLUMN: 'column',\n BASE_GREY: 'base-grey',\n OVER_BACKGROUND: 'over-background',\n HEAD: 'head',\n BORDER: 'border',\n ACTION_BACKGROUND: 'action-background',\n} as const\n\nconst
|
|
1
|
+
{"version":3,"file":"Input.js","sources":["../../../src/components/Input/Input.tsx"],"sourcesContent":["'use client'\n\nimport {\n type ComponentPropsWithRef,\n type MutableRefObject,\n type ReactNode,\n type WheelEvent,\n forwardRef,\n useImperativeHandle,\n useMemo,\n useRef,\n} from 'react'\nimport { tv } from 'tailwind-variants'\n\nimport { useLatest } from '../../hooks/useLatest'\nimport { useTheme } from '../../hooks/useTheme'\n\ntype BaseProps = {\n /** input 要素の `type` 値 */\n type?: HTMLInputElement['type']\n /** フォームにエラーがあるかどうか */\n error?: boolean\n /** コンポーネントの幅 */\n width?: number | string\n /** オートフォーカスを行うかどうか */\n autoFocus?: boolean\n /** コンポーネント内の先頭に表示する内容 */\n prefix?: ReactNode\n /** コンポーネント内の末尾に表示する内容 */\n suffix?: ReactNode\n /** 背景色。readOnly を下地の上に載せる場合に使う */\n bgColor?: keyof typeof backgroundColor\n /**\n * @deprecated placeholder属性は非推奨です。別途ヒント用要素を設置するか、それらの領域を確保出来ない場合はTooltipコンポーネントの利用を検討してください。\n */\n placeholder?: string\n}\ntype Props = BaseProps & Omit<ComponentPropsWithRef<'input'>, keyof BaseProps | 'onWheel'>\n\nexport const backgroundColor = {\n BACKGROUND: 'background',\n COLUMN: 'column',\n BASE_GREY: 'base-grey',\n OVER_BACKGROUND: 'over-background',\n HEAD: 'head',\n BORDER: 'border',\n ACTION_BACKGROUND: 'action-background',\n} as const\n\nconst classNameGenerator = tv({\n slots: {\n wrapper: [\n 'smarthr-ui-Input',\n 'shr-border-shorthand shr-box-border shr-inline-flex shr-cursor-text shr-items-center shr-gap-0.5 shr-rounded-m shr-bg-white shr-px-0.5',\n 'contrast-more:shr-border-high-contrast',\n 'focus-within:shr-focus-indicator',\n 'has-[[aria-invalid]]:shr-border-danger',\n 'has-[:disabled]:[&&&]:shr-border-default/50',\n 'has-[:disabled]:shr-pointer-events-none has-[:disabled]:shr-bg-white-darken',\n 'has-[[readonly]:not(:disabled)]:[&&&]:shr-border-[theme(backgroundColor.column)] has-[[readonly]:not(:disabled)]:[&&&]:shr-bg-column',\n ],\n input: [\n 'smarthr-ui-Input-input',\n 'shr-inline-block shr-w-full shr-grow shr-border-none shr-bg-transparent shr-py-0.75 shr-text-base shr-leading-none shr-text-black shr-outline-none shr-outline-0',\n 'placeholder:shr-text-grey',\n 'disabled:shr-text-disabled disabled:shr-opacity-100',\n 'shr-h-[theme(fontSize.base)]',\n // HINT: 日付系inputがsafariなどで対応されていないため、input要素内が空白になりフォームが潰れる場合がある\n // マジックナンバーになるが、ほかに適切なプロパティがないため、min-widthで最低幅を指定することで防ぐ\n '[&[type=\"datetime-local\"]]:shr-min-w-[11em] [&[type=\"month\"]]:shr-min-w-[8em] [&[type=\"time\"]]:shr-min-w-[5em]',\n ],\n affix: [\n 'shr-flex shr-shrink-0 shr-items-center shr-text-grey',\n '[.smarthr-ui-Input:has(:disabled)_&]:shr-text-disabled [.smarthr-ui-Input:has(:disabled)_&]:shr-opacity-100',\n ],\n },\n})\n\n// HINT: PC版ブラウザで年が6桁入力できる場合、コピー&ペーストが正常に動作しないなど、UI上の問題が発生する場合がある\n// 回避のためmaxで年四桁を指定する\nconst DEFAULT_MAX_ATTR = {\n date: '9999-12-31',\n 'datetime-local': '9999-12-31T23:59',\n month: '9999-12',\n}\n\nexport const Input = forwardRef<HTMLInputElement, Props>(\n (\n {\n onFocus,\n onBlur,\n autoFocus,\n prefix,\n suffix,\n className,\n width,\n disabled,\n error,\n readOnly,\n bgColor,\n type,\n max,\n ...rest\n },\n ref,\n ) => {\n const theme = useTheme()\n const innerRef: MutableRefObject<HTMLInputElement | null> = useRef(null)\n\n useImperativeHandle<HTMLInputElement | null, HTMLInputElement | null>(\n ref,\n () => innerRef.current,\n [],\n )\n\n const latest = useLatest({ autoFocus })\n\n const functions = useMemo(\n () => ({\n handleInnerRef: (node: HTMLInputElement | null) => {\n innerRef.current = node\n\n if (latest.autoFocus && node) {\n node.focus()\n }\n },\n handleDelegateClick: () => innerRef.current?.focus(),\n }),\n [latest],\n )\n\n const classNames = useMemo(() => {\n const { wrapper, input, affix } = classNameGenerator()\n\n return {\n wrapper: wrapper({ className }),\n input: input(),\n prefix: affix({ className: 'smarthr-ui-Input-prefix' }),\n suffix: affix({ className: 'smarthr-ui-Input-suffix' }),\n }\n }, [className])\n\n const styleColor = bgColor ? theme.backgroundColor[backgroundColor[bgColor]] : undefined\n const styleMaxWidth = typeof width === 'number' ? `${width}px` : width\n\n return (\n <span\n role=\"presentation\"\n onClick={functions.handleDelegateClick}\n className={classNames.wrapper}\n style={{\n borderColor: styleColor,\n backgroundColor: styleColor,\n maxWidth: styleMaxWidth,\n width: styleMaxWidth ? '100%' : undefined,\n }}\n >\n {prefix && <span className={classNames.prefix}>{prefix}</span>}\n <input\n {...rest}\n ref={functions.handleInnerRef}\n type={type}\n disabled={disabled}\n readOnly={readOnly}\n max={\n max || (type && DEFAULT_MAX_ATTR[type as keyof typeof DEFAULT_MAX_ATTR]) || undefined\n }\n aria-invalid={error || undefined}\n data-smarthr-ui-input=\"true\"\n onWheel={type === 'number' ? disableWheel : undefined}\n onFocus={onFocus}\n onBlur={onBlur}\n className={classNames.input}\n />\n {suffix && <span className={classNames.suffix}>{suffix}</span>}\n </span>\n )\n },\n)\n\nconst disableWheel = (e: WheelEvent) => {\n // wheel イベントに preventDefault はないため\n if (e.target) {\n ;(e.target as HTMLInputElement).blur()\n }\n}\n"],"names":[],"mappings":";;;;;;;AAuCO;AACL;AACA;AACA;AACA;AACA;AACA;AACA;;AAGF;AACE;AACE;;;;;;;;;AASC;AACD;;;;;;;;;AASC;AACD;;;AAGC;AACF;AACF;AAED;AACA;AACA;AACE;AACA;AACA;;AAGK;AAoBH;AACA;AAEA;;AAQA;AAEI;AACE;AAEA;;;;;AAKH;AAIH;;;AAII;;;;;AAKJ;AAEA;AACA;AAEA;AAMM;AACA;AACA;;;AAwBR;AAGF;;AAEE;AACI;;AAEN;;"}
|