smarthr-ui 96.1.1 → 96.1.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.
Files changed (163) hide show
  1. package/lib/components/AppHeader/components/common/CommonButton.cjs +3 -2
  2. package/lib/components/AppHeader/components/common/CommonButton.cjs.map +1 -1
  3. package/lib/components/AppHeader/components/common/CommonButton.js +3 -2
  4. package/lib/components/AppHeader/components/common/CommonButton.js.map +1 -1
  5. package/lib/components/Base/Base.cjs +7 -1
  6. package/lib/components/Base/Base.cjs.map +1 -1
  7. package/lib/components/Base/Base.js +7 -1
  8. package/lib/components/Base/Base.js.map +1 -1
  9. package/lib/components/Base/BaseColumn/BaseColumn.cjs +7 -1
  10. package/lib/components/Base/BaseColumn/BaseColumn.cjs.map +1 -1
  11. package/lib/components/Base/BaseColumn/BaseColumn.js +7 -1
  12. package/lib/components/Base/BaseColumn/BaseColumn.js.map +1 -1
  13. package/lib/components/Button/AnchorButton.cjs +2 -7
  14. package/lib/components/Button/AnchorButton.cjs.map +1 -1
  15. package/lib/components/Button/AnchorButton.js +2 -7
  16. package/lib/components/Button/AnchorButton.js.map +1 -1
  17. package/lib/components/Combobox/MultiCombobox/MultiSelectedItem.cjs +8 -4
  18. package/lib/components/Combobox/MultiCombobox/MultiSelectedItem.cjs.map +1 -1
  19. package/lib/components/Combobox/MultiCombobox/MultiSelectedItem.js +8 -4
  20. package/lib/components/Combobox/MultiCombobox/MultiSelectedItem.js.map +1 -1
  21. package/lib/components/Dialog/DialogBody.cjs +7 -1
  22. package/lib/components/Dialog/DialogBody.cjs.map +1 -1
  23. package/lib/components/Dialog/DialogBody.js +7 -1
  24. package/lib/components/Dialog/DialogBody.js.map +1 -1
  25. package/lib/components/Dialog/DialogContentInner.cjs +7 -1
  26. package/lib/components/Dialog/DialogContentInner.cjs.map +1 -1
  27. package/lib/components/Dialog/DialogContentInner.js +7 -1
  28. package/lib/components/Dialog/DialogContentInner.js.map +1 -1
  29. package/lib/components/Dialog/DialogOverlap.cjs +22 -5
  30. package/lib/components/Dialog/DialogOverlap.cjs.map +1 -1
  31. package/lib/components/Dialog/DialogOverlap.js +23 -6
  32. package/lib/components/Dialog/DialogOverlap.js.map +1 -1
  33. package/lib/components/Dialog/ModelessDialog/ModelessDialog.cjs +7 -1
  34. package/lib/components/Dialog/ModelessDialog/ModelessDialog.cjs.map +1 -1
  35. package/lib/components/Dialog/ModelessDialog/ModelessDialog.js +7 -1
  36. package/lib/components/Dialog/ModelessDialog/ModelessDialog.js.map +1 -1
  37. package/lib/components/Dialog/RemoteDialogTrigger/RemoteDialogTrigger.cjs +51 -11
  38. package/lib/components/Dialog/RemoteDialogTrigger/RemoteDialogTrigger.cjs.map +1 -1
  39. package/lib/components/Dialog/RemoteDialogTrigger/RemoteDialogTrigger.d.ts +3 -4
  40. package/lib/components/Dialog/RemoteDialogTrigger/RemoteDialogTrigger.js +52 -12
  41. package/lib/components/Dialog/RemoteDialogTrigger/RemoteDialogTrigger.js.map +1 -1
  42. package/lib/components/Dropdown/Dropdown.cjs +3 -3
  43. package/lib/components/Dropdown/Dropdown.cjs.map +1 -1
  44. package/lib/components/Dropdown/Dropdown.d.ts +1 -1
  45. package/lib/components/Dropdown/Dropdown.js +3 -3
  46. package/lib/components/Dropdown/Dropdown.js.map +1 -1
  47. package/lib/components/Dropdown/DropdownMenuButton/DropdownMenuButton.cjs +19 -7
  48. package/lib/components/Dropdown/DropdownMenuButton/DropdownMenuButton.cjs.map +1 -1
  49. package/lib/components/Dropdown/DropdownMenuButton/DropdownMenuButton.js +19 -7
  50. package/lib/components/Dropdown/DropdownMenuButton/DropdownMenuButton.js.map +1 -1
  51. package/lib/components/Dropdown/DropdownTrigger.cjs +31 -14
  52. package/lib/components/Dropdown/DropdownTrigger.cjs.map +1 -1
  53. package/lib/components/Dropdown/DropdownTrigger.js +31 -14
  54. package/lib/components/Dropdown/DropdownTrigger.js.map +1 -1
  55. package/lib/components/Icon/generateIcon.cjs +9 -1
  56. package/lib/components/Icon/generateIcon.cjs.map +1 -1
  57. package/lib/components/Icon/generateIcon.js +9 -1
  58. package/lib/components/Icon/generateIcon.js.map +1 -1
  59. package/lib/components/Layout/Cluster/Cluster.cjs +1 -0
  60. package/lib/components/Layout/Cluster/Cluster.cjs.map +1 -1
  61. package/lib/components/Layout/Cluster/Cluster.js +1 -0
  62. package/lib/components/Layout/Cluster/Cluster.js.map +1 -1
  63. package/lib/components/Layout/Container/Container.cjs +2 -2
  64. package/lib/components/Layout/Container/Container.js +2 -2
  65. package/lib/components/Scroller/Scroller.cjs +12 -19
  66. package/lib/components/Scroller/Scroller.cjs.map +1 -1
  67. package/lib/components/Scroller/Scroller.js +13 -20
  68. package/lib/components/Scroller/Scroller.js.map +1 -1
  69. package/lib/components/Table/TableReel.cjs +1 -1
  70. package/lib/components/Table/TableReel.cjs.map +1 -1
  71. package/lib/components/Table/TableReel.js +1 -1
  72. package/lib/components/Table/TableReel.js.map +1 -1
  73. package/lib/components/Table/TableScrollContext.cjs +4 -4
  74. package/lib/components/Table/TableScrollContext.js +4 -4
  75. package/lib/components/Table/useReelCells.cjs +13 -7
  76. package/lib/components/Table/useReelCells.cjs.map +1 -1
  77. package/lib/components/Table/useReelCells.d.ts +1 -3
  78. package/lib/components/Table/useReelCells.js +13 -7
  79. package/lib/components/Table/useReelCells.js.map +1 -1
  80. package/lib/components/Text/Text.cjs +3 -1
  81. package/lib/components/Text/Text.cjs.map +1 -1
  82. package/lib/components/Text/Text.js +3 -1
  83. package/lib/components/Text/Text.js.map +1 -1
  84. package/lib/components/TextLink/TextLink.cjs +14 -28
  85. package/lib/components/TextLink/TextLink.cjs.map +1 -1
  86. package/lib/components/TextLink/TextLink.js +15 -29
  87. package/lib/components/TextLink/TextLink.js.map +1 -1
  88. package/lib/components/TextLink/UpwardLink/UpwardLink.cjs +2 -2
  89. package/lib/components/TextLink/UpwardLink/UpwardLink.js +2 -2
  90. package/lib/components/Textarea/Textarea.cjs +7 -6
  91. package/lib/components/Textarea/Textarea.cjs.map +1 -1
  92. package/lib/components/Textarea/Textarea.js +7 -6
  93. package/lib/components/Textarea/Textarea.js.map +1 -1
  94. package/lib/components/Tooltip/tooltipHelper.cjs +2 -2
  95. package/lib/components/Tooltip/tooltipHelper.cjs.map +1 -1
  96. package/lib/components/Tooltip/tooltipHelper.js +2 -2
  97. package/lib/components/Tooltip/tooltipHelper.js.map +1 -1
  98. package/lib/components/WarekiPicker/WarekiPicker.cjs +2 -3
  99. package/lib/components/WarekiPicker/WarekiPicker.cjs.map +1 -1
  100. package/lib/components/WarekiPicker/WarekiPicker.js +2 -3
  101. package/lib/components/WarekiPicker/WarekiPicker.js.map +1 -1
  102. package/lib/configureTwMerge.cjs +2 -2
  103. package/lib/configureTwMerge.js +2 -2
  104. package/lib/hooks/useDevice/DeviceProvider.cjs +2 -2
  105. package/lib/hooks/useDevice/DeviceProvider.js +2 -2
  106. package/lib/hooks/useEnvironment/EnvironmentProvider.cjs +2 -2
  107. package/lib/hooks/useEnvironment/EnvironmentProvider.js +2 -2
  108. package/lib/hooks/useOuterClick.cjs +5 -2
  109. package/lib/hooks/useOuterClick.cjs.map +1 -1
  110. package/lib/hooks/useOuterClick.js +6 -3
  111. package/lib/hooks/useOuterClick.js.map +1 -1
  112. package/lib/hooks/useSV/useSV.cjs +2 -2
  113. package/lib/hooks/useSV/useSV.js +2 -2
  114. package/lib/hooks/useTheme.cjs +3 -0
  115. package/lib/hooks/useTheme.cjs.map +1 -1
  116. package/lib/hooks/useTheme.js +3 -0
  117. package/lib/hooks/useTheme.js.map +1 -1
  118. package/lib/index.cjs +14 -8
  119. package/lib/index.cjs.map +1 -1
  120. package/lib/index.d.ts +1 -10
  121. package/lib/index.js +10 -4
  122. package/lib/index.js.map +1 -1
  123. package/lib/intl/locales/index.cjs +1 -0
  124. package/lib/intl/locales/index.cjs.map +1 -1
  125. package/lib/intl/locales/index.js +1 -0
  126. package/lib/intl/locales/index.js.map +1 -1
  127. package/lib/libs/ua.cjs +1 -2
  128. package/lib/libs/ua.cjs.map +1 -1
  129. package/lib/libs/ua.js +1 -2
  130. package/lib/libs/ua.js.map +1 -1
  131. package/lib/smarthr-ui-preset.cjs +4 -4
  132. package/lib/smarthr-ui-preset.js +4 -4
  133. package/lib/tailwind/color.cjs +9 -0
  134. package/lib/tailwind/color.cjs.map +1 -1
  135. package/lib/tailwind/color.d.ts +1 -1
  136. package/lib/tailwind/color.js +9 -0
  137. package/lib/tailwind/color.js.map +1 -1
  138. package/lib/tailwind/fontSize.cjs +10 -0
  139. package/lib/tailwind/fontSize.cjs.map +1 -1
  140. package/lib/tailwind/fontSize.d.ts +1 -1
  141. package/lib/tailwind/fontSize.js +10 -0
  142. package/lib/tailwind/fontSize.js.map +1 -1
  143. package/lib/tailwind/index.cjs +10 -2
  144. package/lib/tailwind/index.cjs.map +1 -1
  145. package/lib/tailwind/index.js +9 -1
  146. package/lib/tailwind/index.js.map +1 -1
  147. package/lib/tailwind/textColor.cjs +9 -0
  148. package/lib/tailwind/textColor.cjs.map +1 -1
  149. package/lib/tailwind/textColor.d.ts +1 -1
  150. package/lib/tailwind/textColor.js +9 -0
  151. package/lib/tailwind/textColor.js.map +1 -1
  152. package/lib/themes/createWidth.cjs +1 -3
  153. package/lib/themes/createWidth.cjs.map +1 -1
  154. package/lib/themes/createWidth.d.ts +3 -1
  155. package/lib/themes/createWidth.js +1 -3
  156. package/lib/themes/createWidth.js.map +1 -1
  157. package/lib/themes/index.cjs +14 -0
  158. package/lib/themes/index.cjs.map +1 -1
  159. package/lib/themes/index.d.ts +7 -1
  160. package/lib/themes/index.js +9 -1
  161. package/lib/themes/index.js.map +1 -1
  162. package/metadata.json +1 -1
  163. package/package.json +1 -1
@@ -4,10 +4,16 @@ import { memo, useMemo, useRef } from 'react';
4
4
  import { tv as ce } from './../../vendor/.pnpm/tailwind-variants@0.3.1_tailwindcss@3.4.19_ts-node@10.9.2_@swc_core@1.15.17_@types_node@20.19.35_typescript@5.9.3__/vendor/tailwind-variants/dist/index.js';
