react-panel-layout 0.6.0 → 0.6.1

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 (216) hide show
  1. package/dist/{FloatingPanelFrame-SgYLc6Ud.js → FloatingPanelFrame-3eU9AwPo.js} +2 -2
  2. package/dist/{FloatingPanelFrame-SgYLc6Ud.js.map → FloatingPanelFrame-3eU9AwPo.js.map} +1 -1
  3. package/dist/FloatingWindow-CUXnEtrb.js +827 -0
  4. package/dist/FloatingWindow-CUXnEtrb.js.map +1 -0
  5. package/dist/FloatingWindow-DMwyK0eK.cjs +2 -0
  6. package/dist/FloatingWindow-DMwyK0eK.cjs.map +1 -0
  7. package/dist/GridLayout-DKTg_N61.cjs +2 -0
  8. package/dist/{GridLayout-B4VRsC0r.cjs.map → GridLayout-DKTg_N61.cjs.map} +1 -1
  9. package/dist/{GridLayout-BltqeCPK.js → GridLayout-UWNxXw77.js} +34 -35
  10. package/dist/{GridLayout-BltqeCPK.js.map → GridLayout-UWNxXw77.js.map} +1 -1
  11. package/dist/{HorizontalDivider-WF1k_qND.js → HorizontalDivider-DdxzfV0l.js} +3 -3
  12. package/dist/{HorizontalDivider-WF1k_qND.js.map → HorizontalDivider-DdxzfV0l.js.map} +1 -1
  13. package/dist/{HorizontalDivider-B5Z-KZLk.cjs → HorizontalDivider-_pgV4Mcv.cjs} +2 -2
  14. package/dist/{HorizontalDivider-B5Z-KZLk.cjs.map → HorizontalDivider-_pgV4Mcv.cjs.map} +1 -1
  15. package/dist/{PanelSystem-Dr1TBhxM.js → PanelSystem-BqUzNtf2.js} +5 -5
  16. package/dist/{PanelSystem-Dr1TBhxM.js.map → PanelSystem-BqUzNtf2.js.map} +1 -1
  17. package/dist/{PanelSystem-Bs8bQwQF.cjs → PanelSystem-D603LKKv.cjs} +2 -2
  18. package/dist/{PanelSystem-Bs8bQwQF.cjs.map → PanelSystem-D603LKKv.cjs.map} +1 -1
  19. package/dist/ResizeHandle-CBcAS918.cjs +2 -0
  20. package/dist/{ResizeHandle-CScipO5l.cjs.map → ResizeHandle-CBcAS918.cjs.map} +1 -1
  21. package/dist/{ResizeHandle-CdA_JYfN.js → ResizeHandle-CXjc1meV.js} +28 -29
  22. package/dist/{ResizeHandle-CdA_JYfN.js.map → ResizeHandle-CXjc1meV.js.map} +1 -1
  23. package/dist/SwipePivotTabBar-DWrCuwEI.js +411 -0
  24. package/dist/SwipePivotTabBar-DWrCuwEI.js.map +1 -0
  25. package/dist/SwipePivotTabBar-fjjXkpj7.cjs +2 -0
  26. package/dist/SwipePivotTabBar-fjjXkpj7.cjs.map +1 -0
  27. package/dist/components/gesture/SwipeSafeZone.d.ts +40 -0
  28. package/dist/components/window/Drawer.d.ts +3 -1
  29. package/dist/components/window/DrawerLayers.d.ts +1 -1
  30. package/dist/components/window/drawerStyles.d.ts +69 -0
  31. package/dist/components/window/drawerSwipeConfig.d.ts +29 -0
  32. package/dist/components/window/useDrawerSwipeTransform.d.ts +23 -0
  33. package/dist/config.cjs +1 -1
  34. package/dist/config.js +3 -3
  35. package/dist/constants/styles.d.ts +17 -0
  36. package/dist/dialog/index.d.ts +69 -0
  37. package/dist/floating.js +1 -1
  38. package/dist/grid.cjs +1 -1
  39. package/dist/grid.js +2 -2
  40. package/dist/hooks/gesture/testing/createGestureSimulator.d.ts +7 -0
  41. package/dist/hooks/gesture/types.d.ts +48 -5
  42. package/dist/hooks/gesture/utils.d.ts +19 -0
  43. package/dist/hooks/useAnimationFrame.d.ts +2 -0
  44. package/dist/hooks/useOperationContinuity.d.ts +64 -0
  45. package/dist/hooks/useResizeObserver.d.ts +33 -1
  46. package/dist/hooks/useSharedElementTransition.d.ts +112 -0
  47. package/dist/hooks/useSwipeContentTransform.d.ts +9 -2
  48. package/dist/index.cjs +1 -1
  49. package/dist/index.js +7 -7
  50. package/dist/modules/dialog/AlertDialog.d.ts +9 -0
  51. package/dist/modules/dialog/DialogContainer.d.ts +37 -0
  52. package/dist/modules/dialog/Modal.d.ts +26 -0
  53. package/dist/modules/dialog/SwipeDialogContainer.d.ts +16 -0
  54. package/dist/modules/dialog/dialogAnimationUtils.d.ts +113 -0
  55. package/dist/modules/dialog/types.d.ts +183 -0
  56. package/dist/modules/dialog/useDialog.d.ts +39 -0
  57. package/dist/modules/dialog/useDialogContainer.d.ts +47 -0
  58. package/dist/modules/dialog/useDialogSwipeInput.d.ts +70 -0
  59. package/dist/modules/dialog/useDialogTransform.d.ts +82 -0
  60. package/dist/modules/drawer/types.d.ts +74 -0
  61. package/dist/modules/drawer/useDrawerSwipeInput.d.ts +24 -0
  62. package/dist/modules/pivot/SwipePivotTabBar.d.ts +3 -0
  63. package/dist/modules/stack/SwipeStackContent.d.ts +6 -3
  64. package/dist/modules/stack/SwipeStackOutlet.d.ts +4 -4
  65. package/dist/modules/stack/computeSwipeStackTransform.d.ts +1 -1
  66. package/dist/panels.cjs +1 -1
  67. package/dist/panels.js +1 -1
  68. package/dist/pivot.cjs +1 -1
  69. package/dist/pivot.js +1 -1
  70. package/dist/resizer.cjs +1 -1
  71. package/dist/resizer.js +2 -2
  72. package/dist/stack.cjs +1 -1
  73. package/dist/stack.cjs.map +1 -1
  74. package/dist/stack.js +503 -762
  75. package/dist/stack.js.map +1 -1
  76. package/dist/sticky-header/calculateStickyMetrics.d.ts +28 -0
  77. package/dist/sticky-header.cjs +1 -1
  78. package/dist/sticky-header.cjs.map +1 -1
  79. package/dist/sticky-header.js +59 -51
  80. package/dist/sticky-header.js.map +1 -1
  81. package/dist/{styles-DPPuJ0sf.js → styles-NkjuMOVS.js} +13 -13
  82. package/dist/{styles-DPPuJ0sf.js.map → styles-NkjuMOVS.js.map} +1 -1
  83. package/dist/styles-qf6ptVLD.cjs.map +1 -1
  84. package/dist/types.d.ts +16 -0
  85. package/dist/useDocumentPointerEvents-DXxw3qWj.js +54 -0
  86. package/dist/useDocumentPointerEvents-DXxw3qWj.js.map +1 -0
  87. package/dist/useDocumentPointerEvents-DxDSOtip.cjs +2 -0
  88. package/dist/useDocumentPointerEvents-DxDSOtip.cjs.map +1 -0
  89. package/dist/useNativeGestureGuard-C7TSqEkr.cjs +2 -0
  90. package/dist/useNativeGestureGuard-C7TSqEkr.cjs.map +1 -0
  91. package/dist/useNativeGestureGuard-CGYo6O0r.js +347 -0
  92. package/dist/useNativeGestureGuard-CGYo6O0r.js.map +1 -0
  93. package/dist/window/index.d.ts +2 -0
  94. package/dist/window.cjs +1 -1
  95. package/dist/window.cjs.map +1 -1
  96. package/dist/window.js +114 -103
  97. package/dist/window.js.map +1 -1
  98. package/package.json +6 -1
  99. package/src/components/gesture/SwipeSafeZone.tsx +69 -0
  100. package/src/components/window/Drawer.tsx +249 -162
  101. package/src/components/window/DrawerLayers.tsx +13 -3
  102. package/src/components/window/drawerStyles.spec.ts +263 -0
  103. package/src/components/window/drawerStyles.ts +228 -0
  104. package/src/components/window/drawerSwipeConfig.spec.ts +131 -0
  105. package/src/components/window/drawerSwipeConfig.ts +112 -0
  106. package/src/components/window/useDrawerSwipeTransform.spec.ts +234 -0
  107. package/src/components/window/useDrawerSwipeTransform.ts +129 -0
  108. package/src/constants/styles.ts +19 -0
  109. package/src/demo/pages/Dialog/alerts/index.tsx +22 -0
  110. package/src/demo/pages/Dialog/card/index.tsx +22 -0
  111. package/src/demo/pages/Dialog/components/AlertDialogDemo.tsx +124 -0
  112. package/src/demo/pages/Dialog/components/CardExpandDemo.module.css +243 -0
  113. package/src/demo/pages/Dialog/components/CardExpandDemo.tsx +204 -0
  114. package/src/demo/pages/Dialog/components/CustomAlertDialogDemo.tsx +219 -0
  115. package/src/demo/pages/Dialog/components/DialogDemos.module.css +77 -0
  116. package/src/demo/pages/Dialog/components/ModalBasics.tsx +45 -0
  117. package/src/demo/pages/Dialog/components/SwipeDialogDemo.module.css +77 -0
  118. package/src/demo/pages/Dialog/components/SwipeDialogDemo.tsx +181 -0
  119. package/src/demo/pages/Dialog/custom-alert/index.tsx +22 -0
  120. package/src/demo/pages/Dialog/modal/index.tsx +17 -0
  121. package/src/demo/pages/Dialog/swipe/index.tsx +22 -0
  122. package/src/demo/pages/Drawer/components/DrawerSwipe.module.css +316 -0
  123. package/src/demo/pages/Drawer/components/DrawerSwipe.tsx +178 -0
  124. package/src/demo/pages/Drawer/swipe/index.tsx +17 -0
  125. package/src/demo/pages/Pivot/components/SwipeTabsPivot.tsx +54 -23
  126. package/src/demo/pages/Pivot/swipe-debug/index.tsx +1 -1
  127. package/src/demo/pages/Stack/components/StackBasics.spec.tsx +152 -0
  128. package/src/demo/pages/Stack/components/StackBasics.tsx +179 -95
  129. package/src/demo/pages/Stack/components/StackTablet.spec.tsx +120 -0
  130. package/src/demo/pages/Stack/components/StackTablet.tsx +42 -21
  131. package/src/demo/routes.tsx +22 -1
  132. package/src/dialog/index.ts +85 -0
  133. package/src/hooks/gesture/testing/createGestureSimulator.spec.ts +68 -64
  134. package/src/hooks/gesture/testing/createGestureSimulator.ts +112 -37
  135. package/src/hooks/gesture/types.ts +83 -6
  136. package/src/hooks/gesture/useEdgeSwipeInput.spec.ts +22 -14
  137. package/src/hooks/gesture/useNativeGestureGuard.spec.ts +91 -31
  138. package/src/hooks/gesture/useNativeGestureGuard.ts +3 -1
  139. package/src/hooks/gesture/utils.ts +91 -0
  140. package/src/hooks/useAnimatedVisibility.spec.ts +44 -24
  141. package/src/hooks/useAnimatedVisibility.ts +28 -2
  142. package/src/hooks/useAnimationFrame.ts +8 -0
  143. package/src/hooks/useOperationContinuity.spec.ts +387 -0
  144. package/src/hooks/useOperationContinuity.ts +135 -0
  145. package/src/hooks/useResizeObserver.spec.tsx +277 -0
  146. package/src/hooks/useResizeObserver.tsx +108 -39
  147. package/src/hooks/useScrollContainer.ts +4 -10
  148. package/src/hooks/useSharedElementTransition.ts +333 -0
  149. package/src/hooks/useSwipeContentTransform.spec.ts +18 -18
  150. package/src/hooks/useSwipeContentTransform.ts +166 -28
  151. package/src/modules/dialog/AlertDialog.spec.tsx +387 -0
  152. package/src/modules/dialog/AlertDialog.tsx +221 -0
  153. package/src/modules/dialog/DialogContainer.spec.tsx +228 -0
  154. package/src/modules/dialog/DialogContainer.tsx +188 -0
  155. package/src/modules/dialog/Modal.spec.tsx +220 -0
  156. package/src/modules/dialog/Modal.tsx +182 -0
  157. package/src/modules/dialog/SwipeDialogContainer.tsx +208 -0
  158. package/src/modules/dialog/dialogAnimationUtils.spec.ts +253 -0
  159. package/src/modules/dialog/dialogAnimationUtils.ts +297 -0
  160. package/src/modules/dialog/types.ts +186 -0
  161. package/src/modules/dialog/useDialog.spec.tsx +447 -0
  162. package/src/modules/dialog/useDialog.ts +214 -0
  163. package/src/modules/dialog/useDialogContainer.spec.ts +331 -0
  164. package/src/modules/dialog/useDialogContainer.ts +150 -0
  165. package/src/modules/dialog/useDialogSwipeInput.spec.ts +157 -0
  166. package/src/modules/dialog/useDialogSwipeInput.ts +319 -0
  167. package/src/modules/dialog/useDialogTransform.spec.ts +370 -0
  168. package/src/modules/dialog/useDialogTransform.ts +407 -0
  169. package/src/modules/drawer/types.ts +102 -0
  170. package/src/modules/drawer/useDrawerSwipeInput.spec.ts +566 -0
  171. package/src/modules/drawer/useDrawerSwipeInput.ts +399 -0
  172. package/src/modules/panels/rendering/ContentRegistry.spec.tsx +21 -14
  173. package/src/modules/pivot/SwipePivotContent.position.spec.tsx +12 -8
  174. package/src/modules/pivot/SwipePivotContent.spec.tsx +55 -25
  175. package/src/modules/pivot/SwipePivotContent.tsx +2 -2
  176. package/src/modules/pivot/SwipePivotTabBar.spec.tsx +85 -68
  177. package/src/modules/pivot/SwipePivotTabBar.tsx +75 -15
  178. package/src/modules/pivot/scaleInputState.spec.ts +11 -2
  179. package/src/modules/pivot/usePivot.spec.ts +17 -3
  180. package/src/modules/pivot/usePivotSwipeInput.spec.ts +182 -123
  181. package/src/modules/stack/SwipeStackContent.spec.tsx +387 -100
  182. package/src/modules/stack/SwipeStackContent.tsx +43 -33
  183. package/src/modules/stack/SwipeStackOutlet.spec.tsx +14 -16
  184. package/src/modules/stack/SwipeStackOutlet.tsx +6 -6
  185. package/src/modules/stack/computeSwipeStackTransform.spec.ts +5 -5
  186. package/src/modules/stack/computeSwipeStackTransform.ts +3 -3
  187. package/src/modules/stack/swipeTransitionContinuity.spec.tsx +1133 -0
  188. package/src/modules/stack/useStackAnimationState.spec.ts +3 -1
  189. package/src/modules/stack/useStackAnimationState.ts +18 -13
  190. package/src/modules/stack/useStackNavigation.spec.ts +198 -3
  191. package/src/modules/stack/useStackNavigation.tsx +113 -56
  192. package/src/modules/stack/useStackSwipeInput.spec.ts +65 -32
  193. package/src/modules/stack/useStackSwipeInput.ts +1 -1
  194. package/src/sticky-header/StickyArea.tsx +29 -57
  195. package/src/sticky-header/calculateStickyMetrics.spec.ts +105 -0
  196. package/src/sticky-header/calculateStickyMetrics.ts +50 -0
  197. package/src/types.ts +18 -0
  198. package/src/window/index.ts +2 -0
  199. package/dist/FloatingWindow-BpdOpg_L.js +0 -400
  200. package/dist/FloatingWindow-BpdOpg_L.js.map +0 -1
  201. package/dist/FloatingWindow-TCDNY5gE.cjs +0 -2
  202. package/dist/FloatingWindow-TCDNY5gE.cjs.map +0 -1
  203. package/dist/GridLayout-B4VRsC0r.cjs +0 -2
  204. package/dist/ResizeHandle-CScipO5l.cjs +0 -2
  205. package/dist/SwipePivotTabBar-BGO9X94m.js +0 -407
  206. package/dist/SwipePivotTabBar-BGO9X94m.js.map +0 -1
  207. package/dist/SwipePivotTabBar-BrQismcZ.cjs +0 -2
  208. package/dist/SwipePivotTabBar-BrQismcZ.cjs.map +0 -1
  209. package/dist/useDocumentPointerEvents-CKdhGXd0.js +0 -46
  210. package/dist/useDocumentPointerEvents-CKdhGXd0.js.map +0 -1
  211. package/dist/useDocumentPointerEvents-ChqrKXDk.cjs +0 -2
  212. package/dist/useDocumentPointerEvents-ChqrKXDk.cjs.map +0 -1
  213. package/dist/useEffectEvent-Dp7HLCf0.js +0 -13
  214. package/dist/useEffectEvent-Dp7HLCf0.js.map +0 -1
  215. package/dist/useEffectEvent-huSsGUnl.cjs +0 -2
  216. package/dist/useEffectEvent-huSsGUnl.cjs.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useNativeGestureGuard-CGYo6O0r.js","sources":["../src/hooks/gesture/usePointerTracking.ts","../src/hooks/gesture/useDirectionalLock.ts","../src/hooks/gesture/utils.ts","../src/hooks/gesture/types.ts","../src/hooks/gesture/useSwipeInput.ts","../src/hooks/gesture/useEdgeSwipeInput.ts","../src/hooks/gesture/useNativeGestureGuard.ts"],"sourcesContent":["/**\n * @file Hook for tracking pointer state during gestures.\n *\n * Provides low-level pointer tracking with position, velocity, and timing.\n * This hook serves as the foundation for higher-level gesture hooks.\n */\nimport * as React from \"react\";\nimport { useDocumentPointerEvents } from \"../useDocumentPointerEvents.js\";\nimport { useEffectEvent } from \"../useEffectEvent.js\";\nimport type {\n PointerTrackingState,\n TimestampedPoint,\n UsePointerTrackingOptions,\n UsePointerTrackingResult,\n} from \"./types.js\";\n\n/**\n * Initial state for pointer tracking.\n */\nconst INITIAL_STATE: PointerTrackingState = {\n isDown: false,\n start: null,\n current: null,\n pointerId: null,\n wasCanceled: false,\n};\n\n/**\n * Creates a timestamped point from pointer event coordinates.\n */\nconst createTimestampedPoint = (clientX: number, clientY: number): TimestampedPoint => ({\n x: clientX,\n y: clientY,\n timestamp: performance.now(),\n});\n\n/**\n * Hook for tracking pointer state during gestures.\n *\n * Tracks pointer down/move/up events and provides position and timing data\n * that can be used by higher-level gesture detection hooks.\n *\n * @example\n * ```tsx\n * const { state, onPointerDown, reset } = usePointerTracking({\n * enabled: true,\n * primaryOnly: true,\n * });\n *\n * return (\n * <div onPointerDown={onPointerDown}>\n * {state.isDown && <span>Tracking...</span>}\n * </div>\n * );\n * ```\n */\nexport function usePointerTracking(options: UsePointerTrackingOptions): UsePointerTrackingResult {\n const { enabled, primaryOnly = true } = options;\n\n const [state, setState] = React.useState<PointerTrackingState>(INITIAL_STATE);\n\n const reset = React.useCallback(() => {\n setState(INITIAL_STATE);\n }, []);\n\n const handlePointerDown = useEffectEvent((event: React.PointerEvent) => {\n if (!enabled) {\n return;\n }\n\n // Filter non-primary pointers if primaryOnly is set\n if (primaryOnly && !event.isPrimary) {\n return;\n }\n\n // Only track left mouse button or touch/pen\n if (event.pointerType === \"mouse\" && event.button !== 0) {\n return;\n }\n\n const point = createTimestampedPoint(event.clientX, event.clientY);\n\n setState({\n isDown: true,\n start: point,\n current: point,\n pointerId: event.pointerId,\n wasCanceled: false,\n });\n });\n\n const handlePointerMove = useEffectEvent((event: PointerEvent) => {\n // Verify this is the tracked pointer\n if (state.pointerId !== event.pointerId) {\n return;\n }\n\n const point = createTimestampedPoint(event.clientX, event.clientY);\n\n setState((prev) => ({\n ...prev,\n current: point,\n }));\n });\n\n const handlePointerUp = useEffectEvent(() => {\n setState(INITIAL_STATE);\n });\n\n const handlePointerCancel = useEffectEvent(() => {\n setState({ ...INITIAL_STATE, wasCanceled: true });\n });\n\n // Use document-level pointer events for tracking after pointer down\n const shouldTrackDocument = state.isDown ? enabled : false;\n useDocumentPointerEvents(shouldTrackDocument, {\n onMove: handlePointerMove,\n onUp: handlePointerUp,\n onCancel: handlePointerCancel,\n });\n\n // Reset state when disabled\n React.useEffect(() => {\n if (!enabled && state.isDown) {\n reset();\n }\n }, [enabled, state.isDown, reset]);\n\n return {\n state,\n onPointerDown: handlePointerDown,\n reset,\n };\n}\n","/**\n * @file Hook for locking gesture direction after threshold is exceeded.\n *\n * Once the user moves beyond the lock threshold, the direction is locked\n * to either horizontal or vertical, preventing diagonal gestures from\n * triggering both scroll and swipe.\n */\nimport * as React from \"react\";\nimport type {\n GestureAxis,\n UseDirectionalLockOptions,\n UseDirectionalLockResult,\n} from \"./types.js\";\n\nconst DEFAULT_LOCK_THRESHOLD = 10;\n\n/**\n * Determines which axis the gesture is primarily moving along.\n *\n * @param deltaX - Horizontal displacement from start\n * @param deltaY - Vertical displacement from start\n * @returns The dominant axis, or null if movement is insufficient\n */\nconst determineAxis = (deltaX: number, deltaY: number): GestureAxis | null => {\n const absX = Math.abs(deltaX);\n const absY = Math.abs(deltaY);\n\n // Require at least some movement in one direction\n if (absX === 0 && absY === 0) {\n return null;\n }\n\n // Use a 1.5x ratio to ensure clear direction before locking\n // This prevents near-diagonal gestures from locking prematurely\n if (absX > absY * 1.5) {\n return \"horizontal\";\n }\n\n if (absY > absX * 1.5) {\n return \"vertical\";\n }\n\n // Still ambiguous\n return null;\n};\n\n/**\n * Hook for locking gesture direction after threshold is exceeded.\n *\n * This hook tracks pointer movement and locks to horizontal or vertical\n * direction once the movement exceeds the configured threshold. This\n * prevents diagonal gestures from triggering both scroll and swipe behaviors.\n *\n * @example\n * ```tsx\n * const { state: tracking, onPointerDown } = usePointerTracking({ enabled: true });\n * const { lockedAxis, isLocked } = useDirectionalLock({\n * tracking,\n * lockThreshold: 10,\n * });\n *\n * // lockedAxis will be \"horizontal\" or \"vertical\" once determined\n * ```\n */\nexport function useDirectionalLock(options: UseDirectionalLockOptions): UseDirectionalLockResult {\n const { tracking, lockThreshold = DEFAULT_LOCK_THRESHOLD } = options;\n\n const [lockedAxis, setLockedAxis] = React.useState<GestureAxis | null>(null);\n\n const reset = React.useCallback(() => {\n setLockedAxis(null);\n }, []);\n\n // Determine direction when tracking is active\n React.useEffect(() => {\n // Reset lock when pointer is released\n if (!tracking.isDown) {\n if (lockedAxis !== null) {\n reset();\n }\n return;\n }\n\n // Already locked, no need to recalculate\n if (lockedAxis !== null) {\n return;\n }\n\n // Need start and current positions\n if (!tracking.start || !tracking.current) {\n return;\n }\n\n const deltaX = tracking.current.x - tracking.start.x;\n const deltaY = tracking.current.y - tracking.start.y;\n\n // Check if we've exceeded the lock threshold\n const distance = Math.max(Math.abs(deltaX), Math.abs(deltaY));\n if (distance < lockThreshold) {\n return;\n }\n\n // Try to determine axis\n const axis = determineAxis(deltaX, deltaY);\n if (axis !== null) {\n setLockedAxis(axis);\n }\n }, [tracking.isDown, tracking.start, tracking.current, lockedAxis, lockThreshold, reset]);\n\n return {\n lockedAxis,\n isLocked: lockedAxis !== null,\n reset,\n };\n}\n","/**\n * @file Utility functions for gesture detection hooks.\n *\n * Contains shared calculations and helper functions used across\n * gesture-related hooks to avoid code duplication.\n */\nimport type * as React from \"react\";\n\n/**\n * Calculate velocity from displacement and time elapsed.\n *\n * @param displacement - Distance traveled in pixels\n * @param startTime - Start timestamp in milliseconds\n * @param currentTime - Current timestamp in milliseconds\n * @returns Velocity in pixels per millisecond\n */\nexport const calculateVelocity = (\n displacement: number,\n startTime: number,\n currentTime: number,\n): number => {\n const elapsed = currentTime - startTime;\n if (elapsed <= 0) {\n return 0;\n }\n return displacement / elapsed;\n};\n\n/**\n * Determine direction from displacement.\n *\n * @param displacement - Distance from start position\n * @returns -1 for backward (left/up), 0 for no movement, 1 for forward (right/down)\n */\nexport const determineDirection = (displacement: number): -1 | 0 | 1 => {\n if (displacement > 0) {\n return 1;\n }\n if (displacement < 0) {\n return -1;\n }\n return 0;\n};\n\n/**\n * Container props type for gesture handling.\n */\nexport type GestureContainerProps = React.HTMLAttributes<HTMLElement> & {\n style: React.CSSProperties;\n};\n\n/**\n * Merge multiple container props objects for gesture handling.\n *\n * Combines style objects and chains onPointerDown handlers.\n * Useful when combining multiple gesture hooks that each provide\n * their own container props (e.g., swipe input + native gesture guard).\n *\n * @param propsArray - Array of container props to merge\n * @returns Merged container props with combined styles and handlers\n */\nexport const mergeGestureContainerProps = (\n ...propsArray: GestureContainerProps[]\n): GestureContainerProps => {\n const mergedStyle: React.CSSProperties = {};\n const pointerDownHandlers: Array<\n ((event: React.PointerEvent<HTMLElement>) => void) | undefined\n > = [];\n\n for (const props of propsArray) {\n Object.assign(mergedStyle, props.style);\n if (props.onPointerDown) {\n pointerDownHandlers.push(props.onPointerDown);\n }\n }\n\n const handlePointerDown = (event: React.PointerEvent<HTMLElement>) => {\n for (const handler of pointerDownHandlers) {\n handler?.(event);\n }\n };\n\n return {\n onPointerDown: handlePointerDown,\n style: mergedStyle,\n };\n};\n\n// ============================================================================\n// Scroll Detection Utilities\n// ============================================================================\n\n/**\n * Check if an element is scrollable in any direction.\n */\nexport function isScrollableElement(element: HTMLElement): boolean {\n const style = getComputedStyle(element);\n\n const isScrollableX =\n (style.overflowX === \"scroll\" || style.overflowX === \"auto\") &&\n element.scrollWidth > element.clientWidth;\n\n const isScrollableY =\n (style.overflowY === \"scroll\" || style.overflowY === \"auto\") &&\n element.scrollHeight > element.clientHeight;\n\n return isScrollableX || isScrollableY;\n}\n\n/**\n * Check if we should start drag based on scroll state.\n * Returns false if the target is inside a scrollable element.\n */\nexport function shouldStartDrag(\n event: React.PointerEvent,\n container: HTMLElement,\n): boolean {\n // eslint-disable-next-line no-restricted-syntax -- loop variable requires let\n let current = event.target as HTMLElement | null;\n\n while (current !== null && current !== container) {\n if (isScrollableElement(current)) {\n return false;\n }\n current = current.parentElement;\n }\n\n return true;\n}\n\n/**\n * Check if an element or its ancestors are scrollable in the specified direction.\n * Returns true if scrolling is possible and would block the swipe gesture.\n *\n * @param element - The target element to check\n * @param container - The container boundary\n * @param axis - The axis to check (\"x\" or \"y\")\n * @param direction - The swipe direction (1 = right/down, -1 = left/up)\n */\nexport function isScrollableInDirection(\n element: HTMLElement,\n container: HTMLElement,\n axis: \"x\" | \"y\",\n direction: 1 | -1,\n): boolean {\n // eslint-disable-next-line no-restricted-syntax -- loop variable requires let\n let current: HTMLElement | null = element;\n\n while (current !== null && current !== container) {\n const style = getComputedStyle(current);\n const isHorizontal = axis === \"x\";\n\n const overflow = isHorizontal ? style.overflowX : style.overflowY;\n const isScrollable = overflow === \"scroll\" || overflow === \"auto\";\n\n if (isScrollable) {\n const scrollSize = isHorizontal\n ? current.scrollWidth - current.clientWidth\n : current.scrollHeight - current.clientHeight;\n\n if (scrollSize > 0) {\n const scrollPos = isHorizontal ? current.scrollLeft : current.scrollTop;\n\n // If swiping in close direction and not at boundary, block swipe\n if (direction === -1 && scrollPos > 1) {\n return true; // Can scroll left/up, block swipe\n }\n if (direction === 1 && scrollPos < scrollSize - 1) {\n return true; // Can scroll right/down, block swipe\n }\n }\n }\n\n current = current.parentElement;\n }\n\n return false;\n}\n","/**\n * @file Type definitions for gesture input detection hooks.\n *\n * These types support the separation of concerns:\n * - Operation: what to do (navigate, push, pop)\n * - Input: how to command (swipe, click, keyboard)\n * - Presentation: how to show (animation, transition)\n *\n * This file defines types for the Input layer, including the abstract\n * ContinuousOperationState that represents any continuous state transition\n * (whether controlled by human gesture or system animation).\n */\nimport type * as React from \"react\";\n\n/**\n * Axis for gesture detection.\n */\nexport type GestureAxis = \"horizontal\" | \"vertical\";\n\n/**\n * 2D vector for displacement and velocity.\n */\nexport type Vector2 = {\n x: number;\n y: number;\n};\n\n// ============================================================================\n// Continuous Operation State\n// ============================================================================\n// A continuous operation is any state transition that occurs over time,\n// where progress can be observed incrementally. The controlling agent\n// may be human (gesture) or system (animation).\n\n/**\n * Phase of a continuous operation lifecycle.\n * - \"idle\": No operation in progress\n * - \"operating\": Operation is in progress (human or system controlled)\n * - \"ended\": Operation has completed\n */\nexport type ContinuousOperationPhase = \"idle\" | \"operating\" | \"ended\";\n\n/**\n * State of a continuous operation.\n *\n * This is the abstract representation of any operation that occurs over time,\n * whether controlled by human gesture or system animation. Components that\n * accept this state can respond to both input types uniformly.\n */\nexport type ContinuousOperationState = {\n /** Current phase of the operation */\n phase: ContinuousOperationPhase;\n /** Displacement from start position in pixels */\n displacement: Vector2;\n /** Current velocity in pixels per millisecond */\n velocity: Vector2;\n};\n\n/**\n * Initial idle state for ContinuousOperationState.\n */\nexport const IDLE_CONTINUOUS_OPERATION_STATE: ContinuousOperationState = {\n phase: \"idle\",\n displacement: { x: 0, y: 0 },\n velocity: { x: 0, y: 0 },\n};\n\n/**\n * Convert SwipeInputPhase to ContinuousOperationPhase.\n * - \"idle\" → \"idle\"\n * - \"tracking\" | \"swiping\" → \"operating\"\n * - \"ended\" → \"ended\"\n */\nexport function toContinuousPhase(phase: SwipeInputPhase): ContinuousOperationPhase {\n if (phase === \"idle\") {\n return \"idle\";\n }\n if (phase === \"ended\") {\n return \"ended\";\n }\n return \"operating\";\n}\n\n/**\n * Convert SwipeInputState to ContinuousOperationState.\n */\nexport function toContinuousOperationState(state: SwipeInputState): ContinuousOperationState {\n return {\n phase: toContinuousPhase(state.phase),\n displacement: state.displacement,\n velocity: state.velocity,\n };\n}\n\n// ============================================================================\n// Swipe Input (concrete implementation of continuous operation)\n// ============================================================================\n\n/**\n * Phase of swipe input lifecycle.\n * - \"idle\": No swipe in progress\n * - \"tracking\": Pointer down, tracking movement (direction not yet locked)\n * - \"swiping\": Direction locked, actively swiping\n * - \"ended\": Swipe gesture completed\n */\nexport type SwipeInputPhase = \"idle\" | \"tracking\" | \"swiping\" | \"ended\";\n\n/**\n * Point with timestamp for velocity calculation.\n */\nexport type TimestampedPoint = {\n x: number;\n y: number;\n timestamp: number;\n};\n\n/**\n * Swipe input state during gesture.\n */\nexport type SwipeInputState = {\n /** Current phase of the swipe input */\n phase: SwipeInputPhase;\n /** Displacement from start position in pixels */\n displacement: Vector2;\n /** Current velocity in pixels per millisecond */\n velocity: Vector2;\n /**\n * Direction of movement as a number.\n * -1 = backward (left/up), 0 = no movement, 1 = forward (right/down)\n */\n direction: -1 | 0 | 1;\n};\n\n/**\n * Thresholds for swipe input recognition.\n */\nexport type SwipeInputThresholds = {\n /** Minimum distance in pixels to trigger swipe. @default 50 */\n distanceThreshold: number;\n /** Minimum velocity in px/ms to trigger swipe. @default 0.3 */\n velocityThreshold: number;\n /** Distance threshold before direction is locked. @default 10 */\n lockThreshold: number;\n};\n\n/**\n * Options for usePointerTracking hook.\n */\nexport type UsePointerTrackingOptions = {\n /** Whether tracking is enabled */\n enabled: boolean;\n /** Restrict to primary pointer only (ignore multitouch). @default true */\n primaryOnly?: boolean;\n};\n\n/**\n * Result from usePointerTracking hook.\n */\nexport type UsePointerTrackingResult = {\n /** Current tracking state */\n state: PointerTrackingState;\n /** Handler to attach to onPointerDown */\n onPointerDown: (event: React.PointerEvent) => void;\n /** Reset tracking state */\n reset: () => void;\n};\n\n/**\n * Internal pointer tracking state.\n */\nexport type PointerTrackingState = {\n /** Whether pointer is currently down */\n isDown: boolean;\n /** Start position and timestamp */\n start: TimestampedPoint | null;\n /** Current position and timestamp */\n current: TimestampedPoint | null;\n /** Active pointer ID */\n pointerId: number | null;\n /** Whether tracking ended via pointercancel (not pointerup) */\n wasCanceled: boolean;\n};\n\n/**\n * Options for useDirectionalLock hook.\n */\nexport type UseDirectionalLockOptions = {\n /** Pointer tracking state from usePointerTracking */\n tracking: PointerTrackingState;\n /** Lock threshold in pixels. @default 10 */\n lockThreshold?: number;\n};\n\n/**\n * Result from useDirectionalLock hook.\n */\nexport type UseDirectionalLockResult = {\n /** Locked axis, or null if not yet locked */\n lockedAxis: GestureAxis | null;\n /** Whether lock has been determined */\n isLocked: boolean;\n /** Reset the lock state */\n reset: () => void;\n};\n\n/**\n * Options for useScrollBoundary hook.\n */\nexport type UseScrollBoundaryOptions = {\n /** Scroll container ref. If null, checks document scroll. */\n containerRef: React.RefObject<HTMLElement | null>;\n /** Axis to monitor */\n axis: GestureAxis;\n /** Tolerance in pixels for \"at boundary\" detection. @default 1 */\n tolerance?: number;\n};\n\n/**\n * Result from useScrollBoundary hook.\n */\nexport type UseScrollBoundaryResult = {\n /** Whether at the start boundary (top/left) */\n atStart: boolean;\n /** Whether at the end boundary (bottom/right) */\n atEnd: boolean;\n /** Current scroll position */\n scrollPosition: number;\n /** Maximum scroll position */\n maxScrollPosition: number;\n};\n\n/**\n * Filter function to determine if a pointer event should start tracking.\n * Receives the pointer event and container element.\n * Return true to allow tracking, false to ignore the event.\n */\nexport type PointerStartFilter = (\n event: React.PointerEvent,\n container: HTMLElement,\n) => boolean;\n\n/**\n * Options for useSwipeInput hook.\n */\nexport type UseSwipeInputOptions = {\n /** Ref to the container element */\n containerRef: React.RefObject<HTMLElement | null>;\n /** Axis to detect swipes on */\n axis: GestureAxis;\n /** Whether swipe detection is enabled. @default true */\n enabled?: boolean;\n /** Swipe thresholds configuration */\n thresholds?: Partial<SwipeInputThresholds>;\n /** Callback when swipe is completed */\n onSwipeEnd?: (state: SwipeInputState) => void;\n /** Whether to enable trackpad two-finger swipe (wheel events). @default true */\n enableWheel?: boolean;\n /**\n * Optional filter to determine if a pointer event should start tracking.\n * If provided, only events that pass this filter will be tracked.\n * Useful for edge-based swipe detection.\n */\n pointerStartFilter?: PointerStartFilter;\n};\n\n/**\n * Result from useSwipeInput hook.\n */\nexport type UseSwipeInputResult = {\n /** Current swipe input state */\n state: SwipeInputState;\n /** Props to spread on the container element */\n containerProps: React.HTMLAttributes<HTMLElement> & {\n style: React.CSSProperties;\n };\n};\n\n/**\n * Edge for edge-originated gestures.\n */\nexport type GestureEdge = \"left\" | \"right\" | \"top\" | \"bottom\";\n\n/**\n * Options for useEdgeSwipeInput hook.\n */\nexport type UseEdgeSwipeInputOptions = {\n /** Ref to the container element */\n containerRef: React.RefObject<HTMLElement | null>;\n /** Which edge to detect swipes from */\n edge: GestureEdge;\n /** Width of the edge detection zone in pixels. @default 20 */\n edgeWidth?: number;\n /** Whether edge swipe detection is enabled. @default true */\n enabled?: boolean;\n /** Swipe thresholds configuration */\n thresholds?: Partial<SwipeInputThresholds>;\n /** Callback when edge swipe is completed */\n onSwipeEnd?: (state: SwipeInputState) => void;\n};\n\n/**\n * Result from useEdgeSwipeInput hook.\n */\nexport type UseEdgeSwipeInputResult = {\n /** Whether the current gesture started from the edge */\n isEdgeGesture: boolean;\n /** Current swipe input state */\n state: SwipeInputState;\n /** Props to spread on the container element */\n containerProps: React.HTMLAttributes<HTMLElement> & {\n style: React.CSSProperties;\n };\n};\n\n/**\n * Options for useNativeGestureGuard hook.\n */\nexport type UseNativeGestureGuardOptions = {\n /** Ref to the container element */\n containerRef: React.RefObject<HTMLElement | null>;\n /** Whether the guard is active */\n active: boolean;\n /** Prevent iOS/macOS edge back gesture. @default true */\n preventEdgeBack?: boolean;\n /** Prevent overscroll bounce effect. @default true */\n preventOverscroll?: boolean;\n /** Width of edge zone where back gesture is prevented. @default 20 */\n edgeWidth?: number;\n};\n\n/**\n * Result from useNativeGestureGuard hook.\n */\nexport type UseNativeGestureGuardResult = {\n /** Props to spread on the container element */\n containerProps: React.HTMLAttributes<HTMLElement> & {\n style: React.CSSProperties;\n };\n};\n\n/**\n * Default swipe input thresholds.\n *\n * - distanceThreshold: 100px is ~27% of a 375px mobile screen\n * - velocityThreshold: 0.5px/ms = 500px/s, a moderate flick speed\n * - lockThreshold: 10px before direction is locked\n */\nexport const DEFAULT_SWIPE_THRESHOLDS: SwipeInputThresholds = {\n distanceThreshold: 100,\n velocityThreshold: 0.5,\n lockThreshold: 10,\n};\n\n/**\n * Default edge width for edge gesture detection.\n */\nexport const DEFAULT_EDGE_WIDTH = 20;\n\n/**\n * Initial idle state for SwipeInputState.\n */\nexport const IDLE_SWIPE_INPUT_STATE: SwipeInputState = {\n phase: \"idle\",\n displacement: { x: 0, y: 0 },\n velocity: { x: 0, y: 0 },\n direction: 0,\n};\n","/**\n * @file Hook for detecting swipe gestures on a container element.\n *\n * Combines pointer tracking and directional locking to detect swipe gestures.\n * Also supports trackpad two-finger swipe via wheel events.\n * Returns swipe state and container props for gesture handling.\n */\nimport * as React from \"react\";\nimport { usePointerTracking } from \"./usePointerTracking.js\";\nimport { useDirectionalLock } from \"./useDirectionalLock.js\";\nimport { useEffectEvent } from \"../useEffectEvent.js\";\nimport { calculateVelocity, determineDirection } from \"./utils.js\";\nimport type {\n GestureAxis,\n SwipeInputState,\n SwipeInputThresholds,\n UseSwipeInputOptions,\n UseSwipeInputResult,\n Vector2,\n} from \"./types.js\";\nimport { DEFAULT_SWIPE_THRESHOLDS, IDLE_SWIPE_INPUT_STATE } from \"./types.js\";\n\n/** Idle timeout to reset wheel state after swipe stops */\nconst WHEEL_RESET_TIMEOUT = 150;\n\n/**\n * Evaluate swipe end and call callback if threshold is met.\n */\nconst evaluateSwipeEnd = (\n displacement: Vector2,\n velocity: Vector2,\n axis: GestureAxis,\n thresholds: SwipeInputThresholds,\n onSwipeEnd: ((state: SwipeInputState) => void) | undefined,\n): void => {\n const axisDisplacement = axis === \"horizontal\" ? displacement.x : displacement.y;\n const axisVelocity = axis === \"horizontal\" ? velocity.x : velocity.y;\n\n const absDisplacement = Math.abs(axisDisplacement);\n const absVelocity = Math.abs(axisVelocity);\n\n const triggered = absDisplacement >= thresholds.distanceThreshold || absVelocity >= thresholds.velocityThreshold;\n\n if (triggered) {\n const direction = determineDirection(axisDisplacement);\n const endState: SwipeInputState = {\n phase: \"ended\",\n displacement,\n velocity,\n direction,\n };\n onSwipeEnd?.(endState);\n }\n};\n\n/**\n * Hook for detecting swipe gestures on a container element.\n */\nexport function useSwipeInput(options: UseSwipeInputOptions): UseSwipeInputResult {\n const {\n containerRef,\n axis,\n enabled = true,\n thresholds: customThresholds,\n onSwipeEnd,\n enableWheel = true,\n pointerStartFilter,\n } = options;\n\n const thresholds: SwipeInputThresholds = {\n ...DEFAULT_SWIPE_THRESHOLDS,\n ...customThresholds,\n };\n\n // Stable callback for swipe end\n const handleSwipeEnd = useEffectEvent(onSwipeEnd);\n\n // ===== Pointer-based swipe tracking =====\n const { state: tracking, onPointerDown: baseOnPointerDown } = usePointerTracking({\n enabled,\n });\n\n // Wrap pointer down handler with optional filter\n const onPointerDown = React.useCallback(\n (event: React.PointerEvent) => {\n if (!enabled) {\n return;\n }\n if (pointerStartFilter) {\n const container = containerRef.current;\n if (!container) {\n return;\n }\n if (!pointerStartFilter(event, container)) {\n return;\n }\n }\n baseOnPointerDown(event);\n },\n [enabled, pointerStartFilter, containerRef, baseOnPointerDown],\n );\n\n const { lockedAxis, isLocked } = useDirectionalLock({\n tracking,\n lockThreshold: thresholds.lockThreshold,\n });\n\n const lastActiveStateRef = React.useRef<SwipeInputState | null>(null);\n\n // Prevent native scroll when swiping on iOS\n const isLockedToSwipeAxisRef = React.useRef(false);\n\n React.useEffect(() => {\n isLockedToSwipeAxisRef.current = isLocked ? lockedAxis === axis : false;\n }, [isLocked, lockedAxis, axis]);\n\n React.useEffect(() => {\n const container = containerRef.current;\n if (!container || !enabled) {\n return;\n }\n const disableTouchMove = (event: TouchEvent) => {\n event.preventDefault();\n };\n const handleTouchStart = (event: TouchEvent) => {\n if (isLockedToSwipeAxisRef.current) {\n event.preventDefault();\n }\n document.addEventListener(\"touchmove\", disableTouchMove, { passive: false });\n };\n const handleTouchEnd = () => {\n document.removeEventListener(\"touchmove\", disableTouchMove);\n };\n document.addEventListener(\"touchend\", handleTouchEnd);\n document.addEventListener(\"touchcancel\", handleTouchEnd);\n container.addEventListener(\"touchstart\", handleTouchStart, { passive: false });\n\n return () => {\n container.removeEventListener(\"touchstart\", handleTouchStart);\n document.removeEventListener(\"touchend\", handleTouchEnd);\n document.removeEventListener(\"touchcancel\", handleTouchEnd);\n };\n }, [containerRef, enabled]);\n\n // ===== Wheel-based swipe tracking =====\n const [wheelState, setWheelState] = React.useState<SwipeInputState>(IDLE_SWIPE_INPUT_STATE);\n const wheelAccumulatedRef = React.useRef({ x: 0, y: 0 });\n const wheelIdleTimerRef = React.useRef<ReturnType<typeof setTimeout> | null>(null);\n const wheelLockedRef = React.useRef(false);\n const wheelLockedAxisRef = React.useRef<GestureAxis | null>(null);\n\n const resetWheelState = React.useCallback(() => {\n wheelAccumulatedRef.current = { x: 0, y: 0 };\n wheelLockedRef.current = false;\n wheelLockedAxisRef.current = null;\n setWheelState(IDLE_SWIPE_INPUT_STATE);\n }, []);\n\n const endWheelSwipe = React.useCallback(() => {\n const displacement = { ...wheelAccumulatedRef.current };\n evaluateSwipeEnd(displacement, { x: 0, y: 0 }, axis, thresholds, handleSwipeEnd);\n resetWheelState();\n }, [axis, thresholds, handleSwipeEnd, resetWheelState]);\n\n const handleWheel = useEffectEvent((event: WheelEvent) => {\n if (!enabled || !enableWheel || tracking.isDown) {\n return;\n }\n\n const { deltaX, deltaY } = event;\n\n // Direction lock\n if (!wheelLockedRef.current) {\n const absX = Math.abs(deltaX);\n const absY = Math.abs(deltaY);\n\n if (absX >= thresholds.lockThreshold || absY >= thresholds.lockThreshold) {\n wheelLockedRef.current = true;\n wheelLockedAxisRef.current = absX > absY ? \"horizontal\" : \"vertical\";\n }\n }\n\n // If locked to wrong axis, ignore\n if (wheelLockedRef.current && wheelLockedAxisRef.current !== axis) {\n return;\n }\n\n // Accumulate displacement (negate: wheel delta is scroll direction)\n wheelAccumulatedRef.current.x -= deltaX;\n wheelAccumulatedRef.current.y -= deltaY;\n\n const accumulated = wheelAccumulatedRef.current;\n const axisDisplacement = axis === \"horizontal\" ? accumulated.x : accumulated.y;\n\n setWheelState({\n phase: \"swiping\",\n displacement: { ...accumulated },\n velocity: { x: 0, y: 0 },\n direction: determineDirection(axisDisplacement),\n });\n\n // When wheel stops, treat as \"release\"\n if (wheelIdleTimerRef.current !== null) {\n clearTimeout(wheelIdleTimerRef.current);\n }\n wheelIdleTimerRef.current = setTimeout(endWheelSwipe, WHEEL_RESET_TIMEOUT);\n });\n\n // Set up wheel event listener\n React.useEffect(() => {\n const container = containerRef.current;\n if (!container || !enabled || !enableWheel) {\n return;\n }\n\n const listener = (event: WheelEvent) => {\n event.preventDefault();\n handleWheel(event);\n };\n\n container.addEventListener(\"wheel\", listener, { passive: false });\n\n return () => {\n container.removeEventListener(\"wheel\", listener);\n if (wheelIdleTimerRef.current !== null) {\n clearTimeout(wheelIdleTimerRef.current);\n }\n };\n }, [containerRef, enabled, enableWheel, handleWheel]);\n\n React.useEffect(() => {\n return () => {\n if (wheelIdleTimerRef.current !== null) {\n clearTimeout(wheelIdleTimerRef.current);\n }\n };\n }, []);\n\n // ===== Pointer swipe state =====\n const pointerState = React.useMemo<SwipeInputState>(() => {\n if (!tracking.isDown || !tracking.start || !tracking.current) {\n return IDLE_SWIPE_INPUT_STATE;\n }\n\n const deltaX = tracking.current.x - tracking.start.x;\n const deltaY = tracking.current.y - tracking.start.y;\n const displacement = { x: deltaX, y: deltaY };\n\n const velocity = {\n x: calculateVelocity(deltaX, tracking.start.timestamp, tracking.current.timestamp),\n y: calculateVelocity(deltaY, tracking.start.timestamp, tracking.current.timestamp),\n };\n\n if (!isLocked || lockedAxis !== axis) {\n return { phase: \"tracking\", displacement, velocity, direction: 0 };\n }\n\n const axisDisplacement = axis === \"horizontal\" ? deltaX : deltaY;\n return {\n phase: \"swiping\",\n displacement,\n velocity,\n direction: determineDirection(axisDisplacement),\n };\n }, [tracking.isDown, tracking.start, tracking.current, isLocked, lockedAxis, axis]);\n\n React.useEffect(() => {\n if (pointerState.phase !== \"idle\") {\n lastActiveStateRef.current = pointerState;\n }\n }, [pointerState]);\n\n // Handle pointer up (but not cancel)\n React.useEffect(() => {\n if (tracking.isDown) {\n return;\n }\n\n const lastState = lastActiveStateRef.current;\n if (!lastState || (lastState.phase !== \"swiping\" && lastState.phase !== \"tracking\")) {\n return;\n }\n\n lastActiveStateRef.current = null;\n\n // Skip navigation if the gesture was canceled (e.g., browser took over for native scroll)\n if (tracking.wasCanceled) {\n return;\n }\n\n evaluateSwipeEnd(lastState.displacement, lastState.velocity, axis, thresholds, handleSwipeEnd);\n }, [tracking.isDown, tracking.wasCanceled, axis, thresholds, handleSwipeEnd]);\n\n // Merge states\n const state = pointerState.phase !== \"idle\" ? pointerState : wheelState;\n\n const containerProps = React.useMemo(() => {\n const touchAction = axis === \"horizontal\" ? \"pan-y pinch-zoom\" : \"pan-x pinch-zoom\";\n return {\n onPointerDown,\n style: {\n touchAction,\n userSelect: \"none\" as const,\n WebkitUserSelect: \"none\" as const,\n },\n };\n }, [axis, onPointerDown]);\n\n return { state, containerProps };\n}\n","/**\n * @file Hook for detecting swipe gestures that originate from the edge of a container.\n *\n * Edge swipes are commonly used for \"swipe back\" navigation in mobile apps.\n * This hook detects swipes that start within a configurable edge zone.\n *\n * Built on top of useSwipeInput with edge zone filtering.\n */\nimport * as React from \"react\";\nimport { useSwipeInput } from \"./useSwipeInput.js\";\nimport type {\n GestureAxis,\n GestureEdge,\n SwipeInputState,\n UseEdgeSwipeInputOptions,\n UseEdgeSwipeInputResult,\n} from \"./types.js\";\nimport { DEFAULT_EDGE_WIDTH, DEFAULT_SWIPE_THRESHOLDS, IDLE_SWIPE_INPUT_STATE } from \"./types.js\";\n\n/**\n * Get the axis associated with an edge.\n */\nconst getAxisForEdge = (edge: GestureEdge): GestureAxis => {\n if (edge === \"left\" || edge === \"right\") {\n return \"horizontal\";\n }\n return \"vertical\";\n};\n\n/**\n * Check if a point is within the edge zone of a container.\n */\nconst isInEdgeZone = (\n clientX: number,\n clientY: number,\n container: HTMLElement,\n edge: GestureEdge,\n edgeWidth: number,\n): boolean => {\n const rect = container.getBoundingClientRect();\n\n switch (edge) {\n case \"left\":\n return clientX >= rect.left && clientX <= rect.left + edgeWidth;\n case \"right\":\n return clientX >= rect.right - edgeWidth && clientX <= rect.right;\n case \"top\":\n return clientY >= rect.top && clientY <= rect.top + edgeWidth;\n case \"bottom\":\n return clientY >= rect.bottom - edgeWidth && clientY <= rect.bottom;\n }\n};\n\n/**\n * Hook for detecting swipe gestures that originate from the edge of a container.\n *\n * This is useful for implementing \"swipe back\" navigation patterns where\n * the user must start their swipe from the edge of the screen.\n *\n * @example\n * ```tsx\n * const containerRef = useRef<HTMLDivElement>(null);\n * const { isEdgeGesture, state, containerProps } = useEdgeSwipeInput({\n * containerRef,\n * edge: \"left\",\n * edgeWidth: 20,\n * onSwipeEnd: (state) => {\n * if (state.direction === 1) goBack();\n * },\n * });\n *\n * return <div ref={containerRef} {...containerProps}>{children}</div>;\n * ```\n */\nexport function useEdgeSwipeInput(options: UseEdgeSwipeInputOptions): UseEdgeSwipeInputResult {\n const {\n containerRef,\n edge,\n edgeWidth = DEFAULT_EDGE_WIDTH,\n enabled = true,\n thresholds: customThresholds,\n onSwipeEnd,\n } = options;\n\n const thresholds = {\n ...DEFAULT_SWIPE_THRESHOLDS,\n ...customThresholds,\n };\n\n const axis = getAxisForEdge(edge);\n\n // Track whether the current gesture started from the edge\n const [isEdgeGesture, setIsEdgeGesture] = React.useState(false);\n\n // Create edge zone filter for pointer events\n const pointerStartFilter = React.useCallback(\n (event: React.PointerEvent, container: HTMLElement): boolean => {\n const inEdge = isInEdgeZone(event.clientX, event.clientY, container, edge, edgeWidth);\n setIsEdgeGesture(inEdge);\n return inEdge;\n },\n [edge, edgeWidth],\n );\n\n // Use base swipe input with edge filtering\n const { state, containerProps } = useSwipeInput({\n containerRef,\n axis,\n enabled,\n thresholds,\n onSwipeEnd,\n enableWheel: false, // Edge swipe doesn't use wheel events\n pointerStartFilter,\n });\n\n // Reset edge gesture state when swipe ends\n React.useEffect(() => {\n if (state.phase === \"idle\") {\n setIsEdgeGesture(false);\n }\n }, [state.phase]);\n\n // If not an edge gesture, return idle state\n const effectiveState: SwipeInputState = isEdgeGesture ? state : IDLE_SWIPE_INPUT_STATE;\n\n return {\n isEdgeGesture,\n state: effectiveState,\n containerProps,\n };\n}\n","/**\n * @file Hook for preventing conflicts with native OS gestures.\n *\n * This hook helps prevent conflicts with:\n * - iOS/macOS edge swipe back navigation\n * - Overscroll bounce effects\n *\n * It applies appropriate CSS properties and event handlers to the container.\n */\nimport * as React from \"react\";\nimport type {\n UseNativeGestureGuardOptions,\n UseNativeGestureGuardResult,\n} from \"./types.js\";\nimport { DEFAULT_EDGE_WIDTH } from \"./types.js\";\n\n/**\n * Check if a pointer event is within the left edge zone.\n */\nconst isInLeftEdge = (clientX: number, container: HTMLElement, edgeWidth: number): boolean => {\n const rect = container.getBoundingClientRect();\n return clientX >= rect.left && clientX <= rect.left + edgeWidth;\n};\n\n/**\n * Hook for preventing conflicts with native OS gestures.\n *\n * When active, this hook:\n * - Prevents iOS/macOS edge back gesture by capturing pointerdown events in the edge zone\n * - Prevents overscroll bounce effect using CSS overscroll-behavior\n * - Dynamically applies overscroll-behavior: none to html element during gesture\n *\n * @example\n * ```tsx\n * const containerRef = useRef<HTMLDivElement>(null);\n * const { containerProps } = useNativeGestureGuard({\n * containerRef,\n * active: isSwipeActive,\n * preventEdgeBack: true,\n * preventOverscroll: true,\n * });\n *\n * return <div ref={containerRef} {...containerProps}>{children}</div>;\n * ```\n */\nexport function useNativeGestureGuard(options: UseNativeGestureGuardOptions): UseNativeGestureGuardResult {\n const {\n containerRef,\n active,\n preventEdgeBack = true,\n preventOverscroll = true,\n edgeWidth = DEFAULT_EDGE_WIDTH,\n } = options;\n\n // Track previous html overscroll-behavior value for restoration\n const previousHtmlOverscrollRef = React.useRef<string | null>(null);\n\n // Apply overscroll-behavior to html synchronously (called from onPointerDown)\n const applyHtmlOverscroll = React.useCallback(() => {\n if (!preventOverscroll) {\n return;\n }\n\n const html = document.documentElement;\n if (previousHtmlOverscrollRef.current === null) {\n previousHtmlOverscrollRef.current = html.style.overscrollBehavior;\n }\n html.style.overscrollBehavior = \"none\";\n }, [preventOverscroll]);\n\n // Remove overscroll-behavior from html when gesture ends\n React.useEffect(() => {\n if (active || !preventOverscroll) {\n return;\n }\n\n // Cleanup: restore previous value when deactivated\n if (previousHtmlOverscrollRef.current !== null) {\n document.documentElement.style.overscrollBehavior = previousHtmlOverscrollRef.current;\n previousHtmlOverscrollRef.current = null;\n }\n }, [active, preventOverscroll]);\n\n // Cleanup on unmount\n React.useEffect(() => {\n return () => {\n if (previousHtmlOverscrollRef.current !== null) {\n document.documentElement.style.overscrollBehavior = previousHtmlOverscrollRef.current;\n previousHtmlOverscrollRef.current = null;\n }\n };\n }, []);\n\n // Pointer down handler that prevents edge back gesture\n // Note: This must run on EVERY pointerdown in edge zone, not just when active,\n // because browser gesture recognition starts immediately on first touch.\n const onPointerDown = React.useCallback((event: React.PointerEvent) => {\n if (!preventEdgeBack) {\n return;\n }\n\n const container = containerRef.current;\n if (!container) {\n return;\n }\n\n // Prevent for touch events in the left edge zone\n // This must happen immediately, before we know if it's \"our\" gesture\n if (event.pointerType === \"touch\" && isInLeftEdge(event.clientX, container, edgeWidth)) {\n // Apply html overscroll-behavior synchronously before browser can recognize gesture\n applyHtmlOverscroll();\n // Prevent the browser from handling this as a back gesture\n event.preventDefault();\n }\n }, [preventEdgeBack, containerRef, edgeWidth, applyHtmlOverscroll]);\n\n // Build container props\n // Styles are applied immediately (not waiting for active) to prevent browser gestures\n const containerProps = React.useMemo(() => {\n const style: React.CSSProperties = {\n // Always apply to prevent browser navigation gestures\n overscrollBehavior: preventOverscroll ? \"contain\" : undefined,\n WebkitOverflowScrolling: \"touch\",\n };\n\n return {\n onPointerDown: preventEdgeBack ? onPointerDown : undefined,\n style,\n };\n }, [preventOverscroll, preventEdgeBack, onPointerDown]);\n\n return {\n containerProps,\n };\n}\n"],"names":["INITIAL_STATE","createTimestampedPoint","clientX","clientY","usePointerTracking","options","enabled","primaryOnly","state","setState","React","reset","handlePointerDown","useEffectEvent","event","point","handlePointerMove","prev","handlePointerUp","handlePointerCancel","shouldTrackDocument","useDocumentPointerEvents","DEFAULT_LOCK_THRESHOLD","determineAxis","deltaX","deltaY","absX","absY","useDirectionalLock","tracking","lockThreshold","lockedAxis","setLockedAxis","axis","calculateVelocity","displacement","startTime","currentTime","elapsed","determineDirection","mergeGestureContainerProps","propsArray","mergedStyle","pointerDownHandlers","props","handler","isScrollableInDirection","element","container","direction","current","style","isHorizontal","overflow","scrollSize","scrollPos","IDLE_CONTINUOUS_OPERATION_STATE","DEFAULT_SWIPE_THRESHOLDS","DEFAULT_EDGE_WIDTH","IDLE_SWIPE_INPUT_STATE","WHEEL_RESET_TIMEOUT","evaluateSwipeEnd","velocity","thresholds","onSwipeEnd","axisDisplacement","axisVelocity","absDisplacement","absVelocity","useSwipeInput","containerRef","customThresholds","enableWheel","pointerStartFilter","handleSwipeEnd","baseOnPointerDown","onPointerDown","isLocked","lastActiveStateRef","isLockedToSwipeAxisRef","disableTouchMove","handleTouchStart","handleTouchEnd","wheelState","setWheelState","wheelAccumulatedRef","wheelIdleTimerRef","wheelLockedRef","wheelLockedAxisRef","resetWheelState","endWheelSwipe","handleWheel","accumulated","listener","pointerState","lastState","containerProps","getAxisForEdge","edge","isInEdgeZone","edgeWidth","rect","useEdgeSwipeInput","isEdgeGesture","setIsEdgeGesture","inEdge","isInLeftEdge","useNativeGestureGuard","active","preventEdgeBack","preventOverscroll","previousHtmlOverscrollRef","applyHtmlOverscroll","html"],"mappings":"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"}
