react-native-drawer-layout 4.2.0 → 5.0.0-alpha.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/lib/module/utils/getDrawerWidth.js +8 -9
- package/lib/module/utils/getDrawerWidth.js.map +1 -1
- package/lib/module/views/Drawer.js +28 -8
- package/lib/module/views/Drawer.js.map +1 -1
- package/lib/module/views/Drawer.native.js +101 -51
- package/lib/module/views/Drawer.native.js.map +1 -1
- package/lib/module/views/Overlay.native.js +3 -2
- package/lib/module/views/Overlay.native.js.map +1 -1
- package/lib/typescript/src/types.d.ts +1 -13
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/lib/typescript/src/utils/getDrawerWidth.d.ts +5 -8
- package/lib/typescript/src/utils/getDrawerWidth.d.ts.map +1 -1
- package/lib/typescript/src/views/Drawer.d.ts.map +1 -1
- package/lib/typescript/src/views/Drawer.native.d.ts +1 -1
- package/lib/typescript/src/views/Drawer.native.d.ts.map +1 -1
- package/lib/typescript/src/views/Overlay.native.d.ts.map +1 -1
- package/package.json +10 -13
- package/src/types.tsx +1 -9
- package/src/utils/getDrawerWidth.tsx +13 -11
- package/src/views/Drawer.native.tsx +148 -73
- package/src/views/Drawer.tsx +35 -13
- package/src/views/Overlay.native.tsx +3 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Drawer.d.ts","sourceRoot":"","sources":["../../../../src/views/Drawer.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAM5C,wBAAgB,MAAM,CAAC,EACrB,SAAiB,EACjB,cAAuD,EACvD,WAAW,EACX,UAAoB,EACpB,OAAO,EACP,iBAAiB,EACjB,eAAe,EACf,IAAI,EACJ,YAAY,EACZ,yBAAyB,EACzB,mBAAmB,EACnB,QAAQ,EACR,KAAK,GACN,EAAE,WAAW,
|
|
1
|
+
{"version":3,"file":"Drawer.d.ts","sourceRoot":"","sources":["../../../../src/views/Drawer.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAM5C,wBAAgB,MAAM,CAAC,EACrB,SAAiB,EACjB,cAAuD,EACvD,WAAW,EACX,UAAoB,EACpB,OAAO,EACP,iBAAiB,EACjB,eAAe,EACf,IAAI,EACJ,YAAY,EACZ,yBAAyB,EACzB,mBAAmB,EACnB,QAAQ,EACR,KAAK,GACN,EAAE,WAAW,2CA2Hb"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { DrawerProps } from '../types';
|
|
2
|
-
export declare function Drawer({
|
|
2
|
+
export declare function Drawer({ direction, drawerPosition, drawerStyle, drawerType, configureGestureHandler, hideStatusBarOnOpen, keyboardDismissMode, onClose, onOpen, onGestureStart, onGestureCancel, onGestureEnd, onTransitionStart, onTransitionEnd, open, overlayStyle, overlayAccessibilityLabel, statusBarAnimation, swipeEnabled, swipeEdgeWidth, swipeMinDistance, swipeMinVelocity, renderDrawerContent, children, style, }: DrawerProps): import("react/jsx-runtime").JSX.Element;
|
|
3
3
|
//# sourceMappingURL=Drawer.native.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Drawer.native.d.ts","sourceRoot":"","sources":["../../../../src/views/Drawer.native.tsx"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAuB5C,wBAAgB,MAAM,CAAC,EACrB,
|
|
1
|
+
{"version":3,"file":"Drawer.native.d.ts","sourceRoot":"","sources":["../../../../src/views/Drawer.native.tsx"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAuB5C,wBAAgB,MAAM,CAAC,EACrB,SAA4D,EAC5D,cAAuD,EACvD,WAAW,EACX,UAAoB,EACpB,uBAAuB,EACvB,mBAA2B,EAC3B,mBAA+B,EAC/B,OAAO,EACP,MAAM,EACN,cAAc,EACd,eAAe,EACf,YAAY,EACZ,iBAAiB,EACjB,eAAe,EACf,IAAI,EACJ,YAAY,EACZ,yBAAyB,EACzB,kBAA4B,EAC5B,YAEyB,EACzB,cAAiC,EACjC,gBAAqC,EACrC,gBAAqC,EACrC,mBAAmB,EACnB,QAAQ,EACR,KAAK,GACN,EAAE,WAAW,2CA4db"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Overlay.native.d.ts","sourceRoot":"","sources":["../../../../src/views/Overlay.native.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAI7C,wBAAgB,OAAO,CAAC,EACtB,QAAQ,EACR,OAAO,EACP,KAAK,EACL,kBAAmC,EACnC,GAAG,IAAI,EACR,EAAE,YAAY,
|
|
1
|
+
{"version":3,"file":"Overlay.native.d.ts","sourceRoot":"","sources":["../../../../src/views/Overlay.native.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAI7C,wBAAgB,OAAO,CAAC,EACtB,QAAQ,EACR,OAAO,EACP,KAAK,EACL,kBAAmC,EACnC,GAAG,IAAI,EACR,EAAE,YAAY,2CAiCd"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-drawer-layout",
|
|
3
3
|
"description": "Drawer component for React Native",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "5.0.0-alpha.0",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react-native-component",
|
|
7
7
|
"react-component",
|
|
@@ -43,25 +43,22 @@
|
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"color": "^4.2.3",
|
|
46
|
-
"use-latest-callback": "^0.2
|
|
46
|
+
"use-latest-callback": "^0.3.2"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
|
-
"@jest/globals": "^30.0.0",
|
|
50
|
-
"@testing-library/react-native": "^13.2.0",
|
|
51
|
-
"@types/color": "^4.2.0",
|
|
52
|
-
"@types/react": "~19.0.10",
|
|
53
49
|
"del-cli": "^6.0.0",
|
|
54
|
-
"react": "19.
|
|
55
|
-
"react-native": "0.
|
|
50
|
+
"react": "19.1.0",
|
|
51
|
+
"react-native": "0.81.4",
|
|
56
52
|
"react-native-builder-bob": "^0.40.12",
|
|
57
|
-
"react-
|
|
58
|
-
"
|
|
53
|
+
"react-native-reanimated": "4.1.3",
|
|
54
|
+
"react-native-worklets": "0.6.1",
|
|
55
|
+
"typescript": "^5.9.2"
|
|
59
56
|
},
|
|
60
57
|
"peerDependencies": {
|
|
61
|
-
"react": ">=
|
|
58
|
+
"react": ">= 19.0.0",
|
|
62
59
|
"react-native": "*",
|
|
63
60
|
"react-native-gesture-handler": ">= 2.0.0",
|
|
64
|
-
"react-native-reanimated": ">=
|
|
61
|
+
"react-native-reanimated": ">= 4.0.0"
|
|
65
62
|
},
|
|
66
63
|
"react-native-builder-bob": {
|
|
67
64
|
"source": "src",
|
|
@@ -81,5 +78,5 @@
|
|
|
81
78
|
]
|
|
82
79
|
]
|
|
83
80
|
},
|
|
84
|
-
"gitHead": "
|
|
81
|
+
"gitHead": "46daae524ec6a59737147ed506222dd09a5b6e39"
|
|
85
82
|
}
|
package/src/types.tsx
CHANGED
|
@@ -3,8 +3,6 @@ import type { StyleProp, View, ViewStyle } from 'react-native';
|
|
|
3
3
|
import type { PanGesture } from 'react-native-gesture-handler';
|
|
4
4
|
import type { SharedValue } from 'react-native-reanimated';
|
|
5
5
|
|
|
6
|
-
export type Layout = { width: number; height: number };
|
|
7
|
-
|
|
8
6
|
export type DrawerProps = {
|
|
9
7
|
/**
|
|
10
8
|
* Whether the drawer is open or not.
|
|
@@ -51,15 +49,9 @@ export type DrawerProps = {
|
|
|
51
49
|
*/
|
|
52
50
|
renderDrawerContent: () => React.ReactNode;
|
|
53
51
|
|
|
54
|
-
/**
|
|
55
|
-
* Object containing the layout of the container.
|
|
56
|
-
* Defaults to the dimensions of the application's window.
|
|
57
|
-
*/
|
|
58
|
-
layout?: { width: number; height: number };
|
|
59
|
-
|
|
60
52
|
/**
|
|
61
53
|
* Locale direction of the drawer.
|
|
62
|
-
* Defaults to `rtl` when `I18nManager.isRTL` is `true` on Android & iOS, otherwise `ltr`.
|
|
54
|
+
* Defaults to `rtl` when `I18nManager.getConstants().isRTL` is `true` on Android & iOS, otherwise `ltr`.
|
|
63
55
|
*/
|
|
64
56
|
direction?: 'ltr' | 'rtl';
|
|
65
57
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type DimensionValue, type ViewStyle } from 'react-native';
|
|
2
2
|
|
|
3
3
|
const APPROX_APP_BAR_HEIGHT = 56;
|
|
4
4
|
const DEFAULT_DRAWER_WIDTH = 360;
|
|
@@ -11,25 +11,27 @@ const DEFAULT_DRAWER_WIDTH = 360;
|
|
|
11
11
|
const DRAWER_DEFAULT_WIDTH_WEB = `min(calc(100% - ${APPROX_APP_BAR_HEIGHT}px), ${DEFAULT_DRAWER_WIDTH}px)`;
|
|
12
12
|
|
|
13
13
|
export function getDrawerWidthNative({
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
containerWidth,
|
|
15
|
+
customWidth,
|
|
16
16
|
}: {
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
containerWidth: number;
|
|
18
|
+
customWidth: DimensionValue | undefined | null;
|
|
19
19
|
}) {
|
|
20
|
+
'worklet';
|
|
21
|
+
|
|
20
22
|
const defaultWidth =
|
|
21
|
-
|
|
22
|
-
?
|
|
23
|
+
containerWidth - APPROX_APP_BAR_HEIGHT <= 360
|
|
24
|
+
? containerWidth - APPROX_APP_BAR_HEIGHT
|
|
23
25
|
: DEFAULT_DRAWER_WIDTH;
|
|
24
26
|
|
|
25
|
-
const
|
|
27
|
+
const width = customWidth ?? defaultWidth;
|
|
26
28
|
|
|
27
29
|
if (typeof width === 'string' && width.endsWith('%')) {
|
|
28
30
|
// Try to calculate width if a percentage is given
|
|
29
31
|
const percentage = Number(width.replace(/%$/, ''));
|
|
30
32
|
|
|
31
33
|
if (Number.isFinite(percentage)) {
|
|
32
|
-
return
|
|
34
|
+
return containerWidth * (percentage / 100);
|
|
33
35
|
}
|
|
34
36
|
}
|
|
35
37
|
|
|
@@ -39,9 +41,9 @@ export function getDrawerWidthNative({
|
|
|
39
41
|
export function getDrawerWidthWeb({
|
|
40
42
|
drawerStyle,
|
|
41
43
|
}: {
|
|
42
|
-
drawerStyle
|
|
44
|
+
drawerStyle: ViewStyle;
|
|
43
45
|
}): string {
|
|
44
|
-
const { width } =
|
|
46
|
+
const { width } = drawerStyle;
|
|
45
47
|
|
|
46
48
|
if (typeof width === 'number') {
|
|
47
49
|
return `${width}px`;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import {
|
|
3
|
+
Dimensions,
|
|
3
4
|
I18nManager,
|
|
4
|
-
InteractionManager,
|
|
5
5
|
Keyboard,
|
|
6
|
+
type LayoutChangeEvent,
|
|
6
7
|
Platform,
|
|
7
8
|
StatusBar,
|
|
8
9
|
StyleSheet,
|
|
9
|
-
useWindowDimensions,
|
|
10
10
|
View,
|
|
11
11
|
} from 'react-native';
|
|
12
12
|
import Animated, {
|
|
@@ -44,7 +44,6 @@ const minmax = (value: number, start: number, end: number) => {
|
|
|
44
44
|
};
|
|
45
45
|
|
|
46
46
|
export function Drawer({
|
|
47
|
-
layout: customLayout,
|
|
48
47
|
direction = I18nManager.getConstants().isRTL ? 'rtl' : 'ltr',
|
|
49
48
|
drawerPosition = direction === 'rtl' ? 'right' : 'left',
|
|
50
49
|
drawerStyle,
|
|
@@ -73,25 +72,27 @@ export function Drawer({
|
|
|
73
72
|
children,
|
|
74
73
|
style,
|
|
75
74
|
}: DrawerProps) {
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
const layout = customLayout ?? windowDimensions;
|
|
79
|
-
const drawerWidth = getDrawerWidthNative({ layout, drawerStyle });
|
|
75
|
+
const { width: customWidth } = StyleSheet.flatten(drawerStyle) || {};
|
|
80
76
|
|
|
81
77
|
const isOpen = drawerType === 'permanent' ? true : open;
|
|
82
78
|
const isRight = drawerPosition === 'right';
|
|
83
79
|
|
|
84
80
|
const getDrawerTranslationX = React.useCallback(
|
|
85
|
-
(open: boolean) => {
|
|
81
|
+
(open: boolean, containerWidth: number) => {
|
|
86
82
|
'worklet';
|
|
87
83
|
|
|
84
|
+
const drawerWidth = getDrawerWidthNative({
|
|
85
|
+
containerWidth,
|
|
86
|
+
customWidth,
|
|
87
|
+
});
|
|
88
|
+
|
|
88
89
|
if (drawerPosition === 'left') {
|
|
89
90
|
return open ? 0 : -drawerWidth;
|
|
90
91
|
}
|
|
91
92
|
|
|
92
93
|
return open ? 0 : drawerWidth;
|
|
93
94
|
},
|
|
94
|
-
[
|
|
95
|
+
[customWidth, drawerPosition]
|
|
95
96
|
);
|
|
96
97
|
|
|
97
98
|
const hideStatusBar = React.useCallback(
|
|
@@ -109,19 +110,6 @@ export function Drawer({
|
|
|
109
110
|
return () => hideStatusBar(false);
|
|
110
111
|
}, [isOpen, hideStatusBarOnOpen, statusBarAnimation, hideStatusBar]);
|
|
111
112
|
|
|
112
|
-
const interactionHandleRef = React.useRef<number | null>(null);
|
|
113
|
-
|
|
114
|
-
const startInteraction = useLatestCallback(() => {
|
|
115
|
-
interactionHandleRef.current = InteractionManager.createInteractionHandle();
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
const endInteraction = useLatestCallback(() => {
|
|
119
|
-
if (interactionHandleRef.current != null) {
|
|
120
|
-
InteractionManager.clearInteractionHandle(interactionHandleRef.current);
|
|
121
|
-
interactionHandleRef.current = null;
|
|
122
|
-
}
|
|
123
|
-
});
|
|
124
|
-
|
|
125
113
|
const hideKeyboard = useLatestCallback(() => {
|
|
126
114
|
if (keyboardDismissMode === 'on-drag') {
|
|
127
115
|
Keyboard.dismiss();
|
|
@@ -130,19 +118,16 @@ export function Drawer({
|
|
|
130
118
|
|
|
131
119
|
const onGestureBegin = useLatestCallback(() => {
|
|
132
120
|
onGestureStart?.();
|
|
133
|
-
startInteraction();
|
|
134
121
|
hideKeyboard();
|
|
135
122
|
hideStatusBar(true);
|
|
136
123
|
});
|
|
137
124
|
|
|
138
125
|
const onGestureFinish = useLatestCallback(() => {
|
|
139
126
|
onGestureEnd?.();
|
|
140
|
-
endInteraction();
|
|
141
127
|
});
|
|
142
128
|
|
|
143
129
|
const onGestureAbort = useLatestCallback(() => {
|
|
144
130
|
onGestureCancel?.();
|
|
145
|
-
endInteraction();
|
|
146
131
|
});
|
|
147
132
|
|
|
148
133
|
const hitSlop = React.useMemo(
|
|
@@ -155,9 +140,55 @@ export function Drawer({
|
|
|
155
140
|
[isRight, isOpen, swipeEdgeWidth]
|
|
156
141
|
);
|
|
157
142
|
|
|
143
|
+
const [initialWidth] = React.useState(() => Dimensions.get('window').width);
|
|
144
|
+
|
|
145
|
+
const layoutWidth = useSharedValue(initialWidth);
|
|
146
|
+
const translationX = useSharedValue(
|
|
147
|
+
getDrawerTranslationX(open, initialWidth)
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
const contentRef = React.useRef<View>(null);
|
|
151
|
+
|
|
152
|
+
React.useLayoutEffect(() => {
|
|
153
|
+
const measureLayout = () => {
|
|
154
|
+
contentRef.current?.measure((_x, _y, width) => {
|
|
155
|
+
layoutWidth.set(width);
|
|
156
|
+
translationX.set(getDrawerTranslationX(open, width));
|
|
157
|
+
});
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
measureLayout();
|
|
161
|
+
|
|
162
|
+
// FIXME: the layout is off after screen rotation
|
|
163
|
+
// Measure the layout again on screen rotation
|
|
164
|
+
let handle: number | undefined;
|
|
165
|
+
|
|
166
|
+
const subscription = Dimensions.addEventListener('change', () => {
|
|
167
|
+
// The measurement is not accurate without the delay
|
|
168
|
+
handle = requestAnimationFrame(() => {
|
|
169
|
+
measureLayout();
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
return () => {
|
|
174
|
+
if (handle != null) {
|
|
175
|
+
cancelAnimationFrame(handle);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
subscription.remove();
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
// only measure on initial render
|
|
182
|
+
// subsequent updates will be handled by onLayout
|
|
183
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
184
|
+
}, []);
|
|
185
|
+
|
|
186
|
+
const onLayout = (event: LayoutChangeEvent) => {
|
|
187
|
+
layoutWidth.set(event.nativeEvent.layout.width);
|
|
188
|
+
};
|
|
189
|
+
|
|
158
190
|
const touchStartX = useSharedValue(0);
|
|
159
191
|
const touchX = useSharedValue(0);
|
|
160
|
-
const translationX = useSharedValue(getDrawerTranslationX(open));
|
|
161
192
|
const gestureState = useSharedValue<GestureState>(GestureState.UNDETERMINED);
|
|
162
193
|
|
|
163
194
|
const onAnimationStart = useLatestCallback((open: boolean) => {
|
|
@@ -174,31 +205,47 @@ export function Drawer({
|
|
|
174
205
|
}
|
|
175
206
|
);
|
|
176
207
|
|
|
208
|
+
const animatingTo = useSharedValue<'open' | 'close' | null>(null);
|
|
209
|
+
|
|
177
210
|
const toggleDrawer = React.useCallback(
|
|
178
211
|
(open: boolean, velocity?: number) => {
|
|
179
212
|
'worklet';
|
|
180
213
|
|
|
181
|
-
|
|
214
|
+
touchStartX.set(0);
|
|
215
|
+
touchX.set(0);
|
|
216
|
+
|
|
217
|
+
const containerWidth = layoutWidth.get();
|
|
218
|
+
const toValue = getDrawerTranslationX(open, containerWidth);
|
|
219
|
+
|
|
220
|
+
if (translationX.get() === toValue) {
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (animatingTo.get() === (open ? 'open' : 'close')) {
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
182
227
|
|
|
183
228
|
if (velocity === undefined) {
|
|
184
229
|
runOnJS(onAnimationStart)(open);
|
|
185
230
|
}
|
|
186
231
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
232
|
+
animatingTo.set(open ? 'open' : 'close');
|
|
233
|
+
translationX.set(
|
|
234
|
+
withSpring(
|
|
235
|
+
toValue,
|
|
236
|
+
{
|
|
237
|
+
velocity,
|
|
238
|
+
stiffness: 1000,
|
|
239
|
+
damping: 500,
|
|
240
|
+
mass: 3,
|
|
241
|
+
overshootClamping: true,
|
|
242
|
+
reduceMotion: ReduceMotion.Never,
|
|
243
|
+
},
|
|
244
|
+
(finished) => {
|
|
245
|
+
animatingTo.set(null);
|
|
246
|
+
runOnJS(onAnimationEnd)(open, finished);
|
|
247
|
+
}
|
|
248
|
+
)
|
|
202
249
|
);
|
|
203
250
|
|
|
204
251
|
if (open) {
|
|
@@ -208,18 +255,22 @@ export function Drawer({
|
|
|
208
255
|
}
|
|
209
256
|
},
|
|
210
257
|
[
|
|
211
|
-
getDrawerTranslationX,
|
|
212
|
-
onAnimationEnd,
|
|
213
|
-
onAnimationStart,
|
|
214
|
-
onClose,
|
|
215
|
-
onOpen,
|
|
216
258
|
touchStartX,
|
|
217
259
|
touchX,
|
|
260
|
+
layoutWidth,
|
|
261
|
+
getDrawerTranslationX,
|
|
218
262
|
translationX,
|
|
263
|
+
animatingTo,
|
|
264
|
+
onAnimationStart,
|
|
265
|
+
onAnimationEnd,
|
|
266
|
+
onOpen,
|
|
267
|
+
onClose,
|
|
219
268
|
]
|
|
220
269
|
);
|
|
221
270
|
|
|
222
|
-
React.useEffect(() =>
|
|
271
|
+
React.useEffect(() => {
|
|
272
|
+
toggleDrawer(open);
|
|
273
|
+
}, [animatingTo, open, toggleDrawer]);
|
|
223
274
|
|
|
224
275
|
const startX = useSharedValue(0);
|
|
225
276
|
|
|
@@ -228,9 +279,9 @@ export function Drawer({
|
|
|
228
279
|
.onBegin((event) => {
|
|
229
280
|
'worklet';
|
|
230
281
|
|
|
231
|
-
startX.
|
|
232
|
-
gestureState.
|
|
233
|
-
touchStartX.
|
|
282
|
+
startX.set(translationX.get());
|
|
283
|
+
gestureState.set(event.state);
|
|
284
|
+
touchStartX.set(event.x);
|
|
234
285
|
})
|
|
235
286
|
.onStart(() => {
|
|
236
287
|
'worklet';
|
|
@@ -240,14 +291,14 @@ export function Drawer({
|
|
|
240
291
|
.onChange((event) => {
|
|
241
292
|
'worklet';
|
|
242
293
|
|
|
243
|
-
touchX.
|
|
244
|
-
translationX.
|
|
245
|
-
gestureState.
|
|
294
|
+
touchX.set(event.x);
|
|
295
|
+
translationX.set(startX.get() + event.translationX);
|
|
296
|
+
gestureState.set(event.state);
|
|
246
297
|
})
|
|
247
298
|
.onEnd((event, success) => {
|
|
248
299
|
'worklet';
|
|
249
300
|
|
|
250
|
-
gestureState.
|
|
301
|
+
gestureState.set(event.state);
|
|
251
302
|
|
|
252
303
|
if (!success) {
|
|
253
304
|
runOnJS(onGestureAbort)();
|
|
@@ -300,6 +351,11 @@ export function Drawer({
|
|
|
300
351
|
]);
|
|
301
352
|
|
|
302
353
|
const translateX = useDerivedValue(() => {
|
|
354
|
+
const drawerWidth = getDrawerWidthNative({
|
|
355
|
+
containerWidth: layoutWidth.get(),
|
|
356
|
+
customWidth,
|
|
357
|
+
});
|
|
358
|
+
|
|
303
359
|
// Comment stolen from react-native-gesture-handler/DrawerLayout
|
|
304
360
|
//
|
|
305
361
|
// While closing the drawer when user starts gesture outside of its area (in greyed
|
|
@@ -326,32 +382,38 @@ export function Drawer({
|
|
|
326
382
|
//
|
|
327
383
|
// This is used only when drawerType is "front"
|
|
328
384
|
const touchDistance =
|
|
329
|
-
drawerType === 'front' && gestureState.
|
|
385
|
+
drawerType === 'front' && gestureState.get() === GestureState.ACTIVE
|
|
330
386
|
? minmax(
|
|
331
387
|
drawerPosition === 'left'
|
|
332
|
-
? touchStartX.
|
|
333
|
-
:
|
|
388
|
+
? touchStartX.get() - drawerWidth
|
|
389
|
+
: layoutWidth.get() - drawerWidth - touchStartX.get(),
|
|
334
390
|
0,
|
|
335
|
-
|
|
391
|
+
layoutWidth.get()
|
|
336
392
|
)
|
|
337
393
|
: 0;
|
|
338
394
|
|
|
339
395
|
const translateX =
|
|
340
396
|
drawerPosition === 'left'
|
|
341
|
-
? minmax(translationX.
|
|
342
|
-
: minmax(translationX.
|
|
397
|
+
? minmax(translationX.get() + touchDistance, -drawerWidth, 0)
|
|
398
|
+
: minmax(translationX.get() - touchDistance, 0, drawerWidth);
|
|
343
399
|
|
|
344
400
|
return translateX;
|
|
345
401
|
});
|
|
346
402
|
|
|
347
403
|
const drawerAnimatedStyle = useAnimatedStyle(() => {
|
|
348
|
-
const
|
|
404
|
+
const drawerWidth = getDrawerWidthNative({
|
|
405
|
+
containerWidth: layoutWidth.get(),
|
|
406
|
+
customWidth,
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
const distanceFromEdge = layoutWidth.get() - drawerWidth;
|
|
349
410
|
|
|
350
411
|
return {
|
|
412
|
+
width: drawerWidth,
|
|
351
413
|
// FIXME: Reanimated skips committing to the shadow tree if no layout props are animated
|
|
352
414
|
// This results in pressables not getting their correct position and can't be pressed
|
|
353
415
|
// So we animate the zIndex to force the commit - it doesn't affect the drawer visually
|
|
354
|
-
zIndex: translateX.
|
|
416
|
+
zIndex: translateX.get() === 0 ? 0 : 1,
|
|
355
417
|
transform:
|
|
356
418
|
drawerType === 'permanent'
|
|
357
419
|
? // Reanimated needs the property to be present, but it results in Browser bug
|
|
@@ -361,7 +423,7 @@ export function Drawer({
|
|
|
361
423
|
{
|
|
362
424
|
translateX:
|
|
363
425
|
// The drawer stays in place when `drawerType` is `back`
|
|
364
|
-
(drawerType === 'back' ? 0 : translateX.
|
|
426
|
+
(drawerType === 'back' ? 0 : translateX.get()) +
|
|
365
427
|
(direction === 'rtl'
|
|
366
428
|
? drawerPosition === 'left'
|
|
367
429
|
? -distanceFromEdge
|
|
@@ -373,18 +435,23 @@ export function Drawer({
|
|
|
373
435
|
],
|
|
374
436
|
};
|
|
375
437
|
}, [
|
|
438
|
+
customWidth,
|
|
376
439
|
direction,
|
|
377
440
|
drawerPosition,
|
|
378
441
|
drawerType,
|
|
379
|
-
|
|
380
|
-
layout.width,
|
|
442
|
+
layoutWidth,
|
|
381
443
|
translateX,
|
|
382
444
|
]);
|
|
383
445
|
|
|
384
446
|
const contentAnimatedStyle = useAnimatedStyle(() => {
|
|
447
|
+
const drawerWidth = getDrawerWidthNative({
|
|
448
|
+
containerWidth: layoutWidth.get(),
|
|
449
|
+
customWidth,
|
|
450
|
+
});
|
|
451
|
+
|
|
385
452
|
return {
|
|
386
453
|
// FIXME: Force Reanimated to commit to the shadow tree
|
|
387
|
-
zIndex: translateX.
|
|
454
|
+
zIndex: translateX.get() === 0 ? 0 : drawerType === 'back' ? 2 : 1,
|
|
388
455
|
transform:
|
|
389
456
|
drawerType === 'permanent'
|
|
390
457
|
? // Reanimated needs the property to be present, but it results in Browser bug
|
|
@@ -396,19 +463,24 @@ export function Drawer({
|
|
|
396
463
|
// The screen content stays in place when `drawerType` is `front`
|
|
397
464
|
drawerType === 'front'
|
|
398
465
|
? 0
|
|
399
|
-
: translateX.
|
|
466
|
+
: translateX.get() +
|
|
400
467
|
drawerWidth * (drawerPosition === 'left' ? 1 : -1),
|
|
401
468
|
},
|
|
402
469
|
],
|
|
403
470
|
};
|
|
404
|
-
}, [drawerPosition, drawerType,
|
|
471
|
+
}, [customWidth, drawerPosition, drawerType, layoutWidth, translateX]);
|
|
405
472
|
|
|
406
473
|
const progress = useDerivedValue(() => {
|
|
474
|
+
const containerWidth = layoutWidth.get();
|
|
475
|
+
|
|
407
476
|
return drawerType === 'permanent'
|
|
408
477
|
? 1
|
|
409
478
|
: interpolate(
|
|
410
|
-
translateX.
|
|
411
|
-
[
|
|
479
|
+
translateX.get(),
|
|
480
|
+
[
|
|
481
|
+
getDrawerTranslationX(false, containerWidth),
|
|
482
|
+
getDrawerTranslationX(true, containerWidth),
|
|
483
|
+
],
|
|
412
484
|
[0, 1]
|
|
413
485
|
);
|
|
414
486
|
});
|
|
@@ -433,7 +505,11 @@ export function Drawer({
|
|
|
433
505
|
},
|
|
434
506
|
]}
|
|
435
507
|
>
|
|
436
|
-
<Animated.View
|
|
508
|
+
<Animated.View
|
|
509
|
+
ref={contentRef}
|
|
510
|
+
onLayout={onLayout}
|
|
511
|
+
style={[styles.content, contentAnimatedStyle]}
|
|
512
|
+
>
|
|
437
513
|
<View
|
|
438
514
|
aria-hidden={isOpen && drawerType !== 'permanent'}
|
|
439
515
|
style={styles.content}
|
|
@@ -455,7 +531,6 @@ export function Drawer({
|
|
|
455
531
|
style={[
|
|
456
532
|
styles.drawer,
|
|
457
533
|
{
|
|
458
|
-
width: drawerWidth,
|
|
459
534
|
position:
|
|
460
535
|
drawerType === 'permanent' ? 'relative' : 'absolute',
|
|
461
536
|
zIndex: drawerType === 'back' ? -1 : 0,
|
package/src/views/Drawer.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { StyleSheet, View } from 'react-native';
|
|
2
|
+
import { StyleSheet, View, type ViewStyle } from 'react-native';
|
|
3
3
|
import useLatestCallback from 'use-latest-callback';
|
|
4
4
|
|
|
5
5
|
import type { DrawerProps } from '../types';
|
|
@@ -23,14 +23,15 @@ export function Drawer({
|
|
|
23
23
|
children,
|
|
24
24
|
style,
|
|
25
25
|
}: DrawerProps) {
|
|
26
|
+
const flattenedDrawerStyle = StyleSheet.flatten(drawerStyle) || {};
|
|
26
27
|
const drawerWidth = getDrawerWidthWeb({
|
|
27
|
-
drawerStyle,
|
|
28
|
+
drawerStyle: flattenedDrawerStyle,
|
|
28
29
|
});
|
|
29
30
|
|
|
30
31
|
const progress = useFakeSharedValue(open ? 1 : 0);
|
|
31
32
|
|
|
32
33
|
React.useEffect(() => {
|
|
33
|
-
progress.
|
|
34
|
+
progress.set(open ? 1 : 0);
|
|
34
35
|
}, [open, progress]);
|
|
35
36
|
|
|
36
37
|
const drawerRef = React.useRef<View>(null);
|
|
@@ -99,9 +100,9 @@ export function Drawer({
|
|
|
99
100
|
position: drawerType === 'permanent' ? 'relative' : 'absolute',
|
|
100
101
|
zIndex: drawerType === 'back' ? -1 : 1,
|
|
101
102
|
},
|
|
102
|
-
//
|
|
103
|
-
{ width: drawerWidth },
|
|
104
|
-
// @ts-expect-error
|
|
103
|
+
// FIXME: width contains `px` on web
|
|
104
|
+
{ width: drawerWidth } as ViewStyle,
|
|
105
|
+
// @ts-expect-error offset contains `calc` for web
|
|
105
106
|
drawerType !== 'permanent'
|
|
106
107
|
? // Position drawer off-screen by default in closed state
|
|
107
108
|
// And add a translation only when drawer is open
|
|
@@ -114,18 +115,15 @@ export function Drawer({
|
|
|
114
115
|
drawerStyle,
|
|
115
116
|
]}
|
|
116
117
|
>
|
|
117
|
-
{
|
|
118
|
+
<Inert enabled={drawerType !== 'permanent' && !isOpen}>
|
|
119
|
+
{renderDrawerContent()}
|
|
120
|
+
</Inert>
|
|
118
121
|
</View>
|
|
119
122
|
);
|
|
120
123
|
|
|
121
124
|
const mainContent = (
|
|
122
125
|
<View key="content" style={[styles.content, contentAnimatedStyle]}>
|
|
123
|
-
<
|
|
124
|
-
aria-hidden={isOpen && drawerType !== 'permanent'}
|
|
125
|
-
style={styles.content}
|
|
126
|
-
>
|
|
127
|
-
{children}
|
|
128
|
-
</View>
|
|
126
|
+
<Inert enabled={drawerType !== 'permanent' && isOpen}>{children}</Inert>
|
|
129
127
|
{drawerType !== 'permanent' ? (
|
|
130
128
|
<Overlay
|
|
131
129
|
open={open}
|
|
@@ -149,6 +147,30 @@ export function Drawer({
|
|
|
149
147
|
);
|
|
150
148
|
}
|
|
151
149
|
|
|
150
|
+
function Inert({
|
|
151
|
+
enabled,
|
|
152
|
+
children,
|
|
153
|
+
}: {
|
|
154
|
+
enabled: boolean;
|
|
155
|
+
children: React.ReactNode;
|
|
156
|
+
}) {
|
|
157
|
+
return (
|
|
158
|
+
<div
|
|
159
|
+
inert={enabled}
|
|
160
|
+
aria-hidden={enabled}
|
|
161
|
+
style={{
|
|
162
|
+
display: 'flex',
|
|
163
|
+
flexDirection: 'column',
|
|
164
|
+
flexGrow: 1,
|
|
165
|
+
flexShrink: 1,
|
|
166
|
+
flexBasis: 'auto',
|
|
167
|
+
}}
|
|
168
|
+
>
|
|
169
|
+
{children}
|
|
170
|
+
</div>
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
152
174
|
const styles = StyleSheet.create({
|
|
153
175
|
container: {
|
|
154
176
|
flex: 1,
|
|
@@ -16,8 +16,11 @@ export function Overlay({
|
|
|
16
16
|
...rest
|
|
17
17
|
}: OverlayProps) {
|
|
18
18
|
const animatedStyle = useAnimatedStyle(() => {
|
|
19
|
+
const active = progress.value > PROGRESS_EPSILON;
|
|
20
|
+
|
|
19
21
|
return {
|
|
20
22
|
opacity: progress.value,
|
|
23
|
+
pointerEvents: active ? 'auto' : 'none',
|
|
21
24
|
};
|
|
22
25
|
}, [progress]);
|
|
23
26
|
|
|
@@ -25,7 +28,6 @@ export function Overlay({
|
|
|
25
28
|
const active = progress.value > PROGRESS_EPSILON;
|
|
26
29
|
|
|
27
30
|
return {
|
|
28
|
-
'pointerEvents': active ? 'auto' : 'none',
|
|
29
31
|
'aria-hidden': !active,
|
|
30
32
|
} as const;
|
|
31
33
|
}, [progress]);
|