5
5
  import { useHandleEscape } from '../../hooks/useHandleEscape.js';
6
6
  import { dialogSize } from '../../tailwind/dialogSize.js';
7
- import '../../themes/createTextColor.js';
7
+ import '../../themes/createSpacing.js';
8
8
  import '../../_virtual/index.js';
9
9
  import '../../_virtual/index2.js';
10
10
  import '../../themes/createColor.js';
11
+ import '../../themes/createTextColor.js';
12
+ import 'styled-components';
13
+ import '../../themes/createShadow/defaultShadow.js';
14
+ import '../../themes/createWidth.js';
15
+ import '../../themes/createBackgroundColor.js';
16
+ import '../../themes/createBorder.js';
11
17
  import { DialogOverlap } from './DialogOverlap.js';
12
18
  import { FocusTrap } from './FocusTrap.js';
13
19
  import { useBodyScrollLock } from './useBodyScrollLock.js';
@@ -1 +1 @@
1
- {"version":3,"file":"DialogContentInner.js","sources":["../../../src/components/Dialog/DialogContentInner.tsx"],"sourcesContent":["'use client'\n\nimport {\n type ComponentProps,\n type FC,\n type PropsWithChildren,\n type RefObject,\n memo,\n useMemo,\n useRef,\n} from 'react'\nimport { tv } from 'tailwind-variants'\n\nimport { useHandleEscape } from '../../hooks/useHandleEscape'\nimport { dialogSize } from '../../tailwind'\n\nimport { DialogOverlap } from './DialogOverlap'\nimport { FocusTrap, type FocusTrapRef } from './FocusTrap'\nimport { useBodyScrollLock } from './useBodyScrollLock'\n\nimport type { DialogSize } from './types'\n\nexport type DialogContentInnerProps = PropsWithChildren<{\n /**\n * オーバーレイをクリックした時に発火するコールバック関数\n */\n onClickOverlay?: () => void\n /**\n * エスケープキーを押下した時に発火するコールバック関数\n */\n onPressEscape?: () => void\n /**\n * ダイアログを開いているかどうか\n */\n isOpen: boolean\n /**\n * @deprecated ダイアログの幅を指定する場合は、`width` ではなく `size` を使用してください。\n * ダイアログの幅\n */\n width?: string | number\n /**\n * ダイアログの大きさ\n */\n size?: DialogSize\n /**\n * ダイアログの `id`\n * TODO 使われてなさそうなので確認\n */\n id?: string\n /**\n * ダイアログを開いた時にフォーカスする対象\n */\n firstFocusTarget?: RefObject<HTMLElement>\n /**\n * ダイアログの `aria-label`\n */\n ariaLabel?: string\n /**\n * ダイアログの `aria-labelledby`\n */\n ariaLabelledby?: string\n /**\n * ダイアログトップのフォーカストラップへの ref\n */\n focusTrapRef?: RefObject<FocusTrapRef>\n}>\ntype Props = DialogContentInnerProps & Omit<ComponentProps<'div'>, keyof DialogContentInnerProps>\n\nconst classNameGenerator = tv({\n slots: {\n layout: ['smarthr-ui-Dialog-wrapper', 'shr-max-w-[calc(100dvw-theme(spacing.1))]'],\n inner: [\n 'smarthr-ui-Dialog',\n 'shr-border-shorthand shr-relative shr-z-1 shr-rounded-m shr-bg-white shr-shadow-layer-3',\n 'contrast-more:shr-border-high-contrast',\n ],\n background: ['smarthr-ui-Dialog-background', 'shr-absolute shr-inset-0 shr-bg-scrim'],\n },\n variants: {\n size: {\n XS: { layout: dialogSize.XS },\n S: { layout: dialogSize.S },\n M: { layout: dialogSize.M },\n L: { layout: dialogSize.L },\n XL: { layout: dialogSize.XL },\n XXL: { layout: dialogSize.XXL },\n FULL: { layout: dialogSize.FULL },\n },\n },\n})\n\nexport const DialogContentInner: FC<Props> = ({\n onClickOverlay,\n onPressEscape,\n isOpen,\n id,\n width,\n size,\n firstFocusTarget,\n ariaLabel,\n ariaLabelledby,\n children,\n className,\n focusTrapRef,\n ...rest\n}) => {\n const classNames = useMemo(() => {\n const { layout, inner, background } = classNameGenerator()\n\n return {\n layout: layout({ size }),\n inner: inner({ className }),\n background: background(),\n }\n }, [size, className])\n const style = useMemo(() => {\n // width は deprecated なので、size が指定されている場合は width を無視する\n const actualWidth = size ? undefined : typeof width === 'number' ? `${width}px` : width\n\n return actualWidth ? { width: actualWidth } : undefined\n }, [width, size])\n\n const innerRef = useRef<HTMLDivElement>(null)\n\n useHandleEscape(\n useMemo(() => (onPressEscape && isOpen ? onPressEscape : undefined), [isOpen, onPressEscape]),\n )\n\n useBodyScrollLock(isOpen)\n\n return (\n <DialogOverlap isOpen={isOpen}>\n <div id={id} className={classNames.layout} style={style}>\n <Overlay\n isOpen={isOpen}\n onClickOverlay={onClickOverlay}\n className={classNames.background}\n />\n <div\n {...rest}\n ref={innerRef}\n role=\"dialog\"\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledby}\n aria-modal=\"true\"\n className={classNames.inner}\n >\n <FocusTrap firstFocusTarget={firstFocusTarget} ref={focusTrapRef}>\n {children}\n </FocusTrap>\n </div>\n </div>\n </DialogOverlap>\n )\n}\n\nconst Overlay = memo<Pick<Props, 'onClickOverlay' | 'isOpen'> & { className: string }>(\n ({ onClickOverlay, isOpen, className }) => {\n const onClick = useMemo(\n () => (onClickOverlay && isOpen ? onClickOverlay : undefined),\n [isOpen, onClickOverlay],\n )\n\n // eslint-disable-next-line smarthr/best-practice-for-interactive-element\n return <div onClick={onClick} className={className} role=\"presentation\" />\n },\n)\n"],"names":[],"mappings":";;;;;;;;;;;;;;AAoEA;AACE;AACE;AACA;;;;AAIC;AACD;AACD;AACD;AACE;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACD;AACF;AACF;AAEM;AAeL;;;AAII;AACA;;;AAGJ;AACA;;;AAIE;AACF;AAEA;;;AAQA;AAwBF;AAEA;;;AAQI;AACF;;"}
1
+ {"version":3,"file":"DialogContentInner.js","sources":["../../../src/components/Dialog/DialogContentInner.tsx"],"sourcesContent":["'use client'\n\nimport {\n type ComponentProps,\n type FC,\n type PropsWithChildren,\n type RefObject,\n memo,\n useMemo,\n useRef,\n} from 'react'\nimport { tv } from 'tailwind-variants'\n\nimport { useHandleEscape } from '../../hooks/useHandleEscape'\nimport { dialogSize } from '../../tailwind'\n\nimport { DialogOverlap } from './DialogOverlap'\nimport { FocusTrap, type FocusTrapRef } from './FocusTrap'\nimport { useBodyScrollLock } from './useBodyScrollLock'\n\nimport type { DialogSize } from './types'\n\nexport type DialogContentInnerProps = PropsWithChildren<{\n /**\n * オーバーレイをクリックした時に発火するコールバック関数\n */\n onClickOverlay?: () => void\n /**\n * エスケープキーを押下した時に発火するコールバック関数\n */\n onPressEscape?: () => void\n /**\n * ダイアログを開いているかどうか\n */\n isOpen: boolean\n /**\n * @deprecated ダイアログの幅を指定する場合は、`width` ではなく `size` を使用してください。\n * ダイアログの幅\n */\n width?: string | number\n /**\n * ダイアログの大きさ\n */\n size?: DialogSize\n /**\n * ダイアログの `id`\n * TODO 使われてなさそうなので確認\n */\n id?: string\n /**\n * ダイアログを開いた時にフォーカスする対象\n */\n firstFocusTarget?: RefObject<HTMLElement>\n /**\n * ダイアログの `aria-label`\n */\n ariaLabel?: string\n /**\n * ダイアログの `aria-labelledby`\n */\n ariaLabelledby?: string\n /**\n * ダイアログトップのフォーカストラップへの ref\n */\n focusTrapRef?: RefObject<FocusTrapRef>\n}>\ntype Props = DialogContentInnerProps & Omit<ComponentProps<'div'>, keyof DialogContentInnerProps>\n\nconst classNameGenerator = tv({\n slots: {\n layout: ['smarthr-ui-Dialog-wrapper', 'shr-max-w-[calc(100dvw-theme(spacing.1))]'],\n inner: [\n 'smarthr-ui-Dialog',\n 'shr-border-shorthand shr-relative shr-z-1 shr-rounded-m shr-bg-white shr-shadow-layer-3',\n 'contrast-more:shr-border-high-contrast',\n ],\n background: ['smarthr-ui-Dialog-background', 'shr-absolute shr-inset-0 shr-bg-scrim'],\n },\n variants: {\n size: {\n XS: { layout: dialogSize.XS },\n S: { layout: dialogSize.S },\n M: { layout: dialogSize.M },\n L: { layout: dialogSize.L },\n XL: { layout: dialogSize.XL },\n XXL: { layout: dialogSize.XXL },\n FULL: { layout: dialogSize.FULL },\n },\n },\n})\n\nexport const DialogContentInner: FC<Props> = ({\n onClickOverlay,\n onPressEscape,\n isOpen,\n id,\n width,\n size,\n firstFocusTarget,\n ariaLabel,\n ariaLabelledby,\n children,\n className,\n focusTrapRef,\n ...rest\n}) => {\n const classNames = useMemo(() => {\n const { layout, inner, background } = classNameGenerator()\n\n return {\n layout: layout({ size }),\n inner: inner({ className }),\n background: background(),\n }\n }, [size, className])\n const style = useMemo(() => {\n // width は deprecated なので、size が指定されている場合は width を無視する\n const actualWidth = size ? undefined : typeof width === 'number' ? `${width}px` : width\n\n return actualWidth ? { width: actualWidth } : undefined\n }, [width, size])\n\n const innerRef = useRef<HTMLDivElement>(null)\n\n useHandleEscape(\n useMemo(() => (onPressEscape && isOpen ? onPressEscape : undefined), [isOpen, onPressEscape]),\n )\n\n useBodyScrollLock(isOpen)\n\n return (\n <DialogOverlap isOpen={isOpen}>\n <div id={id} className={classNames.layout} style={style}>\n <Overlay\n isOpen={isOpen}\n onClickOverlay={onClickOverlay}\n className={classNames.background}\n />\n <div\n {...rest}\n ref={innerRef}\n role=\"dialog\"\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledby}\n aria-modal=\"true\"\n className={classNames.inner}\n >\n <FocusTrap firstFocusTarget={firstFocusTarget} ref={focusTrapRef}>\n {children}\n </FocusTrap>\n </div>\n </div>\n </DialogOverlap>\n )\n}\n\nconst Overlay = memo<Pick<Props, 'onClickOverlay' | 'isOpen'> & { className: string }>(\n ({ onClickOverlay, isOpen, className }) => {\n const onClick = useMemo(\n () => (onClickOverlay && isOpen ? onClickOverlay : undefined),\n [isOpen, onClickOverlay],\n )\n\n // eslint-disable-next-line smarthr/best-practice-for-interactive-element\n return <div onClick={onClick} className={className} role=\"presentation\" />\n },\n)\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;AAoEA;AACE;AACE;AACA;;;;AAIC;AACD;AACD;AACD;AACE;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACD;AACF;AACF;AAEM;AAeL;;;AAII;AACA;;;AAGJ;AACA;;;AAIE;AACF;AAEA;;;AAQA;AAwBF;AAEA;;;AAQI;AACF;;"}
@@ -29,15 +29,32 @@ const classNameGenerator = index.tv({
29
29
  ],
30
30
  });
