Skip to content

Commit

Permalink
Merge pull request #15 from kanzitelli/update-options-stacks-etc
Browse files Browse the repository at this point in the history
Update options for drawer + Better docs + Other improvs
  • Loading branch information
kanzitelli authored Mar 19, 2023
2 parents f4d7f50 + 4260a35 commit 2b2bd75
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 41 deletions.
32 changes: 28 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -414,7 +414,7 @@ export default () => <navio.App />;

</details>

Advanced example with all available props can be found @ [expo-starter](https://github.com/kanzitelli/expo-starter/blob/master/src/navio.tsx)
Advanced example with all available props can be found at [expo-starter](https://github.com/kanzitelli/expo-starter/blob/master/src/navio.tsx)

## Layout

Expand Down Expand Up @@ -847,6 +847,10 @@ Can be used to open the drawer pane if closed, or close if open.

Can be used to jump to an existing route in the drawer navigator.

#### `.drawers.updateOptions(name, options)`

Updates options for a given drawer menu content. Can be used to change its title.

#### `.drawers.setRoot(name)`

Sets a new app root from drawers.
Expand All @@ -861,7 +865,27 @@ Can be used to show an existing modal.

## TypeScript

Navio is developed in TypeScript from the beginning. TypeScript helps with autocompletion and to achieve better DX. There are still some cases where I don't know the best way of doing it in TypeScript. So if you are a TypeScript expert, please open an issue for help.
Navio is developed in TypeScript from the beginning. TypeScript helps with autocompletion and to achieve better DX. There are still some issues (could be found at `index.tsx`). So if you are a TypeScript expert, please open an issue for help.

#### Autocompletion

In order to use full power of TS autocompletion, you'll need to define all layout components (could be just empty object). I don't know how to fix that at the moment.

```tsx
const navio = Navio.build({
screens: {Home, Settings},
stacks: {MainStack: ['Main', 'Settings']},
root: '...', // 🚫 won't help w/ autocompletion
});

const navio = Navio.build({
screens: {Home, Settings},
stacks: {MainStack: ['Main', 'Settings']},
drawers: {},
tabs: {},
root: '...', // ✅ will help w/ autocompletion
});
```

## Navio + React Navigation

Expand All @@ -873,11 +897,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.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -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 <dev@batyr.io>",
"homepage": "https://github.com/kanzitelli/rn-navio",
Expand Down
110 changes: 79 additions & 31 deletions src/navio.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -135,6 +135,7 @@ export class Navio<

// we use them to store tabs updated options during session
private __tabsUpdatedOptions: Record<string, BottomTabNavigationOptions> = {};
private __drawerUpdatedOptions: Record<string, DrawerNavigationOptions> = {};

// ========
// | Init |
Expand Down Expand Up @@ -400,7 +401,7 @@ export class Navio<
self.tunnel.echo('tabs.updateOptions', {
name,
options,
} as TunnelEvent$Tabs$UpdateOptions$Params<TabsContentName>);
} as TunnelEvent$UpdateOptions$Params<TabsContentName, BottomTabNavigationOptions>);
}
},

Expand Down Expand Up @@ -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<T extends DrawersContentName>(name: T, options: DrawerNavigationOptions) {
if (self.navIsReady) {
self.tunnel.echo('drawer.updateOptions', {
name,
options,
} as TunnelEvent$UpdateOptions$Params<DrawersContentName, DrawerNavigationOptions>);
}
},

/**
* `setRoot(...)` action sets a new app root from drawers.
*
Expand Down Expand Up @@ -635,9 +651,6 @@ export class Navio<
return <></>;
}

// -- getting navigator props
const navigatorProps = this.__StackGetNavigatorProps(stackDef);

// -- building navigator
const Stack = createNativeStackNavigator();
const StackScreensMemo = useMemo(() => {
Expand All @@ -646,6 +659,9 @@ export class Navio<
);
}, [stackDef, screens, stacks]);

// -- getting navigator props
const navigatorProps = this.__StackGetNavigatorProps(stackDef);

return <Stack.Navigator {...navigatorProps}>{StackScreensMemo}</Stack.Navigator>;
};

Expand Down Expand Up @@ -753,7 +769,7 @@ export class Navio<
useEffect(() => {
this.tunnel.on(
'tabs.updateOptions',
(params: TunnelEvent$Tabs$UpdateOptions$Params<string>) => {
(params: TunnelEvent$UpdateOptions$Params<string, BottomTabNavigationOptions>) => {
const tcname = params.name;
const tcopts = params.options;
this.__tabsUpdatedOptions = {
Expand Down Expand Up @@ -873,10 +889,7 @@ export class Navio<
private Drawer: React.FC<{
drawerDef: TDrawerDefinition<DrawersName> | 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');
Expand All @@ -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<Record<string, DrawerNavigationOptions>>(
{},
);

// -- internal effects
useEffect(() => {
this.tunnel.on(
'drawer.updateOptions',
(params: TunnelEvent$UpdateOptions$Params<string, DrawerNavigationOptions>) => {
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<BottomTabNavigationOptions> = 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 (
<Drawer.Navigator {...currentDrawer.navigatorProps}>{DrawerScreensMemo}</Drawer.Navigator>
<Drawer.Navigator {...currentDrawer.navigatorProps} screenOptions={Opts}>
{DrawerScreensMemo}
</Drawer.Navigator>
);
};

Expand All @@ -917,8 +977,6 @@ export class Navio<
name: string;
content: TDrawerContentValue<ScreensName, StacksName, TabsName>;
}> = ({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;
Expand All @@ -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<DrawerNavigationOptions> = 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 <DrawerNavigator.Screen key={name} name={name} component={C} options={Opts} />;
return <DrawerNavigator.Screen key={name} name={name} component={C} />;
};

// | Modals |
Expand Down
7 changes: 2 additions & 5 deletions src/types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -159,15 +159,12 @@ export type RootProps<RootName extends string> = {
};

// Tunnel (Event Emitter)
export type TunnelEvent$Tabs$UpdateOptions$Params<
Name = string,
Options = BottomTabNavigationOptions,
> = {
export type TunnelEvent$UpdateOptions$Params<Name = string, Options = any> = {
name: Name;
options: Options;
};

export type TunnelEvent = 'tabs.updateOptions';
export type TunnelEvent = 'tabs.updateOptions' | 'drawer.updateOptions';
export type TunnelParams<T = any> = T;
export type TunnelListener = (params: TunnelParams) => void;
export type TunnelEvents = Partial<Record<TunnelEvent, TunnelListener[]>>;

0 comments on commit 2b2bd75

Please sign in to comment.