react-panel-layout 0.6.1 → 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.
- package/dist/FloatingWindow-Bw2djgpz.js +1542 -0
- package/dist/FloatingWindow-Bw2djgpz.js.map +1 -0
- package/dist/FloatingWindow-Cvyokf0m.cjs +2 -0
- package/dist/FloatingWindow-Cvyokf0m.cjs.map +1 -0
- package/dist/GridLayout-B4aCqSyd.js +947 -0
- package/dist/{GridLayout-UWNxXw77.js.map → GridLayout-B4aCqSyd.js.map} +1 -1
- package/dist/GridLayout-DNOClFzz.cjs +2 -0
- package/dist/{GridLayout-DKTg_N61.cjs.map → GridLayout-DNOClFzz.cjs.map} +1 -1
- package/dist/PanelSystem-B8Igvnb2.cjs +3 -0
- package/dist/PanelSystem-B8Igvnb2.cjs.map +1 -0
- package/dist/{PanelSystem-BqUzNtf2.js → PanelSystem-DDUSFjXD.js} +208 -247
- package/dist/PanelSystem-DDUSFjXD.js.map +1 -0
- package/dist/components/window/Drawer.d.ts +1 -0
- package/dist/components/window/DrawerRevealContext.d.ts +61 -0
- package/dist/components/window/drawerRevealAnimationUtils.d.ts +212 -0
- package/dist/components/window/drawerStyles.d.ts +5 -0
- package/dist/components/window/useDrawerSwipeTransform.d.ts +8 -2
- package/dist/components/window/useDrawerTransform.d.ts +68 -0
- package/dist/components/window/useRevealDrawerTransform.d.ts +56 -0
- package/dist/config.cjs +1 -1
- package/dist/config.cjs.map +1 -1
- package/dist/config.js +8 -7
- package/dist/config.js.map +1 -1
- package/dist/grid.cjs +1 -1
- package/dist/grid.js +2 -2
- package/dist/index.cjs +1 -1
- package/dist/index.js +4 -4
- package/dist/modules/drawer/drawerStateMachine.d.ts +168 -0
- package/dist/modules/drawer/revealDrawerConstants.d.ts +33 -0
- package/dist/modules/drawer/revealDrawerStateMachine.d.ts +146 -0
- package/dist/modules/drawer/strategies/index.d.ts +8 -0
- package/dist/modules/drawer/strategies/overlayStrategy.d.ts +12 -0
- package/dist/modules/drawer/strategies/revealStrategy.d.ts +12 -0
- package/dist/modules/drawer/strategies/types.d.ts +116 -0
- package/dist/panels.cjs +1 -1
- package/dist/panels.js +1 -1
- package/dist/stack.cjs +1 -1
- package/dist/stack.cjs.map +1 -1
- package/dist/stack.js +306 -347
- package/dist/stack.js.map +1 -1
- package/dist/types.d.ts +14 -0
- package/dist/useAnimationFrame-BZ6D2lMq.cjs +2 -0
- package/dist/useAnimationFrame-BZ6D2lMq.cjs.map +1 -0
- package/dist/useAnimationFrame-Bg4e-H8O.js +394 -0
- package/dist/useAnimationFrame-Bg4e-H8O.js.map +1 -0
- package/dist/window.cjs +1 -1
- package/dist/window.js +1 -1
- package/package.json +1 -1
- package/src/components/gesture/SwipeSafeZone.tsx +1 -0
- package/src/components/grid/GridLayout.tsx +110 -38
- package/src/components/window/Drawer.tsx +114 -10
- package/src/components/window/DrawerLayers.tsx +48 -15
- package/src/components/window/DrawerRevealContext.spec.ts +20 -0
- package/src/components/window/DrawerRevealContext.tsx +99 -0
- package/src/components/window/drawerRevealAnimationUtils.spec.ts +375 -0
- package/src/components/window/drawerRevealAnimationUtils.ts +415 -0
- package/src/components/window/drawerStyles.spec.ts +39 -0
- package/src/components/window/drawerStyles.ts +24 -0
- package/src/components/window/useDrawerSwipeTransform.ts +28 -90
- package/src/components/window/useDrawerTransform.ts +505 -0
- package/src/components/window/useRevealDrawerTransform.spec.ts +1936 -0
- package/src/components/window/useRevealDrawerTransform.ts +105 -0
- package/src/demo/components/FullscreenDemoPage.tsx +47 -0
- package/src/demo/fullscreenRoutes.tsx +32 -0
- package/src/demo/index.tsx +5 -0
- package/src/demo/pages/Dialog/components/CardExpandDemo.tsx +23 -8
- package/src/demo/pages/Drawer/components/DrawerBasics.module.css +6 -1
- package/src/demo/pages/Drawer/components/DrawerBasics.tsx +14 -4
- package/src/demo/pages/Drawer/components/DrawerReveal.module.css +157 -0
- package/src/demo/pages/Drawer/components/DrawerReveal.tsx +128 -0
- package/src/demo/pages/Drawer/reveal/index.tsx +17 -0
- package/src/demo/pages/Drawer/reveal-fullscreen/index.tsx +135 -0
- package/src/demo/pages/Drawer/reveal-fullscreen/styles.module.css +233 -0
- package/src/demo/pages/Stack/components/StackBasics.spec.tsx +56 -52
- package/src/demo/pages/Stack/components/StackTablet.spec.tsx +39 -49
- package/src/demo/routes.tsx +2 -0
- package/src/hooks/gesture/testing/createGestureSimulator.ts +1 -0
- package/src/hooks/gesture/useNativeGestureGuard.spec.ts +10 -2
- package/src/hooks/gesture/utils.ts +15 -4
- package/src/hooks/useAnimatedVisibility.spec.ts +3 -3
- package/src/hooks/useOperationContinuity.spec.ts +17 -10
- package/src/hooks/useOperationContinuity.ts +5 -5
- package/src/hooks/useSharedElementTransition.ts +28 -7
- package/src/modules/dialog/dialogAnimationUtils.spec.ts +0 -1
- package/src/modules/dialog/useDialogContainer.spec.ts +11 -3
- package/src/modules/dialog/useDialogSwipeInput.spec.ts +49 -28
- package/src/modules/dialog/useDialogSwipeInput.ts +37 -6
- package/src/modules/dialog/useDialogTransform.spec.ts +63 -30
- package/src/modules/drawer/drawerStateMachine.ts +500 -0
- package/src/modules/drawer/revealDrawerConstants.ts +38 -0
- package/src/modules/drawer/revealDrawerStateMachine.spec.ts +558 -0
- package/src/modules/drawer/revealDrawerStateMachine.ts +197 -0
- package/src/modules/drawer/strategies/index.ts +9 -0
- package/src/modules/drawer/strategies/overlayStrategy.ts +133 -0
- package/src/modules/drawer/strategies/revealStrategy.ts +111 -0
- package/src/modules/drawer/strategies/types.ts +160 -0
- package/src/modules/drawer/useDrawerSwipeInput.ts +7 -4
- package/src/modules/pivot/SwipePivotContent.spec.tsx +48 -37
- package/src/modules/pivot/usePivotSwipeInput.spec.ts +8 -8
- package/src/modules/stack/swipeTransitionContinuity.spec.tsx +1 -1
- package/src/types.ts +15 -0
- package/dist/FloatingWindow-CUXnEtrb.js +0 -827
- package/dist/FloatingWindow-CUXnEtrb.js.map +0 -1
- package/dist/FloatingWindow-DMwyK0eK.cjs +0 -2
- package/dist/FloatingWindow-DMwyK0eK.cjs.map +0 -1
- package/dist/GridLayout-DKTg_N61.cjs +0 -2
- package/dist/GridLayout-UWNxXw77.js +0 -926
- package/dist/PanelSystem-BqUzNtf2.js.map +0 -1
- package/dist/PanelSystem-D603LKKv.cjs +0 -3
- package/dist/PanelSystem-D603LKKv.cjs.map +0 -1
- package/dist/useNativeGestureGuard-C7TSqEkr.cjs +0 -2
- package/dist/useNativeGestureGuard-C7TSqEkr.cjs.map +0 -1
- package/dist/useNativeGestureGuard-CGYo6O0r.js +0 -347
- package/dist/useNativeGestureGuard-CGYo6O0r.js.map +0 -1
- 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
|
-
*
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
}, [
|
|
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
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
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
|
}
|