31
31
  const DialogOverlap = ({ isOpen, className, children, as }) => {
32
- const [childrenBuffer, setChildrenBuffer] = React.useState(null);
32
+ const childrenBufferRef = React.useRef(null);
33
33
  const nodeRef = React.useRef(null);
34
34
  const actualClassName = React.useMemo(() => classNameGenerator({ className }), [className]);
35
+ // childrenをrefに保存(毎レンダリング時に最新の値を設定)
36
+ const childrenRef = React.useRef(children);
37
+ childrenRef.current = children;
35
38
  React.useEffect(() => {
36
- if (isOpen) {
37
- setChildrenBuffer(children);
39
+ if (!isOpen || !nodeRef.current) {
40
+ return;
38
41
  }
39
- }, [isOpen, children]);
40
- return (jsxRuntime.jsx(CSSTransition, { nodeRef: nodeRef, in: isOpen, timeout: 300, unmountOnExit: true, classNames: "shr-dialog-transition", children: jsxRuntime.jsx(components_Layout_Center_Center.Center, { ref: nodeRef, verticalCentering: true, className: actualClassName, as: as, children: isOpen ? children : childrenBuffer }) }));
42
+ // isOpen が true になった時に初期値を設定
43
+ childrenBufferRef.current = childrenRef.current;
44
+ // MutationObserver で DOM の変更を監視
45
+ const observer = new MutationObserver(() => {
46
+ childrenBufferRef.current = childrenRef.current;
47
+ });
48
+ observer.observe(nodeRef.current, {
49
+ childList: true,
50
+ subtree: true,
51
+ characterData: true,
52
+ });
53
+ return () => {
54
+ observer.disconnect();
55
+ };
56
+ }, [isOpen]);
57
+ return (jsxRuntime.jsx(CSSTransition, { nodeRef: nodeRef, in: isOpen, timeout: 300, unmountOnExit: true, classNames: "shr-dialog-transition", children: jsxRuntime.jsx(components_Layout_Center_Center.Center, { ref: nodeRef, verticalCentering: true, className: actualClassName, as: as, children: isOpen ? children : childrenBufferRef.current }) }));
41
58
  };
42
59
 
43
60
  exports.DialogOverlap = DialogOverlap;
@@ -1 +1 @@
1
- {"version":3,"file":"DialogOverlap.cjs","sources":["../../../src/components/Dialog/DialogOverlap.tsx"],"sourcesContent":["import {\n type ComponentProps,\n type FC,\n type PropsWithChildren,\n type ReactNode,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react'\nimport { CSSTransition } from 'react-transition-group'\nimport { tv } from 'tailwind-variants'\n\nimport { Center } from '../Layout'\n\ntype Props = PropsWithChildren<\n {\n isOpen: boolean\n as?: ComponentProps<typeof Center>['as']\n } & ComponentProps<'div'>\n>\n\nconst classNameGenerator = tv({\n base: [\n 'shr-fixed shr-inset-0 shr-z-overlap-base',\n '[&.shr-dialog-transition-enter]:shr-opacity-0',\n '[&.shr-dialog-transition-enter-active]:shr-transition-opacity',\n '[&.shr-dialog-transition-enter-active]:shr-duration-300',\n '[&.shr-dialog-transition-enter-active]:shr-ease-in-out',\n // enter と enter-active は同時に付与されるので、enter-active を強める\n '[&&.shr-dialog-transition-enter-active]:shr-opacity-100',\n '[&.shr-dialog-transition-exit]:shr-opacity-100',\n '[&.shr-dialog-transition-exit-active]:shr-transition-opacity',\n '[&.shr-dialog-transition-exit-active]:shr-duration-300',\n '[&.shr-dialog-transition-exit-active]:shr-ease-in-out',\n // exit と exit-active は同時に付与されるので、exit-active を強める\n '[&&.shr-dialog-transition-exit-active]:shr-opacity-0',\n ],\n})\n\nexport const DialogOverlap: FC<Props> = ({ isOpen, className, children, as }) => {\n const [childrenBuffer, setChildrenBuffer] = useState<ReactNode>(null)\n const nodeRef = useRef<HTMLDivElement>(null)\n\n const actualClassName = useMemo(() => classNameGenerator({ className }), [className])\n\n useEffect(() => {\n if (isOpen) {\n setChildrenBuffer(children)\n }\n }, [isOpen, children])\n\n return (\n <CSSTransition\n nodeRef={nodeRef}\n in={isOpen}\n timeout={300}\n unmountOnExit\n classNames=\"shr-dialog-transition\"\n >\n <Center ref={nodeRef} verticalCentering className={actualClassName} as={as}>\n {isOpen ? children : childrenBuffer}\n </Center>\n </CSSTransition>\n )\n}\n"],"names":["tv","useState","useRef","useMemo","useEffect","_jsx","Center"],"mappings":";;;;;;;;;;;;;AAsBA,MAAM,kBAAkB,GAAGA,QAAE,CAAC;AAC5B,IAAA,IAAI,EAAE;QACJ,0CAA0C;QAC1C,+CAA+C;QAC/C,+DAA+D;QAC/D,yDAAyD;QACzD,wDAAwD;;QAExD,yDAAyD;QACzD,gDAAgD;QAChD,8DAA8D;QAC9D,wDAAwD;QACxD,uDAAuD;;QAEvD,sDAAsD;AACvD,KAAA;AACF,CAAA,CAAC;AAEK,MAAM,aAAa,GAAc,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,EAAE,EAAE,KAAI;IAC9E,MAAM,CAAC,cAAc,EAAE,iBAAiB,CAAC,GAAGC,cAAQ,CAAY,IAAI,CAAC;AACrE,IAAA,MAAM,OAAO,GAAGC,YAAM,CAAiB,IAAI,CAAC;AAE5C,IAAA,MAAM,eAAe,GAAGC,aAAO,CAAC,MAAM,kBAAkB,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC;IAErFC,eAAS,CAAC,MAAK;QACb,IAAI,MAAM,EAAE;YACV,iBAAiB,CAAC,QAAQ,CAAC;QAC7B;AACF,IAAA,CAAC,EAAE,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAEtB,QACEC,eAAC,aAAa,EAAA,EACZ,OAAO,EAAE,OAAO,EAChB,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,GAAG,EACZ,aAAa,EAAA,IAAA,EACb,UAAU,EAAC,uBAAuB,EAAA,QAAA,EAElCA,cAAA,CAACC,sCAAM,EAAA,EAAC,GAAG,EAAE,OAAO,EAAE,iBAAiB,EAAA,IAAA,EAAC,SAAS,EAAE,eAAe,EAAE,EAAE,EAAE,EAAE,EAAA,QAAA,EACvE,MAAM,GAAG,QAAQ,GAAG,cAAc,EAAA,CAC5B,EAAA,CACK;AAEpB;;;;"}
1
+ {"version":3,"file":"DialogOverlap.cjs","sources":["../../../src/components/Dialog/DialogOverlap.tsx"],"sourcesContent":["import {\n type ComponentProps,\n type FC,\n type PropsWithChildren,\n type ReactNode,\n useEffect,\n useMemo,\n useRef,\n} from 'react'\nimport { CSSTransition } from 'react-transition-group'\nimport { tv } from 'tailwind-variants'\n\nimport { Center } from '../Layout'\n\ntype Props = PropsWithChildren<\n {\n isOpen: boolean\n as?: ComponentProps<typeof Center>['as']\n } & ComponentProps<'div'>\n>\n\nconst classNameGenerator = tv({\n base: [\n 'shr-fixed shr-inset-0 shr-z-overlap-base',\n '[&.shr-dialog-transition-enter]:shr-opacity-0',\n '[&.shr-dialog-transition-enter-active]:shr-transition-opacity',\n '[&.shr-dialog-transition-enter-active]:shr-duration-300',\n '[&.shr-dialog-transition-enter-active]:shr-ease-in-out',\n // enter と enter-active は同時に付与されるので、enter-active を強める\n '[&&.shr-dialog-transition-enter-active]:shr-opacity-100',\n '[&.shr-dialog-transition-exit]:shr-opacity-100',\n '[&.shr-dialog-transition-exit-active]:shr-transition-opacity',\n '[&.shr-dialog-transition-exit-active]:shr-duration-300',\n '[&.shr-dialog-transition-exit-active]:shr-ease-in-out',\n // exit と exit-active は同時に付与されるので、exit-active を強める\n '[&&.shr-dialog-transition-exit-active]:shr-opacity-0',\n ],\n})\n\nexport const DialogOverlap: FC<Props> = ({ isOpen, className, children, as }) => {\n const childrenBufferRef = useRef<ReactNode>(null)\n const nodeRef = useRef<HTMLDivElement>(null)\n\n const actualClassName = useMemo(() => classNameGenerator({ className }), [className])\n\n // childrenをrefに保存(毎レンダリング時に最新の値を設定)\n const childrenRef = useRef(children)\n childrenRef.current = children\n\n useEffect(() => {\n if (!isOpen || !nodeRef.current) {\n return\n }\n\n // isOpen が true になった時に初期値を設定\n childrenBufferRef.current = childrenRef.current\n\n // MutationObserver で DOM の変更を監視\n const observer = new MutationObserver(() => {\n childrenBufferRef.current = childrenRef.current\n })\n\n observer.observe(nodeRef.current, {\n childList: true,\n subtree: true,\n characterData: true,\n })\n\n return () => {\n observer.disconnect()\n }\n }, [isOpen])\n\n return (\n <CSSTransition\n nodeRef={nodeRef}\n in={isOpen}\n timeout={300}\n unmountOnExit\n classNames=\"shr-dialog-transition\"\n >\n <Center ref={nodeRef} verticalCentering className={actualClassName} as={as}>\n {isOpen ? children : childrenBufferRef.current}\n </Center>\n </CSSTransition>\n )\n}\n"],"names":["tv","useRef","useMemo","useEffect","_jsx","Center"],"mappings":";;;;;;;;;;;;;AAqBA,MAAM,kBAAkB,GAAGA,QAAE,CAAC;AAC5B,IAAA,IAAI,EAAE;QACJ,0CAA0C;QAC1C,+CAA+C;QAC/C,+DAA+D;QAC/D,yDAAyD;QACzD,wDAAwD;;QAExD,yDAAyD;QACzD,gDAAgD;QAChD,8DAA8D;QAC9D,wDAAwD;QACxD,uDAAuD;;QAEvD,sDAAsD;AACvD,KAAA;AACF,CAAA,CAAC;AAEK,MAAM,aAAa,GAAc,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,EAAE,EAAE,KAAI;AAC9E,IAAA,MAAM,iBAAiB,GAAGC,YAAM,CAAY,IAAI,CAAC;AACjD,IAAA,MAAM,OAAO,GAAGA,YAAM,CAAiB,IAAI,CAAC;AAE5C,IAAA,MAAM,eAAe,GAAGC,aAAO,CAAC,MAAM,kBAAkB,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC;;AAGrF,IAAA,MAAM,WAAW,GAAGD,YAAM,CAAC,QAAQ,CAAC;AACpC,IAAA,WAAW,CAAC,OAAO,GAAG,QAAQ;IAE9BE,eAAS,CAAC,MAAK;QACb,IAAI,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE;YAC/B;QACF;;AAGA,QAAA,iBAAiB,CAAC,OAAO,GAAG,WAAW,CAAC,OAAO;;AAG/C,QAAA,MAAM,QAAQ,GAAG,IAAI,gBAAgB,CAAC,MAAK;AACzC,YAAA,iBAAiB,CAAC,OAAO,GAAG,WAAW,CAAC,OAAO;AACjD,QAAA,CAAC,CAAC;AAEF,QAAA,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE;AAChC,YAAA,SAAS,EAAE,IAAI;AACf,YAAA,OAAO,EAAE,IAAI;AACb,YAAA,aAAa,EAAE,IAAI;AACpB,SAAA,CAAC;AAEF,QAAA,OAAO,MAAK;YACV,QAAQ,CAAC,UAAU,EAAE;AACvB,QAAA,CAAC;AACH,IAAA,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC;IAEZ,QACEC,cAAA,CAAC,aAAa,EAAA,EACZ,OAAO,EAAE,OAAO,EAChB,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,GAAG,EACZ,aAAa,EAAA,IAAA,EACb,UAAU,EAAC,uBAAuB,EAAA,QAAA,EAElCA,cAAA,CAACC,sCAAM,EAAA,EAAC,GAAG,EAAE,OAAO,EAAE,iBAAiB,EAAA,IAAA,EAAC,SAAS,EAAE,eAAe,EAAE,EAAE,EAAE,EAAE,EAAA,QAAA,EACvE,MAAM,GAAG,QAAQ,GAAG,iBAAiB,CAAC,OAAO,EAAA,CACvC,EAAA,CACK;AAEpB;;;;"}
@@ -1,5 +1,5 @@
1
1
  import { jsx } from 'react/jsx-runtime';
