react-navigation / rfcs

RFCs for changes to React Navigation
Other
88 stars 16 forks source link

Improve ergonomics of overlays / popovers / modals #15

Open brentvatne opened 6 years ago

brentvatne commented 6 years ago
ericvicenti commented 6 years ago

There are so many types of modals, when you think about it! I think most NUX, popovers, and dropdowns should also be considered modals, because they respond to the Android back button and escape key, they cover up other content on the screen, and are dismiss-able by tapping away.

dantman commented 6 years ago

The exact UI of a modal, whatever kind it is, doesn't have to be defined by React Navigation. I think we can break down pretty much any type of modal into a common set of route features the modal needs in order to work as a React Navigation route that will allow the screen component to take over and do whatever it wants.

The ones I can think of so far:

A menu:

A dialog:

A full-screen dialog:

We could even have responsive dialogs that switch settings based on the screen dimensions. i.e. on mobile the navigationOptions would return the settings used for a dialog and on a tablet would return the settings for a full-screen dialog.

ericvicenti commented 6 years ago

I'm loving the exploration! One idea for simplification: ignore the header in this proposal entirely, and focus on full-screen behavior. We can always nest a StackNavigator "card mode" navigator inside the modal, which is the current, explicit way of specifying this. I expect that many users of the library already have a modal-mode stack on the outside, and this "ModalNavigator" could be a replacement for that.

In the distant future, I think we could use some shared element magic within "ModalNavigator" to re-implement StackNavigator-card and all of the fancy transitions. This would effectively flatten the navigation state to a single stack, which is what a lot of people are kind of clamoring for, but I think it will be a while before we can do this. The complication with header configuration is the main reason stack flattening isn't possible on todays code.

If it seems too boilerplatey to have people wrap their existing card-mode StackNavigator in ModalNavigator, we could figure out how to automagically wrap the ModalNavigator if there isn't one at the top level, such that modal features are always available.

We could even have responsive dialogs that switch settings based on the screen dimensions. i.e. on mobile the navigationOptions would return the settings used for a dialog and on a tablet would return the settings for a full-screen dialog.

This is an awesome idea, and I think we should make sure its entirely possible to do things like this in user-space. We won't have all the answers here, but we can implement some awesome defaults, like the tablet dialog vs full-screen-modal use case you mentioned.

ericvicenti commented 6 years ago

So I'm picturing something roughly like this:

const App = ModalNavigator({
  Main: StackNavigator({
    ...
  }),
  RateAppDialog,
  SupportModal
});

The idea is, RateAppDialog is a component which takes special props and manages its own transition, gesture, and view masking. Including adding the back action when the mask is tapped.

SupportModal would also manage its own animation and gesture, but because it is covers the full screen, it would somehow report to the ModalNavigator when the transition completes, so that ModalNavigator knows when it can unmount previous screens.

Keep in mind, we couldn't easily use this API for StackNavigator card-mode! Whys that? Because the translate of the previous screen needs to somehow be aware of the gesture happening on the next screen, (damn iOS transitions!). Not to mention header composition. So yeah, lets please leave StackNavigator card-mode and header out of this proposal! 🙏

If you're liking this stuff @brentvatne, I can get started on a real RFC here. Between this modal cleanup project and @grabbou's card stack hacks, I think we'll be on a solid track for flexible animations!

ghost commented 6 years ago

can react navigation can support different animations for stack children?

brentvatne commented 6 years ago

@Tomatoo - wrong issue: https://github.com/react-navigation/rfcs/issues/10#issuecomment-374363873

benadamstyles commented 6 years ago

Sorry to butt in here but I just wanted to ask – are you planning to use the native modal in this new modal navigator? If so, I'd like to mention now that the whole reason I had to refactor my app to use a react-navigation modal stack was to avoid issues that I have been having with the native modal on the native side, so hopefully you can keep the new modal navigator as a JS-only solution 🙂 if that was already the case please ignore this and sorry for the unnecessary notification.

brentvatne commented 6 years ago

no plans to use the native modal component

satya164 commented 6 years ago

how about an API similar to React DOM Portals?

// somewhere inside your render method
<View>
  {this.props.navigation.createModal(
    // whatever it's rendered here will be rendered on top of current screen
    <Text>this is modal content</Text>
  )}
</View>

we could also export a <Modal /> component which gets the navigation prop via context and uses this API internally.

