Skip to content
This repository has been archived by the owner on May 8, 2019. It is now read-only.

Tab navigator experiments #19

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions example/src/App.re
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
let stack = StackExample.render;
let tab = TabExample.render;
/* let tab = TabExample.render; */
let drawer = DrawerExample.render;

let switchNavigator = SwitchExample.render;

let app = drawer;
let app = stack;
6 changes: 2 additions & 4 deletions example/src/DrawerExample.re
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,8 @@ module Drawer =
let drawerOptions =
DrawerNavigation.drawerOptions(~activeTintColor="#847", ());

let order = [Dashbord, Settings];

let getItem = tab =>
switch (tab) {
let getItem = drawerItem =>
switch (drawerItem) {
| Dashbord => (
"Dashbord",
(() => <Items.Dashboard />),
Expand Down
2 changes: 1 addition & 1 deletion example/src/StackExample.re
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ module Stack =
<Screen navigation text={"Browsing profile of: " ++ userId} />,
screenOptions(~title="Hello " ++ userId, ()),
)
| TabExample => (<TabExample navigation />, screenOptions())
| TabExample => (<TabExample navigation/>, screenOptions())
};
});

Expand Down
14 changes: 8 additions & 6 deletions example/src/TabExample.re
Original file line number Diff line number Diff line change
Expand Up @@ -6,35 +6,37 @@ module Tabs =
type tabs = Config.tabs;
type order = list(tabs);

let containerName="TabExample";

let tabBarOptions =
TabNavigator.tabBarOptions(~activeTintColor="#847", ());

let order = [Info, Profile, Settings];

let getTab = tab => {
let getTab = (tab, navigationProp) => {
switch (tab) {
| Info => (
"Info",
((navigation) => <Tabs.Info navigation/>),
(() => <Tabs.Info navigation={navigationProp}/>),
TabNavigator.screenOptions(~title="Info", ()),
)
| Profile => (
"Profile",
((navigation) => <Tabs.Profile navigation/>),
(() => <Tabs.Profile navigation={navigationProp}/>),
TabNavigator.screenOptions(~title="Profile", ()),
)
| Settings => (
"Settings",
((navigation) => <Tabs.Settings navigation/>),
(() => <Tabs.Settings navigation={navigationProp}/>),
TabNavigator.screenOptions(~title="Settings", ()),
)
};
};
});

let render = Tabs.render;
let render = Tabs.make;

let make = (~navigation, _children) => {
...(ReasonReact.statelessComponent("TabExample")),
render: _ => Tabs.render
render: _ => ReasonReact.createElement(render,[||])
}
2 changes: 1 addition & 1 deletion example/src/Tabs.re
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ module Settings = {
Styles.settingsContainer,
])}>
<View> <Text style=Styles.titile> {str("Settings")} </Text> </View>
<Button onPress={() => navigation.navigate("Profile")} title="info"/>
<Button onPress={() => navigation.navigate("Profile")} title="info" />
</SafeAreaView>,
};
};
2 changes: 0 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,6 @@
"react-navigation": "^3.0.5",
"react-navigation-drawer": "^1.0.5",
"react-navigation-stack": "^1.0.5",
"@react-navigation/core": "^3.0.1",
czystyl marked this conversation as resolved.
Show resolved Hide resolved
"@react-navigation/native": "^3.0.3",
"react-navigation-tabs": "1.0.1",
"reason-react": "^0.5.3"
},
Expand Down
18 changes: 9 additions & 9 deletions src/ReactNavigation.re
Original file line number Diff line number Diff line change
@@ -1,39 +1,39 @@
module Core = {
[@bs.module "@react-navigation/core"]
external stackRouter: ('a, 'b) => ReasonReact.reactElement = "StackRouter";
external stackRouter: ('a, 'b) => ReasonReact.reactClass = "StackRouter";

[@bs.module "@react-navigation/core"]
external switchRouter: ('a, 'b) => ReasonReact.reactElement = "SwitchRouter";
external switchRouter: ('a, 'b) => ReasonReact.reactClass = "SwitchRouter";

[@bs.module "@react-navigation/core"]
external createNavigator: ('a, 'b, 'c) => ReasonReact.reactElement = "";
external createNavigator: ('a, 'b, 'c) => ReasonReact.reactClass = "";
};

module Native = {
[@bs.module "@react-navigation/native"]
external createAppContainer: 'a => ReasonReact.reactElement = "";
external createAppContainer: 'a => ReasonReact.reactClass = "";
};

module Stack = {
[@bs.module "react-navigation-stack"]
external stackView: ReasonReact.reactElement = "StackView";
external stackView: ReasonReact.reactClass = "StackView";
};

module Tab = {
[@bs.module "react-navigation-tabs"]
external createBottomTabNavigator: ('a, 'b) => ReasonReact.reactElement = "";
external createBottomTabNavigator: ('a, 'b) => ReasonReact.reactClass = "";

[@bs.module "react-navigation-tabs"]
external createMaterialTopTabNavigator: 'a => ReasonReact.reactElement = ""
external createMaterialTopTabNavigator: 'a => ReasonReact.reactClass = ""
};

module Switch = {
[@bs.module "@react-navigation/core"]
external switchView: ReasonReact.reactElement = "SwitchView";
external switchView: ReasonReact.reactClass = "SwitchView";
};

module Drawer = {
[@bs.module "react-navigation-drawer"]
external create: ('a, 'b) => ReasonReact.reactElement =
external create: ('a, 'b) => ReasonReact.reactClass =
"createDrawerNavigator";
};
25 changes: 18 additions & 7 deletions src/TabNavigator.re
Original file line number Diff line number Diff line change
Expand Up @@ -56,26 +56,39 @@ module type TabConfig = {
type tabs;
type order = list(tabs);
let order: order;
let containerName: string;
let tabBarOptions: tabBarOptions;
let getTab:
tabs => (Js.Dict.key, navigation => ReasonReact.reactElement, screenOptions);
(tabs, navigation) =>
(Js.Dict.key, unit => ReasonReact.reactElement, screenOptions);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does "screen" has to be a function?

unit => ReasonReact.reactElement

I would unify it with StackNavigator and just make it a React element. It will make the API much easier. If we need a functional form, we can always wrap it later on our side.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need users to define a string of a route explicitly. I know that TabNavigator's router requires us to use a concept of string to register a route.

We can do better.

There are two ways of handling that:

a) StackNavigator's way - we define only one "route", let's call it Container. Every "route" is a "" element that accepts current screen to render as a prop. For example, "" where "EditUser(5)" could be a ReasonML variant.

b) Define a hashing function that can generate a UUID based on a variant it is given. For example, for "EditUser(5)", this could be "Route_EditUser_5". It's representation is not important as long as the function is stable and can generate same string for a given variant. This will be only used internally by the React Navigation.

Given that TabNavigator doesn't have StackNavigator's limitations, I propose we go with "b" for the sake of simplicity. We can always change it later, transparently for the end users.

};

