grahammendick / navigation

Scene-Based Navigation for React and React Native
https://grahammendick.github.io/navigation/
Apache License 2.0
571 stars 40 forks source link

custom navigation bar being stacked over on native web #769

Closed aleedmanath closed 7 months ago

aleedmanath commented 7 months ago

Hi @grahammendick. Thanks for your work on this library. I'm appreciating the simplicity of the router; though still getting a feel for the navigation views.

Regarding issue, I'm trying to render a set of stacks scenes with a custom navigation bar (see below). This works on native but not on native web. The ReactNavigationNativeWeb logic seems to always render one scene. Looks like both Navbar and Scene1 get mounted but Scene1 gets placed on top.

I imagine the render or animation logic is replacing them. But really strange that that happens outside the NavigationStack, and I can't place the custom Navbar outside the NavigationHandler since I'm using it's context to highlight active routes.

Example:

    <NavigationHandler stateNavigator={navigator}>
            <Navbar />
            <NavigationStack>
                <Scene stateKey="1"><Scene1 /></Scene >
                <Scene stateKey="2"><Scene2 /></Scene>
        </NavigationStack >
    </NavigationHandler>

For now I'll just use the regular navigation for web. But it would be great to know why/where the Navbar is getting replaced (or stacked over) and what the expected way of doing this is (assuming this behavior is expected). Thank you.

grahammendick commented 7 months ago

Hey @aleedmanath, it's great to hear from you.

The rendering on the web is completely up to you. For example, here's how to add a 'Hello World' header to the React Native Web twitter sample. You can paste this into App.js, run npm run build, open up app.html and you'll see the header.

const App = () => (
  <NavigationHandler stateNavigator={stateNavigator}>
    <h1 style={{color: 'white'}}>Hello World</h1>
    <NavigationStack
      crumbStyle={from => from ? 'scale_in' : 'scale_out'}
      unmountStyle={from => from ? 'slide_in' : 'slide_out'}
      unmountedStyle={{translate: 100, scale: 1, opacity: 1}}
      mountedStyle={{translate: 0, scale: 1, opacity: 1}}
      crumbedStyle={{translate: 5, scale: 0.9, opacity: 0}}
      renderTransition={({translate, scale, opacity}, scene, key) => (
        <View key={key}
          style={{
            transform: `translate(${translate}%) scale(${scale}, ${scale})`,
            opacity,
            position: 'absolute',
            backgroundColor: '#fff',
            left: 0, right: 0, top: '100px', bottom: 0,
            overflow: 'hidden'
          }}>
          {scene}
        </View>
      )}>
      <Scene stateKey="tabs"><Tabs /></Scene>
      <Scene stateKey="notifications"><Notifications /></Scene>
      <Scene stateKey="tweet"><Tweet /></Scene>
      <Scene stateKey="timeline"><Timeline /></Scene>
    </NavigationStack>
  </NavigationHandler>
);

But I don't recommend sharing a header across the stack. On native, I recommend using the Navigation router's NavigationBar component. You add a NavigationBar to each scene. This gives you 100% native UX. For example, on iOS you'll get the platform title animation as the new scene animates into place.

aleedmanath commented 7 months ago

I think this may be a bug then. Did you try that yourself? Did as described and didn't work for me.

Btw, is there a way to get it to default to web behavior? So one API (like React Navigation) but Stack works like vanilla web routes unless specified. I'm thinking otherwise NavigationReact would be a better fit and I build abstractions myself.

grahammendick commented 7 months ago

Yea, I did that myself. Here's a recording of it I just took.

https://github.com/grahammendick/navigation/assets/1761227/df477ad1-14b2-4e60-8b86-244f1e31df4c

Navigation React Native Web is native running on web. So the stack works the same as on native, with visited scenes held in memory and browser history takes the user back through the stack. That probably isn't what you want on web desktop. You can turn off the stack on web by setting trackCrumbTrail to false. That might be right for you. But, yea, you can work with Navigation React directly instead if you prefer.

I'm always happy to help but might need to know a bit more about what you're after before I can be more specific.

aleedmanath commented 7 months ago

Weird, I'm running NavigationReactNativeWeb/sample/twitter and it's not working for me.

You can even see the DOM stack issue I'm talking about:

Screenshot 2024-02-13 at 10 03 46 AM
<NavigationHandler stateNavigator={stateNavigator}>
    <h1 style={{ backgroundColor: 'green' }}>Hello world</h1>
    <NavigationStack>[..]</NavigationStack>
  </NavigationHandler>

Thanks for help. Right now I'm just trying to get basics working and setting up best defaults. I'll give trackCrumbTrail a try; otherwise I'll switch to custom unified solution.

aleedmanath commented 7 months ago

Ah I figured it out. You're changing renderTransition prop too. (More obvious with this example, lol)

grahammendick commented 7 months ago

You need to paste the code I wrote into App.js. But here's another way just to show that the rendering is in your hands

<NavigationHandler stateNavigator={stateNavigator}>
  <div style={{display: 'flex', flexDirection: 'column', height: '100vh'}}>
    <h1 style={{color: 'white'}}>Hello World</h1>
    <div style={{position: 'relative', flex: 1}}>
      <NavigationStack>
        <Scene stateKey="tabs"><Tabs /></Scene>
        <Scene stateKey="notifications"><Notifications /></Scene>
        <Scene stateKey="tweet"><Tweet /></Scene>
        <Scene stateKey="timeline"><Timeline /></Scene>
      </NavigationStack>
    </div>
  </div>
</NavigationHandler>
aleedmanath commented 7 months ago

Yes, I notice now you were changing styling of View inside render position. Thanks I'll close issue.