@granite-js/react-native 0.0.0-dev-20250725013859
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/LICENSE +202 -0
- package/README.md +24 -0
- package/bin/cli.js +3 -0
- package/cli.d.ts +1 -0
- package/cli.js +4 -0
- package/config.d.ts +2 -0
- package/config.js +5 -0
- package/dist/app/App/index.android.d.ts +2 -0
- package/dist/app/App/index.ios.d.ts +6 -0
- package/dist/app/AppRoot.d.ts +1 -0
- package/dist/app/Granite.d.ts +61 -0
- package/dist/app/HostAppRoot.d.ts +1 -0
- package/dist/app/index.d.ts +2 -0
- package/dist/async-bridges.d.ts +2 -0
- package/dist/blur/BlurView.d.ts +78 -0
- package/dist/blur/ReactNativeBlurModule.d.ts +6 -0
- package/dist/blur/constants.d.ts +1 -0
- package/dist/blur/index.d.ts +1 -0
- package/dist/constant-bridges.d.ts +1 -0
- package/dist/constants.d.ts +1 -0
- package/dist/dev-entrypoint/index.d.ts +2 -0
- package/dist/event/abstract.d.ts +42 -0
- package/dist/event/index.d.ts +2 -0
- package/dist/event/useGraniteEvent.d.ts +14 -0
- package/dist/impression-area/ImpressionArea.d.ts +231 -0
- package/dist/impression-area/index.d.ts +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/initial-props/InitialProps.d.ts +92 -0
- package/dist/initial-props/index.d.ts +1 -0
- package/dist/intersection-observer/IOContext.d.ts +10 -0
- package/dist/intersection-observer/IOFlatList.d.ts +55 -0
- package/dist/intersection-observer/IOManager.d.ts +24 -0
- package/dist/intersection-observer/IOScrollView.d.ts +59 -0
- package/dist/intersection-observer/InView.d.ts +107 -0
- package/dist/intersection-observer/IntersectionObserver.d.ts +67 -0
- package/dist/intersection-observer/index.d.ts +8 -0
- package/dist/intersection-observer/withIO.d.ts +20 -0
- package/dist/jest/index.d.ts +1 -0
- package/dist/jest/index.js +32 -0
- package/dist/keyboard/KeyboardAboveView.d.ts +40 -0
- package/dist/keyboard/index.d.ts +2 -0
- package/dist/keyboard/useKeyboardAnimatedHeight.d.ts +20 -0
- package/dist/native-event-emitter/eventEmitters/index.d.ts +2 -0
- package/dist/native-event-emitter/eventEmitters/types.d.ts +4 -0
- package/dist/native-event-emitter/eventEmitters/visibilityChanged.d.ts +10 -0
- package/dist/native-event-emitter/index.d.ts +1 -0
- package/dist/native-event-emitter/nativeEventEmitter.d.ts +15 -0
- package/dist/native-modules/core/GraniteCoreModule.d.ts +8 -0
- package/dist/native-modules/index.d.ts +3 -0
- package/dist/native-modules/natives/GraniteModule.d.ts +7 -0
- package/dist/native-modules/natives/closeView.d.ts +21 -0
- package/dist/native-modules/natives/getSchemeUri.d.ts +23 -0
- package/dist/native-modules/natives/index.d.ts +3 -0
- package/dist/native-modules/natives/openURL.d.ts +36 -0
- package/dist/react/index.d.ts +1 -0
- package/dist/react/useWaitForReturnNavigator.d.ts +39 -0
- package/dist/rn-polyfills/index.d.ts +1 -0
- package/dist/rn-polyfills/symbol-asynciterator/index.d.ts +9 -0
- package/dist/rn-polyfills/url/index.d.ts +1 -0
- package/dist/router/Router.d.ts +59 -0
- package/dist/router/components/BackButton.d.ts +7 -0
- package/dist/router/components/CanGoBackGuard.d.ts +6 -0
- package/dist/router/components/RouterBackButton.d.ts +9 -0
- package/dist/router/components/StackNavigator.d.ts +54 -0
- package/dist/router/constants.d.ts +2 -0
- package/dist/router/createRoute.d.ts +39 -0
- package/dist/router/createRoute.test-d.d.ts +9 -0
- package/dist/router/hooks/useInitialRouteName.d.ts +1 -0
- package/dist/router/hooks/useIsInitialScreen.d.ts +1 -0
- package/dist/router/hooks/useRouterControls.d.ts +11 -0
- package/dist/router/index.d.ts +3 -0
- package/dist/router/types/RequireContext.d.ts +7 -0
- package/dist/router/types/RouteScreen.d.ts +16 -0
- package/dist/router/types/Screen.d.ts +23 -0
- package/dist/router/types/index.d.ts +3 -0
- package/dist/router/types/screen-option.d.ts +4 -0
- package/dist/router/utils/createParentRouteScreenMap.d.ts +8 -0
- package/dist/router/utils/defaultParserParams.d.ts +9 -0
- package/dist/router/utils/index.d.ts +2 -0
- package/dist/router/utils/matchers.d.ts +2 -0
- package/dist/router/utils/mergeParentLayoutScreen.d.ts +18 -0
- package/dist/router/utils/path.d.ts +53 -0
- package/dist/router/utils/screen.d.ts +37 -0
- package/dist/scroll-view-inertial-background/ScrollViewInertialBackground.d.ts +49 -0
- package/dist/scroll-view-inertial-background/index.d.ts +1 -0
- package/dist/status-bar/StatusBar.android.d.ts +3 -0
- package/dist/status-bar/StatusBar.ios.d.ts +3 -0
- package/dist/status-bar/index.d.ts +2 -0
- package/dist/status-bar/types.d.ts +20 -0
- package/dist/status-bar/utils.d.ts +3 -0
- package/dist/types/global.d.ts +14 -0
- package/dist/use-back-event/index.d.ts +1 -0
- package/dist/use-back-event/useBackEvent.d.ts +135 -0
- package/dist/utils/noop.d.ts +1 -0
- package/dist/utils/usePreservedCallback.d.ts +1 -0
- package/dist/video/Video.d.ts +67 -0
- package/dist/video/index.d.ts +1 -0
- package/dist/video/instance.d.ts +9 -0
- package/dist/visibility/VisibilityProvider.d.ts +27 -0
- package/dist/visibility/index.d.ts +6 -0
- package/dist/visibility/react-navigation/index.d.ts +2 -0
- package/dist/visibility/react-navigation/useIsFocusedSafely.d.ts +20 -0
- package/dist/visibility/react-navigation/useNavigationSafely.d.ts +19 -0
- package/dist/visibility/useIsAppForeground.d.ts +39 -0
- package/dist/visibility/useVisibility.d.ts +35 -0
- package/dist/visibility/useVisibilityChange.d.ts +51 -0
- package/dist/visibility/useVisibilityChanged.d.ts +41 -0
- package/dist/visibility/utils/usePrevious.d.ts +15 -0
- package/jest.d.ts +1 -0
- package/package.json +94 -0
- package/presets.d.ts +1 -0
- package/src/app/App/index.android.tsx +6 -0
- package/src/app/App/index.d.ts +6 -0
- package/src/app/App/index.ios.tsx +13 -0
- package/src/app/AppRoot.tsx +39 -0
- package/src/app/Granite.tsx +128 -0
- package/src/app/HostAppRoot.tsx +19 -0
- package/src/app/index.ts +2 -0
- package/src/async-bridges.ts +2 -0
- package/src/blur/BlurView.tsx +103 -0
- package/src/blur/ReactNativeBlurModule.ts +19 -0
- package/src/blur/constants.ts +3 -0
- package/src/blur/index.ts +1 -0
- package/src/constant-bridges.ts +1 -0
- package/src/constants.ts +1 -0
- package/src/dev-entrypoint/index.tsx +17 -0
- package/src/event/abstract.ts +130 -0
- package/src/event/index.ts +2 -0
- package/src/event/useGraniteEvent.ts +34 -0
- package/src/impression-area/ImpressionArea.tsx +341 -0
- package/src/impression-area/index.ts +1 -0
- package/src/index.ts +24 -0
- package/src/initial-props/InitialProps.ts +95 -0
- package/src/initial-props/index.ts +1 -0
- package/src/intersection-observer/IOContext.ts +16 -0
- package/src/intersection-observer/IOFlatList.ts +72 -0
- package/src/intersection-observer/IOManager.ts +73 -0
- package/src/intersection-observer/IOScrollView.ts +69 -0
- package/src/intersection-observer/InView.tsx +205 -0
- package/src/intersection-observer/IntersectionObserver.ts +212 -0
- package/src/intersection-observer/index.ts +24 -0
- package/src/intersection-observer/withIO.tsx +151 -0
- package/src/jest/index.ts +1 -0
- package/src/keyboard/KeyboardAboveView.tsx +62 -0
- package/src/keyboard/index.ts +2 -0
- package/src/keyboard/useKeyboardAnimatedHeight.tsx +81 -0
- package/src/native-event-emitter/eventEmitters/index.ts +3 -0
- package/src/native-event-emitter/eventEmitters/types.ts +4 -0
- package/src/native-event-emitter/eventEmitters/visibilityChanged.ts +11 -0
- package/src/native-event-emitter/index.ts +1 -0
- package/src/native-event-emitter/nativeEventEmitter.ts +18 -0
- package/src/native-modules/core/GraniteCoreModule.ts +9 -0
- package/src/native-modules/index.ts +3 -0
- package/src/native-modules/natives/GraniteModule.ts +8 -0
- package/src/native-modules/natives/closeView.ts +25 -0
- package/src/native-modules/natives/getSchemeUri.ts +27 -0
- package/src/native-modules/natives/index.ts +3 -0
- package/src/native-modules/natives/openURL.ts +40 -0
- package/src/react/index.ts +1 -0
- package/src/react/useWaitForReturnNavigator.ts +75 -0
- package/src/rn-polyfills/index.ts +7 -0
- package/src/rn-polyfills/symbol-asynciterator/index.ts +15 -0
- package/src/rn-polyfills/url/index.ts +1 -0
- package/src/router/Router.tsx +164 -0
- package/src/router/components/BackButton.tsx +58 -0
- package/src/router/components/CanGoBackGuard.tsx +31 -0
- package/src/router/components/RouterBackButton.tsx +32 -0
- package/src/router/components/StackNavigator.tsx +12 -0
- package/src/router/constants.ts +3 -0
- package/src/router/createRoute.test-d.ts +52 -0
- package/src/router/createRoute.ts +161 -0
- package/src/router/hooks/useInitialRouteName.tsx +22 -0
- package/src/router/hooks/useIsInitialScreen.ts +7 -0
- package/src/router/hooks/useRouterControls.tsx +72 -0
- package/src/router/index.ts +3 -0
- package/src/router/types/RequireContext.ts +7 -0
- package/src/router/types/RouteScreen.ts +17 -0
- package/src/router/types/Screen.tsx +24 -0
- package/src/router/types/index.ts +3 -0
- package/src/router/types/screen-option.ts +23 -0
- package/src/router/utils/createParentRouteScreenMap.spec.ts +166 -0
- package/src/router/utils/createParentRouteScreenMap.ts +136 -0
- package/src/router/utils/defaultParserParams.spec.ts +46 -0
- package/src/router/utils/defaultParserParams.ts +19 -0
- package/src/router/utils/index.ts +2 -0
- package/src/router/utils/matchers.ts +5 -0
- package/src/router/utils/mergeParentLayoutScreen.spec.tsx +112 -0
- package/src/router/utils/mergeParentLayoutScreen.tsx +43 -0
- package/src/router/utils/path.spec.ts +135 -0
- package/src/router/utils/path.ts +105 -0
- package/src/router/utils/screen.tsx +95 -0
- package/src/scroll-view-inertial-background/ScrollViewInertialBackground.tsx +99 -0
- package/src/scroll-view-inertial-background/index.ts +1 -0
- package/src/status-bar/StatusBar.android.tsx +36 -0
- package/src/status-bar/StatusBar.d.ts +4 -0
- package/src/status-bar/StatusBar.ios.tsx +34 -0
- package/src/status-bar/index.ts +2 -0
- package/src/status-bar/types.ts +21 -0
- package/src/status-bar/utils.ts +20 -0
- package/src/types/global.ts +21 -0
- package/src/use-back-event/index.ts +1 -0
- package/src/use-back-event/useBackEvent.tsx +260 -0
- package/src/utils/noop.ts +1 -0
- package/src/utils/usePreservedCallback.ts +16 -0
- package/src/video/Video.tsx +104 -0
- package/src/video/index.ts +1 -0
- package/src/video/instance.tsx +28 -0
- package/src/visibility/VisibilityProvider.tsx +36 -0
- package/src/visibility/index.ts +6 -0
- package/src/visibility/react-navigation/index.ts +2 -0
- package/src/visibility/react-navigation/useIsFocusedSafely.tsx +58 -0
- package/src/visibility/react-navigation/useNavigationSafely.tsx +30 -0
- package/src/visibility/useIsAppForeground.tsx +73 -0
- package/src/visibility/useVisibility.tsx +54 -0
- package/src/visibility/useVisibilityChange.ts +69 -0
- package/src/visibility/useVisibilityChanged.tsx +69 -0
- package/src/visibility/utils/usePrevious.tsx +24 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createNavigationContainerRef,
|
|
3
|
+
NavigationContainer,
|
|
4
|
+
NavigationContainerRefWithCurrent,
|
|
5
|
+
ParamListBase,
|
|
6
|
+
RouteProp,
|
|
7
|
+
} from '@granite-js/native/@react-navigation/native';
|
|
8
|
+
import { NativeStackNavigationOptions } from '@granite-js/native/@react-navigation/native-stack';
|
|
9
|
+
import { ComponentProps, ComponentType, Fragment, PropsWithChildren, ReactElement, useCallback, useMemo } from 'react';
|
|
10
|
+
import { InitialProps } from '..';
|
|
11
|
+
import { CanGoBackGuard } from './components/CanGoBackGuard';
|
|
12
|
+
import { RouterBackButton, RouterBackButtonProps } from './components/RouterBackButton';
|
|
13
|
+
import { StackNavigator } from './components/StackNavigator';
|
|
14
|
+
import { useInitialRouteName } from './hooks/useInitialRouteName';
|
|
15
|
+
import { useRouterControls } from './hooks/useRouterControls';
|
|
16
|
+
import { RequireContext } from './types';
|
|
17
|
+
import { BASE_STACK_NAVIGATOR_STYLE } from './types/screen-option';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @internal
|
|
21
|
+
*/
|
|
22
|
+
export interface InternalRouterProps {
|
|
23
|
+
/**
|
|
24
|
+
* @name context
|
|
25
|
+
* @description
|
|
26
|
+
* Object containing screen information used for file-based routing.
|
|
27
|
+
*/
|
|
28
|
+
context: RequireContext;
|
|
29
|
+
/**
|
|
30
|
+
* @name prefix
|
|
31
|
+
* @description
|
|
32
|
+
* Prefix to use when the scheme is executed. For example, to enter 'scheme://my-service/intro', you need to set 'scheme://my-service' as the prefix.
|
|
33
|
+
*/
|
|
34
|
+
prefix: string;
|
|
35
|
+
/**
|
|
36
|
+
* @name canGoBack
|
|
37
|
+
* @description
|
|
38
|
+
* Whether navigation back is possible. Default is true, and when set to true, you can use the back gesture or back button from @react-navigation/native.
|
|
39
|
+
* @default true
|
|
40
|
+
*/
|
|
41
|
+
canGoBack?: boolean;
|
|
42
|
+
/**
|
|
43
|
+
* @name onBack
|
|
44
|
+
* @description
|
|
45
|
+
* Callback function called when navigating back.
|
|
46
|
+
*/
|
|
47
|
+
onBack?: () => void;
|
|
48
|
+
/**
|
|
49
|
+
* @name container
|
|
50
|
+
* @description
|
|
51
|
+
* Container component that wraps the Navigator from @react-navigation/native.
|
|
52
|
+
*/
|
|
53
|
+
container: ComponentType<PropsWithChildren<InitialProps>>;
|
|
54
|
+
initialProps: InitialProps;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export type RouterProps = StackNavigatorProps & NavigationContainerProps;
|
|
58
|
+
|
|
59
|
+
interface StackNavigatorProps {
|
|
60
|
+
/**
|
|
61
|
+
* @name navigationContainerRef
|
|
62
|
+
* @description
|
|
63
|
+
* You can create and pass a NavigationContainerRef from @react-navigation/native externally. This allows external control of the router.
|
|
64
|
+
*/
|
|
65
|
+
navigationContainerRef?: NavigationContainerRefWithCurrent<any>;
|
|
66
|
+
/**
|
|
67
|
+
* @name defaultScreenOption
|
|
68
|
+
* @description
|
|
69
|
+
* Default options for screens. You can set options to be applied commonly to screens, such as title or headerStyle.
|
|
70
|
+
*/
|
|
71
|
+
defaultScreenOption?:
|
|
72
|
+
| NativeStackNavigationOptions
|
|
73
|
+
| ((props: { route: RouteProp<ParamListBase>; navigation: any }) => NativeStackNavigationOptions);
|
|
74
|
+
/**
|
|
75
|
+
* @name screenContainer
|
|
76
|
+
* @description
|
|
77
|
+
* Container component that wraps each Screen component.
|
|
78
|
+
*/
|
|
79
|
+
screenContainer?: ComponentType<PropsWithChildren<any>>;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
type NavigationContainerProps = Pick<
|
|
83
|
+
ComponentProps<typeof NavigationContainer>,
|
|
84
|
+
'ref' | 'documentTitle' | 'fallback' | 'onReady' | 'onUnhandledAction' | 'onStateChange'
|
|
85
|
+
>;
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* @category Components
|
|
89
|
+
* @kind function
|
|
90
|
+
* @name Router
|
|
91
|
+
* @description
|
|
92
|
+
* Router component for page navigation in React Native environment.
|
|
93
|
+
* Automatically assigns appropriate paths to screens based on file naming rules in pages/*.
|
|
94
|
+
* Using this component, you can manage screens in a way similar to Next.js's file-based routing.
|
|
95
|
+
*
|
|
96
|
+
* @param {string} prefix Prefix to use when the scheme is executed. For example, to enter 'scheme://my-service/intro', you need to set 'scheme://my-service' as the prefix.
|
|
97
|
+
* @param {RequireContext} context Object containing information about screens for file-based routing.
|
|
98
|
+
* @param {NavigationContainerRefWithCurrent<any>} [navigationContainerRef] You can create and pass a NavigationContainerRef from @react-navigation/native externally. This allows external control of the router.
|
|
99
|
+
* @param {NativeStackNavigationOptions | ((props: { route: RouteProp<ParamListBase>; navigation: any }) => NativeStackNavigationOptions)} [defaultScreenOption] Default options for screens. You can set options to be applied commonly to screens, such as title or headerStyle.
|
|
100
|
+
* @param {boolean} [canGoBack=true] Whether navigation back is possible. Default is true, and when set to true, you can use the back gesture or back button from @react-navigation/native.
|
|
101
|
+
* @param {() => void} [onBack] Callback function called when the user presses the back button or uses the back gesture. For example, you can set it to log when the user presses the back button.
|
|
102
|
+
* @param {ComponentType<{ children: ReactNode }>} [container=Fragment] Container component that wraps the Navigator from @react-navigation/native.
|
|
103
|
+
* @param {NavigationContainerProps} [navigationContainerProps] - You can set props to be passed to NavigationContainer from @react-navigation/native.
|
|
104
|
+
*
|
|
105
|
+
* @returns {ReactElement} - Returns the router component.
|
|
106
|
+
* @example
|
|
107
|
+
* ```tsx
|
|
108
|
+
* import { Router } from '@granite-js/react-native';
|
|
109
|
+
* import { context } from '../require.context';
|
|
110
|
+
*
|
|
111
|
+
* function App() {
|
|
112
|
+
* return <Router context={context} prefix={'scheme://testbench'} />;
|
|
113
|
+
* }
|
|
114
|
+
* ```
|
|
115
|
+
*/
|
|
116
|
+
export function Router({
|
|
117
|
+
// Internal props
|
|
118
|
+
prefix,
|
|
119
|
+
context,
|
|
120
|
+
canGoBack = true,
|
|
121
|
+
onBack,
|
|
122
|
+
container: Container = Fragment,
|
|
123
|
+
initialProps,
|
|
124
|
+
// Public props (NavigationContainer)
|
|
125
|
+
navigationContainerRef,
|
|
126
|
+
defaultScreenOption,
|
|
127
|
+
screenContainer,
|
|
128
|
+
// Public props (StackNavigator)
|
|
129
|
+
...navigationContainerProps
|
|
130
|
+
}: InternalRouterProps & RouterProps): ReactElement {
|
|
131
|
+
const initialRouteName = useInitialRouteName(prefix);
|
|
132
|
+
const { Screens, linkingOptions } = useRouterControls({ prefix, context, screenContainer });
|
|
133
|
+
|
|
134
|
+
const ref = useMemo(() => navigationContainerRef ?? createNavigationContainerRef<any>(), [navigationContainerRef]);
|
|
135
|
+
|
|
136
|
+
const headerLeft = useCallback(
|
|
137
|
+
(backButtonProps: Omit<RouterBackButtonProps, 'navigationContainerRef'>) => (
|
|
138
|
+
<RouterBackButton {...backButtonProps} onBack={onBack} canGoBack={canGoBack} navigationContainerRef={ref} />
|
|
139
|
+
),
|
|
140
|
+
[onBack, canGoBack, ref]
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
const screenOptions = useCallback(
|
|
144
|
+
(screenProps: any) => ({
|
|
145
|
+
...BASE_STACK_NAVIGATOR_STYLE,
|
|
146
|
+
gestureEnabled: canGoBack,
|
|
147
|
+
headerLeft,
|
|
148
|
+
...(typeof defaultScreenOption === 'function' ? defaultScreenOption(screenProps) : defaultScreenOption),
|
|
149
|
+
}),
|
|
150
|
+
[canGoBack, defaultScreenOption, headerLeft]
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
return (
|
|
154
|
+
<NavigationContainer ref={ref} {...navigationContainerProps} linking={linkingOptions}>
|
|
155
|
+
<CanGoBackGuard canGoBack={canGoBack} onBack={onBack}>
|
|
156
|
+
<Container {...initialProps}>
|
|
157
|
+
<StackNavigator.Navigator initialRouteName={initialRouteName} screenOptions={screenOptions}>
|
|
158
|
+
{Screens}
|
|
159
|
+
</StackNavigator.Navigator>
|
|
160
|
+
</Container>
|
|
161
|
+
</CanGoBackGuard>
|
|
162
|
+
</NavigationContainer>
|
|
163
|
+
);
|
|
164
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { SvgXml } from '@granite-js/native/react-native-svg';
|
|
2
|
+
import { Platform, TouchableOpacity, TouchableOpacityProps, View } from 'react-native';
|
|
3
|
+
|
|
4
|
+
interface BackButtonProps extends TouchableOpacityProps {
|
|
5
|
+
tintColor?: string;
|
|
6
|
+
onPress?: () => void;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const DEFAULT_COLOR = '#191f28'; // grey900
|
|
10
|
+
function BackButton({ tintColor, onPress }: BackButtonProps) {
|
|
11
|
+
return <NavbarBackButton onPress={onPress} color={tintColor ?? DEFAULT_COLOR} />;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function NavbarBackButton({ onPress, color }: { onPress?: () => void; color?: string }) {
|
|
15
|
+
return (
|
|
16
|
+
<TouchableOpacity
|
|
17
|
+
hitSlop={{ top: 10, right: 10, bottom: 10, left: 10 }}
|
|
18
|
+
style={{ width: 24, height: 24 }}
|
|
19
|
+
onPress={onPress}
|
|
20
|
+
accessible={true}
|
|
21
|
+
accessibilityLabel="Go back"
|
|
22
|
+
accessibilityRole="button"
|
|
23
|
+
>
|
|
24
|
+
<View style={{ width: 24, height: 24 }}>
|
|
25
|
+
<SvgXml
|
|
26
|
+
width={24}
|
|
27
|
+
height={24}
|
|
28
|
+
xml={BACK_BUTTON_XML.replace(/fill="#[0-9a-fA-F]{6}"/g, `fill="${color}"`)}
|
|
29
|
+
style={{
|
|
30
|
+
marginLeft: BACK_BUTTON_MARGIN,
|
|
31
|
+
}}
|
|
32
|
+
/>
|
|
33
|
+
</View>
|
|
34
|
+
</TouchableOpacity>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const ANDROID_BACK_BUTTON = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" class="line-icon">
|
|
39
|
+
<path fill="#B0B8C1" fill-rule="evenodd" d="M20.966 10.8H6.93l5.451-5.451a1.2 1.2 0 10-1.697-1.697l-7.5 7.5c-.003.002-.004.006-.007.009a1.2 1.2 0 00-.252 1.298c.06.143.145.27.252.38l.007.01 7.5 7.5c.235.234.542.35.848.35a1.2 1.2 0 00.849-2.048L6.931 13.2h14.036a1.2 1.2 0 100-2.4"/>
|
|
40
|
+
</svg>`;
|
|
41
|
+
|
|
42
|
+
const IOS_BACK_BUTTON = `<svg enable-background="new 0 0 24 24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
43
|
+
<path d="m20.8 20.7c-.3 0-.6-.1-.8-.4l-7.5-7.5c-.5-.5-.5-1.2 0-1.7l7.5-7.5c.5-.5 1.2-.5 1.7 0s.5 1.2 0 1.7l-6.8 6.7 6.7 6.7c.5.5.5 1.2 0 1.7-.2.2-.5.3-.8.3z" fill="#b0b8c1"/>
|
|
44
|
+
</svg>`;
|
|
45
|
+
|
|
46
|
+
const BACK_BUTTON_XML = Platform.select<string>({
|
|
47
|
+
android: ANDROID_BACK_BUTTON,
|
|
48
|
+
ios: IOS_BACK_BUTTON,
|
|
49
|
+
default: IOS_BACK_BUTTON,
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const BACK_BUTTON_MARGIN = Platform.select<number>({
|
|
53
|
+
android: -4,
|
|
54
|
+
ios: -12,
|
|
55
|
+
default: -12,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
export { BackButton };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { ReactNode, useEffect } from 'react';
|
|
2
|
+
import { BackHandler } from 'react-native';
|
|
3
|
+
|
|
4
|
+
export function CanGoBackGuard({
|
|
5
|
+
children,
|
|
6
|
+
canGoBack,
|
|
7
|
+
onBack,
|
|
8
|
+
}: {
|
|
9
|
+
canGoBack: boolean;
|
|
10
|
+
children: ReactNode;
|
|
11
|
+
onBack?: () => void;
|
|
12
|
+
}) {
|
|
13
|
+
const shouldBlockGoingBack = !canGoBack;
|
|
14
|
+
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
if (shouldBlockGoingBack) {
|
|
17
|
+
const subscription = BackHandler.addEventListener('hardwareBackPress', () => {
|
|
18
|
+
onBack?.();
|
|
19
|
+
return true;
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
return () => {
|
|
23
|
+
subscription.remove();
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return;
|
|
28
|
+
}, [shouldBlockGoingBack, onBack]);
|
|
29
|
+
|
|
30
|
+
return <>{children}</>;
|
|
31
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { NavigationContainerRefWithCurrent } from '@granite-js/native/@react-navigation/native';
|
|
2
|
+
import { BackButton } from './BackButton';
|
|
3
|
+
import { closeView } from '../../native-modules';
|
|
4
|
+
|
|
5
|
+
export type RouterBackButtonProps = {
|
|
6
|
+
onPress?: () => void;
|
|
7
|
+
tintColor?: string;
|
|
8
|
+
canGoBack?: boolean;
|
|
9
|
+
onBack?: () => void;
|
|
10
|
+
navigationContainerRef: NavigationContainerRefWithCurrent<any>;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export function RouterBackButton({ tintColor, canGoBack, onBack, navigationContainerRef }: RouterBackButtonProps) {
|
|
14
|
+
return (
|
|
15
|
+
<BackButton
|
|
16
|
+
tintColor={tintColor}
|
|
17
|
+
onPress={() => {
|
|
18
|
+
onBack?.();
|
|
19
|
+
|
|
20
|
+
if (!canGoBack) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (navigationContainerRef.canGoBack()) {
|
|
25
|
+
navigationContainerRef.goBack();
|
|
26
|
+
} else {
|
|
27
|
+
closeView();
|
|
28
|
+
}
|
|
29
|
+
}}
|
|
30
|
+
/>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { createNativeStackNavigator } from '@granite-js/native/@react-navigation/native-stack';
|
|
2
|
+
|
|
3
|
+
function createStackNavigator() {
|
|
4
|
+
const Stack = createNativeStackNavigator();
|
|
5
|
+
|
|
6
|
+
return {
|
|
7
|
+
Navigator: Stack.Navigator,
|
|
8
|
+
Screen: Stack.Screen,
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const StackNavigator = createStackNavigator();
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { assertType, describe, it } from 'vitest';
|
|
2
|
+
import { createRoute, useParams } from './createRoute';
|
|
3
|
+
|
|
4
|
+
declare module './createRoute' {
|
|
5
|
+
interface RegisterScreen {
|
|
6
|
+
'/test': {
|
|
7
|
+
id: string;
|
|
8
|
+
name: string;
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
describe('createRoute', () => {
|
|
14
|
+
const Route = createRoute('/test', {
|
|
15
|
+
component: () => null,
|
|
16
|
+
validateParams: (params) => params as { id: string; name: string },
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('useParams', () => {
|
|
20
|
+
assertType<{
|
|
21
|
+
id: string;
|
|
22
|
+
name: string;
|
|
23
|
+
}>(Route.useParams());
|
|
24
|
+
|
|
25
|
+
assertType<Readonly<object | undefined>>(useParams({ strict: false }));
|
|
26
|
+
|
|
27
|
+
// @ts-expect-error - Type error should occur when strict is false
|
|
28
|
+
assertType<{ id: string; name: string }>(useParams({ strict: false }));
|
|
29
|
+
|
|
30
|
+
assertType<{
|
|
31
|
+
id: string;
|
|
32
|
+
name: string;
|
|
33
|
+
}>(useParams({ from: '/test', strict: true }));
|
|
34
|
+
|
|
35
|
+
assertType<{
|
|
36
|
+
id: string;
|
|
37
|
+
name: string;
|
|
38
|
+
}>(useParams({ from: '/test' }));
|
|
39
|
+
|
|
40
|
+
// @ts-expect-error Type error should occur when no options
|
|
41
|
+
assertType(useParams());
|
|
42
|
+
|
|
43
|
+
// @ts-expect-error Type error should occur when empty object
|
|
44
|
+
assertType(useParams({}));
|
|
45
|
+
|
|
46
|
+
// @ts-expect-error Type error should occur when path is not registered
|
|
47
|
+
assertType(useParams({ from: '/abcdefg' }));
|
|
48
|
+
|
|
49
|
+
// @ts-expect-error Type error should occur since 'from' and 'strict: false' are conflicting options
|
|
50
|
+
assertType(useParams({ from: '/test', strict: false }));
|
|
51
|
+
});
|
|
52
|
+
});
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type ParamListBase,
|
|
3
|
+
useNavigation as useNavigationNative,
|
|
4
|
+
useRoute,
|
|
5
|
+
} from '@granite-js/native/@react-navigation/native';
|
|
6
|
+
import { NativeStackNavigationProp } from '@granite-js/native/@react-navigation/native-stack';
|
|
7
|
+
import { useMemo } from 'react';
|
|
8
|
+
import { RESERVED_PATHS } from './constants';
|
|
9
|
+
import { defaultParserParams } from './utils/defaultParserParams';
|
|
10
|
+
|
|
11
|
+
export interface RouteOptions<T extends Readonly<object | undefined>> {
|
|
12
|
+
parserParams?: (params: Record<string, unknown>) => Record<string, unknown>;
|
|
13
|
+
validateParams?: (params: Readonly<object | undefined>) => T;
|
|
14
|
+
component: React.FC<any>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type NavigationProps = NativeStackNavigationProp<
|
|
18
|
+
// @ts-expect-error - override type
|
|
19
|
+
keyof RegisterScreen extends never ? ParamListBase : RegisterScreen
|
|
20
|
+
>;
|
|
21
|
+
|
|
22
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
23
|
+
export interface RegisterScreen {}
|
|
24
|
+
|
|
25
|
+
export function useNavigation() {
|
|
26
|
+
return useNavigationNative<NavigationProps>();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export type RouteHooksOptions<TScreen extends keyof RegisterScreen> =
|
|
30
|
+
| {
|
|
31
|
+
from: TScreen;
|
|
32
|
+
strict?: true;
|
|
33
|
+
}
|
|
34
|
+
| {
|
|
35
|
+
strict: false;
|
|
36
|
+
from?: never;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export const routeMap = new Map<
|
|
40
|
+
keyof RegisterScreen,
|
|
41
|
+
{ options: Omit<RouteOptions<any>, 'component'>; component: React.FC<any> }
|
|
42
|
+
>();
|
|
43
|
+
|
|
44
|
+
export function useMatchOptions<TScreen extends keyof RegisterScreen>(options: RouteHooksOptions<TScreen>) {
|
|
45
|
+
const route = useRoute();
|
|
46
|
+
const from = 'from' in options ? options.from : (route.name as TScreen);
|
|
47
|
+
const strict = 'from' in options ? true : options.strict;
|
|
48
|
+
|
|
49
|
+
if (strict && from !== route.name) {
|
|
50
|
+
throw new Error(`Cannot access parameters from route '${from}' in current route '${route.name}'`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return useMemo(() => {
|
|
54
|
+
if (!from) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (!(routeMap.has(from) || RESERVED_PATHS.includes(from))) {
|
|
59
|
+
throw new Error(`Route '${from}' is not registered`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const routeOptions = routeMap.get(from);
|
|
63
|
+
return {
|
|
64
|
+
...routeOptions?.options,
|
|
65
|
+
parserParams: routeOptions?.options.parserParams ?? defaultParserParams,
|
|
66
|
+
};
|
|
67
|
+
}, [from]);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function useParams<TScreen extends keyof RegisterScreen>(options: {
|
|
71
|
+
from: TScreen;
|
|
72
|
+
strict?: true;
|
|
73
|
+
}): RegisterScreen[TScreen];
|
|
74
|
+
export function useParams(options: { strict: false }): Readonly<object | undefined>;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* @public
|
|
78
|
+
* @category Screen Control
|
|
79
|
+
* @name useParams
|
|
80
|
+
* @description
|
|
81
|
+
*
|
|
82
|
+
* `useParams` is a hook that retrieves parameters from a specified route.
|
|
83
|
+
* Using this hook, you can easily access parameters of the current route.
|
|
84
|
+
* With the `validateParams` option, you can validate parameter structure and transform types,
|
|
85
|
+
* reducing runtime errors and writing safer code.
|
|
86
|
+
*
|
|
87
|
+
* @param {RouteHooksOptions<TScreen>} options Object containing information about the route to retrieve.
|
|
88
|
+
* @param {string} [options.from] Route path to retrieve parameters from. If not specified, retrieves parameters from the current route. Must be specified when strict mode is true.
|
|
89
|
+
* @param {boolean} [options.strict] Strict mode setting. When set to true, throws an error if the specified route doesn't match the current route. When set to false, skips validateParams validation and returns parameters of the current screen as is.
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* ### Retrieving Route Parameters
|
|
93
|
+
*
|
|
94
|
+
*
|
|
95
|
+
* ::: code-group
|
|
96
|
+
*
|
|
97
|
+
* ```tsx [pages/examples/use-params.tsx]
|
|
98
|
+
* import React from 'react';
|
|
99
|
+
* import { Text } from 'react-native';
|
|
100
|
+
* import { createRoute, useParams } from '@granite-js/react-native';
|
|
101
|
+
*
|
|
102
|
+
* export const Route = createRoute('/examples/use-params', {
|
|
103
|
+
* validateParams: (params) => params as { id: string },
|
|
104
|
+
* component: UseParamsExample,
|
|
105
|
+
* });
|
|
106
|
+
*
|
|
107
|
+
* function UseParamsExample() {
|
|
108
|
+
* // First method: Using the useParams method of the route object
|
|
109
|
+
* const params = Route.useParams();
|
|
110
|
+
*
|
|
111
|
+
* // Second method: Using the useParams hook directly
|
|
112
|
+
* const params2 = useParams({ from: '/examples/use-params' });
|
|
113
|
+
*
|
|
114
|
+
* // Third method: Using with strict mode set to false
|
|
115
|
+
* // When strict is false, retrieves parameters from the current route
|
|
116
|
+
* // and skips validation even if validateParams is defined
|
|
117
|
+
* const params3 = useParams({ strict: false }) as { id: string };
|
|
118
|
+
*
|
|
119
|
+
* return (
|
|
120
|
+
* <>
|
|
121
|
+
* <Text>{params.id}</Text>
|
|
122
|
+
* <Text>{params2.id}</Text>
|
|
123
|
+
* <Text>{params3.id}</Text>
|
|
124
|
+
* </>
|
|
125
|
+
* );
|
|
126
|
+
* }
|
|
127
|
+
* ```
|
|
128
|
+
* :::
|
|
129
|
+
*/
|
|
130
|
+
export function useParams<TScreen extends keyof RegisterScreen>(
|
|
131
|
+
options: RouteHooksOptions<TScreen>
|
|
132
|
+
): TScreen extends keyof RegisterScreen ? RegisterScreen[TScreen] : Readonly<object | undefined> {
|
|
133
|
+
const routeOptions = useMatchOptions(options);
|
|
134
|
+
const route = useRoute();
|
|
135
|
+
|
|
136
|
+
const params = useMemo(() => {
|
|
137
|
+
if (!routeOptions) {
|
|
138
|
+
return (route.params ?? {}) as Readonly<object | undefined>;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const parsedParams = routeOptions.parserParams(route.params as Record<string, string>);
|
|
142
|
+
return options.strict && routeOptions.validateParams ? routeOptions.validateParams(parsedParams) : parsedParams;
|
|
143
|
+
}, [routeOptions, route.params, options.strict]);
|
|
144
|
+
|
|
145
|
+
return params;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export const createRoute = <T extends Readonly<object | undefined>>(
|
|
149
|
+
path: keyof RegisterScreen,
|
|
150
|
+
options: RouteOptions<T>
|
|
151
|
+
) => {
|
|
152
|
+
const { component, ...restOptions } = options;
|
|
153
|
+
routeMap.set(path, { options: restOptions, component });
|
|
154
|
+
|
|
155
|
+
const _path = path as keyof RegisterScreen;
|
|
156
|
+
return {
|
|
157
|
+
_path,
|
|
158
|
+
useNavigation,
|
|
159
|
+
useParams: () => useParams({ from: _path, strict: true }) as T,
|
|
160
|
+
};
|
|
161
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Platform } from 'react-native';
|
|
2
|
+
import { getSchemeUri } from '../../native-modules';
|
|
3
|
+
|
|
4
|
+
export function useInitialRouteName(prefix: string) {
|
|
5
|
+
const initialScheme = getInitialScheme();
|
|
6
|
+
const pathname = initialScheme?.slice(prefix.length).split('?')[0];
|
|
7
|
+
const shouldUseIndex = initialScheme == null || pathname?.length === 0;
|
|
8
|
+
|
|
9
|
+
return shouldUseIndex ? '/' : pathname;
|
|
10
|
+
}
|
|
11
|
+
function getInitialScheme() {
|
|
12
|
+
const scheme = getSchemeUri();
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Removes trailing '/' on Android.
|
|
16
|
+
*/
|
|
17
|
+
if (Platform.OS === 'android') {
|
|
18
|
+
return scheme?.replaceAll(/\/+$/g, '');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return scheme;
|
|
22
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { NavigationContainer } from '@granite-js/native/@react-navigation/native';
|
|
2
|
+
import { useMemo, type ComponentProps, type ComponentType, type PropsWithChildren } from 'react';
|
|
3
|
+
import { getSchemeUri } from '../../native-modules';
|
|
4
|
+
import { StackNavigator } from '../components/StackNavigator';
|
|
5
|
+
import { RESERVED_KEYWORDS } from '../constants';
|
|
6
|
+
import { RequireContext } from '../types/RequireContext';
|
|
7
|
+
import { getRouteScreens, getScreenPathMapConfig } from '../utils';
|
|
8
|
+
import { createParentRouteScreenMap } from '../utils/createParentRouteScreenMap';
|
|
9
|
+
import { mergeParentLayoutScreen } from '../utils/mergeParentLayoutScreen';
|
|
10
|
+
import { getFileNameFromPath } from '../utils/path';
|
|
11
|
+
|
|
12
|
+
type NavigationContainerProps = ComponentProps<typeof NavigationContainer>;
|
|
13
|
+
|
|
14
|
+
export interface RouterControlsConfig {
|
|
15
|
+
prefix: string;
|
|
16
|
+
context: RequireContext;
|
|
17
|
+
screenContainer?: ComponentType<PropsWithChildren<any>>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function useRouterControls({ prefix, context, screenContainer: ScreenContainer }: RouterControlsConfig) {
|
|
21
|
+
const routeScreens = useMemo(() => getRouteScreens(context), [context]);
|
|
22
|
+
|
|
23
|
+
const registerScreens = useMemo(() => {
|
|
24
|
+
return routeScreens.filter(
|
|
25
|
+
(screen) => !RESERVED_KEYWORDS.includes(getFileNameFromPath(screen.path, { withExtension: false }))
|
|
26
|
+
);
|
|
27
|
+
}, [routeScreens]);
|
|
28
|
+
|
|
29
|
+
const layoutScreenMap = useMemo(() => createParentRouteScreenMap(routeScreens, '_layout'), [routeScreens]);
|
|
30
|
+
|
|
31
|
+
const Screens = useMemo(() => {
|
|
32
|
+
return registerScreens.map((routeScreen) => {
|
|
33
|
+
const Layout = mergeParentLayoutScreen(layoutScreenMap, routeScreen.path);
|
|
34
|
+
|
|
35
|
+
const Component = function Component() {
|
|
36
|
+
const element = (
|
|
37
|
+
<Layout>
|
|
38
|
+
<routeScreen.component />
|
|
39
|
+
</Layout>
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
return ScreenContainer == null ? element : <ScreenContainer>{element}</ScreenContainer>;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const routePath = routeScreen.path;
|
|
46
|
+
const screenOptions = routeScreen.component?.screenOptions ?? {};
|
|
47
|
+
|
|
48
|
+
return <StackNavigator.Screen key={routePath} name={routePath} component={Component} options={screenOptions} />;
|
|
49
|
+
});
|
|
50
|
+
}, [registerScreens, layoutScreenMap, ScreenContainer]);
|
|
51
|
+
|
|
52
|
+
const linkingOptions: NavigationContainerProps['linking'] = useMemo(() => {
|
|
53
|
+
return {
|
|
54
|
+
prefixes: [prefix],
|
|
55
|
+
config: {
|
|
56
|
+
screens: getScreenPathMapConfig(registerScreens),
|
|
57
|
+
},
|
|
58
|
+
async getInitialURL() {
|
|
59
|
+
const initialURL = getSchemeUri();
|
|
60
|
+
|
|
61
|
+
if (initialURL == null) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** @NOTE Korean paths need to be decoded. */
|
|
66
|
+
return decodeURI(initialURL);
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
}, [prefix, registerScreens]);
|
|
70
|
+
|
|
71
|
+
return { Screens, linkingOptions };
|
|
72
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Screen } from './Screen';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @name RouteScreen
|
|
5
|
+
*/
|
|
6
|
+
export interface RouteScreen {
|
|
7
|
+
/**
|
|
8
|
+
* @name path
|
|
9
|
+
* @description Path information (e.g. "/", "/list", "/list/:id", etc.)
|
|
10
|
+
*/
|
|
11
|
+
path: string;
|
|
12
|
+
/**
|
|
13
|
+
* @name component
|
|
14
|
+
* @description Screen component
|
|
15
|
+
*/
|
|
16
|
+
component: Screen;
|
|
17
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { NativeStackNavigationOptions } from '@granite-js/native/@react-navigation/native-stack';
|
|
2
|
+
import type { ComponentType } from 'react';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @name Screen
|
|
6
|
+
* @description A Screen that serves as a unit of navigation
|
|
7
|
+
* @example
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* function Page() {
|
|
11
|
+
* // ...
|
|
12
|
+
* }
|
|
13
|
+
*
|
|
14
|
+
* Page.screenOptions = {
|
|
15
|
+
* // ...
|
|
16
|
+
* }
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export type Screen = ComponentType & GraniteScreenOptions;
|
|
20
|
+
|
|
21
|
+
interface GraniteScreenOptions {
|
|
22
|
+
/** @description Add this when customization is needed for NativeStack Screen's screenOptions */
|
|
23
|
+
screenOptions?: NativeStackNavigationOptions;
|
|
24
|
+
}
|