@natsuneko-laboratory/react-native-desktop-navigation 0.1.0-alpha.12
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/DesktopNavigation.podspec +18 -0
- package/LICENSE +21 -0
- package/NATIVE.md +300 -0
- package/README.md +127 -0
- package/dist/core/NavigationContainer.d.ts +23 -0
- package/dist/core/NavigationContainer.js +68 -0
- package/dist/core/NavigationItem.d.ts +27 -0
- package/dist/core/NavigationItem.js +71 -0
- package/dist/core/Scene.d.ts +19 -0
- package/dist/core/Scene.js +124 -0
- package/dist/core/SelectionNavigator.d.ts +38 -0
- package/dist/core/SelectionNavigator.js +194 -0
- package/dist/core/builder.d.ts +40 -0
- package/dist/core/builder.js +81 -0
- package/dist/core/context.d.ts +26 -0
- package/dist/core/context.js +36 -0
- package/dist/core/focus.d.ts +14 -0
- package/dist/core/focus.js +39 -0
- package/dist/core/hooks.d.ts +8 -0
- package/dist/core/hooks.js +48 -0
- package/dist/core/navigation.d.ts +6 -0
- package/dist/core/navigation.js +57 -0
- package/dist/core/store.d.ts +39 -0
- package/dist/core/store.js +391 -0
- package/dist/core/types.d.ts +155 -0
- package/dist/core/types.js +2 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +46 -0
- package/dist/native/host.d.ts +100 -0
- package/dist/native/host.js +190 -0
- package/dist/native/icons.d.ts +56 -0
- package/dist/native/icons.js +55 -0
- package/dist/native/index.d.ts +13 -0
- package/dist/native/index.js +44 -0
- package/dist/native/sidebar.d.ts +39 -0
- package/dist/native/sidebar.js +126 -0
- package/dist/native/specs/DesktopNavigationHostNativeComponent.d.ts +10 -0
- package/dist/native/specs/DesktopNavigationHostNativeComponent.js +4 -0
- package/dist/native/split.d.ts +27 -0
- package/dist/native/split.js +105 -0
- package/dist/native/stack.d.ts +26 -0
- package/dist/native/stack.js +66 -0
- package/dist/platform/index.d.ts +52 -0
- package/dist/platform/index.js +28 -0
- package/dist/platform/macos.d.ts +2 -0
- package/dist/platform/macos.js +18 -0
- package/dist/platform/windows.d.ts +2 -0
- package/dist/platform/windows.js +18 -0
- package/dist/routers/index.d.ts +23 -0
- package/dist/routers/index.js +393 -0
- package/dist/routers/types.d.ts +116 -0
- package/dist/routers/types.js +2 -0
- package/dist/sidebar/index.d.ts +17 -0
- package/dist/sidebar/index.js +18 -0
- package/dist/split/index.d.ts +42 -0
- package/dist/split/index.js +199 -0
- package/dist/stack/index.d.ts +23 -0
- package/dist/stack/index.js +88 -0
- package/dist/tabs/index.d.ts +13 -0
- package/dist/tabs/index.js +15 -0
- package/docs/GUIDE.md +384 -0
- package/macos/DDNNavigationComponentView.h +4 -0
- package/macos/DDNNavigationComponentView.mm +40 -0
- package/macos/DDNNavigationView.swift +398 -0
- package/macos/DDNNavigationViewManager.mm +11 -0
- package/package.json +82 -0
- package/react-native.config.js +19 -0
- package/src/core/NavigationContainer.tsx +137 -0
- package/src/core/NavigationItem.tsx +149 -0
- package/src/core/Scene.tsx +194 -0
- package/src/core/SelectionNavigator.tsx +371 -0
- package/src/core/builder.tsx +147 -0
- package/src/core/context.tsx +43 -0
- package/src/core/focus.tsx +50 -0
- package/src/core/hooks.ts +42 -0
- package/src/core/navigation.ts +77 -0
- package/src/core/store.ts +471 -0
- package/src/core/types.ts +176 -0
- package/src/index.ts +36 -0
- package/src/native/host.tsx +377 -0
- package/src/native/icons.ts +110 -0
- package/src/native/index.ts +56 -0
- package/src/native/sidebar.tsx +230 -0
- package/src/native/specs/DesktopNavigationHostNativeComponent.ts +12 -0
- package/src/native/split.tsx +160 -0
- package/src/native/stack.tsx +157 -0
- package/src/platform/index.tsx +76 -0
- package/src/platform/macos.ts +15 -0
- package/src/platform/windows.ts +15 -0
- package/src/routers/index.ts +442 -0
- package/src/routers/types.ts +117 -0
- package/src/sidebar/index.tsx +42 -0
- package/src/split/index.tsx +350 -0
- package/src/stack/index.tsx +213 -0
- package/src/tabs/index.tsx +40 -0
- package/windows/DesktopNavigation/DesktopNavigation.def +3 -0
- package/windows/DesktopNavigation/DesktopNavigation.vcxproj +125 -0
- package/windows/DesktopNavigation/NavigationHost.cpp +432 -0
- package/windows/DesktopNavigation/ReactPackageProvider.cpp +9 -0
- package/windows/DesktopNavigation/ReactPackageProvider.h +10 -0
- package/windows/DesktopNavigation/ReactPackageProvider.idl +6 -0
- package/windows/DesktopNavigation/pch.cpp +1 -0
- package/windows/DesktopNavigation/pch.h +27 -0
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.createSplitNavigator = createSplitNavigator;
|
|
7
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
8
|
+
const react_1 = __importDefault(require("react"));
|
|
9
|
+
const react_native_1 = require("react-native");
|
|
10
|
+
const routers_1 = require("../routers");
|
|
11
|
+
const builder_1 = require("../core/builder");
|
|
12
|
+
const Scene_1 = require("../core/Scene");
|
|
13
|
+
const context_1 = require("../core/context");
|
|
14
|
+
const platform_1 = require("../platform");
|
|
15
|
+
function Divider({ column, width, resize, style, renderContent, onWidthChange, }) {
|
|
16
|
+
const [dragging, setDragging] = react_1.default.useState(false);
|
|
17
|
+
const start = react_1.default.useRef({ width, pageX: 0 });
|
|
18
|
+
const latest = react_1.default.useRef({ width, resize });
|
|
19
|
+
latest.current = { width, resize };
|
|
20
|
+
const responder = react_1.default.useMemo(() => react_native_1.PanResponder.create({
|
|
21
|
+
onStartShouldSetPanResponder: () => true,
|
|
22
|
+
onMoveShouldSetPanResponder: () => true,
|
|
23
|
+
onPanResponderGrant: (event) => {
|
|
24
|
+
start.current = {
|
|
25
|
+
width: latest.current.width,
|
|
26
|
+
pageX: event.nativeEvent.pageX,
|
|
27
|
+
};
|
|
28
|
+
setDragging(true);
|
|
29
|
+
},
|
|
30
|
+
// Use root coordinates: the handle itself moves during resizing.
|
|
31
|
+
onPanResponderMove: (event) => latest.current.resize(start.current.width + event.nativeEvent.pageX - start.current.pageX),
|
|
32
|
+
onPanResponderRelease: () => setDragging(false),
|
|
33
|
+
onPanResponderTerminate: () => setDragging(false),
|
|
34
|
+
onPanResponderTerminationRequest: () => false,
|
|
35
|
+
}), []);
|
|
36
|
+
const { colors } = (0, context_1.useNavigationTheme)();
|
|
37
|
+
const context = { columnId: column.id, width, dragging };
|
|
38
|
+
return ((0, jsx_runtime_1.jsx)(platform_1.DesktopView, { ...responder.panHandlers, testID: `split-divider-${column.id}`, style: styles.dividerHitArea, pointerEvents: "box-only", accessible: true, focusable: true, accessibilityRole: "adjustable", accessibilityLabel: `Resize ${column.id}`, accessibilityValue: {
|
|
39
|
+
min: column.minWidth ?? 100,
|
|
40
|
+
max: column.maxWidth,
|
|
41
|
+
now: width,
|
|
42
|
+
}, accessibilityActions: [{ name: 'increment' }, { name: 'decrement' }], onAccessibilityAction: (event) => resize(width + (event.nativeEvent.actionName === 'increment' ? 10 : -10)), keyDownEvents: [{ key: 'ArrowLeft' }, { key: 'ArrowRight' }], onKeyDown: (event) => {
|
|
43
|
+
if (!(0, platform_1.eventHandled)(event) &&
|
|
44
|
+
['ArrowLeft', 'ArrowRight'].includes(event.nativeEvent.key)) {
|
|
45
|
+
resize(width + (event.nativeEvent.key === 'ArrowRight' ? 10 : -10));
|
|
46
|
+
(0, platform_1.consumeKey)(event);
|
|
47
|
+
}
|
|
48
|
+
}, children: (0, jsx_runtime_1.jsx)(react_native_1.View, { testID: `split-divider-visual-${column.id}`, onLayout: (event) => onWidthChange(event.nativeEvent.layout.width), accessible: false, style: [
|
|
49
|
+
{ flex: 1, width: 6, backgroundColor: colors.border },
|
|
50
|
+
typeof style === 'function' ? style(context) : style,
|
|
51
|
+
typeof column.dividerStyle === 'function'
|
|
52
|
+
? column.dividerStyle(context)
|
|
53
|
+
: column.dividerStyle,
|
|
54
|
+
], children: (column.renderDivider ?? renderContent)?.(context) }) }));
|
|
55
|
+
}
|
|
56
|
+
function createSplitNavigator() {
|
|
57
|
+
function Column(_props) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
function Navigator(props) {
|
|
61
|
+
const columns = [];
|
|
62
|
+
react_1.default.Children.forEach(props.children, (child) => {
|
|
63
|
+
if (child == null || typeof child === 'boolean')
|
|
64
|
+
return;
|
|
65
|
+
if (!react_1.default.isValidElement(child) || child.type !== Column)
|
|
66
|
+
throw new Error('Split.Navigator children must be Split.Column elements.');
|
|
67
|
+
columns.push(child.props);
|
|
68
|
+
});
|
|
69
|
+
const { id, state, store } = (0, builder_1.useNavigator)('split', { routes: columns.map((c) => ({ ...c, name: c.id })) }, props.id);
|
|
70
|
+
const [size, setSize] = react_1.default.useState(null);
|
|
71
|
+
const [dividerWidths, setDividerWidths] = react_1.default.useState({});
|
|
72
|
+
const [collapsedWidths, setCollapsedWidths] = react_1.default.useState({});
|
|
73
|
+
const onSidebarChange = react_1.default.useCallback((columnId, width) => {
|
|
74
|
+
setCollapsedWidths((previous) => {
|
|
75
|
+
if (previous[columnId] === width)
|
|
76
|
+
return previous;
|
|
77
|
+
return { ...previous, [columnId]: width };
|
|
78
|
+
});
|
|
79
|
+
}, []);
|
|
80
|
+
const layout = props.layout;
|
|
81
|
+
const dividerSpace = (ids) => ids.slice(0, -1).reduce((total, columnId) => {
|
|
82
|
+
const column = columns.find((c) => c.id === columnId);
|
|
83
|
+
return (total +
|
|
84
|
+
((column?.dividerShown ?? props.dividerShown ?? true)
|
|
85
|
+
? (dividerWidths[columnId] ?? 6)
|
|
86
|
+
: 0));
|
|
87
|
+
}, 0);
|
|
88
|
+
react_1.default.useLayoutEffect(() => {
|
|
89
|
+
const ids = size
|
|
90
|
+
? layout
|
|
91
|
+
? layout(size)
|
|
92
|
+
: columns.map((c) => c.id)
|
|
93
|
+
: state.visibleColumnIds;
|
|
94
|
+
const before = store.getNode(id).state;
|
|
95
|
+
store.dispatch({
|
|
96
|
+
target: id,
|
|
97
|
+
type: 'layout',
|
|
98
|
+
payload: {
|
|
99
|
+
ids,
|
|
100
|
+
availableWidth: size ? size.width - dividerSpace(ids) : undefined,
|
|
101
|
+
collapsedWidths: Object.fromEntries(columns
|
|
102
|
+
.filter((c) => c.collapsible === false || c.id in collapsedWidths)
|
|
103
|
+
.map((c) => [
|
|
104
|
+
c.id,
|
|
105
|
+
c.collapsible === false ? null : collapsedWidths[c.id],
|
|
106
|
+
])),
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
const after = store.getNode(id).state;
|
|
110
|
+
if (before.collapsedColumns !== after.collapsedColumns) {
|
|
111
|
+
for (const columnId of Object.keys(after.widths)) {
|
|
112
|
+
if (before.widths[columnId] !== after.widths[columnId])
|
|
113
|
+
props.onColumnResize?.(columnId, after.widths[columnId]);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
const ordered = [
|
|
118
|
+
...state.visibleColumnIds,
|
|
119
|
+
...columns
|
|
120
|
+
.filter((c) => !state.visibleColumnIds.includes(c.id))
|
|
121
|
+
.map((c) => c.id),
|
|
122
|
+
];
|
|
123
|
+
return ((0, jsx_runtime_1.jsx)(builder_1.NavigatorStateContext.Provider, { value: state, children: (0, jsx_runtime_1.jsx)(react_native_1.View, { style: [styles.split, props.style], children: (0, jsx_runtime_1.jsx)(react_native_1.View, { testID: "split-layout", style: styles.row, onLayout: (event) => {
|
|
124
|
+
const { width, height } = event.nativeEvent.layout;
|
|
125
|
+
setSize((previous) => previous?.width === width && previous.height === height
|
|
126
|
+
? previous
|
|
127
|
+
: { width, height });
|
|
128
|
+
}, children: ordered.map((columnId) => {
|
|
129
|
+
const column = columns.find((c) => c.id === columnId);
|
|
130
|
+
const constraints = (0, routers_1.splitColumnConfig)(state, {
|
|
131
|
+
...column,
|
|
132
|
+
name: columnId,
|
|
133
|
+
});
|
|
134
|
+
const route = state.routes.find((r) => r.name === columnId);
|
|
135
|
+
const visible = state.visibleColumnIds.includes(columnId);
|
|
136
|
+
const last = columnId ===
|
|
137
|
+
state.visibleColumnIds[state.visibleColumnIds.length - 1];
|
|
138
|
+
const resize = (width) => {
|
|
139
|
+
const before = store.getNode(id).state;
|
|
140
|
+
store.dispatch({
|
|
141
|
+
target: id,
|
|
142
|
+
// Native layout precedes input. The fallback supports pre-layout imperative use.
|
|
143
|
+
type: size ? 'resizeBoundary' : 'resize',
|
|
144
|
+
payload: { id: columnId, width },
|
|
145
|
+
});
|
|
146
|
+
const after = store.getNode(id).state;
|
|
147
|
+
for (const changedId of after.visibleColumnIds) {
|
|
148
|
+
if (before.widths[changedId] !== after.widths[changedId])
|
|
149
|
+
props.onColumnResize?.(changedId, after.widths[changedId]);
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
return ((0, jsx_runtime_1.jsxs)(react_1.default.Fragment, { children: [(0, jsx_runtime_1.jsx)(react_native_1.View, { testID: `split-column-${columnId}`, style: [
|
|
153
|
+
props.columnStyle,
|
|
154
|
+
column.style,
|
|
155
|
+
{
|
|
156
|
+
overflow: 'hidden',
|
|
157
|
+
width: state.widths[columnId],
|
|
158
|
+
minWidth: constraints.minWidth ?? 100,
|
|
159
|
+
maxWidth: constraints.maxWidth,
|
|
160
|
+
flexBasis: 'auto',
|
|
161
|
+
flexGrow: !size && last && !state.collapsedColumns?.[columnId]
|
|
162
|
+
? 1
|
|
163
|
+
: 0,
|
|
164
|
+
flexShrink: 0,
|
|
165
|
+
},
|
|
166
|
+
!visible && { display: 'none' },
|
|
167
|
+
], children: (0, jsx_runtime_1.jsx)(context_1.SplitColumnContext.Provider, { value: { nodeId: id, columnId, onSidebarChange }, children: (0, jsx_runtime_1.jsx)(Scene_1.Scene, { nodeId: id, route: route, component: column.component, visible: visible, options: {
|
|
168
|
+
inactiveBehavior: 'keep',
|
|
169
|
+
focusBehavior: 'none',
|
|
170
|
+
contentStyle: column.contentStyle,
|
|
171
|
+
} }) }) }), visible &&
|
|
172
|
+
!last &&
|
|
173
|
+
(column.dividerShown ?? props.dividerShown ?? true) && ((0, jsx_runtime_1.jsx)(Divider, { column: {
|
|
174
|
+
...column,
|
|
175
|
+
minWidth: constraints.minWidth,
|
|
176
|
+
maxWidth: constraints.maxWidth,
|
|
177
|
+
}, width: state.widths[columnId], resize: resize, style: props.dividerStyle, renderContent: props.renderDivider, onWidthChange: (width) => {
|
|
178
|
+
if (!Number.isFinite(width) || width < 0)
|
|
179
|
+
return;
|
|
180
|
+
setDividerWidths((previous) => previous[columnId] === width
|
|
181
|
+
? previous
|
|
182
|
+
: { ...previous, [columnId]: width });
|
|
183
|
+
} }))] }, route.key));
|
|
184
|
+
}) }) }) }));
|
|
185
|
+
}
|
|
186
|
+
return { Navigator, Column };
|
|
187
|
+
}
|
|
188
|
+
const styles = react_native_1.StyleSheet.create({
|
|
189
|
+
split: { flex: 1, flexDirection: 'row', overflow: 'hidden' },
|
|
190
|
+
row: { flex: 1, minWidth: 0, flexDirection: 'row', overflow: 'hidden' },
|
|
191
|
+
dividerHitArea: {
|
|
192
|
+
// Enlarge the native view's bounds; negative margins keep the columns
|
|
193
|
+
// and visible divider at their original positions. Measure only the visual.
|
|
194
|
+
paddingHorizontal: 8,
|
|
195
|
+
marginHorizontal: -8,
|
|
196
|
+
flexShrink: 0,
|
|
197
|
+
zIndex: 1,
|
|
198
|
+
},
|
|
199
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { type StyleProp, type ViewStyle } from 'react-native';
|
|
3
|
+
import type { HistoryBehavior, ParamListBase, RouteName } from '../routers';
|
|
4
|
+
import { type ScreenConfig } from '../core/builder';
|
|
5
|
+
import type { StackNavigation, StackScreenOptions, HeaderProps } from '../core/types';
|
|
6
|
+
export interface StackNavigatorProps {
|
|
7
|
+
children: React.ReactNode;
|
|
8
|
+
id?: string;
|
|
9
|
+
initialRouteName?: string;
|
|
10
|
+
historyBehavior?: HistoryBehavior;
|
|
11
|
+
screenOptions?: StackScreenOptions | ((context: {
|
|
12
|
+
navigation: StackNavigation;
|
|
13
|
+
route: import('../routers').NavigationRoute;
|
|
14
|
+
}) => StackScreenOptions);
|
|
15
|
+
style?: StyleProp<ViewStyle>;
|
|
16
|
+
}
|
|
17
|
+
export declare function StackHeader(props: HeaderProps): React.JSX.Element | null;
|
|
18
|
+
export declare function createStackNavigator<P extends ParamListBase = ParamListBase>(): {
|
|
19
|
+
Navigator: (props: Omit<StackNavigatorProps, "initialRouteName"> & {
|
|
20
|
+
initialRouteName?: RouteName<P>;
|
|
21
|
+
}) => React.JSX.Element;
|
|
22
|
+
Screen: <N extends RouteName<P>>(_props: ScreenConfig<P, N, StackScreenOptions, StackNavigation<P>>) => null;
|
|
23
|
+
};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.StackHeader = StackHeader;
|
|
7
|
+
exports.createStackNavigator = createStackNavigator;
|
|
8
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
9
|
+
const react_1 = __importDefault(require("react"));
|
|
10
|
+
const react_native_1 = require("react-native");
|
|
11
|
+
const builder_1 = require("../core/builder");
|
|
12
|
+
const Scene_1 = require("../core/Scene");
|
|
13
|
+
const navigation_1 = require("../core/navigation");
|
|
14
|
+
const context_1 = require("../core/context");
|
|
15
|
+
function renderAction(action, props) {
|
|
16
|
+
return typeof action === 'function' ? action(props) : action;
|
|
17
|
+
}
|
|
18
|
+
function StackHeader(props) {
|
|
19
|
+
const { options, route, navigation, canGoBack } = props;
|
|
20
|
+
const { colors } = (0, context_1.useNavigationTheme)();
|
|
21
|
+
if (options.headerShown === false)
|
|
22
|
+
return null;
|
|
23
|
+
if (options.header !== undefined)
|
|
24
|
+
return ((0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: typeof options.header === 'function'
|
|
25
|
+
? options.header(props)
|
|
26
|
+
: options.header }));
|
|
27
|
+
return ((0, jsx_runtime_1.jsxs)(react_native_1.View, { style: [
|
|
28
|
+
styles.header,
|
|
29
|
+
{ backgroundColor: colors.surface, borderColor: colors.border },
|
|
30
|
+
options.headerStyle,
|
|
31
|
+
], children: [(0, jsx_runtime_1.jsx)(react_native_1.View, { style: [styles.action, options.headerLeftContainerStyle], children: options.headerLeft !== undefined
|
|
32
|
+
? renderAction(options.headerLeft, props)
|
|
33
|
+
: canGoBack && ((0, jsx_runtime_1.jsx)(react_native_1.Pressable, { accessibilityRole: "button", accessibilityLabel: options.headerBackTitle ?? 'Back', style: options.headerBackButtonStyle, focusable: true, onPress: () => navigation.goBack(), children: (0, jsx_runtime_1.jsxs)(react_native_1.Text, { style: [
|
|
34
|
+
{ color: options.headerTintColor ?? colors.accent },
|
|
35
|
+
options.headerBackTitleStyle,
|
|
36
|
+
], children: ["\u2039 ", options.headerBackTitle ?? 'Back'] }) })) }), (0, jsx_runtime_1.jsx)(react_native_1.Text, { accessibilityRole: "header", numberOfLines: 1, style: [
|
|
37
|
+
styles.title,
|
|
38
|
+
{ color: options.headerTintColor ?? colors.text },
|
|
39
|
+
options.headerTitleStyle,
|
|
40
|
+
], children: options.title ?? route.name }), (0, jsx_runtime_1.jsx)(react_native_1.View, { style: [styles.action, options.headerRightContainerStyle], children: renderAction(options.headerRight, props) })] }));
|
|
41
|
+
}
|
|
42
|
+
function createStackNavigator() {
|
|
43
|
+
function Screen(_props) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
function Navigator(props) {
|
|
47
|
+
const definitions = (0, builder_1.readScreens)(props.children, Screen);
|
|
48
|
+
const { id, state, store } = (0, builder_1.useNavigator)('stack', {
|
|
49
|
+
routes: definitions,
|
|
50
|
+
initialRouteName: props.initialRouteName,
|
|
51
|
+
historyBehavior: props.historyBehavior,
|
|
52
|
+
}, props.id);
|
|
53
|
+
const getCanGoBack = react_1.default.useCallback(() => store.canDispatch('back', id), [store, id]);
|
|
54
|
+
const canGoBack = react_1.default.useSyncExternalStore(store.subscribe, getCanGoBack, getCanGoBack);
|
|
55
|
+
const scenes = state.routes.map((route) => {
|
|
56
|
+
const definition = definitions.find((d) => d.name === route.name);
|
|
57
|
+
if (!definition)
|
|
58
|
+
throw new Error(`Screen ${route.name} was removed. Remount the navigator to change its screen list.`);
|
|
59
|
+
const navigation = (0, navigation_1.createNavigation)(store, id, route.key);
|
|
60
|
+
return {
|
|
61
|
+
route,
|
|
62
|
+
definition,
|
|
63
|
+
navigation,
|
|
64
|
+
options: (0, builder_1.resolveOptions)(definition, route, navigation, props.screenOptions),
|
|
65
|
+
};
|
|
66
|
+
});
|
|
67
|
+
let visibleStart = scenes.length - 1;
|
|
68
|
+
while (visibleStart > 0 &&
|
|
69
|
+
['modal', 'dialog'].includes(scenes[visibleStart].options.presentation ?? 'card'))
|
|
70
|
+
visibleStart--;
|
|
71
|
+
return ((0, jsx_runtime_1.jsx)(builder_1.NavigatorStateContext.Provider, { value: state, children: (0, jsx_runtime_1.jsx)(react_native_1.View, { style: [styles.stack, props.style], children: scenes.map(({ route, definition, navigation, options }, index) => ((0, jsx_runtime_1.jsx)(Scene_1.Scene, { nodeId: id, route: route, component: definition.component, visible: index >= visibleStart, options: options, animation: options.animation ?? 'default', overlayStyle: options.overlayStyle, dialogStyle: options.dialogStyle, overlay: options.presentation === 'card'
|
|
72
|
+
? undefined
|
|
73
|
+
: options.presentation, children: (0, jsx_runtime_1.jsx)(StackHeader, { route: route, navigation: navigation, options: options, canGoBack: canGoBack }) }, route.key))) }) }));
|
|
74
|
+
}
|
|
75
|
+
return { Navigator, Screen };
|
|
76
|
+
}
|
|
77
|
+
const styles = react_native_1.StyleSheet.create({
|
|
78
|
+
stack: { flex: 1, overflow: 'hidden' },
|
|
79
|
+
header: {
|
|
80
|
+
minHeight: 44,
|
|
81
|
+
paddingHorizontal: 12,
|
|
82
|
+
flexDirection: 'row',
|
|
83
|
+
alignItems: 'center',
|
|
84
|
+
borderBottomWidth: react_native_1.StyleSheet.hairlineWidth,
|
|
85
|
+
},
|
|
86
|
+
title: { flex: 1, textAlign: 'center', fontWeight: '600' },
|
|
87
|
+
action: { minWidth: 60 },
|
|
88
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { ParamListBase, RouteName } from '../routers';
|
|
3
|
+
import { type ScreenConfig } from '../core/builder';
|
|
4
|
+
import { type SelectionNavigatorProps } from '../core/SelectionNavigator';
|
|
5
|
+
import type { TabNavigation, TabScreenOptions } from '../core/types';
|
|
6
|
+
export type TabNavigatorOptions = Pick<SelectionNavigatorProps, 'id' | 'initialRouteName' | 'screenOptions' | 'style' | 'barStyle' | 'barContentStyle' | 'contentStyle'>;
|
|
7
|
+
export declare function createTabNavigator<P extends ParamListBase = ParamListBase>(): {
|
|
8
|
+
Navigator: (props: Omit<TabNavigatorOptions, "initialRouteName"> & {
|
|
9
|
+
initialRouteName?: RouteName<P>;
|
|
10
|
+
children: React.ReactNode;
|
|
11
|
+
}) => React.JSX.Element;
|
|
12
|
+
Screen: <N extends RouteName<P>>(_props: ScreenConfig<P, N, TabScreenOptions, TabNavigation<P>>) => null;
|
|
13
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createTabNavigator = createTabNavigator;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const builder_1 = require("../core/builder");
|
|
6
|
+
const SelectionNavigator_1 = require("../core/SelectionNavigator");
|
|
7
|
+
function createTabNavigator() {
|
|
8
|
+
function Screen(_props) {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
function Navigator(props) {
|
|
12
|
+
return ((0, jsx_runtime_1.jsx)(SelectionNavigator_1.SelectionNavigator, { ...props, type: "tabs", definitions: (0, builder_1.readScreens)(props.children, Screen) }));
|
|
13
|
+
}
|
|
14
|
+
return { Navigator, Screen };
|
|
15
|
+
}
|