2
- import { useState, useRef, useMemo, useEffect } from 'react';
2
+ import { useRef, useMemo, useEffect } from 'react';
3
3
  import { tv as ce } from './../../vendor/.pnpm/tailwind-variants@0.3.1_tailwindcss@3.4.19_ts-node@10.9.2_@swc_core@1.15.17_@types_node@20.19.35_typescript@5.9.3__/vendor/tailwind-variants/dist/index.js';
4
4
  import '../Layout/Stack/Stack.js';
5
5
  import '../Layout/Cluster/Cluster.js';
@@ -27,15 +27,32 @@ const classNameGenerator = ce({
27
27
  ],
28
28
  });
29
29
  const DialogOverlap = ({ isOpen, className, children, as }) => {
30
- const [childrenBuffer, setChildrenBuffer] = useState(null);
30
+ const childrenBufferRef = useRef(null);
31
31
  const nodeRef = useRef(null);
32
32
  const actualClassName = useMemo(() => classNameGenerator({ className }), [className]);
33
+ // childrenをrefに保存(毎レンダリング時に最新の値を設定)
34
+ const childrenRef = useRef(children);
35
+ childrenRef.current = children;
33
36
  useEffect(() => {
34
- if (isOpen) {
35
- setChildrenBuffer(children);
37
+ if (!isOpen || !nodeRef.current) {
38
+ return;
36
39
  }
37
- }, [isOpen, children]);
38
- return (jsx(CSSTransition, { nodeRef: nodeRef, in: isOpen, timeout: 300, unmountOnExit: true, classNames: "shr-dialog-transition", children: jsx(Center, { ref: nodeRef, verticalCentering: true, className: actualClassName, as: as, children: isOpen ? children : childrenBuffer }) }));
40
+ // isOpen が true になった時に初期値を設定
41
+ childrenBufferRef.current = childrenRef.current;
42
+ // MutationObserver で DOM の変更を監視
43
+ const observer = new MutationObserver(() => {
44
+ childrenBufferRef.current = childrenRef.current;
45
+ });
46
+ observer.observe(nodeRef.current, {
47
+ childList: true,
48
+ subtree: true,
49
+ characterData: true,
50
+ });
51
+ return () => {
52
+ observer.disconnect();
53
+ };
54
+ }, [isOpen]);
55
+ return (jsx(CSSTransition, { nodeRef: nodeRef, in: isOpen, timeout: 300, unmountOnExit: true, classNames: "shr-dialog-transition", children: jsx(Center, { ref: nodeRef, verticalCentering: true, className: actualClassName, as: as, children: isOpen ? children : childrenBufferRef.current }) }));
39
56
  };
40
57
 
41
58
  export { DialogOverlap };
@@ -1 +1 @@
1
- {"version":3,"file":"DialogOverlap.js","sources":["../../../src/components/Dialog/DialogOverlap.tsx"],"sourcesContent":["import {\n type ComponentProps,\n type FC,\n type PropsWithChildren,\n type ReactNode,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react'\nimport { CSSTransition } from 'react-transition-group'\nimport { tv } from 'tailwind-variants'\n\nimport { Center } from '../Layout'\n\ntype Props = PropsWithChildren<\n {\n isOpen: boolean\n as?: ComponentProps<typeof Center>['as']\n } & ComponentProps<'div'>\n>\n\nconst classNameGenerator = tv({\n base: [\n 'shr-fixed shr-inset-0 shr-z-overlap-base',\n '[&.shr-dialog-transition-enter]:shr-opacity-0',\n '[&.shr-dialog-transition-enter-active]:shr-transition-opacity',\n '[&.shr-dialog-transition-enter-active]:shr-duration-300',\n '[&.shr-dialog-transition-enter-active]:shr-ease-in-out',\n // enter と enter-active は同時に付与されるので、enter-active を強める\n '[&&.shr-dialog-transition-enter-active]:shr-opacity-100',\n '[&.shr-dialog-transition-exit]:shr-opacity-100',\n '[&.shr-dialog-transition-exit-active]:shr-transition-opacity',\n '[&.shr-dialog-transition-exit-active]:shr-duration-300',\n '[&.shr-dialog-transition-exit-active]:shr-ease-in-out',\n // exit と exit-active は同時に付与されるので、exit-active を強める\n '[&&.shr-dialog-transition-exit-active]:shr-opacity-0',\n ],\n})\n\nexport const DialogOverlap: FC<Props> = ({ isOpen, className, children, as }) => {\n const [childrenBuffer, setChildrenBuffer] = useState<ReactNode>(null)\n const nodeRef = useRef<HTMLDivElement>(null)\n\n const actualClassName = useMemo(() => classNameGenerator({ className }), [className])\n\n useEffect(() => {\n if (isOpen) {\n setChildrenBuffer(children)\n }\n }, [isOpen, children])\n\n return (\n <CSSTransition\n nodeRef={nodeRef}\n in={isOpen}\n timeout={300}\n unmountOnExit\n classNames=\"shr-dialog-transition\"\n >\n <Center ref={nodeRef} verticalCentering className={actualClassName} as={as}>\n {isOpen ? children : childrenBuffer}\n </Center>\n </CSSTransition>\n )\n}\n"],"names":["tv","_jsx"],"mappings":";;;;;;;;;;;AAsBA,MAAM,kBAAkB,GAAGA,EAAE,CAAC;AAC5B,IAAA,IAAI,EAAE;QACJ,0CAA0C;QAC1C,+CAA+C;QAC/C,+DAA+D;QAC/D,yDAAyD;QACzD,wDAAwD;;QAExD,yDAAyD;QACzD,gDAAgD;QAChD,8DAA8D;QAC9D,wDAAwD;QACxD,uDAAuD;;QAEvD,sDAAsD;AACvD,KAAA;AACF,CAAA,CAAC;AAEK,MAAM,aAAa,GAAc,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,EAAE,EAAE,KAAI;IAC9E,MAAM,CAAC,cAAc,EAAE,iBAAiB,CAAC,GAAG,QAAQ,CAAY,IAAI,CAAC;AACrE,IAAA,MAAM,OAAO,GAAG,MAAM,CAAiB,IAAI,CAAC;AAE5C,IAAA,MAAM,eAAe,GAAG,OAAO,CAAC,MAAM,kBAAkB,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC;IAErF,SAAS,CAAC,MAAK;QACb,IAAI,MAAM,EAAE;YACV,iBAAiB,CAAC,QAAQ,CAAC;QAC7B;AACF,IAAA,CAAC,EAAE,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAEtB,QACEC,IAAC,aAAa,EAAA,EACZ,OAAO,EAAE,OAAO,EAChB,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,GAAG,EACZ,aAAa,EAAA,IAAA,EACb,UAAU,EAAC,uBAAuB,EAAA,QAAA,EAElCA,GAAA,CAAC,MAAM,EAAA,EAAC,GAAG,EAAE,OAAO,EAAE,iBAAiB,EAAA,IAAA,EAAC,SAAS,EAAE,eAAe,EAAE,EAAE,EAAE,EAAE,EAAA,QAAA,EACvE,MAAM,GAAG,QAAQ,GAAG,cAAc,EAAA,CAC5B,EAAA,CACK;AAEpB;;;;"}
1
+ {"version":3,"file":"DialogOverlap.js","sources":["../../../src/components/Dialog/DialogOverlap.tsx"],"sourcesContent":["import {\n type ComponentProps,\n type FC,\n type PropsWithChildren,\n type ReactNode,\n useEffect,\n useMemo,\n useRef,\n} from 'react'\nimport { CSSTransition } from 'react-transition-group'\nimport { tv } from 'tailwind-variants'\n\nimport { Center } from '../Layout'\n\ntype Props = PropsWithChildren<\n {\n isOpen: boolean\n as?: ComponentProps<typeof Center>['as']\n } & ComponentProps<'div'>\n>\n\nconst classNameGenerator = tv({\n base: [\n 'shr-fixed shr-inset-0 shr-z-overlap-base',\n '[&.shr-dialog-transition-enter]:shr-opacity-0',\n '[&.shr-dialog-transition-enter-active]:shr-transition-opacity',\n '[&.shr-dialog-transition-enter-active]:shr-duration-300',\n '[&.shr-dialog-transition-enter-active]:shr-ease-in-out',\n // enter と enter-active は同時に付与されるので、enter-active を強める\n '[&&.shr-dialog-transition-enter-active]:shr-opacity-100',\n '[&.shr-dialog-transition-exit]:shr-opacity-100',\n '[&.shr-dialog-transition-exit-active]:shr-transition-opacity',\n '[&.shr-dialog-transition-exit-active]:shr-duration-300',\n '[&.shr-dialog-transition-exit-active]:shr-ease-in-out',\n // exit と exit-active は同時に付与されるので、exit-active を強める\n '[&&.shr-dialog-transition-exit-active]:shr-opacity-0',\n ],\n})\n\nexport const DialogOverlap: FC<Props> = ({ isOpen, className, children, as }) => {\n const childrenBufferRef = useRef<ReactNode>(null)\n const nodeRef = useRef<HTMLDivElement>(null)\n\n const actualClassName = useMemo(() => classNameGenerator({ className }), [className])\n\n // childrenをrefに保存(毎レンダリング時に最新の値を設定)\n const childrenRef = useRef(children)\n childrenRef.current = children\n\n useEffect(() => {\n if (!isOpen || !nodeRef.current) {\n return\n }\n\n // isOpen が true になった時に初期値を設定\n childrenBufferRef.current = childrenRef.current\n\n // MutationObserver で DOM の変更を監視\n const observer = new MutationObserver(() => {\n childrenBufferRef.current = childrenRef.current\n })\n\n observer.observe(nodeRef.current, {\n childList: true,\n subtree: true,\n characterData: true,\n })\n\n return () => {\n observer.disconnect()\n }\n }, [isOpen])\n\n return (\n <CSSTransition\n nodeRef={nodeRef}\n in={isOpen}\n timeout={300}\n unmountOnExit\n classNames=\"shr-dialog-transition\"\n >\n <Center ref={nodeRef} verticalCentering className={actualClassName} as={as}>\n {isOpen ? children : childrenBufferRef.current}\n </Center>\n </CSSTransition>\n )\n}\n"],"names":["tv","_jsx"],"mappings":";;;;;;;;;;;AAqBA,MAAM,kBAAkB,GAAGA,EAAE,CAAC;AAC5B,IAAA,IAAI,EAAE;QACJ,0CAA0C;QAC1C,+CAA+C;QAC/C,+DAA+D;QAC/D,yDAAyD;QACzD,wDAAwD;;QAExD,yDAAyD;QACzD,gDAAgD;QAChD,8DAA8D;QAC9D,wDAAwD;QACxD,uDAAuD;;QAEvD,sDAAsD;AACvD,KAAA;AACF,CAAA,CAAC;AAEK,MAAM,aAAa,GAAc,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,EAAE,EAAE,KAAI;AAC9E,IAAA,MAAM,iBAAiB,GAAG,MAAM,CAAY,IAAI,CAAC;AACjD,IAAA,MAAM,OAAO,GAAG,MAAM,CAAiB,IAAI,CAAC;AAE5C,IAAA,MAAM,eAAe,GAAG,OAAO,CAAC,MAAM,kBAAkB,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC;;AAGrF,IAAA,MAAM,WAAW,GAAG,MAAM,CAAC,QAAQ,CAAC;AACpC,IAAA,WAAW,CAAC,OAAO,GAAG,QAAQ;IAE9B,SAAS,CAAC,MAAK;QACb,IAAI,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE;YAC/B;QACF;;AAGA,QAAA,iBAAiB,CAAC,OAAO,GAAG,WAAW,CAAC,OAAO;;AAG/C,QAAA,MAAM,QAAQ,GAAG,IAAI,gBAAgB,CAAC,MAAK;AACzC,YAAA,iBAAiB,CAAC,OAAO,GAAG,WAAW,CAAC,OAAO;AACjD,QAAA,CAAC,CAAC;AAEF,QAAA,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE;AAChC,YAAA,SAAS,EAAE,IAAI;AACf,YAAA,OAAO,EAAE,IAAI;AACb,YAAA,aAAa,EAAE,IAAI;AACpB,SAAA,CAAC;AAEF,QAAA,OAAO,MAAK;YACV,QAAQ,CAAC,UAAU,EAAE;AACvB,QAAA,CAAC;AACH,IAAA,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC;IAEZ,QACEC,GAAA,CAAC,aAAa,EAAA,EACZ,OAAO,EAAE,OAAO,EAChB,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,GAAG,EACZ,aAAa,EAAA,IAAA,EACb,UAAU,EAAC,uBAAuB,EAAA,QAAA,EAElCA,GAAA,CAAC,MAAM,EAAA,EAAC,GAAG,EAAE,OAAO,EAAE,iBAAiB,EAAA,IAAA,EAAC,SAAS,EAAE,eAAe,EAAE,EAAE,EAAE,EAAE,EAAA,QAAA,EACvE,MAAM,GAAG,QAAQ,GAAG,iBAAiB,CAAC,OAAO,EAAA,CACvC,EAAA,CACK;AAEpB;;;;"}
@@ -13,10 +13,16 @@ require('../../../_virtual/dayjs.min.cjs');
13
13
  require('../../../intl/TimeFormatter.cjs');
