@applicaster/zapp-react-native-ui-components 13.0.0-alpha.8532113155 → 13.0.0-alpha.9103242840
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Components/BaseFocusable/index.ios.ts +1 -1
- package/Components/BaseFocusable/index.tsx +1 -1
- package/Components/Cell/Cell.tsx +4 -7
- package/Components/Cell/CellWithFocusable.tsx +59 -43
- package/Components/Cell/TvOSCellComponent.tsx +20 -22
- package/Components/Cell/__tests__/CellWIthFocusable.test.js +6 -13
- package/Components/Cell/index.js +3 -1
- package/Components/Focusable/FocusableTvOS.tsx +5 -1
- package/Components/Focusable/Touchable.tsx +20 -19
- package/Components/FocusableList/index.tsx +26 -9
- package/Components/FocusableScrollView/index.tsx +39 -12
- package/Components/Layout/TV/NavBarContainer.tsx +8 -7
- package/Components/Layout/TV/__tests__/__snapshots__/NavBarContainer.test.tsx.snap +1 -1
- package/Components/Layout/TV/__tests__/__snapshots__/ScreenContainer.test.tsx.snap +1 -1
- package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +1 -4
- package/Components/MasterCell/DefaultComponents/Image/Image.ios.tsx +11 -5
- package/Components/MasterCell/DefaultComponents/SecondaryImage/utils.ts +1 -1
- package/Components/MasterCell/DefaultComponents/__tests__/image.test.js +1 -1
- package/Components/MasterCell/hooks/useAsyncRendering/MasterCellAsyncRenderManager.ts +2 -2
- package/Components/MasterCell/utils/index.ts +1 -1
- package/Components/ModalComponent/Header/index.tsx +3 -3
- package/Components/River/ComponentsMap/ComponentsMap.tsx +65 -39
- package/Components/River/ComponentsMap/hooks/useLoadingState.ts +51 -78
- package/Components/River/RiverFooter.tsx +9 -39
- package/Components/River/RiverItem.tsx +2 -37
- package/Components/River/TV/index.tsx +6 -1
- package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +31 -100
- package/Components/River/__tests__/componentsMap.test.js +5 -17
- package/Components/Screen/index.tsx +39 -13
- package/Components/Tabs/Tab.tsx +6 -6
- package/Components/TextInputTv/index.tsx +2 -2
- package/Components/Transitioner/AnimationManager.js +8 -8
- package/Components/Transitioner/Scene.tsx +23 -52
- package/Components/Transitioner/__tests__/__snapshots__/Scene.test.js.snap +43 -59
- package/Components/Transitioner/__tests__/__snapshots__/transitioner.test.js.snap +2 -2
- package/Components/Transitioner/index.js +4 -8
- package/Components/VideoLive/LiveImageManager.ts +1 -27
- package/Components/VideoLive/PlayerLiveImageComponent.tsx +21 -29
- package/Components/VideoLive/__tests__/PlayerLiveImageComponent.test.tsx +1 -51
- package/Components/VideoLive/__tests__/__snapshots__/PlayerLiveImageComponent.test.tsx.snap +5 -0
- package/Components/VideoModal/ModalAnimation/AnimationComponent.tsx +6 -7
- package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +3 -2
- package/Components/VideoModal/ModalAnimation/utils.ts +2 -2
- package/Components/VideoModal/PlayerWrapper.tsx +35 -16
- package/Components/VideoModal/VideoModal.tsx +23 -14
- package/Components/VideoModal/__tests__/PlayerWrapper.test.tsx +1 -1
- package/Components/VideoModal/__tests__/__snapshots__/PlayerWrapper.test.tsx.snap +90 -0
- package/Components/VideoModal/hooks/utils/index.ts +0 -33
- package/Components/VideoModal/utils.ts +1 -1
- package/Contexts/ComponentsMapOffsetContext/index.tsx +46 -0
- package/Contexts/ScreenContext/index.tsx +2 -3
- package/Decorators/ZappPipesDataConnector/__tests__/Hero.js +1 -1
- package/Decorators/ZappPipesDataConnector/index.tsx +1 -31
- package/package.json +5 -5
- package/Components/Cell/CellWithFocusable.ios.tsx +0 -126
- package/Components/MasterCell/DefaultComponents/BorderContainerView/__tests__/index.test.tsx +0 -66
- package/Components/MasterCell/DefaultComponents/ImageBorderContainer/__tests__/index.test.ts +0 -93
- package/Components/Screen/hooks.ts +0 -56
- package/Components/VideoModal/OpaqueLayer.tsx +0 -33
- package/Components/VideoModal/hooks/__tests__/useDelayedPlayerDetails.test.ts +0 -89
- package/Components/VideoModal/hooks/index.ts +0 -7
- package/Components/VideoModal/hooks/useDelayedPlayerDetails.ts +0 -49
- package/Components/VideoModal/hooks/utils/__tests__/showDetails.test.ts +0 -91
package/Components/Cell/Cell.tsx
CHANGED
|
@@ -61,6 +61,7 @@ type Props = {
|
|
|
61
61
|
skipFocusManagerRegistration?: boolean;
|
|
62
62
|
shouldUpdate: boolean;
|
|
63
63
|
behavior: Behavior;
|
|
64
|
+
componentsMapOffset: number;
|
|
64
65
|
};
|
|
65
66
|
|
|
66
67
|
type State = {
|
|
@@ -273,14 +274,10 @@ export class CellComponent extends React.Component<Props, State> {
|
|
|
273
274
|
if (isFocused) {
|
|
274
275
|
const accessibilityManager = AccessibilityManager.getInstance();
|
|
275
276
|
|
|
276
|
-
const accessibilityTitle =
|
|
277
|
-
item?.extensions?.accessibility?.label || item?.title || "";
|
|
278
|
-
|
|
279
|
-
const accessibilityHint =
|
|
280
|
-
item?.extensions?.accessibility?.hint || "";
|
|
281
|
-
|
|
282
277
|
accessibilityManager.readText({
|
|
283
|
-
text:
|
|
278
|
+
text: String(
|
|
279
|
+
item.extensions?.accessibility?.label || item.title
|
|
280
|
+
),
|
|
284
281
|
});
|
|
285
282
|
}
|
|
286
283
|
|
|
@@ -1,10 +1,48 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
|
|
3
|
+
import { focusManager } from "@applicaster/zapp-react-native-utils/appUtils/focusManager";
|
|
4
|
+
import * as FOCUS_EVENTS from "@applicaster/zapp-react-native-utils/appUtils/focusManager/events";
|
|
3
5
|
import { noop } from "@applicaster/zapp-react-native-utils/functionUtils";
|
|
4
6
|
import { toBooleanWithDefaultFalse } from "@applicaster/zapp-react-native-utils/booleanUtils";
|
|
5
7
|
|
|
8
|
+
import { isAppleTV } from "../../Helpers/Platform";
|
|
6
9
|
import { useCellState } from "../MasterCell/utils";
|
|
7
|
-
|
|
10
|
+
|
|
11
|
+
const useCellFocusedState = (
|
|
12
|
+
skipFocusManagerRegistration: boolean,
|
|
13
|
+
groupId: string,
|
|
14
|
+
id: string
|
|
15
|
+
) => {
|
|
16
|
+
const [currentCellFocused, setCurrentCellFocused] = React.useState(false);
|
|
17
|
+
|
|
18
|
+
React.useEffect(() => {
|
|
19
|
+
const isGroupItemFocused = () => {
|
|
20
|
+
if (!skipFocusManagerRegistration) {
|
|
21
|
+
const isFocused = focusManager.isGroupItemFocused(groupId, id);
|
|
22
|
+
setCurrentCellFocused(isFocused);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const handler = () => {
|
|
27
|
+
// tvOS hack for properly checking focus
|
|
28
|
+
if (isAppleTV()) {
|
|
29
|
+
setTimeout(() => {
|
|
30
|
+
isGroupItemFocused();
|
|
31
|
+
}, 0);
|
|
32
|
+
} else {
|
|
33
|
+
isGroupItemFocused();
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
focusManager.on(FOCUS_EVENTS.FOCUS, handler);
|
|
38
|
+
|
|
39
|
+
return () => {
|
|
40
|
+
focusManager.removeHandler(FOCUS_EVENTS.FOCUS, handler);
|
|
41
|
+
};
|
|
42
|
+
}, [groupId, skipFocusManagerRegistration]);
|
|
43
|
+
|
|
44
|
+
return currentCellFocused;
|
|
45
|
+
};
|
|
8
46
|
|
|
9
47
|
type Props = {
|
|
10
48
|
item: ZappEntry;
|
|
@@ -37,7 +75,11 @@ export function CellWithFocusable(props: Props) {
|
|
|
37
75
|
focused,
|
|
38
76
|
} = props;
|
|
39
77
|
|
|
40
|
-
const
|
|
78
|
+
const isFocused = useCellFocusedState(
|
|
79
|
+
skipFocusManagerRegistration,
|
|
80
|
+
groupId,
|
|
81
|
+
id
|
|
82
|
+
);
|
|
41
83
|
|
|
42
84
|
const state = useCellState({
|
|
43
85
|
id: item.id,
|
|
@@ -54,52 +96,26 @@ export function CellWithFocusable(props: Props) {
|
|
|
54
96
|
}
|
|
55
97
|
}, [focusedButtonId]);
|
|
56
98
|
|
|
57
|
-
const handleToggleFocus =
|
|
58
|
-
(value)
|
|
59
|
-
setFocusedButtonId(value.focusedButtonId);
|
|
60
|
-
|
|
61
|
-
if (value.focusable) {
|
|
62
|
-
onFocus(value.focusable, value.mouse);
|
|
63
|
-
}
|
|
64
|
-
},
|
|
65
|
-
[onFocus]
|
|
66
|
-
);
|
|
67
|
-
|
|
68
|
-
const onGroupFocus = React.useCallback(() => {
|
|
69
|
-
if (!skipFocusManagerRegistration) {
|
|
70
|
-
setIsFocused(true);
|
|
71
|
-
}
|
|
72
|
-
}, [skipFocusManagerRegistration]);
|
|
99
|
+
const handleToggleFocus = (value) => {
|
|
100
|
+
setFocusedButtonId(value.focusedButtonId);
|
|
73
101
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
setIsFocused(false);
|
|
102
|
+
if (value.focusable) {
|
|
103
|
+
onFocus(value.focusable, value.mouse);
|
|
77
104
|
}
|
|
78
|
-
}
|
|
105
|
+
};
|
|
79
106
|
|
|
80
107
|
return (
|
|
81
|
-
<
|
|
82
|
-
|
|
83
|
-
testID={"cell-with-focusable-cell-renderer-focusable-group"}
|
|
108
|
+
<CellRenderer
|
|
109
|
+
item={item}
|
|
84
110
|
groupId={groupId}
|
|
111
|
+
onToggleFocus={handleToggleFocus}
|
|
112
|
+
state={state}
|
|
113
|
+
prefixId={id}
|
|
114
|
+
focusedButtonId={focusedButtonId}
|
|
85
115
|
preferredFocus={preferredFocus}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
<CellRenderer
|
|
91
|
-
testID={"cell-with-focusable-cell-renderer"}
|
|
92
|
-
item={item}
|
|
93
|
-
groupId={`focusable-cell-wrapper-${id}`}
|
|
94
|
-
onToggleFocus={handleToggleFocus}
|
|
95
|
-
state={state}
|
|
96
|
-
prefixId={id}
|
|
97
|
-
focusedButtonId={focusedButtonId}
|
|
98
|
-
preferredFocus={true}
|
|
99
|
-
skipFocusManagerRegistration={skipFocusManagerRegistration}
|
|
100
|
-
isFocusable={isFocusable}
|
|
101
|
-
focused={focused}
|
|
102
|
-
/>
|
|
103
|
-
</FocusableGroup>
|
|
116
|
+
skipFocusManagerRegistration={skipFocusManagerRegistration}
|
|
117
|
+
isFocusable={isFocusable}
|
|
118
|
+
focused={focused}
|
|
119
|
+
/>
|
|
104
120
|
);
|
|
105
121
|
}
|
|
@@ -2,7 +2,7 @@ import * as React from "react";
|
|
|
2
2
|
import * as R from "ramda";
|
|
3
3
|
import { View, StyleSheet } from "react-native";
|
|
4
4
|
|
|
5
|
-
import { Focusable } from "@applicaster/zapp-react-native-ui-components/Components/Focusable";
|
|
5
|
+
import { Focusable } from "@applicaster/zapp-react-native-ui-components/Components/Focusable/FocusableTvOS";
|
|
6
6
|
import { FocusableCell } from "@applicaster/zapp-react-native-ui-components/Components/FocusableCell";
|
|
7
7
|
import { getItemType } from "@applicaster/zapp-react-native-utils/navigationUtils";
|
|
8
8
|
import { SCREEN_TYPES } from "@applicaster/zapp-react-native-utils/navigationUtils/itemTypes";
|
|
@@ -65,6 +65,7 @@ type Props = {
|
|
|
65
65
|
isFocusable: boolean;
|
|
66
66
|
shouldUpdate: boolean;
|
|
67
67
|
behavior: Behavior;
|
|
68
|
+
componentsMapOffset: number;
|
|
68
69
|
};
|
|
69
70
|
|
|
70
71
|
type State = {
|
|
@@ -179,6 +180,7 @@ export class TvOSCellComponent extends React.Component<Props, State> {
|
|
|
179
180
|
groupId,
|
|
180
181
|
component,
|
|
181
182
|
index,
|
|
183
|
+
componentsMapOffset,
|
|
182
184
|
} = this.props;
|
|
183
185
|
|
|
184
186
|
this.setScreenLayout(componentAnchorPointY, screenLayout);
|
|
@@ -193,13 +195,11 @@ export class TvOSCellComponent extends React.Component<Props, State> {
|
|
|
193
195
|
const extraAnchorPointYOffset =
|
|
194
196
|
screenLayout?.extraAnchorPointYOffset || 0;
|
|
195
197
|
|
|
196
|
-
const marginTop = screenLayout?.screenMarginTop || 0;
|
|
197
|
-
|
|
198
198
|
const totalOffset =
|
|
199
199
|
headerOffset +
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
200
|
+
(componentAnchorPointY || 0) +
|
|
201
|
+
extraAnchorPointYOffset -
|
|
202
|
+
componentsMapOffset || 0;
|
|
203
203
|
|
|
204
204
|
mainOffsetUpdater?.(
|
|
205
205
|
{ tag: this.target },
|
|
@@ -247,7 +247,7 @@ export class TvOSCellComponent extends React.Component<Props, State> {
|
|
|
247
247
|
|
|
248
248
|
const focusableId = R.join("-", [component?.id, id, index]);
|
|
249
249
|
|
|
250
|
-
const handleFocus = (arg1: any, index
|
|
250
|
+
const handleFocus = (arg1: any, index?: number) => {
|
|
251
251
|
const focusFn = onFocus || noop;
|
|
252
252
|
focusFn(arg1, index);
|
|
253
253
|
|
|
@@ -280,7 +280,7 @@ export class TvOSCellComponent extends React.Component<Props, State> {
|
|
|
280
280
|
<View onLayout={this.onLayout} style={styles.container}>
|
|
281
281
|
<Focusable
|
|
282
282
|
id={focusableId}
|
|
283
|
-
groupId={groupId || component?.id}
|
|
283
|
+
groupId={String(groupId || component?.id)}
|
|
284
284
|
onFocus={handleFocus}
|
|
285
285
|
onBlur={onBlur || this.onBlur}
|
|
286
286
|
isParallaxDisabled={this.layout?.width > 1740}
|
|
@@ -292,20 +292,18 @@ export class TvOSCellComponent extends React.Component<Props, State> {
|
|
|
292
292
|
style={baseCellStyles}
|
|
293
293
|
isFocusable={isFocusable}
|
|
294
294
|
>
|
|
295
|
-
{(focused) =>
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
);
|
|
308
|
-
}}
|
|
295
|
+
{(focused) => (
|
|
296
|
+
<FocusableCell
|
|
297
|
+
{...{
|
|
298
|
+
index,
|
|
299
|
+
CellRenderer,
|
|
300
|
+
item,
|
|
301
|
+
focused: !!(this.props.focused || selectedCell || focused),
|
|
302
|
+
scrollTo: this.scrollTo,
|
|
303
|
+
behavior,
|
|
304
|
+
}}
|
|
305
|
+
/>
|
|
306
|
+
)}
|
|
309
307
|
</Focusable>
|
|
310
308
|
</View>
|
|
311
309
|
);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { View } from "react-native";
|
|
2
2
|
import React from "react";
|
|
3
3
|
import { act, render } from "@testing-library/react-native";
|
|
4
|
-
import { CellWithFocusable } from "../CellWithFocusable
|
|
4
|
+
import { CellWithFocusable } from "../CellWithFocusable";
|
|
5
5
|
|
|
6
6
|
import { focusManager } from "@applicaster/zapp-react-native-utils/appUtils/focusManager";
|
|
7
7
|
|
|
@@ -23,9 +23,7 @@ describe("CellWithFocusable", () => {
|
|
|
23
23
|
|
|
24
24
|
const wrapper = renderWith(props);
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
expect(element.props.state).toBe("default");
|
|
26
|
+
expect(wrapper.UNSAFE_getByType("View").props.state).toBe("default");
|
|
29
27
|
});
|
|
30
28
|
|
|
31
29
|
it("should render in default state", () => {
|
|
@@ -42,9 +40,8 @@ describe("CellWithFocusable", () => {
|
|
|
42
40
|
focusManager.isGroupItemFocused = jest.fn(() => true);
|
|
43
41
|
|
|
44
42
|
const wrapper = renderWith(props);
|
|
45
|
-
const element = wrapper.getByTestId("cell-with-focusable-cell-renderer");
|
|
46
43
|
|
|
47
|
-
expect(
|
|
44
|
+
expect(wrapper.UNSAFE_getByType("View").props.state).toBe("default");
|
|
48
45
|
});
|
|
49
46
|
|
|
50
47
|
it("should render in focused state", () => {
|
|
@@ -58,17 +55,13 @@ describe("CellWithFocusable", () => {
|
|
|
58
55
|
scrollTo: jest.fn(),
|
|
59
56
|
};
|
|
60
57
|
|
|
58
|
+
focusManager.isGroupItemFocused = jest.fn(() => true);
|
|
61
59
|
const wrapper = renderWith(props);
|
|
62
60
|
|
|
63
|
-
const focusableGroupComponent = wrapper.getByTestId(
|
|
64
|
-
"cell-with-focusable-cell-renderer-focusable-group"
|
|
65
|
-
);
|
|
66
|
-
|
|
67
61
|
act(() => {
|
|
68
|
-
|
|
62
|
+
focusManager.on.mock.calls[focusManager.on.mock.calls.length - 1][1]();
|
|
69
63
|
});
|
|
70
64
|
|
|
71
|
-
|
|
72
|
-
expect(element.props.state).toBe("focused");
|
|
65
|
+
expect(wrapper.UNSAFE_getByType("View").props.state).toBe("focused");
|
|
73
66
|
});
|
|
74
67
|
});
|
package/Components/Cell/index.js
CHANGED
|
@@ -11,6 +11,7 @@ import { ScreenScrollingContext } from "../../Contexts/ScreenScrollingContext";
|
|
|
11
11
|
|
|
12
12
|
import { ScreenLayoutContextConsumer } from "../../Contexts/ScreenLayoutContext";
|
|
13
13
|
import { createContext } from "@applicaster/zapp-react-native-utils/reactUtils/createContext";
|
|
14
|
+
import { withComponentsMapOffsetContextConsumer } from "../../Contexts/ComponentsMapOffsetContext";
|
|
14
15
|
|
|
15
16
|
// TODO | Gallery QB | Extract this
|
|
16
17
|
export const ScrollInterceptorContext = createContext(
|
|
@@ -30,5 +31,6 @@ export const Cell = R.compose(
|
|
|
30
31
|
RiverOffsetContext.withConsumer,
|
|
31
32
|
HorizontalScrollContext.withConsumer,
|
|
32
33
|
withConsumer,
|
|
33
|
-
ScreenLayoutContextConsumer
|
|
34
|
+
ScreenLayoutContextConsumer,
|
|
35
|
+
withComponentsMapOffsetContextConsumer
|
|
34
36
|
)(Component);
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
focusManager,
|
|
10
10
|
forceFocusableFocus,
|
|
11
11
|
} from "@applicaster/zapp-react-native-utils/appUtils/focusManager/index.ios";
|
|
12
|
-
import { findNodeHandle } from "react-native";
|
|
12
|
+
import { findNodeHandle, ViewStyle } from "react-native";
|
|
13
13
|
|
|
14
14
|
function noop() {}
|
|
15
15
|
|
|
@@ -35,6 +35,10 @@ type Props = {
|
|
|
35
35
|
forceFocus?: boolean;
|
|
36
36
|
initialFocus?: boolean;
|
|
37
37
|
onLayout?: (e: any) => void;
|
|
38
|
+
willReceiveFocus: () => void;
|
|
39
|
+
hasReceivedFocus: () => void;
|
|
40
|
+
offsetUpdater: (arg1: string, arg2: number) => number;
|
|
41
|
+
style: ViewStyle;
|
|
38
42
|
};
|
|
39
43
|
|
|
40
44
|
export class Focusable extends BaseFocusable<Props> {
|
|
@@ -1,24 +1,25 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
|
|
3
|
-
type Props =
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
3
|
+
type Props =
|
|
4
|
+
| {
|
|
5
|
+
id: string;
|
|
6
|
+
children: React.ReactNode;
|
|
7
|
+
onPress?: (ref: FocusManager.TouchableRef) => void;
|
|
8
|
+
onPressIn?: (ref: FocusManager.TouchableRef) => void;
|
|
9
|
+
onPressOut?: (ref: FocusManager.TouchableRef) => void;
|
|
10
|
+
onLongPress?: (ref: FocusManager.TouchableRef) => void;
|
|
11
|
+
onFocus?: (
|
|
12
|
+
ref: FocusManager.TouchableRef,
|
|
13
|
+
options: FocusManager.Android.CallbackOptions
|
|
14
|
+
) => void;
|
|
15
|
+
onBlur?: (
|
|
16
|
+
ref: FocusManager.TouchableRef,
|
|
17
|
+
options: FocusManager.Android.CallbackOptions
|
|
18
|
+
) => void;
|
|
19
|
+
|
|
20
|
+
disableFocus?: boolean;
|
|
21
|
+
blockFocus?: boolean;
|
|
22
|
+
} & Partial<ParentFocus>;
|
|
22
23
|
|
|
23
24
|
export class Touchable extends React.Component<Props> {
|
|
24
25
|
onPress(focusableRef: FocusManager.TouchableRef): void {
|
|
@@ -15,6 +15,7 @@ import { noop } from "@applicaster/zapp-react-native-utils/functionUtils";
|
|
|
15
15
|
|
|
16
16
|
import { useParentFocus, useCheckItemIdsForUnique } from "./hooks";
|
|
17
17
|
import { FocusableListItemWrapper } from "./FocusableListItemWrapper";
|
|
18
|
+
import { FocusableScrollView } from "../FocusableScrollView";
|
|
18
19
|
|
|
19
20
|
const mapIndexed = R.addIndex(R.map);
|
|
20
21
|
|
|
@@ -54,6 +55,7 @@ export type Props<ItemT> = FlatListProps<ItemT> & {
|
|
|
54
55
|
disableNextFocusUp?: boolean;
|
|
55
56
|
disableNextFocusLeft?: boolean;
|
|
56
57
|
disableNextFocusRight?: boolean;
|
|
58
|
+
useScrollView?: boolean;
|
|
57
59
|
} & ParentFocus;
|
|
58
60
|
|
|
59
61
|
function FocusableListComponent<ItemT>(props: Props<ItemT>, ref) {
|
|
@@ -88,6 +90,7 @@ function FocusableListComponent<ItemT>(props: Props<ItemT>, ref) {
|
|
|
88
90
|
data = [],
|
|
89
91
|
// eslint-disable-next-line unused-imports/no-unused-vars
|
|
90
92
|
omitPropsPropagation = [],
|
|
93
|
+
useScrollView = false,
|
|
91
94
|
} = props;
|
|
92
95
|
|
|
93
96
|
useCheckItemIdsForUnique({ componentId: props.id, items: data });
|
|
@@ -273,6 +276,7 @@ function FocusableListComponent<ItemT>(props: Props<ItemT>, ref) {
|
|
|
273
276
|
"initialFocusDirection",
|
|
274
277
|
"withStateMemory",
|
|
275
278
|
"useSequentialLoading",
|
|
279
|
+
"useScrollView",
|
|
276
280
|
...omitPropsPropagation,
|
|
277
281
|
],
|
|
278
282
|
R.__
|
|
@@ -295,15 +299,28 @@ function FocusableListComponent<ItemT>(props: Props<ItemT>, ref) {
|
|
|
295
299
|
return (
|
|
296
300
|
// @ts-ignore
|
|
297
301
|
<ChildrenFocusDeactivatorView flex={1}>
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
302
|
+
{useScrollView ? (
|
|
303
|
+
<FocusableScrollView
|
|
304
|
+
ref={ref}
|
|
305
|
+
{...getFlatListProps(props)}
|
|
306
|
+
onEndReached={onEndReached}
|
|
307
|
+
initialNumToRender={initialNumToRender}
|
|
308
|
+
renderItem={renderItem}
|
|
309
|
+
focused={focused}
|
|
310
|
+
data={data}
|
|
311
|
+
/>
|
|
312
|
+
) : (
|
|
313
|
+
<FlatList
|
|
314
|
+
testID="focusable-list-flat-list"
|
|
315
|
+
focusable={false}
|
|
316
|
+
scrollEnabled={false}
|
|
317
|
+
ref={ref}
|
|
318
|
+
{...getFlatListProps(props)}
|
|
319
|
+
renderItem={renderItem}
|
|
320
|
+
onEndReached={onEndReached}
|
|
321
|
+
initialNumToRender={initialNumToRender}
|
|
322
|
+
/>
|
|
323
|
+
)}
|
|
307
324
|
</ChildrenFocusDeactivatorView>
|
|
308
325
|
);
|
|
309
326
|
}
|
|
@@ -11,6 +11,13 @@ import {
|
|
|
11
11
|
useInitialFocus,
|
|
12
12
|
} from "@applicaster/zapp-react-native-utils/focusManager";
|
|
13
13
|
|
|
14
|
+
export type FocusableScrollViewRenderItem = (info: {
|
|
15
|
+
item: ZappUIComponent;
|
|
16
|
+
index: number;
|
|
17
|
+
focused: boolean;
|
|
18
|
+
parentFocus: ParentFocus;
|
|
19
|
+
}) => React.ReactElement;
|
|
20
|
+
|
|
14
21
|
export type Props = ScrollViewProps & {
|
|
15
22
|
id?: number | string;
|
|
16
23
|
horizontal?: boolean;
|
|
@@ -32,17 +39,27 @@ export type Props = ScrollViewProps & {
|
|
|
32
39
|
initialFocusDirection?: FocusManager.Android.FocusNavigationDirections;
|
|
33
40
|
onAllComponentsLoaded?: () => void;
|
|
34
41
|
omitPropsPropagation?: Array<keyof FlatListProps<any>>;
|
|
35
|
-
renderItem:
|
|
36
|
-
item: ZappUIComponent;
|
|
37
|
-
index: number;
|
|
38
|
-
focused: boolean;
|
|
39
|
-
parentFocus: ParentFocus;
|
|
40
|
-
}) => React.ReactElement;
|
|
42
|
+
renderItem: FocusableScrollViewRenderItem;
|
|
41
43
|
} & ParentFocus;
|
|
42
44
|
|
|
43
45
|
type State = boolean[];
|
|
44
46
|
type Action = { index?: number; isFocusable: boolean; type?: string };
|
|
45
47
|
|
|
48
|
+
const unwrapArray = (maybeArray) =>
|
|
49
|
+
Array.isArray(maybeArray) ? maybeArray[0] : maybeArray;
|
|
50
|
+
|
|
51
|
+
function getEntry<T>(data: T | T[], index: unknown): T {
|
|
52
|
+
let entry;
|
|
53
|
+
|
|
54
|
+
if (R.isNil(index)) {
|
|
55
|
+
entry = unwrapArray(data);
|
|
56
|
+
} else if (typeof index === "number") {
|
|
57
|
+
entry = unwrapArray(data[index]);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return entry;
|
|
61
|
+
}
|
|
62
|
+
|
|
46
63
|
const focusableStateReducer = (
|
|
47
64
|
state: State,
|
|
48
65
|
{ index = null, isFocusable, type = null }: Action
|
|
@@ -73,7 +90,7 @@ const extendScrollViewRef = (_ref, childCompsMeasurementsMap) => {
|
|
|
73
90
|
};
|
|
74
91
|
|
|
75
92
|
_ref.scrollToIndex = (params) => {
|
|
76
|
-
const { index, viewOffset } = params;
|
|
93
|
+
const { index, viewOffset = 0 } = params;
|
|
77
94
|
|
|
78
95
|
const itemByIndex = R.find(R.propEq("index", index))(
|
|
79
96
|
R.values(childCompsMeasurementsMap)
|
|
@@ -129,8 +146,11 @@ function FocusableScrollViewComponent(props: Props, ref) {
|
|
|
129
146
|
);
|
|
130
147
|
|
|
131
148
|
const getFocusableEntryId = React.useCallback(
|
|
132
|
-
(_entry: ZappEntry, index?: number) => {
|
|
133
|
-
const entry
|
|
149
|
+
(_entry: ZappEntry | null, index?: number) => {
|
|
150
|
+
const entry: { id?: string } = getEntry(
|
|
151
|
+
R.isNil(index) ? _entry : data,
|
|
152
|
+
index
|
|
153
|
+
);
|
|
134
154
|
|
|
135
155
|
return entry ? `${props.id}--${entry?.id}` : undefined;
|
|
136
156
|
},
|
|
@@ -273,11 +293,18 @@ function FocusableScrollViewComponent(props: Props, ref) {
|
|
|
273
293
|
const renderItem = React.useCallback(
|
|
274
294
|
(item, index) => {
|
|
275
295
|
const renderArgs = { item, index };
|
|
276
|
-
|
|
277
|
-
const
|
|
296
|
+
|
|
297
|
+
const id = getFocusableEntryId(
|
|
298
|
+
Array.isArray(item) ? item[0] : item,
|
|
299
|
+
index
|
|
300
|
+
);
|
|
301
|
+
|
|
302
|
+
const nextFocus = getNextFocus(index);
|
|
278
303
|
|
|
279
304
|
const onItemLayout = (event) => {
|
|
280
|
-
childCompsMeasurementsMap.current[
|
|
305
|
+
childCompsMeasurementsMap.current[
|
|
306
|
+
Array.isArray(item) ? index : item.id
|
|
307
|
+
] = {
|
|
281
308
|
index,
|
|
282
309
|
layout: event.nativeEvent.layout,
|
|
283
310
|
};
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { View, StyleSheet } from "react-native";
|
|
3
|
-
import { ScreenLayoutContext } from "@applicaster/zapp-react-native-ui-components/Contexts/ScreenLayoutContext";
|
|
4
|
-
import { ifEmptyUseFallback } from "@applicaster/zapp-react-native-utils/cellUtils";
|
|
5
3
|
import { useCurrentScreenData } from "@applicaster/zapp-react-native-utils/reactHooks/screen";
|
|
6
4
|
import { useScreenConfiguration } from "../../River/useScreenConfiguration";
|
|
7
5
|
|
|
@@ -12,12 +10,15 @@ type Props = {
|
|
|
12
10
|
};
|
|
13
11
|
|
|
14
12
|
const styles = StyleSheet.create({
|
|
15
|
-
container: {
|
|
13
|
+
container: {
|
|
14
|
+
position: "absolute",
|
|
15
|
+
top: 0,
|
|
16
|
+
zIndex: 10,
|
|
17
|
+
width: 1920,
|
|
18
|
+
},
|
|
16
19
|
});
|
|
17
20
|
|
|
18
21
|
export const NavBarContainer = ({ children, isVisible, onReady }: Props) => {
|
|
19
|
-
const screenLayout = React.useContext(ScreenLayoutContext);
|
|
20
|
-
const screenMarginTop = screenLayout?.screenMarginTop || 0;
|
|
21
22
|
const screen = useCurrentScreenData();
|
|
22
23
|
const screenConfig = useScreenConfiguration(screen?.id);
|
|
23
24
|
|
|
@@ -26,9 +27,9 @@ export const NavBarContainer = ({ children, isVisible, onReady }: Props) => {
|
|
|
26
27
|
// limits the height of the focusable container of the TopMenuBarTV component
|
|
27
28
|
// to prevent it from being overlapped by the screen content,
|
|
28
29
|
// as it makes TopMenuBarTV unfocusable on tvOS
|
|
29
|
-
maxHeight:
|
|
30
|
+
maxHeight: 1,
|
|
30
31
|
}),
|
|
31
|
-
[
|
|
32
|
+
[screenConfig]
|
|
32
33
|
);
|
|
33
34
|
|
|
34
35
|
React.useEffect(() => {
|
|
@@ -28,10 +28,7 @@ const styles = StyleSheet.create({
|
|
|
28
28
|
},
|
|
29
29
|
});
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
borderPosition: BorderPosition,
|
|
33
|
-
borderWidth: number
|
|
34
|
-
) => {
|
|
31
|
+
const getBorderPadding = (borderPosition: BorderPosition, borderWidth) => {
|
|
35
32
|
switch (borderPosition) {
|
|
36
33
|
case "inside":
|
|
37
34
|
return 0;
|
|
@@ -38,16 +38,22 @@ function Image({
|
|
|
38
38
|
|
|
39
39
|
const shouldRenderImg = source && !error;
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
// Default placeholder image fallback is empty string because it won't rerender
|
|
42
|
+
// in case of undefined or {uri: undefined} source
|
|
43
|
+
// https://github.com/facebook/react-native/issues/9195
|
|
44
|
+
const defaultPlaceHolderImage = React.useMemo(
|
|
45
|
+
() => ({ uri: placeholderImage || "" }),
|
|
46
|
+
[source, error]
|
|
47
|
+
);
|
|
44
48
|
|
|
45
49
|
return (
|
|
50
|
+
// @ts-ignore
|
|
46
51
|
<MemoizedImage
|
|
47
52
|
style={style as ImageStyle}
|
|
48
53
|
onError={React.useCallback(() => setErrorState(true), [])}
|
|
49
|
-
|
|
50
|
-
|
|
54
|
+
source={
|
|
55
|
+
shouldRenderImg ? withDimensions(source) : defaultPlaceHolderImage
|
|
56
|
+
}
|
|
51
57
|
{...R.omit(["source"], otherProps)}
|
|
52
58
|
/>
|
|
53
59
|
);
|
|
@@ -17,7 +17,7 @@ const SECONDARY_IMAGE_PREFIX = "secondary_image";
|
|
|
17
17
|
|
|
18
18
|
type ImageSizing = typeof IMAGE_SIZING_FIT | typeof IMAGE_SIZING_FILL;
|
|
19
19
|
|
|
20
|
-
type ImagePosition =
|
|
20
|
+
type ImagePosition = typeof IMAGE_POSITION[keyof typeof IMAGE_POSITION];
|
|
21
21
|
|
|
22
22
|
export type DisplayMode =
|
|
23
23
|
| typeof DISPLAY_MODE_FIXED
|