NoriginMedia / react-spatial-navigation

DEPRECATED. HOC-based Spatial Navigation. NEW Hooks version is available here: https://github.com/NoriginMedia/norigin-spatial-navigation
MIT License
226 stars 64 forks source link

How to restrict sets of focusables based on screen/route? #23

Closed shirakaba closed 4 years ago

shirakaba commented 5 years ago

Is your feature request related to a problem? Please describe. I'm using react-navigation together with React Native and React Native Web. It's working nicely, but poses some challenges for usage with react-spatial-navigation.

In my app, I have a MainMenu screen with a selection of items to pick from. Upon picking an item, a modal will open, showing the VideoModal screen, which presents both a video and a set of related items (that are focusable).

I achieve this flow using a StackNavigator, which renders one screen at a time and provides transitions between screens. When a new screen is opened it is placed on top of the stack.

The problem is that StackNavigator does not unmount the previous screen when presenting the modal; it merely hides it. Thus, the focusables are all still mounted, and so if the user presses up/down/left/right, the focus may leave the bounds of the screen and select these hidden focusables. Instead, it should ignore those hidden modals belonging to a different route.

Describe the solution you'd like I'd like an enabled prop (defaults to true) to include/exclude any focusable from the tree of 'available' focusables. I could toggled this to true/false based on whether the given navigation route is active (visible) or not.

Describe alternatives you've considered In practice, I write my route names into the focusKey (e.g. MainMenu/Carousel and VideoModal/Carousel) as if to compose a directory hierarchy. Routes could be excluded based on string-matching with the focusKey, but this requires users of the library to be diligent with how they name their focus keys and may harm component re-use.

asgvard commented 5 years ago

Hi,

We've been thinking about implementing something like this. In practice, when we show modals, we make "fullscreen" mask, usually even with the transparent background, so its coordinates are always fullscreen and it never goes outside of its boundaries. But if this to be a more controlled feature, I would suggest to implement it in the way where you can set a prop named something like focusBoundary, which restricts the navigation within the focusable component when it is doing smartNavigate in the SpatialNavigation.js. There is a place where it tries to find siblings, then if it doesn't find them, it delegates the directional function to its parent. So this is the moment where you can check for this prop, and if it has focusBoundary=true, do not propagate up to the parent.

shirakaba commented 5 years ago

I unfortunately missed the notification of this comment so I went ahead and implemented enabled, which solves my immediate use-case.

I believe that there is probably scope for both an enabled prop (which is particularly well suited for disabling a submission button while a request is in progress) and a focusBoundary prop (perhaps admittedly more suited to my issue of restricting focus to within the bounds of a modal).

I was able to apply enabled for my use-case by toggling enabled={false} for every component on my screen whenever a modal is presented. Setting a focusBoundary prop on a single focusable sounds far more attractive, but then again, you say it's based on direction, and my components are actually on-screen (just hidden, e.g. with opacity: 0), so there may be some difficulties there.

asgvard commented 4 years ago

I believe this PR also addresses this use-case: https://github.com/NoriginMedia/react-spatial-navigation/pull/72 So perhaps we can close this :)