-
-
Notifications
You must be signed in to change notification settings - Fork 643
Expand file tree
/
Copy pathSplitHost.tsx
More file actions
117 lines (105 loc) · 4.1 KB
/
SplitHost.tsx
File metadata and controls
117 lines (105 loc) · 4.1 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
110
111
112
113
114
115
116
117
import React from 'react';
import { StyleSheet } from 'react-native';
import SplitHostNativeComponent, {
Commands as SplitHostNativeCommands,
} from '../../../fabric/gamma/split/SplitHostNativeComponent';
import type {
SplitDisplayMode,
SplitHostProps,
SplitBehavior,
} from './SplitHost.types';
import SplitScreen from './SplitScreen';
type NativeRef = React.ComponentRef<typeof SplitHostNativeComponent>;
// According to the UIKit documentation: https://developer.apple.com/documentation/uikit/uisplitviewcontroller/displaymode-swift.enum
// Only specific pairs for displayMode - splitBehavior are valid and others may lead to unexpected results.
// Therefore, we're adding check on the JS side to return a feedback to the client when that pairing isn't valid.
// However, we're not blocking these props to be set on the native side, because it doesn't crash, just the result or transitions may not work as expected.
const displayModeForSplitCompatibilityMap: Record<
SplitBehavior,
SplitDisplayMode[]
> = {
tile: ['secondaryOnly', 'oneBesideSecondary', 'twoBesideSecondary'],
overlay: ['secondaryOnly', 'oneOverSecondary', 'twoOverSecondary'],
displace: ['secondaryOnly', 'oneBesideSecondary', 'twoDisplaceSecondary'],
automatic: [], // placeholder for satisfying types; we'll handle it specially in logic
};
const isValidDisplayModeForSplitBehavior = (
displayMode: SplitDisplayMode,
splitBehavior: SplitBehavior,
) => {
if (splitBehavior === 'automatic') {
// for automatic we cannot easily verify the compatibility, because it depends on the system preference for display mode, therefore we're assuming that 'automatic' has only valid combinations
return true;
}
return displayModeForSplitCompatibilityMap[splitBehavior].includes(
displayMode,
);
};
/**
* EXPERIMENTAL API, MIGHT CHANGE W/O ANY NOTICE
*/
function SplitHost({ ref, ...props }: SplitHostProps) {
const { direction, preferredDisplayMode, preferredSplitBehavior } = props;
const nativeRef = React.useRef<NativeRef>(null);
React.useImperativeHandle(
ref,
() => ({
show: column => {
if (nativeRef.current) {
SplitHostNativeCommands.showColumn(nativeRef.current, column);
} else {
console.warn(
'[RNScreens] Reference to native SplitHost component has not been updated yet',
);
}
},
}),
[],
);
React.useEffect(() => {
if (preferredDisplayMode && preferredSplitBehavior) {
const isValid = isValidDisplayModeForSplitBehavior(
preferredDisplayMode,
preferredSplitBehavior,
);
if (!isValid) {
const validDisplayModes =
displayModeForSplitCompatibilityMap[preferredSplitBehavior];
console.warn(
`Invalid display mode "${preferredDisplayMode}" for split behavior "${preferredSplitBehavior}".` +
`\nValid modes for "${preferredSplitBehavior}" are: ${validDisplayModes.join(
', ',
)}.`,
);
}
}
}, [preferredDisplayMode, preferredSplitBehavior]);
const children = React.Children.toArray(props.children);
const columns = children.filter(
// @ts-ignore - type is valid attribute for child
child => child.type === SplitScreen.Column,
);
const inspectors = children.filter(
// @ts-ignore - type is valid attribute for child
child => child.type === SplitScreen.Inspector,
);
return (
<SplitHostNativeComponent
ref={nativeRef}
// UISplitViewController requires the number of columns to be specified at initialization and it cannot be changed dynamically later.
// By using a specific key in this form, we can detect changes in the number of React children.
// This enables us to fully recreate the Split when necessary, ensuring the correct column configuration is always applied.
key={`columns-${columns.length}-inspectors-${inspectors.length}`}
{...props}
layoutDirection={direction}
style={styles.container}>
{props.children}
</SplitHostNativeComponent>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
});
export default SplitHost;