-
Notifications
You must be signed in to change notification settings - Fork 44
Expand file tree
/
Copy pathFocusableView.tsx
More file actions
109 lines (100 loc) · 3.14 KB
/
FocusableView.tsx
File metadata and controls
109 lines (100 loc) · 3.14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
import {
FocusableNodeState,
SpatialNavigationNode,
SpatialNavigationNodeDefaultProps,
} from './Node';
import { Platform, View, ViewStyle, ViewProps } from 'react-native';
import { forwardRef, useImperativeHandle, useMemo, useRef } from 'react';
import { SpatialNavigationNodeRef } from '../types/SpatialNavigationNodeRef';
import { SpatialNavigationFocusableViewRef } from '../types/SpatialNavigationFocusableViewRef';
import { useSpatialNavigationDeviceType } from '../context/DeviceContext';
import { useSpatialNavigatorFocusableAccessibilityProps } from '../hooks/useSpatialNavigatorFocusableAccessibilityProps';
type FocusableViewProps = {
style?: ViewStyle;
children: React.ReactElement | ((props: FocusableNodeState) => React.ReactElement);
viewProps?: ViewProps & {
onMouseEnter?: () => void;
};
};
type Props = SpatialNavigationNodeDefaultProps & FocusableViewProps;
export const SpatialNavigationFocusableView = forwardRef<SpatialNavigationFocusableViewRef, Props>(
({ children, style, viewProps, ...props }, ref) => {
const { deviceTypeRef } = useSpatialNavigationDeviceType();
const nodeRef = useRef<SpatialNavigationNodeRef>(null);
const viewRef = useRef<View>(null);
useImperativeHandle(
ref,
() => ({
focus: () => nodeRef.current?.focus(),
viewRef,
}),
[nodeRef],
);
const webProps = Platform.select({
web: {
onMouseEnter: () => {
if (viewProps?.onMouseEnter) {
viewProps?.onMouseEnter();
}
if (deviceTypeRef.current === 'remotePointer') {
nodeRef.current?.focus();
}
},
onClick: () => {
props.onSelect?.();
},
},
default: {},
});
return (
<SpatialNavigationNode isFocusable {...props} ref={nodeRef}>
{(nodeState) => (
<InnerFocusableView
ref={viewRef}
viewProps={viewProps}
webProps={webProps}
style={style}
nodeState={nodeState}
>
{children}
</InnerFocusableView>
)}
</SpatialNavigationNode>
);
},
);
SpatialNavigationFocusableView.displayName = 'SpatialNavigationFocusableView';
type InnerFocusableViewProps = FocusableViewProps & {
webProps:
| {
onMouseEnter: () => void;
onClick: () => void;
}
| {
onMouseEnter?: undefined;
onClick?: undefined;
};
nodeState: FocusableNodeState;
};
const InnerFocusableView = forwardRef<View, InnerFocusableViewProps>(
({ viewProps, webProps, children, nodeState, style }, ref) => {
const accessibilityProps = useSpatialNavigatorFocusableAccessibilityProps();
const accessibilityState = useMemo(
() => ({ selected: nodeState.isFocused }),
[nodeState.isFocused],
);
return (
<View
ref={ref}
style={style}
accessibilityState={accessibilityState}
{...accessibilityProps}
{...viewProps}
{...webProps}
>
{typeof children === 'function' ? children(nodeState) : children}
</View>
);
},
);
InnerFocusableView.displayName = 'InnerFocusableView';