@niibase/bottom-sheet-manager 1.1.0 → 1.3.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/README.md +372 -38
- package/lib/commonjs/events.js +100 -15
- package/lib/commonjs/events.js.map +1 -1
- package/lib/commonjs/index.js +7 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/manager.js +107 -29
- package/lib/commonjs/manager.js.map +1 -1
- package/lib/commonjs/provider.js +69 -28
- package/lib/commonjs/provider.js.map +1 -1
- package/lib/commonjs/router/index.js +50 -21
- package/lib/commonjs/router/index.js.map +1 -1
- package/lib/commonjs/router/router.js +137 -12
- package/lib/commonjs/router/router.js.map +1 -1
- package/lib/commonjs/router/view.js +194 -84
- package/lib/commonjs/router/view.js.map +1 -1
- package/lib/commonjs/sheet.js +125 -76
- package/lib/commonjs/sheet.js.map +1 -1
- package/lib/module/events.js +100 -15
- package/lib/module/events.js.map +1 -1
- package/lib/module/index.js +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/manager.js +108 -29
- package/lib/module/manager.js.map +1 -1
- package/lib/module/provider.js +65 -25
- package/lib/module/provider.js.map +1 -1
- package/lib/module/router/index.js +34 -18
- package/lib/module/router/index.js.map +1 -1
- package/lib/module/router/router.js +135 -11
- package/lib/module/router/router.js.map +1 -1
- package/lib/module/router/view.js +194 -84
- package/lib/module/router/view.js.map +1 -1
- package/lib/module/sheet.js +127 -78
- package/lib/module/sheet.js.map +1 -1
- package/lib/typescript/events.d.ts +46 -12
- package/lib/typescript/events.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +1 -1
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/manager.d.ts +57 -7
- package/lib/typescript/manager.d.ts.map +1 -1
- package/lib/typescript/provider.d.ts +22 -3
- package/lib/typescript/provider.d.ts.map +1 -1
- package/lib/typescript/router/index.d.ts +33 -17
- package/lib/typescript/router/index.d.ts.map +1 -1
- package/lib/typescript/router/router.d.ts +44 -5
- package/lib/typescript/router/router.d.ts.map +1 -1
- package/lib/typescript/router/types.d.ts +113 -17
- package/lib/typescript/router/types.d.ts.map +1 -1
- package/lib/typescript/router/view.d.ts +1 -1
- package/lib/typescript/router/view.d.ts.map +1 -1
- package/lib/typescript/sheet.d.ts.map +1 -1
- package/lib/typescript/types.d.ts +27 -12
- package/lib/typescript/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/events.ts +118 -27
- package/src/index.ts +6 -5
- package/src/manager.ts +156 -33
- package/src/provider.tsx +98 -44
- package/src/router/index.tsx +38 -31
- package/src/router/router.ts +184 -15
- package/src/router/types.ts +119 -22
- package/src/router/view.tsx +252 -132
- package/src/sheet.tsx +176 -95
- package/src/types.ts +144 -129
package/src/manager.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { RefObject } from "react";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { BottomSheetInstance, Sheets, StackBehavior } from "./types";
|
|
4
4
|
import { providerRegistryStack, sheetsRegistry } from "./provider";
|
|
5
|
-
import {
|
|
5
|
+
import { eventManager } from "./events";
|
|
6
6
|
|
|
7
7
|
// Array of all the ids of Sheets currently rendered in the app.
|
|
8
8
|
const ids: string[] = [];
|
|
@@ -12,14 +12,20 @@ const DEFAULT_Z_INDEX = 999;
|
|
|
12
12
|
|
|
13
13
|
const makeKey = (id: string, context: string) => `${id}:${context}`;
|
|
14
14
|
|
|
15
|
+
interface HistoryEntry {
|
|
16
|
+
id: string;
|
|
17
|
+
context: string;
|
|
18
|
+
behavior: StackBehavior;
|
|
19
|
+
}
|
|
20
|
+
|
|
15
21
|
export const PrivateManager = {
|
|
16
|
-
//
|
|
17
|
-
history: [] as
|
|
22
|
+
// Stack of sheet history for restoration when sheets are closed
|
|
23
|
+
history: [] as HistoryEntry[],
|
|
18
24
|
|
|
19
25
|
context(options?: { context?: string; id?: string }) {
|
|
20
26
|
if (!options) options = {};
|
|
21
27
|
if (!options?.context) {
|
|
22
|
-
// If no context is provided, use
|
|
28
|
+
// If no context is provided, use the current top-most context
|
|
23
29
|
// to render the sheet.
|
|
24
30
|
for (const context of providerRegistryStack.slice().reverse()) {
|
|
25
31
|
// We only automatically select nested sheet providers.
|
|
@@ -42,11 +48,12 @@ export const PrivateManager = {
|
|
|
42
48
|
) => {
|
|
43
49
|
const key = makeKey(id, context);
|
|
44
50
|
refs[key] = instance;
|
|
45
|
-
keys.
|
|
51
|
+
if (!keys.includes(key)) {
|
|
52
|
+
keys.push(key);
|
|
53
|
+
}
|
|
46
54
|
},
|
|
47
55
|
|
|
48
56
|
/**
|
|
49
|
-
*
|
|
50
57
|
* Get internal ref of a sheet by the given id.
|
|
51
58
|
*
|
|
52
59
|
* @param id Id of the sheet
|
|
@@ -57,8 +64,8 @@ export const PrivateManager = {
|
|
|
57
64
|
context?: string,
|
|
58
65
|
): RefObject<BottomSheetInstance<SheetId>> => {
|
|
59
66
|
if (!context) {
|
|
60
|
-
for (
|
|
61
|
-
for (
|
|
67
|
+
for (const ctx of providerRegistryStack.slice().reverse()) {
|
|
68
|
+
for (const _id in sheetsRegistry[ctx]) {
|
|
62
69
|
if (_id === id) {
|
|
63
70
|
context = ctx;
|
|
64
71
|
break;
|
|
@@ -70,14 +77,17 @@ export const PrivateManager = {
|
|
|
70
77
|
},
|
|
71
78
|
|
|
72
79
|
add: (id: string, context: string) => {
|
|
73
|
-
|
|
74
|
-
|
|
80
|
+
const key = makeKey(id, context);
|
|
81
|
+
if (!ids.includes(key)) {
|
|
82
|
+
ids.push(key);
|
|
75
83
|
}
|
|
76
84
|
},
|
|
77
85
|
|
|
78
86
|
remove: (id: string, context: string) => {
|
|
79
|
-
|
|
80
|
-
|
|
87
|
+
const key = makeKey(id, context);
|
|
88
|
+
const index = ids.indexOf(key);
|
|
89
|
+
if (index > -1) {
|
|
90
|
+
ids.splice(index, 1);
|
|
81
91
|
}
|
|
82
92
|
},
|
|
83
93
|
|
|
@@ -87,12 +97,39 @@ export const PrivateManager = {
|
|
|
87
97
|
},
|
|
88
98
|
|
|
89
99
|
stack: () =>
|
|
90
|
-
ids.map((id) => {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
100
|
+
ids.map((id) => ({
|
|
101
|
+
id: id.split(":")[0],
|
|
102
|
+
context: id.split(":")?.[1] || "global",
|
|
103
|
+
})),
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Get the top-most sheet in the stack
|
|
107
|
+
*/
|
|
108
|
+
topSheet: () => {
|
|
109
|
+
if (ids.length === 0) return null;
|
|
110
|
+
const topId = ids[ids.length - 1];
|
|
111
|
+
return {
|
|
112
|
+
id: topId.split(":")[0],
|
|
113
|
+
context: topId.split(":")?.[1] || "global",
|
|
114
|
+
};
|
|
115
|
+
},
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Check if a sheet is currently visible
|
|
119
|
+
*/
|
|
120
|
+
isSheetVisible: (id: string, context?: string): boolean => {
|
|
121
|
+
if (context) {
|
|
122
|
+
return ids.includes(makeKey(id, context));
|
|
123
|
+
}
|
|
124
|
+
return ids.some((key) => key.startsWith(`${id}:`));
|
|
125
|
+
},
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Clear all history entries
|
|
129
|
+
*/
|
|
130
|
+
clearHistory: () => {
|
|
131
|
+
PrivateManager.history = [];
|
|
132
|
+
},
|
|
96
133
|
};
|
|
97
134
|
|
|
98
135
|
class _SheetManager {
|
|
@@ -119,36 +156,67 @@ class _SheetManager {
|
|
|
119
156
|
* Provide `context` of the `SheetProvider` where you want to show the action sheet.
|
|
120
157
|
*/
|
|
121
158
|
context?: string;
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Stack behavior for this sheet.
|
|
162
|
+
* - `switch`: (default) Closes current sheet, shows new one
|
|
163
|
+
* - `replace`: Swaps content with crossfade animation
|
|
164
|
+
* - `push`: Stacks new sheet on top of current
|
|
165
|
+
*/
|
|
166
|
+
stackBehavior?: StackBehavior;
|
|
122
167
|
},
|
|
123
168
|
): Promise<Sheets[SheetId]["returnValue"]> {
|
|
124
169
|
return new Promise((resolve) => {
|
|
125
170
|
const currentContext = PrivateManager.context({ ...options, id: id });
|
|
126
|
-
const
|
|
171
|
+
const behavior = options?.stackBehavior ?? "switch";
|
|
172
|
+
|
|
173
|
+
const handler = (
|
|
174
|
+
data: unknown,
|
|
175
|
+
context = "global",
|
|
176
|
+
_reopened?: boolean,
|
|
177
|
+
_behavior?: StackBehavior,
|
|
178
|
+
) => {
|
|
127
179
|
if (context !== "global" && currentContext && currentContext !== context)
|
|
128
180
|
return;
|
|
129
|
-
options?.onClose?.(data);
|
|
181
|
+
options?.onClose?.(data as Sheets[SheetId]["returnValue"]);
|
|
130
182
|
sub?.unsubscribe();
|
|
131
|
-
resolve(data);
|
|
183
|
+
resolve(data as Sheets[SheetId]["returnValue"]);
|
|
132
184
|
};
|
|
133
185
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
186
|
+
const sub = eventManager.subscribe(`onclose_${id}`, handler);
|
|
187
|
+
|
|
188
|
+
// Handle existing sheets based on stack behavior
|
|
189
|
+
const currentStack = PrivateManager.stack();
|
|
190
|
+
if (currentStack.length > 0) {
|
|
191
|
+
currentStack.forEach(({ id: sheetId, context }) => {
|
|
192
|
+
eventManager.publish(
|
|
193
|
+
`hide_${sheetId}`,
|
|
194
|
+
undefined,
|
|
195
|
+
context,
|
|
196
|
+
true,
|
|
197
|
+
behavior,
|
|
198
|
+
);
|
|
199
|
+
});
|
|
200
|
+
}
|
|
138
201
|
|
|
139
202
|
// Check if the sheet is registered with any `SheetProviders`.
|
|
140
203
|
let isRegisteredWithSheetProvider = false;
|
|
141
|
-
for (
|
|
142
|
-
for (
|
|
204
|
+
for (const ctx in sheetsRegistry) {
|
|
205
|
+
for (const _id in sheetsRegistry[ctx]) {
|
|
143
206
|
if (_id === id) {
|
|
144
207
|
isRegisteredWithSheetProvider = true;
|
|
208
|
+
break;
|
|
145
209
|
}
|
|
146
210
|
}
|
|
211
|
+
if (isRegisteredWithSheetProvider) break;
|
|
147
212
|
}
|
|
213
|
+
|
|
148
214
|
eventManager.publish(
|
|
149
215
|
isRegisteredWithSheetProvider ? `show_wrap_${id}` : `show_${id}`,
|
|
150
216
|
options?.payload,
|
|
151
217
|
currentContext || "global",
|
|
218
|
+
false,
|
|
219
|
+
behavior,
|
|
152
220
|
);
|
|
153
221
|
});
|
|
154
222
|
}
|
|
@@ -157,7 +225,7 @@ class _SheetManager {
|
|
|
157
225
|
* An async hide function. This is useful when you want to show one Sheet after closing another.
|
|
158
226
|
*
|
|
159
227
|
* @param id id of the Sheet to show
|
|
160
|
-
* @param
|
|
228
|
+
* @param options
|
|
161
229
|
*/
|
|
162
230
|
async hide<SheetId extends keyof Sheets>(
|
|
163
231
|
id: SheetId | (string & {}),
|
|
@@ -172,10 +240,11 @@ class _SheetManager {
|
|
|
172
240
|
context?: string;
|
|
173
241
|
},
|
|
174
242
|
): Promise<Sheets[SheetId]["returnValue"]> {
|
|
175
|
-
|
|
243
|
+
const currentContext = PrivateManager.context({
|
|
176
244
|
...options,
|
|
177
245
|
id: id,
|
|
178
246
|
});
|
|
247
|
+
|
|
179
248
|
return new Promise((resolve) => {
|
|
180
249
|
let isRegisteredWithSheetProvider = false;
|
|
181
250
|
// Check if the sheet is registered with any `SheetProviders`
|
|
@@ -188,14 +257,14 @@ class _SheetManager {
|
|
|
188
257
|
}
|
|
189
258
|
}
|
|
190
259
|
|
|
191
|
-
const hideHandler = (data:
|
|
260
|
+
const hideHandler = (data: unknown, context = "global") => {
|
|
192
261
|
if (context !== "global" && currentContext && currentContext !== context)
|
|
193
262
|
return;
|
|
194
263
|
sub?.unsubscribe();
|
|
195
|
-
resolve(data);
|
|
264
|
+
resolve(data as Sheets[SheetId]["returnValue"]);
|
|
196
265
|
};
|
|
197
266
|
|
|
198
|
-
|
|
267
|
+
const sub = eventManager.subscribe(`onclose_${id}`, hideHandler);
|
|
199
268
|
eventManager.publish(
|
|
200
269
|
isRegisteredWithSheetProvider ? `hide_wrap_${id}` : `hide_${id}`,
|
|
201
270
|
options?.payload,
|
|
@@ -210,11 +279,65 @@ class _SheetManager {
|
|
|
210
279
|
* @param id Hide all sheets for the specific id.
|
|
211
280
|
*/
|
|
212
281
|
hideAll<SheetId extends keyof Sheets>(id?: SheetId | (string & {})) {
|
|
282
|
+
// Clear history when hiding all sheets
|
|
283
|
+
PrivateManager.clearHistory();
|
|
284
|
+
|
|
213
285
|
PrivateManager.stack().forEach(({ id: _id, context }) => {
|
|
214
286
|
if (id && !_id.startsWith(id)) return;
|
|
215
287
|
eventManager.publish(`hide_${_id}`, undefined, context);
|
|
216
288
|
});
|
|
217
289
|
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Replace the current sheet with a new one using crossfade animation.
|
|
293
|
+
* This is a convenience method for show() with stackBehavior: 'replace'.
|
|
294
|
+
*/
|
|
295
|
+
async replace<SheetId extends keyof Sheets>(
|
|
296
|
+
id: SheetId | (string & {}),
|
|
297
|
+
options?: {
|
|
298
|
+
payload?: Sheets[SheetId]["payload"];
|
|
299
|
+
onClose?: (data: Sheets[SheetId]["returnValue"] | undefined) => void;
|
|
300
|
+
context?: string;
|
|
301
|
+
},
|
|
302
|
+
): Promise<Sheets[SheetId]["returnValue"]> {
|
|
303
|
+
return this.show(id, { ...options, stackBehavior: "replace" });
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Push a new sheet on top of the current one, creating a stack.
|
|
308
|
+
* This is a convenience method for show() with stackBehavior: 'push'.
|
|
309
|
+
*/
|
|
310
|
+
async push<SheetId extends keyof Sheets>(
|
|
311
|
+
id: SheetId | (string & {}),
|
|
312
|
+
options?: {
|
|
313
|
+
payload?: Sheets[SheetId]["payload"];
|
|
314
|
+
onClose?: (data: Sheets[SheetId]["returnValue"] | undefined) => void;
|
|
315
|
+
context?: string;
|
|
316
|
+
},
|
|
317
|
+
): Promise<Sheets[SheetId]["returnValue"]> {
|
|
318
|
+
return this.show(id, { ...options, stackBehavior: "push" });
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Pop the top sheet from the stack and restore the previous one.
|
|
323
|
+
* Only works when sheets were opened with stackBehavior: 'push'.
|
|
324
|
+
*/
|
|
325
|
+
pop(): void {
|
|
326
|
+
const topSheet = PrivateManager.topSheet();
|
|
327
|
+
if (topSheet) {
|
|
328
|
+
eventManager.publish(`hide_${topSheet.id}`, undefined, topSheet.context);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Check if a specific sheet is currently visible.
|
|
334
|
+
*/
|
|
335
|
+
isVisible<SheetId extends keyof Sheets>(
|
|
336
|
+
id: SheetId | (string & {}),
|
|
337
|
+
context?: string,
|
|
338
|
+
): boolean {
|
|
339
|
+
return PrivateManager.isSheetVisible(id, context);
|
|
340
|
+
}
|
|
218
341
|
}
|
|
219
342
|
|
|
220
343
|
/**
|
package/src/provider.tsx
CHANGED
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
2
|
-
import { BottomSheetModalProvider } from "@gorhom/bottom-sheet";
|
|
3
|
-
import { StatusBar } from "react-native";
|
|
4
|
-
import React from "react";
|
|
5
1
|
import Animated, {
|
|
6
2
|
interpolate,
|
|
7
3
|
interpolateColor,
|
|
@@ -11,10 +7,13 @@ import Animated, {
|
|
|
11
7
|
useAnimatedStyle,
|
|
12
8
|
useSharedValue,
|
|
13
9
|
withSpring,
|
|
14
|
-
withTiming,
|
|
15
10
|
} from "react-native-reanimated";
|
|
11
|
+
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
12
|
+
import { BottomSheetModalProvider } from "@gorhom/bottom-sheet";
|
|
13
|
+
import { StatusBar } from "react-native";
|
|
14
|
+
import React from "react";
|
|
16
15
|
|
|
17
|
-
import { BottomSheetInstance, SheetPayload, Sheets } from "./types";
|
|
16
|
+
import { BottomSheetInstance, SheetPayload, Sheets, StackBehavior } from "./types";
|
|
18
17
|
import { eventManager } from "./events";
|
|
19
18
|
|
|
20
19
|
export const providerRegistryStack: string[] = [];
|
|
@@ -50,6 +49,15 @@ export function registerSheet<SheetId extends keyof Sheets = never>(
|
|
|
50
49
|
}
|
|
51
50
|
}
|
|
52
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Animation configuration for iOS modal sheet style animations.
|
|
54
|
+
* @deprecated Use duration prop directly instead
|
|
55
|
+
*/
|
|
56
|
+
export interface ModalSheetAnimationConfig {
|
|
57
|
+
/** Duration of the animation in milliseconds */
|
|
58
|
+
duration: number;
|
|
59
|
+
}
|
|
60
|
+
|
|
53
61
|
/**
|
|
54
62
|
* The SheetProvider makes available the sheets in a given context. The default context is
|
|
55
63
|
* `global`. However if you want to render a Sheet within another sheet or if you want to render
|
|
@@ -71,7 +79,7 @@ export function registerSheet<SheetId extends keyof Sheets = never>(
|
|
|
71
79
|
export function SheetProvider({
|
|
72
80
|
iosModalSheetTypeOfAnimation = false,
|
|
73
81
|
context = "global",
|
|
74
|
-
duration =
|
|
82
|
+
duration = 150,
|
|
75
83
|
children,
|
|
76
84
|
}: React.PropsWithChildren<{
|
|
77
85
|
context?: string;
|
|
@@ -82,17 +90,13 @@ export function SheetProvider({
|
|
|
82
90
|
const [, forceUpdate] = React.useReducer((x) => x + 1, 0);
|
|
83
91
|
const sheetIds = Object.keys(sheetsRegistry[context] || sheetsRegistry["global"] || {});
|
|
84
92
|
|
|
85
|
-
// Rerender when a new sheet is added.
|
|
86
|
-
const onRegister = React.useCallback(forceUpdate, [forceUpdate]);
|
|
87
|
-
|
|
88
93
|
// IOS modal sheet type of animation
|
|
89
94
|
const isFullScreen = useSharedValue(-1);
|
|
90
95
|
const colorStyle = useAnimatedStyle(() => ({
|
|
91
96
|
flex: 1,
|
|
92
|
-
backgroundColor:
|
|
93
|
-
isFullScreen.value,
|
|
94
|
-
|
|
95
|
-
["transparent", "#000"],
|
|
97
|
+
backgroundColor: withSpring(
|
|
98
|
+
interpolateColor(isFullScreen.value, [0, 1], ["transparent", "#000"]),
|
|
99
|
+
{ duration },
|
|
96
100
|
),
|
|
97
101
|
}));
|
|
98
102
|
const animatedStyle = useAnimatedStyle(
|
|
@@ -102,14 +106,14 @@ export function SheetProvider({
|
|
|
102
106
|
borderRadius: interpolate(isFullScreen.value, [0, 0.8, 1], [0, 20, 24], "clamp"),
|
|
103
107
|
transform: [
|
|
104
108
|
{
|
|
105
|
-
scaleX:
|
|
106
|
-
interpolate(isFullScreen.value, [0, 0.
|
|
109
|
+
scaleX: withSpring(
|
|
110
|
+
interpolate(isFullScreen.value, [0, 0.8], [1, 0.92], "clamp"),
|
|
107
111
|
{ duration },
|
|
108
112
|
),
|
|
109
113
|
},
|
|
110
114
|
{
|
|
111
115
|
translateY: withSpring(
|
|
112
|
-
interpolate(isFullScreen.value, [0, 0.
|
|
116
|
+
interpolate(isFullScreen.value, [0, 0.8, 1], [0, top, top + 5], "clamp"),
|
|
113
117
|
{ duration, dampingRatio: 1.5 },
|
|
114
118
|
),
|
|
115
119
|
},
|
|
@@ -135,23 +139,23 @@ export function SheetProvider({
|
|
|
135
139
|
providerRegistryStack.indexOf(context) > -1
|
|
136
140
|
? providerRegistryStack.indexOf(context)
|
|
137
141
|
: providerRegistryStack.push(context) - 1;
|
|
138
|
-
const unsub = eventManager.subscribe(`${context}-on-register`,
|
|
142
|
+
const unsub = eventManager.subscribe(`${context}-on-register`, forceUpdate);
|
|
139
143
|
return () => {
|
|
140
144
|
providerRegistryStack.splice(providerRegistryStack.indexOf(context), 1);
|
|
141
145
|
unsub?.unsubscribe();
|
|
142
146
|
};
|
|
143
|
-
}, [context,
|
|
147
|
+
}, [context, forceUpdate]);
|
|
144
148
|
|
|
145
149
|
return (
|
|
146
150
|
<SheetAnimationContext.Provider
|
|
147
|
-
value={{ isFullScreen, iosModalSheetTypeOfAnimation }}
|
|
151
|
+
value={{ isFullScreen, iosModalSheetTypeOfAnimation, duration }}
|
|
148
152
|
>
|
|
149
153
|
<Animated.View style={colorStyle}>
|
|
150
154
|
<Animated.View style={animatedStyle}>{children}</Animated.View>
|
|
151
155
|
</Animated.View>
|
|
152
156
|
<BottomSheetModalProvider>
|
|
153
157
|
{sheetIds.map((id) => (
|
|
154
|
-
<RenderSheet key={id} id={id} context={context}
|
|
158
|
+
<RenderSheet key={id} id={id} context={context} />
|
|
155
159
|
))}
|
|
156
160
|
</BottomSheetModalProvider>
|
|
157
161
|
</SheetAnimationContext.Provider>
|
|
@@ -162,13 +166,31 @@ const SheetIDContext = React.createContext<string | undefined>(undefined);
|
|
|
162
166
|
const SheetAnimationContext = React.createContext<{
|
|
163
167
|
iosModalSheetTypeOfAnimation: boolean;
|
|
164
168
|
isFullScreen: SharedValue<number>;
|
|
165
|
-
|
|
169
|
+
duration: number;
|
|
170
|
+
}>({
|
|
171
|
+
isFullScreen: { value: 0 } as SharedValue<number>,
|
|
172
|
+
iosModalSheetTypeOfAnimation: false,
|
|
173
|
+
duration: 300,
|
|
174
|
+
});
|
|
166
175
|
|
|
167
176
|
export const SheetRefContext = React.createContext<
|
|
168
177
|
React.RefObject<BottomSheetInstance | null>
|
|
169
|
-
>({} as
|
|
178
|
+
>({} as React.RefObject<BottomSheetInstance | null>);
|
|
170
179
|
|
|
171
|
-
const SheetPayloadContext = React.createContext<
|
|
180
|
+
const SheetPayloadContext = React.createContext<unknown>(undefined);
|
|
181
|
+
|
|
182
|
+
// Stack behavior context for managing sheet transitions
|
|
183
|
+
interface StackBehaviorContextValue {
|
|
184
|
+
behavior: StackBehavior;
|
|
185
|
+
isTransitioning: boolean;
|
|
186
|
+
previousSheetId: string | null;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const StackBehaviorContext = React.createContext<StackBehaviorContextValue>({
|
|
190
|
+
behavior: "switch",
|
|
191
|
+
isTransitioning: false,
|
|
192
|
+
previousSheetId: null,
|
|
193
|
+
});
|
|
172
194
|
|
|
173
195
|
/**
|
|
174
196
|
* Get id of the current context.
|
|
@@ -182,6 +204,10 @@ export const useSheetIDContext = () => React.useContext(SheetIDContext);
|
|
|
182
204
|
* Get the current sheet animation context.
|
|
183
205
|
*/
|
|
184
206
|
export const useSheetAnimationContext = () => React.useContext(SheetAnimationContext);
|
|
207
|
+
/**
|
|
208
|
+
* Get stack behavior context for the current sheet.
|
|
209
|
+
*/
|
|
210
|
+
export const useStackBehaviorContext = () => React.useContext(StackBehaviorContext);
|
|
185
211
|
/**
|
|
186
212
|
* Get the current Sheet's internal ref.
|
|
187
213
|
*/
|
|
@@ -205,25 +231,26 @@ export function useSheetPayload<SheetId extends keyof Sheets = never>() {
|
|
|
205
231
|
export function useOnSheet<SheetId extends keyof Sheets = never>(
|
|
206
232
|
id: SheetId | (string & {}),
|
|
207
233
|
type: "show" | "hide" | "onclose",
|
|
208
|
-
listener: (payload: SheetPayload<SheetId>, context: string, ...args:
|
|
234
|
+
listener: (payload: SheetPayload<SheetId>, context: string, ...args: unknown[]) => void,
|
|
209
235
|
) {
|
|
210
236
|
React.useEffect(() => {
|
|
211
237
|
const subscription = eventManager.subscribe(`${type}_${id}`, listener);
|
|
212
238
|
return () => subscription.unsubscribe();
|
|
213
|
-
}, [id, listener]);
|
|
239
|
+
}, [id, listener, type]);
|
|
214
240
|
}
|
|
215
241
|
|
|
216
|
-
|
|
217
|
-
id,
|
|
218
|
-
context,
|
|
219
|
-
duration,
|
|
220
|
-
}: {
|
|
242
|
+
interface RenderSheetProps {
|
|
221
243
|
id: string;
|
|
222
244
|
context: string;
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const RenderSheet = ({ id, context }: RenderSheetProps) => {
|
|
248
|
+
const [payload, setPayload] = React.useState<unknown>();
|
|
226
249
|
const [visible, setVisible] = React.useState(false);
|
|
250
|
+
const [stackBehavior, setStackBehavior] = React.useState<StackBehavior>("switch");
|
|
251
|
+
const [isPending, startTransition] = React.useTransition();
|
|
252
|
+
const [previousSheetId, setPreviousSheetId] = React.useState<string | null>(null);
|
|
253
|
+
|
|
227
254
|
const ref = React.useRef<BottomSheetInstance | null>(null);
|
|
228
255
|
const Sheet = context.startsWith("$$-auto-")
|
|
229
256
|
? sheetsRegistry?.global?.[id]
|
|
@@ -232,29 +259,46 @@ const RenderSheet = ({
|
|
|
232
259
|
: undefined;
|
|
233
260
|
|
|
234
261
|
const onShow = React.useCallback(
|
|
235
|
-
(data:
|
|
262
|
+
(data: unknown, ctx = "global", reopened?: boolean, behavior?: StackBehavior) => {
|
|
236
263
|
if (ctx !== context) return;
|
|
237
|
-
|
|
238
|
-
|
|
264
|
+
|
|
265
|
+
if (behavior) {
|
|
266
|
+
setStackBehavior(behavior);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if (!reopened) {
|
|
270
|
+
setPayload(data);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Smooth transition handling using React's useTransition
|
|
274
|
+
startTransition(() => {
|
|
275
|
+
setVisible(true);
|
|
276
|
+
});
|
|
239
277
|
},
|
|
240
278
|
[context],
|
|
241
279
|
);
|
|
242
280
|
|
|
243
281
|
const onClose = React.useCallback(
|
|
244
|
-
(_data:
|
|
282
|
+
(_data: unknown, ctx = "global", reopened?: boolean, nextSheetId?: string) => {
|
|
245
283
|
if (context !== ctx) return;
|
|
284
|
+
|
|
285
|
+
if (nextSheetId) {
|
|
286
|
+
setPreviousSheetId(nextSheetId);
|
|
287
|
+
}
|
|
288
|
+
|
|
246
289
|
if (!reopened) {
|
|
247
290
|
setPayload(undefined);
|
|
248
|
-
|
|
291
|
+
setVisible(false);
|
|
249
292
|
} else {
|
|
250
293
|
setVisible(false);
|
|
294
|
+
setPreviousSheetId(null);
|
|
251
295
|
}
|
|
252
296
|
},
|
|
253
297
|
[context],
|
|
254
298
|
);
|
|
255
299
|
|
|
256
300
|
const onHide = React.useCallback(
|
|
257
|
-
(data:
|
|
301
|
+
(data: unknown, ctx = "global") => {
|
|
258
302
|
eventManager.publish(`hide_${id}`, data, ctx);
|
|
259
303
|
},
|
|
260
304
|
[id],
|
|
@@ -267,7 +311,7 @@ const RenderSheet = ({
|
|
|
267
311
|
}, [context, id, payload, visible]);
|
|
268
312
|
|
|
269
313
|
React.useEffect(() => {
|
|
270
|
-
|
|
314
|
+
const subs = [
|
|
271
315
|
eventManager.subscribe(`show_wrap_${id}`, onShow),
|
|
272
316
|
eventManager.subscribe(`onclose_${id}`, onClose),
|
|
273
317
|
eventManager.subscribe(`hide_wrap_${id}`, onHide),
|
|
@@ -279,15 +323,25 @@ const RenderSheet = ({
|
|
|
279
323
|
|
|
280
324
|
if (!Sheet) return null;
|
|
281
325
|
|
|
282
|
-
|
|
326
|
+
const stackContextValue: StackBehaviorContextValue = {
|
|
327
|
+
behavior: stackBehavior,
|
|
328
|
+
isTransitioning: isPending,
|
|
329
|
+
previousSheetId,
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
if (!visible) return null;
|
|
333
|
+
|
|
334
|
+
return (
|
|
283
335
|
<ProviderContext.Provider value={context}>
|
|
284
336
|
<SheetIDContext.Provider value={id}>
|
|
285
337
|
<SheetRefContext.Provider value={ref}>
|
|
286
338
|
<SheetPayloadContext.Provider value={payload}>
|
|
287
|
-
<
|
|
339
|
+
<StackBehaviorContext.Provider value={stackContextValue}>
|
|
340
|
+
<Sheet id={id} payload={payload} context={context} />
|
|
341
|
+
</StackBehaviorContext.Provider>
|
|
288
342
|
</SheetPayloadContext.Provider>
|
|
289
343
|
</SheetRefContext.Provider>
|
|
290
344
|
</SheetIDContext.Provider>
|
|
291
345
|
</ProviderContext.Provider>
|
|
292
|
-
)
|
|
346
|
+
);
|
|
293
347
|
};
|