in react-native-paper we stopped using our Portal component by default, so if react-navigation had this API, users will be able to use it easily.

brentvatne commented 5 years ago

@satya164 - I'd be interested in seeing a RFC for that approach!

satya164 commented 5 years ago

will do

lxcid commented 5 years ago

I want to add that @dantman break down at https://github.com/react-navigation/rfcs/issues/15#issuecomment-365412922 is much better for me:

I'm more familiar with iOS and a bit of Web so I'm gonna try to make the case for @dantman argument base on what I am familiar with… If anyone is familiar with android, feel free to make correction. :)

Based on WAI-ARIA Authoring Practices 1.1: Dialog Modal, Dialog Modal is described as:

A dialog is a window overlaid on either the primary window or another dialog window. Windows under a modal dialog are inert. That is, users cannot interact with content outside an active dialog window. Inert content outside an active dialog is typically visually obscured or dimmed so it is difficult to discern, and in some implementations, attempts to interact with the inert content cause the dialog to close.

There's a few interesting terms to define here:

In iOS windowing system, there's UIWindow which also have the concept of primary window called key window. You can become or resign from being primary window and they stack up, although in iOS u can jump the queue by becoming key window again. This is how UIAlertController are presented

So, based on @ericvicenti examples at https://github.com/react-navigation/rfcs/issues/15#issuecomment-365423059, I would make the follow changes:

// I think window is more appropriate naming, we are basically tracking primary window
const createWindowNavigator = createStackNavigator;
const App = createWindowNavigator(
  {
    MainWindow: createStackNavigator({
      ...mainScreens,
    }),
    RateAppModalDialog: {
      screen: RateAppModalDialog,
      fullscreen: false, // hint to the navigator to keep previous primary window in stack
    },
    SupportModalDialog, // default to fullscreen: true
  },
  {
    headerMode: 'none',
    initialRouteName: 'MainWindow', // The initial primary window is Main
    mode: 'modal',
  },
);

So I would say non-modal dialog is out of react navigation scope.

A modal dialog will be a window dialog and a non modal dialog will be a non window (a.k.a view) dialog. Tooltip can be considered a non modal dialog and doesn't seems like a responsibility for React Navigation. For me, I would categorize @satya164 solution https://github.com/react-navigation/rfcs/issues/15#issuecomment-417511484 as non-modal dialog as it share input events with the current window, although if it implement React portal like behaviour, it potentially could mimic a modal dialog, but to keep things simple, I think we assume its non-modal dialog.

(Also, we could potentially use React portal for createWindowNavigator() if it make sense)

What could be a modal dialog?

In iOS, iPad/iPhone usually present modal as non full screen modal dialog in portrait orientation and full screen modal dialog in landscape orientation. So we just need to be able to dynamically control the configuration at orientation change?

brentvatne commented 5 years ago

from: https://github.com/react-navigation/react-navigation/issues/5808


react-native 3.5.1 @react-navigation/core 3.2.0 @react-navigation/web 1.0.0-alpha.7

My use case is: I'm coming from react-native and now I have to implement a react-native-web implementation based on it. My "mobile app" is implemented on part of the screen. The screen also contains other components. The modal implementation from react-navigation is far too limited to be able to use comfortably on a wider screen scenario. Here are some of the things I ran into.

Initially, I implemented https://github.com/Dekoruma/react-native-web-modal/tree/master/packages/modal-enhanced-react-native-web It offers an experience that allows you to implement a modal that displays e.g. on a large screen as a centered box with a transparent background. It dropped the ball in one area though, it also applies the centered box with transparent background approach on a mobile screen, where you want pure full screen. But basically, it works.

So, I started looking at react-navigation. Also because I assumed that it would be better integrated with things like back button press, swipes, etc and I like to keep navigation logic centralized. Also, I like to use the default header with a back button on full screen mobile, but something more like a default modal header without back button on a desktop screen.

First pass

Second pass

Third pass

So, at this point, I have to admit defeat. react-navigation modals are not yet ready for cross platform use. As a short term fix, I would recommend implementing contentComponent. This will allow use to create a fixed position wrapper and a centered flexbox which right away offers the basics.

Requirements for me:

vamshi9666 commented 2 years ago

How about a prop isModal to screen.

Code will look like

 <UserStack.Navigator>
     <UserStack.Screen name="list" /> //normal screen
     <UserStack.Screen name="addUser" isModal={true}  />. //modal screen
 </UserStack.Navigator>