react-panel-layout 0.6.1 → 0.7.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 (127) hide show
  1. package/dist/FloatingWindow-CE-WzkNv.js +1542 -0
  2. package/dist/FloatingWindow-CE-WzkNv.js.map +1 -0
  3. package/dist/FloatingWindow-DpFpmX1f.cjs +2 -0
  4. package/dist/FloatingWindow-DpFpmX1f.cjs.map +1 -0
  5. package/dist/GridLayout-EwKszYBy.cjs +2 -0
  6. package/dist/{GridLayout-DKTg_N61.cjs.map → GridLayout-EwKszYBy.cjs.map} +1 -1
  7. package/dist/GridLayout-kiWdpMLQ.js +947 -0
  8. package/dist/{GridLayout-UWNxXw77.js.map → GridLayout-kiWdpMLQ.js.map} +1 -1
  9. package/dist/PanelSystem-Dmy5YI_6.cjs +3 -0
  10. package/dist/PanelSystem-Dmy5YI_6.cjs.map +1 -0
  11. package/dist/{PanelSystem-BqUzNtf2.js → PanelSystem-DrYsYwuV.js} +208 -247
  12. package/dist/PanelSystem-DrYsYwuV.js.map +1 -0
  13. package/dist/components/window/Drawer.d.ts +1 -0
  14. package/dist/components/window/DrawerRevealContext.d.ts +61 -0
  15. package/dist/components/window/drawerRevealAnimationUtils.d.ts +212 -0
  16. package/dist/components/window/drawerStyles.d.ts +5 -0
  17. package/dist/components/window/useDrawerSwipeTransform.d.ts +8 -2
  18. package/dist/components/window/useDrawerTransform.d.ts +68 -0
  19. package/dist/components/window/useRevealDrawerTransform.d.ts +56 -0
  20. package/dist/config.cjs +1 -1
  21. package/dist/config.cjs.map +1 -1
  22. package/dist/config.js +8 -7
  23. package/dist/config.js.map +1 -1
  24. package/dist/dialog/index.d.ts +1 -1
  25. package/dist/grid.cjs +1 -1
  26. package/dist/grid.js +2 -2
  27. package/dist/index.cjs +1 -1
  28. package/dist/index.js +4 -4
  29. package/dist/modules/dialog/DialogContainer.d.ts +22 -2
  30. package/dist/modules/dialog/Modal.d.ts +23 -2
  31. package/dist/modules/dialog/SwipeDialogContainer.d.ts +6 -2
  32. package/dist/modules/dialog/types.d.ts +12 -0
  33. package/dist/modules/drawer/drawerStateMachine.d.ts +168 -0
  34. package/dist/modules/drawer/revealDrawerConstants.d.ts +33 -0
  35. package/dist/modules/drawer/revealDrawerStateMachine.d.ts +146 -0
  36. package/dist/modules/drawer/strategies/index.d.ts +8 -0
  37. package/dist/modules/drawer/strategies/overlayStrategy.d.ts +12 -0
  38. package/dist/modules/drawer/strategies/revealStrategy.d.ts +12 -0
  39. package/dist/modules/drawer/strategies/types.d.ts +116 -0
  40. package/dist/panels.cjs +1 -1
  41. package/dist/panels.js +1 -1
  42. package/dist/stack.cjs +1 -1
  43. package/dist/stack.cjs.map +1 -1
  44. package/dist/stack.js +306 -347
  45. package/dist/stack.js.map +1 -1
  46. package/dist/types.d.ts +14 -0
  47. package/dist/useAnimationFrame-CRuFlk5t.js +394 -0
  48. package/dist/useAnimationFrame-CRuFlk5t.js.map +1 -0
  49. package/dist/useAnimationFrame-XRpDXkwV.cjs +2 -0
  50. package/dist/useAnimationFrame-XRpDXkwV.cjs.map +1 -0
  51. package/dist/window.cjs +1 -1
  52. package/dist/window.js +1 -1
  53. package/package.json +1 -1
  54. package/src/components/gesture/SwipeSafeZone.tsx +1 -0
  55. package/src/components/grid/GridLayout.tsx +110 -38
  56. package/src/components/window/Drawer.tsx +114 -10
  57. package/src/components/window/DrawerLayers.tsx +48 -15
  58. package/src/components/window/DrawerRevealContext.spec.ts +20 -0
  59. package/src/components/window/DrawerRevealContext.tsx +99 -0
  60. package/src/components/window/drawerRevealAnimationUtils.spec.ts +375 -0
  61. package/src/components/window/drawerRevealAnimationUtils.ts +415 -0
  62. package/src/components/window/drawerStyles.spec.ts +39 -0
  63. package/src/components/window/drawerStyles.ts +24 -0
  64. package/src/components/window/useDrawerSwipeTransform.ts +28 -90
  65. package/src/components/window/useDrawerTransform.ts +505 -0
  66. package/src/components/window/useRevealDrawerTransform.spec.ts +1936 -0
  67. package/src/components/window/useRevealDrawerTransform.ts +105 -0
  68. package/src/demo/components/FullscreenDemoPage.tsx +47 -0
  69. package/src/demo/fullscreenRoutes.tsx +32 -0
  70. package/src/demo/index.tsx +5 -0
  71. package/src/demo/pages/Dialog/components/CardExpandDemo.tsx +23 -8
  72. package/src/demo/pages/Drawer/components/DrawerBasics.module.css +6 -1
  73. package/src/demo/pages/Drawer/components/DrawerBasics.tsx +14 -4
  74. package/src/demo/pages/Drawer/components/DrawerReveal.module.css +157 -0
  75. package/src/demo/pages/Drawer/components/DrawerReveal.tsx +128 -0
  76. package/src/demo/pages/Drawer/reveal/index.tsx +17 -0
  77. package/src/demo/pages/Drawer/reveal-fullscreen/index.tsx +135 -0
  78. package/src/demo/pages/Drawer/reveal-fullscreen/styles.module.css +233 -0
  79. package/src/demo/pages/Stack/components/StackBasics.spec.tsx +56 -52
  80. package/src/demo/pages/Stack/components/StackTablet.spec.tsx +39 -49
  81. package/src/demo/routes.tsx +2 -0
  82. package/src/dialog/index.ts +2 -0
  83. package/src/hooks/gesture/testing/createGestureSimulator.ts +1 -0
  84. package/src/hooks/gesture/useNativeGestureGuard.spec.ts +10 -2
  85. package/src/hooks/gesture/useSwipeInput.spec.ts +69 -0
  86. package/src/hooks/gesture/useSwipeInput.ts +2 -0
  87. package/src/hooks/gesture/utils.ts +15 -4
  88. package/src/hooks/useAnimatedVisibility.spec.ts +3 -3
  89. package/src/hooks/useOperationContinuity.spec.ts +17 -10
  90. package/src/hooks/useOperationContinuity.ts +5 -5
  91. package/src/hooks/useSharedElementTransition.ts +28 -7
  92. package/src/modules/dialog/DialogContainer.tsx +39 -5
  93. package/src/modules/dialog/Modal.tsx +46 -4
  94. package/src/modules/dialog/SwipeDialogContainer.tsx +12 -2
  95. package/src/modules/dialog/dialogAnimationUtils.spec.ts +0 -1
  96. package/src/modules/dialog/types.ts +14 -0
  97. package/src/modules/dialog/useDialogContainer.spec.ts +11 -3
  98. package/src/modules/dialog/useDialogSwipeInput.spec.ts +49 -28
  99. package/src/modules/dialog/useDialogSwipeInput.ts +37 -6
  100. package/src/modules/dialog/useDialogTransform.spec.ts +63 -30
  101. package/src/modules/drawer/drawerStateMachine.ts +500 -0
  102. package/src/modules/drawer/revealDrawerConstants.ts +38 -0
  103. package/src/modules/drawer/revealDrawerStateMachine.spec.ts +558 -0
  104. package/src/modules/drawer/revealDrawerStateMachine.ts +197 -0
  105. package/src/modules/drawer/strategies/index.ts +9 -0
  106. package/src/modules/drawer/strategies/overlayStrategy.ts +133 -0
  107. package/src/modules/drawer/strategies/revealStrategy.ts +111 -0
  108. package/src/modules/drawer/strategies/types.ts +160 -0
  109. package/src/modules/drawer/useDrawerSwipeInput.ts +7 -4
  110. package/src/modules/pivot/SwipePivotContent.spec.tsx +48 -37
  111. package/src/modules/pivot/usePivotSwipeInput.spec.ts +8 -8
  112. package/src/modules/stack/swipeTransitionContinuity.spec.tsx +1 -1
  113. package/src/types.ts +15 -0
  114. package/dist/FloatingWindow-CUXnEtrb.js +0 -827
  115. package/dist/FloatingWindow-CUXnEtrb.js.map +0 -1
  116. package/dist/FloatingWindow-DMwyK0eK.cjs +0 -2
  117. package/dist/FloatingWindow-DMwyK0eK.cjs.map +0 -1
  118. package/dist/GridLayout-DKTg_N61.cjs +0 -2
  119. package/dist/GridLayout-UWNxXw77.js +0 -926
  120. package/dist/PanelSystem-BqUzNtf2.js.map +0 -1
  121. package/dist/PanelSystem-D603LKKv.cjs +0 -3
  122. package/dist/PanelSystem-D603LKKv.cjs.map +0 -1
  123. package/dist/useNativeGestureGuard-C7TSqEkr.cjs +0 -2
  124. package/dist/useNativeGestureGuard-C7TSqEkr.cjs.map +0 -1
  125. package/dist/useNativeGestureGuard-CGYo6O0r.js +0 -347
  126. package/dist/useNativeGestureGuard-CGYo6O0r.js.map +0 -1
  127. package/src/components/window/useDrawerSwipeTransform.spec.ts +0 -234