@@ -55,7 +55,9 @@ export { Drawer } from "../components/window/Drawer.js";
55
55
  export { DrawerLayers } from "../components/window/DrawerLayers.js";
56
56
  export { DialogOverlay } from "../components/window/DialogOverlay.js";
57
57
  export { PopupLayerPortal } from "../components/window/PopupLayerPortal.js";
58
+ export { SwipeSafeZone } from "../components/gesture/SwipeSafeZone.js";
58
59
  export { useFloatingState } from "../modules/window/useFloatingState.js";
59
60
  export { useDrawerState } from "../modules/window/useDrawerState.js";
60
61
  export type { FloatingWindowProps } from "../components/window/FloatingWindow.js";
61
62
  export type { DrawerProps } from "../components/window/Drawer.js";
63
+ export type { SwipeSafeZoneProps } from "../components/gesture/SwipeSafeZone.js";
package/dist/window.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const b=require("./FloatingWindow-TCDNY5gE.cjs"),y=require("react/jsx-runtime"),E=require("react"),p=require("./useEffectEvent-huSsGUnl.cjs"),S=require("./useIsomorphicLayoutEffect-DGRNF4Lf.cjs"),M=require("./styles-qf6ptVLD.cjs"),j=require("./useFloatingState-C4kRaW_R.cjs");function P(e){const n=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(e){for(const t in e)if(t!=="default"){const s=Object.getOwnPropertyDescriptor(e,t);Object.defineProperty(n,t,s.get?s:{enumerable:!0,get:()=>e[t]})}}return n.default=e,Object.freeze(n)}const c=P(E),R=()=>typeof window>"u"?{width:0,height:0}:{width:window.innerWidth,height:window.innerHeight},z=(e,n,t,s,r)=>{const o=e+t>r.width?Math.max(0,r.width-t):e,i=n+s>r.height?Math.max(0,r.height-s):n;return{x:o,y:i}},v=new Map,L=e=>{const{box:n="content-box"}=e,t=`resize-box:${n}`,s=v.get(t);if(s)return s;const r=new class{#e=new Map;#t=new ResizeObserver((o,i)=>{o.forEach(a=>{const u=this.#e.get(a.target);u&&u(a,i)})});observe(o,i){return this.#e.set(o,i),this.#t.observe(o,e),()=>{this.#e.delete(o),this.#t.unobserve(o)}}};return v.set(t,r),r};function q(e,{box:n}){const[t,s]=c.useState(null),r=e.current;c.useEffect(()=>r?L({box:n}).observe(r,a=>{s(a)}):void 0,[n,r]);const o=c.useMemo(()=>{if(!t)return null;if(t.borderBoxSize?.length>0){const i=t.borderBoxSize[0];return new DOMRect(0,0,i.inlineSize,i.blockSize)}return t.contentRect},[t]);return{entry:t,rect:o}}const C={border:"none",padding:0,background:"transparent"},I={position:"fixed",zIndex:M.DIALOG_OVERLAY_Z_INDEX},k=typeof window<"u"&&typeof document<"u",F=({anchor:e,onClose:n,children:t,contentClassName:s,contentStyle:r,dataAttributes:o,onKeyDown:i,onPositionChange:a})=>{const u=c.useRef(null),{rect:l}=q(u,{box:"border-box"}),d=c.useMemo(()=>{const f=R(),h=l?.width??0,w=l?.height??0;return z(e.x,e.y,h,w,f)},[e.x,e.y,l?.width,l?.height]),g=p.useEffectEvent(a);c.useEffect(()=>{g?.(d)},[d]);const m=p.useEffectEvent(n);c.useEffect(()=>{const f=h=>{h.target instanceof Node&&u.current&&!u.current.contains(h.target)&&m()};return document.addEventListener("pointerdown",f),()=>document.removeEventListener("pointerdown",f)},[]);const D=c.useMemo(()=>({...I,...r,left:d.x,top:d.y}),[r,d.x,d.y]),O=c.useMemo(()=>o?Object.entries(o).reduce((f,[h,w])=>(w==null||(f[`data-${h}`]=w),f),{}):{},[o]);return y.jsx("div",{ref:u,className:s,style:D,onKeyDown:i,...O,children:t})},_=({visible:e,onClose:n,anchor:t,children:s,contentClassName:r,contentStyle:o,dataAttributes:i,onKeyDown:a,onPositionChange:u})=>{const l=c.useRef(null);S.useIsomorphicLayoutEffect(()=>{if(!l.current)return;const g=l.current;e?g.showModal():g.open&&g.close()},[e]);const d=c.useCallback(g=>{g.preventDefault(),n()},[n]);return y.jsx("dialog",{ref:l,style:C,onCancel:d,children:y.jsx(c.Activity,{mode:e?"visible":"hidden",children:y.jsx(F,{anchor:t,onClose:n,contentClassName:r,contentStyle:o,dataAttributes:i,onKeyDown:a,onPositionChange:u,children:s})})})},x=e=>k?y.jsx(_,{...e}):null;x.displayName="DialogOverlay";exports.Drawer=b.Drawer;exports.DrawerLayers=b.DrawerLayers;exports.FloatingWindow=b.FloatingWindow;exports.PopupLayerPortal=b.PopupLayerPortal;exports.useDrawerState=b.useDrawerState;exports.useFloatingState=j.useFloatingState;exports.DialogOverlay=x;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const w=require("./FloatingWindow-DMwyK0eK.cjs"),b=require("react/jsx-runtime"),z=require("react"),p=require("./useDocumentPointerEvents-DxDSOtip.cjs"),m=require("./useIsomorphicLayoutEffect-DGRNF4Lf.cjs"),E=require("./styles-qf6ptVLD.cjs"),R=require("./useFloatingState-C4kRaW_R.cjs");function P(e){const t=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(e){for(const n in e)if(n!=="default"){const r=Object.getOwnPropertyDescriptor(e,n);Object.defineProperty(t,n,r.get?r:{enumerable:!0,get:()=>e[n]})}}return t.default=e,Object.freeze(t)}const c=P(z),M=()=>typeof window>"u"?{width:0,height:0}:{width:window.innerWidth,height:window.innerHeight},j=(e,t,n,r,o)=>{const s=e+n>o.width?Math.max(0,o.width-n):e,i=t+r>o.height?Math.max(0,o.height-r):t;return{x:s,y:i}},S=new Map,L=e=>{const t=`resize-box:${e}`,n=S.get(t);if(n)return n;const r=new Map,o=new ResizeObserver(i=>{for(const a of i){const u=r.get(a.target);u&&u(a)}}),s={observe(i,a){return r.set(i,a),o.observe(i,{box:e}),()=>{r.delete(i),o.unobserve(i)}}};return S.set(t,s),s},C=e=>{const t=e.getBoundingClientRect();return{target:e,contentRect:t,borderBoxSize:[{inlineSize:t.width,blockSize:t.height}],contentBoxSize:[{inlineSize:t.width,blockSize:t.height}],devicePixelContentBoxSize:[]}},k=e=>{if(e.borderBoxSize?.length>0){const t=e.borderBoxSize[0];return new DOMRect(0,0,t.inlineSize,t.blockSize)}return e.contentRect};function I(e,{box:t="content-box"}){const[n,r]=c.useState(null);m.useIsomorphicLayoutEffect(()=>{const s=e.current;if(!s){r(null);return}return r(C(s)),L(t).observe(s,r)},[e,t]);const o=c.useMemo(()=>n?k(n):null,[n]);return{entry:n,rect:o}}const q={border:"none",padding:0,background:"transparent"},B={position:"fixed",zIndex:E.DIALOG_OVERLAY_Z_INDEX},F=typeof window<"u"&&typeof document<"u",_=({anchor:e,onClose:t,children:n,contentClassName:r,contentStyle:o,dataAttributes:s,onKeyDown:i,onPositionChange:a})=>{const u=c.useRef(null),{rect:l}=I(u,{box:"border-box"}),d=c.useMemo(()=>{const f=M(),h=l?.width??0,y=l?.height??0;return j(e.x,e.y,h,y,f)},[e.x,e.y,l?.width,l?.height]),g=p.useEffectEvent(a);c.useEffect(()=>{g?.(d)},[d]);const v=p.useEffectEvent(t);c.useEffect(()=>{const f=h=>{h.target instanceof Node&&u.current&&!u.current.contains(h.target)&&v()};return document.addEventListener("pointerdown",f),()=>document.removeEventListener("pointerdown",f)},[]);const D=c.useMemo(()=>({...B,...o,left:d.x,top:d.y}),[o,d.x,d.y]),O=c.useMemo(()=>s?Object.entries(s).reduce((f,[h,y])=>(y==null||(f[`data-${h}`]=y),f),{}):{},[s]);return b.jsx("div",{ref:u,className:r,style:D,onKeyDown:i,...O,children:n})},N=({visible:e,onClose:t,anchor:n,children:r,contentClassName:o,contentStyle:s,dataAttributes:i,onKeyDown:a,onPositionChange:u})=>{const l=c.useRef(null);m.useIsomorphicLayoutEffect(()=>{if(!l.current)return;const g=l.current;e?g.showModal():g.open&&g.close()},[e]);const d=c.useCallback(g=>{g.preventDefault(),t()},[t]);return b.jsx("dialog",{ref:l,style:q,onCancel:d,children:b.jsx(c.Activity,{mode:e?"visible":"hidden",children:b.jsx(_,{anchor:n,onClose:t,contentClassName:o,contentStyle:s,dataAttributes:i,onKeyDown:a,onPositionChange:u,children:r})})})},x=e=>F?b.jsx(N,{...e}):null;x.displayName="DialogOverlay";exports.Drawer=w.Drawer;exports.DrawerLayers=w.DrawerLayers;exports.FloatingWindow=w.FloatingWindow;exports.PopupLayerPortal=w.PopupLayerPortal;exports.SwipeSafeZone=w.SwipeSafeZone;exports.useDrawerState=w.useDrawerState;exports.useFloatingState=R.useFloatingState;exports.DialogOverlay=x;
2
2
  //# sourceMappingURL=window.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"window.cjs","sources":["../src/utils/dialogUtils.ts","../src/hooks/useResizeObserver.tsx","../src/components/window/DialogOverlay.tsx"],"sourcesContent":["/**\n * @file Dialog positioning utilities\n */\n\nexport type ViewportInfo = {\n width: number;\n height: number;\n};\n\n/**\n * Get viewport dimensions\n */\nexport const getViewportInfo = (): ViewportInfo => {\n if (typeof window === \"undefined\") {\n return { width: 0, height: 0 };\n }\n\n return {\n width: window.innerWidth,\n height: window.innerHeight,\n };\n};\n\n/**\n * Calculate context menu position to keep it within viewport\n */\nexport const calculateContextMenuPosition = (\n anchorX: number,\n anchorY: number,\n menuWidth: number,\n menuHeight: number,\n viewport: ViewportInfo,\n): { x: number; y: number } => {\n // Adjust horizontal position if menu would overflow\n const x =\n anchorX + menuWidth > viewport.width ? Math.max(0, viewport.width - menuWidth) : anchorX;\n\n // Adjust vertical position if menu would overflow\n const y =\n anchorY + menuHeight > viewport.height ? Math.max(0, viewport.height - menuHeight) : anchorY;\n\n return { x, y };\n};\n","/**\n * @file Shared useResizeObserver hook with cached observer instances.\n */\nimport * as React from \"react\";\n\ntype Unobserve = () => void;\ntype Callback = (entry: ResizeObserverEntry, observer: ResizeObserver) => void;\ntype SharedObserver = {\n observe: (target: Element, callback: Callback) => Unobserve;\n};\nconst observerCache = new Map<string, SharedObserver>();\nconst getSharedObserver = (options: ResizeObserverOptions) => {\n const { box = \"content-box\" } = options;\n const observerKey = `resize-box:${box}`;\n const cached = observerCache.get(observerKey);\n if (cached) {\n return cached;\n }\n const observer = new (class {\n #callbackMap = new Map<Element, Callback>();\n #resizeObserver = new ResizeObserver((entries, observer) => {\n entries.forEach((entry) => {\n const callback = this.#callbackMap.get(entry.target);\n if (callback) {\n callback(entry, observer);\n }\n });\n });\n observe(target: Element, callback: Callback) {\n this.#callbackMap.set(target, callback);\n this.#resizeObserver.observe(target, options);\n return () => {\n this.#callbackMap.delete(target);\n this.#resizeObserver.unobserve(target);\n };\n }\n })();\n observerCache.set(observerKey, observer);\n\n return observer;\n};\n/**\n * Observe size changes for a given element reference using shared resize observers.\n *\n * @param ref - Ref holding the element whose size to monitor.\n * @param options - Resize observer configuration.\n * @returns Latest resize entry and a derived DOMRect snapshot.\n */\nexport function useResizeObserver<T extends HTMLElement>(\n ref: React.RefObject<T | null>,\n { box }: ResizeObserverOptions,\n) {\n const [entry, setEntry] = React.useState<ResizeObserverEntry | null>(null);\n const target = ref.current;\n\n React.useEffect(() => {\n if (!target) {\n return;\n }\n\n const observer = getSharedObserver({ box });\n return observer.observe(target, (nextEntry) => {\n setEntry(nextEntry);\n });\n }, [box, target]);\n\n const rect = React.useMemo(() => {\n if (!entry) {\n return null;\n }\n\n if (entry.borderBoxSize?.length > 0) {\n const size = entry.borderBoxSize[0];\n return new DOMRect(0, 0, size.inlineSize, size.blockSize);\n }\n\n return entry.contentRect;\n }, [entry]);\n\n return { entry, rect };\n}\n","/**\n * @file Dialog-based overlay component with automatic positioning\n */\nimport * as React from \"react\";\nimport type { Position } from \"../../types\";\nimport { calculateContextMenuPosition, getViewportInfo } from \"../../utils/dialogUtils\";\nimport { ensureDialogPolyfill } from \"../../utils/polyfills/createDialogPolyfill\";\nimport { useEffectEvent } from \"../../hooks/useEffectEvent\";\nimport { useIsomorphicLayoutEffect } from \"../../hooks/useIsomorphicLayoutEffect\";\nimport { useResizeObserver } from \"../../hooks/useResizeObserver\";\nimport { DIALOG_OVERLAY_Z_INDEX } from \"../../constants/styles\";\n\nconst contextDialogStyle: React.CSSProperties = {\n border: \"none\",\n padding: 0,\n background: \"transparent\",\n};\n\nconst contextContentStyle: React.CSSProperties = {\n position: \"fixed\",\n zIndex: DIALOG_OVERLAY_Z_INDEX,\n};\n\ntype DataAttributes = Record<string, string | number | boolean>;\n\nexport type DialogOverlayProps = {\n anchor: Position;\n visible: boolean;\n onClose: () => void;\n children: React.ReactNode;\n contentClassName?: string;\n contentStyle?: React.CSSProperties;\n dataAttributes?: Record<string, string | number | boolean | null | undefined>;\n onKeyDown?: React.KeyboardEventHandler<HTMLDivElement>;\n onPositionChange?: (position: Position) => void;\n};\n\nconst isBrowser = typeof window !== \"undefined\" && typeof document !== \"undefined\";\n\nensureDialogPolyfill();\n\nconst DialogOverlayContent: React.FC<Omit<DialogOverlayProps, \"visible\">> = ({\n anchor,\n onClose,\n children,\n contentClassName,\n contentStyle,\n dataAttributes,\n onKeyDown,\n onPositionChange,\n}) => {\n const contentRef = React.useRef<HTMLDivElement>(null);\n const { rect } = useResizeObserver(contentRef, { box: \"border-box\" });\n\n const computedPosition = React.useMemo(() => {\n const viewport = getViewportInfo();\n const width = rect?.width ?? 0;\n const height = rect?.height ?? 0;\n return calculateContextMenuPosition(anchor.x, anchor.y, width, height, viewport);\n }, [anchor.x, anchor.y, rect?.width, rect?.height]);\n\n const handlePositionChange = useEffectEvent(onPositionChange);\n\n React.useEffect(() => {\n handlePositionChange?.(computedPosition);\n }, [computedPosition]);\n\n const handleClose = useEffectEvent(onClose);\n\n React.useEffect(() => {\n const handlePointerDown = (event: PointerEvent) => {\n if (!(event.target instanceof Node)) {\n return;\n }\n if (contentRef.current && !contentRef.current.contains(event.target)) {\n handleClose();\n }\n };\n\n document.addEventListener(\"pointerdown\", handlePointerDown);\n return () => document.removeEventListener(\"pointerdown\", handlePointerDown);\n }, []);\n\n const mergedStyle: React.CSSProperties = React.useMemo(\n () => ({\n ...contextContentStyle,\n ...contentStyle,\n left: computedPosition.x,\n top: computedPosition.y,\n }),\n [contentStyle, computedPosition.x, computedPosition.y],\n );\n\n const dataProps = React.useMemo<DataAttributes>(() => {\n if (!dataAttributes) {\n return {};\n }\n return Object.entries(dataAttributes).reduce<DataAttributes>((acc, [key, value]) => {\n if (value === null || value === undefined) {\n return acc;\n }\n acc[`data-${key}`] = value;\n return acc;\n }, {});\n }, [dataAttributes]);\n\n return (\n <div\n ref={contentRef}\n className={contentClassName}\n style={mergedStyle}\n onKeyDown={onKeyDown}\n {...dataProps}\n >\n {children}\n </div>\n );\n};\n\nconst DialogOverlayImpl: React.FC<DialogOverlayProps> = ({\n visible,\n onClose,\n anchor,\n children,\n contentClassName,\n contentStyle,\n dataAttributes,\n onKeyDown,\n onPositionChange,\n}) => {\n const dialogRef = React.useRef<HTMLDialogElement>(null);\n\n useIsomorphicLayoutEffect(() => {\n if (!dialogRef.current) {\n return;\n }\n\n const dialog = dialogRef.current;\n if (visible) {\n dialog.showModal();\n } else if (dialog.open) {\n dialog.close();\n }\n }, [visible]);\n\n const handleCancel = React.useCallback(\n (event: React.SyntheticEvent) => {\n event.preventDefault();\n onClose();\n },\n [onClose],\n );\n\n return (\n <dialog ref={dialogRef} style={contextDialogStyle} onCancel={handleCancel}>\n <React.Activity mode={visible ? \"visible\" : \"hidden\"}>\n <DialogOverlayContent\n anchor={anchor}\n onClose={onClose}\n contentClassName={contentClassName}\n contentStyle={contentStyle}\n dataAttributes={dataAttributes}\n onKeyDown={onKeyDown}\n onPositionChange={onPositionChange}\n >\n {children}\n </DialogOverlayContent>\n </React.Activity>\n </dialog>\n );\n};\n\nexport const DialogOverlay: React.FC<DialogOverlayProps> = (props) => {\n if (!isBrowser) {\n return null;\n }\n return <DialogOverlayImpl {...props} />;\n};\n\nDialogOverlay.displayName = \"DialogOverlay\";\n"],"names":["getViewportInfo","calculateContextMenuPosition","anchorX","anchorY","menuWidth","menuHeight","viewport","x","y","observerCache","getSharedObserver","options","box","observerKey","cached","observer","#callbackMap","#resizeObserver","entries","entry","callback","target","useResizeObserver","ref","setEntry","React","nextEntry","rect","size","contextDialogStyle","contextContentStyle","DIALOG_OVERLAY_Z_INDEX","isBrowser","DialogOverlayContent","anchor","onClose","children","contentClassName","contentStyle","dataAttributes","onKeyDown","onPositionChange","contentRef","computedPosition","width","height","handlePositionChange","useEffectEvent","handleClose","handlePointerDown","event","mergedStyle","dataProps","acc","key","value","jsx","DialogOverlayImpl","visible","dialogRef","useIsomorphicLayoutEffect","dialog","handleCancel","DialogOverlay","props"],"mappings":"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"}
1
+ {"version":3,"file":"window.cjs","sources":["../src/utils/dialogUtils.ts","../src/hooks/useResizeObserver.tsx","../src/components/window/DialogOverlay.tsx"],"sourcesContent":["/**\n * @file Dialog positioning utilities\n */\n\nexport type ViewportInfo = {\n width: number;\n height: number;\n};\n\n/**\n * Get viewport dimensions\n */\nexport const getViewportInfo = (): ViewportInfo => {\n if (typeof window === \"undefined\") {\n return { width: 0, height: 0 };\n }\n\n return {\n width: window.innerWidth,\n height: window.innerHeight,\n };\n};\n\n/**\n * Calculate context menu position to keep it within viewport\n */\nexport const calculateContextMenuPosition = (\n anchorX: number,\n anchorY: number,\n menuWidth: number,\n menuHeight: number,\n viewport: ViewportInfo,\n): { x: number; y: number } => {\n // Adjust horizontal position if menu would overflow\n const x =\n anchorX + menuWidth > viewport.width ? Math.max(0, viewport.width - menuWidth) : anchorX;\n\n // Adjust vertical position if menu would overflow\n const y =\n anchorY + menuHeight > viewport.height ? Math.max(0, viewport.height - menuHeight) : anchorY;\n\n return { x, y };\n};\n","/**\n * @file Shared useResizeObserver hook with cached observer instances.\n *\n * Provides element size observation with shared observers for memory efficiency.\n * Size becomes available after the first useLayoutEffect cycle completes.\n *\n * Note: Due to React's effect execution order (children before parents),\n * child components may see containerSize=0 on their first effect run.\n * This is a React constraint, not a bug. Consumers should check for\n * valid size before using it for calculations like animation positions.\n */\nimport * as React from \"react\";\nimport { useIsomorphicLayoutEffect } from \"./useIsomorphicLayoutEffect.js\";\n\n/**\n * Shared ResizeObserver that can observe multiple elements.\n */\ntype SharedObserver = {\n observe: (target: Element, callback: (entry: ResizeObserverEntry) => void) => () => void;\n};\n\n/** Cache of shared observers per box option */\nconst observerCache = new Map<string, SharedObserver>();\n\n/**\n * Get or create a shared ResizeObserver for the given box option.\n */\nconst getSharedObserver = (box: ResizeObserverBoxOptions): SharedObserver => {\n const observerKey = `resize-box:${box}`;\n const cached = observerCache.get(observerKey);\n if (cached) {\n return cached;\n }\n\n const callbacks = new Map<Element, (entry: ResizeObserverEntry) => void>();\n\n const resizeObserver = new ResizeObserver((entries) => {\n for (const entry of entries) {\n const callback = callbacks.get(entry.target);\n if (callback) {\n callback(entry);\n }\n }\n });\n\n const sharedObserver: SharedObserver = {\n observe(target, callback) {\n callbacks.set(target, callback);\n resizeObserver.observe(target, { box });\n\n return () => {\n callbacks.delete(target);\n resizeObserver.unobserve(target);\n };\n },\n };\n\n observerCache.set(observerKey, sharedObserver);\n return sharedObserver;\n};\n\n/**\n * Create a ResizeObserverEntry from getBoundingClientRect.\n */\nconst measureElement = (target: Element): ResizeObserverEntry => {\n const rect = target.getBoundingClientRect();\n return {\n target,\n contentRect: rect,\n borderBoxSize: [{ inlineSize: rect.width, blockSize: rect.height }],\n contentBoxSize: [{ inlineSize: rect.width, blockSize: rect.height }],\n devicePixelContentBoxSize: [],\n };\n};\n\n/**\n * Extract DOMRect from ResizeObserverEntry.\n */\nconst entryToRect = (entry: ResizeObserverEntry): DOMRect => {\n if (entry.borderBoxSize?.length > 0) {\n const size = entry.borderBoxSize[0];\n return new DOMRect(0, 0, size.inlineSize, size.blockSize);\n }\n return entry.contentRect;\n};\n\n/**\n * Clear observer cache. Exported for testing purposes.\n */\nexport function clearObserverCache(): void {\n observerCache.clear();\n}\n\n/**\n * Observe size changes for a given element reference using shared resize observers.\n *\n * @param ref - Ref holding the element whose size to monitor.\n * @param options - Resize observer configuration.\n * @returns Latest resize entry and a derived DOMRect snapshot.\n *\n * @remarks\n * The `rect` will be `null` on the first render. After the initial\n * useLayoutEffect runs and triggers a re-render, `rect` will contain\n * the measured size.\n *\n * Due to React's effect execution order, child components' effects run\n * before parent effects. If you pass `rect.width` to a child as a prop,\n * the child's first effect will see `0` (or whatever default you use).\n * This is expected React behavior.\n *\n * @example\n * ```tsx\n * const containerRef = useRef<HTMLDivElement>(null);\n * const { rect } = useResizeObserver(containerRef, { box: \"border-box\" });\n * const width = rect?.width ?? 0;\n *\n * // Check if size is ready before using for calculations\n * const isReady = rect !== null;\n * ```\n */\nexport function useResizeObserver<T extends HTMLElement>(\n ref: React.RefObject<T | null>,\n { box = \"content-box\" }: ResizeObserverOptions,\n) {\n const [entry, setEntry] = React.useState<ResizeObserverEntry | null>(null);\n\n useIsomorphicLayoutEffect(() => {\n const target = ref.current;\n if (!target) {\n setEntry(null);\n return;\n }\n\n // Measure immediately\n setEntry(measureElement(target));\n\n // Set up ResizeObserver for subsequent updates\n const observer = getSharedObserver(box);\n return observer.observe(target, setEntry);\n }, [ref, box]);\n\n const rect = React.useMemo(() => {\n if (!entry) {\n return null;\n }\n return entryToRect(entry);\n }, [entry]);\n\n return { entry, rect };\n}\n","/**\n * @file Dialog-based overlay component with automatic positioning\n */\nimport * as React from \"react\";\nimport type { Position } from \"../../types\";\nimport { calculateContextMenuPosition, getViewportInfo } from \"../../utils/dialogUtils\";\nimport { ensureDialogPolyfill } from \"../../utils/polyfills/createDialogPolyfill\";\nimport { useEffectEvent } from \"../../hooks/useEffectEvent\";\nimport { useIsomorphicLayoutEffect } from \"../../hooks/useIsomorphicLayoutEffect\";\nimport { useResizeObserver } from \"../../hooks/useResizeObserver\";\nimport { DIALOG_OVERLAY_Z_INDEX } from \"../../constants/styles\";\n\nconst contextDialogStyle: React.CSSProperties = {\n border: \"none\",\n padding: 0,\n background: \"transparent\",\n};\n\nconst contextContentStyle: React.CSSProperties = {\n position: \"fixed\",\n zIndex: DIALOG_OVERLAY_Z_INDEX,\n};\n\ntype DataAttributes = Record<string, string | number | boolean>;\n\nexport type DialogOverlayProps = {\n anchor: Position;\n visible: boolean;\n onClose: () => void;\n children: React.ReactNode;\n contentClassName?: string;\n contentStyle?: React.CSSProperties;\n dataAttributes?: Record<string, string | number | boolean | null | undefined>;\n onKeyDown?: React.KeyboardEventHandler<HTMLDivElement>;\n onPositionChange?: (position: Position) => void;\n};\n\nconst isBrowser = typeof window !== \"undefined\" && typeof document !== \"undefined\";\n\nensureDialogPolyfill();\n\nconst DialogOverlayContent: React.FC<Omit<DialogOverlayProps, \"visible\">> = ({\n anchor,\n onClose,\n children,\n contentClassName,\n contentStyle,\n dataAttributes,\n onKeyDown,\n onPositionChange,\n}) => {\n const contentRef = React.useRef<HTMLDivElement>(null);\n const { rect } = useResizeObserver(contentRef, { box: \"border-box\" });\n\n const computedPosition = React.useMemo(() => {\n const viewport = getViewportInfo();\n const width = rect?.width ?? 0;\n const height = rect?.height ?? 0;\n return calculateContextMenuPosition(anchor.x, anchor.y, width, height, viewport);\n }, [anchor.x, anchor.y, rect?.width, rect?.height]);\n\n const handlePositionChange = useEffectEvent(onPositionChange);\n\n React.useEffect(() => {\n handlePositionChange?.(computedPosition);\n }, [computedPosition]);\n\n const handleClose = useEffectEvent(onClose);\n\n React.useEffect(() => {\n const handlePointerDown = (event: PointerEvent) => {\n if (!(event.target instanceof Node)) {\n return;\n }\n if (contentRef.current && !contentRef.current.contains(event.target)) {\n handleClose();\n }\n };\n\n document.addEventListener(\"pointerdown\", handlePointerDown);\n return () => document.removeEventListener(\"pointerdown\", handlePointerDown);\n }, []);\n\n const mergedStyle: React.CSSProperties = React.useMemo(\n () => ({\n ...contextContentStyle,\n ...contentStyle,\n left: computedPosition.x,\n top: computedPosition.y,\n }),\n [contentStyle, computedPosition.x, computedPosition.y],\n );\n\n const dataProps = React.useMemo<DataAttributes>(() => {\n if (!dataAttributes) {\n return {};\n }\n return Object.entries(dataAttributes).reduce<DataAttributes>((acc, [key, value]) => {\n if (value === null || value === undefined) {\n return acc;\n }\n acc[`data-${key}`] = value;\n return acc;\n }, {});\n }, [dataAttributes]);\n\n return (\n <div\n ref={contentRef}\n className={contentClassName}\n style={mergedStyle}\n onKeyDown={onKeyDown}\n {...dataProps}\n >\n {children}\n </div>\n );\n};\n\nconst DialogOverlayImpl: React.FC<DialogOverlayProps> = ({\n visible,\n onClose,\n anchor,\n children,\n contentClassName,\n contentStyle,\n dataAttributes,\n onKeyDown,\n onPositionChange,\n}) => {\n const dialogRef = React.useRef<HTMLDialogElement>(null);\n\n useIsomorphicLayoutEffect(() => {\n if (!dialogRef.current) {\n return;\n }\n\n const dialog = dialogRef.current;\n if (visible) {\n dialog.showModal();\n } else if (dialog.open) {\n dialog.close();\n }\n }, [visible]);\n\n const handleCancel = React.useCallback(\n (event: React.SyntheticEvent) => {\n event.preventDefault();\n onClose();\n },\n [onClose],\n );\n\n return (\n <dialog ref={dialogRef} style={contextDialogStyle} onCancel={handleCancel}>\n <React.Activity mode={visible ? \"visible\" : \"hidden\"}>\n <DialogOverlayContent\n anchor={anchor}\n onClose={onClose}\n contentClassName={contentClassName}\n contentStyle={contentStyle}\n dataAttributes={dataAttributes}\n onKeyDown={onKeyDown}\n onPositionChange={onPositionChange}\n >\n {children}\n </DialogOverlayContent>\n </React.Activity>\n </dialog>\n );\n};\n\nexport const DialogOverlay: React.FC<DialogOverlayProps> = (props) => {\n if (!isBrowser) {\n return null;\n }\n return <DialogOverlayImpl {...props} />;\n};\n\nDialogOverlay.displayName = \"DialogOverlay\";\n"],"names":["getViewportInfo","calculateContextMenuPosition","anchorX","anchorY","menuWidth","menuHeight","viewport","x","y","observerCache","getSharedObserver","box","observerKey","cached","callbacks","resizeObserver","entries","entry","callback","sharedObserver","target","measureElement","rect","entryToRect","size","useResizeObserver","ref","setEntry","React","useIsomorphicLayoutEffect","contextDialogStyle","contextContentStyle","DIALOG_OVERLAY_Z_INDEX","isBrowser","DialogOverlayContent","anchor","onClose","children","contentClassName","contentStyle","dataAttributes","onKeyDown","onPositionChange","contentRef","computedPosition","width","height","handlePositionChange","useEffectEvent","handleClose","handlePointerDown","event","mergedStyle","dataProps","acc","key","value","jsx","DialogOverlayImpl","visible","dialogRef","dialog","handleCancel","DialogOverlay","props"],"mappings":"woBAYaA,EAAkB,IACzB,OAAO,OAAW,IACb,CAAE,MAAO,EAAG,OAAQ,CAAA,EAGtB,CACL,MAAO,OAAO,WACd,OAAQ,OAAO,WAAA,EAONC,EAA+B,CAC1CC,EACAC,EACAC,EACAC,EACAC,IAC6B,CAE7B,MAAMC,EACJL,EAAUE,EAAYE,EAAS,MAAQ,KAAK,IAAI,EAAGA,EAAS,MAAQF,CAAS,EAAIF,EAG7EM,EACJL,EAAUE,EAAaC,EAAS,OAAS,KAAK,IAAI,EAAGA,EAAS,OAASD,CAAU,EAAIF,EAEvF,MAAO,CAAE,EAAAI,EAAG,EAAAC,CAAA,CACd,ECpBMC,MAAoB,IAKpBC,EAAqBC,GAAkD,CAC3E,MAAMC,EAAc,cAAcD,CAAG,GAC/BE,EAASJ,EAAc,IAAIG,CAAW,EAC5C,GAAIC,EACF,OAAOA,EAGT,MAAMC,MAAgB,IAEhBC,EAAiB,IAAI,eAAgBC,GAAY,CACrD,UAAWC,KAASD,EAAS,CAC3B,MAAME,EAAWJ,EAAU,IAAIG,EAAM,MAAM,EACvCC,GACFA,EAASD,CAAK,CAElB,CACF,CAAC,EAEKE,EAAiC,CACrC,QAAQC,EAAQF,EAAU,CACxB,OAAAJ,EAAU,IAAIM,EAAQF,CAAQ,EAC9BH,EAAe,QAAQK,EAAQ,CAAE,IAAAT,CAAA,CAAK,EAE/B,IAAM,CACXG,EAAU,OAAOM,CAAM,EACvBL,EAAe,UAAUK,CAAM,CACjC,CACF,CAAA,EAGF,OAAAX,EAAc,IAAIG,EAAaO,CAAc,EACtCA,CACT,EAKME,EAAkBD,GAAyC,CAC/D,MAAME,EAAOF,EAAO,sBAAA,EACpB,MAAO,CACL,OAAAA,EACA,YAAaE,EACb,cAAe,CAAC,CAAE,WAAYA,EAAK,MAAO,UAAWA,EAAK,OAAQ,EAClE,eAAgB,CAAC,CAAE,WAAYA,EAAK,MAAO,UAAWA,EAAK,OAAQ,EACnE,0BAA2B,CAAA,CAAC,CAEhC,EAKMC,EAAeN,GAAwC,CAC3D,GAAIA,EAAM,eAAe,OAAS,EAAG,CACnC,MAAMO,EAAOP,EAAM,cAAc,CAAC,EAClC,OAAO,IAAI,QAAQ,EAAG,EAAGO,EAAK,WAAYA,EAAK,SAAS,CAC1D,CACA,OAAOP,EAAM,WACf,EAoCO,SAASQ,EACdC,EACA,CAAE,IAAAf,EAAM,eACR,CACA,KAAM,CAACM,EAAOU,CAAQ,EAAIC,EAAM,SAAqC,IAAI,EAEzEC,EAAAA,0BAA0B,IAAM,CAC9B,MAAMT,EAASM,EAAI,QACnB,GAAI,CAACN,EAAQ,CACXO,EAAS,IAAI,EACb,MACF,CAGA,OAAAA,EAASN,EAAeD,CAAM,CAAC,EAGdV,EAAkBC,CAAG,EACtB,QAAQS,EAAQO,CAAQ,CAC1C,EAAG,CAACD,EAAKf,CAAG,CAAC,EAEb,MAAMW,EAAOM,EAAM,QAAQ,IACpBX,EAGEM,EAAYN,CAAK,EAFf,KAGR,CAACA,CAAK,CAAC,EAEV,MAAO,CAAE,MAAAA,EAAO,KAAAK,CAAA,CAClB,CCzIA,MAAMQ,EAA0C,CAC9C,OAAQ,OACR,QAAS,EACT,WAAY,aACd,EAEMC,EAA2C,CAC/C,SAAU,QACV,OAAQC,EAAAA,sBACV,EAgBMC,EAAY,OAAO,OAAW,KAAe,OAAO,SAAa,IAIjEC,EAAsE,CAAC,CAC3E,OAAAC,EACA,QAAAC,EACA,SAAAC,EACA,iBAAAC,EACA,aAAAC,EACA,eAAAC,EACA,UAAAC,EACA,iBAAAC,CACF,IAAM,CACJ,MAAMC,EAAaf,EAAM,OAAuB,IAAI,EAC9C,CAAE,KAAAN,GAASG,EAAkBkB,EAAY,CAAE,IAAK,aAAc,EAE9DC,EAAmBhB,EAAM,QAAQ,IAAM,CAC3C,MAAMtB,EAAWN,EAAA,EACX6C,EAAQvB,GAAM,OAAS,EACvBwB,EAASxB,GAAM,QAAU,EAC/B,OAAOrB,EAA6BkC,EAAO,EAAGA,EAAO,EAAGU,EAAOC,EAAQxC,CAAQ,CACjF,EAAG,CAAC6B,EAAO,EAAGA,EAAO,EAAGb,GAAM,MAAOA,GAAM,MAAM,CAAC,EAE5CyB,EAAuBC,EAAAA,eAAeN,CAAgB,EAE5Dd,EAAM,UAAU,IAAM,CACpBmB,IAAuBH,CAAgB,CACzC,EAAG,CAACA,CAAgB,CAAC,EAErB,MAAMK,EAAcD,EAAAA,eAAeZ,CAAO,EAE1CR,EAAM,UAAU,IAAM,CACpB,MAAMsB,EAAqBC,GAAwB,CAC3CA,EAAM,kBAAkB,MAG1BR,EAAW,SAAW,CAACA,EAAW,QAAQ,SAASQ,EAAM,MAAM,GACjEF,EAAA,CAEJ,EAEA,gBAAS,iBAAiB,cAAeC,CAAiB,EACnD,IAAM,SAAS,oBAAoB,cAAeA,CAAiB,CAC5E,EAAG,CAAA,CAAE,EAEL,MAAME,EAAmCxB,EAAM,QAC7C,KAAO,CACL,GAAGG,EACH,GAAGQ,EACH,KAAMK,EAAiB,EACvB,IAAKA,EAAiB,CAAA,GAExB,CAACL,EAAcK,EAAiB,EAAGA,EAAiB,CAAC,CAAA,EAGjDS,EAAYzB,EAAM,QAAwB,IACzCY,EAGE,OAAO,QAAQA,CAAc,EAAE,OAAuB,CAACc,EAAK,CAACC,EAAKC,CAAK,KACxEA,GAAU,OAGdF,EAAI,QAAQC,CAAG,EAAE,EAAIC,GACdF,GACN,CAAA,CAAE,EARI,CAAA,EASR,CAACd,CAAc,CAAC,EAEnB,OACEiB,EAAAA,IAAC,MAAA,CACC,IAAKd,EACL,UAAWL,EACX,MAAOc,EACP,UAAAX,EACC,GAAGY,EAEH,SAAAhB,CAAA,CAAA,CAGP,EAEMqB,EAAkD,CAAC,CACvD,QAAAC,EACA,QAAAvB,EACA,OAAAD,EACA,SAAAE,EACA,iBAAAC,EACA,aAAAC,EACA,eAAAC,EACA,UAAAC,EACA,iBAAAC,CACF,IAAM,CACJ,MAAMkB,EAAYhC,EAAM,OAA0B,IAAI,EAEtDC,EAAAA,0BAA0B,IAAM,CAC9B,GAAI,CAAC+B,EAAU,QACb,OAGF,MAAMC,EAASD,EAAU,QACrBD,EACFE,EAAO,UAAA,EACEA,EAAO,MAChBA,EAAO,MAAA,CAEX,EAAG,CAACF,CAAO,CAAC,EAEZ,MAAMG,EAAelC,EAAM,YACxBuB,GAAgC,CAC/BA,EAAM,eAAA,EACNf,EAAA,CACF,EACA,CAACA,CAAO,CAAA,EAGV,OACEqB,EAAAA,IAAC,SAAA,CAAO,IAAKG,EAAW,MAAO9B,EAAoB,SAAUgC,EAC3D,SAAAL,MAAC7B,EAAM,SAAN,CAAe,KAAM+B,EAAU,UAAY,SAC1C,SAAAF,EAAAA,IAACvB,EAAA,CACC,OAAAC,EACA,QAAAC,EACA,iBAAAE,EACA,aAAAC,EACA,eAAAC,EACA,UAAAC,EACA,iBAAAC,EAEC,SAAAL,CAAA,CAAA,EAEL,CAAA,CACF,CAEJ,EAEa0B,EAA+CC,GACrD/B,EAGEwB,MAACC,EAAA,CAAmB,GAAGM,CAAA,CAAO,EAF5B,KAKXD,EAAc,YAAc"}
package/dist/window.js CHANGED
@@ -1,149 +1,160 @@
1
- import { D as $, b as A, F as G, P as K, d as W } from "./FloatingWindow-BpdOpg_L.js";
1
+ import { D as A, b as G, F as K, P as T, S as W, d as q } from "./FloatingWindow-CUXnEtrb.js";
2
2
  import { jsx as m } from "react/jsx-runtime";
3
- import * as i from "react";
4
- import { u as p } from "./useEffectEvent-Dp7HLCf0.js";
5
- import { u as D } from "./useIsomorphicLayoutEffect-DhmEnmZ_.js";
6
- import { D as E } from "./styles-DPPuJ0sf.js";
7
- import { u as q } from "./useFloatingState-tEfA_wbc.js";
8
- const M = () => typeof window > "u" ? { width: 0, height: 0 } : {
3
+ import * as c from "react";
4
+ import { u as p } from "./useDocumentPointerEvents-DXxw3qWj.js";
5
+ import { u as x } from "./useIsomorphicLayoutEffect-DhmEnmZ_.js";
6
+ import { D as z } from "./styles-NkjuMOVS.js";
7
+ import { u as J } from "./useFloatingState-tEfA_wbc.js";
8
+ const D = () => typeof window > "u" ? { width: 0, height: 0 } : {
9
9
  width: window.innerWidth,
10
10
  height: window.innerHeight
11
- }, O = (e, o, t, c, n) => {
12
- const r = e + t > n.width ? Math.max(0, n.width - t) : e, s = o + c > n.height ? Math.max(0, n.height - c) : o;
13
- return { x: r, y: s };
14
- }, b = /* @__PURE__ */ new Map(), z = (e) => {
15
- const { box: o = "content-box" } = e, t = `resize-box:${o}`, c = b.get(t);
16
- if (c)
17
- return c;
18
- const n = new class {
19
- #e = /* @__PURE__ */ new Map();
20
- #t = new ResizeObserver((r, s) => {
21
- r.forEach((u) => {
22
- const a = this.#e.get(u.target);
23
- a && a(u, s);
24
- });
25
- });
26
- observe(r, s) {
27
- return this.#e.set(r, s), this.#t.observe(r, e), () => {
28
- this.#e.delete(r), this.#t.unobserve(r);
11
+ }, O = (e, t, n, o, r) => {
12
+ const s = e + n > r.width ? Math.max(0, r.width - n) : e, i = t + o > r.height ? Math.max(0, r.height - o) : t;
13
+ return { x: s, y: i };
14
+ }, b = /* @__PURE__ */ new Map(), R = (e) => {
15
+ const t = `resize-box:${e}`, n = b.get(t);
16
+ if (n)
17
+ return n;
18
+ const o = /* @__PURE__ */ new Map(), r = new ResizeObserver((i) => {
19
+ for (const a of i) {
20
+ const u = o.get(a.target);
21
+ u && u(a);
22
+ }
23
+ }), s = {
24
+ observe(i, a) {
25
+ return o.set(i, a), r.observe(i, { box: e }), () => {
26
+ o.delete(i), r.unobserve(i);
29
27
  };
30
28
  }
31
- }();
32
- return b.set(t, n), n;
29
+ };
30
+ return b.set(t, s), s;
31
+ }, E = (e) => {
32
+ const t = e.getBoundingClientRect();
33
+ return {
34
+ target: e,
35
+ contentRect: t,
36
+ borderBoxSize: [{ inlineSize: t.width, blockSize: t.height }],
37
+ contentBoxSize: [{ inlineSize: t.width, blockSize: t.height }],
38
+ devicePixelContentBoxSize: []
39
+ };
40
+ }, M = (e) => {
41
+ if (e.borderBoxSize?.length > 0) {
42
+ const t = e.borderBoxSize[0];
43
+ return new DOMRect(0, 0, t.inlineSize, t.blockSize);
44
+ }
45
+ return e.contentRect;
33
46
  };
34
- function R(e, { box: o }) {
35
- const [t, c] = i.useState(null), n = e.current;
36
- i.useEffect(() => n ? z({ box: o }).observe(n, (u) => {
37
- c(u);
38
- }) : void 0, [o, n]);
39
- const r = i.useMemo(() => {
40
- if (!t)
41
- return null;
42
- if (t.borderBoxSize?.length > 0) {
43
- const s = t.borderBoxSize[0];
44
- return new DOMRect(0, 0, s.inlineSize, s.blockSize);
47
+ function C(e, { box: t = "content-box" }) {
48
+ const [n, o] = c.useState(null);
49
+ x(() => {
50
+ const s = e.current;
51
+ if (!s) {
52
+ o(null);
53
+ return;
45
54
  }
46
- return t.contentRect;
47
- }, [t]);
48
- return { entry: t, rect: r };
55
+ return o(E(s)), R(t).observe(s, o);
56
+ }, [e, t]);
57
+ const r = c.useMemo(() => n ? M(n) : null, [n]);
58
+ return { entry: n, rect: r };
49
59
  }
50
- const S = {
60
+ const P = {
51
61
  border: "none",
52
62
  padding: 0,
53
63
  background: "transparent"
54
- }, P = {
64
+ }, k = {
55
65
  position: "fixed",
56
- zIndex: E
57
- }, C = typeof window < "u" && typeof document < "u", L = ({
66
+ zIndex: z
67
+ }, B = typeof window < "u" && typeof document < "u", L = ({
58
68
  anchor: e,
59
- onClose: o,
60
- children: t,
61
- contentClassName: c,
62
- contentStyle: n,
63
- dataAttributes: r,
64
- onKeyDown: s,
65
- onPositionChange: u
69
+ onClose: t,
70
+ children: n,
71
+ contentClassName: o,
72
+ contentStyle: r,
73
+ dataAttributes: s,
74
+ onKeyDown: i,
75
+ onPositionChange: a
66
76
  }) => {
67
- const a = i.useRef(null), { rect: d } = R(a, { box: "border-box" }), l = i.useMemo(() => {
68
- const f = M(), w = d?.width ?? 0, g = d?.height ?? 0;
69
- return O(e.x, e.y, w, g, f);
70
- }, [e.x, e.y, d?.width, d?.height]), h = p(u);
71
- i.useEffect(() => {
77
+ const u = c.useRef(null), { rect: d } = C(u, { box: "border-box" }), l = c.useMemo(() => {
78
+ const f = D(), g = d?.width ?? 0, w = d?.height ?? 0;
79
+ return O(e.x, e.y, g, w, f);
80
+ }, [e.x, e.y, d?.width, d?.height]), h = p(a);
81
+ c.useEffect(() => {
72
82
  h?.(l);
73
83
  }, [l]);
74
- const y = p(o);
75
- i.useEffect(() => {
76
- const f = (w) => {
77
- w.target instanceof Node && a.current && !a.current.contains(w.target) && y();
84
+ const y = p(t);
85
+ c.useEffect(() => {
86
+ const f = (g) => {
87
+ g.target instanceof Node && u.current && !u.current.contains(g.target) && y();
78
88
  };
79
89
  return document.addEventListener("pointerdown", f), () => document.removeEventListener("pointerdown", f);
80
90
  }, []);
81
- const x = i.useMemo(
91
+ const v = c.useMemo(
82
92
  () => ({
83
- ...P,
84
- ...n,
93
+ ...k,
94
+ ...r,
85
95
  left: l.x,
86
96
  top: l.y
87
97
  }),
88
- [n, l.x, l.y]
89
- ), v = i.useMemo(() => r ? Object.entries(r).reduce((f, [w, g]) => (g == null || (f[`data-${w}`] = g), f), {}) : {}, [r]);
98
+ [r, l.x, l.y]
99
+ ), S = c.useMemo(() => s ? Object.entries(s).reduce((f, [g, w]) => (w == null || (f[`data-${g}`] = w), f), {}) : {}, [s]);
90
100
  return /* @__PURE__ */ m(
91
101
  "div",
92
102
  {
93
- ref: a,
94
- className: c,
95
- style: x,
96
- onKeyDown: s,
97
- ...v,
98
- children: t
103
+ ref: u,
104
+ className: o,
105
+ style: v,
106
+ onKeyDown: i,
107
+ ...S,
108
+ children: n
99
109
  }
100
110
  );
101
111
  }, I = ({
102
112
  visible: e,
103
- onClose: o,
104
- anchor: t,
105
- children: c,
106
- contentClassName: n,
107
- contentStyle: r,
108
- dataAttributes: s,
109
- onKeyDown: u,
110
- onPositionChange: a
113
+ onClose: t,
114
+ anchor: n,
115
+ children: o,
116
+ contentClassName: r,
117
+ contentStyle: s,
118
+ dataAttributes: i,
119
+ onKeyDown: a,
120
+ onPositionChange: u
111
121
  }) => {
112
- const d = i.useRef(null);
113
- D(() => {
122
+ const d = c.useRef(null);
123
+ x(() => {
114
124
  if (!d.current)
115
125
  return;
116
126
  const h = d.current;
117
127
  e ? h.showModal() : h.open && h.close();
118
128
  }, [e]);
119
- const l = i.useCallback(
129
+ const l = c.useCallback(
120
130
  (h) => {
121
- h.preventDefault(), o();
131
+ h.preventDefault(), t();
122
132
  },
123
- [o]
133
+ [t]
124
134
  );
125
- return /* @__PURE__ */ m("dialog", { ref: d, style: S, onCancel: l, children: /* @__PURE__ */ m(i.Activity, { mode: e ? "visible" : "hidden", children: /* @__PURE__ */ m(
135
+ return /* @__PURE__ */ m("dialog", { ref: d, style: P, onCancel: l, children: /* @__PURE__ */ m(c.Activity, { mode: e ? "visible" : "hidden", children: /* @__PURE__ */ m(
126
136
  L,
127
137
  {
128
- anchor: t,
129
- onClose: o,
130
- contentClassName: n,
131
- contentStyle: r,
132
- dataAttributes: s,
133
- onKeyDown: u,
134
- onPositionChange: a,
135
- children: c
138
+ anchor: n,
139
+ onClose: t,
140
+ contentClassName: r,
141
+ contentStyle: s,
142
+ dataAttributes: i,
143
+ onKeyDown: a,
144
+ onPositionChange: u,
145
+ children: o
136
146
  }
137
147
  ) }) });
138
- }, k = (e) => C ? /* @__PURE__ */ m(I, { ...e }) : null;
139
- k.displayName = "DialogOverlay";
148
+ }, N = (e) => B ? /* @__PURE__ */ m(I, { ...e }) : null;
149
+ N.displayName = "DialogOverlay";
140
150
  export {
141
- k as DialogOverlay,
142
- $ as Drawer,
143
- A as DrawerLayers,
144
- G as FloatingWindow,
145
- K as PopupLayerPortal,
146
- W as useDrawerState,
147
- q as useFloatingState
151
+ N as DialogOverlay,
152
+ A as Drawer,
153
+ G as DrawerLayers,
154
+ K as FloatingWindow,
155
+ T as PopupLayerPortal,
156
+ W as SwipeSafeZone,
157
+ q as useDrawerState,
158
+ J as useFloatingState
148
159
  };
149
160
  //# sourceMappingURL=window.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"window.js","sources":["../src/utils/dialogUtils.ts","../src/hooks/useResizeObserver.tsx","../src/components/window/DialogOverlay.tsx"],"sourcesContent":["/**\n * @file Dialog positioning utilities\n */\n\nexport type ViewportInfo = {\n width: number;\n height: number;\n};\n\n/**\n * Get viewport dimensions\n */\nexport const getViewportInfo = (): ViewportInfo => {\n if (typeof window === \"undefined\") {\n return { width: 0, height: 0 };\n }\n\n return {\n width: window.innerWidth,\n height: window.innerHeight,\n };\n};\n\n/**\n * Calculate context menu position to keep it within viewport\n */\nexport const calculateContextMenuPosition = (\n anchorX: number,\n anchorY: number,\n menuWidth: number,\n menuHeight: number,\n viewport: ViewportInfo,\n): { x: number; y: number } => {\n // Adjust horizontal position if menu would overflow\n const x =\n anchorX + menuWidth > viewport.width ? Math.max(0, viewport.width - menuWidth) : anchorX;\n\n // Adjust vertical position if menu would overflow\n const y =\n anchorY + menuHeight > viewport.height ? Math.max(0, viewport.height - menuHeight) : anchorY;\n\n return { x, y };\n};\n","/**\n * @file Shared useResizeObserver hook with cached observer instances.\n */\nimport * as React from \"react\";\n\ntype Unobserve = () => void;\ntype Callback = (entry: ResizeObserverEntry, observer: ResizeObserver) => void;\ntype SharedObserver = {\n observe: (target: Element, callback: Callback) => Unobserve;\n};\nconst observerCache = new Map<string, SharedObserver>();\nconst getSharedObserver = (options: ResizeObserverOptions) => {\n const { box = \"content-box\" } = options;\n const observerKey = `resize-box:${box}`;\n const cached = observerCache.get(observerKey);\n if (cached) {\n return cached;\n }\n const observer = new (class {\n #callbackMap = new Map<Element, Callback>();\n #resizeObserver = new ResizeObserver((entries, observer) => {\n entries.forEach((entry) => {\n const callback = this.#callbackMap.get(entry.target);\n if (callback) {\n callback(entry, observer);\n }\n });\n });\n observe(target: Element, callback: Callback) {\n this.#callbackMap.set(target, callback);\n this.#resizeObserver.observe(target, options);\n return () => {\n this.#callbackMap.delete(target);\n this.#resizeObserver.unobserve(target);\n };\n }\n })();\n observerCache.set(observerKey, observer);\n\n return observer;\n};\n/**\n * Observe size changes for a given element reference using shared resize observers.\n *\n * @param ref - Ref holding the element whose size to monitor.\n * @param options - Resize observer configuration.\n * @returns Latest resize entry and a derived DOMRect snapshot.\n */\nexport function useResizeObserver<T extends HTMLElement>(\n ref: React.RefObject<T | null>,\n { box }: ResizeObserverOptions,\n) {\n const [entry, setEntry] = React.useState<ResizeObserverEntry | null>(null);\n const target = ref.current;\n\n React.useEffect(() => {\n if (!target) {\n return;\n }\n\n const observer = getSharedObserver({ box });\n return observer.observe(target, (nextEntry) => {\n setEntry(nextEntry);\n });\n }, [box, target]);\n\n const rect = React.useMemo(() => {\n if (!entry) {\n return null;\n }\n\n if (entry.borderBoxSize?.length > 0) {\n const size = entry.borderBoxSize[0];\n return new DOMRect(0, 0, size.inlineSize, size.blockSize);\n }\n\n return entry.contentRect;\n }, [entry]);\n\n return { entry, rect };\n}\n","/**\n * @file Dialog-based overlay component with automatic positioning\n */\nimport * as React from \"react\";\nimport type { Position } from \"../../types\";\nimport { calculateContextMenuPosition, getViewportInfo } from \"../../utils/dialogUtils\";\nimport { ensureDialogPolyfill } from \"../../utils/polyfills/createDialogPolyfill\";\nimport { useEffectEvent } from \"../../hooks/useEffectEvent\";\nimport { useIsomorphicLayoutEffect } from \"../../hooks/useIsomorphicLayoutEffect\";\nimport { useResizeObserver } from \"../../hooks/useResizeObserver\";\nimport { DIALOG_OVERLAY_Z_INDEX } from \"../../constants/styles\";\n\nconst contextDialogStyle: React.CSSProperties = {\n border: \"none\",\n padding: 0,\n background: \"transparent\",\n};\n\nconst contextContentStyle: React.CSSProperties = {\n position: \"fixed\",\n zIndex: DIALOG_OVERLAY_Z_INDEX,\n};\n\ntype DataAttributes = Record<string, string | number | boolean>;\n\nexport type DialogOverlayProps = {\n anchor: Position;\n visible: boolean;\n onClose: () => void;\n children: React.ReactNode;\n contentClassName?: string;\n contentStyle?: React.CSSProperties;\n dataAttributes?: Record<string, string | number | boolean | null | undefined>;\n onKeyDown?: React.KeyboardEventHandler<HTMLDivElement>;\n onPositionChange?: (position: Position) => void;\n};\n\nconst isBrowser = typeof window !== \"undefined\" && typeof document !== \"undefined\";\n\nensureDialogPolyfill();\n\nconst DialogOverlayContent: React.FC<Omit<DialogOverlayProps, \"visible\">> = ({\n anchor,\n onClose,\n children,\n contentClassName,\n contentStyle,\n dataAttributes,\n onKeyDown,\n onPositionChange,\n}) => {\n const contentRef = React.useRef<HTMLDivElement>(null);\n const { rect } = useResizeObserver(contentRef, { box: \"border-box\" });\n\n const computedPosition = React.useMemo(() => {\n const viewport = getViewportInfo();\n const width = rect?.width ?? 0;\n const height = rect?.height ?? 0;\n return calculateContextMenuPosition(anchor.x, anchor.y, width, height, viewport);\n }, [anchor.x, anchor.y, rect?.width, rect?.height]);\n\n const handlePositionChange = useEffectEvent(onPositionChange);\n\n React.useEffect(() => {\n handlePositionChange?.(computedPosition);\n }, [computedPosition]);\n\n const handleClose = useEffectEvent(onClose);\n\n React.useEffect(() => {\n const handlePointerDown = (event: PointerEvent) => {\n if (!(event.target instanceof Node)) {\n return;\n }\n if (contentRef.current && !contentRef.current.contains(event.target)) {\n handleClose();\n }\n };\n\n document.addEventListener(\"pointerdown\", handlePointerDown);\n return () => document.removeEventListener(\"pointerdown\", handlePointerDown);\n }, []);\n\n const mergedStyle: React.CSSProperties = React.useMemo(\n () => ({\n ...contextContentStyle,\n ...contentStyle,\n left: computedPosition.x,\n top: computedPosition.y,\n }),\n [contentStyle, computedPosition.x, computedPosition.y],\n );\n\n const dataProps = React.useMemo<DataAttributes>(() => {\n if (!dataAttributes) {\n return {};\n }\n return Object.entries(dataAttributes).reduce<DataAttributes>((acc, [key, value]) => {\n if (value === null || value === undefined) {\n return acc;\n }\n acc[`data-${key}`] = value;\n return acc;\n }, {});\n }, [dataAttributes]);\n\n return (\n <div\n ref={contentRef}\n className={contentClassName}\n style={mergedStyle}\n onKeyDown={onKeyDown}\n {...dataProps}\n >\n {children}\n </div>\n );\n};\n\nconst DialogOverlayImpl: React.FC<DialogOverlayProps> = ({\n visible,\n onClose,\n anchor,\n children,\n contentClassName,\n contentStyle,\n dataAttributes,\n onKeyDown,\n onPositionChange,\n}) => {\n const dialogRef = React.useRef<HTMLDialogElement>(null);\n\n useIsomorphicLayoutEffect(() => {\n if (!dialogRef.current) {\n return;\n }\n\n const dialog = dialogRef.current;\n if (visible) {\n dialog.showModal();\n } else if (dialog.open) {\n dialog.close();\n }\n }, [visible]);\n\n const handleCancel = React.useCallback(\n (event: React.SyntheticEvent) => {\n event.preventDefault();\n onClose();\n },\n [onClose],\n );\n\n return (\n <dialog ref={dialogRef} style={contextDialogStyle} onCancel={handleCancel}>\n <React.Activity mode={visible ? \"visible\" : \"hidden\"}>\n <DialogOverlayContent\n anchor={anchor}\n onClose={onClose}\n contentClassName={contentClassName}\n contentStyle={contentStyle}\n dataAttributes={dataAttributes}\n onKeyDown={onKeyDown}\n onPositionChange={onPositionChange}\n >\n {children}\n </DialogOverlayContent>\n </React.Activity>\n </dialog>\n );\n};\n\nexport const DialogOverlay: React.FC<DialogOverlayProps> = (props) => {\n if (!isBrowser) {\n return null;\n }\n return <DialogOverlayImpl {...props} />;\n};\n\nDialogOverlay.displayName = \"DialogOverlay\";\n"],"names":["getViewportInfo","calculateContextMenuPosition","anchorX","anchorY","menuWidth","menuHeight","viewport","x","y","observerCache","getSharedObserver","options","box","observerKey","cached","observer","#callbackMap","#resizeObserver","entries","entry","callback","target","useResizeObserver","ref","setEntry","React","nextEntry","rect","size","contextDialogStyle","contextContentStyle","DIALOG_OVERLAY_Z_INDEX","isBrowser","DialogOverlayContent","anchor","onClose","children","contentClassName","contentStyle","dataAttributes","onKeyDown","onPositionChange","contentRef","computedPosition","width","height","handlePositionChange","useEffectEvent","handleClose","handlePointerDown","event","mergedStyle","dataProps","acc","key","value","jsx","DialogOverlayImpl","visible","dialogRef","useIsomorphicLayoutEffect","dialog","handleCancel","DialogOverlay","props"],"mappings":"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"}
1
+ {"version":3,"file":"window.js","sources":["../src/utils/dialogUtils.ts","../src/hooks/useResizeObserver.tsx","../src/components/window/DialogOverlay.tsx"],"sourcesContent":["/**\n * @file Dialog positioning utilities\n */\n\nexport type ViewportInfo = {\n width: number;\n height: number;\n};\n\n/**\n * Get viewport dimensions\n */\nexport const getViewportInfo = (): ViewportInfo => {\n if (typeof window === \"undefined\") {\n return { width: 0, height: 0 };\n }\n\n return {\n width: window.innerWidth,\n height: window.innerHeight,\n };\n};\n\n/**\n * Calculate context menu position to keep it within viewport\n */\nexport const calculateContextMenuPosition = (\n anchorX: number,\n anchorY: number,\n menuWidth: number,\n menuHeight: number,\n viewport: ViewportInfo,\n): { x: number; y: number } => {\n // Adjust horizontal position if menu would overflow\n const x =\n anchorX + menuWidth > viewport.width ? Math.max(0, viewport.width - menuWidth) : anchorX;\n\n // Adjust vertical position if menu would overflow\n const y =\n anchorY + menuHeight > viewport.height ? Math.max(0, viewport.height - menuHeight) : anchorY;\n\n return { x, y };\n};\n","/**\n * @file Shared useResizeObserver hook with cached observer instances.\n *\n * Provides element size observation with shared observers for memory efficiency.\n * Size becomes available after the first useLayoutEffect cycle completes.\n *\n * Note: Due to React's effect execution order (children before parents),\n * child components may see containerSize=0 on their first effect run.\n * This is a React constraint, not a bug. Consumers should check for\n * valid size before using it for calculations like animation positions.\n */\nimport * as React from \"react\";\nimport { useIsomorphicLayoutEffect } from \"./useIsomorphicLayoutEffect.js\";\n\n/**\n * Shared ResizeObserver that can observe multiple elements.\n */\ntype SharedObserver = {\n observe: (target: Element, callback: (entry: ResizeObserverEntry) => void) => () => void;\n};\n\n/** Cache of shared observers per box option */\nconst observerCache = new Map<string, SharedObserver>();\n\n/**\n * Get or create a shared ResizeObserver for the given box option.\n */\nconst getSharedObserver = (box: ResizeObserverBoxOptions): SharedObserver => {\n const observerKey = `resize-box:${box}`;\n const cached = observerCache.get(observerKey);\n if (cached) {\n return cached;\n }\n\n const callbacks = new Map<Element, (entry: ResizeObserverEntry) => void>();\n\n const resizeObserver = new ResizeObserver((entries) => {\n for (const entry of entries) {\n const callback = callbacks.get(entry.target);\n if (callback) {\n callback(entry);\n }\n }\n });\n\n const sharedObserver: SharedObserver = {\n observe(target, callback) {\n callbacks.set(target, callback);\n resizeObserver.observe(target, { box });\n\n return () => {\n callbacks.delete(target);\n resizeObserver.unobserve(target);\n };\n },\n };\n\n observerCache.set(observerKey, sharedObserver);\n return sharedObserver;\n};\n\n/**\n * Create a ResizeObserverEntry from getBoundingClientRect.\n */\nconst measureElement = (target: Element): ResizeObserverEntry => {\n const rect = target.getBoundingClientRect();\n return {\n target,\n contentRect: rect,\n borderBoxSize: [{ inlineSize: rect.width, blockSize: rect.height }],\n contentBoxSize: [{ inlineSize: rect.width, blockSize: rect.height }],\n devicePixelContentBoxSize: [],\n };\n};\n\n/**\n * Extract DOMRect from ResizeObserverEntry.\n */\nconst entryToRect = (entry: ResizeObserverEntry): DOMRect => {\n if (entry.borderBoxSize?.length > 0) {\n const size = entry.borderBoxSize[0];\n return new DOMRect(0, 0, size.inlineSize, size.blockSize);\n }\n return entry.contentRect;\n};\n\n/**\n * Clear observer cache. Exported for testing purposes.\n */\nexport function clearObserverCache(): void {\n observerCache.clear();\n}\n\n/**\n * Observe size changes for a given element reference using shared resize observers.\n *\n * @param ref - Ref holding the element whose size to monitor.\n * @param options - Resize observer configuration.\n * @returns Latest resize entry and a derived DOMRect snapshot.\n *\n * @remarks\n * The `rect` will be `null` on the first render. After the initial\n * useLayoutEffect runs and triggers a re-render, `rect` will contain\n * the measured size.\n *\n * Due to React's effect execution order, child components' effects run\n * before parent effects. If you pass `rect.width` to a child as a prop,\n * the child's first effect will see `0` (or whatever default you use).\n * This is expected React behavior.\n *\n * @example\n * ```tsx\n * const containerRef = useRef<HTMLDivElement>(null);\n * const { rect } = useResizeObserver(containerRef, { box: \"border-box\" });\n * const width = rect?.width ?? 0;\n *\n * // Check if size is ready before using for calculations\n * const isReady = rect !== null;\n * ```\n */\nexport function useResizeObserver<T extends HTMLElement>(\n ref: React.RefObject<T | null>,\n { box = \"content-box\" }: ResizeObserverOptions,\n) {\n const [entry, setEntry] = React.useState<ResizeObserverEntry | null>(null);\n\n useIsomorphicLayoutEffect(() => {\n const target = ref.current;\n if (!target) {\n setEntry(null);\n return;\n }\n\n // Measure immediately\n setEntry(measureElement(target));\n\n // Set up ResizeObserver for subsequent updates\n const observer = getSharedObserver(box);\n return observer.observe(target, setEntry);\n }, [ref, box]);\n\n const rect = React.useMemo(() => {\n if (!entry) {\n return null;\n }\n return entryToRect(entry);\n }, [entry]);\n\n return { entry, rect };\n}\n","/**\n * @file Dialog-based overlay component with automatic positioning\n */\nimport * as React from \"react\";\nimport type { Position } from \"../../types\";\nimport { calculateContextMenuPosition, getViewportInfo } from \"../../utils/dialogUtils\";\nimport { ensureDialogPolyfill } from \"../../utils/polyfills/createDialogPolyfill\";\nimport { useEffectEvent } from \"../../hooks/useEffectEvent\";\nimport { useIsomorphicLayoutEffect } from \"../../hooks/useIsomorphicLayoutEffect\";\nimport { useResizeObserver } from \"../../hooks/useResizeObserver\";\nimport { DIALOG_OVERLAY_Z_INDEX } from \"../../constants/styles\";\n\nconst contextDialogStyle: React.CSSProperties = {\n border: \"none\",\n padding: 0,\n background: \"transparent\",\n};\n\nconst contextContentStyle: React.CSSProperties = {\n position: \"fixed\",\n zIndex: DIALOG_OVERLAY_Z_INDEX,\n};\n\ntype DataAttributes = Record<string, string | number | boolean>;\n\nexport type DialogOverlayProps = {\n anchor: Position;\n visible: boolean;\n onClose: () => void;\n children: React.ReactNode;\n contentClassName?: string;\n contentStyle?: React.CSSProperties;\n dataAttributes?: Record<string, string | number | boolean | null | undefined>;\n onKeyDown?: React.KeyboardEventHandler<HTMLDivElement>;\n onPositionChange?: (position: Position) => void;\n};\n\nconst isBrowser = typeof window !== \"undefined\" && typeof document !== \"undefined\";\n\nensureDialogPolyfill();\n\nconst DialogOverlayContent: React.FC<Omit<DialogOverlayProps, \"visible\">> = ({\n anchor,\n onClose,\n children,\n contentClassName,\n contentStyle,\n dataAttributes,\n onKeyDown,\n onPositionChange,\n}) => {\n const contentRef = React.useRef<HTMLDivElement>(null);\n const { rect } = useResizeObserver(contentRef, { box: \"border-box\" });\n\n const computedPosition = React.useMemo(() => {\n const viewport = getViewportInfo();\n const width = rect?.width ?? 0;\n const height = rect?.height ?? 0;\n return calculateContextMenuPosition(anchor.x, anchor.y, width, height, viewport);\n }, [anchor.x, anchor.y, rect?.width, rect?.height]);\n\n const handlePositionChange = useEffectEvent(onPositionChange);\n\n React.useEffect(() => {\n handlePositionChange?.(computedPosition);\n }, [computedPosition]);\n\n const handleClose = useEffectEvent(onClose);\n\n React.useEffect(() => {\n const handlePointerDown = (event: PointerEvent) => {\n if (!(event.target instanceof Node)) {\n return;\n }\n if (contentRef.current && !contentRef.current.contains(event.target)) {\n handleClose();\n }\n };\n\n document.addEventListener(\"pointerdown\", handlePointerDown);\n return () => document.removeEventListener(\"pointerdown\", handlePointerDown);\n }, []);\n\n const mergedStyle: React.CSSProperties = React.useMemo(\n () => ({\n ...contextContentStyle,\n ...contentStyle,\n left: computedPosition.x,\n top: computedPosition.y,\n }),\n [contentStyle, computedPosition.x, computedPosition.y],\n );\n\n const dataProps = React.useMemo<DataAttributes>(() => {\n if (!dataAttributes) {\n return {};\n }\n return Object.entries(dataAttributes).reduce<DataAttributes>((acc, [key, value]) => {\n if (value === null || value === undefined) {\n return acc;\n }\n acc[`data-${key}`] = value;\n return acc;\n }, {});\n }, [dataAttributes]);\n\n return (\n <div\n ref={contentRef}\n className={contentClassName}\n style={mergedStyle}\n onKeyDown={onKeyDown}\n {...dataProps}\n >\n {children}\n </div>\n );\n};\n\nconst DialogOverlayImpl: React.FC<DialogOverlayProps> = ({\n visible,\n onClose,\n anchor,\n children,\n contentClassName,\n contentStyle,\n dataAttributes,\n onKeyDown,\n onPositionChange,\n}) => {\n const dialogRef = React.useRef<HTMLDialogElement>(null);\n\n useIsomorphicLayoutEffect(() => {\n if (!dialogRef.current) {\n return;\n }\n\n const dialog = dialogRef.current;\n if (visible) {\n dialog.showModal();\n } else if (dialog.open) {\n dialog.close();\n }\n }, [visible]);\n\n const handleCancel = React.useCallback(\n (event: React.SyntheticEvent) => {\n event.preventDefault();\n onClose();\n },\n [onClose],\n );\n\n return (\n <dialog ref={dialogRef} style={contextDialogStyle} onCancel={handleCancel}>\n <React.Activity mode={visible ? \"visible\" : \"hidden\"}>\n <DialogOverlayContent\n anchor={anchor}\n onClose={onClose}\n contentClassName={contentClassName}\n contentStyle={contentStyle}\n dataAttributes={dataAttributes}\n onKeyDown={onKeyDown}\n onPositionChange={onPositionChange}\n >\n {children}\n </DialogOverlayContent>\n </React.Activity>\n </dialog>\n );\n};\n\nexport const DialogOverlay: React.FC<DialogOverlayProps> = (props) => {\n if (!isBrowser) {\n return null;\n }\n return <DialogOverlayImpl {...props} />;\n};\n\nDialogOverlay.displayName = \"DialogOverlay\";\n"],"names":["getViewportInfo","calculateContextMenuPosition","anchorX","anchorY","menuWidth","menuHeight","viewport","x","y","observerCache","getSharedObserver","box","observerKey","cached","callbacks","resizeObserver","entries","entry","callback","sharedObserver","target","measureElement","rect","entryToRect","size","useResizeObserver","ref","setEntry","React","useIsomorphicLayoutEffect","contextDialogStyle","contextContentStyle","DIALOG_OVERLAY_Z_INDEX","isBrowser","DialogOverlayContent","anchor","onClose","children","contentClassName","contentStyle","dataAttributes","onKeyDown","onPositionChange","contentRef","computedPosition","width","height","handlePositionChange","useEffectEvent","handleClose","handlePointerDown","event","mergedStyle","dataProps","acc","key","value","jsx","DialogOverlayImpl","visible","dialogRef","dialog","handleCancel","DialogOverlay","props"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-panel-layout",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "license": "Unlicense",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -62,6 +62,11 @@
62
62
  "import": "./dist/panels.js",
63
63
  "require": "./dist/panels.cjs"
64
64
  },
65
+ "./dialog": {
66
+ "types": "./dist/dialog/index.d.ts",
67
+ "import": "./dist/dialog.js",
68
+ "require": "./dist/dialog.cjs"
69
+ },
65
70
  "./package.json": "./package.json"
66
71
  },
67
72
  "files": [