import { createNavigationContainerRef, NavigationContainer, NavigationContainerRefWithCurrent, ParamListBase, RouteProp, } from '@react-native-bedrock/native/@react-navigation/native'; import { NativeStackNavigationOptions } from '@react-native-bedrock/native/@react-navigation/native-stack'; import { ComponentProps, ComponentType, Fragment, PropsWithChildren, ReactElement, useCallback, useMemo } from 'react'; import { BedrockStack } from './components/BedrockStack'; import { CanGoBackGuard } from './components/CanGoBackGuard'; import { RouterBackButton, RouterBackButtonProps } from './components/RouterBackButton'; import { useBedrockRouterControls } from './hooks/useBedrockRouterControls'; import { useInitialRouteName } from './hooks/useInitialRouteName'; import { RequireContext } from './types'; import { BASE_STACK_NAVIGATOR_STYLE } from './types/screen-option'; import { InitialProps } from '../initial-props'; /** * @internal */ export interface InternalRouterProps { /** * @name context * @description * 파일 기반 라우팅에 사용되는 화면 정보를 담은 객체예요. */ context: RequireContext; /** * @name prefix * @description * 스킴이 실행될 때 사용할 prefix 이에요. 예를 들어, `intoss://my-service/intro` 를 진입하기 위해서는 `intoss://my-service`를 prefix로 설정해야 해요. */ prefix: string; /** * @name canGoBack * @description * 뒤로 갈 수 있는지 여부예요. 기본값은 `true`이고, `true` 로 설정하면 `@react-navigation/native` 의 뒤로 가기 제스처나 뒤로가기 버튼을 사용할 수 있어요. * @default true */ canGoBack?: boolean; /** * @name onBack * @description * 뒤로 이동했을 때 호출되는 콜백 함수예요. */ onBack?: () => void; /** * @name container * @description * `@react-navigation/native`의 `Navigator`를 감싸는 컨테이너 컴포넌트예요. */ container: ComponentType>; initialProps: InitialProps; } export type RouterProps = StackNavigatorProps & NavigationContainerProps; interface StackNavigatorProps { /** * @name navigationContainerRef * @description * `@react-navigation/native`의 `NavigationContainerRef`를 외부에서 만들어 전달할 수 있어요. 이렇게 하면 외부에서 라우터를 제어할 수 있어요. */ navigationContainerRef?: NavigationContainerRefWithCurrent; /** * @name defaultScreenOption * @description * 화면에 대한 기본 옵션이에요. 공통적으로 화면에 적용할 옵션을 설정할 수 있어요. 예를 들어, `title`이나 `headerStyle` 등을 설정할 수 있어요. */ defaultScreenOption?: | NativeStackNavigationOptions | ((props: { route: RouteProp; navigation: any }) => NativeStackNavigationOptions); /** * @name screenContainer * @description * 각 `Screen` 컴포넌트를 감싸는 컨테이너 컴포넌트예요. */ screenContainer?: ComponentType>; } type NavigationContainerProps = Pick< ComponentProps, 'ref' | 'documentTitle' | 'fallback' | 'onReady' | 'onUnhandledAction' | 'onStateChange' >; /** * @category Components * @kind function * @name Router * @description * React Native 환경에서 페이지 이동을 위한 라우터 컴포넌트예요. * `pages/*`의 파일 이름 규칙에 따라 화면에 맞는 경로를 자동으로 지정해줘요. * 이 컴포넌트를 사용하면 Next.js 의 파일 기반 라우팅과 같은 방식으로 화면을 관리할 수 있어요. * * @param {string} prefix 스킴이 실행될 때 사용할 prefix 예요. 예를 들어, `intoss://my-service/intro` 경로로 진입하려면 `intoss://my-service`를 prefix로 설정해야 해요. * @param {RequireContext} context 파일 기반 라우팅을 위한 화면들에 대한 정보를 담은 객체예요. * @param {NavigationContainerRefWithCurrent} [navigationContainerRef] `@react-navigation/native`의 `NavigationContainerRef`를 외부에서 만들어 전달할 수 있어요. 이렇게 하면 외부에서 라우터를 제어할 수 있어요. * @param {NativeStackNavigationOptions | ((props: { route: RouteProp; navigation: any }) => NativeStackNavigationOptions)} [defaultScreenOption] 화면에 대한 기본 옵션이에요. 공통적으로 화면에 적용할 옵션을 설정할 수 있어요. 예를 들어, title 이나 headerStyle 등을 설정할 수 있어요. * @param {boolean} [canGoBack=true] 뒤로 갈 수 있는지 여부예요. 기본값은 `true`이며, `true` 로 설정되면 `@react-navigation/native` 의 뒤로 가기 제스처 또는 뒤로가기 버튼을 사용할 수 있어요. * @param {() => void} [onBack] 사용자가 뒤로가기 버튼을 누르거나 뒤로가기 제스처를 사용했을 때 호출되는 콜백 함수예요. 예를 들어, 사용자가 뒤로가기 버튼을 눌렀을 때 이 함수에서 로그를 남기도록 설정할 수 있어요. * @param {ComponentType<{ children: ReactNode }>} [container=Fragment] `@react-navigation/native`의 `Navigator`를 감싸는 컨테이너 컴포넌트예요. * @param {NavigationContainerProps} [navigationContainerProps] - `@react-navigation/native`의 `NavigationContainer`에 전달할 props를 설정할 수 있어요. * * @returns {ReactElement} - 라우터 컴포넌트를 반환해요. * @example * ```tsx * import { Router } from 'react-native-bedrock'; * import { context } from '../require.context'; * * function App() { * return ; * } * ``` */ export function Router({ // Internal props prefix, context, canGoBack = true, onBack, container: BedrockContainer = Fragment, initialProps, // Public props (NavigationContainer) navigationContainerRef, defaultScreenOption, screenContainer, // Public props (StackNavigator) ...navigationContainerProps }: InternalRouterProps & RouterProps): ReactElement { const initialRouteName = useInitialRouteName(prefix); const { BedrockScreens, linkingOptions } = useBedrockRouterControls({ prefix, context, screenContainer }); const ref = useMemo(() => navigationContainerRef ?? createNavigationContainerRef(), [navigationContainerRef]); const headerLeft = useCallback( (backButtonProps: Omit) => ( ), [onBack, canGoBack, ref] ); const screenOptions = useCallback( (screenProps: any) => ({ ...BASE_STACK_NAVIGATOR_STYLE, gestureEnabled: canGoBack, headerLeft, ...(typeof defaultScreenOption === 'function' ? defaultScreenOption(screenProps) : defaultScreenOption), }), [canGoBack, defaultScreenOption, headerLeft] ); return ( {BedrockScreens} ); }