@@ -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
+ }
@@ -5,6 +5,7 @@ import {
5
5
  getPlacementStyle,
6
6
  getOpenTransform,
7
7
  getClosedTransform,
8
+ getRevealTransform,
8
9
  computeTransitionValue,
9
10
  computeBackdropTransition,
10
11
  isHorizontalPlacement,
@@ -68,6 +69,44 @@ describe("drawerStyles", () => {
68
69
  });
69
70
  });
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
+
71
110
  describe("computeTransitionValue", () => {
72
111
  it("returns undefined when mode is none", () => {
73
112
  expect(computeTransitionValue("none", undefined, undefined)).toBeUndefined();
@@ -66,6 +66,19 @@ const OPEN_TRANSFORMS: Record<DrawerPlacement, string> = {
66
66
  bottom: "translateY(0)",
67
67
  };
68
68
 
69
+ /**
70
+ * Reveal mode: drawer starts partially hidden and slides in with content.
71
+ * This creates a "pull out" effect rather than just content sliding away.
72
+ */
73
+ const REVEAL_CLOSED_OFFSET_PERCENT = 30;
74
+
75
+ const REVEAL_CLOSED_TRANSFORMS: Record<DrawerPlacement, string> = {
76
+ left: `translateX(-${REVEAL_CLOSED_OFFSET_PERCENT}%)`,
77
+ right: `translateX(${REVEAL_CLOSED_OFFSET_PERCENT}%)`,
78
+ top: `translateY(-${REVEAL_CLOSED_OFFSET_PERCENT}%)`,
79
+ bottom: `translateY(${REVEAL_CLOSED_OFFSET_PERCENT}%)`,
80
+ };
81
+
69
82
  /**
70
83
  * Get placement-specific style.
71
84
  */
@@ -87,6 +100,17 @@ export function getClosedTransform(placement: DrawerPlacement): string {
87
100
  return PLACEMENT_STYLES[placement].transform as string;
88
101
  }
89
102
 
103
+ /**
104
+ * Get reveal mode transform based on open state.
105
+ * When closed, drawer is partially hidden. When open, fully visible.
106
+ */
107
+ export function getRevealTransform(placement: DrawerPlacement, isOpen: boolean): string {
108
+ if (isOpen) {
109
+ return OPEN_TRANSFORMS[placement];
110
+ }
111
+ return REVEAL_CLOSED_TRANSFORMS[placement];
112
+ }
113
+
90
114
  /**
91
115
  * Compute CSS transition value.
92
116
  */
@@ -1,12 +1,15 @@
1
1
  /**
2
- * @file Hook for applying real-time transform during drawer swipe gestures.
2
+ * @file Hook for applying real-time transform during drawer swipe gestures (backward-compatible wrapper).
3
3
  *
4
- * Handles DOM manipulation for smooth swipe animations.
4
+ * This hook is a thin wrapper around useDrawerTransform with mode="overlay".
5
+ * For new code, prefer using useDrawerTransform directly.
6
+ *
7
+ * @deprecated Use useDrawerTransform with mode="overlay" instead.
5
8
  */
6
9
  import * as React from "react";
7
10
  import type { ContinuousOperationState } from "../../hooks/gesture/types.js";
8
- import { getDrawerAnimationAxis, getDrawerCloseSwipeSign } from "../../modules/drawer/types.js";
9
11
  import type { DrawerSwipeDirection } from "../../modules/drawer/types.js";
12
+ import { useDrawerTransform } from "./useDrawerTransform.js";
10
13
 
11
14
  type UseDrawerSwipeTransformOptions = {
12
15
  drawerRef: React.RefObject<HTMLDivElement | null>;
@@ -16,11 +19,14 @@ type UseDrawerSwipeTransformOptions = {
16
19
  displacement: number;
17
20
  isOpening: boolean;
18
21
  isClosing: boolean;
22
+ isOpen: boolean;
19
23
  enabled: boolean;
20
24
  };
21
25
 
22
26
  /**
23
27
  * Apply real-time transform to drawer and backdrop during swipe.
28
+ *
29
+ * @deprecated Use useDrawerTransform with mode="overlay" instead.
24
30
  */
25
31
  export function useDrawerSwipeTransform(options: UseDrawerSwipeTransformOptions): void {
26
32
  const {
@@ -31,99 +37,31 @@ export function useDrawerSwipeTransform(options: UseDrawerSwipeTransformOptions)
31
37
  displacement,
32
38
  isOpening,
33
39
  isClosing,
40
+ isOpen,
34
41
  enabled,
35
42
  } = options;
36
43
 
37
- const isOperating = swipeState.phase === "operating";
38
-
39
- // Apply real-time transform during swipe
40
- React.useLayoutEffect(() => {
41
- if (!enabled || !isOperating) {
42
- return;
43
- }
44
-
45
- const drawer = drawerRef.current;
46
- const backdrop = backdropRef.current;
47
-
48
- if (!drawer) {
49
- return;
50
- }
51
-
52
- const axis = getDrawerAnimationAxis(placement);
53
- const closeSign = getDrawerCloseSwipeSign(placement);
54
- const drawerSize = axis === "x" ? drawer.clientWidth : drawer.clientHeight;
55
-
56
- if (drawerSize <= 0) {
57
- return;
58
- }
59
-
60
- const translateFn = axis === "x" ? "translateX" : "translateY";
61
-
62
- if (isClosing) {
63
- applyClosingTransform(drawer, backdrop, translateFn, closeSign, displacement, drawerSize);
64
- } else if (isOpening) {
65
- applyOpeningTransform(drawer, backdrop, translateFn, closeSign, displacement, drawerSize);
66
- }
67
- }, [enabled, isOperating, isClosing, isOpening, displacement, placement, drawerRef, backdropRef]);
68
-
69
- // Reset transform after swipe ends
44
+ // Get drawer size from element
45
+ const drawerSizeRef = React.useRef(0);
70
46
  React.useLayoutEffect(() => {
71
- if (!enabled || swipeState.phase !== "ended") {
72
- return;
73
- }
74
-
75
47
  const drawer = drawerRef.current;
76
- const backdrop = backdropRef.current;
77
-
78
48
  if (drawer) {
79
- drawer.style.transform = "";
80
- }
81
- if (backdrop) {
82
- backdrop.style.opacity = "";
49
+ const isHorizontal = placement === "left" || placement === "right";
50
+ drawerSizeRef.current = isHorizontal ? drawer.clientWidth : drawer.clientHeight;
83
51
  }
84
- }, [enabled, swipeState.phase, drawerRef, backdropRef]);
85
- }
86
-
87
- function applyClosingTransform(
88
- drawer: HTMLDivElement,
89
- backdrop: HTMLDivElement | null,
90
- translateFn: "translateX" | "translateY",
91
- closeSign: 1 | -1,
92
- displacement: number,
93
- drawerSize: number,
94
- ): void {
95
- const translate = closeSign * displacement;
96
- drawer.style.transform = `${translateFn}(${translate}px)`;
97
-
98
- const progress = Math.min(displacement / drawerSize, 1);
99
- if (backdrop) {
100
- backdrop.style.opacity = String(1 - progress);
101
- }
102
- }
103
-
104
- function applyOpeningTransform(
105
- drawer: HTMLDivElement,
106
- backdrop: HTMLDivElement | null,
107
- translateFn: "translateX" | "translateY",
108
- closeSign: 1 | -1,
109
- displacement: number,
110
- drawerSize: number,
111
- ): void {
112
- const closedPosition = closeSign * drawerSize;
113
- const translate = closedPosition + closeSign * -1 * displacement;
114
- const clampedTranslate = clampTranslate(translate, closeSign);
115
- drawer.style.transform = `${translateFn}(${clampedTranslate}px)`;
116
-
117
- const progress = Math.min(displacement / drawerSize, 1);
118
- if (backdrop) {
119
- backdrop.style.opacity = String(progress);
120
- backdrop.style.pointerEvents = "auto";
121
- }
122
- }
52
+ }, [drawerRef, placement]);
123
53
 
124
- function clampTranslate(translate: number, closeSign: 1 | -1): number {
125
- if (closeSign > 0) {
126
- return Math.max(0, translate);
127
- }
128
- return Math.min(0, translate);
54
+ useDrawerTransform({
55
+ mode: "overlay",
56
+ drawerRef,
57
+ backdropRef,
58
+ placement,
59
+ drawerSize: drawerSizeRef.current,
60
+ isOpen,
61
+ swipeState,
62
+ displacement,
63
+ isOpening,
64
+ isClosing,
65
+ enabled,
66
+ });
129
67
  }