Skip to content

Commit

Permalink
Merge pull request #65 from bamlab/add-isActive
Browse files Browse the repository at this point in the history
Add isActive to SpatialNavigationNode
  • Loading branch information
remilry authored Feb 15, 2024
2 parents 6b759c7 + aef892f commit 3932e7c
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 23 deletions.
24 changes: 18 additions & 6 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,9 @@ The `SpatialNavigationNode` component receives the following props:
| `onSelect` | `function` | `undefined` | Callback function to be called when the node is selected. |
| `orientation` | `'vertical' \| 'horizontal` | `'vertical'` | Determines the orientation of the node. |
| `isFocusable` | `boolean` | `false` | Determines if the node is focusable or not. If it's `true`, the `children` prop must be a function that returns a React element and accepts a parameter with a `isFocused` property. If it's `false` or not provided, `children` can be any valid React node. |

| `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` | `function` 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. |
| `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. |

## Usage

Expand All @@ -71,6 +68,21 @@ The `SpatialNavigationNode` component receives the following props:
</SpatialNavigationNode>
```

```jsx
<SpatialNavigationNode
orientation="horizontal"
isFocusable={false}
>
{({ isActive }) => (
// This node is active if one of its nodes is focused
<View style={{ backgroundColor: isActive ? 'grey' : 'black' }}>
<FocusableNodes/>
</View>)}
</SpatialNavigationNode>
```

Note : It is not recommend to use isActive on virtualized focusable nodes, as this can lead to unexpected behaviour.

## Important note

The SpatialNavigationNode will use the ref of your component to handle the scrolling.
Expand Down
1 change: 1 addition & 0 deletions packages/example/src/design-system/theme/colors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export const colors = {
background: {
main: '#111111',
mainHover: '#1a1a1a',
mainActive: '#2a2a2a',
light: '#EEF0F5',
lightHover: '#D5D7DC',
contrastText: '#FFFFFF',
Expand Down
34 changes: 20 additions & 14 deletions packages/example/src/modules/program/view/ProgramList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useTheme } from '@emotion/react';
import { useNavigation } from '@react-navigation/native';
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
import { useCallback, useMemo } from 'react';
import { SpatialNavigationVirtualizedList } from 'react-tv-space-navigation';
import { SpatialNavigationNode, SpatialNavigationVirtualizedList } from 'react-tv-space-navigation';
import { RootStackParamList } from '../../../../App';
import { ProgramInfo } from '../domain/programInfo';
import { getPrograms } from '../infra/programInfos';
Expand Down Expand Up @@ -37,17 +37,21 @@ export const ProgramList = ({
const programInfos = useMemo(() => getPrograms(1000), []);

return (
<Container style={containerStyle}>
<SpatialNavigationVirtualizedList
orientation={orientation}
data={programInfos}
renderItem={renderItem}
itemSize={theme.sizes.program.portrait.width + 30}
numberOfRenderedItems={WINDOW_SIZE}
numberOfItemsVisibleOnScreen={NUMBER_OF_ITEMS_VISIBLE_ON_SCREEN}
onEndReachedThresholdItemsNumber={NUMBER_OF_ITEMS_VISIBLE_ON_SCREEN}
/>
</Container>
<SpatialNavigationNode>
{({ isActive }) => (
<Container isActive={isActive} style={containerStyle}>
<SpatialNavigationVirtualizedList
orientation={orientation}
data={programInfos}
renderItem={renderItem}
itemSize={theme.sizes.program.portrait.width + 30}
numberOfRenderedItems={WINDOW_SIZE}
numberOfItemsVisibleOnScreen={NUMBER_OF_ITEMS_VISIBLE_ON_SCREEN}
onEndReachedThresholdItemsNumber={NUMBER_OF_ITEMS_VISIBLE_ON_SCREEN}
/>
</Container>
)}
</SpatialNavigationNode>
);
};

Expand All @@ -63,8 +67,10 @@ export const ProgramsRow = ({ containerStyle }: { containerStyle?: object }) =>
);
};

const Container = styled.View(({ theme }) => ({
backgroundColor: theme.colors.background.mainHover,
const Container = styled.View<{ isActive: boolean }>(({ isActive, theme }) => ({
backgroundColor: isActive
? theme.colors.background.mainActive
: theme.colors.background.mainHover,
padding: theme.spacings.$8,
borderRadius: scaledPixels(20),
overflow: 'hidden',
Expand Down
11 changes: 8 additions & 3 deletions packages/lib/src/spatial-navigation/components/Node.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ import { NodeIndexRange } from '@bam.tech/lrud';

type FocusableProps = {
isFocusable: true;
children: (props: { isFocused: boolean }) => React.ReactElement;
children: (props: { isFocused: boolean; isActive: boolean }) => React.ReactElement;
};
type NonFocusableProps = {
isFocusable?: false;
children: React.ReactElement;
children: React.ReactElement | ((props: { isActive: boolean }) => React.ReactElement);
};
type DefaultProps = {
onFocus?: () => void;
Expand Down Expand Up @@ -79,6 +79,7 @@ export const SpatialNavigationNode = ({
const spatialNavigator = useSpatialNavigator();
const parentId = useParentId();
const [isFocused, setIsFocused] = useState(false);
const [isActive, setIsActive] = useState(false);
// If parent changes, we have to re-register the Node + all children -> adding the parentId to the nodeId makes the children re-register.
const id = useUniqueId({ prefix: `${parentId}_node_` });

Expand Down Expand Up @@ -122,6 +123,8 @@ export const SpatialNavigationNode = ({
orientation,
isIndexAlign: alignInGrid,
indexRange,
onActive: () => setIsActive(true),
onInactive: () => setIsActive(false),
});

return () => spatialNavigator.unregisterNode(id);
Expand All @@ -135,7 +138,9 @@ export const SpatialNavigationNode = ({

return (
<ParentIdContext.Provider value={id}>
{typeof children === 'function' ? bindRefToChild(children({ isFocused })) : children}
{typeof children === 'function'
? bindRefToChild(children({ isFocused, isActive }))
: children}
</ParentIdContext.Provider>
);
};

0 comments on commit 3932e7c

Please sign in to comment.