react-native-bottom-sheet-stack 1.6.0 → 1.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/README.md +9 -7
- package/lib/commonjs/BottomSheet.context.js +1 -2
- package/lib/commonjs/BottomSheet.context.js.map +1 -1
- package/lib/commonjs/BottomSheetBackdrop.js +23 -32
- package/lib/commonjs/BottomSheetBackdrop.js.map +1 -1
- package/lib/commonjs/BottomSheetHost.js +17 -254
- package/lib/commonjs/BottomSheetHost.js.map +1 -1
- package/lib/commonjs/BottomSheetManaged.js +87 -54
- package/lib/commonjs/BottomSheetManaged.js.map +1 -1
- package/lib/commonjs/BottomSheetPersistent.js +113 -0
- package/lib/commonjs/BottomSheetPersistent.js.map +1 -0
- package/lib/commonjs/BottomSheetPortal.js +4 -3
- package/lib/commonjs/BottomSheetPortal.js.map +1 -1
- package/lib/commonjs/BottomSheetRef.context.js +17 -0
- package/lib/commonjs/BottomSheetRef.context.js.map +1 -0
- package/lib/commonjs/QueueItem.js +167 -0
- package/lib/commonjs/QueueItem.js.map +1 -0
- package/lib/commonjs/animatedRegistry.js +9 -0
- package/lib/commonjs/animatedRegistry.js.map +1 -1
- package/lib/commonjs/bottomSheet.store.js +11 -133
- package/lib/commonjs/bottomSheet.store.js.map +1 -1
- package/lib/commonjs/bottomSheetCoordinator.js +9 -10
- package/lib/commonjs/bottomSheetCoordinator.js.map +1 -1
- package/lib/commonjs/index.js +28 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/portalSessionRegistry.js +32 -0
- package/lib/commonjs/portalSessionRegistry.js.map +1 -0
- package/lib/commonjs/refsMap.js +9 -0
- package/lib/commonjs/refsMap.js.map +1 -1
- package/lib/commonjs/store/helpers.js +59 -0
- package/lib/commonjs/store/helpers.js.map +1 -0
- package/lib/commonjs/store/hooks.js +176 -0
- package/lib/commonjs/store/hooks.js.map +1 -0
- package/lib/commonjs/store/index.js +52 -0
- package/lib/commonjs/store/index.js.map +1 -0
- package/lib/commonjs/store/store.js +140 -0
- package/lib/commonjs/store/store.js.map +1 -0
- package/lib/commonjs/store/types.js +6 -0
- package/lib/commonjs/store/types.js.map +1 -0
- package/lib/commonjs/useBottomSheetContext.js +24 -42
- package/lib/commonjs/useBottomSheetContext.js.map +1 -1
- package/lib/commonjs/useBottomSheetControl.js +8 -14
- package/lib/commonjs/useBottomSheetControl.js.map +1 -1
- package/lib/commonjs/useBottomSheetManager.js +3 -13
- package/lib/commonjs/useBottomSheetManager.js.map +1 -1
- package/lib/commonjs/useBottomSheetStatus.js +9 -17
- package/lib/commonjs/useBottomSheetStatus.js.map +1 -1
- package/lib/commonjs/useEvent.js +39 -0
- package/lib/commonjs/useEvent.js.map +1 -0
- package/lib/commonjs/useScaleAnimation.js +53 -30
- package/lib/commonjs/useScaleAnimation.js.map +1 -1
- package/lib/commonjs/useSheetRenderData.js +62 -0
- package/lib/commonjs/useSheetRenderData.js.map +1 -0
- package/lib/typescript/example/src/App.d.ts.map +1 -1
- package/lib/typescript/example/src/screens/HomeScreen.d.ts.map +1 -1
- package/lib/typescript/example/src/sheets/NavigationSheets.d.ts.map +1 -1
- package/lib/typescript/example/src/sheets/ScannerNestedSheets.d.ts +4 -0
- package/lib/typescript/example/src/sheets/ScannerNestedSheets.d.ts.map +1 -0
- package/lib/typescript/example/src/sheets/ScannerSheet.d.ts +3 -0
- package/lib/typescript/example/src/sheets/ScannerSheet.d.ts.map +1 -0
- package/lib/typescript/example/src/sheets/index.d.ts +1 -0
- package/lib/typescript/example/src/sheets/index.d.ts.map +1 -1
- package/lib/typescript/src/BottomSheet.context.d.ts.map +1 -1
- package/lib/typescript/src/BottomSheetBackdrop.d.ts +0 -5
- package/lib/typescript/src/BottomSheetBackdrop.d.ts.map +1 -1
- package/lib/typescript/src/BottomSheetHost.d.ts +1 -3
- package/lib/typescript/src/BottomSheetHost.d.ts.map +1 -1
- package/lib/typescript/src/BottomSheetManaged.d.ts.map +1 -1
- package/lib/typescript/src/BottomSheetPersistent.d.ts +9 -0
- package/lib/typescript/src/BottomSheetPersistent.d.ts.map +1 -0
- package/lib/typescript/src/BottomSheetPortal.d.ts.map +1 -1
- package/lib/typescript/src/BottomSheetRef.context.d.ts +11 -0
- package/lib/typescript/src/BottomSheetRef.context.d.ts.map +1 -0
- package/lib/typescript/src/QueueItem.d.ts +8 -0
- package/lib/typescript/src/QueueItem.d.ts.map +1 -0
- package/lib/typescript/src/animatedRegistry.d.ts +5 -0
- package/lib/typescript/src/animatedRegistry.d.ts.map +1 -1
- package/lib/typescript/src/bottomSheet.store.d.ts +1 -37
- package/lib/typescript/src/bottomSheet.store.d.ts.map +1 -1
- package/lib/typescript/src/bottomSheetCoordinator.d.ts +2 -1
- package/lib/typescript/src/bottomSheetCoordinator.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +5 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/portalSessionRegistry.d.ts +8 -0
- package/lib/typescript/src/portalSessionRegistry.d.ts.map +1 -0
- package/lib/typescript/src/refsMap.d.ts +5 -0
- package/lib/typescript/src/refsMap.d.ts.map +1 -1
- package/lib/typescript/src/store/helpers.d.ts +11 -0
- package/lib/typescript/src/store/helpers.d.ts.map +1 -0
- package/lib/typescript/src/store/hooks.d.ts +16 -0
- package/lib/typescript/src/store/hooks.d.ts.map +1 -0
- package/lib/typescript/src/store/index.d.ts +5 -0
- package/lib/typescript/src/store/index.d.ts.map +1 -0
- package/lib/typescript/src/store/store.d.ts +11 -0
- package/lib/typescript/src/store/store.d.ts.map +1 -0
- package/lib/typescript/src/store/types.d.ts +37 -0
- package/lib/typescript/src/store/types.d.ts.map +1 -0
- package/lib/typescript/src/useBottomSheetContext.d.ts.map +1 -1
- package/lib/typescript/src/useBottomSheetControl.d.ts.map +1 -1
- package/lib/typescript/src/useBottomSheetManager.d.ts.map +1 -1
- package/lib/typescript/src/useBottomSheetStatus.d.ts +1 -2
- package/lib/typescript/src/useBottomSheetStatus.d.ts.map +1 -1
- package/lib/typescript/src/useEvent.d.ts +4 -0
- package/lib/typescript/src/useEvent.d.ts.map +1 -0
- package/lib/typescript/src/useScaleAnimation.d.ts +10 -13
- package/lib/typescript/src/useScaleAnimation.d.ts.map +1 -1
- package/lib/typescript/src/useSheetRenderData.d.ts +17 -0
- package/lib/typescript/src/useSheetRenderData.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/BottomSheet.context.ts +1 -3
- package/src/BottomSheetBackdrop.tsx +10 -19
- package/src/BottomSheetHost.tsx +13 -99
- package/src/BottomSheetManaged.tsx +24 -2
- package/src/BottomSheetPersistent.tsx +57 -0
- package/src/BottomSheetPortal.tsx +5 -7
- package/src/BottomSheetRef.context.ts +14 -0
- package/src/QueueItem.tsx +83 -0
- package/src/animatedRegistry.ts +8 -0
- package/src/bottomSheet.store.ts +1 -173
- package/src/bottomSheetCoordinator.ts +10 -8
- package/src/index.tsx +7 -1
- package/src/portalSessionRegistry.ts +25 -0
- package/src/refsMap.ts +8 -0
- package/src/store/helpers.ts +65 -0
- package/src/store/hooks.ts +50 -0
- package/src/store/index.ts +4 -0
- package/src/store/store.ts +168 -0
- package/src/store/types.ts +42 -0
- package/src/useBottomSheetContext.ts +6 -15
- package/src/useBottomSheetControl.ts +16 -7
- package/src/useBottomSheetManager.tsx +9 -10
- package/src/useBottomSheetStatus.ts +4 -14
- package/src/useEvent.ts +17 -0
- package/src/useScaleAnimation.ts +67 -35
- package/src/useSheetRenderData.ts +74 -0
|
@@ -43,19 +43,20 @@ export function createSheetEventHandlers(sheetId: string) {
|
|
|
43
43
|
const { startClosing, finishClosing, markOpen } =
|
|
44
44
|
useBottomSheetStore.getState();
|
|
45
45
|
|
|
46
|
-
const handleAnimate = (
|
|
46
|
+
const handleAnimate = (_fromIndex: number, toIndex: number) => {
|
|
47
47
|
const state = useBottomSheetStore.getState();
|
|
48
48
|
const currentStatus = state.sheetsById[sheetId]?.status;
|
|
49
49
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
if (currentStatus === 'open' || currentStatus === 'opening') {
|
|
53
|
-
startClosing(sheetId);
|
|
54
|
-
}
|
|
50
|
+
if (toIndex === -1 && currentStatus === 'open') {
|
|
51
|
+
startClosing(sheetId);
|
|
55
52
|
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const handleChange = (index: number) => {
|
|
56
|
+
const state = useBottomSheetStore.getState();
|
|
57
|
+
const currentStatus = state.sheetsById[sheetId]?.status;
|
|
56
58
|
|
|
57
|
-
|
|
58
|
-
if (fromIndex === -1 && toIndex >= 0) {
|
|
59
|
+
if (index >= 0 && currentStatus === 'opening') {
|
|
59
60
|
markOpen(sheetId);
|
|
60
61
|
}
|
|
61
62
|
};
|
|
@@ -71,6 +72,7 @@ export function createSheetEventHandlers(sheetId: string) {
|
|
|
71
72
|
|
|
72
73
|
return {
|
|
73
74
|
handleAnimate,
|
|
75
|
+
handleChange,
|
|
74
76
|
handleClose,
|
|
75
77
|
};
|
|
76
78
|
}
|
package/src/index.tsx
CHANGED
|
@@ -4,6 +4,7 @@ export { BottomSheetHost } from './BottomSheetHost';
|
|
|
4
4
|
export { BottomSheetScaleView } from './BottomSheetScaleView';
|
|
5
5
|
export { BottomSheetManaged, type BottomSheetRef } from './BottomSheetManaged';
|
|
6
6
|
export { BottomSheetPortal } from './BottomSheetPortal';
|
|
7
|
+
export { BottomSheetPersistent } from './BottomSheetPersistent';
|
|
7
8
|
|
|
8
9
|
// Hooks
|
|
9
10
|
export { useBottomSheetManager } from './useBottomSheetManager';
|
|
@@ -22,7 +23,7 @@ export {
|
|
|
22
23
|
} from './useBottomSheetStatus';
|
|
23
24
|
|
|
24
25
|
// Types
|
|
25
|
-
export type { ScaleConfig } from './useScaleAnimation';
|
|
26
|
+
export type { ScaleConfig, ScaleAnimationConfig } from './useScaleAnimation';
|
|
26
27
|
export type {
|
|
27
28
|
BottomSheetStatus,
|
|
28
29
|
OpenMode,
|
|
@@ -33,3 +34,8 @@ export type {
|
|
|
33
34
|
BottomSheetPortalId,
|
|
34
35
|
BottomSheetPortalParams,
|
|
35
36
|
} from './portal.types';
|
|
37
|
+
|
|
38
|
+
// Testing utilities (internal use)
|
|
39
|
+
export { __resetSheetRefs } from './refsMap';
|
|
40
|
+
export { __resetAnimatedIndexes } from './animatedRegistry';
|
|
41
|
+
export { __resetPortalSessions } from './portalSessionRegistry';
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Registry for portal session counters per sheet.
|
|
3
|
+
* This persists across sheet deletions to ensure unique Portal/PortalHost names
|
|
4
|
+
* and work around react-native-teleport connection issues after replace flows.
|
|
5
|
+
*/
|
|
6
|
+
const portalSessionRegistry = new Map<string, number>();
|
|
7
|
+
|
|
8
|
+
export function getNextPortalSession(sheetId: string): number {
|
|
9
|
+
const current = portalSessionRegistry.get(sheetId) ?? 0;
|
|
10
|
+
const next = current + 1;
|
|
11
|
+
portalSessionRegistry.set(sheetId, next);
|
|
12
|
+
return next;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function getCurrentPortalSession(sheetId: string): number | undefined {
|
|
16
|
+
return portalSessionRegistry.get(sheetId);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Reset all portal sessions. Useful for testing.
|
|
21
|
+
* @internal
|
|
22
|
+
*/
|
|
23
|
+
export function __resetPortalSessions(): void {
|
|
24
|
+
portalSessionRegistry.clear();
|
|
25
|
+
}
|
package/src/refsMap.ts
CHANGED
|
@@ -16,3 +16,11 @@ export function setSheetRef(sheetId: string, ref: SheetRef): void {
|
|
|
16
16
|
export function cleanupSheetRef(sheetId: string): void {
|
|
17
17
|
sheetRefsMap.delete(sheetId);
|
|
18
18
|
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Reset all sheet refs. Useful for testing.
|
|
22
|
+
* @internal
|
|
23
|
+
*/
|
|
24
|
+
export function __resetSheetRefs(): void {
|
|
25
|
+
sheetRefsMap.clear();
|
|
26
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { BottomSheetState, BottomSheetStatus, OpenMode } from './types';
|
|
2
|
+
|
|
3
|
+
export const MODE_STATUS_MAP: Record<OpenMode, BottomSheetStatus | null> = {
|
|
4
|
+
push: null,
|
|
5
|
+
switch: 'hidden',
|
|
6
|
+
replace: 'closing',
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export function isActivatableKeepMounted(
|
|
10
|
+
sheet: BottomSheetState | undefined
|
|
11
|
+
): sheet is BottomSheetState {
|
|
12
|
+
return Boolean(sheet?.keepMounted && sheet.status === 'hidden');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function isHidden(sheet: BottomSheetState | undefined): boolean {
|
|
16
|
+
return sheet?.status === 'hidden';
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function isOpening(sheet: BottomSheetState | undefined): boolean {
|
|
20
|
+
return sheet?.status === 'opening';
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function updateSheet(
|
|
24
|
+
sheetsById: Record<string, BottomSheetState>,
|
|
25
|
+
id: string,
|
|
26
|
+
update: Partial<BottomSheetState>
|
|
27
|
+
): Record<string, BottomSheetState> {
|
|
28
|
+
const sheet = sheetsById[id];
|
|
29
|
+
if (!sheet) return sheetsById;
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
...sheetsById,
|
|
33
|
+
[id]: { ...sheet, ...update },
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function applyModeToTopSheet(
|
|
38
|
+
sheetsById: Record<string, BottomSheetState>,
|
|
39
|
+
stackOrder: string[],
|
|
40
|
+
mode: OpenMode
|
|
41
|
+
): Record<string, BottomSheetState> {
|
|
42
|
+
const targetStatus = MODE_STATUS_MAP[mode];
|
|
43
|
+
if (!targetStatus) return sheetsById;
|
|
44
|
+
|
|
45
|
+
const topId = stackOrder[stackOrder.length - 1];
|
|
46
|
+
if (!topId || !sheetsById[topId]) return sheetsById;
|
|
47
|
+
|
|
48
|
+
return updateSheet(sheetsById, topId, { status: targetStatus });
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function removeFromStack(stackOrder: string[], id: string): string[] {
|
|
52
|
+
return stackOrder.filter((sheetId) => sheetId !== id);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function getTopSheetId(stackOrder: string[]): string | undefined {
|
|
56
|
+
return stackOrder[stackOrder.length - 1];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function getSheetBelowId(
|
|
60
|
+
stackOrder: string[],
|
|
61
|
+
id: string
|
|
62
|
+
): string | undefined {
|
|
63
|
+
const index = stackOrder.indexOf(id);
|
|
64
|
+
return index > 0 ? stackOrder[index - 1] : undefined;
|
|
65
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { shallow } from 'zustand/shallow';
|
|
2
|
+
import { useBottomSheetStore } from './store';
|
|
3
|
+
|
|
4
|
+
// State hooks
|
|
5
|
+
|
|
6
|
+
export const useSheet = (id: string) =>
|
|
7
|
+
useBottomSheetStore((state) => state.sheetsById[id], shallow);
|
|
8
|
+
|
|
9
|
+
export const useSheetStatus = (id: string) =>
|
|
10
|
+
useBottomSheetStore((state) => state.sheetsById[id]?.status, shallow);
|
|
11
|
+
|
|
12
|
+
export const useSheetParams = (id: string) =>
|
|
13
|
+
useBottomSheetStore((state) => state.sheetsById[id]?.params, shallow);
|
|
14
|
+
|
|
15
|
+
export const useSheetContent = (id: string) =>
|
|
16
|
+
useBottomSheetStore((state) => state.sheetsById[id]?.content, shallow);
|
|
17
|
+
|
|
18
|
+
export const useSheetUsePortal = (id: string) =>
|
|
19
|
+
useBottomSheetStore((state) => state.sheetsById[id]?.usePortal, shallow);
|
|
20
|
+
|
|
21
|
+
export const useSheetKeepMounted = (id: string) =>
|
|
22
|
+
useBottomSheetStore((state) => state.sheetsById[id]?.keepMounted, shallow);
|
|
23
|
+
export const useSheetPortalSession = (id: string) =>
|
|
24
|
+
useBottomSheetStore((state) => state.sheetsById[id]?.portalSession, shallow);
|
|
25
|
+
|
|
26
|
+
export const useSheetExists = (id: string) =>
|
|
27
|
+
useBottomSheetStore((state) => !!state.sheetsById[id], shallow);
|
|
28
|
+
|
|
29
|
+
export const useIsSheetOpen = (id: string) =>
|
|
30
|
+
useBottomSheetStore((state) => {
|
|
31
|
+
const status = state.sheetsById[id]?.status;
|
|
32
|
+
return status === 'open' || status === 'opening';
|
|
33
|
+
}, shallow);
|
|
34
|
+
|
|
35
|
+
// Action hooks
|
|
36
|
+
|
|
37
|
+
export const useOpen = () => useBottomSheetStore((state) => state.open);
|
|
38
|
+
|
|
39
|
+
export const useStartClosing = () =>
|
|
40
|
+
useBottomSheetStore((state) => state.startClosing);
|
|
41
|
+
|
|
42
|
+
export const useUpdateParams = () =>
|
|
43
|
+
useBottomSheetStore((state) => state.updateParams);
|
|
44
|
+
|
|
45
|
+
export const useClearGroup = () =>
|
|
46
|
+
useBottomSheetStore((state) => state.clearGroup);
|
|
47
|
+
|
|
48
|
+
export const useMount = () => useBottomSheetStore((state) => state.mount);
|
|
49
|
+
|
|
50
|
+
export const useUnmount = () => useBottomSheetStore((state) => state.unmount);
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { subscribeWithSelector } from 'zustand/middleware';
|
|
2
|
+
import { createWithEqualityFn as create } from 'zustand/traditional';
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
applyModeToTopSheet,
|
|
6
|
+
getSheetBelowId,
|
|
7
|
+
getTopSheetId,
|
|
8
|
+
isActivatableKeepMounted,
|
|
9
|
+
isHidden,
|
|
10
|
+
isOpening,
|
|
11
|
+
removeFromStack,
|
|
12
|
+
updateSheet,
|
|
13
|
+
} from './helpers';
|
|
14
|
+
import { getNextPortalSession } from '../portalSessionRegistry';
|
|
15
|
+
import type { BottomSheetState, BottomSheetStore } from './types';
|
|
16
|
+
|
|
17
|
+
export const useBottomSheetStore = create(
|
|
18
|
+
subscribeWithSelector<BottomSheetStore>((set) => ({
|
|
19
|
+
sheetsById: {},
|
|
20
|
+
stackOrder: [],
|
|
21
|
+
|
|
22
|
+
open: (sheet, mode = 'push') =>
|
|
23
|
+
set((state) => {
|
|
24
|
+
const existingSheet = state.sheetsById[sheet.id];
|
|
25
|
+
|
|
26
|
+
if (existingSheet && !isActivatableKeepMounted(existingSheet)) {
|
|
27
|
+
return state;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const updatedSheetsById = applyModeToTopSheet(
|
|
31
|
+
state.sheetsById,
|
|
32
|
+
state.stackOrder,
|
|
33
|
+
mode
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
// Get next portalSession from registry for portal-based sheets
|
|
37
|
+
// Registry persists across sheet deletions to ensure unique Portal/PortalHost names
|
|
38
|
+
const nextPortalSession = sheet.usePortal
|
|
39
|
+
? getNextPortalSession(sheet.id)
|
|
40
|
+
: undefined;
|
|
41
|
+
|
|
42
|
+
const newSheet: BottomSheetState = existingSheet
|
|
43
|
+
? {
|
|
44
|
+
...existingSheet,
|
|
45
|
+
status: 'opening',
|
|
46
|
+
scaleBackground:
|
|
47
|
+
sheet.scaleBackground ?? existingSheet.scaleBackground,
|
|
48
|
+
params: sheet.params ?? existingSheet.params,
|
|
49
|
+
portalSession: nextPortalSession ?? existingSheet.portalSession,
|
|
50
|
+
}
|
|
51
|
+
: { ...sheet, status: 'opening', portalSession: nextPortalSession };
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
sheetsById: { ...updatedSheetsById, [sheet.id]: newSheet },
|
|
55
|
+
stackOrder: [...state.stackOrder, sheet.id],
|
|
56
|
+
};
|
|
57
|
+
}),
|
|
58
|
+
|
|
59
|
+
markOpen: (id) =>
|
|
60
|
+
set((state) => {
|
|
61
|
+
if (!state.sheetsById[id]) return state;
|
|
62
|
+
return {
|
|
63
|
+
sheetsById: updateSheet(state.sheetsById, id, { status: 'open' }),
|
|
64
|
+
};
|
|
65
|
+
}),
|
|
66
|
+
|
|
67
|
+
startClosing: (id) =>
|
|
68
|
+
set((state) => {
|
|
69
|
+
const sheet = state.sheetsById[id];
|
|
70
|
+
if (!sheet || isHidden(sheet) || isOpening(sheet)) return state;
|
|
71
|
+
|
|
72
|
+
let updatedSheetsById = updateSheet(state.sheetsById, id, {
|
|
73
|
+
status: 'closing',
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const belowId = getSheetBelowId(state.stackOrder, id);
|
|
77
|
+
if (belowId && isHidden(updatedSheetsById[belowId])) {
|
|
78
|
+
updatedSheetsById = updateSheet(updatedSheetsById, belowId, {
|
|
79
|
+
status: 'opening',
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return { sheetsById: updatedSheetsById };
|
|
84
|
+
}),
|
|
85
|
+
|
|
86
|
+
finishClosing: (id) =>
|
|
87
|
+
set((state) => {
|
|
88
|
+
const sheet = state.sheetsById[id];
|
|
89
|
+
if (!sheet) return state;
|
|
90
|
+
|
|
91
|
+
let updatedSheetsById = { ...state.sheetsById };
|
|
92
|
+
|
|
93
|
+
if (sheet.keepMounted) {
|
|
94
|
+
updatedSheetsById = updateSheet(updatedSheetsById, id, {
|
|
95
|
+
status: 'hidden',
|
|
96
|
+
});
|
|
97
|
+
} else {
|
|
98
|
+
delete updatedSheetsById[id];
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const newStackOrder = removeFromStack(state.stackOrder, id);
|
|
102
|
+
const topId = getTopSheetId(newStackOrder);
|
|
103
|
+
|
|
104
|
+
if (topId && isHidden(updatedSheetsById[topId])) {
|
|
105
|
+
updatedSheetsById = updateSheet(updatedSheetsById, topId, {
|
|
106
|
+
status: 'opening',
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return {
|
|
111
|
+
sheetsById: updatedSheetsById,
|
|
112
|
+
stackOrder: newStackOrder,
|
|
113
|
+
};
|
|
114
|
+
}),
|
|
115
|
+
|
|
116
|
+
updateParams: (id, params) =>
|
|
117
|
+
set((state) => {
|
|
118
|
+
if (!state.sheetsById[id]) return state;
|
|
119
|
+
return { sheetsById: updateSheet(state.sheetsById, id, { params }) };
|
|
120
|
+
}),
|
|
121
|
+
|
|
122
|
+
clearGroup: (groupId) =>
|
|
123
|
+
set((state) => {
|
|
124
|
+
const idsToRemove = new Set(
|
|
125
|
+
Object.keys(state.sheetsById).filter(
|
|
126
|
+
(id) => state.sheetsById[id]?.groupId === groupId
|
|
127
|
+
)
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
if (idsToRemove.size === 0) return state;
|
|
131
|
+
|
|
132
|
+
const updatedSheetsById = { ...state.sheetsById };
|
|
133
|
+
idsToRemove.forEach((id) => delete updatedSheetsById[id]);
|
|
134
|
+
|
|
135
|
+
return {
|
|
136
|
+
sheetsById: updatedSheetsById,
|
|
137
|
+
stackOrder: state.stackOrder.filter((id) => !idsToRemove.has(id)),
|
|
138
|
+
};
|
|
139
|
+
}),
|
|
140
|
+
|
|
141
|
+
clearAll: () => set({ sheetsById: {}, stackOrder: [] }),
|
|
142
|
+
|
|
143
|
+
mount: (sheet) =>
|
|
144
|
+
set((state) => {
|
|
145
|
+
if (state.sheetsById[sheet.id]) return state;
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
sheetsById: {
|
|
149
|
+
...state.sheetsById,
|
|
150
|
+
[sheet.id]: { ...sheet, status: 'hidden' },
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
}),
|
|
154
|
+
|
|
155
|
+
unmount: (id) =>
|
|
156
|
+
set((state) => {
|
|
157
|
+
if (!state.sheetsById[id]) return state;
|
|
158
|
+
|
|
159
|
+
const updatedSheetsById = { ...state.sheetsById };
|
|
160
|
+
delete updatedSheetsById[id];
|
|
161
|
+
|
|
162
|
+
return {
|
|
163
|
+
sheetsById: updatedSheetsById,
|
|
164
|
+
stackOrder: removeFromStack(state.stackOrder, id),
|
|
165
|
+
};
|
|
166
|
+
}),
|
|
167
|
+
}))
|
|
168
|
+
);
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
export type BottomSheetStatus = 'opening' | 'open' | 'closing' | 'hidden';
|
|
4
|
+
export type OpenMode = 'push' | 'switch' | 'replace';
|
|
5
|
+
|
|
6
|
+
export interface BottomSheetState {
|
|
7
|
+
groupId: string;
|
|
8
|
+
id: string;
|
|
9
|
+
content?: ReactNode;
|
|
10
|
+
status: BottomSheetStatus;
|
|
11
|
+
scaleBackground?: boolean;
|
|
12
|
+
usePortal?: boolean;
|
|
13
|
+
params?: Record<string, unknown>;
|
|
14
|
+
keepMounted?: boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Incremented each time a portal-based sheet is opened.
|
|
17
|
+
* Used to create unique Portal/PortalHost names to work around
|
|
18
|
+
* react-native-teleport connection issues after replace flows.
|
|
19
|
+
*/
|
|
20
|
+
portalSession?: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type TriggerState = Omit<BottomSheetState, 'status'>;
|
|
24
|
+
|
|
25
|
+
export interface BottomSheetStoreState {
|
|
26
|
+
sheetsById: Record<string, BottomSheetState>;
|
|
27
|
+
stackOrder: string[];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface BottomSheetStoreActions {
|
|
31
|
+
open(sheet: TriggerState, mode?: OpenMode): void;
|
|
32
|
+
markOpen(id: string): void;
|
|
33
|
+
startClosing(id: string): void;
|
|
34
|
+
finishClosing(id: string): void;
|
|
35
|
+
updateParams(id: string, params: Record<string, unknown> | undefined): void;
|
|
36
|
+
clearGroup(groupId: string): void;
|
|
37
|
+
clearAll(): void;
|
|
38
|
+
mount(sheet: Omit<BottomSheetState, 'status'>): void;
|
|
39
|
+
unmount(id: string): void;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export type BottomSheetStore = BottomSheetStoreState & BottomSheetStoreActions;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { shallow } from 'zustand/shallow';
|
|
2
1
|
import { useMaybeBottomSheetContext } from './BottomSheet.context';
|
|
3
|
-
import {
|
|
2
|
+
import { useSheetParams, useStartClosing } from './bottomSheet.store';
|
|
4
3
|
import type {
|
|
5
4
|
BottomSheetPortalId,
|
|
6
5
|
BottomSheetPortalParams,
|
|
@@ -24,27 +23,19 @@ export function useBottomSheetContext<
|
|
|
24
23
|
T extends BottomSheetPortalId,
|
|
25
24
|
>(): UseBottomSheetContextReturn<BottomSheetPortalParams<T> | unknown> {
|
|
26
25
|
const context = useMaybeBottomSheetContext();
|
|
26
|
+
const params = useSheetParams(context?.id || '');
|
|
27
|
+
const startClosing = useStartClosing();
|
|
27
28
|
|
|
28
|
-
|
|
29
|
-
(state) => ({
|
|
30
|
-
id: state.sheetsById[context?.id!]?.id,
|
|
31
|
-
params: state.sheetsById[context?.id!]?.params,
|
|
32
|
-
}),
|
|
33
|
-
shallow
|
|
34
|
-
);
|
|
35
|
-
|
|
36
|
-
const startClosing = useBottomSheetStore((state) => state.startClosing);
|
|
37
|
-
|
|
38
|
-
if (!id) {
|
|
29
|
+
if (!context?.id) {
|
|
39
30
|
throw new Error(
|
|
40
31
|
'useBottomSheetContext must be used within a BottomSheet component'
|
|
41
32
|
);
|
|
42
33
|
}
|
|
43
34
|
|
|
44
|
-
const close = () => startClosing(id);
|
|
35
|
+
const close = () => startClosing(context.id);
|
|
45
36
|
|
|
46
37
|
return {
|
|
47
|
-
id,
|
|
38
|
+
id: context.id,
|
|
48
39
|
params: params as BottomSheetPortalParams<T>,
|
|
49
40
|
close,
|
|
50
41
|
closeBottomSheet: close,
|
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { BottomSheetMethods } from '@gorhom/bottom-sheet/lib/typescript/types';
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
useOpen,
|
|
6
|
+
useStartClosing,
|
|
7
|
+
useUpdateParams,
|
|
8
|
+
type OpenMode,
|
|
9
|
+
} from './bottomSheet.store';
|
|
5
10
|
import { useMaybeBottomSheetManagerContext } from './BottomSheetManager.provider';
|
|
6
11
|
import type {
|
|
7
12
|
BottomSheetPortalId,
|
|
8
13
|
BottomSheetPortalParams,
|
|
9
14
|
HasParams,
|
|
10
15
|
} from './portal.types';
|
|
11
|
-
import { setSheetRef } from './refsMap';
|
|
16
|
+
import { getSheetRef, setSheetRef } from './refsMap';
|
|
12
17
|
|
|
13
18
|
interface BaseOpenOptions<TParams> {
|
|
14
19
|
mode?: OpenMode;
|
|
@@ -41,15 +46,19 @@ export function useBottomSheetControl<T extends BottomSheetPortalId>(
|
|
|
41
46
|
): UseBottomSheetControlReturn<T> {
|
|
42
47
|
const bottomSheetManagerContext = useMaybeBottomSheetManagerContext();
|
|
43
48
|
|
|
44
|
-
const storeOpen =
|
|
45
|
-
const startClosing =
|
|
46
|
-
const storeUpdateParams =
|
|
49
|
+
const storeOpen = useOpen();
|
|
50
|
+
const startClosing = useStartClosing();
|
|
51
|
+
const storeUpdateParams = useUpdateParams();
|
|
47
52
|
|
|
48
53
|
const open = (options?: OpenOptions<T>) => {
|
|
49
54
|
const groupId = bottomSheetManagerContext?.groupId || 'default';
|
|
50
55
|
|
|
51
|
-
|
|
52
|
-
|
|
56
|
+
// Only create ref if it doesn't exist (keepMounted sheets already have one)
|
|
57
|
+
const existingRef = getSheetRef(id);
|
|
58
|
+
if (!existingRef) {
|
|
59
|
+
const ref = React.createRef<BottomSheetMethods>();
|
|
60
|
+
setSheetRef(id, ref);
|
|
61
|
+
}
|
|
53
62
|
|
|
54
63
|
storeOpen(
|
|
55
64
|
{
|
|
@@ -1,22 +1,21 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
useOpen,
|
|
5
|
+
useStartClosing,
|
|
6
|
+
useClearGroup,
|
|
7
|
+
type OpenMode,
|
|
8
|
+
} from './bottomSheet.store';
|
|
4
9
|
import { useMaybeBottomSheetManagerContext } from './BottomSheetManager.provider';
|
|
5
10
|
import { setSheetRef } from './refsMap';
|
|
6
11
|
import type { BottomSheetMethods } from '@gorhom/bottom-sheet/lib/typescript/types';
|
|
7
|
-
import { shallow } from 'zustand/shallow';
|
|
8
12
|
|
|
9
13
|
export const useBottomSheetManager = () => {
|
|
10
14
|
const bottomSheetManagerContext = useMaybeBottomSheetManagerContext();
|
|
11
15
|
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
storeClearGroup: store.clearGroup,
|
|
16
|
-
startClosing: store.startClosing,
|
|
17
|
-
}),
|
|
18
|
-
shallow
|
|
19
|
-
);
|
|
16
|
+
const storeOpen = useOpen();
|
|
17
|
+
const startClosing = useStartClosing();
|
|
18
|
+
const storeClearGroup = useClearGroup();
|
|
20
19
|
|
|
21
20
|
const openBottomSheet = (
|
|
22
21
|
content: React.ReactElement,
|
|
@@ -1,25 +1,15 @@
|
|
|
1
|
-
import {
|
|
2
|
-
useBottomSheetStore,
|
|
3
|
-
type BottomSheetStatus,
|
|
4
|
-
} from './bottomSheet.store';
|
|
5
|
-
import type { BottomSheetPortalId } from './portal.types';
|
|
1
|
+
import { useSheetStatus, type BottomSheetStatus } from './bottomSheet.store';
|
|
6
2
|
|
|
7
3
|
export interface UseBottomSheetStatusReturn {
|
|
8
4
|
status: BottomSheetStatus | null;
|
|
9
5
|
isOpen: boolean;
|
|
10
6
|
}
|
|
11
7
|
|
|
12
|
-
export function useBottomSheetStatus(
|
|
13
|
-
id
|
|
14
|
-
): UseBottomSheetStatusReturn {
|
|
15
|
-
const status = useBottomSheetStore(
|
|
16
|
-
(state) => state.sheetsById[id]?.status ?? null
|
|
17
|
-
);
|
|
18
|
-
|
|
19
|
-
const isOpen = status === 'open' || status === 'opening';
|
|
8
|
+
export function useBottomSheetStatus(id: string): UseBottomSheetStatusReturn {
|
|
9
|
+
const status = useSheetStatus(id) ?? null;
|
|
20
10
|
|
|
21
11
|
return {
|
|
22
12
|
status,
|
|
23
|
-
isOpen,
|
|
13
|
+
isOpen: status === 'open' || status === 'opening',
|
|
24
14
|
};
|
|
25
15
|
}
|
package/src/useEvent.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { useCallback, useLayoutEffect, useRef } from 'react';
|
|
2
|
+
|
|
3
|
+
// biome-ignore lint/suspicious/noExplicitAny: No better alternative available.
|
|
4
|
+
type CallbackType = (...args: any[]) => any;
|
|
5
|
+
|
|
6
|
+
// RFC: https://github.com/reactjs/rfcs/blob/useevent/text/0000-useevent.md
|
|
7
|
+
export const useEvent = <T extends CallbackType>(callback: T) => {
|
|
8
|
+
const callbackRef = useRef(callback);
|
|
9
|
+
|
|
10
|
+
useLayoutEffect(() => {
|
|
11
|
+
callbackRef.current = callback;
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
return useCallback((...args: Parameters<T>): ReturnType<T> => {
|
|
15
|
+
return callbackRef.current(...args);
|
|
16
|
+
}, []);
|
|
17
|
+
};
|