@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,147 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import {
|
|
3
|
+
routers,
|
|
4
|
+
type NavigationState,
|
|
5
|
+
type Router,
|
|
6
|
+
type RouterOptions,
|
|
7
|
+
type ParamListBase,
|
|
8
|
+
type RouteName,
|
|
9
|
+
type NavigationRoute,
|
|
10
|
+
} from '../routers';
|
|
11
|
+
import type { SidebarSectionOptions } from './types';
|
|
12
|
+
import { ScopeContext, useStore } from './context';
|
|
13
|
+
import { createNavigation, type AnyNavigation } from './navigation';
|
|
14
|
+
export const NavigatorStateContext =
|
|
15
|
+
React.createContext<NavigationState | null>(null);
|
|
16
|
+
export interface ScreenConfig<
|
|
17
|
+
P extends ParamListBase,
|
|
18
|
+
N extends RouteName<P>,
|
|
19
|
+
O,
|
|
20
|
+
Nav = AnyNavigation<P>,
|
|
21
|
+
> {
|
|
22
|
+
name: N;
|
|
23
|
+
component: React.ComponentType<{
|
|
24
|
+
navigation: Nav;
|
|
25
|
+
route: NavigationRoute<N, P[N]>;
|
|
26
|
+
}>;
|
|
27
|
+
initialParams?: P[N];
|
|
28
|
+
options?:
|
|
29
|
+
| O
|
|
30
|
+
| ((context: { navigation: Nav; route: NavigationRoute<N, P[N]> }) => O);
|
|
31
|
+
}
|
|
32
|
+
export interface Definition<O> {
|
|
33
|
+
name: string;
|
|
34
|
+
component: React.ComponentType<any>;
|
|
35
|
+
initialParams?: unknown;
|
|
36
|
+
options?:
|
|
37
|
+
| O
|
|
38
|
+
| ((context: { navigation: AnyNavigation; route: NavigationRoute }) => O);
|
|
39
|
+
section?: SidebarSectionOptions & { key: string };
|
|
40
|
+
}
|
|
41
|
+
export function readScreens<O>(
|
|
42
|
+
children: React.ReactNode,
|
|
43
|
+
Screen: React.ElementType,
|
|
44
|
+
Section?: React.ElementType,
|
|
45
|
+
): Definition<O>[] {
|
|
46
|
+
const result: Definition<O>[] = [];
|
|
47
|
+
function visit(items: React.ReactNode, section?: Definition<O>['section']) {
|
|
48
|
+
React.Children.forEach(items, (child) => {
|
|
49
|
+
if (!React.isValidElement(child)) {
|
|
50
|
+
if (child != null && typeof child !== 'boolean')
|
|
51
|
+
throw new Error('Navigator children must be Screen elements.');
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const props = child.props as {
|
|
55
|
+
children?: React.ReactNode;
|
|
56
|
+
} & SidebarSectionOptions;
|
|
57
|
+
if (child.type === React.Fragment) visit(props.children, section);
|
|
58
|
+
else if (Section && child.type === Section)
|
|
59
|
+
visit(props.children, {
|
|
60
|
+
key: String(child.key ?? result.length),
|
|
61
|
+
title: props.title,
|
|
62
|
+
style: props.style,
|
|
63
|
+
titleStyle: props.titleStyle,
|
|
64
|
+
renderTitle: props.renderTitle,
|
|
65
|
+
});
|
|
66
|
+
else if (child.type === Screen)
|
|
67
|
+
result.push({ ...(child.props as Definition<O>), section });
|
|
68
|
+
else
|
|
69
|
+
throw new Error(
|
|
70
|
+
'Navigator children must be Screen, Section, or Fragment elements.',
|
|
71
|
+
);
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
visit(children);
|
|
75
|
+
if (
|
|
76
|
+
!result.length ||
|
|
77
|
+
new Set(result.map((r) => r.name)).size !== result.length
|
|
78
|
+
)
|
|
79
|
+
throw new Error('Navigator requires unique Screen names.');
|
|
80
|
+
return result;
|
|
81
|
+
}
|
|
82
|
+
export function useNavigator<S extends NavigationState>(
|
|
83
|
+
type: S['type'],
|
|
84
|
+
options: Omit<RouterOptions, 'key'>,
|
|
85
|
+
explicitId?: string,
|
|
86
|
+
) {
|
|
87
|
+
const store = useStore();
|
|
88
|
+
const parent = React.useContext(ScopeContext);
|
|
89
|
+
const id = parent.nodeId
|
|
90
|
+
? `${parent.nodeId}/${parent.routeKey}/${explicitId ?? type}`
|
|
91
|
+
: (explicitId ?? 'root');
|
|
92
|
+
const config: RouterOptions = { ...options, key: id };
|
|
93
|
+
const [initial] = React.useState(
|
|
94
|
+
() =>
|
|
95
|
+
(store.getNode(id)?.state as S | undefined) ??
|
|
96
|
+
(routers[type] as Router<S>).getInitialState(config),
|
|
97
|
+
);
|
|
98
|
+
const getSnapshot = React.useCallback(
|
|
99
|
+
() => (store.getNode(id)?.state as S | undefined) ?? initial,
|
|
100
|
+
[store, id, initial],
|
|
101
|
+
);
|
|
102
|
+
const state = React.useSyncExternalStore(
|
|
103
|
+
store.subscribe,
|
|
104
|
+
getSnapshot,
|
|
105
|
+
getSnapshot,
|
|
106
|
+
);
|
|
107
|
+
React.useLayoutEffect(
|
|
108
|
+
() =>
|
|
109
|
+
store.register(
|
|
110
|
+
{
|
|
111
|
+
id,
|
|
112
|
+
parentId: parent.nodeId,
|
|
113
|
+
parentRouteKey: parent.routeKey,
|
|
114
|
+
type,
|
|
115
|
+
state: initial,
|
|
116
|
+
},
|
|
117
|
+
config,
|
|
118
|
+
),
|
|
119
|
+
[store, id],
|
|
120
|
+
);
|
|
121
|
+
React.useLayoutEffect(() => {
|
|
122
|
+
store.updateOptions(id, config);
|
|
123
|
+
});
|
|
124
|
+
const navigation = React.useMemo(
|
|
125
|
+
() => createNavigation(store, id),
|
|
126
|
+
[store, id],
|
|
127
|
+
);
|
|
128
|
+
return { id, state, store, navigation };
|
|
129
|
+
}
|
|
130
|
+
export function resolveOptions<O>(
|
|
131
|
+
definition: Definition<O>,
|
|
132
|
+
route: NavigationRoute,
|
|
133
|
+
navigation: AnyNavigation,
|
|
134
|
+
defaults?:
|
|
135
|
+
| O
|
|
136
|
+
| ((context: { route: NavigationRoute; navigation: AnyNavigation }) => O),
|
|
137
|
+
): O {
|
|
138
|
+
const context = { route, navigation };
|
|
139
|
+
return {
|
|
140
|
+
...(typeof defaults === 'function'
|
|
141
|
+
? (defaults as (c: typeof context) => O)(context)
|
|
142
|
+
: defaults),
|
|
143
|
+
...(typeof definition.options === 'function'
|
|
144
|
+
? (definition.options as (c: typeof context) => O)(context)
|
|
145
|
+
: definition.options),
|
|
146
|
+
} as O;
|
|
147
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { NavigationStore } from './store';
|
|
3
|
+
import type { NavigationRoute } from '../routers';
|
|
4
|
+
import type { AnyNavigation } from './navigation';
|
|
5
|
+
import type { NavigationTheme } from './types';
|
|
6
|
+
import { defaultPlatformAdapter } from '../platform';
|
|
7
|
+
export const StoreContext = React.createContext<NavigationStore | null>(null);
|
|
8
|
+
export const ScopeContext = React.createContext<{
|
|
9
|
+
nodeId?: string;
|
|
10
|
+
routeKey?: string;
|
|
11
|
+
}>({});
|
|
12
|
+
/** Only a Sidebar directly owned by this Split column may resize it. */
|
|
13
|
+
export const SplitColumnContext = React.createContext<{
|
|
14
|
+
nodeId: string;
|
|
15
|
+
columnId: string;
|
|
16
|
+
onSidebarChange: (columnId: string, width: number | null) => void;
|
|
17
|
+
} | null>(null);
|
|
18
|
+
export const ScreenContext = React.createContext<{
|
|
19
|
+
navigation: AnyNavigation;
|
|
20
|
+
route: NavigationRoute;
|
|
21
|
+
focused: boolean;
|
|
22
|
+
} | null>(null);
|
|
23
|
+
export const PlatformContext = React.createContext(defaultPlatformAdapter);
|
|
24
|
+
export const DefaultTheme: NavigationTheme = {
|
|
25
|
+
dark: false,
|
|
26
|
+
colors: {
|
|
27
|
+
background: '#ffffff',
|
|
28
|
+
surface: '#f5f5f7',
|
|
29
|
+
text: '#202124',
|
|
30
|
+
mutedText: '#64656b',
|
|
31
|
+
border: '#d8d8dc',
|
|
32
|
+
accent: '#0067c0',
|
|
33
|
+
selectedBackground: '#dcecff',
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
export const ThemeContext = React.createContext(DefaultTheme);
|
|
37
|
+
export const useNavigationTheme = () => React.useContext(ThemeContext);
|
|
38
|
+
export function useStore() {
|
|
39
|
+
const store = React.useContext(StoreContext);
|
|
40
|
+
if (!store)
|
|
41
|
+
throw new Error('Navigation must be rendered inside NavigationContainer.');
|
|
42
|
+
return store;
|
|
43
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export interface FocusTarget {
|
|
3
|
+
focus(): void;
|
|
4
|
+
}
|
|
5
|
+
export interface FocusRegistry {
|
|
6
|
+
register(id: string, target: FocusTarget): () => void;
|
|
7
|
+
record(id: string): void;
|
|
8
|
+
}
|
|
9
|
+
export const FocusContext = React.createContext<FocusRegistry | null>(null);
|
|
10
|
+
/** Wrap a ref-forwarding native control (Pressable, TextInput, or a custom control). */
|
|
11
|
+
export function NavigationFocusable({
|
|
12
|
+
id,
|
|
13
|
+
children,
|
|
14
|
+
}: {
|
|
15
|
+
id: string;
|
|
16
|
+
children: React.ReactElement<any>;
|
|
17
|
+
}) {
|
|
18
|
+
const registry = React.useContext(FocusContext);
|
|
19
|
+
const target = React.useRef<FocusTarget | null>(null);
|
|
20
|
+
const childProps = children.props as {
|
|
21
|
+
ref?: React.Ref<FocusTarget>;
|
|
22
|
+
onFocus?: (...args: any[]) => void;
|
|
23
|
+
};
|
|
24
|
+
const originalRef =
|
|
25
|
+
Number(React.version.split('.')[0]) >= 19
|
|
26
|
+
? childProps.ref
|
|
27
|
+
: (children as unknown as { ref?: React.Ref<FocusTarget> }).ref;
|
|
28
|
+
const setRef = React.useCallback(
|
|
29
|
+
(value: FocusTarget | null) => {
|
|
30
|
+
target.current = value;
|
|
31
|
+
if (typeof originalRef === 'function') originalRef(value);
|
|
32
|
+
else if (originalRef)
|
|
33
|
+
(originalRef as React.MutableRefObject<FocusTarget | null>).current =
|
|
34
|
+
value;
|
|
35
|
+
},
|
|
36
|
+
[originalRef],
|
|
37
|
+
);
|
|
38
|
+
React.useLayoutEffect(() => {
|
|
39
|
+
if (!registry)
|
|
40
|
+
throw new Error('NavigationFocusable must be inside a Screen.');
|
|
41
|
+
if (target.current) return registry.register(id, target.current);
|
|
42
|
+
}, [id, registry]);
|
|
43
|
+
return React.cloneElement(children, {
|
|
44
|
+
ref: setRef,
|
|
45
|
+
onFocus: (...args: any[]) => {
|
|
46
|
+
registry?.record(id);
|
|
47
|
+
childProps.onFocus?.(...args);
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { NavigatorStateContext } from './builder';
|
|
3
|
+
import { ScreenContext, ScopeContext, useStore } from './context';
|
|
4
|
+
import type { Navigation } from './types';
|
|
5
|
+
import type { NavigationRoute, NavigationState } from '../routers';
|
|
6
|
+
export function useNavigation<N = Navigation>(): N {
|
|
7
|
+
const screen = React.useContext(ScreenContext);
|
|
8
|
+
if (!screen) throw new Error('useNavigation must be used inside a Screen.');
|
|
9
|
+
return screen.navigation as N;
|
|
10
|
+
}
|
|
11
|
+
export function useRoute<R extends NavigationRoute = NavigationRoute>(): R {
|
|
12
|
+
const screen = React.useContext(ScreenContext);
|
|
13
|
+
if (!screen) throw new Error('useRoute must be used inside a Screen.');
|
|
14
|
+
return screen.route as R;
|
|
15
|
+
}
|
|
16
|
+
export function useNavigationState<T>(
|
|
17
|
+
selector: (state: NavigationState) => T,
|
|
18
|
+
): T {
|
|
19
|
+
const store = useStore();
|
|
20
|
+
const { nodeId } = React.useContext(ScopeContext);
|
|
21
|
+
const snapshot = React.useCallback(
|
|
22
|
+
() => store.getNode(nodeId ?? store.getState().rootNodeId)?.state,
|
|
23
|
+
[store, nodeId],
|
|
24
|
+
);
|
|
25
|
+
const state = React.useSyncExternalStore(store.subscribe, snapshot, snapshot);
|
|
26
|
+
// Screen render can precede the navigator's layout-effect registration.
|
|
27
|
+
const initial = React.useContext(NavigatorStateContext);
|
|
28
|
+
const value = state ?? initial;
|
|
29
|
+
if (!value)
|
|
30
|
+
throw new Error('useNavigationState requires a registered navigator.');
|
|
31
|
+
return selector(value);
|
|
32
|
+
}
|
|
33
|
+
export function useIsFocused() {
|
|
34
|
+
const screen = React.useContext(ScreenContext);
|
|
35
|
+
return screen?.focused ?? false;
|
|
36
|
+
}
|
|
37
|
+
export function useFocusEffect(effect: React.EffectCallback) {
|
|
38
|
+
const focused = useIsFocused();
|
|
39
|
+
React.useEffect(() => {
|
|
40
|
+
if (focused) return effect();
|
|
41
|
+
}, [focused, effect]);
|
|
42
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { NavigationStore } from './store';
|
|
2
|
+
import type { ParamListBase, NavigationAction } from '../routers';
|
|
3
|
+
import type {
|
|
4
|
+
StackNavigation,
|
|
5
|
+
SidebarNavigation,
|
|
6
|
+
TabNavigation,
|
|
7
|
+
NavigationRef,
|
|
8
|
+
} from './types';
|
|
9
|
+
export type AnyNavigation<P extends ParamListBase = ParamListBase> =
|
|
10
|
+
StackNavigation<P> & SidebarNavigation<P> & TabNavigation<P>;
|
|
11
|
+
export function createNavigation<P extends ParamListBase = ParamListBase>(
|
|
12
|
+
store: NavigationStore,
|
|
13
|
+
id: string,
|
|
14
|
+
routeKey?: string,
|
|
15
|
+
): AnyNavigation<P> {
|
|
16
|
+
const dispatch = (action: NavigationAction) => {
|
|
17
|
+
store.dispatch({ ...action, target: action.target ?? id });
|
|
18
|
+
};
|
|
19
|
+
const routeAction =
|
|
20
|
+
(type: 'navigate' | 'push' | 'replace' | 'popTo') =>
|
|
21
|
+
(...[name, params]: [string, unknown?]) =>
|
|
22
|
+
dispatch({ type, payload: { name, params } });
|
|
23
|
+
return {
|
|
24
|
+
navigate: routeAction('navigate'),
|
|
25
|
+
push: routeAction('push'),
|
|
26
|
+
replace: routeAction('replace'),
|
|
27
|
+
popTo: routeAction('popTo'),
|
|
28
|
+
dispatch,
|
|
29
|
+
goBack: () => dispatch({ type: 'back' }),
|
|
30
|
+
goForward: () => dispatch({ type: 'forward' }),
|
|
31
|
+
canGoBack: () => store.canDispatch('back', id),
|
|
32
|
+
canGoForward: () => store.canDispatch('forward', id),
|
|
33
|
+
pop: (count) => dispatch({ type: 'pop', payload: { count } }),
|
|
34
|
+
popToRoot: () => dispatch({ type: 'popToRoot' }),
|
|
35
|
+
select: (name) => dispatch({ type: 'select', payload: { name } }),
|
|
36
|
+
collapseSidebar: () => dispatch({ type: 'collapseSidebar' }),
|
|
37
|
+
expandSidebar: () => dispatch({ type: 'expandSidebar' }),
|
|
38
|
+
toggleSidebar: () => dispatch({ type: 'toggleSidebar' }),
|
|
39
|
+
nextTab: () => dispatch({ type: 'nextTab' }),
|
|
40
|
+
previousTab: () => dispatch({ type: 'previousTab' }),
|
|
41
|
+
getState: () => {
|
|
42
|
+
const node = store.getNode(id);
|
|
43
|
+
if (!node) throw new Error('Navigator is not mounted.');
|
|
44
|
+
return node.state;
|
|
45
|
+
},
|
|
46
|
+
getParent: () => {
|
|
47
|
+
const node = store.getNode(id);
|
|
48
|
+
return node?.parentId
|
|
49
|
+
? createNavigation(store, node.parentId, node.parentRouteKey)
|
|
50
|
+
: undefined;
|
|
51
|
+
},
|
|
52
|
+
isFocused: () =>
|
|
53
|
+
routeKey ? store.isRouteFocused(id, routeKey) : store.isNodeFocused(id),
|
|
54
|
+
addListener: (type, listener) =>
|
|
55
|
+
store.addListener(
|
|
56
|
+
routeKey ?? store.getNode(id)?.state.activeRouteKey ?? id,
|
|
57
|
+
type,
|
|
58
|
+
listener,
|
|
59
|
+
),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
export function createNavigationRef<
|
|
63
|
+
P extends ParamListBase = ParamListBase,
|
|
64
|
+
>(): NavigationRef<P> {
|
|
65
|
+
const ref: NavigationRef<P> = {
|
|
66
|
+
current: null,
|
|
67
|
+
isReady: () => ref.current?.isReady() ?? false,
|
|
68
|
+
navigate: (...args) => ref.current?.navigate(...args),
|
|
69
|
+
goBack: () => ref.current?.goBack(),
|
|
70
|
+
goForward: () => ref.current?.goForward(),
|
|
71
|
+
canGoBack: () => ref.current?.canGoBack() ?? false,
|
|
72
|
+
canGoForward: () => ref.current?.canGoForward() ?? false,
|
|
73
|
+
dispatch: (action) => ref.current?.dispatch(action),
|
|
74
|
+
getRootState: () => ref.current?.getRootState(),
|
|
75
|
+
};
|
|
76
|
+
return ref;
|
|
77
|
+
}
|