react-panel-layout 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (258) 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-Bw2djgpz.js +1542 -0
  4. package/dist/FloatingWindow-Bw2djgpz.js.map +1 -0
  5. package/dist/FloatingWindow-Cvyokf0m.cjs +2 -0
  6. package/dist/FloatingWindow-Cvyokf0m.cjs.map +1 -0
  7. package/dist/GridLayout-B4aCqSyd.js +947 -0
  8. package/dist/{GridLayout-BltqeCPK.js.map → GridLayout-B4aCqSyd.js.map} +1 -1
  9. package/dist/GridLayout-DNOClFzz.cjs +2 -0
  10. package/dist/{GridLayout-B4VRsC0r.cjs.map → GridLayout-DNOClFzz.cjs.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-B8Igvnb2.cjs +3 -0
  16. package/dist/PanelSystem-B8Igvnb2.cjs.map +1 -0
  17. package/dist/{PanelSystem-Dr1TBhxM.js → PanelSystem-DDUSFjXD.js} +209 -248
  18. package/dist/PanelSystem-DDUSFjXD.js.map +1 -0
  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 +4 -1
  29. package/dist/components/window/DrawerLayers.d.ts +1 -1
  30. package/dist/components/window/DrawerRevealContext.d.ts +61 -0
  31. package/dist/components/window/drawerRevealAnimationUtils.d.ts +212 -0
  32. package/dist/components/window/drawerStyles.d.ts +74 -0
  33. package/dist/components/window/drawerSwipeConfig.d.ts +29 -0
  34. package/dist/components/window/useDrawerSwipeTransform.d.ts +29 -0
  35. package/dist/components/window/useDrawerTransform.d.ts +68 -0
  36. package/dist/components/window/useRevealDrawerTransform.d.ts +56 -0
  37. package/dist/config.cjs +1 -1
  38. package/dist/config.cjs.map +1 -1
  39. package/dist/config.js +9 -8
  40. package/dist/config.js.map +1 -1
  41. package/dist/constants/styles.d.ts +17 -0
  42. package/dist/dialog/index.d.ts +69 -0
  43. package/dist/floating.js +1 -1
  44. package/dist/grid.cjs +1 -1
  45. package/dist/grid.js +2 -2
  46. package/dist/hooks/gesture/testing/createGestureSimulator.d.ts +7 -0
  47. package/dist/hooks/gesture/types.d.ts +48 -5
  48. package/dist/hooks/gesture/utils.d.ts +19 -0
  49. package/dist/hooks/useAnimationFrame.d.ts +2 -0
  50. package/dist/hooks/useOperationContinuity.d.ts +64 -0
  51. package/dist/hooks/useResizeObserver.d.ts +33 -1
  52. package/dist/hooks/useSharedElementTransition.d.ts +112 -0
  53. package/dist/hooks/useSwipeContentTransform.d.ts +9 -2
  54. package/dist/index.cjs +1 -1
  55. package/dist/index.js +7 -7
  56. package/dist/modules/dialog/AlertDialog.d.ts +9 -0
  57. package/dist/modules/dialog/DialogContainer.d.ts +37 -0
  58. package/dist/modules/dialog/Modal.d.ts +26 -0
  59. package/dist/modules/dialog/SwipeDialogContainer.d.ts +16 -0
  60. package/dist/modules/dialog/dialogAnimationUtils.d.ts +113 -0
  61. package/dist/modules/dialog/types.d.ts +183 -0
  62. package/dist/modules/dialog/useDialog.d.ts +39 -0
  63. package/dist/modules/dialog/useDialogContainer.d.ts +47 -0
  64. package/dist/modules/dialog/useDialogSwipeInput.d.ts +70 -0
  65. package/dist/modules/dialog/useDialogTransform.d.ts +82 -0
  66. package/dist/modules/drawer/drawerStateMachine.d.ts +168 -0
  67. package/dist/modules/drawer/revealDrawerConstants.d.ts +33 -0
  68. package/dist/modules/drawer/revealDrawerStateMachine.d.ts +146 -0
  69. package/dist/modules/drawer/strategies/index.d.ts +8 -0
  70. package/dist/modules/drawer/strategies/overlayStrategy.d.ts +12 -0
  71. package/dist/modules/drawer/strategies/revealStrategy.d.ts +12 -0
  72. package/dist/modules/drawer/strategies/types.d.ts +116 -0
  73. package/dist/modules/drawer/types.d.ts +74 -0
  74. package/dist/modules/drawer/useDrawerSwipeInput.d.ts +24 -0
  75. package/dist/modules/pivot/SwipePivotTabBar.d.ts +3 -0
  76. package/dist/modules/stack/SwipeStackContent.d.ts +6 -3
  77. package/dist/modules/stack/SwipeStackOutlet.d.ts +4 -4
  78. package/dist/modules/stack/computeSwipeStackTransform.d.ts +1 -1
  79. package/dist/panels.cjs +1 -1
  80. package/dist/panels.js +1 -1
  81. package/dist/pivot.cjs +1 -1
  82. package/dist/pivot.js +1 -1
  83. package/dist/resizer.cjs +1 -1
  84. package/dist/resizer.js +2 -2
  85. package/dist/stack.cjs +1 -1
  86. package/dist/stack.cjs.map +1 -1
  87. package/dist/stack.js +480 -780
  88. package/dist/stack.js.map +1 -1
  89. package/dist/sticky-header/calculateStickyMetrics.d.ts +28 -0
  90. package/dist/sticky-header.cjs +1 -1
  91. package/dist/sticky-header.cjs.map +1 -1
  92. package/dist/sticky-header.js +59 -51
  93. package/dist/sticky-header.js.map +1 -1
  94. package/dist/{styles-DPPuJ0sf.js → styles-NkjuMOVS.js} +13 -13
  95. package/dist/{styles-DPPuJ0sf.js.map → styles-NkjuMOVS.js.map} +1 -1
  96. package/dist/styles-qf6ptVLD.cjs.map +1 -1
  97. package/dist/types.d.ts +30 -0
  98. package/dist/useAnimationFrame-BZ6D2lMq.cjs +2 -0
  99. package/dist/useAnimationFrame-BZ6D2lMq.cjs.map +1 -0
  100. package/dist/useAnimationFrame-Bg4e-H8O.js +394 -0
  101. package/dist/useAnimationFrame-Bg4e-H8O.js.map +1 -0
  102. package/dist/useDocumentPointerEvents-DXxw3qWj.js +54 -0
  103. package/dist/useDocumentPointerEvents-DXxw3qWj.js.map +1 -0
  104. package/dist/useDocumentPointerEvents-DxDSOtip.cjs +2 -0
  105. package/dist/useDocumentPointerEvents-DxDSOtip.cjs.map +1 -0
  106. package/dist/window/index.d.ts +2 -0
  107. package/dist/window.cjs +1 -1
  108. package/dist/window.cjs.map +1 -1
  109. package/dist/window.js +114 -103
  110. package/dist/window.js.map +1 -1
  111. package/package.json +6 -1
  112. package/src/components/gesture/SwipeSafeZone.tsx +70 -0
  113. package/src/components/grid/GridLayout.tsx +110 -38
  114. package/src/components/window/Drawer.tsx +353 -162
  115. package/src/components/window/DrawerLayers.tsx +54 -11
  116. package/src/components/window/DrawerRevealContext.spec.ts +20 -0
  117. package/src/components/window/DrawerRevealContext.tsx +99 -0
  118. package/src/components/window/drawerRevealAnimationUtils.spec.ts +375 -0
  119. package/src/components/window/drawerRevealAnimationUtils.ts +415 -0
  120. package/src/components/window/drawerStyles.spec.ts +302 -0
  121. package/src/components/window/drawerStyles.ts +252 -0
  122. package/src/components/window/drawerSwipeConfig.spec.ts +131 -0
  123. package/src/components/window/drawerSwipeConfig.ts +112 -0
  124. package/src/components/window/useDrawerSwipeTransform.ts +67 -0
  125. package/src/components/window/useDrawerTransform.ts +505 -0
  126. package/src/components/window/useRevealDrawerTransform.spec.ts +1936 -0
  127. package/src/components/window/useRevealDrawerTransform.ts +105 -0
  128. package/src/constants/styles.ts +19 -0
  129. package/src/demo/components/FullscreenDemoPage.tsx +47 -0
  130. package/src/demo/fullscreenRoutes.tsx +32 -0
  131. package/src/demo/index.tsx +5 -0
  132. package/src/demo/pages/Dialog/alerts/index.tsx +22 -0
  133. package/src/demo/pages/Dialog/card/index.tsx +22 -0
  134. package/src/demo/pages/Dialog/components/AlertDialogDemo.tsx +124 -0
  135. package/src/demo/pages/Dialog/components/CardExpandDemo.module.css +243 -0
  136. package/src/demo/pages/Dialog/components/CardExpandDemo.tsx +219 -0
  137. package/src/demo/pages/Dialog/components/CustomAlertDialogDemo.tsx +219 -0
  138. package/src/demo/pages/Dialog/components/DialogDemos.module.css +77 -0
  139. package/src/demo/pages/Dialog/components/ModalBasics.tsx +45 -0
  140. package/src/demo/pages/Dialog/components/SwipeDialogDemo.module.css +77 -0
  141. package/src/demo/pages/Dialog/components/SwipeDialogDemo.tsx +181 -0
  142. package/src/demo/pages/Dialog/custom-alert/index.tsx +22 -0
  143. package/src/demo/pages/Dialog/modal/index.tsx +17 -0
  144. package/src/demo/pages/Dialog/swipe/index.tsx +22 -0
  145. package/src/demo/pages/Drawer/components/DrawerBasics.module.css +6 -1
  146. package/src/demo/pages/Drawer/components/DrawerBasics.tsx +14 -4
  147. package/src/demo/pages/Drawer/components/DrawerReveal.module.css +157 -0
  148. package/src/demo/pages/Drawer/components/DrawerReveal.tsx +128 -0
  149. package/src/demo/pages/Drawer/components/DrawerSwipe.module.css +316 -0
  150. package/src/demo/pages/Drawer/components/DrawerSwipe.tsx +178 -0
  151. package/src/demo/pages/Drawer/reveal/index.tsx +17 -0
  152. package/src/demo/pages/Drawer/reveal-fullscreen/index.tsx +135 -0
  153. package/src/demo/pages/Drawer/reveal-fullscreen/styles.module.css +233 -0
  154. package/src/demo/pages/Drawer/swipe/index.tsx +17 -0
  155. package/src/demo/pages/Pivot/components/SwipeTabsPivot.tsx +54 -23
  156. package/src/demo/pages/Pivot/swipe-debug/index.tsx +1 -1
  157. package/src/demo/pages/Stack/components/StackBasics.spec.tsx +156 -0
  158. package/src/demo/pages/Stack/components/StackBasics.tsx +179 -95
  159. package/src/demo/pages/Stack/components/StackTablet.spec.tsx +110 -0
  160. package/src/demo/pages/Stack/components/StackTablet.tsx +42 -21
  161. package/src/demo/routes.tsx +24 -1
  162. package/src/dialog/index.ts +85 -0
  163. package/src/hooks/gesture/testing/createGestureSimulator.spec.ts +68 -64
  164. package/src/hooks/gesture/testing/createGestureSimulator.ts +113 -37
  165. package/src/hooks/gesture/types.ts +83 -6
  166. package/src/hooks/gesture/useEdgeSwipeInput.spec.ts +22 -14
  167. package/src/hooks/gesture/useNativeGestureGuard.spec.ts +99 -31
  168. package/src/hooks/gesture/useNativeGestureGuard.ts +3 -1
  169. package/src/hooks/gesture/utils.ts +102 -0
  170. package/src/hooks/useAnimatedVisibility.spec.ts +44 -24
  171. package/src/hooks/useAnimatedVisibility.ts +28 -2
  172. package/src/hooks/useAnimationFrame.ts +8 -0
  173. package/src/hooks/useOperationContinuity.spec.ts +394 -0
  174. package/src/hooks/useOperationContinuity.ts +135 -0
  175. package/src/hooks/useResizeObserver.spec.tsx +277 -0
  176. package/src/hooks/useResizeObserver.tsx +108 -39
  177. package/src/hooks/useScrollContainer.ts +4 -10
  178. package/src/hooks/useSharedElementTransition.ts +354 -0
  179. package/src/hooks/useSwipeContentTransform.spec.ts +18 -18
  180. package/src/hooks/useSwipeContentTransform.ts +166 -28
  181. package/src/modules/dialog/AlertDialog.spec.tsx +387 -0
  182. package/src/modules/dialog/AlertDialog.tsx +221 -0
  183. package/src/modules/dialog/DialogContainer.spec.tsx +228 -0
  184. package/src/modules/dialog/DialogContainer.tsx +188 -0
  185. package/src/modules/dialog/Modal.spec.tsx +220 -0
  186. package/src/modules/dialog/Modal.tsx +182 -0
  187. package/src/modules/dialog/SwipeDialogContainer.tsx +208 -0
  188. package/src/modules/dialog/dialogAnimationUtils.spec.ts +252 -0
  189. package/src/modules/dialog/dialogAnimationUtils.ts +297 -0
  190. package/src/modules/dialog/types.ts +186 -0
  191. package/src/modules/dialog/useDialog.spec.tsx +447 -0
  192. package/src/modules/dialog/useDialog.ts +214 -0
  193. package/src/modules/dialog/useDialogContainer.spec.ts +339 -0
  194. package/src/modules/dialog/useDialogContainer.ts +150 -0
  195. package/src/modules/dialog/useDialogSwipeInput.spec.ts +178 -0
  196. package/src/modules/dialog/useDialogSwipeInput.ts +350 -0
  197. package/src/modules/dialog/useDialogTransform.spec.ts +403 -0
  198. package/src/modules/dialog/useDialogTransform.ts +407 -0
  199. package/src/modules/drawer/drawerStateMachine.ts +500 -0
  200. package/src/modules/drawer/revealDrawerConstants.ts +38 -0
  201. package/src/modules/drawer/revealDrawerStateMachine.spec.ts +558 -0
  202. package/src/modules/drawer/revealDrawerStateMachine.ts +197 -0
  203. package/src/modules/drawer/strategies/index.ts +9 -0
  204. package/src/modules/drawer/strategies/overlayStrategy.ts +133 -0
  205. package/src/modules/drawer/strategies/revealStrategy.ts +111 -0
  206. package/src/modules/drawer/strategies/types.ts +160 -0
  207. package/src/modules/drawer/types.ts +102 -0
  208. package/src/modules/drawer/useDrawerSwipeInput.spec.ts +566 -0
  209. package/src/modules/drawer/useDrawerSwipeInput.ts +402 -0
  210. package/src/modules/panels/rendering/ContentRegistry.spec.tsx +21 -14
  211. package/src/modules/pivot/SwipePivotContent.position.spec.tsx +12 -8
  212. package/src/modules/pivot/SwipePivotContent.spec.tsx +66 -25
  213. package/src/modules/pivot/SwipePivotContent.tsx +2 -2
  214. package/src/modules/pivot/SwipePivotTabBar.spec.tsx +85 -68
  215. package/src/modules/pivot/SwipePivotTabBar.tsx +75 -15
  216. package/src/modules/pivot/scaleInputState.spec.ts +11 -2
  217. package/src/modules/pivot/usePivot.spec.ts +17 -3
  218. package/src/modules/pivot/usePivotSwipeInput.spec.ts +182 -123
  219. package/src/modules/stack/SwipeStackContent.spec.tsx +387 -100
  220. package/src/modules/stack/SwipeStackContent.tsx +43 -33
  221. package/src/modules/stack/SwipeStackOutlet.spec.tsx +14 -16
  222. package/src/modules/stack/SwipeStackOutlet.tsx +6 -6
  223. package/src/modules/stack/computeSwipeStackTransform.spec.ts +5 -5
  224. package/src/modules/stack/computeSwipeStackTransform.ts +3 -3
  225. package/src/modules/stack/swipeTransitionContinuity.spec.tsx +1133 -0
  226. package/src/modules/stack/useStackAnimationState.spec.ts +3 -1
  227. package/src/modules/stack/useStackAnimationState.ts +18 -13
  228. package/src/modules/stack/useStackNavigation.spec.ts +198 -3
  229. package/src/modules/stack/useStackNavigation.tsx +113 -56
  230. package/src/modules/stack/useStackSwipeInput.spec.ts +65 -32
  231. package/src/modules/stack/useStackSwipeInput.ts +1 -1
  232. package/src/sticky-header/StickyArea.tsx +29 -57
  233. package/src/sticky-header/calculateStickyMetrics.spec.ts +105 -0
  234. package/src/sticky-header/calculateStickyMetrics.ts +50 -0
  235. package/src/types.ts +33 -0
  236. package/src/window/index.ts +2 -0
  237. package/dist/FloatingWindow-BpdOpg_L.js +0 -400
  238. package/dist/FloatingWindow-BpdOpg_L.js.map +0 -1
  239. package/dist/FloatingWindow-TCDNY5gE.cjs +0 -2
  240. package/dist/FloatingWindow-TCDNY5gE.cjs.map +0 -1
  241. package/dist/GridLayout-B4VRsC0r.cjs +0 -2
  242. package/dist/GridLayout-BltqeCPK.js +0 -927
  243. package/dist/PanelSystem-Bs8bQwQF.cjs +0 -3
  244. package/dist/PanelSystem-Bs8bQwQF.cjs.map +0 -1
  245. package/dist/PanelSystem-Dr1TBhxM.js.map +0 -1
  246. package/dist/ResizeHandle-CScipO5l.cjs +0 -2
  247. package/dist/SwipePivotTabBar-BGO9X94m.js +0 -407
  248. package/dist/SwipePivotTabBar-BGO9X94m.js.map +0 -1
  249. package/dist/SwipePivotTabBar-BrQismcZ.cjs +0 -2
  250. package/dist/SwipePivotTabBar-BrQismcZ.cjs.map +0 -1
  251. package/dist/useDocumentPointerEvents-CKdhGXd0.js +0 -46
  252. package/dist/useDocumentPointerEvents-CKdhGXd0.js.map +0 -1
  253. package/dist/useDocumentPointerEvents-ChqrKXDk.cjs +0 -2
  254. package/dist/useDocumentPointerEvents-ChqrKXDk.cjs.map +0 -1
  255. package/dist/useEffectEvent-Dp7HLCf0.js +0 -13
  256. package/dist/useEffectEvent-Dp7HLCf0.js.map +0 -1
  257. package/dist/useEffectEvent-huSsGUnl.cjs +0 -2
  258. package/dist/useEffectEvent-huSsGUnl.cjs.map +0 -1
@@ -0,0 +1,415 @@
1
+ /**
2
+ * @file Drawer reveal animation utilities
3
+ *
4
+ * Provides transform calculations for reveal-mode drawer animations.
5
+ * The reveal mode creates a "pull out" effect where:
6
+ * - Drawer slides from partially hidden (-30%) to fully visible (0%)
7
+ * - Content slides away to reveal the drawer behind it
8
+ */
9
+
10
+ import type * as React from "react";
11
+ import type { DrawerPlacement } from "./drawerStyles.js";
12
+
13
+ /**
14
+ * Percentage offset for drawer when closed (creates "pull out" effect).
15
+ */
16
+ export const DRAWER_CLOSED_OFFSET_PERCENT = 30;
17
+
18
+ /**
19
+ * Default animation duration in milliseconds.
20
+ */
21
+ export const DEFAULT_ANIMATION_DURATION = 300;
22
+
23
+ /**
24
+ * Placement configuration for transforms.
25
+ */
26
+ export type PlacementConfig = {
27
+ axis: "X" | "Y";
28
+ sign: 1 | -1;
29
+ };
30
+
31
+ /**
32
+ * Lookup table for placement configuration.
33
+ */
34
+ const PLACEMENT_CONFIG: Record<DrawerPlacement, PlacementConfig> = {
35
+ left: { axis: "X", sign: 1 },
36
+ right: { axis: "X", sign: -1 },
37
+ top: { axis: "Y", sign: 1 },
38
+ bottom: { axis: "Y", sign: -1 },
39
+ };
40
+
41
+ /**
42
+ * Get placement configuration for a drawer placement.
43
+ */
44
+ export function getPlacementConfig(placement: DrawerPlacement): PlacementConfig {
45
+ return PLACEMENT_CONFIG[placement];
46
+ }
47
+
48
+ /**
49
+ * Transform values for reveal drawer animation.
50
+ */
51
+ export type RevealDrawerTransform = {
52
+ /** Drawer offset in percentage */
53
+ drawerOffsetPercent: number;
54
+ /** Content offset in pixels */
55
+ contentOffsetPx: number;
56
+ };
57
+
58
+ /**
59
+ * Compute progress from swipe displacement.
60
+ * Returns 0 (closed) to 1 (open).
61
+ *
62
+ * @param displacement - Current swipe displacement in pixels
63
+ * @param drawerSize - Size of the drawer in pixels
64
+ * @param isOpening - Whether the gesture is opening the drawer
65
+ * @param isClosing - Whether the gesture is closing the drawer
66
+ * @returns Progress value between 0 and 1
67
+ */
68
+ export function computeSwipeProgress(
69
+ displacement: number,
70
+ drawerSize: number,
71
+ isOpening: boolean,
72
+ isClosing: boolean,
73
+ ): number {
74
+ if (drawerSize <= 0) {
75
+ return 0;
76
+ }
77
+
78
+ if (isOpening) {
79
+ return Math.min(1, Math.max(0, displacement / drawerSize));
80
+ }
81
+ if (isClosing) {
82
+ return Math.min(1, Math.max(0, 1 - displacement / drawerSize));
83
+ }
84
+ return 0;
85
+ }
86
+
87
+ /**
88
+ * Compute reveal drawer transforms from progress.
89
+ *
90
+ * @param progress - Animation progress (0 = closed, 1 = open)
91
+ * @param drawerSize - Size of the drawer in pixels
92
+ * @param placement - Drawer placement
93
+ * @returns Transform values for drawer and content
94
+ */
95
+ export function computeRevealTransform(
96
+ progress: number,
97
+ drawerSize: number,
98
+ placement: DrawerPlacement,
99
+ ): RevealDrawerTransform {
100
+ const { sign } = PLACEMENT_CONFIG[placement];
101
+
102
+ // Drawer moves from -30% (closed) to 0% (open)
103
+ // For left: -30% to 0%
104
+ // For right: +30% to 0%
105
+ const offsetPercent = DRAWER_CLOSED_OFFSET_PERCENT * (1 - progress);
106
+ const drawerOffsetPercent = sign > 0 ? -offsetPercent : offsetPercent;
107
+
108
+ // Content moves from 0px (closed) to drawerSize (open)
109
+ const contentOffsetPx = sign * drawerSize * progress;
110
+
111
+ return {
112
+ drawerOffsetPercent,
113
+ contentOffsetPx,
114
+ };
115
+ }
116
+
117
+ /**
118
+ * Build drawer transform string from progress.
119
+ *
120
+ * @param progress - Animation progress (0 = closed, 1 = open)
121
+ * @param placement - Drawer placement
122
+ * @returns CSS transform string
123
+ */
124
+ export function buildDrawerTransformString(
125
+ progress: number,
126
+ placement: DrawerPlacement,
127
+ ): string {
128
+ const { axis } = PLACEMENT_CONFIG[placement];
129
+ const transform = computeRevealTransform(progress, 0, placement);
130
+ return `translate${axis}(${transform.drawerOffsetPercent}%)`;
131
+ }
132
+
133
+ /**
134
+ * Build content transform string from progress.
135
+ *
136
+ * @param progress - Animation progress (0 = closed, 1 = open)
137
+ * @param drawerSize - Size of the drawer in pixels
138
+ * @param placement - Drawer placement
139
+ * @returns CSS transform string
140
+ */
141
+ export function buildContentTransformString(
142
+ progress: number,
143
+ drawerSize: number,
144
+ placement: DrawerPlacement,
145
+ ): string {
146
+ const { axis } = PLACEMENT_CONFIG[placement];
147
+ const transform = computeRevealTransform(progress, drawerSize, placement);
148
+ return `translate${axis}(${transform.contentOffsetPx}px)`;
149
+ }
150
+
151
+ /**
152
+ * Stacking context styles for content element.
153
+ * Applied during reveal animation to ensure proper z-index layering.
154
+ */
155
+ export type StackingContextStyles = {
156
+ position: string;
157
+ zIndex: string;
158
+ background: string;
159
+ };
160
+
161
+ /**
162
+ * Default stacking context styles.
163
+ */
164
+ export const STACKING_CONTEXT_STYLES: StackingContextStyles = {
165
+ position: "relative",
166
+ zIndex: "1",
167
+ background: "#fff",
168
+ };
169
+
170
+ /**
171
+ * Apply stacking context styles to an element.
172
+ */
173
+ export function applyStackingContext(element: HTMLElement): void {
174
+ element.style.position = STACKING_CONTEXT_STYLES.position;
175
+ element.style.zIndex = STACKING_CONTEXT_STYLES.zIndex;
176
+ element.style.background = STACKING_CONTEXT_STYLES.background;
177
+ }
178
+
179
+ /**
180
+ * Clear stacking context styles from an element.
181
+ */
182
+ export function clearStackingContext(element: HTMLElement): void {
183
+ element.style.position = "";
184
+ element.style.zIndex = "";
185
+ element.style.background = "";
186
+ }
187
+
188
+ /**
189
+ * Apply overflow hidden to body to prevent scrolling during drawer open.
190
+ * This prevents both horizontal and vertical scrolling of the body
191
+ * while the drawer is open or animating.
192
+ */
193
+ export function applyOverflowHidden(): void {
194
+ document.body.style.overflow = "hidden";
195
+ }
196
+
197
+ /**
198
+ * Clear overflow hidden from body.
199
+ */
200
+ export function clearOverflowHidden(): void {
201
+ document.body.style.overflow = "";
202
+ }
203
+
204
+ /**
205
+ * Get the content element to transform.
206
+ *
207
+ * @param inline - Whether drawer is inline mode
208
+ * @param gridRef - Grid container ref for inline mode
209
+ * @returns The content element or null
210
+ */
211
+ export function getContentElement(
212
+ inline: boolean,
213
+ gridRef?: React.RefObject<HTMLElement | null>,
214
+ ): HTMLElement | null {
215
+ if (inline) {
216
+ return gridRef?.current ?? null;
217
+ }
218
+ return document.getElementById("root") ?? (document.body.firstElementChild as HTMLElement | null);
219
+ }
220
+
221
+ // ============================================================================
222
+ // Pixel-based transform computation (for animation continuity)
223
+ // ============================================================================
224
+
225
+ /**
226
+ * Tracked position for drawer and content elements in pixels.
227
+ */
228
+ export type RevealPositionPx = {
229
+ /** Drawer offset in pixels */
230
+ drawerPx: number;
231
+ /** Content offset in pixels */
232
+ contentPx: number;
233
+ };
234
+
235
+ /**
236
+ * Compute drawer offset in pixels from progress.
237
+ *
238
+ * For reveal mode, the drawer animates from an offset position to 0.
239
+ * The offset is DRAWER_CLOSED_OFFSET_PERCENT of the drawer's own size.
240
+ *
241
+ * @param progress - Animation progress (0 = closed, 1 = open)
242
+ * @param drawerSize - Drawer size in pixels
243
+ * @param placement - Drawer placement
244
+ * @returns Drawer offset in pixels
245
+ */
246
+ export function computeDrawerOffsetPx(
247
+ progress: number,
248
+ drawerSize: number,
249
+ placement: DrawerPlacement,
250
+ ): number {
251
+ const { sign } = PLACEMENT_CONFIG[placement];
252
+ // Drawer offset at closed state: -30% of drawer size (or +30% for right/bottom)
253
+ const maxOffsetPx = drawerSize * (DRAWER_CLOSED_OFFSET_PERCENT / 100);
254
+ const offsetPx = maxOffsetPx * (1 - progress);
255
+ return sign > 0 ? -offsetPx : offsetPx;
256
+ }
257
+
258
+ /**
259
+ * Compute content offset in pixels from progress.
260
+ *
261
+ * Content moves from 0 (closed) to ±drawerSize (open).
262
+ *
263
+ * @param progress - Animation progress (0 = closed, 1 = open)
264
+ * @param drawerSize - Drawer size in pixels
265
+ * @param placement - Drawer placement
266
+ * @returns Content offset in pixels
267
+ */
268
+ export function computeContentOffsetPx(
269
+ progress: number,
270
+ drawerSize: number,
271
+ placement: DrawerPlacement,
272
+ ): number {
273
+ const { sign } = PLACEMENT_CONFIG[placement];
274
+ return sign * drawerSize * progress;
275
+ }
276
+
277
+ /**
278
+ * Compute both drawer and content offsets from progress.
279
+ *
280
+ * @param progress - Animation progress (0 = closed, 1 = open)
281
+ * @param drawerSize - Drawer size in pixels
282
+ * @param placement - Drawer placement
283
+ * @returns Position in pixels for both elements
284
+ */
285
+ export function computePositionFromProgress(
286
+ progress: number,
287
+ drawerSize: number,
288
+ placement: DrawerPlacement,
289
+ ): RevealPositionPx {
290
+ return {
291
+ drawerPx: computeDrawerOffsetPx(progress, drawerSize, placement),
292
+ contentPx: computeContentOffsetPx(progress, drawerSize, placement),
293
+ };
294
+ }
295
+
296
+ /**
297
+ * Compute target positions for open or closed state.
298
+ *
299
+ * @param isOpen - Whether target is the open state
300
+ * @param drawerSize - Drawer size in pixels
301
+ * @param placement - Drawer placement
302
+ * @returns Target positions in pixels
303
+ */
304
+ export function computeTargetPosition(
305
+ isOpen: boolean,
306
+ drawerSize: number,
307
+ placement: DrawerPlacement,
308
+ ): RevealPositionPx {
309
+ const targetProgress = isOpen ? 1 : 0;
310
+ return computePositionFromProgress(targetProgress, drawerSize, placement);
311
+ }
312
+
313
+ /**
314
+ * Compute position from swipe displacement.
315
+ *
316
+ * This is the main function for tracking position during swipe.
317
+ *
318
+ * @param displacement - Current swipe displacement in pixels
319
+ * @param drawerSize - Drawer size in pixels
320
+ * @param placement - Drawer placement
321
+ * @param isOpening - Whether opening via swipe
322
+ * @param isClosing - Whether closing via swipe
323
+ * @returns Current position in pixels
324
+ */
325
+ export function computePositionFromDisplacement(
326
+ displacement: number,
327
+ drawerSize: number,
328
+ placement: DrawerPlacement,
329
+ isOpening: boolean,
330
+ isClosing: boolean,
331
+ ): RevealPositionPx {
332
+ const progress = computeSwipeProgress(displacement, drawerSize, isOpening, isClosing);
333
+ return computePositionFromProgress(progress, drawerSize, placement);
334
+ }
335
+
336
+ // ============================================================================
337
+ // Pixel-based transform string builders
338
+ // ============================================================================
339
+
340
+ /**
341
+ * Build drawer transform string from pixel offset.
342
+ *
343
+ * @param drawerPx - Drawer offset in pixels
344
+ * @param placement - Drawer placement
345
+ * @returns CSS transform string
346
+ */
347
+ export function buildDrawerTransformPx(
348
+ drawerPx: number,
349
+ placement: DrawerPlacement,
350
+ ): string {
351
+ const { axis } = PLACEMENT_CONFIG[placement];
352
+ return `translate${axis}(${drawerPx}px)`;
353
+ }
354
+
355
+ /**
356
+ * Build content transform string from pixel offset.
357
+ *
358
+ * @param contentPx - Content offset in pixels
359
+ * @param placement - Drawer placement
360
+ * @returns CSS transform string
361
+ */
362
+ export function buildContentTransformPx(
363
+ contentPx: number,
364
+ placement: DrawerPlacement,
365
+ ): string {
366
+ const { axis } = PLACEMENT_CONFIG[placement];
367
+ return `translate${axis}(${contentPx}px)`;
368
+ }
369
+
370
+ // ============================================================================
371
+ // Visibility management
372
+ // ============================================================================
373
+
374
+ /**
375
+ * Show the drawer element.
376
+ */
377
+ export function showDrawer(element: HTMLElement): void {
378
+ element.style.visibility = "visible";
379
+ element.style.pointerEvents = "auto";
380
+ }
381
+
382
+ /**
383
+ * Hide the drawer element.
384
+ */
385
+ export function hideDrawer(element: HTMLElement): void {
386
+ element.style.visibility = "hidden";
387
+ element.style.pointerEvents = "none";
388
+ }
389
+
390
+ /**
391
+ * Clear drawer visibility styles.
392
+ */
393
+ export function clearDrawerVisibility(element: HTMLElement): void {
394
+ element.style.visibility = "";
395
+ element.style.pointerEvents = "";
396
+ }
397
+
398
+ // ============================================================================
399
+ // Stacking context with configurable background
400
+ // ============================================================================
401
+
402
+ /**
403
+ * Apply stacking context styles with configurable background.
404
+ *
405
+ * @param element - Content element
406
+ * @param background - Background color (defaults to #fff)
407
+ */
408
+ export function applyStackingContextWithBackground(
409
+ element: HTMLElement,
410
+ background: string = STACKING_CONTEXT_STYLES.background,
411
+ ): void {
412
+ element.style.position = STACKING_CONTEXT_STYLES.position;
413
+ element.style.zIndex = STACKING_CONTEXT_STYLES.zIndex;
414
+ element.style.background = background;
415
+ }
@@ -0,0 +1,302 @@
1
+ /**
2
+ * @file Tests for drawerStyles utilities.
3
+ */
4
+ import {
5
+ getPlacementStyle,
6
+ getOpenTransform,
7
+ getClosedTransform,
8
+ getRevealTransform,
9
+ computeTransitionValue,
10
+ computeBackdropTransition,
11
+ isHorizontalPlacement,
12
+ formatDimension,
13
+ computeEdgeZoneStyle,
14
+ } from "./drawerStyles.js";
15
+
16
+ describe("drawerStyles", () => {
17
+ describe("getPlacementStyle", () => {
18
+ it("returns correct style for left placement", () => {
19
+ const style = getPlacementStyle("left");
20
+ expect(style.left).toBe(0);
21
+ expect(style.top).toBe(0);
22
+ expect(style.bottom).toBe(0);
23
+ expect(style.transform).toBe("translateX(-100%)");
24
+ });
25
+
26
+ it("returns correct style for right placement", () => {
27
+ const style = getPlacementStyle("right");
28
+ expect(style.right).toBe(0);
29
+ expect(style.top).toBe(0);
30
+ expect(style.bottom).toBe(0);
31
+ expect(style.transform).toBe("translateX(100%)");
32
+ });
33
+
34
+ it("returns correct style for top placement", () => {
35
+ const style = getPlacementStyle("top");
36
+ expect(style.top).toBe(0);
37
+ expect(style.left).toBe(0);
38
+ expect(style.right).toBe(0);
39
+ expect(style.transform).toBe("translateY(-100%)");
40
+ });
41
+
42
+ it("returns correct style for bottom placement", () => {
43
+ const style = getPlacementStyle("bottom");
44
+ expect(style.bottom).toBe(0);
45
+ expect(style.left).toBe(0);
46
+ expect(style.right).toBe(0);
47
+ expect(style.transform).toBe("translateY(100%)");
48
+ });
49
+ });
50
+
51
+ describe("getOpenTransform", () => {
52
+ it("returns translateX(0) for horizontal placements", () => {
53
+ expect(getOpenTransform("left")).toBe("translateX(0)");
54
+ expect(getOpenTransform("right")).toBe("translateX(0)");
55
+ });
56
+
57
+ it("returns translateY(0) for vertical placements", () => {
58
+ expect(getOpenTransform("top")).toBe("translateY(0)");
59
+ expect(getOpenTransform("bottom")).toBe("translateY(0)");
60
+ });
61
+ });
62
+
63
+ describe("getClosedTransform", () => {
64
+ it("returns correct closed transform for each placement", () => {
65
+ expect(getClosedTransform("left")).toBe("translateX(-100%)");
66
+ expect(getClosedTransform("right")).toBe("translateX(100%)");
67
+ expect(getClosedTransform("top")).toBe("translateY(-100%)");
68
+ expect(getClosedTransform("bottom")).toBe("translateY(100%)");
69
+ });
70
+ });
71
+
72
+ describe("getRevealTransform", () => {
73
+ describe("when drawer is closed", () => {
74
+ it("returns -30% offset for left placement", () => {
75
+ expect(getRevealTransform("left", false)).toBe("translateX(-30%)");
76
+ });
77
+
78
+ it("returns +30% offset for right placement", () => {
79
+ expect(getRevealTransform("right", false)).toBe("translateX(30%)");
80
+ });
81
+
82
+ it("returns -30% offset for top placement", () => {
83
+ expect(getRevealTransform("top", false)).toBe("translateY(-30%)");
84
+ });
85
+
86
+ it("returns +30% offset for bottom placement", () => {
87
+ expect(getRevealTransform("bottom", false)).toBe("translateY(30%)");
88
+ });
89
+ });
90
+
91
+ describe("when drawer is open", () => {
92
+ it("returns translateX(0) for left placement", () => {
93
+ expect(getRevealTransform("left", true)).toBe("translateX(0)");
94
+ });
95
+
96
+ it("returns translateX(0) for right placement", () => {
97
+ expect(getRevealTransform("right", true)).toBe("translateX(0)");
98
+ });
99
+
100
+ it("returns translateY(0) for top placement", () => {
101
+ expect(getRevealTransform("top", true)).toBe("translateY(0)");
102
+ });
103
+
104
+ it("returns translateY(0) for bottom placement", () => {
105
+ expect(getRevealTransform("bottom", true)).toBe("translateY(0)");
106
+ });
107
+ });
108
+ });
109
+
110
+ describe("computeTransitionValue", () => {
111
+ it("returns undefined when mode is none", () => {
112
+ expect(computeTransitionValue("none", undefined, undefined)).toBeUndefined();
113
+ });
114
+
115
+ it("returns transition with defaults when no values provided", () => {
116
+ const result = computeTransitionValue(undefined, undefined, undefined);
117
+ expect(result).toContain("transform");
118
+ });
119
+
120
+ it("uses provided duration and easing", () => {
121
+ const result = computeTransitionValue("css", "300ms", "ease-in-out");
122
+ expect(result).toBe("transform 300ms ease-in-out");
123
+ });
124
+ });
125
+
126
+ describe("computeBackdropTransition", () => {
127
+ it("returns undefined when mode is none", () => {
128
+ expect(computeBackdropTransition("none", undefined)).toBeUndefined();
129
+ });
130
+
131
+ it("returns opacity transition with defaults", () => {
132
+ const result = computeBackdropTransition(undefined, undefined);
133
+ expect(result).toContain("opacity");
134
+ expect(result).toContain("220ms");
135
+ });
136
+
137
+ it("uses provided duration", () => {
138
+ const result = computeBackdropTransition("css", "400ms");
139
+ expect(result).toBe("opacity 400ms ease");
140
+ });
141
+ });
142
+
143
+ describe("isHorizontalPlacement", () => {
144
+ it("returns true for left and right", () => {
145
+ expect(isHorizontalPlacement("left")).toBe(true);
146
+ expect(isHorizontalPlacement("right")).toBe(true);
147
+ });
148
+
149
+ it("returns false for top and bottom", () => {
150
+ expect(isHorizontalPlacement("top")).toBe(false);
151
+ expect(isHorizontalPlacement("bottom")).toBe(false);
152
+ });
153
+ });
154
+
155
+ describe("formatDimension", () => {
156
+ it("returns undefined for undefined input", () => {
157
+ expect(formatDimension(undefined)).toBeUndefined();
158
+ });
159
+
160
+ it("returns number with px suffix", () => {
161
+ expect(formatDimension(100)).toBe("100px");
162
+ expect(formatDimension(0)).toBe("0px");
163
+ });
164
+
165
+ it("returns string as-is", () => {
166
+ expect(formatDimension("50%")).toBe("50%");
167
+ expect(formatDimension("auto")).toBe("auto");
168
+ });
169
+ });
170
+
171
+ describe("computeEdgeZoneStyle", () => {
172
+ describe("positioning context", () => {
173
+ it("uses fixed positioning when inline is false", () => {
174
+ const style = computeEdgeZoneStyle({
175
+ placement: "left",
176
+ inline: false,
177
+ edgeWidth: 20,
178
+ zIndex: 1000,
179
+ });
180
+ expect(style.position).toBe("fixed");
181
+ });
182
+
183
+ it("uses absolute positioning when inline is true", () => {
184
+ const style = computeEdgeZoneStyle({
185
+ placement: "left",
186
+ inline: true,
187
+ edgeWidth: 20,
188
+ zIndex: 1000,
189
+ });
190
+ expect(style.position).toBe("absolute");
191
+ });
192
+ });
193
+
194
+ describe("left placement", () => {
195
+ it("positions at left edge with correct dimensions", () => {
196
+ const style = computeEdgeZoneStyle({
197
+ placement: "left",
198
+ inline: false,
199
+ edgeWidth: 20,
200
+ zIndex: 1000,
201
+ });
202
+ expect(style.left).toBe(0);
203
+ expect(style.top).toBe(0);
204
+ expect(style.bottom).toBe(0);
205
+ expect(style.width).toBe(20);
206
+ expect(style.right).toBeUndefined();
207
+ });
208
+ });
209
+
210
+ describe("right placement", () => {
211
+ it("positions at right edge with correct dimensions", () => {
212
+ const style = computeEdgeZoneStyle({
213
+ placement: "right",
214
+ inline: false,
215
+ edgeWidth: 30,
216
+ zIndex: 2000,
217
+ });
218
+ expect(style.right).toBe(0);
219
+ expect(style.top).toBe(0);
220
+ expect(style.bottom).toBe(0);
221
+ expect(style.width).toBe(30);
222
+ expect(style.left).toBeUndefined();
223
+ });
224
+ });
225
+
226
+ describe("top placement", () => {
227
+ it("positions at top edge with correct dimensions", () => {
228
+ const style = computeEdgeZoneStyle({
229
+ placement: "top",
230
+ inline: false,
231
+ edgeWidth: 25,
232
+ zIndex: 1000,
233
+ });
234
+ expect(style.top).toBe(0);
235
+ expect(style.left).toBe(0);
236
+ expect(style.right).toBe(0);
237
+ expect(style.height).toBe(25);
238
+ expect(style.bottom).toBeUndefined();
239
+ });
240
+ });
241
+
242
+ describe("bottom placement", () => {
243
+ it("positions at bottom edge with correct dimensions", () => {
244
+ const style = computeEdgeZoneStyle({
245
+ placement: "bottom",
246
+ inline: false,
247
+ edgeWidth: 40,
248
+ zIndex: 1000,
249
+ });
250
+ expect(style.bottom).toBe(0);
251
+ expect(style.left).toBe(0);
252
+ expect(style.right).toBe(0);
253
+ expect(style.height).toBe(40);
254
+ expect(style.top).toBeUndefined();
255
+ });
256
+ });
257
+
258
+ describe("zIndex handling", () => {
259
+ it("sets zIndex to provided value minus 2", () => {
260
+ const style = computeEdgeZoneStyle({
261
+ placement: "left",
262
+ inline: false,
263
+ edgeWidth: 20,
264
+ zIndex: 1000,
265
+ });
266
+ expect(style.zIndex).toBe(998);
267
+ });
268
+
269
+ it("uses default zIndex of 1000 when zIndex is undefined", () => {
270
+ const style = computeEdgeZoneStyle({
271
+ placement: "left",
272
+ inline: false,
273
+ edgeWidth: 20,
274
+ zIndex: undefined,
275
+ });
276
+ expect(style.zIndex).toBe(1000);
277
+ });
278
+ });
279
+
280
+ describe("common properties", () => {
281
+ it("has transparent background", () => {
282
+ const style = computeEdgeZoneStyle({
283
+ placement: "left",
284
+ inline: false,
285
+ edgeWidth: 20,
286
+ zIndex: 1000,
287
+ });
288
+ expect(style.background).toBe("transparent");
289
+ });
290
+
291
+ it("has pointer events enabled", () => {
292
+ const style = computeEdgeZoneStyle({
293
+ placement: "left",
294
+ inline: false,
295
+ edgeWidth: 20,
296
+ zIndex: 1000,
297
+ });
298
+ expect(style.pointerEvents).toBe("auto");
299
+ });
300
+ });
301
+ });
302
+ });