Skip to content

Commit

Permalink
feat: nodes can take an additional scrolloffset
Browse files Browse the repository at this point in the history
It alllows to dynamically change the scrollview offsetFromStart depending on each focused item
  • Loading branch information
JulienIzz committed Apr 9, 2024
1 parent db1defd commit b87654b
Show file tree
Hide file tree
Showing 5 changed files with 19 additions and 8 deletions.
1 change: 1 addition & 0 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ The `SpatialNavigationNode` component receives the following props:
| `alignInGrid` | `boolean` | `false` | Determines whether child lists should behave like a grid. |
| `indexRange` | `number[]` | `undefined` | Determines the indexes when using long nodes in a grid. If a grid row has one `indexRange`, you should specify each element's `indexRange`. You can check for more details in [`GridWithLongNodesPage`](https://github.com/bamlab/react-tv-space-navigation/blob/31bfe1def4a7e18e9e41f26a520090d1b7a5b149/packages/example/src/pages/GridWithLongNodesPage.tsx) example or in [lrud documentation](https://github.com/bbc/lrud/blob/master/docs/usage.md#indexrange). |
| `children` | `({ isFocused, isActive }: { isFocused: boolean, isActive: boolean }) => ReactNode` or `ReactNode` | `null` | Child elements of the component. It can be a function that returns a React element and accepts a parameter with a `isFocused` property when `isFocusable` is `true`. If `isFocusable` is `false` or not provided, it can be any valid React node. |
| `additionalOffset` | `number` | `0` | An additional offset used when the node is a children of a scrollview. If specified, this offset will be added to the `offsetFromStart` of the ScrollView. It allows a fine grained scroll for each scrollview item. |

The `SpatialNavigationNode` component ref expose the following methods:

Expand Down
14 changes: 12 additions & 2 deletions packages/lib/src/spatial-navigation/components/Node.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,25 @@ type DefaultProps = {
* @see LRUD docs */
alignInGrid?: boolean;
indexRange?: NodeIndexRange;
/**
* This is an additional offset useful only for the scrollview. It adds up to the offsetFromStart of the scrollview.
*/
additionalOffset?: number;
};
type Props = DefaultProps & (FocusableProps | NonFocusableProps);

export type SpatialNavigationNodeDefaultProps = DefaultProps;

const useScrollToNodeIfNeeded = ({
childRef,
additionalOffset,
}: {
childRef: React.MutableRefObject<View | null>;
additionalOffset?: number;
}) => {
const { scrollToNodeIfNeeded } = useSpatialNavigatorParentScroll();

return () => scrollToNodeIfNeeded(childRef);
return () => scrollToNodeIfNeeded(childRef, additionalOffset);
};

const useBindRefToChild = () => {
Expand Down Expand Up @@ -79,6 +85,7 @@ export const SpatialNavigationNode = forwardRef<SpatialNavigationNodeRef, Props>
alignInGrid = false,
indexRange,
children,
additionalOffset = 0,
}: Props,
ref,
) => {
Expand All @@ -99,7 +106,10 @@ export const SpatialNavigationNode = forwardRef<SpatialNavigationNodeRef, Props>

const { childRef, bindRefToChild } = useBindRefToChild();

const scrollToNodeIfNeeded = useScrollToNodeIfNeeded({ childRef });
const scrollToNodeIfNeeded = useScrollToNodeIfNeeded({
childRef,
additionalOffset,
});

/*
* We don't re-register in LRUD on each render, because LRUD does not allow updating the nodes.
Expand Down
6 changes: 3 additions & 3 deletions packages/lib/src/spatial-navigation/components/ScrollView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ export const SpatialNavigationScrollView = ({
useRemotePointerScrollviewScrollProps({ pointerScrollSpeed, scrollY, scrollViewRef });

const scrollToNode = useCallback(
(newlyFocusedElementRef: RefObject<View>) => {
(newlyFocusedElementRef: RefObject<View>, additionalOffset = 0) => {
try {
if (deviceTypeRef.current === 'remoteKeys') {
newlyFocusedElementRef?.current?.measureLayout(
Expand All @@ -153,7 +153,7 @@ export const SpatialNavigationScrollView = ({
newlyFocusedElementDistanceToLeftRelativeToLayout: left,
newlyFocusedElementDistanceToTopRelativeToLayout: top,
horizontal,
offsetFromStart,
offsetFromStart: offsetFromStart + additionalOffset,
scrollViewRef,
}),
() => {},
Expand All @@ -162,7 +162,7 @@ export const SpatialNavigationScrollView = ({
} catch {
// A crash can happen when calling measureLayout when a page unmounts. No impact on focus detected in regular use cases.
}
makeParentsScrollToNodeIfNeeded(newlyFocusedElementRef); // We need to propagate the scroll event for parents if we have nested ScrollViews/VirtualizedLists.
makeParentsScrollToNodeIfNeeded(newlyFocusedElementRef, additionalOffset); // We need to propagate the scroll event for parents if we have nested ScrollViews/VirtualizedLists.
},
[makeParentsScrollToNodeIfNeeded, horizontal, offsetFromStart, deviceTypeRef],
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@ const ItemWrapperWithScrollContext = typedMemo(
useSpatialNavigatorParentScroll();

const scrollToItem: ScrollToNodeCallback = useCallback(
(newlyFocusedElementRef) => {
(newlyFocusedElementRef, additionalOffset) => {
setCurrentlyFocusedItemIndex(item.index);
// We need to propagate the scroll event for parents if we have nested ScrollViews/VirtualizedLists.
makeParentsScrollToNodeIfNeeded(newlyFocusedElementRef);
makeParentsScrollToNodeIfNeeded(newlyFocusedElementRef, additionalOffset);
},
[makeParentsScrollToNodeIfNeeded, setCurrentlyFocusedItemIndex, item.index],
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { createContext, RefObject, useContext } from 'react';
import { View } from 'react-native';

export type ScrollToNodeCallback = (ref: RefObject<View>) => void;
export type ScrollToNodeCallback = (ref: RefObject<View>, additionalOffset?: number) => void;
export const SpatialNavigatorParentScrollContext = createContext<ScrollToNodeCallback>(() => {});

export const useSpatialNavigatorParentScroll = (): {
Expand Down

0 comments on commit b87654b

Please sign in to comment.