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
@@ -2,83 +2,56 @@
2
2
  * @file Drawer component
3
3
  *
4
4
  * Mobile-friendly slide-in panel with backdrop support.
5
+ * Supports swipe gestures for opening/closing.
6
+ * Supports reveal mode where content slides to show drawer behind.
5
7
  */
6
8
  import * as React from "react";
7
- import type { DrawerBehavior, WindowPosition } from "../../types";
9
+ import type { DrawerBehavior, WindowPosition } from "../../types.js";
8
10
  import {
9
11
  FloatingPanelCloseButton,
10
12
  FloatingPanelContent,
11
13
  FloatingPanelFrame,
12
14
  FloatingPanelHeader,
13
15
  FloatingPanelTitle,
14
- } from "../paneling/FloatingPanelFrame";
16
+ } from "../paneling/FloatingPanelFrame.js";
15
17
  import {
16
18
  DRAWER_HEADER_PADDING_Y,
17
19
  DRAWER_HEADER_PADDING_X,
18
20
  DRAWER_HEADER_GAP,
19
21
  DRAWER_CONTENT_PADDING,
20
- COLOR_DRAWER_BACKDROP,
21
- DRAWER_TRANSITION_DURATION,
22
- DRAWER_TRANSITION_EASING,
23
- } from "../../constants/styles";
24
-
25
- const drawerBackdropStyle: React.CSSProperties = {
26
- position: "fixed",
27
- inset: 0,
28
- background: COLOR_DRAWER_BACKDROP,
29
- };
30
-
31
- const drawerBaseStyle: React.CSSProperties = {
32
- willChange: "transform",
33
- };
34
-
35
- const drawerPlacementStyles: Record<string, React.CSSProperties> = {
36
- left: {
37
- top: 0,
38
- bottom: 0,
39
- left: 0,
40
- transform: "translateX(-100%)",
41
- },
42
- right: {
43
- top: 0,
44
- bottom: 0,
45
- right: 0,
46
- transform: "translateX(100%)",
47
- },
48
- top: {
49
- top: 0,
50
- left: 0,
51
- right: 0,
52
- transform: "translateY(-100%)",
53
- },
54
- bottom: {
55
- bottom: 0,
56
- left: 0,
57
- right: 0,
58
- transform: "translateY(100%)",
59
- },
60
- };
61
-
62
- const computeTransitionValue = (
63
- mode: DrawerBehavior["transitionMode"] | undefined,
64
- duration: DrawerBehavior["transitionDuration"],
65
- easing: DrawerBehavior["transitionEasing"],
66
- ): string | undefined => {
67
- if (mode === "none") {
68
- return undefined;
69
- }
70
-
71
- const durationValue = duration ?? DRAWER_TRANSITION_DURATION;
72
- const easingValue = easing ?? DRAWER_TRANSITION_EASING;
22
+ } from "../../constants/styles.js";
23
+ import { useDrawerSwipeInput } from "../../modules/drawer/useDrawerSwipeInput.js";
24
+ import type { DrawerSwipeDirection } from "../../modules/drawer/types.js";
25
+ import {
26
+ DRAWER_BACKDROP_BASE_STYLE,
27
+ DRAWER_PANEL_BASE_STYLE,
28
+ getPlacementStyle,
29
+ getOpenTransform,
30
+ computeTransitionValue,
31
+ computeBackdropTransition,
32
+ formatDimension,
33
+ computeEdgeZoneStyle,
34
+ } from "./drawerStyles.js";
35
+ import type { DrawerPlacement } from "./drawerStyles.js";
36
+ import {
37
+ parseSwipeGesturesConfig,
38
+ resolvePlacement,
39
+ shouldShowEdgeZone,
40
+ } from "./drawerSwipeConfig.js";
41
+ import { useDrawerSwipeTransform } from "./useDrawerSwipeTransform.js";
42
+ import { useDrawerReveal, isRevealMode } from "./DrawerRevealContext.js";
43
+ import { useRevealDrawerTransform } from "./useRevealDrawerTransform.js";
73
44
 
74
- return `transform ${durationValue} ${easingValue}`;
75
- };
45
+ // ============================================================================
46
+ // Types
47
+ // ============================================================================
76
48
 
