@sigmela/router 0.3.6 → 0.4.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/CHANGELOG.md +23 -0
- package/LICENSE +2 -1
- package/README.md +108 -479
- package/dist/Router.d.ts +190 -0
- package/dist/Router.d.ts.map +1 -0
- package/dist/components/Modal.d.ts +56 -0
- package/dist/components/Modal.d.ts.map +1 -0
- package/dist/components/OverlayContentScope.d.ts +8 -0
- package/dist/components/OverlayContentScope.d.ts.map +1 -0
- package/dist/components/RouterCloseButton.d.ts +9 -0
- package/dist/components/RouterCloseButton.d.ts.map +1 -0
- package/dist/components/Sheet.d.ts +53 -0
- package/dist/components/Sheet.d.ts.map +1 -0
- package/dist/components/SheetSurface.d.ts +27 -0
- package/dist/components/SheetSurface.d.ts.map +1 -0
- package/dist/components/index.d.ts +4 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/pageScrollLock.d.ts +3 -0
- package/dist/components/pageScrollLock.d.ts.map +1 -0
- package/dist/components/sheetDragEngine.d.ts +27 -0
- package/dist/components/sheetDragEngine.d.ts.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3649 -0
- package/dist/index.js.map +1 -0
- package/dist/navigationHistory.d.ts +17 -0
- package/dist/navigationHistory.d.ts.map +1 -0
- package/dist/router.css +971 -0
- package/dist/types.d.ts +3 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/utils/id.d.ts +2 -0
- package/dist/utils/id.d.ts.map +1 -0
- package/package.json +69 -174
- package/lib/module/Drawer/Drawer.js +0 -250
- package/lib/module/Drawer/DrawerContext.js +0 -4
- package/lib/module/Drawer/DrawerIcon.web.js +0 -47
- package/lib/module/Drawer/RenderDrawer.native.js +0 -248
- package/lib/module/Drawer/RenderDrawer.web.js +0 -197
- package/lib/module/Drawer/useDrawer.js +0 -11
- package/lib/module/Navigation.js +0 -79
- package/lib/module/NavigationStack.js +0 -254
- package/lib/module/Router.js +0 -2171
- package/lib/module/RouterContext.js +0 -33
- package/lib/module/ScreenStack/ScreenStack.native.js +0 -3
- package/lib/module/ScreenStack/ScreenStack.web.js +0 -561
- package/lib/module/ScreenStack/ScreenStackContext.js +0 -21
- package/lib/module/ScreenStack/animationHelpers.js +0 -87
- package/lib/module/ScreenStack/index.js +0 -3
- package/lib/module/ScreenStackItem/ScreenStackItem.js +0 -70
- package/lib/module/ScreenStackItem/ScreenStackItem.types.js +0 -3
- package/lib/module/ScreenStackItem/ScreenStackItem.web.js +0 -171
- package/lib/module/ScreenStackItem/index.js +0 -3
- package/lib/module/ScreenStackSheetItem/ScreenStackSheetItem.native.js +0 -70
- package/lib/module/ScreenStackSheetItem/ScreenStackSheetItem.web.js +0 -5
- package/lib/module/ScreenStackSheetItem/index.js +0 -3
- package/lib/module/SplitView/RenderSplitView.native.js +0 -101
- package/lib/module/SplitView/RenderSplitView.web.js +0 -107
- package/lib/module/SplitView/SplitView.js +0 -97
- package/lib/module/SplitView/SplitViewContext.js +0 -4
- package/lib/module/SplitView/index.js +0 -5
- package/lib/module/SplitView/useSplitView.js +0 -11
- package/lib/module/StackRenderer.js +0 -42
- package/lib/module/TabBar/RenderTabBar.native.js +0 -418
- package/lib/module/TabBar/RenderTabBar.web.js +0 -218
- package/lib/module/TabBar/TabBar.js +0 -208
- package/lib/module/TabBar/TabBarContext.js +0 -4
- package/lib/module/TabBar/TabIcon.web.js +0 -47
- package/lib/module/TabBar/index.js +0 -6
- package/lib/module/TabBar/useTabBar.js +0 -11
- package/lib/module/TabBar/useTabBarHeight.js +0 -22
- package/lib/module/createController.js +0 -5
- package/lib/module/index.js +0 -15
- package/lib/module/navigationNode.js +0 -3
- package/lib/module/package.json +0 -1
- package/lib/module/styles.css +0 -1111
- package/lib/module/types.js +0 -13
- package/lib/typescript/package.json +0 -1
- package/lib/typescript/src/Drawer/Drawer.d.ts +0 -100
- package/lib/typescript/src/Drawer/DrawerContext.d.ts +0 -3
- package/lib/typescript/src/Drawer/DrawerIcon.web.d.ts +0 -7
- package/lib/typescript/src/Drawer/RenderDrawer.native.d.ts +0 -8
- package/lib/typescript/src/Drawer/RenderDrawer.web.d.ts +0 -8
- package/lib/typescript/src/Drawer/useDrawer.d.ts +0 -2
- package/lib/typescript/src/Navigation.d.ts +0 -17
- package/lib/typescript/src/NavigationStack.d.ts +0 -46
- package/lib/typescript/src/Router.d.ts +0 -232
- package/lib/typescript/src/RouterContext.d.ts +0 -18
- package/lib/typescript/src/ScreenStack/ScreenStack.native.d.ts +0 -2
- package/lib/typescript/src/ScreenStack/ScreenStack.web.d.ts +0 -9
- package/lib/typescript/src/ScreenStack/ScreenStackContext.d.ts +0 -31
- package/lib/typescript/src/ScreenStack/animationHelpers.d.ts +0 -6
- package/lib/typescript/src/ScreenStack/index.d.ts +0 -2
- package/lib/typescript/src/ScreenStackItem/ScreenStackItem.d.ts +0 -3
- package/lib/typescript/src/ScreenStackItem/ScreenStackItem.types.d.ts +0 -14
- package/lib/typescript/src/ScreenStackItem/ScreenStackItem.web.d.ts +0 -3
- package/lib/typescript/src/ScreenStackItem/index.d.ts +0 -3
- package/lib/typescript/src/ScreenStackSheetItem/ScreenStackSheetItem.native.d.ts +0 -11
- package/lib/typescript/src/ScreenStackSheetItem/ScreenStackSheetItem.web.d.ts +0 -2
- package/lib/typescript/src/ScreenStackSheetItem/index.d.ts +0 -2
- package/lib/typescript/src/SplitView/RenderSplitView.native.d.ts +0 -19
- package/lib/typescript/src/SplitView/RenderSplitView.web.d.ts +0 -8
- package/lib/typescript/src/SplitView/SplitView.d.ts +0 -33
- package/lib/typescript/src/SplitView/SplitViewContext.d.ts +0 -3
- package/lib/typescript/src/SplitView/index.d.ts +0 -5
- package/lib/typescript/src/SplitView/useSplitView.d.ts +0 -2
- package/lib/typescript/src/StackRenderer.d.ts +0 -8
- package/lib/typescript/src/TabBar/RenderTabBar.native.d.ts +0 -8
- package/lib/typescript/src/TabBar/RenderTabBar.web.d.ts +0 -8
- package/lib/typescript/src/TabBar/TabBar.d.ts +0 -112
- package/lib/typescript/src/TabBar/TabBarContext.d.ts +0 -3
- package/lib/typescript/src/TabBar/TabIcon.web.d.ts +0 -7
- package/lib/typescript/src/TabBar/index.d.ts +0 -5
- package/lib/typescript/src/TabBar/useTabBar.d.ts +0 -2
- package/lib/typescript/src/TabBar/useTabBarHeight.d.ts +0 -18
- package/lib/typescript/src/createController.d.ts +0 -15
- package/lib/typescript/src/index.d.ts +0 -21
- package/lib/typescript/src/navigationNode.d.ts +0 -41
- package/lib/typescript/src/types.d.ts +0 -140
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
import { isModalLikePresentation } from "../types.js";
|
|
4
|
-
export function getPresentationTypeClass(presentation) {
|
|
5
|
-
switch (presentation) {
|
|
6
|
-
case 'push':
|
|
7
|
-
return 'push';
|
|
8
|
-
case 'modal':
|
|
9
|
-
return 'modal';
|
|
10
|
-
case 'modalRight':
|
|
11
|
-
return 'modal-right';
|
|
12
|
-
case 'transparentModal':
|
|
13
|
-
return 'transparent-modal';
|
|
14
|
-
case 'containedModal':
|
|
15
|
-
return 'contained-modal';
|
|
16
|
-
case 'containedTransparentModal':
|
|
17
|
-
return 'contained-transparent-modal';
|
|
18
|
-
case 'fullScreenModal':
|
|
19
|
-
return 'fullscreen-modal';
|
|
20
|
-
case 'formSheet':
|
|
21
|
-
return 'formsheet';
|
|
22
|
-
case 'pageSheet':
|
|
23
|
-
return 'pagesheet';
|
|
24
|
-
case 'sheet':
|
|
25
|
-
return 'sheet';
|
|
26
|
-
default:
|
|
27
|
-
return 'push';
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
export function getAnimationTypeForPresentation(presentation, isEntering, direction) {
|
|
31
|
-
const suffix = isEntering ? 'enter' : 'exit';
|
|
32
|
-
const presentationClass = getPresentationTypeClass(presentation);
|
|
33
|
-
if (presentation === 'push') {
|
|
34
|
-
return direction === 'forward' ? `push-${suffix}` : `pop-${suffix}`;
|
|
35
|
-
}
|
|
36
|
-
return `${presentationClass}-${suffix}`;
|
|
37
|
-
}
|
|
38
|
-
export function computeAnimationType(_key, isInStack, isTop, direction, presentation, isInitialPhase, animated = true, isBulkRemoval = false) {
|
|
39
|
-
if (!animated) {
|
|
40
|
-
return 'no-animate';
|
|
41
|
-
}
|
|
42
|
-
if (isInitialPhase) {
|
|
43
|
-
return 'none';
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// When multiple screens are removed at once (bulk removal), don't animate them
|
|
47
|
-
if (isBulkRemoval && !isInStack) {
|
|
48
|
-
return 'no-animate';
|
|
49
|
-
}
|
|
50
|
-
const isEntering = isInStack && isTop;
|
|
51
|
-
const isModalLike = isModalLikePresentation(presentation);
|
|
52
|
-
if (isModalLike) {
|
|
53
|
-
if (!isInStack) {
|
|
54
|
-
return getAnimationTypeForPresentation(presentation, false, direction);
|
|
55
|
-
}
|
|
56
|
-
if (isEntering) {
|
|
57
|
-
return getAnimationTypeForPresentation(presentation, true, direction);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// Modal-like screen that's NOT top (background) - animate like push
|
|
61
|
-
// This happens when navigating inside a modal stack
|
|
62
|
-
if (direction === 'forward') {
|
|
63
|
-
return 'push-background';
|
|
64
|
-
} else {
|
|
65
|
-
return 'pop-background';
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
if (!isInStack) {
|
|
69
|
-
if (direction === 'forward') {
|
|
70
|
-
return 'push-exit';
|
|
71
|
-
} else {
|
|
72
|
-
return 'pop-exit';
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
if (isTop) {
|
|
76
|
-
if (direction === 'forward') {
|
|
77
|
-
return 'push-enter';
|
|
78
|
-
} else {
|
|
79
|
-
return 'pop-enter';
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
if (direction === 'forward') {
|
|
83
|
-
return 'push-background';
|
|
84
|
-
} else {
|
|
85
|
-
return 'pop-background';
|
|
86
|
-
}
|
|
87
|
-
}
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
import { ScreenStackItem as RNSScreenStackItem } from 'react-native-screens';
|
|
4
|
-
import { RouteLocalContext, useRouter } from "../RouterContext.js";
|
|
5
|
-
import { ScreenStackSheetItem } from "../ScreenStackSheetItem/index.js";
|
|
6
|
-
import { StyleSheet } from 'react-native';
|
|
7
|
-
import { memo, useMemo, useCallback } from 'react';
|
|
8
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
9
|
-
export const ScreenStackItem = /*#__PURE__*/memo(({
|
|
10
|
-
item,
|
|
11
|
-
stackId,
|
|
12
|
-
stackAnimation,
|
|
13
|
-
appearance
|
|
14
|
-
}) => {
|
|
15
|
-
const {
|
|
16
|
-
header,
|
|
17
|
-
stackPresentation,
|
|
18
|
-
...screenProps
|
|
19
|
-
} = item.options || {};
|
|
20
|
-
|
|
21
|
-
// On native, modalRight behaves as regular modal
|
|
22
|
-
const nativePresentation = stackPresentation === 'modalRight' ? 'modal' : stackPresentation;
|
|
23
|
-
const route = useMemo(() => ({
|
|
24
|
-
presentation: stackPresentation ?? 'push',
|
|
25
|
-
params: item.params,
|
|
26
|
-
query: item.query,
|
|
27
|
-
pattern: item.pattern,
|
|
28
|
-
path: item.path
|
|
29
|
-
}), [stackPresentation, item.params, item.query, item.pattern, item.path]);
|
|
30
|
-
const router = useRouter();
|
|
31
|
-
const onDismissed = useCallback(() => {
|
|
32
|
-
if (stackId) {
|
|
33
|
-
const history = router.getStackHistory(stackId);
|
|
34
|
-
const topKey = history.length ? history[history.length - 1]?.key : null;
|
|
35
|
-
if (topKey && topKey === item.key) {
|
|
36
|
-
router.goBack();
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
}, [stackId, router, item.key]);
|
|
40
|
-
const headerConfig = useMemo(() => ({
|
|
41
|
-
...header,
|
|
42
|
-
hidden: !header?.title || header?.hidden,
|
|
43
|
-
backgroundColor: appearance?.header?.backgroundColor ?? 'transparent'
|
|
44
|
-
}), [header, appearance?.header?.backgroundColor]);
|
|
45
|
-
if (route.presentation === 'sheet') {
|
|
46
|
-
return /*#__PURE__*/_jsx(ScreenStackSheetItem, {
|
|
47
|
-
appearance: appearance?.sheet,
|
|
48
|
-
onDismissed: onDismissed,
|
|
49
|
-
route: route,
|
|
50
|
-
item: item
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
return /*#__PURE__*/_jsx(RNSScreenStackItem, {
|
|
54
|
-
...screenProps,
|
|
55
|
-
screenId: item.key,
|
|
56
|
-
onDismissed: onDismissed,
|
|
57
|
-
style: StyleSheet.absoluteFill,
|
|
58
|
-
contentStyle: appearance?.screen,
|
|
59
|
-
headerConfig: headerConfig,
|
|
60
|
-
stackPresentation: nativePresentation,
|
|
61
|
-
stackAnimation: stackAnimation ?? item.options?.stackAnimation,
|
|
62
|
-
children: /*#__PURE__*/_jsx(RouteLocalContext.Provider, {
|
|
63
|
-
value: route,
|
|
64
|
-
children: /*#__PURE__*/_jsx(item.component, {
|
|
65
|
-
...item.passProps,
|
|
66
|
-
appearance: appearance
|
|
67
|
-
})
|
|
68
|
-
})
|
|
69
|
-
}, item.key);
|
|
70
|
-
});
|
|
@@ -1,171 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
import { RouteLocalContext, useRouter } from "../RouterContext.js";
|
|
4
|
-
import { isModalLikePresentation } from "../types.js";
|
|
5
|
-
import { memo, useMemo, useCallback, useEffect, useRef } from 'react';
|
|
6
|
-
import { StyleSheet, View } from 'react-native';
|
|
7
|
-
import { useScreenStackItemsContext } from "../ScreenStack/ScreenStackContext.js";
|
|
8
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
9
|
-
export const ScreenStackItem = /*#__PURE__*/memo(({
|
|
10
|
-
item,
|
|
11
|
-
appearance,
|
|
12
|
-
style
|
|
13
|
-
}) => {
|
|
14
|
-
const itemsContext = useScreenStackItemsContext();
|
|
15
|
-
const router = useRouter();
|
|
16
|
-
const containerRef = useRef(null);
|
|
17
|
-
const debugEnabled = router.isDebugEnabled();
|
|
18
|
-
const devLog = useCallback((msg, data) => {
|
|
19
|
-
if (!debugEnabled) return;
|
|
20
|
-
console.log(msg, data !== undefined ? JSON.stringify(data) : '');
|
|
21
|
-
}, [debugEnabled]);
|
|
22
|
-
const key = item.key;
|
|
23
|
-
const itemState = itemsContext.items[key];
|
|
24
|
-
const presentationType = itemState?.presentationType;
|
|
25
|
-
const animationType = itemState?.animationType;
|
|
26
|
-
const phase = itemState?.phase;
|
|
27
|
-
const transitionStatus = itemState?.transitionStatus;
|
|
28
|
-
const zIndex = itemState?.zIndex ?? 0;
|
|
29
|
-
const presentation = item.options?.stackPresentation ?? 'push';
|
|
30
|
-
const isModalLike = isModalLikePresentation(presentation);
|
|
31
|
-
const className = useMemo(() => {
|
|
32
|
-
const classes = ['screen-stack-item'];
|
|
33
|
-
if (presentationType) {
|
|
34
|
-
classes.push(presentationType);
|
|
35
|
-
}
|
|
36
|
-
if (animationType) {
|
|
37
|
-
classes.push(animationType);
|
|
38
|
-
}
|
|
39
|
-
if (transitionStatus) {
|
|
40
|
-
classes.push(`transition-${transitionStatus}`);
|
|
41
|
-
}
|
|
42
|
-
if (phase) {
|
|
43
|
-
classes.push(`phase-${phase}`);
|
|
44
|
-
}
|
|
45
|
-
devLog('[ScreenStackItem] className', {
|
|
46
|
-
key: item.key,
|
|
47
|
-
path: item.path,
|
|
48
|
-
presentationType,
|
|
49
|
-
animationType,
|
|
50
|
-
phase,
|
|
51
|
-
transitionStatus,
|
|
52
|
-
className: classes.join(' ')
|
|
53
|
-
});
|
|
54
|
-
return classes.join(' ');
|
|
55
|
-
}, [presentationType, animationType, transitionStatus, phase, devLog, item.key, item.path]);
|
|
56
|
-
const mergedStyle = useMemo(() => ({
|
|
57
|
-
flex: 1,
|
|
58
|
-
...style,
|
|
59
|
-
zIndex
|
|
60
|
-
}), [style, zIndex]);
|
|
61
|
-
const modalContainerStyle = useMemo(() => {
|
|
62
|
-
if (!isModalLike || !item.options?.maxWidth) {
|
|
63
|
-
return undefined;
|
|
64
|
-
}
|
|
65
|
-
return {
|
|
66
|
-
maxWidth: `${item.options.maxWidth}px`
|
|
67
|
-
};
|
|
68
|
-
}, [isModalLike, item.options?.maxWidth]);
|
|
69
|
-
const routeValue = useMemo(() => ({
|
|
70
|
-
presentation,
|
|
71
|
-
params: item.params,
|
|
72
|
-
query: item.query,
|
|
73
|
-
pattern: item.pattern,
|
|
74
|
-
path: item.path
|
|
75
|
-
}), [presentation, item.params, item.query, item.pattern, item.path]);
|
|
76
|
-
const handleDismiss = useCallback(() => router.dismiss(), [router]);
|
|
77
|
-
const handleDismissKeyDown = useCallback(e => {
|
|
78
|
-
if (e.key === 'Enter' || e.key === ' ') {
|
|
79
|
-
e.preventDefault?.();
|
|
80
|
-
router.dismiss();
|
|
81
|
-
}
|
|
82
|
-
}, [router]);
|
|
83
|
-
|
|
84
|
-
// Escape key handler for modals — only dismiss if this is the topmost modal
|
|
85
|
-
useEffect(() => {
|
|
86
|
-
if (!isModalLike) return;
|
|
87
|
-
const handler = e => {
|
|
88
|
-
if (e.key !== 'Escape') return;
|
|
89
|
-
const allModals = document.querySelectorAll('[aria-modal="true"]');
|
|
90
|
-
if (allModals.length > 0) {
|
|
91
|
-
const last = allModals[allModals.length - 1] ?? null;
|
|
92
|
-
if (!containerRef.current?.contains(last)) return;
|
|
93
|
-
}
|
|
94
|
-
router.dismiss();
|
|
95
|
-
};
|
|
96
|
-
document.addEventListener('keydown', handler);
|
|
97
|
-
return () => document.removeEventListener('keydown', handler);
|
|
98
|
-
}, [isModalLike, router]);
|
|
99
|
-
|
|
100
|
-
// Focus trap for modals — re-queries focusable elements on each Tab
|
|
101
|
-
useEffect(() => {
|
|
102
|
-
if (!isModalLike || !containerRef.current) return;
|
|
103
|
-
const container = containerRef.current;
|
|
104
|
-
const FOCUSABLE = 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])';
|
|
105
|
-
const focusable = container.querySelectorAll(FOCUSABLE);
|
|
106
|
-
focusable[0]?.focus();
|
|
107
|
-
const trap = e => {
|
|
108
|
-
if (e.key !== 'Tab') return;
|
|
109
|
-
const els = container.querySelectorAll(FOCUSABLE);
|
|
110
|
-
if (!els.length) return;
|
|
111
|
-
const first = els[0];
|
|
112
|
-
const last = els[els.length - 1];
|
|
113
|
-
if (e.shiftKey && document.activeElement === first) {
|
|
114
|
-
e.preventDefault();
|
|
115
|
-
last?.focus();
|
|
116
|
-
} else if (!e.shiftKey && document.activeElement === last) {
|
|
117
|
-
e.preventDefault();
|
|
118
|
-
first?.focus();
|
|
119
|
-
}
|
|
120
|
-
};
|
|
121
|
-
container.addEventListener('keydown', trap);
|
|
122
|
-
return () => container.removeEventListener('keydown', trap);
|
|
123
|
-
}, [isModalLike]);
|
|
124
|
-
if (!itemState) {
|
|
125
|
-
return null;
|
|
126
|
-
}
|
|
127
|
-
return /*#__PURE__*/_jsxs("div", {
|
|
128
|
-
ref: containerRef,
|
|
129
|
-
style: mergedStyle,
|
|
130
|
-
className: className,
|
|
131
|
-
children: [isModalLike && /*#__PURE__*/_jsx("div", {
|
|
132
|
-
className: "stack-modal-overlay",
|
|
133
|
-
onClick: handleDismiss,
|
|
134
|
-
onKeyDown: handleDismissKeyDown,
|
|
135
|
-
role: "button",
|
|
136
|
-
"aria-label": "Close modal",
|
|
137
|
-
tabIndex: 0
|
|
138
|
-
}), /*#__PURE__*/_jsx("div", {
|
|
139
|
-
className: isModalLike ? 'stack-modal-container' : 'stack-screen-container',
|
|
140
|
-
style: modalContainerStyle,
|
|
141
|
-
...(isModalLike ? {
|
|
142
|
-
'role': 'dialog',
|
|
143
|
-
'aria-modal': true,
|
|
144
|
-
'aria-label': item.pattern || 'Dialog'
|
|
145
|
-
} : undefined),
|
|
146
|
-
children: appearance?.screen ? /*#__PURE__*/_jsx(View, {
|
|
147
|
-
style: [appearance?.screen, styles.flex],
|
|
148
|
-
children: /*#__PURE__*/_jsx(RouteLocalContext.Provider, {
|
|
149
|
-
value: routeValue,
|
|
150
|
-
children: /*#__PURE__*/_jsx(item.component, {
|
|
151
|
-
...(item.passProps || {}),
|
|
152
|
-
appearance: appearance
|
|
153
|
-
})
|
|
154
|
-
})
|
|
155
|
-
}) : /*#__PURE__*/_jsx(RouteLocalContext.Provider, {
|
|
156
|
-
value: routeValue,
|
|
157
|
-
children: /*#__PURE__*/_jsx(item.component, {
|
|
158
|
-
...(item.passProps || {}),
|
|
159
|
-
appearance: appearance
|
|
160
|
-
})
|
|
161
|
-
})
|
|
162
|
-
})]
|
|
163
|
-
});
|
|
164
|
-
}, (prevProps, nextProps) => {
|
|
165
|
-
return prevProps.item.key === nextProps.item.key && prevProps.item === nextProps.item && prevProps.appearance === nextProps.appearance && prevProps.style === nextProps.style;
|
|
166
|
-
});
|
|
167
|
-
const styles = StyleSheet.create({
|
|
168
|
-
flex: {
|
|
169
|
-
flex: 1
|
|
170
|
-
}
|
|
171
|
-
});
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
import { ScreenStackItem as RNSScreenStackItem } from 'react-native-screens';
|
|
4
|
-
import { NativeSheetView, Commands } from '@sigmela/native-sheet';
|
|
5
|
-
import { RouteLocalContext, useRouter } from "../RouterContext.js";
|
|
6
|
-
import { memo, useRef, useEffect, useCallback } from 'react';
|
|
7
|
-
import { StyleSheet } from 'react-native';
|
|
8
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
9
|
-
export const ScreenStackSheetItem = /*#__PURE__*/memo(props => {
|
|
10
|
-
const {
|
|
11
|
-
item,
|
|
12
|
-
onDismissed,
|
|
13
|
-
route,
|
|
14
|
-
appearance
|
|
15
|
-
} = props;
|
|
16
|
-
const {
|
|
17
|
-
convertModalToSheetForAndroid
|
|
18
|
-
} = item.options || {};
|
|
19
|
-
const headerConfig = {
|
|
20
|
-
hidden: true
|
|
21
|
-
};
|
|
22
|
-
const ref = useRef(null);
|
|
23
|
-
const router = useRouter();
|
|
24
|
-
useEffect(() => {
|
|
25
|
-
if (route.presentation === 'sheet') {
|
|
26
|
-
router.registerSheetDismisser(item.key, () => {
|
|
27
|
-
if (ref.current) {
|
|
28
|
-
Commands.dismiss(ref.current);
|
|
29
|
-
}
|
|
30
|
-
});
|
|
31
|
-
return () => router.unregisterSheetDismisser(item.key);
|
|
32
|
-
}
|
|
33
|
-
return undefined;
|
|
34
|
-
}, [route.presentation, router, item.key]);
|
|
35
|
-
const handleSheetDismissed = useCallback(() => {
|
|
36
|
-
router.unregisterSheetDismisser(item.key);
|
|
37
|
-
onDismissed();
|
|
38
|
-
}, [router, item.key, onDismissed]);
|
|
39
|
-
return /*#__PURE__*/_jsx(RNSScreenStackItem, {
|
|
40
|
-
stackPresentation: "transparentModal",
|
|
41
|
-
headerConfig: headerConfig,
|
|
42
|
-
style: StyleSheet.absoluteFill,
|
|
43
|
-
contentStyle: styles.content,
|
|
44
|
-
screenId: item.key,
|
|
45
|
-
stackAnimation: "none",
|
|
46
|
-
onDismissed: onDismissed,
|
|
47
|
-
children: /*#__PURE__*/_jsx(NativeSheetView, {
|
|
48
|
-
containerBackgroundColor: appearance?.backgroundColor,
|
|
49
|
-
cornerRadius: appearance?.cornerRadius ?? 18,
|
|
50
|
-
onDismissed: handleSheetDismissed,
|
|
51
|
-
style: styles.flex,
|
|
52
|
-
ref: ref,
|
|
53
|
-
fullscreenTopInset: convertModalToSheetForAndroid ? appearance?.androidFullScreenTopInset ?? 40 : undefined,
|
|
54
|
-
children: /*#__PURE__*/_jsx(RouteLocalContext.Provider, {
|
|
55
|
-
value: route,
|
|
56
|
-
children: /*#__PURE__*/_jsx(item.component, {
|
|
57
|
-
...item.passProps
|
|
58
|
-
})
|
|
59
|
-
})
|
|
60
|
-
})
|
|
61
|
-
}, `${item.key}-sheet-item`);
|
|
62
|
-
});
|
|
63
|
-
const styles = StyleSheet.create({
|
|
64
|
-
flex: {
|
|
65
|
-
flex: 1
|
|
66
|
-
},
|
|
67
|
-
content: {
|
|
68
|
-
backgroundColor: 'transparent'
|
|
69
|
-
}
|
|
70
|
-
});
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
import { ScreenStackItem } from "../ScreenStackItem/index.js";
|
|
4
|
-
import { ScreenStack } from "../ScreenStack/index.js";
|
|
5
|
-
import { SplitViewContext } from "./SplitViewContext.js";
|
|
6
|
-
import { useRouter } from "../RouterContext.js";
|
|
7
|
-
import { memo, useMemo, useRef, useState, useEffect, startTransition } from 'react';
|
|
8
|
-
import { StyleSheet } from 'react-native';
|
|
9
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
10
|
-
const EMPTY_HISTORY = [];
|
|
11
|
-
/**
|
|
12
|
-
* On native, SplitView renders primary and secondary screens in a SINGLE
|
|
13
|
-
* ScreenStack to get native push/pop animations.
|
|
14
|
-
*
|
|
15
|
-
* The combined history is: [...primaryHistory, ...secondaryHistory]
|
|
16
|
-
* This way, navigating from primary to secondary is a native push.
|
|
17
|
-
*
|
|
18
|
-
* History starts empty and is populated via useEffect + startTransition,
|
|
19
|
-
* following the pattern from react-native-screens' BottomTabsContainer.
|
|
20
|
-
* This lets the parent Screen attach before child ScreenStackItems mount.
|
|
21
|
-
*/
|
|
22
|
-
export const RenderSplitView = /*#__PURE__*/memo(({
|
|
23
|
-
splitView,
|
|
24
|
-
appearance
|
|
25
|
-
}) => {
|
|
26
|
-
const router = useRouter();
|
|
27
|
-
const primaryId = splitView.primary.getId();
|
|
28
|
-
const secondaryId = splitView.secondary.getId();
|
|
29
|
-
|
|
30
|
-
// Start with empty history — useEffect populates via startTransition.
|
|
31
|
-
const hydratedRef = useRef(false);
|
|
32
|
-
const [primaryHistory, setPrimaryHistory] = useState(EMPTY_HISTORY);
|
|
33
|
-
const [secondaryHistory, setSecondaryHistory] = useState(EMPTY_HISTORY);
|
|
34
|
-
useEffect(() => {
|
|
35
|
-
hydratedRef.current = true;
|
|
36
|
-
const updatePrimary = () => {
|
|
37
|
-
startTransition(() => {
|
|
38
|
-
setPrimaryHistory(router.getStackHistory(primaryId));
|
|
39
|
-
});
|
|
40
|
-
};
|
|
41
|
-
const updateSecondary = () => {
|
|
42
|
-
startTransition(() => {
|
|
43
|
-
setSecondaryHistory(router.getStackHistory(secondaryId));
|
|
44
|
-
});
|
|
45
|
-
};
|
|
46
|
-
const unsub1 = router.subscribeStack(primaryId, updatePrimary);
|
|
47
|
-
const unsub2 = router.subscribeStack(secondaryId, updateSecondary);
|
|
48
|
-
|
|
49
|
-
// Sync in case the store changed between useState init and useEffect
|
|
50
|
-
updatePrimary();
|
|
51
|
-
updateSecondary();
|
|
52
|
-
return () => {
|
|
53
|
-
unsub1();
|
|
54
|
-
unsub2();
|
|
55
|
-
};
|
|
56
|
-
}, [router, primaryId, secondaryId]);
|
|
57
|
-
|
|
58
|
-
// Fallback: if primary is empty, seed with first route.
|
|
59
|
-
// Skip the seed during the initial empty mount (before useEffect
|
|
60
|
-
// hydrates history) so no ScreenStackItems mount before the parent Fragment attaches.
|
|
61
|
-
const primaryHistoryToRender = useMemo(() => {
|
|
62
|
-
if (primaryHistory.length > 0) {
|
|
63
|
-
return primaryHistory;
|
|
64
|
-
}
|
|
65
|
-
if (!hydratedRef.current) return [];
|
|
66
|
-
const first = splitView.primary.getFirstRoute();
|
|
67
|
-
if (!first) return [];
|
|
68
|
-
const activePath = router.getActiveRoute()?.path;
|
|
69
|
-
return [{
|
|
70
|
-
key: `splitview-seed-${primaryId}`,
|
|
71
|
-
routeId: first.routeId,
|
|
72
|
-
component: first.component,
|
|
73
|
-
options: first.options,
|
|
74
|
-
stackId: primaryId,
|
|
75
|
-
pattern: first.path,
|
|
76
|
-
path: activePath ?? first.path
|
|
77
|
-
}];
|
|
78
|
-
}, [primaryHistory, splitView.primary, primaryId, router]);
|
|
79
|
-
|
|
80
|
-
// Combine histories: primary screens first, then secondary screens on top
|
|
81
|
-
// This gives native push animation when navigating from primary to secondary
|
|
82
|
-
const combinedHistory = useMemo(() => {
|
|
83
|
-
return [...primaryHistoryToRender, ...secondaryHistory];
|
|
84
|
-
}, [primaryHistoryToRender, secondaryHistory]);
|
|
85
|
-
return /*#__PURE__*/_jsx(SplitViewContext.Provider, {
|
|
86
|
-
value: splitView,
|
|
87
|
-
children: /*#__PURE__*/_jsx(ScreenStack, {
|
|
88
|
-
style: styles.container,
|
|
89
|
-
children: combinedHistory.map(item => /*#__PURE__*/_jsx(ScreenStackItem, {
|
|
90
|
-
appearance: appearance,
|
|
91
|
-
stackId: item.stackId,
|
|
92
|
-
item: item
|
|
93
|
-
}, `splitview-${item.key}`))
|
|
94
|
-
})
|
|
95
|
-
});
|
|
96
|
-
});
|
|
97
|
-
const styles = StyleSheet.create({
|
|
98
|
-
container: {
|
|
99
|
-
flex: 1
|
|
100
|
-
}
|
|
101
|
-
});
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
import { StackRenderer } from "../StackRenderer.js";
|
|
4
|
-
import { SplitViewContext } from "./SplitViewContext.js";
|
|
5
|
-
import { useRouter } from "../RouterContext.js";
|
|
6
|
-
import { ScreenStackConfigContext } from "../ScreenStack/ScreenStackContext.js";
|
|
7
|
-
import { memo, useCallback, useEffect, useMemo, useState, useSyncExternalStore } from 'react';
|
|
8
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
9
|
-
const StackSliceRenderer = /*#__PURE__*/memo(({
|
|
10
|
-
stack,
|
|
11
|
-
appearance,
|
|
12
|
-
fallbackToFirstRoute
|
|
13
|
-
}) => {
|
|
14
|
-
const router = useRouter();
|
|
15
|
-
const stackId = stack.getId();
|
|
16
|
-
const subscribe = useCallback(cb => router.subscribeStack(stackId, cb), [router, stackId]);
|
|
17
|
-
const get = useCallback(() => router.getStackHistory(stackId), [router, stackId]);
|
|
18
|
-
const history = useSyncExternalStore(subscribe, get, get);
|
|
19
|
-
let historyToRender = history;
|
|
20
|
-
|
|
21
|
-
// When a container route (like /mail -> SplitView) is active, the child stack can be empty,
|
|
22
|
-
// yet we still want to render its root screen. We keep Router history intact and provide a
|
|
23
|
-
// renderer-only fallback item.
|
|
24
|
-
if (fallbackToFirstRoute && historyToRender.length === 0) {
|
|
25
|
-
const first = stack.getFirstRoute();
|
|
26
|
-
if (first) {
|
|
27
|
-
const activePath = router.getActiveRoute()?.path;
|
|
28
|
-
historyToRender = [{
|
|
29
|
-
key: `splitview-seed-${stackId}`,
|
|
30
|
-
routeId: first.routeId,
|
|
31
|
-
component: first.component,
|
|
32
|
-
options: first.options,
|
|
33
|
-
stackId,
|
|
34
|
-
pattern: first.path,
|
|
35
|
-
path: activePath ?? first.path
|
|
36
|
-
}];
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
return /*#__PURE__*/_jsx(StackRenderer, {
|
|
40
|
-
appearance: appearance,
|
|
41
|
-
stackId: stackId,
|
|
42
|
-
history: historyToRender
|
|
43
|
-
});
|
|
44
|
-
});
|
|
45
|
-
StackSliceRenderer.displayName = 'SplitViewStackSliceRenderer';
|
|
46
|
-
export const RenderSplitView = /*#__PURE__*/memo(({
|
|
47
|
-
splitView,
|
|
48
|
-
appearance
|
|
49
|
-
}) => {
|
|
50
|
-
const [isWide, setIsWide] = useState(() => {
|
|
51
|
-
if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') {
|
|
52
|
-
return false;
|
|
53
|
-
}
|
|
54
|
-
return window.matchMedia(`(min-width: ${splitView.minWidth}px)`).matches;
|
|
55
|
-
});
|
|
56
|
-
useEffect(() => {
|
|
57
|
-
if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') {
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
const mq = window.matchMedia(`(min-width: ${splitView.minWidth}px)`);
|
|
61
|
-
const onChange = ev => setIsWide(ev.matches);
|
|
62
|
-
|
|
63
|
-
// Sync immediately.
|
|
64
|
-
setIsWide(mq.matches);
|
|
65
|
-
if (typeof mq.addEventListener === 'function') {
|
|
66
|
-
mq.addEventListener('change', onChange);
|
|
67
|
-
return () => mq.removeEventListener('change', onChange);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Safari < 14 fallback (deprecated, but still needed).
|
|
71
|
-
const mqAny = mq;
|
|
72
|
-
mqAny.addListener?.(onChange);
|
|
73
|
-
return () => mqAny.removeListener?.(onChange);
|
|
74
|
-
}, [splitView.minWidth]);
|
|
75
|
-
const containerStyle = useMemo(() => {
|
|
76
|
-
return {
|
|
77
|
-
'--split-view-primary-max-width': `${splitView.primaryMaxWidth}px`
|
|
78
|
-
};
|
|
79
|
-
}, [splitView.primaryMaxWidth]);
|
|
80
|
-
const secondaryConfig = useMemo(() => ({
|
|
81
|
-
animateFirstScreenAfterEmpty: !isWide
|
|
82
|
-
}), [isWide]);
|
|
83
|
-
return /*#__PURE__*/_jsx(SplitViewContext.Provider, {
|
|
84
|
-
value: splitView,
|
|
85
|
-
children: /*#__PURE__*/_jsxs("div", {
|
|
86
|
-
className: "split-view-container",
|
|
87
|
-
style: containerStyle,
|
|
88
|
-
children: [/*#__PURE__*/_jsx("div", {
|
|
89
|
-
className: "split-view-primary",
|
|
90
|
-
children: /*#__PURE__*/_jsx(StackSliceRenderer, {
|
|
91
|
-
appearance: appearance,
|
|
92
|
-
stack: splitView.primary,
|
|
93
|
-
fallbackToFirstRoute: true
|
|
94
|
-
})
|
|
95
|
-
}), /*#__PURE__*/_jsx("div", {
|
|
96
|
-
className: "split-view-secondary",
|
|
97
|
-
children: /*#__PURE__*/_jsx(ScreenStackConfigContext.Provider, {
|
|
98
|
-
value: secondaryConfig,
|
|
99
|
-
children: /*#__PURE__*/_jsx(StackSliceRenderer, {
|
|
100
|
-
appearance: appearance,
|
|
101
|
-
stack: splitView.secondary
|
|
102
|
-
})
|
|
103
|
-
})
|
|
104
|
-
})]
|
|
105
|
-
})
|
|
106
|
-
});
|
|
107
|
-
});
|