14
14
  var libs_debounce = require('../../../libs/debounce.cjs');
15
15
  var tailwind_dialogSize = require('../../../tailwind/dialogSize.cjs');
16
- require('../../../themes/createTextColor.cjs');
16
+ require('../../../themes/createSpacing.cjs');
17
17
  require('../../../_virtual/index.cjs');
18
18
  require('../../../_virtual/index2.cjs');
19
19
  require('../../../themes/createColor.cjs');
20
+ require('../../../themes/createTextColor.cjs');
21
+ require('styled-components');
22
+ require('../../../themes/createShadow/defaultShadow.cjs');
23
+ require('../../../themes/createWidth.cjs');
24
+ require('../../../themes/createBackgroundColor.cjs');
25
+ require('../../../themes/createBorder.cjs');
20
26
  var components_Base_Base = require('../../Base/Base.cjs');
21
27
  require('../../Base/BaseColumn/BaseColumn.cjs');
22
28
  var components_Button_Button = require('../../Button/Button.cjs');
@@ -1 +1 @@
1
- {"version":3,"file":"ModelessDialog.cjs","sources":["../../../../src/components/Dialog/ModelessDialog/ModelessDialog.tsx"],"sourcesContent":["'use client'\n\nimport {\n type ComponentProps,\n type FC,\n type KeyboardEvent,\n type MouseEvent,\n type PropsWithChildren,\n type ReactNode,\n type RefObject,\n memo,\n useCallback,\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 { useIntl } from '../../../intl'\nimport { debounce } from '../../../libs/debounce'\nimport { dialogSize } from '../../../tailwind'\nimport { Base, type BaseElementProps } from '../../Base'\nimport { Button } from '../../Button'\nimport { Heading } from '../../Heading'\nimport { FaGripIcon, FaXmarkIcon } from '../../Icon'\nimport { DialogBody, type Props as DialogBodyProps } from '../DialogBody'\nimport { DialogOverlap } from '../DialogOverlap'\nimport { useDialogPortal } from '../useDialogPortal'\n\nimport type { DialogSize } from '../types'\n\ntype AbstractProps = 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 = AbstractProps &\n Omit<DialogBodyProps, keyof AbstractProps> &\n Omit<BaseElementProps, keyof AbstractProps> &\n Omit<VariantProps<typeof classNameGenerator>, keyof AbstractProps>\n\nconst classNameGenerator = tv({\n slots: {\n overlap: 'shr-inset-[unset]',\n wrapper: 'smarthr-ui-ModelessDialog shr-fixed shr-flex 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] =\n useState<ComponentProps<typeof Draggable>['bounds']>()\n const debounceLiveRegionText = useMemo(() => debounce(setDebouncedLiveRegionText, 600), [])\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 debounceLiveRegionText(txt)\n }, [localize, wrapperPosition, debounceLiveRegionText])\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 handleArrowKey = useCallback(\n (e: KeyboardEvent) => {\n if (!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 [isOpen],\n )\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 ? window.innerHeight / 2 - rect.height / 2 : undefined,\n left: isXCenter ? 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 const actualOnClickClose = useCallback(\n (e: MouseEvent<HTMLButtonElement>) => {\n lastFocusElementRef.current?.focus()\n onClickClose?.(e)\n },\n [onClickClose],\n )\n\n const actualOnPressEscape = useMemo(\n () =>\n onPressEscape\n ? () => {\n lastFocusElementRef.current?.focus()\n onPressEscape()\n }\n : undefined,\n [onPressEscape],\n )\n\n useHandleEscape(\n useMemo(\n () => (actualOnPressEscape && isOpen ? actualOnPressEscape : undefined),\n [isOpen, actualOnPressEscape],\n ),\n )\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 const onDragStart = useCallback((_: any, data: { x: number; y: number }) => setPosition(data), [])\n const onDrag = useCallback((_: 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 return createPortal(\n <DialogOverlap isOpen={isOpen} className={classNames.overlap} as=\"section\">\n <Draggable\n handle=\".smarthr-ui-ModelessDialog-handle\"\n onStart={onDragStart}\n onDrag={onDrag}\n position={position}\n bounds={draggableBounds}\n nodeRef={wrapperRef}\n >\n <Base\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 onArrowKeyDown={handleArrowKey} className={classNames.dialogHandler} />\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 onClick={actualOnClickClose} className={classNames.closeButtonLayout} />\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 </Base>\n </Draggable>\n </DialogOverlap>,\n )\n}\n\nconst Handler = memo<{\n className: string\n onArrowKeyDown: (e: KeyboardEvent) => void\n}>(({ onArrowKeyDown: onDelegateKeyDown, ...rest }) => {\n const { localize } = useIntl()\n const accessibleDefaultTexts = useMemo(\n () => ({\n dialogHandlerAriaRoleDescription: localize({\n id: 'smarthr-ui/ModelessDialog/dialogHandlerAriaRoleDescription',\n defaultText: 'ドラッグ可能',\n }),\n dialogHandlerDescription: localize({\n id: 'smarthr-ui/ModelessDialog/dialogHandlerDescription',\n defaultText: '矢印キーを押して上下左右に移動できます',\n }),\n dialogHandlerAriaLabel: localize({\n id: 'smarthr-ui/ModelessDialog/dialogHandlerAriaLabel',\n defaultText: 'ダイアログの位置',\n }),\n }),\n [localize],\n )\n\n return (\n <>\n <button\n {...rest}\n type=\"button\"\n aria-label={accessibleDefaultTexts.dialogHandlerAriaLabel}\n aria-roledescription={accessibleDefaultTexts.dialogHandlerAriaRoleDescription}\n aria-describedby=\"handler-description\"\n onKeyDown={onDelegateKeyDown}\n >\n <FaGripIcon />\n </button>\n <div className=\"shr-hidden\" id=\"handler-description\">\n {accessibleDefaultTexts.dialogHandlerDescription}\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 onClick: (e: MouseEvent<HTMLButtonElement>) => void\n}>(({ onClick, className }) => {\n const { localize } = useIntl()\n const closeButtonIconAlt = localize({\n id: 'smarthr-ui/ModelessDialog/closeButtonIconAlt',\n defaultText: '閉じる',\n })\n\n return (\n <div className={className}>\n <Button\n type=\"button\"\n size=\"S\"\n onClick={onClick}\n className=\"smarthr-ui-ModelessDialog-closeButton\"\n >\n <FaXmarkIcon alt={closeButtonIconAlt} />\n </Button>\n </div>\n )\n})\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+FA;AACE;AACE;AACA;AACA;;;;;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;;AAGD;;;;;;;AAUM;AACA;;;;AAKD;;;AAML;AAEI;AACA;;;;;AAKD;AAIH;;;;;AAQI;AACE;AACE;;AAEE;AACD;;;AAGH;AACE;;AAEE;AACD;;;AAGH;AACE;AACE;;AAED;;;AAGH;AACE;AACE;;AAED;;;;AAIP;;AAKA;;;AAGF;;;;;;;;AAWE;;AAGE;AACE;AACA;AACD;;AAEL;;AAGE;;AAEA;AACE;;;AAKF;;AAGE;;;;;;AAOA;;AAEJ;AAEA;AAEI;AACA;AACF;AAIF;;AAIU;AACA;;AAEJ;;;AAYJ;;AAEE;AACE;;AAEJ;;AAIA;;AAGF;;AAEE;AACE;AACA;AACD;;;AA+CL;AAEA;AAIE;AACA;;AAGM;AACA;;;AAGA;AACA;;;AAGA;AACA;;AAEH;AAIH;AAiBF;AAEA;AASA;AAIE;;AAEE;AACA;AACD;AAED;AAYF;;"}
1
+ {"version":3,"file":"ModelessDialog.cjs","sources":["../../../../src/components/Dialog/ModelessDialog/ModelessDialog.tsx"],"sourcesContent":["'use client'\n\nimport {\n type ComponentProps,\n type FC,\n type KeyboardEvent,\n type MouseEvent,\n type PropsWithChildren,\n type ReactNode,\n type RefObject,\n memo,\n useCallback,\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 { useIntl } from '../../../intl'\nimport { debounce } from '../../../libs/debounce'\nimport { dialogSize } from '../../../tailwind'\nimport { Base, type BaseElementProps } from '../../Base'\nimport { Button } from '../../Button'\nimport { Heading } from '../../Heading'\nimport { FaGripIcon, FaXmarkIcon } from '../../Icon'\nimport { DialogBody, type Props as DialogBodyProps } from '../DialogBody'\nimport { DialogOverlap } from '../DialogOverlap'\nimport { useDialogPortal } from '../useDialogPortal'\n\nimport type { DialogSize } from '../types'\n\ntype AbstractProps = 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 = AbstractProps &\n Omit<DialogBodyProps, keyof AbstractProps> &\n Omit<BaseElementProps, keyof AbstractProps> &\n Omit<VariantProps<typeof classNameGenerator>, keyof AbstractProps>\n\nconst classNameGenerator = tv({\n slots: {\n overlap: 'shr-inset-[unset]',\n wrapper: 'smarthr-ui-ModelessDialog shr-fixed shr-flex 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] =\n useState<ComponentProps<typeof Draggable>['bounds']>()\n const debounceLiveRegionText = useMemo(() => debounce(setDebouncedLiveRegionText, 600), [])\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 debounceLiveRegionText(txt)\n }, [localize, wrapperPosition, debounceLiveRegionText])\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 handleArrowKey = useCallback(\n (e: KeyboardEvent) => {\n if (!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 [isOpen],\n )\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 ? window.innerHeight / 2 - rect.height / 2 : undefined,\n left: isXCenter ? 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 const actualOnClickClose = useCallback(\n (e: MouseEvent<HTMLButtonElement>) => {\n lastFocusElementRef.current?.focus()\n onClickClose?.(e)\n },\n [onClickClose],\n )\n\n const actualOnPressEscape = useMemo(\n () =>\n onPressEscape\n ? () => {\n lastFocusElementRef.current?.focus()\n onPressEscape()\n }\n : undefined,\n [onPressEscape],\n )\n\n useHandleEscape(\n useMemo(\n () => (actualOnPressEscape && isOpen ? actualOnPressEscape : undefined),\n [isOpen, actualOnPressEscape],\n ),\n )\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 const onDragStart = useCallback((_: any, data: { x: number; y: number }) => setPosition(data), [])\n const onDrag = useCallback((_: 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 return createPortal(\n <DialogOverlap isOpen={isOpen} className={classNames.overlap} as=\"section\">\n <Draggable\n handle=\".smarthr-ui-ModelessDialog-handle\"\n onStart={onDragStart}\n onDrag={onDrag}\n position={position}\n bounds={draggableBounds}\n nodeRef={wrapperRef}\n >\n <Base\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 onArrowKeyDown={handleArrowKey} className={classNames.dialogHandler} />\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 onClick={actualOnClickClose} className={classNames.closeButtonLayout} />\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 </Base>\n </Draggable>\n </DialogOverlap>,\n )\n}\n\nconst Handler = memo<{\n className: string\n onArrowKeyDown: (e: KeyboardEvent) => void\n}>(({ onArrowKeyDown: onDelegateKeyDown, ...rest }) => {\n const { localize } = useIntl()\n const accessibleDefaultTexts = useMemo(\n () => ({\n dialogHandlerAriaRoleDescription: localize({\n id: 'smarthr-ui/ModelessDialog/dialogHandlerAriaRoleDescription',\n defaultText: 'ドラッグ可能',\n }),\n dialogHandlerDescription: localize({\n id: 'smarthr-ui/ModelessDialog/dialogHandlerDescription',\n defaultText: '矢印キーを押して上下左右に移動できます',\n }),\n dialogHandlerAriaLabel: localize({\n id: 'smarthr-ui/ModelessDialog/dialogHandlerAriaLabel',\n defaultText: 'ダイアログの位置',\n }),\n }),\n [localize],\n )\n\n return (\n <>\n <button\n {...rest}\n type=\"button\"\n aria-label={accessibleDefaultTexts.dialogHandlerAriaLabel}\n aria-roledescription={accessibleDefaultTexts.dialogHandlerAriaRoleDescription}\n aria-describedby=\"handler-description\"\n onKeyDown={onDelegateKeyDown}\n >\n <FaGripIcon />\n </button>\n <div className=\"shr-hidden\" id=\"handler-description\">\n {accessibleDefaultTexts.dialogHandlerDescription}\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 onClick: (e: MouseEvent<HTMLButtonElement>) => void\n}>(({ onClick, className }) => {\n const { localize } = useIntl()\n const closeButtonIconAlt = localize({\n id: 'smarthr-ui/ModelessDialog/closeButtonIconAlt',\n defaultText: '閉じる',\n })\n\n return (\n <div className={className}>\n <Button\n type=\"button\"\n size=\"S\"\n onClick={onClick}\n className=\"smarthr-ui-ModelessDialog-closeButton\"\n >\n <FaXmarkIcon alt={closeButtonIconAlt} />\n </Button>\n </div>\n )\n})\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+FA;AACE;AACE;AACA;AACA;;;;;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;;AAGD;;;;;;;AAUM;AACA;;;;AAKD;;;AAML;AAEI;AACA;;;;;AAKD;AAIH;;;;;AAQI;AACE;AACE;;AAEE;AACD;;;AAGH;AACE;;AAEE;AACD;;;AAGH;AACE;AACE;;AAED;;;AAGH;AACE;AACE;;AAED;;;;AAIP;;AAKA;;;AAGF;;;;;;;;AAWE;;AAGE;AACE;AACA;AACD;;AAEL;;AAGE;;AAEA;AACE;;;AAKF;;AAGE;;;;;;AAOA;;AAEJ;AAEA;AAEI;AACA;AACF;AAIF;;AAIU;AACA;;AAEJ;;;AAYJ;;AAEE;AACE;;AAEJ;;AAIA;;AAGF;;AAEE;AACE;AACA;AACD;;;AA+CL;AAEA;AAIE;AACA;;AAGM;AACA;;;AAGA;AACA;;;AAGA;AACA;;AAEH;AAIH;AAiBF;AAEA;AASA;AAIE;;AAEE;AACA;AACD;AAED;AAYF;;"}
@@ -11,10 +11,16 @@ import '../../../_virtual/dayjs.min.js';
11
11
  import '../../../intl/TimeFormatter.js';
12
12
  import { debounce } from '../../../libs/debounce.js';
13
13
  import { dialogSize } from '../../../tailwind/dialogSize.js';
14
- import '../../../themes/createTextColor.js';
14
+ import '../../../themes/createSpacing.js';
15
15
  import '../../../_virtual/index.js';
16
16
  import '../../../_virtual/index2.js';
17
17
  import '../../../themes/createColor.js';
18
+ import '../../../themes/createTextColor.js';
19
+ import 'styled-components';
20
+ import '../../../themes/createShadow/defaultShadow.js';
21
+ import '../../../themes/createWidth.js';
22
+ import '../../../themes/createBackgroundColor.js';
23
+ import '../../../themes/createBorder.js';
18
24
  import { Base } from '../../Base/Base.js';
19
25
  import '../../Base/BaseColumn/BaseColumn.js';
20
26
  import { Button } from '../../Button/Button.js';
@@ -1 +1 @@
1
- {"version":3,"file":"ModelessDialog.js","sources":["../../../../src/components/Dialog/ModelessDialog/ModelessDialog.tsx"],"sourcesContent":["'use client'\n\nimport {\n type ComponentProps,\n type FC,\n type KeyboardEvent,\n type MouseEvent,\n type PropsWithChildren,\n type ReactNode,\n type RefObject,\n memo,\n useCallback,\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 { useIntl } from '../../../intl'\nimport { debounce } from '../../../libs/debounce'\nimport { dialogSize } from '../../../tailwind'\nimport { Base, type BaseElementProps } from '../../Base'\nimport { Button } from '../../Button'\nimport { Heading } from '../../Heading'\nimport { FaGripIcon, FaXmarkIcon } from '../../Icon'\nimport { DialogBody, type Props as DialogBodyProps } from '../DialogBody'\nimport { DialogOverlap } from '../DialogOverlap'\nimport { useDialogPortal } from '../useDialogPortal'\n\nimport type { DialogSize } from '../types'\n\ntype AbstractProps = 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 = AbstractProps &\n Omit<DialogBodyProps, keyof AbstractProps> &\n Omit<BaseElementProps, keyof AbstractProps> &\n Omit<VariantProps<typeof classNameGenerator>, keyof AbstractProps>\n\nconst classNameGenerator = tv({\n slots: {\n overlap: 'shr-inset-[unset]',\n wrapper: 'smarthr-ui-ModelessDialog shr-fixed shr-flex 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] =\n useState<ComponentProps<typeof Draggable>['bounds']>()\n const debounceLiveRegionText = useMemo(() => debounce(setDebouncedLiveRegionText, 600), [])\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 debounceLiveRegionText(txt)\n }, [localize, wrapperPosition, debounceLiveRegionText])\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 handleArrowKey = useCallback(\n (e: KeyboardEvent) => {\n if (!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 [isOpen],\n )\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 ? window.innerHeight / 2 - rect.height / 2 : undefined,\n left: isXCenter ? 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 const actualOnClickClose = useCallback(\n (e: MouseEvent<HTMLButtonElement>) => {\n lastFocusElementRef.current?.focus()\n onClickClose?.(e)\n },\n [onClickClose],\n )\n\n const actualOnPressEscape = useMemo(\n () =>\n onPressEscape\n ? () => {\n lastFocusElementRef.current?.focus()\n onPressEscape()\n }\n : undefined,\n [onPressEscape],\n )\n\n useHandleEscape(\n useMemo(\n () => (actualOnPressEscape && isOpen ? actualOnPressEscape : undefined),\n [isOpen, actualOnPressEscape],\n ),\n )\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 const onDragStart = useCallback((_: any, data: { x: number; y: number }) => setPosition(data), [])\n const onDrag = useCallback((_: 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 return createPortal(\n <DialogOverlap isOpen={isOpen} className={classNames.overlap} as=\"section\">\n <Draggable\n handle=\".smarthr-ui-ModelessDialog-handle\"\n onStart={onDragStart}\n onDrag={onDrag}\n position={position}\n bounds={draggableBounds}\n nodeRef={wrapperRef}\n >\n <Base\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 onArrowKeyDown={handleArrowKey} className={classNames.dialogHandler} />\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 onClick={actualOnClickClose} className={classNames.closeButtonLayout} />\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 </Base>\n </Draggable>\n </DialogOverlap>,\n )\n}\n\nconst Handler = memo<{\n className: string\n onArrowKeyDown: (e: KeyboardEvent) => void\n}>(({ onArrowKeyDown: onDelegateKeyDown, ...rest }) => {\n const { localize } = useIntl()\n const accessibleDefaultTexts = useMemo(\n () => ({\n dialogHandlerAriaRoleDescription: localize({\n id: 'smarthr-ui/ModelessDialog/dialogHandlerAriaRoleDescription',\n defaultText: 'ドラッグ可能',\n }),\n dialogHandlerDescription: localize({\n id: 'smarthr-ui/ModelessDialog/dialogHandlerDescription',\n defaultText: '矢印キーを押して上下左右に移動できます',\n }),\n dialogHandlerAriaLabel: localize({\n id: 'smarthr-ui/ModelessDialog/dialogHandlerAriaLabel',\n defaultText: 'ダイアログの位置',\n }),\n }),\n [localize],\n )\n\n return (\n <>\n <button\n {...rest}\n type=\"button\"\n aria-label={accessibleDefaultTexts.dialogHandlerAriaLabel}\n aria-roledescription={accessibleDefaultTexts.dialogHandlerAriaRoleDescription}\n aria-describedby=\"handler-description\"\n onKeyDown={onDelegateKeyDown}\n >\n <FaGripIcon />\n </button>\n <div className=\"shr-hidden\" id=\"handler-description\">\n {accessibleDefaultTexts.dialogHandlerDescription}\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 onClick: (e: MouseEvent<HTMLButtonElement>) => void\n}>(({ onClick, className }) => {\n const { localize } = useIntl()\n const closeButtonIconAlt = localize({\n id: 'smarthr-ui/ModelessDialog/closeButtonIconAlt',\n defaultText: '閉じる',\n })\n\n return (\n <div className={className}>\n <Button\n type=\"button\"\n size=\"S\"\n onClick={onClick}\n className=\"smarthr-ui-ModelessDialog-closeButton\"\n >\n <FaXmarkIcon alt={closeButtonIconAlt} />\n </Button>\n </div>\n )\n})\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+FA;AACE;AACE;AACA;AACA;;;;;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;;AAGD;;;;;;;AAUM;AACA;;;;AAKD;;;AAML;AAEI;AACA;;;;;AAKD;AAIH;;;;;AAQI;AACE;AACE;;AAEE;AACD;;;AAGH;AACE;;AAEE;AACD;;;AAGH;AACE;AACE;;AAED;;;AAGH;AACE;AACE;;AAED;;;;AAIP;;AAKA;;;AAGF;;;;;;;;AAWE;;AAGE;AACE;AACA;AACD;;AAEL;;AAGE;;AAEA;AACE;;;AAKF;;AAGE;;;;;;AAOA;;AAEJ;AAEA;AAEI;AACA;AACF;AAIF;;AAIU;AACA;;AAEJ;;;AAYJ;;AAEE;AACE;;AAEJ;;AAIA;;AAGF;;AAEE;AACE;AACA;AACD;;;AA+CL;AAEA;AAIE;AACA;;AAGM;AACA;;;AAGA;AACA;;;AAGA;AACA;;AAEH;AAIH;AAiBF;AAEA;AASA;AAIE;;AAEE;AACA;AACD;AAED;AAYF;;"}
1
+ {"version":3,"file":"ModelessDialog.js","sources":["../../../../src/components/Dialog/ModelessDialog/ModelessDialog.tsx"],"sourcesContent":["'use client'\n\nimport {\n type ComponentProps,\n type FC,\n type KeyboardEvent,\n type MouseEvent,\n type PropsWithChildren,\n type ReactNode,\n type RefObject,\n memo,\n useCallback,\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 { useIntl } from '../../../intl'\nimport { debounce } from '../../../libs/debounce'\nimport { dialogSize } from '../../../tailwind'\nimport { Base, type BaseElementProps } from '../../Base'\nimport { Button } from '../../Button'\nimport { Heading } from '../../Heading'\nimport { FaGripIcon, FaXmarkIcon } from '../../Icon'\nimport { DialogBody, type Props as DialogBodyProps } from '../DialogBody'\nimport { DialogOverlap } from '../DialogOverlap'\nimport { useDialogPortal } from '../useDialogPortal'\n\nimport type { DialogSize } from '../types'\n\ntype AbstractProps = 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 = AbstractProps &\n Omit<DialogBodyProps, keyof AbstractProps> &\n Omit<BaseElementProps, keyof AbstractProps> &\n Omit<VariantProps<typeof classNameGenerator>, keyof AbstractProps>\n\nconst classNameGenerator = tv({\n slots: {\n overlap: 'shr-inset-[unset]',\n wrapper: 'smarthr-ui-ModelessDialog shr-fixed shr-flex 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] =\n useState<ComponentProps<typeof Draggable>['bounds']>()\n const debounceLiveRegionText = useMemo(() => debounce(setDebouncedLiveRegionText, 600), [])\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 debounceLiveRegionText(txt)\n }, [localize, wrapperPosition, debounceLiveRegionText])\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 handleArrowKey = useCallback(\n (e: KeyboardEvent) => {\n if (!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 [isOpen],\n )\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 ? window.innerHeight / 2 - rect.height / 2 : undefined,\n left: isXCenter ? 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 const actualOnClickClose = useCallback(\n (e: MouseEvent<HTMLButtonElement>) => {\n lastFocusElementRef.current?.focus()\n onClickClose?.(e)\n },\n [onClickClose],\n )\n\n const actualOnPressEscape = useMemo(\n () =>\n onPressEscape\n ? () => {\n lastFocusElementRef.current?.focus()\n onPressEscape()\n }\n : undefined,\n [onPressEscape],\n )\n\n useHandleEscape(\n useMemo(\n () => (actualOnPressEscape && isOpen ? actualOnPressEscape : undefined),\n [isOpen, actualOnPressEscape],\n ),\n )\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 const onDragStart = useCallback((_: any, data: { x: number; y: number }) => setPosition(data), [])\n const onDrag = useCallback((_: 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 return createPortal(\n <DialogOverlap isOpen={isOpen} className={classNames.overlap} as=\"section\">\n <Draggable\n handle=\".smarthr-ui-ModelessDialog-handle\"\n onStart={onDragStart}\n onDrag={onDrag}\n position={position}\n bounds={draggableBounds}\n nodeRef={wrapperRef}\n >\n <Base\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 onArrowKeyDown={handleArrowKey} className={classNames.dialogHandler} />\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 onClick={actualOnClickClose} className={classNames.closeButtonLayout} />\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 </Base>\n </Draggable>\n </DialogOverlap>,\n )\n}\n\nconst Handler = memo<{\n className: string\n onArrowKeyDown: (e: KeyboardEvent) => void\n}>(({ onArrowKeyDown: onDelegateKeyDown, ...rest }) => {\n const { localize } = useIntl()\n const accessibleDefaultTexts = useMemo(\n () => ({\n dialogHandlerAriaRoleDescription: localize({\n id: 'smarthr-ui/ModelessDialog/dialogHandlerAriaRoleDescription',\n defaultText: 'ドラッグ可能',\n }),\n dialogHandlerDescription: localize({\n id: 'smarthr-ui/ModelessDialog/dialogHandlerDescription',\n defaultText: '矢印キーを押して上下左右に移動できます',\n }),\n dialogHandlerAriaLabel: localize({\n id: 'smarthr-ui/ModelessDialog/dialogHandlerAriaLabel',\n defaultText: 'ダイアログの位置',\n }),\n }),\n [localize],\n )\n\n return (\n <>\n <button\n {...rest}\n type=\"button\"\n aria-label={accessibleDefaultTexts.dialogHandlerAriaLabel}\n aria-roledescription={accessibleDefaultTexts.dialogHandlerAriaRoleDescription}\n aria-describedby=\"handler-description\"\n onKeyDown={onDelegateKeyDown}\n >\n <FaGripIcon />\n </button>\n <div className=\"shr-hidden\" id=\"handler-description\">\n {accessibleDefaultTexts.dialogHandlerDescription}\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 onClick: (e: MouseEvent<HTMLButtonElement>) => void\n}>(({ onClick, className }) => {\n const { localize } = useIntl()\n const closeButtonIconAlt = localize({\n id: 'smarthr-ui/ModelessDialog/closeButtonIconAlt',\n defaultText: '閉じる',\n })\n\n return (\n <div className={className}>\n <Button\n type=\"button\"\n size=\"S\"\n onClick={onClick}\n className=\"smarthr-ui-ModelessDialog-closeButton\"\n >\n <FaXmarkIcon alt={closeButtonIconAlt} />\n </Button>\n </div>\n )\n})\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+FA;AACE;AACE;AACA;AACA;;;;;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;;AAGD;;;;;;;AAUM;AACA;;;;AAKD;;;AAML;AAEI;AACA;;;;;AAKD;AAIH;;;;;AAQI;AACE;AACE;;AAEE;AACD;;;AAGH;AACE;;AAEE;AACD;;;AAGH;AACE;AACE;;AAED;;;AAGH;AACE;AACE;;AAED;;;;AAIP;;AAKA;;;AAGF;;;;;;;;AAWE;;AAGE;AACE;AACA;AACD;;AAEL;;AAGE;;AAEA;AACE;;;AAKF;;AAGE;;;;;;AAOA;;AAEJ;AAEA;AAEI;AACA;AACF;AAIF;;AAIU;AACA;;AAEJ;;;AAYJ;;AAEE;AACE;;AAEJ;;AAIA;;AAGF;;AAEE;AACE;AACA;AACD;;;AA+CL;AAEA;AAIE;AACA;;AAGM;AACA;;;AAGA;AACA;;;AAGA;AACA;;AAEH;AAIH;AAiBF;AAEA;AASA;AAIE;;AAEE;AACA;AACD;AAED;AAYF;;"}
@@ -1,33 +1,73 @@
1
1
  "use client";
2
2
  'use strict';
3
3
 
4
+ var jsxRuntime = require('react/jsx-runtime');
4
5
  var React = require('react');
5
6
  var components_Dialog_useRemoteTrigger = require('../useRemoteTrigger.cjs');
6
7
 
8
+ const CAPTURE_OPTION = {
9
+ capture: true,
10
+ };
7
11
  const onClickRemoteDialogTrigger = (ariaControls) => {
8
12
  document.dispatchEvent(new CustomEvent(components_Dialog_useRemoteTrigger.TRIGGER_EVENT, {
9
13
  detail: { id: ariaControls },
10
14
  }));
11
15
  };
12
- const RemoteDialogTrigger = ({ targetId, children, onClick, ...rest }) => {
16
+ const RemoteDialogTrigger = ({ targetId, children, onClick }) => {
17
+ const ref = React.useRef(null);
18
+ const onClickRef = React.useRef(onClick);
19
+ onClickRef.current = onClick;
13
20
  const actualOnClick = React.useCallback((e) => {
14
21
  // HINT: onClick内で非同期処理される場合、e.currentTargetがnullになってしまう可能性があるため
15
22
  // 先にariaControlsを取得しておく
16
23
  const ariaControls = e.currentTarget.getAttribute('aria-controls');
17
- if (onClick) {
18
- return onClick(() => {
24
+ if (onClickRef.current) {
25
+ return onClickRef.current(() => {
19
26
  onClickRemoteDialogTrigger(ariaControls);
20
27
  });
21
28
  }
22
29
  onClickRemoteDialogTrigger(ariaControls);
23
- }, [onClick]);
24
- const actualTrigger = React.useMemo(() => React.cloneElement(children, {
25
- ...rest,
26
- onClick: actualOnClick,
27
- 'aria-haspopup': 'dialog',
28
- 'aria-controls': targetId,
29
- }), [children, actualOnClick, targetId, rest]);
30
- return actualTrigger;
30
+ }, []);
31
+ React.useEffect(() => {
32
+ const currentRef = ref.current;
33
+ if (!currentRef) {
34
+ return;
35
+ }
36
+ const getClickableElement = () => currentRef.querySelector('button, a');
37
+ // 現在の子要素に対して処理を実行
38
+ const setupElement = () => {
39
+ const element = getClickableElement();
40
+ if (element) {
41
+ element.setAttribute('aria-haspopup', 'dialog');
42
+ element.setAttribute('aria-controls', targetId);
43
+ // HINT: DropdownCloser のonClickより先に実行するため、キャプチャフェーズで処理する
44
+ element.addEventListener('click', actualOnClick, CAPTURE_OPTION);
45
+ }
46
+ };
47
+ const clearEventListener = () => {
48
+ // 既存のイベントリスナーをクリーンアップ
49
+ const element = getClickableElement();
50
+ if (element) {
51
+ element.removeEventListener('click', actualOnClick, CAPTURE_OPTION);
52
+ }
53
+ };
54
+ // 初回セットアップ
55
+ setupElement();
56
+ // MutationObserverでDOM変更を監視
57
+ const observer = new MutationObserver(() => {
58
+ clearEventListener();
59
+ setupElement();
60
+ });
61
+ observer.observe(currentRef, {
62
+ childList: true, // 直接の子要素の追加・削除を監視
63
+ subtree: true, // 子孫要素の変更も監視
64
+ });
65
+ return () => {
66
+ observer.disconnect();
67
+ clearEventListener();
68
+ };
69
+ }, [targetId, actualOnClick]);
70
+ return (jsxRuntime.jsx("span", { className: "smarthr-ui-RemoteDialogTrigger shr-contents", ref: ref, children: children }));
31
71
  };
32
72
 
33
73
  exports.RemoteDialogTrigger = RemoteDialogTrigger;
@@ -1 +1 @@
1
- {"version":3,"file":"RemoteDialogTrigger.cjs","sources":["../../../../src/components/Dialog/RemoteDialogTrigger/RemoteDialogTrigger.tsx"],"sourcesContent":["'use client'\n\nimport {\n type FC,\n type MouseEvent,\n type ReactElement,\n cloneElement,\n useCallback,\n useMemo,\n} from 'react'\n\nimport { TRIGGER_EVENT } from '../useRemoteTrigger'\n\nconst onClickRemoteDialogTrigger = (ariaControls: string) => {\n document.dispatchEvent(\n new CustomEvent(TRIGGER_EVENT, {\n detail: { id: ariaControls },\n }),\n )\n}\n\nexport const RemoteDialogTrigger: FC<{\n targetId: string\n onClick?: (open: () => void) => void\n children: Omit<ReactElement, 'onClick' | 'aria-haspopup' | 'aria-controls'>\n}> = ({ targetId, children, onClick, ...rest }) => {\n const actualOnClick = useCallback(\n (e: MouseEvent<HTMLElement>) => {\n // HINT: onClick内で非同期処理される場合、e.currentTargetがnullになってしまう可能性があるため\n // 先にariaControlsを取得しておく\n const ariaControls = e.currentTarget.getAttribute('aria-controls') as string\n\n if (onClick) {\n return onClick(() => {\n onClickRemoteDialogTrigger(ariaControls)\n })\n }\n\n onClickRemoteDialogTrigger(ariaControls)\n },\n [onClick],\n )\n const actualTrigger = useMemo(\n () =>\n cloneElement(children as ReactElement, {\n ...rest,\n onClick: actualOnClick,\n 'aria-haspopup': 'dialog',\n 'aria-controls': targetId,\n }),\n [children, actualOnClick, targetId, rest],\n )\n\n return actualTrigger\n}\n"],"names":[],"mappings":";;;;;;AAaA;AACE;AAEI;AACD;AAEL;AAEO;AAKL;;;;;;;AASM;;;AAIJ;;AAMI;AACA;AACA;AACA;;AAKN;AACF;;"}
1
+ {"version":3,"file":"RemoteDialogTrigger.cjs","sources":["../../../../src/components/Dialog/RemoteDialogTrigger/RemoteDialogTrigger.tsx"],"sourcesContent":["'use client'\n\nimport { type FC, type PropsWithChildren, useCallback, useEffect, useRef } from 'react'\n\nimport { TRIGGER_EVENT } from '../useRemoteTrigger'\n\nconst CAPTURE_OPTION = {\n capture: true,\n}\n\nconst onClickRemoteDialogTrigger = (ariaControls: string) => {\n document.dispatchEvent(\n new CustomEvent(TRIGGER_EVENT, {\n detail: { id: ariaControls },\n }),\n )\n}\n\nexport const RemoteDialogTrigger: FC<\n PropsWithChildren<{\n targetId: string\n onClick?: (open: () => void) => void\n }>\n> = ({ targetId, children, onClick }) => {\n const ref = useRef<HTMLSpanElement | null>(null)\n const onClickRef = useRef(onClick)\n onClickRef.current = onClick\n\n const actualOnClick = useCallback((e: Event) => {\n // HINT: onClick内で非同期処理される場合、e.currentTargetがnullになってしまう可能性があるため\n // 先にariaControlsを取得しておく\n const ariaControls = (e.currentTarget as HTMLElement).getAttribute('aria-controls') as string\n\n if (onClickRef.current) {\n return onClickRef.current(() => {\n onClickRemoteDialogTrigger(ariaControls)\n })\n }\n\n onClickRemoteDialogTrigger(ariaControls)\n }, [])\n\n useEffect(() => {\n const currentRef = ref.current\n if (!currentRef) {\n return\n }\n\n const getClickableElement = () =>\n currentRef.querySelector<HTMLButtonElement | HTMLAnchorElement>('button, a')\n\n // 現在の子要素に対して処理を実行\n const setupElement = () => {\n const element = getClickableElement()\n if (element) {\n element.setAttribute('aria-haspopup', 'dialog')\n element.setAttribute('aria-controls', targetId)\n // HINT: DropdownCloser のonClickより先に実行するため、キャプチャフェーズで処理する\n element.addEventListener('click', actualOnClick, CAPTURE_OPTION)\n }\n }\n\n const clearEventListener = () => {\n // 既存のイベントリスナーをクリーンアップ\n const element = getClickableElement()\n if (element) {\n element.removeEventListener('click', actualOnClick, CAPTURE_OPTION)\n }\n }\n\n // 初回セットアップ\n setupElement()\n\n // MutationObserverでDOM変更を監視\n const observer = new MutationObserver(() => {\n clearEventListener()\n setupElement()\n })\n\n observer.observe(currentRef, {\n childList: true, // 直接の子要素の追加・削除を監視\n subtree: true, // 子孫要素の変更も監視\n })\n\n return () => {\n observer.disconnect()\n clearEventListener()\n }\n }, [targetId, actualOnClick])\n\n return (\n <span className=\"smarthr-ui-RemoteDialogTrigger shr-contents\" ref={ref}>\n {children}\n </span>\n )\n}\n"],"names":[],"mappings":";;;;;;;AAMA;AACE;;AAGF;AACE;AAEI;AACD;AAEL;AAEO;AAML;AACA;AACA;AAEA;;;;AAKE;AACE;;AAEA;;;;;AAOF;;;;;;;AAUE;;AAEE;AACA;;;;AAIJ;;;AAIE;;;;AAIF;;AAGA;;AAGA;AACE;AACA;AACF;AAEA;;;AAGC;AAED;;AAEE;AACF;AACF;AAEA;AAKF;;"}
@@ -1,6 +1,5 @@
1
- import { type FC, type ReactElement } from 'react';
2
- export declare const RemoteDialogTrigger: FC<{
1
+ import { type FC, type PropsWithChildren } from 'react';
2
+ export declare const RemoteDialogTrigger: FC<PropsWithChildren<{
3
3
  targetId: string;
4
4
  onClick?: (open: () => void) => void;
5
- children: Omit<ReactElement, 'onClick' | 'aria-haspopup' | 'aria-controls'>;
6
- }>;
5
+ }>>;
@@ -1,31 +1,71 @@
1
1
  "use client";
2
- import { useCallback, useMemo, cloneElement } from 'react';
2
+ import { jsx } from 'react/jsx-runtime';
3
+ import { useRef, useCallback, useEffect } from 'react';
3
4
  import { TRIGGER_EVENT } from '../useRemoteTrigger.js';
4
5
 
6
+ const CAPTURE_OPTION = {
7
+ capture: true,
8
+ };
5
9
  const onClickRemoteDialogTrigger = (ariaControls) => {
6
10
  document.dispatchEvent(new CustomEvent(TRIGGER_EVENT, {
7
11
  detail: { id: ariaControls },
8
12
  }));
9
13
  };
10
- const RemoteDialogTrigger = ({ targetId, children, onClick, ...rest }) => {
14
+ const RemoteDialogTrigger = ({ targetId, children, onClick }) => {
15
+ const ref = useRef(null);
16
+ const onClickRef = useRef(onClick);
17
+ onClickRef.current = onClick;
11
18
  const actualOnClick = useCallback((e) => {
12
19
  // HINT: onClick内で非同期処理される場合、e.currentTargetがnullになってしまう可能性があるため
13
20
  // 先にariaControlsを取得しておく
14
21
  const ariaControls = e.currentTarget.getAttribute('aria-controls');
15
- if (onClick) {
16
- return onClick(() => {
22
+ if (onClickRef.current) {
23
+ return onClickRef.current(() => {
17
24
  onClickRemoteDialogTrigger(ariaControls);
18
25
  });
19
26
  }
20
27
  onClickRemoteDialogTrigger(ariaControls);
21
- }, [onClick]);
22
- const actualTrigger = useMemo(() => cloneElement(children, {
23
- ...rest,
24
- onClick: actualOnClick,
25
- 'aria-haspopup': 'dialog',
26
- 'aria-controls': targetId,
27
- }), [children, actualOnClick, targetId, rest]);
28
- return actualTrigger;
28
+ }, []);
29
+ useEffect(() => {
30
+ const currentRef = ref.current;
31
+ if (!currentRef) {
32
+ return;
33
+ }
34
+ const getClickableElement = () => currentRef.querySelector('button, a');
35
+ // 現在の子要素に対して処理を実行
36
+ const setupElement = () => {
37
+ const element = getClickableElement();
38
+ if (element) {
39
+ element.setAttribute('aria-haspopup', 'dialog');
40
+ element.setAttribute('aria-controls', targetId);
41
+ // HINT: DropdownCloser のonClickより先に実行するため、キャプチャフェーズで処理する
42
+ element.addEventListener('click', actualOnClick, CAPTURE_OPTION);
43
+ }
44
+ };
45
+ const clearEventListener = () => {
46
+ // 既存のイベントリスナーをクリーンアップ
47
+ const element = getClickableElement();
48
+ if (element) {
49
+ element.removeEventListener('click', actualOnClick, CAPTURE_OPTION);
50
+ }
51
+ };
52
+ // 初回セットアップ
53
+ setupElement();
54
+ // MutationObserverでDOM変更を監視
55
+ const observer = new MutationObserver(() => {
56
+ clearEventListener();
57
+ setupElement();
58
+ });
59
+ observer.observe(currentRef, {
60
+ childList: true, // 直接の子要素の追加・削除を監視
61
+ subtree: true, // 子孫要素の変更も監視
62
+ });
63
+ return () => {
64
+ observer.disconnect();
65
+ clearEventListener();
66
+ };
67
+ }, [targetId, actualOnClick]);
68
+ return (jsx("span", { className: "smarthr-ui-RemoteDialogTrigger shr-contents", ref: ref, children: children }));
29
69
  };
30
70
 
31
71
  export { RemoteDialogTrigger };
@@ -1 +1 @@
1
- {"version":3,"file":"RemoteDialogTrigger.js","sources":["../../../../src/components/Dialog/RemoteDialogTrigger/RemoteDialogTrigger.tsx"],"sourcesContent":["'use client'\n\nimport {\n type FC,\n type MouseEvent,\n type ReactElement,\n cloneElement,\n useCallback,\n useMemo,\n} from 'react'\n\nimport { TRIGGER_EVENT } from '../useRemoteTrigger'\n\nconst onClickRemoteDialogTrigger = (ariaControls: string) => {\n document.dispatchEvent(\n new CustomEvent(TRIGGER_EVENT, {\n detail: { id: ariaControls },\n }),\n )\n}\n\nexport const RemoteDialogTrigger: FC<{\n targetId: string\n onClick?: (open: () => void) => void\n children: Omit<ReactElement, 'onClick' | 'aria-haspopup' | 'aria-controls'>\n}> = ({ targetId, children, onClick, ...rest }) => {\n const actualOnClick = useCallback(\n (e: MouseEvent<HTMLElement>) => {\n // HINT: onClick内で非同期処理される場合、e.currentTargetがnullになってしまう可能性があるため\n // 先にariaControlsを取得しておく\n const ariaControls = e.currentTarget.getAttribute('aria-controls') as string\n\n if (onClick) {\n return onClick(() => {\n onClickRemoteDialogTrigger(ariaControls)\n })\n }\n\n onClickRemoteDialogTrigger(ariaControls)\n },\n [onClick],\n )\n const actualTrigger = useMemo(\n () =>\n cloneElement(children as ReactElement, {\n ...rest,\n onClick: actualOnClick,\n 'aria-haspopup': 'dialog',\n 'aria-controls': targetId,\n }),\n [children, actualOnClick, targetId, rest],\n )\n\n return actualTrigger\n}\n"],"names":[],"mappings":";;;;AAaA;AACE;AAEI;AACD;AAEL;AAEO;AAKL;;;;;;;AASM;;;AAIJ;;AAMI;AACA;AACA;AACA;;AAKN;AACF;;"}
1
+ {"version":3,"file":"RemoteDialogTrigger.js","sources":["../../../../src/components/Dialog/RemoteDialogTrigger/RemoteDialogTrigger.tsx"],"sourcesContent":["'use client'\n\nimport { type FC, type PropsWithChildren, useCallback, useEffect, useRef } from 'react'\n\nimport { TRIGGER_EVENT } from '../useRemoteTrigger'\n\nconst CAPTURE_OPTION = {\n capture: true,\n}\n\nconst onClickRemoteDialogTrigger = (ariaControls: string) => {\n document.dispatchEvent(\n new CustomEvent(TRIGGER_EVENT, {\n detail: { id: ariaControls },\n }),\n )\n}\n\nexport const RemoteDialogTrigger: FC<\n PropsWithChildren<{\n targetId: string\n onClick?: (open: () => void) => void\n }>\n> = ({ targetId, children, onClick }) => {\n const ref = useRef<HTMLSpanElement | null>(null)\n const onClickRef = useRef(onClick)\n onClickRef.current = onClick\n\n const actualOnClick = useCallback((e: Event) => {\n // HINT: onClick内で非同期処理される場合、e.currentTargetがnullになってしまう可能性があるため\n // 先にariaControlsを取得しておく\n const ariaControls = (e.currentTarget as HTMLElement).getAttribute('aria-controls') as string\n\n if (onClickRef.current) {\n return onClickRef.current(() => {\n onClickRemoteDialogTrigger(ariaControls)\n })\n }\n\n onClickRemoteDialogTrigger(ariaControls)\n }, [])\n\n useEffect(() => {\n const currentRef = ref.current\n if (!currentRef) {\n return\n }\n\n const getClickableElement = () =>\n currentRef.querySelector<HTMLButtonElement | HTMLAnchorElement>('button, a')\n\n // 現在の子要素に対して処理を実行\n const setupElement = () => {\n const element = getClickableElement()\n if (element) {\n element.setAttribute('aria-haspopup', 'dialog')\n element.setAttribute('aria-controls', targetId)\n // HINT: DropdownCloser のonClickより先に実行するため、キャプチャフェーズで処理する\n element.addEventListener('click', actualOnClick, CAPTURE_OPTION)\n }\n }\n\n const clearEventListener = () => {\n // 既存のイベントリスナーをクリーンアップ\n const element = getClickableElement()\n if (element) {\n element.removeEventListener('click', actualOnClick, CAPTURE_OPTION)\n }\n }\n\n // 初回セットアップ\n setupElement()\n\n // MutationObserverでDOM変更を監視\n const observer = new MutationObserver(() => {\n clearEventListener()\n setupElement()\n })\n\n observer.observe(currentRef, {\n childList: true, // 直接の子要素の追加・削除を監視\n subtree: true, // 子孫要素の変更も監視\n })\n\n return () => {\n observer.disconnect()\n clearEventListener()\n }\n }, [targetId, actualOnClick])\n\n return (\n <span className=\"smarthr-ui-RemoteDialogTrigger shr-contents\" ref={ref}>\n {children}\n </span>\n )\n}\n"],"names":[],"mappings":";;;;;AAMA;AACE;;AAGF;AACE;AAEI;AACD;AAEL;AAEO;AAML;AACA;AACA;AAEA;;;;AAKE;AACE;;AAEA;;;;;AAOF;;;;;;;AAUE;;AAEE;AACA;;;;AAIJ;;;AAIE;;;;AAIF;;AAGA;;AAGA;AACE;AACA;AACF;AAEA;;;AAGC;AAED;;AAEE;AACF;AACF;AAEA;AAKF;;"}