module Create = (Config: TabConfig) => {
[@bs.deriving abstract]
type navigatorConfig = {initialRouteName: string};


[@bs.deriving abstract]
type routeConfig = {
screen: navigation => ReasonReact.reactElement,
screen: unit => ReasonReact.reactElement,
navigationOptions: screenOptions,
};

module NavigationProp = {
[@bs.send] external navigate: string => unit = "navigate";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we pass the Variant to the navigate function, or not?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah in the end we need to pass a variant. We don't want to use string. This is just an experiment. This is not working yet. navigation is WIP.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, would be great to add a comment there to know what's going there

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case we don't. "NavigationProp" module is a binding to React Navigation's "navigation" that operates on strings and is not aware of ReasonML variant.

This is going to be super similar to

module NavigationProp = {
type t;
module State = {
type t;
[@bs.get] external getParams: t => option(routeProps) = "params";
};
[@bs.send] external push: (t, string, routeProps) => unit = "push";
[@bs.get "state"] external getState: t => State.t = "";
let getParams = t => getState(t) |> State.getParams;
};
and once we complete both, I think there's an opportunity to extract a common part.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


[@bs.send] external goBack: unit => unit = "goBack";
};

let makeNavigationProp = () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should accept a "navigation" prop so that we can actually talk to a real "navigation". See how StackNavigation handles that. The code will be exactly the same, except for the "routeName" part. Depending on the approach you take, this will be either similar to StackNavigation (passing variant to a Container route) or generating a hash (to be used as a routeName) from a given variant.

Either way, "navigate" should accept a "tab" (variant) instead of a "routeName" (string). "NavigationProp.navigate" is good - ReactNavigation operates on "string"s and we should keep it that way.

navigate: routeName => NavigationProp.navigate(routeName),
goBack: () => NavigationProp.goBack(),
};

let tabs =
Config.order
|> List.map(tab => {
let (tabname, screen, screenOptionsConfig) = Config.getTab(tab);
let (tabname, screen, screenOptionsConfig) =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As already said, "tabName" could be generated dynamically. "screen" doesn't have to be a function, we can wrap it here if needed.

Config.getTab(tab, makeNavigationProp());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

makeNavigationProp in the future will accept original navigation prop. How do we handle that case? I know that the status of navigation prop is in WIP but I'm wondering how we handle this

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder that too 😉 We need to discuss that. You, me and @grabbou

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should handle that just like we handle it in StackNavigator ->

let makeNavigationProp = (navigation: NavigationProp.t) => {
push: route =>
NavigationProp.push(
navigation,
containerDisplayName,
routeProps(~route),
),
pop: _route => (),
};

(
tabname,
routeConfig(~screen, ~navigationOptions=screenOptionsConfig),
Expand All @@ -89,10 +102,8 @@ module Create = (Config: TabConfig) => {
"tabBarOptions": Config.tabBarOptions,
};

/* navigator */
let navigator =
ReactNavigation.Tab.createBottomTabNavigator(tabs, tabBarOptions);

/* Wrap StackNavigator with the AppContainer - temporary */
let render = ReactNavigation.Native.createAppContainer(navigator);
let make = ReactNavigation.Native.createAppContainer(navigator);
grabbou marked this conversation as resolved.
Show resolved Hide resolved
};
Loading