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
@@ -8,6 +8,7 @@ import { useStackNavigation } from "../../../../modules/stack/useStackNavigation
8
8
  import { useStackSwipeInput } from "../../../../modules/stack/useStackSwipeInput.js";
9
9
  import { SwipeStackContent } from "../../../../modules/stack/SwipeStackContent.js";
10
10
  import { useResizeObserver } from "../../../../hooks/useResizeObserver.js";
11
+ import { toContinuousOperationState } from "../../../../hooks/gesture/types.js";
11
12
  import type { StackPanel } from "../../../../modules/stack/types.js";
12
13
  import styles from "./StackTablet.module.css";
13
14
  import "../../../styles/stack-themes.css";
@@ -271,36 +272,53 @@ export const StackTablet: React.FC<StackTabletProps> = ({ theme = "ios" }) => {
271
272
  // Get visible panels: active + behind (for swipe reveal) + exiting (for back animation)
272
273
  const { stack, depth } = navigation.state;
273
274
 
274
- // Track exiting panel when navigating back
275
- const [exitingPanelId, setExitingPanelId] = React.useState<string | null>(null);
275
+ // Track exiting panel when navigating back.
276
+ // CRITICAL: exitingPanelId must be computed synchronously during render
277
+ // to prevent the exiting panel from being unmounted for a frame.
278
+ // If the panel unmounts, it loses its position state and causes a visual jump.
276
279
  const prevDepthRef = React.useRef(depth);
277
280
  const prevStackRef = React.useRef<ReadonlyArray<string>>(stack);
281
+ const exitingPanelClearTimeoutRef = React.useRef<ReturnType<typeof setTimeout> | null>(null);
282
+ const [exitingPanelState, setExitingPanelState] = React.useState<string | null>(null);
278
283
 
279
- // Detect when we navigate back and need to animate out
280
- React.useLayoutEffect(() => {
281
- const prevDepth = prevDepthRef.current;
282
- const prevStack = prevStackRef.current;
284
+ // Compute exiting panel ID synchronously during render
285
+ // This ensures the panel stays in the render tree even on the navigation change render
286
+ const prevDepth = prevDepthRef.current;
287
+ const prevStack = prevStackRef.current;
288
+ const isNavigatingBack = depth < prevDepth;
289
+ const computedExitingId = isNavigatingBack ? (prevStack[prevDepth] ?? null) : null;
290
+
291
+ // Use computed value if navigating back, otherwise use state (for timeout-based cleanup)
292
+ const exitingPanelId = computedExitingId ?? exitingPanelState;
283
293
 
294
+ // Update refs and state in effect
295
+ React.useLayoutEffect(() => {
284
296
  // Update refs
285
297
  prevDepthRef.current = depth;
286
298
  prevStackRef.current = stack;
287
299
 
288
- // Check if we went back (depth decreased)
289
- if (depth < prevDepth) {
290
- // The panel at prevDepth is exiting
291
- const exitingId = prevStack[prevDepth];
292
- if (exitingId != null) {
293
- setExitingPanelId(exitingId);
294
-
295
- // Clear exiting panel after animation completes
296
- const timeoutId = setTimeout(() => {
297
- setExitingPanelId(null);
298
- }, THEME_DURATIONS[selectedTheme]);
300
+ // If we just started navigating back, update state and schedule cleanup
301
+ if (computedExitingId != null) {
302
+ setExitingPanelState(computedExitingId);
299
303
 
300
- return () => clearTimeout(timeoutId);
304
+ // Clear any existing timeout
305
+ if (exitingPanelClearTimeoutRef.current != null) {
306
+ clearTimeout(exitingPanelClearTimeoutRef.current);
301
307
  }
308
+
309
+ // Clear exiting panel after animation completes
310
+ exitingPanelClearTimeoutRef.current = setTimeout(() => {
311
+ setExitingPanelState(null);
312
+ exitingPanelClearTimeoutRef.current = null;
313
+ }, THEME_DURATIONS[selectedTheme]);
302
314
  }
303
- }, [depth, stack, selectedTheme]);
315
+
316
+ return () => {
317
+ if (exitingPanelClearTimeoutRef.current != null) {
318
+ clearTimeout(exitingPanelClearTimeoutRef.current);
319
+ }
320
+ };
321
+ }, [depth, stack, selectedTheme, computedExitingId]);
304
322
 
305
323
  const visiblePanelIds = React.useMemo(() => {
306
324
  const ids = [stack[depth]]; // Active panel
@@ -327,7 +345,10 @@ export const StackTablet: React.FC<StackTabletProps> = ({ theme = "ios" }) => {
327
345
  return null;
328
346
  }
329
347
 
330
- const isExiting = panelId === exitingPanelId;
348
+ // Panel is only "exiting" if it matches exitingPanelId AND is not in current stack
349
+ // This prevents the bug where a re-pushed panel is incorrectly treated as exiting
350
+ const isInCurrentStack = stack.includes(panelId);
351
+ const isExiting = panelId === exitingPanelId && !isInCurrentStack;
331
352
  // For exiting panels, use depth + 1 since they were previously at the active position
332
353
  const panelDepth = isExiting ? depth + 1 : stack.indexOf(panelId);
333
354
  const isActive = panelDepth === depth && !isExiting;
@@ -339,7 +360,7 @@ export const StackTablet: React.FC<StackTabletProps> = ({ theme = "ios" }) => {
339
360
  depth={panelDepth}
340
361
  navigationDepth={depth}
341
362
  isActive={isActive}
342
- inputState={inputState}
363
+ operationState={toContinuousOperationState(inputState)}
343
364
  containerSize={containerSize}
344
365
  animateOnMount={true}
345
366
  animationDuration={THEME_DURATIONS[selectedTheme]}
@@ -27,6 +27,13 @@ import PL_Overlays from "./pages/PanelLayout/draggable-overlays";
27
27
  import DR_Basics from "./pages/Drawer/basics";
28
28
  import DR_Menu from "./pages/Drawer/menu";
29
29
  import DR_Animations from "./pages/Drawer/animations";
30
+ import DR_Swipe from "./pages/Drawer/swipe";
31
+
32
+ import DL_Modal from "./pages/Dialog/modal";
33
+ import DL_Alerts from "./pages/Dialog/alerts";
34
+ import DL_CustomAlert from "./pages/Dialog/custom-alert";
35
+ import DL_Swipe from "./pages/Dialog/swipe";
36
+ import DL_Card from "./pages/Dialog/card";
30
37
 
31
38
  import PS_Preview from "./pages/PanelSystem/preview";
32
39
  import PS_Tabbar from "./pages/PanelSystem/tabbar";
@@ -46,7 +53,7 @@ import ST_Tablet from "./pages/Stack/tablet";
46
53
 
47
54
  import SH_Basics from "./pages/StickyHeader/basics";
48
55
 
49
- import { FiGrid, FiLayers, FiColumns, FiMaximize2, FiBox, FiCpu, FiSmartphone, FiSidebar, FiNavigation, FiImage } from "react-icons/fi";
56
+ import { FiGrid, FiLayers, FiColumns, FiMaximize2, FiBox, FiCpu, FiSmartphone, FiSidebar, FiNavigation, FiImage, FiMessageSquare } from "react-icons/fi";
50
57
 
51
58
  export type DemoPage = {
52
59
  id: string;
@@ -85,6 +92,20 @@ export const demoCategories: DemoCategory[] = [
85
92
  { id: "basics", label: "Basics", path: "basics", element: <DR_Basics /> },
86
93
  { id: "menu", label: "Menu", path: "menu", element: <DR_Menu /> },
87
94
  { id: "animations", label: "Animations", path: "animations", element: <DR_Animations /> },
95
+ { id: "swipe", label: "Swipe Gestures", path: "swipe", element: <DR_Swipe /> },
96
+ ],
97
+ },
98
+ {
99
+ id: "dialog",
100
+ label: "Dialog",
101
+ icon: <FiMessageSquare />,
102
+ base: "/components/dialog",
103
+ pages: [
104
+ { id: "modal", label: "Modal", path: "modal", element: <DL_Modal /> },
105
+ { id: "alerts", label: "Alert / Confirm / Prompt", path: "alerts", element: <DL_Alerts /> },
106
+ { id: "custom-alert", label: "Custom Alert Component", path: "custom-alert", element: <DL_CustomAlert /> },
107
+ { id: "swipe", label: "Swipe Dismiss", path: "swipe", element: <DL_Swipe /> },
108
+ { id: "card", label: "Card Expansion", path: "card", element: <DL_Card /> },
88
109
  ],
89
110
  },
90
111
  {
@@ -0,0 +1,85 @@
1
+ /**
2
+ * @file Dialog entry point - Modal and alert/confirm/prompt dialogs
3
+ * @packageDocumentation
4
+ *
5
+ * This is a subpath export entry point for `react-panel-layout/dialog`.
6
+ *
7
+ * ## Overview
8
+ * Dialog provides modal dialogs and imperative alert/confirm/prompt APIs
9
+ * using native HTML dialog element for proper accessibility and top-layer rendering.
10
+ *
11
+ * ## Installation
12
+ * ```ts
13
+ * import { Modal, useDialog, DialogContainer } from "react-panel-layout/dialog";
14
+ * ```
15
+ *
16
+ * ## Modal Usage
17
+ * ```tsx
18
+ * const [isOpen, setIsOpen] = useState(false);
19
+ *
20
+ * <Modal
21
+ * visible={isOpen}
22
+ * onClose={() => setIsOpen(false)}
23
+ * header={{ title: "Settings" }}
24
+ * >
25
+ * <form>
26
+ * <input type="text" placeholder="Name" />
27
+ * <button type="submit">Save</button>
28
+ * </form>
29
+ * </Modal>
30
+ * ```
31
+ *
32
+ * ## useDialog Hook Usage
33
+ * ```tsx
34
+ * function MyComponent() {
35
+ * const { alert, confirm, prompt, Outlet } = useDialog();
36
+ *
37
+ * const handleClick = async () => {
38
+ * await alert("Hello!");
39
+ *
40
+ * const confirmed = await confirm({
41
+ * message: "Are you sure?",
42
+ * confirmLabel: "Yes",
43
+ * cancelLabel: "No",
44
+ * });
45
+ *
46
+ * if (confirmed) {
47
+ * const name = await prompt({
48
+ * message: "Enter your name:",
49
+ * defaultValue: "Anonymous",
50
+ * });
51
+ * console.log("Name:", name);
52
+ * }
53
+ * };
54
+ *
55
+ * return (
56
+ * <>
57
+ * <button onClick={handleClick}>Show dialogs</button>
58
+ * <Outlet />
59
+ * </>
60
+ * );
61
+ * }
62
+ * ```
63
+ */
64
+
65
+ // Components
66
+ export { Modal } from "../modules/dialog/Modal.js";
67
+ export { DialogContainer } from "../modules/dialog/DialogContainer.js";
68
+ export { AlertDialog } from "../modules/dialog/AlertDialog.js";
69
+
70
+ // Hooks
71
+ export { useDialog } from "../modules/dialog/useDialog.js";
72
+ export { useDialogContainer } from "../modules/dialog/useDialogContainer.js";
73
+
74
+ // Types
75
+ export type {
76
+ ModalProps,
77
+ ModalHeader,
78
+ DialogContainerProps,
79
+ DialogTransitionMode,
80
+ AlertOptions,
81
+ ConfirmOptions,
82
+ PromptOptions,
83
+ AlertDialogProps,
84
+ UseDialogReturn,
85
+ } from "../modules/dialog/types.js";
@@ -1,158 +1,162 @@
1
1
  /**
2
2
  * @file Tests for gesture simulator utilities.
3
3
  */
4
- /* eslint-disable no-restricted-globals, no-restricted-properties -- test utilities need vi for timing and mocks */
5
4
  import {
6
5
  createGestureSimulator,
7
6
  createMockContainer,
8
7
  createMockContainerRef,
9
8
  } from "./createGestureSimulator.js";
10
9
 
11
- describe("createGestureSimulator", () => {
12
- beforeEach(() => {
13
- vi.useFakeTimers();
14
- });
10
+ type EventTracker = {
11
+ calls: ReadonlyArray<PointerEvent>;
12
+ handler: (event: Event) => void;
13
+ };
15
14
 
16
- afterEach(() => {
17
- vi.useRealTimers();
18
- });
15
+ const createEventTracker = (): EventTracker => {
16
+ const calls: PointerEvent[] = [];
17
+ const handler = (event: Event): void => {
18
+ calls.push(event as PointerEvent);
19
+ };
20
+ return { calls, handler };
21
+ };
19
22
 
23
+ describe("createGestureSimulator", () => {
20
24
  describe("pointerDown", () => {
21
25
  it("dispatches pointerdown event", () => {
22
26
  const simulator = createGestureSimulator();
23
- const handler = vi.fn();
24
- document.addEventListener("pointerdown", handler);
27
+ const tracker = createEventTracker();
28
+ document.addEventListener("pointerdown", tracker.handler);
25
29
 
26
30
  simulator.pointerDown(100, 200);
27
31
 
28
- expect(handler).toHaveBeenCalledTimes(1);
29
- const event = handler.mock.calls[0][0] as PointerEvent;
32
+ expect(tracker.calls).toHaveLength(1);
33
+ const event = tracker.calls[0] as PointerEvent;
30
34
  expect(event.clientX).toBe(100);
31
35
  expect(event.clientY).toBe(200);
32
36
  expect(event.pointerType).toBe("touch");
33
37
  expect(event.pointerId).toBe(1);
34
38
 
35
- document.removeEventListener("pointerdown", handler);
39
+ document.removeEventListener("pointerdown", tracker.handler);
36
40
  });
37
41
  });
38
42
 
39
43
  describe("pointerMove", () => {
40
44
  it("dispatches pointermove event after pointerdown", () => {
41
45
  const simulator = createGestureSimulator();
42
- const handler = vi.fn();
43
- document.addEventListener("pointermove", handler);
46
+ const tracker = createEventTracker();
47
+ document.addEventListener("pointermove", tracker.handler);
44
48
 
45
49
  simulator.pointerDown(100, 100);
46
50
  simulator.pointerMove(150, 120);
47
51
 
48
- expect(handler).toHaveBeenCalledTimes(1);
49
- const event = handler.mock.calls[0][0] as PointerEvent;
52
+ expect(tracker.calls).toHaveLength(1);
53
+ const event = tracker.calls[0] as PointerEvent;
50
54
  expect(event.clientX).toBe(150);
51
55
  expect(event.clientY).toBe(120);
52
56
 
53
- document.removeEventListener("pointermove", handler);
57
+ document.removeEventListener("pointermove", tracker.handler);
54
58
  });
55
59
 
56
60
  it("does not dispatch pointermove if pointer is not down", () => {
57
61
  const simulator = createGestureSimulator();
58
- const handler = vi.fn();
59
- document.addEventListener("pointermove", handler);
62
+ const tracker = createEventTracker();
63
+ document.addEventListener("pointermove", tracker.handler);
60
64
 
61
65
  simulator.pointerMove(150, 120);
62
66
 
63
- expect(handler).not.toHaveBeenCalled();
67
+ expect(tracker.calls).toHaveLength(0);
64
68
 
65
- document.removeEventListener("pointermove", handler);
69
+ document.removeEventListener("pointermove", tracker.handler);
66
70
  });
67
71
  });
68
72
 
69
73
  describe("pointerUp", () => {
70
74
  it("dispatches pointerup event", () => {
71
75
  const simulator = createGestureSimulator();
72
- const handler = vi.fn();
73
- document.addEventListener("pointerup", handler);
76
+ const tracker = createEventTracker();
77
+ document.addEventListener("pointerup", tracker.handler);
74
78
 
75
79
  simulator.pointerDown(100, 100);
76
80
  simulator.pointerUp();
77
81
 
78
- expect(handler).toHaveBeenCalledTimes(1);
82
+ expect(tracker.calls).toHaveLength(1);
79
83
 
80
- document.removeEventListener("pointerup", handler);
84
+ document.removeEventListener("pointerup", tracker.handler);
81
85
  });
82
86
 
83
87
  it("does not dispatch pointerup if pointer is not down", () => {
84
88
  const simulator = createGestureSimulator();
85
- const handler = vi.fn();
86
- document.addEventListener("pointerup", handler);
89
+ const tracker = createEventTracker();
90
+ document.addEventListener("pointerup", tracker.handler);
87
91
 
88
92
  simulator.pointerUp();
89
93
 
90
- expect(handler).not.toHaveBeenCalled();
94
+ expect(tracker.calls).toHaveLength(0);
91
95
 
92
- document.removeEventListener("pointerup", handler);
96
+ document.removeEventListener("pointerup", tracker.handler);
93
97
  });
94
98
  });
95
99
 
96
100
  describe("swipe", () => {
97
101
  it("dispatches complete swipe sequence", () => {
98
102
  const simulator = createGestureSimulator();
99
- const downHandler = vi.fn();
100
- const moveHandler = vi.fn();
101
- const upHandler = vi.fn();
103
+ const downTracker = createEventTracker();
104
+ const moveTracker = createEventTracker();
105
+ const upTracker = createEventTracker();
102
106
 
103
- document.addEventListener("pointerdown", downHandler);
104
- document.addEventListener("pointermove", moveHandler);
105
- document.addEventListener("pointerup", upHandler);
107
+ document.addEventListener("pointerdown", downTracker.handler);
108
+ document.addEventListener("pointermove", moveTracker.handler);
109
+ document.addEventListener("pointerup", upTracker.handler);
106
110
 
107
111
  simulator.swipe({ x: 100, y: 100 }, { x: 200, y: 100 }, 5);
108
112
 
109
- expect(downHandler).toHaveBeenCalledTimes(1);
110
- expect(moveHandler).toHaveBeenCalledTimes(5); // 5 steps
111
- expect(upHandler).toHaveBeenCalledTimes(1);
113
+ expect(downTracker.calls).toHaveLength(1);
114
+ expect(moveTracker.calls).toHaveLength(5); // 5 steps
115
+ expect(upTracker.calls).toHaveLength(1);
112
116
 
113
117
  // Check intermediate positions
114
- const lastMove = moveHandler.mock.calls[4][0] as PointerEvent;
118
+ const lastMove = moveTracker.calls[4] as PointerEvent;
115
119
  expect(lastMove.clientX).toBe(200);
116
120
  expect(lastMove.clientY).toBe(100);
117
121
 
118
- document.removeEventListener("pointerdown", downHandler);
119
- document.removeEventListener("pointermove", moveHandler);
120
- document.removeEventListener("pointerup", upHandler);
122
+ document.removeEventListener("pointerdown", downTracker.handler);
123
+ document.removeEventListener("pointermove", moveTracker.handler);
124
+ document.removeEventListener("pointerup", upTracker.handler);
121
125
  });
122
126
  });
123
127
 
124
128
  describe("edgeSwipe", () => {
125
129
  it("simulates left edge swipe", () => {
126
130
  const simulator = createGestureSimulator();
127
- const downHandler = vi.fn();
128
- const upHandler = vi.fn();
131
+ const downTracker = createEventTracker();
132
+ const upTracker = createEventTracker();
129
133
 
130
- document.addEventListener("pointerdown", downHandler);
131
- document.addEventListener("pointerup", upHandler);
134
+ document.addEventListener("pointerdown", downTracker.handler);
135
+ document.addEventListener("pointerup", upTracker.handler);
132
136
 
133
137
  simulator.edgeSwipe("left", 100, 300);
134
138
 
135
- expect(downHandler).toHaveBeenCalledTimes(1);
136
- const downEvent = downHandler.mock.calls[0][0] as PointerEvent;
139
+ expect(downTracker.calls).toHaveLength(1);
140
+ const downEvent = downTracker.calls[0] as PointerEvent;
137
141
  expect(downEvent.clientX).toBe(10); // Edge offset
138
- expect(upHandler).toHaveBeenCalledTimes(1);
142
+ expect(upTracker.calls).toHaveLength(1);
139
143
 
140
- document.removeEventListener("pointerdown", downHandler);
141
- document.removeEventListener("pointerup", upHandler);
144
+ document.removeEventListener("pointerdown", downTracker.handler);
145
+ document.removeEventListener("pointerup", upTracker.handler);
142
146
  });
143
147
 
144
148
  it("simulates right edge swipe", () => {
145
149
  const simulator = createGestureSimulator();
146
- const downHandler = vi.fn();
150
+ const downTracker = createEventTracker();
147
151
 
148
- document.addEventListener("pointerdown", downHandler);
152
+ document.addEventListener("pointerdown", downTracker.handler);
149
153
 
150
154
  simulator.edgeSwipe("right", 100, 300);
151
155
 
152
- const downEvent = downHandler.mock.calls[0][0] as PointerEvent;
156
+ const downEvent = downTracker.calls[0] as PointerEvent;
153
157
  expect(downEvent.clientX).toBe(290); // containerSize - edgeOffset
154
158
 
155
- document.removeEventListener("pointerdown", downHandler);
159
+ document.removeEventListener("pointerdown", downTracker.handler);
156
160
  });
157
161
  });
158
162
 
@@ -172,28 +176,28 @@ describe("createGestureSimulator", () => {
172
176
  describe("options", () => {
173
177
  it("respects custom pointer type", () => {
174
178
  const simulator = createGestureSimulator({ pointerType: "mouse" });
175
- const handler = vi.fn();
176
- document.addEventListener("pointerdown", handler);
179
+ const tracker = createEventTracker();
180
+ document.addEventListener("pointerdown", tracker.handler);
177
181
 
178
182
  simulator.pointerDown(100, 100);
179
183
 
180
- const event = handler.mock.calls[0][0] as PointerEvent;
184
+ const event = tracker.calls[0] as PointerEvent;
181
185
  expect(event.pointerType).toBe("mouse");
182
186
 
183
- document.removeEventListener("pointerdown", handler);
187
+ document.removeEventListener("pointerdown", tracker.handler);
184
188
  });
185
189
 
186
190
  it("respects custom pointer ID", () => {
187
191
  const simulator = createGestureSimulator({ pointerId: 5 });
188
- const handler = vi.fn();
189
- document.addEventListener("pointerdown", handler);
192
+ const tracker = createEventTracker();
193
+ document.addEventListener("pointerdown", tracker.handler);
190
194
 
191
195
  simulator.pointerDown(100, 100);
192
196
 
193
- const event = handler.mock.calls[0][0] as PointerEvent;
197
+ const event = tracker.calls[0] as PointerEvent;
194
198
  expect(event.pointerId).toBe(5);
195
199
 
196
- document.removeEventListener("pointerdown", handler);
200
+ document.removeEventListener("pointerdown", tracker.handler);
197
201
  });
198
202
  });
199
203
  });