77
49
  export type DrawerProps = {
78
50
  id: string;
79
51
  config: DrawerBehavior;
80
52
  isOpen: boolean;
81
53
  onClose: () => void;
54
+ onOpen?: () => void;
82
55
  children: React.ReactNode;
83
56
  zIndex?: number;
84
57
  width?: string | number;
@@ -86,12 +59,9 @@ export type DrawerProps = {
86
59
  position?: WindowPosition;
87
60
  };
88
61
 
89
- const shouldShowCloseButton = (dismissible: boolean, showClose: boolean): boolean => {
90
- if (!dismissible) {
91
- return false;
92
- }
93
- return showClose;
94
- };
62
+ // ============================================================================
63
+ // Sub-components
64
+ // ============================================================================
95
65
 
96
66
  type DrawerContentProps = {
97
67
  chrome: boolean;
@@ -102,7 +72,14 @@ type DrawerContentProps = {
102
72
  children: React.ReactNode;
103
73
  };
104
74
 
105
- const DrawerContent: React.FC<DrawerContentProps> = ({ chrome, frameStyle, header, dismissible, onClose, children }) => {
75
+ const DrawerContent: React.FC<DrawerContentProps> = ({
76
+ chrome,
77
+ frameStyle,
78
+ header,
79
+ dismissible,
80
+ onClose,
81
+ children,
82
+ }) => {
106
83
  if (!chrome) {
107
84
  return <>{children}</>;
108
85
  }
@@ -118,43 +95,221 @@ const DrawerContent: React.FC<DrawerContentProps> = ({ chrome, frameStyle, heade
118
95
  );
119
96
  };
120
97
 
121
- const DrawerHeaderView: React.FC<{
98
+ type DrawerHeaderViewProps = {
122
99
  header?: DrawerBehavior["header"];
123
100
  dismissible: boolean;
124
101
  onClose: () => void;
125
- }> = ({ header, dismissible, onClose }) => {
102
+ };
103
+
104
+ function shouldShowCloseButton(dismissible: boolean, showCloseButton: boolean): boolean {
105
+ if (!dismissible) {
106
+ return false;
107
+ }
108
+ return showCloseButton;
109
+ }
110
+
111
+ function renderCloseButton(
112
+ shouldShow: boolean,
113
+ onClose: () => void,
114
+ ): React.ReactNode {
115
+ if (!shouldShow) {
116
+ return null;
117
+ }
118
+ return (
119
+ <FloatingPanelCloseButton
120
+ onClick={onClose}
121
+ aria-label="Close drawer"
122
+ style={{ marginLeft: "auto" }}
123
+ />
124
+ );
125
+ }
126
+
127
+ function renderEdgeZone(
128
+ showEdgeZone: boolean,
129
+ edgeZoneRef: React.RefObject<HTMLDivElement | null>,
130
+ edgeZoneStyle: React.CSSProperties,
131
+ onPointerDown: ((e: React.PointerEvent<HTMLElement>) => void) | undefined,
132
+ placement: string,
133
+ ): React.ReactNode {
134
+ if (!showEdgeZone) {
135
+ return null;
136
+ }
137
+ return (
138
+ <div
139
+ ref={edgeZoneRef}
140
+ style={edgeZoneStyle}
141
+ onPointerDown={onPointerDown}
142
+ data-drawer-edge-zone={placement}
143
+ />
144
+ );
145
+ }
146
+
147
+ /**
148
+ * Compute drawer size from props based on placement direction.
149
+ */
150
+ function computeDrawerSize(
151
+ placement: string,
152
+ width: string | number | undefined,
153
+ height: string | number | undefined,
154
+ ): number {
155
+ const isHorizontal = placement === "left" || placement === "right";
156
+ if (isHorizontal) {
157
+ return typeof width === "number" ? width : 0;
158
+ }
159
+ return typeof height === "number" ? height : 0;
160
+ }
161
+
162
+ const DrawerHeaderView: React.FC<DrawerHeaderViewProps> = ({ header, dismissible, onClose }) => {
126
163
  if (!header) {
127
164
  return null;
128
165
  }
129
166
 
130
167
  const showCloseButton = header.showCloseButton ?? true;
131
- const shouldShowClose = shouldShowCloseButton(dismissible, showCloseButton);
168
+ const shouldShow = shouldShowCloseButton(dismissible, showCloseButton);
132
169
 
133
170
  return (
134
- <React.Activity mode={header ? "visible" : "hidden"}>
135
- <FloatingPanelHeader
136
- style={{ padding: `${DRAWER_HEADER_PADDING_Y} ${DRAWER_HEADER_PADDING_X}`, gap: DRAWER_HEADER_GAP }}
137
- >
138
- <React.Activity mode={header ? "visible" : "hidden"}>
139
- <FloatingPanelTitle>{header.title}</FloatingPanelTitle>
140
- </React.Activity>
141
- <React.Activity mode={shouldShowClose ? "visible" : "hidden"}>
142
- <FloatingPanelCloseButton
143
- onClick={onClose}
144
- aria-label="Close drawer"
145
- style={{ marginLeft: "auto" }}
146
- />
147
- </React.Activity>
148
- </FloatingPanelHeader>
149
- </React.Activity>
171
+ <FloatingPanelHeader
172
+ style={{ padding: `${DRAWER_HEADER_PADDING_Y} ${DRAWER_HEADER_PADDING_X}`, gap: DRAWER_HEADER_GAP }}
173
+ >
174
+ <FloatingPanelTitle>{header.title}</FloatingPanelTitle>
175
+ {renderCloseButton(shouldShow, onClose)}
176
+ </FloatingPanelHeader>
150
177
  );
151
178
  };
152
179
 
180
+ // ============================================================================
181
+ // Style computation hooks
182
+ // ============================================================================
183
+
184
+ function useDrawerPanelStyle(
185
+ placement: DrawerPlacement,
186
+ isOpen: boolean,
187
+ isSwipeOperating: boolean,
188
+ config: DrawerBehavior,
189
+ dimensions: { width?: string | number; height?: string | number; zIndex?: number },
190
+ ): React.CSSProperties {
191
+ const revealMode = isRevealMode(config.animationMode);
192
+
193
+ return React.useMemo((): React.CSSProperties => {
194
+ // Position follows inline setting consistently for all modes:
195
+ // - inline: true → absolute (container-relative)
196
+ // - inline: false → fixed (viewport-relative)
197
+ const position = config.inline ? "absolute" : "fixed";
198
+ const placementStyle = getPlacementStyle(placement);
199
+
200
+ // In reveal mode, drawer transform is handled entirely by useRevealDrawerTransform hook.
201
+ // No transform or transition in React styles - the hook manages all DOM manipulation.
202
+ // This is critical to avoid conflicts between React styles and hook's direct DOM updates.
203
+ if (revealMode) {
204
+ return {
205
+ ...DRAWER_PANEL_BASE_STYLE,
206
+ position,
207
+ // Only position properties from placementStyle, NOT transform
208
+ top: placementStyle.top,
209
+ bottom: placementStyle.bottom,
210
+ left: placementStyle.left,
211
+ right: placementStyle.right,
212
+ // No transform here - hook manages it
213
+ // No transition - hook uses requestAnimationFrame
214
+ // z-index: 0 ensures drawer is behind #root (which has z-index: 1)
215
+ zIndex: 0,
216
+ // Hidden by default - hook will show when needed
217
+ visibility: "hidden",
218
+ pointerEvents: "none",
219
+ width: formatDimension(dimensions.width),
220
+ height: formatDimension(dimensions.height),
221
+ };
222
+ }
223
+
224
+ // Overlay mode (default): drawer slides over content
225
+ const effectiveMode = isSwipeOperating ? "none" : config.transitionMode;
226
+ const transitionValue = computeTransitionValue(
227
+ effectiveMode,
228
+ config.transitionDuration,
229
+ config.transitionEasing,
230
+ );
231
+
232
+ const transform = isOpen ? getOpenTransform(placement) : placementStyle.transform;
233
+
234
+ return {
235
+ ...DRAWER_PANEL_BASE_STYLE,
236
+ position,
237
+ ...placementStyle,
238
+ transform,
239
+ transition: transitionValue,
240
+ zIndex: dimensions.zIndex,
241
+ width: formatDimension(dimensions.width),
242
+ height: formatDimension(dimensions.height),
243
+ };
244
+ }, [placement, isOpen, isSwipeOperating, config, dimensions, revealMode]);
245
+ }
246
+
247
+ function useBackdropStyle(
248
+ isOpen: boolean,
249
+ isSwipeOperating: boolean,
250
+ config: DrawerBehavior,
251
+ zIndex: number | undefined,
252
+ hookControlled: boolean,
253
+ ): React.CSSProperties {
254
+ const revealMode = isRevealMode(config.animationMode);
255
+
256
+ return React.useMemo((): React.CSSProperties => {
257
+ // In reveal mode, no backdrop is shown (content slides instead)
258
+ if (revealMode) {
259
+ return {
260
+ display: "none",
261
+ };
262
+ }
263
+
264
+ const position = config.inline ? "absolute" as const : "fixed" as const;
265
+
266
+ // When hook controls the backdrop (overlay mode with swipe enabled),
267
+ // don't set opacity or transition - the hook handles all animations via DOM
268
+ if (hookControlled) {
269
+ return {
270
+ ...DRAWER_BACKDROP_BASE_STYLE,
271
+ position,
272
+ // Initial state: hidden. Hook will manage opacity dynamically.
273
+ opacity: 0,
274
+ pointerEvents: "none",
275
+ zIndex: zIndex !== undefined ? zIndex - 1 : undefined,
276
+ };
277
+ }
278
+
279
+ const effectiveMode = isSwipeOperating ? "none" : config.transitionMode;
280
+ const transitionValue = computeBackdropTransition(effectiveMode, config.transitionDuration);
281
+
282
+ return {
283
+ ...DRAWER_BACKDROP_BASE_STYLE,
284
+ position,
285
+ opacity: isOpen ? 1 : 0,
286
+ pointerEvents: isOpen ? "auto" : "none",
287
+ transition: transitionValue,
288
+ zIndex: zIndex !== undefined ? zIndex - 1 : undefined,
289
+ };
290
+ }, [isOpen, isSwipeOperating, config, zIndex, revealMode, hookControlled]);
291
+ }
292
+
293
+ function useFrameStyle(placement: DrawerPlacement): React.CSSProperties {
294
+ return React.useMemo((): React.CSSProperties => {
295
+ const style: React.CSSProperties = { borderRadius: 0 };
296
+ if (placement === "left" || placement === "right") {
297
+ style.height = "100%";
298
+ }
299
+ return style;
300
+ }, [placement]);
301
+ }
302
+
303
+ // ============================================================================
304
+ // Main Component
305
+ // ============================================================================
306
+
153
307
  export const Drawer: React.FC<DrawerProps> = ({
154
308
  id,
155
309
  config,
156
310
  isOpen,
157
311
  onClose,
312
+ onOpen,
158
313
  children,
159
314
  zIndex,
160
315
  width,
@@ -166,106 +321,142 @@ export const Drawer: React.FC<DrawerProps> = ({
166
321
  header,
167
322
  chrome = true,
168
323
  inline = false,
169
- transitionMode = "css",
170
- transitionDuration,
171
- transitionEasing,
172
324
  } = config;
173
325
 
174
- const resolvePlacement = React.useCallback(
175
- (anchor?: DrawerBehavior["anchor"], pos?: WindowPosition): "left" | "right" | "top" | "bottom" => {
176
- // Prefer explicit anchor from config
177
- if (anchor) {
178
- return anchor;
179
- }
180
- // Fall back to inferring from position
181
- if (!pos) {
182
- return "right";
183
- }
184
- if (pos.left !== undefined) {
185
- return "left";
186
- }
187
- if (pos.right !== undefined) {
188
- return "right";
189
- }
190
- if (pos.top !== undefined) {
191
- return "top";
192
- }
193
- if (pos.bottom !== undefined) {
194
- return "bottom";
195
- }
196
- return "right";
197
- },
198
- [],
199
- );
200
-
326
+ const swipeConfig = parseSwipeGesturesConfig(config.swipeGestures);
201
327
  const placement = resolvePlacement(config.anchor, position);
202
328
 
203
- const openTransforms: Record<string, string> = {
204
- left: "translateX(0)",
205
- right: "translateX(0)",
206
- top: "translateY(0)",
207
- bottom: "translateY(0)",
208
- };
209
-
210
- const drawerStyle = React.useMemo((): React.CSSProperties => {
211
- const transitionValue = computeTransitionValue(transitionMode, transitionDuration, transitionEasing);
212
-
213
- const style: React.CSSProperties = {
214
- ...drawerBaseStyle,
215
- ...(inline ? { position: "absolute" } : { position: "fixed" }),
216
- ...drawerPlacementStyles[placement],
217
- transform: isOpen ? openTransforms[placement] : drawerPlacementStyles[placement].transform,
218
- transition: transitionValue,
219
- };
329
+ // Refs
330
+ const drawerRef = React.useRef<HTMLDivElement>(null);
331
+ const backdropRef = React.useRef<HTMLDivElement>(null);
332
+ const edgeZoneRef = React.useRef<HTMLDivElement>(null);
220
333
 
221
- if (zIndex !== undefined) {
222
- style.zIndex = zIndex;
223
- }
334
+ // Swipe callbacks
335
+ const handleSwipeOpen = React.useCallback(() => {
336
+ onOpen?.();
337
+ config.onStateChange?.(true);
338
+ }, [onOpen, config]);
224
339
 
225
- if (width !== undefined) {
226
- style.width = typeof width === "number" ? `${width}px` : width;
227
- }
228
- if (height !== undefined) {
229
- style.height = typeof height === "number" ? `${height}px` : height;
230
- }
340
+ const handleSwipeClose = React.useCallback(() => {
341
+ onClose();
342
+ }, [onClose]);
231
343
 
232
- return style;
233
- }, [height, inline, isOpen, placement, transitionDuration, transitionEasing, transitionMode, width, zIndex]);
344
+ // Swipe input handling
345
+ const {
346
+ state: swipeState,
347
+ displacement,
348
+ edgeContainerProps,
349
+ drawerContentProps,
350
+ isOpening,
351
+ isClosing,
352
+ } = useDrawerSwipeInput({
353
+ edgeContainerRef: edgeZoneRef,
354
+ drawerContentRef: drawerRef,
355
+ direction: placement as DrawerSwipeDirection,
356
+ isOpen,
357
+ onSwipeOpen: handleSwipeOpen,
358
+ onSwipeClose: handleSwipeClose,
359
+ enableEdgeSwipeOpen: swipeConfig.edgeSwipeOpen,
360
+ enableSwipeClose: swipeConfig.swipeClose,
361
+ edgeWidth: swipeConfig.edgeWidth,
362
+ dismissThreshold: swipeConfig.dismissThreshold,
363
+ });
234
364
 
235
- const ariaLabel = header?.title ?? config.ariaLabel ?? "Drawer";
365
+ // During "ended" phase, isOpen hasn't been updated yet, so we still need to
366
+ // disable transitions to avoid the drawer snapping to closed position briefly.
367
+ const isSwipeOperating = swipeState.phase === "operating" || swipeState.phase === "ended";
368
+ const revealMode = isRevealMode(config.animationMode);
236
369
 
237
- const backdropStyle = React.useMemo((): React.CSSProperties => {
238
- const base = inline ? { ...drawerBackdropStyle, position: "absolute" as const } : drawerBackdropStyle;
239
- const transitionValue = transitionMode === "none" ? undefined : `opacity ${transitionDuration ?? "220ms"} ease`;
240
- return {
241
- ...base,
242
- opacity: isOpen ? 1 : 0,
243
- pointerEvents: isOpen ? "auto" : "none",
244
- transition: transitionValue,
245
- zIndex: zIndex !== undefined ? zIndex - 1 : undefined,
246
- };
247
- }, [inline, isOpen, transitionDuration, transitionMode, zIndex]);
370
+ // Apply swipe transform (only for overlay mode)
371
+ // In reveal mode, swipe transform is handled by useRevealDrawerTransform
372
+ const swipeTransformEnabled = swipeConfig.enabled ? !revealMode : false;
373
+ useDrawerSwipeTransform({
374
+ drawerRef,
375
+ backdropRef,
376
+ placement: placement as DrawerSwipeDirection,
377
+ swipeState,
378
+ displacement,
379
+ isOpening,
380
+ isClosing,
381
+ isOpen,
382
+ enabled: swipeTransformEnabled,
383
+ });
248
384
 
249
- const frameStyle = React.useMemo((): React.CSSProperties => {
250
- const isVertical = placement === "left" || placement === "right";
251
- const style: React.CSSProperties = { borderRadius: 0 };
252
- if (isVertical) {
253
- style.height = "100%";
385
+ // Compute drawer size from props (single source of truth)
386
+ const drawerSize = computeDrawerSize(placement, width, height);
387
+
388
+ // Get gridRef from context for inline mode
389
+ const { gridRef } = useDrawerReveal();
390
+
391
+ // Apply reveal mode transform (handles both drawer and content animation)
392
+ // Enabled for both inline and non-inline reveal mode
393
+ useRevealDrawerTransform({
394
+ drawerRef,
395
+ placement: placement as DrawerPlacement,
396
+ drawerSize,
397
+ isOpen,
398
+ swipeState,
399
+ displacement,
400
+ isOpening,
401
+ isClosing,
402
+ enabled: revealMode,
403
+ inline,
404
+ gridRef: gridRef ?? undefined,
405
+ contentBackground: config.revealBackground,
406
+ });
407
+
408
+ // Computed styles
409
+ const drawerStyle = useDrawerPanelStyle(placement, isOpen, isSwipeOperating, config, { width, height, zIndex });
410
+ const backdropStyle = useBackdropStyle(isOpen, isSwipeOperating, config, zIndex, swipeTransformEnabled);
411
+ const frameStyle = useFrameStyle(placement);
412
+
413
+ // Edge zone style: merge positioning style with gesture handlers' style
414
+ // Edge zone positioning follows the inline setting for consistency with drawer and backdrop:
415
+ // - inline: true → position: absolute (container-relative)
416
+ // - inline: false → position: fixed (viewport-relative)
417
+ const edgeZoneStyle = React.useMemo((): React.CSSProperties => {
418
+ const positioningStyle = computeEdgeZoneStyle({
419
+ placement,
420
+ inline,
421
+ edgeWidth: swipeConfig.edgeWidth,
422
+ zIndex,
423
+ });
424
+ // Merge with gesture container styles (touchAction, etc.)
425
+ return { ...positioningStyle, ...edgeContainerProps.style };
426
+ }, [placement, inline, swipeConfig.edgeWidth, zIndex, edgeContainerProps.style]);
427
+
428
+ // Merged drawer style with swipe props
429
+ const mergedDrawerStyle = React.useMemo((): React.CSSProperties => {
430
+ if (!swipeConfig.enabled) {
431
+ return drawerStyle;
254
432
  }
255
- return style;
256
- }, [placement]);
433
+ return { ...drawerStyle, ...drawerContentProps.style };
434
+ }, [swipeConfig.enabled, drawerStyle, drawerContentProps.style]);
435
+
436
+ // Visibility flags
437
+ const showEdgeZone = shouldShowEdgeZone(swipeConfig, isOpen, isOpening);
438
+ const ariaLabel = header?.title ?? config.ariaLabel ?? "Drawer";
439
+
440
+ const edgeZoneElement = renderEdgeZone(showEdgeZone, edgeZoneRef, edgeZoneStyle, edgeContainerProps.onPointerDown, placement);
257
441
 
258
442
  return (
259
443
  <>
260
- <div style={backdropStyle} onClick={dismissible ? onClose : undefined} />
444
+ {edgeZoneElement}
445
+ <div
446
+ ref={backdropRef}
447
+ style={backdropStyle}
448
+ onClick={dismissible ? onClose : undefined}
449
+ />
261
450
  <div
451
+ ref={drawerRef}
262
452
  data-layer-id={id}
263
453
  data-placement={placement}
264
- style={drawerStyle}
454
+ style={mergedDrawerStyle}
265
455
  role="dialog"
266
456
  aria-modal={dismissible ? true : undefined}
267
457
  aria-hidden={isOpen ? undefined : true}
268
458
  aria-label={ariaLabel}
459
+ onPointerDown={swipeConfig.enabled ? drawerContentProps.onPointerDown : undefined}
269
460
  >
270
461
  <DrawerContent
271
462
  chrome={chrome}