From a6b8ccdfd01f7cc733174734b4babe499158a295 Mon Sep 17 00:00:00 2001 From: Batyr Date: Sun, 19 Mar 2023 20:03:22 +0100 Subject: [PATCH] `drawer.updateOptions` added --- README.md | 4 +- package.json | 2 +- src/navio.tsx | 110 ++++++++++++++++++++++++++++++++++++-------------- src/types.tsx | 7 +--- 4 files changed, 84 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index 7160530..83a8a52 100644 --- a/README.md +++ b/README.md @@ -873,11 +873,11 @@ If you've found any diffilculties with using Navio and [React Navigation](https: There are still some things I would like to add to the library: -- [x] `.updateOptions()` for specific tab. +- [x] `.updateOptions()` for specific tab and drawer. - [x] Tabs can be placed inside Drawer and vice versa. +- [ ] Make deeplinking easier by providing `linking` prop to screens. - [ ] Improve docs. Deeplinking section, etc. Based on this [issue](https://github.com/kanzitelli/expo-starter/issues/29). - [ ] Make Navio universal by adding [RNN](https://github.com/wix/react-native-navigation) and [rnn-screens](https://github.com/kanzitelli/rnn-screens). -- [ ] Make deeplinking easier by providing `linking` prop to screens. - [ ] Extend Navio funtionality and app layout. - [ ] Easy integration of Navio with React Navigation (eg. navio.Stack()) - [ ] TypeScript issues @ `index.tsx` file. diff --git a/package.json b/package.json index 01e639f..7f59ac3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rn-navio", - "version": "0.0.6", + "version": "0.0.7-rc.1", "description": "🧭 Navigation library for React Native (Expo). Build once, navigate from anywhere to everywhere!", "author": "Batyr ", "homepage": "https://github.com/kanzitelli/rn-navio", diff --git a/src/navio.tsx b/src/navio.tsx index 46c3839..705bf09 100644 --- a/src/navio.tsx +++ b/src/navio.tsx @@ -35,7 +35,7 @@ import { TTabsContentValue, TDrawerContentValue, DefaultOptions, - TunnelEvent$Tabs$UpdateOptions$Params, + TunnelEvent$UpdateOptions$Params, } from './types'; import {NavioTunnel} from './tunnel'; import {safeOpts} from './help'; @@ -135,6 +135,7 @@ export class Navio< // we use them to store tabs updated options during session private __tabsUpdatedOptions: Record = {}; + private __drawerUpdatedOptions: Record = {}; // ======== // | Init | @@ -400,7 +401,7 @@ export class Navio< self.tunnel.echo('tabs.updateOptions', { name, options, - } as TunnelEvent$Tabs$UpdateOptions$Params); + } as TunnelEvent$UpdateOptions$Params); } }, @@ -500,6 +501,21 @@ export class Navio< } }, + /** + * `updateOptions(...)` action updates provided drawer's options. + * + * @param name name of the drawer content + * @param options `DrawerNavigationOptions` options for the drawer. + */ + updateOptions(name: T, options: DrawerNavigationOptions) { + if (self.navIsReady) { + self.tunnel.echo('drawer.updateOptions', { + name, + options, + } as TunnelEvent$UpdateOptions$Params); + } + }, + /** * `setRoot(...)` action sets a new app root from drawers. * @@ -635,9 +651,6 @@ export class Navio< return <>; } - // -- getting navigator props - const navigatorProps = this.__StackGetNavigatorProps(stackDef); - // -- building navigator const Stack = createNativeStackNavigator(); const StackScreensMemo = useMemo(() => { @@ -646,6 +659,9 @@ export class Navio< ); }, [stackDef, screens, stacks]); + // -- getting navigator props + const navigatorProps = this.__StackGetNavigatorProps(stackDef); + return {StackScreensMemo}; }; @@ -753,7 +769,7 @@ export class Navio< useEffect(() => { this.tunnel.on( 'tabs.updateOptions', - (params: TunnelEvent$Tabs$UpdateOptions$Params) => { + (params: TunnelEvent$UpdateOptions$Params) => { const tcname = params.name; const tcopts = params.options; this.__tabsUpdatedOptions = { @@ -873,10 +889,7 @@ export class Navio< private Drawer: React.FC<{ drawerDef: TDrawerDefinition | undefined; }> = ({drawerDef}) => { - const {drawers, hooks} = this.layout; - - // -- running hooks - if (hooks) for (const h of hooks) if (h) h(); + const {drawers, defaultOptions, hooks} = this.layout; if (!drawers) { this.log('No drawers registered'); @@ -890,25 +903,72 @@ export class Navio< return <>; } - // -- building navigator - const Drawer = useMemo(() => createDrawerNavigator(), [drawers]); - const currentDrawersContentKeys = useMemo( - () => Object.keys(currentDrawer.content), - [currentDrawer.content], + // -- internal state + const [updatedOptions, setUpdatedOptions] = useState>( + {}, ); + // -- internal effects + useEffect(() => { + this.tunnel.on( + 'drawer.updateOptions', + (params: TunnelEvent$UpdateOptions$Params) => { + const name = params.name; + const opts = params.options; + this.__drawerUpdatedOptions = { + ...this.__drawerUpdatedOptions, + [name]: {...this.__drawerUpdatedOptions[name], ...opts}, + }; + setUpdatedOptions(this.__drawerUpdatedOptions); + }, + ); + }, [drawerDef]); + + // -- internal memos + const currentDrawerContent = useMemo(() => currentDrawer.content, [currentDrawer]); + const currentDrawerContentKeys = useMemo( + () => Object.keys(currentDrawerContent), + [currentDrawerContent], + ); + + // -- running hooks + if (hooks) for (const h of hooks) if (h) h(); + + // -- building navigator + const Drawer = useMemo(() => createDrawerNavigator(), [drawers]); const DrawerScreensMemo = useMemo(() => { - return currentDrawersContentKeys.map(key => + return currentDrawerContentKeys.map(key => this.DrawerScreen({ name: key, DrawerNavigator: Drawer, content: currentDrawer.content[key], }), ); - }, [Drawer, currentDrawersContentKeys]); + }, [Drawer, currentDrawerContentKeys]); + + // options + const Opts: BaseOptions = props => { + const rName = props?.route?.name; + if (!rName) return {}; + + const customDefaultOptions = this.getCustomDefaultOptions()?.drawers?.screen ?? {}; + const dcsDefaultOpts = defaultOptions?.drawers?.screen ?? {}; + const dcsOpts = (currentDrawer?.content[rName] as any)?.options ?? {}; + const dcnpOpts = currentDrawer?.navigatorProps?.screenOptions ?? {}; + const updOpts = updatedOptions[rName] ?? {}; + return { + ...safeOpts(customDefaultOptions)(props), // [!] custom default options + ...safeOpts(dcsDefaultOpts)(props), // navio.defaultOptions.drawers.screen + ...safeOpts(dcnpOpts)(props), // navio.drawers.[].navigatorProps.screenOptions -- because we override it below + ...safeOpts(dcsOpts)(props), // tab-based options + ...safeOpts(updOpts)(props), // upddated options (navio.drawers.updateOptions()) + }; + }; // must be function. merge options from buildNavio. also providing default options return ( - {DrawerScreensMemo} + + {DrawerScreensMemo} + ); }; @@ -917,8 +977,6 @@ export class Navio< name: string; content: TDrawerContentValue; }> = ({DrawerNavigator, name, content}) => { - const {defaultOptions} = this.layout; - if (!content.stack && !content.tabs) { this.log(`Either 'stack' or 'tabs' must be provided for "${name}" drawer content.`); return null; @@ -932,18 +990,8 @@ export class Navio< ? this.Tabs({tabsDef: content.tabs}) : null; - // options - const customDefaultOptions = this.getCustomDefaultOptions()?.drawers?.screen ?? {}; - const dcsDefaultOptions = defaultOptions?.drawers?.screen ?? {}; - const dcsOpts = content?.options ?? {}; - const Opts: BaseOptions = props => ({ - ...safeOpts(customDefaultOptions)(props), // [!] custom default options - ...safeOpts(dcsDefaultOptions)(props), // navio.defaultOptions.drawers.screen - ...safeOpts(dcsOpts)(props), // drawer-based options - }); // must be function. merge options from buildNavio. also providing default options - // screen - return ; + return ; }; // | Modals | diff --git a/src/types.tsx b/src/types.tsx index 21df544..dafa6c5 100644 --- a/src/types.tsx +++ b/src/types.tsx @@ -159,15 +159,12 @@ export type RootProps = { }; // Tunnel (Event Emitter) -export type TunnelEvent$Tabs$UpdateOptions$Params< - Name = string, - Options = BottomTabNavigationOptions, -> = { +export type TunnelEvent$UpdateOptions$Params = { name: Name; options: Options; }; -export type TunnelEvent = 'tabs.updateOptions'; +export type TunnelEvent = 'tabs.updateOptions' | 'drawer.updateOptions'; export type TunnelParams = T; export type TunnelListener = (params: TunnelParams) => void; export type TunnelEvents = Partial>;