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

feat: useFocusable #79

Closed GeraldHost closed 2 years ago

GeraldHost commented 3 years ago

Note: this is not quite ready to merge but wanted to open this pull request as a start to figure out how this can be accomplished. We use this library at work and it would be cool to have a nice hooks implementation.

useFocusable

An initial implementation of useFocusable a hooks based version of the withFocusable HOC. This is current working using a ref callback to get the DOM node. It also requires the user to provide the parentFocusKey manually.

First Note

Because this is using a ref callback instead of findDomNode when used with react native components such as View the ref isn't going to point at a DOM node even if you are running it in the browser. I'm not familiar with how this library works with React native as it doesn't seem you can ever find the position of a native element when in nativeMode?

Second Note

It would be nice to come up with a solution to not have to pass in parentKey manually. This comes with some challenges when using hooks. I made a suggestion in this issue thread that would be worth putting a bit of brain power to to figure out if there is a solution.

asgvard commented 3 years ago

Hi! Thanks for your contribution, I will have some dedicated time to work on this very soon, so I will review it and also will try to figure out a good solution for the parent/child context issue. We are doing some updates internally and also have a need now to migrate to hooks, so this will speed up the process for sure :)

GeraldHost commented 3 years ago

@asgvard Awesome no worries dude. I have update this implementation so it can now track the parent focus key without haven't to explicitly pass it to every child. It does so by taking a isParent prop. There may be an existing prop like trackChildren we could use to infer if something is a parent but I'm not familiar enough with this library to suggest what.

Here is a very quick example of it in use:

const MenuItem = ({ focusKey }) => {
  const { register, focused } = useFocusable({ focusKey });

  const styles = focused
    ? { ...styles.menuItem, ...styles.focusedBorder }
    : { ...styles.menuItem };

  return <button ref={register} style={styles} />;
};

const Menu = ({ focusKey }) => {
  const { register, setFocus } = useFocusable({
    focusKey,
    isParent: true,
  });

  useEffect(() => {
    setFocus();
  }, []);

  return (
    <div ref={register} style={styles.menu}>
      <MenuItem focusKey={"MENU-1"} />
      <MenuItem focusKey={"MENU-2"} />
      <MenuItem focusKey={"MENU-3"} />
      <MenuItem focusKey={"MENU-4"} />
      <MenuItem focusKey={"MENU-5"} />
      <MenuItem focusKey={"MENU-6"} />
    </div>
  );
};
GeraldHost commented 3 years ago

This works but just realised that it only works if the parent item is the only sibling. So this needs more work/brain power. It could be that there really isn't any better way to do this with hooks other than passing a parentFocusKey.

asgvard commented 2 years ago

Here is the new version of this library that is migrated to hooks: https://github.com/NoriginMedia/Norigin-Spatial-Navigation