@applicaster/zapp-react-native-ui-components 13.0.0-alpha.7120331332 → 13.0.0-alpha.7222542422
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.tsx +65 -28
- package/Components/Cell/Cell.tsx +1 -0
- package/Components/Cell/TvOSCellComponent.tsx +20 -26
- package/Components/Cell/index.js +3 -1
- package/Components/Focusable/Focusable.tsx +0 -54
- package/Components/Focusable/FocusableTvOS.tsx +6 -2
- package/Components/FocusableList/index.tsx +25 -10
- package/Components/FocusableScrollView/index.tsx +39 -12
- package/Components/Layout/TV/NavBarContainer.tsx +8 -7
- package/Components/MasterCell/DefaultComponents/Image/Image.ios.tsx +1 -1
- package/Components/MasterCell/hooks/useAsyncRendering/index.ts +2 -2
- package/Components/PlayerContainer/PlayerContainer.tsx +3 -7
- package/Components/PlayerContainer/PlayerContainerContext.tsx +9 -0
- package/Components/River/TV/index.tsx +6 -1
- package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +5 -1
- package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.tsx +5 -1
- package/Components/VideoModal/ModalAnimation/AnimationComponent.tsx +1 -1
- package/Contexts/ComponentsMapOffsetContext/index.tsx +41 -0
- package/package.json +12 -6
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
+
import * as R from "ramda";
|
|
2
3
|
|
|
3
4
|
import {
|
|
4
5
|
BaseFocusable as BaseFocusableInterface,
|
|
@@ -7,7 +8,6 @@ import {
|
|
|
7
8
|
|
|
8
9
|
import { createReactRef } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
9
10
|
import { focusManager } from "@applicaster/zapp-react-native-utils/appUtils/focusManager";
|
|
10
|
-
import * as FOCUS_EVENTS from "@applicaster/zapp-react-native-utils/appUtils/focusManager/events";
|
|
11
11
|
import { noop } from "@applicaster/zapp-react-native-utils/functionUtils";
|
|
12
12
|
|
|
13
13
|
type ScrollDirection = FocusManager.IOS.Direction | FocusManager.Web.Direction;
|
|
@@ -131,12 +131,6 @@ export class BaseFocusable<
|
|
|
131
131
|
willLoseFocus: FocusManager.FocusEventCB = (focusable, scrollDirection) => {
|
|
132
132
|
const { willLoseFocus = noop } = this.props;
|
|
133
133
|
willLoseFocus(focusable, scrollDirection);
|
|
134
|
-
|
|
135
|
-
focusManager.invokeHandler?.(
|
|
136
|
-
FOCUS_EVENTS.WILL_LOSE_FOCUS,
|
|
137
|
-
this,
|
|
138
|
-
scrollDirection
|
|
139
|
-
);
|
|
140
134
|
};
|
|
141
135
|
|
|
142
136
|
willReceiveFocus: FocusManager.FocusEventCB = (
|
|
@@ -145,12 +139,6 @@ export class BaseFocusable<
|
|
|
145
139
|
) => {
|
|
146
140
|
const { willReceiveFocus = noop } = this.props;
|
|
147
141
|
willReceiveFocus(focusable, scrollDirection);
|
|
148
|
-
|
|
149
|
-
focusManager.invokeHandler?.(
|
|
150
|
-
FOCUS_EVENTS.WILL_RECEIVE_FOCUS,
|
|
151
|
-
this,
|
|
152
|
-
scrollDirection
|
|
153
|
-
);
|
|
154
142
|
};
|
|
155
143
|
|
|
156
144
|
/**
|
|
@@ -163,8 +151,6 @@ export class BaseFocusable<
|
|
|
163
151
|
const { onFocus = noop } = this.props;
|
|
164
152
|
this.setFocusedState(true);
|
|
165
153
|
onFocus(focusable, scrollDirection);
|
|
166
|
-
|
|
167
|
-
focusManager.invokeHandler?.(FOCUS_EVENTS.FOCUS, this, scrollDirection);
|
|
168
154
|
};
|
|
169
155
|
|
|
170
156
|
/**
|
|
@@ -179,12 +165,6 @@ export class BaseFocusable<
|
|
|
179
165
|
) => {
|
|
180
166
|
const { hasReceivedFocus = noop } = this.props;
|
|
181
167
|
hasReceivedFocus(focusable, scrollDirection);
|
|
182
|
-
|
|
183
|
-
focusManager.invokeHandler?.(
|
|
184
|
-
FOCUS_EVENTS.HAS_RECEIVED_FOCUS,
|
|
185
|
-
this,
|
|
186
|
-
scrollDirection
|
|
187
|
-
);
|
|
188
168
|
};
|
|
189
169
|
|
|
190
170
|
/**
|
|
@@ -196,12 +176,6 @@ export class BaseFocusable<
|
|
|
196
176
|
hasLostFocus: FocusManager.FocusEventCB = (focusable, scrollDirection) => {
|
|
197
177
|
const { hasLostFocus = noop } = this.props;
|
|
198
178
|
hasLostFocus(focusable, scrollDirection);
|
|
199
|
-
|
|
200
|
-
focusManager.invokeHandler?.(
|
|
201
|
-
FOCUS_EVENTS.HAS_LOST_FOCUS,
|
|
202
|
-
this,
|
|
203
|
-
scrollDirection
|
|
204
|
-
);
|
|
205
179
|
};
|
|
206
180
|
|
|
207
181
|
/**
|
|
@@ -228,7 +202,6 @@ export class BaseFocusable<
|
|
|
228
202
|
const { onBlur = noop } = this.props;
|
|
229
203
|
this.setFocusedState(false);
|
|
230
204
|
onBlur(focusable, scrollDirection);
|
|
231
|
-
focusManager.invokeHandler?.(FOCUS_EVENTS.BLUR, this, scrollDirection);
|
|
232
205
|
};
|
|
233
206
|
|
|
234
207
|
onPress = (keyEvent?: Nullable<React.MouseEvent>) => {
|
|
@@ -269,4 +242,68 @@ export class BaseFocusable<
|
|
|
269
242
|
this.setState({ focused });
|
|
270
243
|
}
|
|
271
244
|
};
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* will invoke the underlying component's focus method
|
|
248
|
+
* @param {Object} scrollDirection
|
|
249
|
+
* @returns {Promise}
|
|
250
|
+
*/
|
|
251
|
+
focus(_, scrollDirection) {
|
|
252
|
+
return this.onFocus(this, scrollDirection); // invokeComponentMethod(this, "onFocus", scrollDirection);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* will invoke the underlying component's blur method
|
|
257
|
+
* @param {Object} scrollDirection
|
|
258
|
+
* @returns {Promise}
|
|
259
|
+
*/
|
|
260
|
+
blur(
|
|
261
|
+
_,
|
|
262
|
+
scrollDirection?: FocusManager.Web.Direction | FocusManager.IOS.Direction
|
|
263
|
+
) {
|
|
264
|
+
return this.onBlur(this, scrollDirection);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Sets the focus on this item. Will trigger sequentially a sequence of
|
|
269
|
+
* functions (willReceiveFocus, focus, hasReceivedFocus). If these functions (defined in the Focusable
|
|
270
|
+
* Item underlying component) return promises, execution will wait before it proceeds to the next. This
|
|
271
|
+
* is useful for triggering sequential operations that specifically require to fully run before or after
|
|
272
|
+
* focus is actually set on that item.
|
|
273
|
+
* @param {string} scrollDirection string representation of the direction of the navigation which landed
|
|
274
|
+
* to this item being focused
|
|
275
|
+
*/
|
|
276
|
+
_executeFocusSequence(methodNames, scrollDirection) {
|
|
277
|
+
return R.reduce(
|
|
278
|
+
(sequence, methodName) => {
|
|
279
|
+
const method = this[methodName]; // Access the method by name
|
|
280
|
+
|
|
281
|
+
if (typeof method !== "function") {
|
|
282
|
+
throw new Error(
|
|
283
|
+
`Method '${methodName}' not found or not a function.`
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
return sequence
|
|
288
|
+
.then(() => method.call(this, scrollDirection))
|
|
289
|
+
.catch((e) => {
|
|
290
|
+
throw e; // Re-throw for consistent error handling
|
|
291
|
+
});
|
|
292
|
+
},
|
|
293
|
+
Promise.resolve(),
|
|
294
|
+
methodNames
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
setFocus(scrollDirection) {
|
|
299
|
+
const focusMethods = ["willReceiveFocus", "focus", "hasReceivedFocus"];
|
|
300
|
+
|
|
301
|
+
return this._executeFocusSequence(focusMethods, scrollDirection);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
setBlur(scrollDirection) {
|
|
305
|
+
const blurMethods = ["willLoseFocus", "blur", "hasLostFocus"];
|
|
306
|
+
|
|
307
|
+
return this._executeFocusSequence(blurMethods, scrollDirection);
|
|
308
|
+
}
|
|
272
309
|
}
|
package/Components/Cell/Cell.tsx
CHANGED
|
@@ -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";
|
|
@@ -10,10 +10,6 @@ import { focusManager } from "@applicaster/zapp-react-native-utils/appUtils/focu
|
|
|
10
10
|
import { sendSelectCellEvent } from "@applicaster/zapp-react-native-utils/analyticsUtils";
|
|
11
11
|
import { noop } from "@applicaster/zapp-react-native-utils/functionUtils";
|
|
12
12
|
import { CellWithFocusable } from "./CellWithFocusable";
|
|
13
|
-
import { getCellState } from "./utils";
|
|
14
|
-
|
|
15
|
-
const _getCellState = (focused, selected) =>
|
|
16
|
-
getCellState({ focused, selected });
|
|
17
13
|
|
|
18
14
|
type Props = {
|
|
19
15
|
item: ZappEntry;
|
|
@@ -69,6 +65,7 @@ type Props = {
|
|
|
69
65
|
isFocusable: boolean;
|
|
70
66
|
shouldUpdate: boolean;
|
|
71
67
|
behavior: Behavior;
|
|
68
|
+
componentsMapOffset: number;
|
|
72
69
|
};
|
|
73
70
|
|
|
74
71
|
type State = {
|
|
@@ -183,6 +180,7 @@ export class TvOSCellComponent extends React.Component<Props, State> {
|
|
|
183
180
|
groupId,
|
|
184
181
|
component,
|
|
185
182
|
index,
|
|
183
|
+
componentsMapOffset,
|
|
186
184
|
} = this.props;
|
|
187
185
|
|
|
188
186
|
this.setScreenLayout(componentAnchorPointY, screenLayout);
|
|
@@ -197,13 +195,11 @@ export class TvOSCellComponent extends React.Component<Props, State> {
|
|
|
197
195
|
const extraAnchorPointYOffset =
|
|
198
196
|
screenLayout?.extraAnchorPointYOffset || 0;
|
|
199
197
|
|
|
200
|
-
const marginTop = screenLayout?.screenMarginTop || 0;
|
|
201
|
-
|
|
202
198
|
const totalOffset =
|
|
203
199
|
headerOffset +
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
200
|
+
(componentAnchorPointY || 0) +
|
|
201
|
+
extraAnchorPointYOffset -
|
|
202
|
+
componentsMapOffset || 0;
|
|
207
203
|
|
|
208
204
|
mainOffsetUpdater?.(
|
|
209
205
|
{ tag: this.target },
|
|
@@ -251,7 +247,7 @@ export class TvOSCellComponent extends React.Component<Props, State> {
|
|
|
251
247
|
|
|
252
248
|
const focusableId = R.join("-", [component?.id, id, index]);
|
|
253
249
|
|
|
254
|
-
const handleFocus = (arg1: any, index
|
|
250
|
+
const handleFocus = (arg1: any, index?: number) => {
|
|
255
251
|
const focusFn = onFocus || noop;
|
|
256
252
|
focusFn(arg1, index);
|
|
257
253
|
|
|
@@ -284,7 +280,7 @@ export class TvOSCellComponent extends React.Component<Props, State> {
|
|
|
284
280
|
<View onLayout={this.onLayout} style={styles.container}>
|
|
285
281
|
<Focusable
|
|
286
282
|
id={focusableId}
|
|
287
|
-
groupId={groupId || component?.id}
|
|
283
|
+
groupId={String(groupId || component?.id)}
|
|
288
284
|
onFocus={handleFocus}
|
|
289
285
|
onBlur={onBlur || this.onBlur}
|
|
290
286
|
isParallaxDisabled={this.layout?.width > 1740}
|
|
@@ -296,20 +292,18 @@ export class TvOSCellComponent extends React.Component<Props, State> {
|
|
|
296
292
|
style={baseCellStyles}
|
|
297
293
|
isFocusable={isFocusable}
|
|
298
294
|
>
|
|
299
|
-
{(focused) =>
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
);
|
|
312
|
-
}}
|
|
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
|
+
)}
|
|
313
307
|
</Focusable>
|
|
314
308
|
</View>
|
|
315
309
|
);
|
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);
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import * as R from "ramda";
|
|
3
2
|
|
|
4
3
|
import { BaseFocusable } from "../BaseFocusable";
|
|
5
4
|
import { focusManager } from "@applicaster/zapp-react-native-utils/appUtils/focusManager";
|
|
@@ -42,8 +41,6 @@ class Focusable extends BaseFocusable<Props> {
|
|
|
42
41
|
this.resetLongPressTimeout = this.resetLongPressTimeout.bind(this);
|
|
43
42
|
this.longPress = this.longPress.bind(this);
|
|
44
43
|
this.press = this.press.bind(this);
|
|
45
|
-
this.focus = this.focus.bind(this);
|
|
46
|
-
this.blur = this.blur.bind(this);
|
|
47
44
|
}
|
|
48
45
|
|
|
49
46
|
/**
|
|
@@ -55,37 +52,6 @@ class Focusable extends BaseFocusable<Props> {
|
|
|
55
52
|
return this.preferredFocus();
|
|
56
53
|
}
|
|
57
54
|
|
|
58
|
-
/**
|
|
59
|
-
* Sets the focus on this item. Will trigger sequentially a sequence of
|
|
60
|
-
* functions (willReceiveFocus, focus, hasReceivedFocus). If these functions (defined in the Focusable
|
|
61
|
-
* Item underlying component) return promises, execution will wait before it proceeds to the next. This
|
|
62
|
-
* is useful for triggering sequential operations that specifically require to fully run before or after
|
|
63
|
-
* focus is actually set on that item.
|
|
64
|
-
* @param {string} scrollDirection string representation of the direction of the navigation which landed
|
|
65
|
-
* to this item being focused
|
|
66
|
-
*/
|
|
67
|
-
setFocus(scrollDirection) {
|
|
68
|
-
const focusMethods = [
|
|
69
|
-
this.willReceiveFocus,
|
|
70
|
-
this.focus,
|
|
71
|
-
this.hasReceivedFocus,
|
|
72
|
-
];
|
|
73
|
-
|
|
74
|
-
const self = this;
|
|
75
|
-
|
|
76
|
-
return R.reduce(
|
|
77
|
-
(sequence, method) => {
|
|
78
|
-
return sequence
|
|
79
|
-
.then(() => method.apply(self, [scrollDirection]))
|
|
80
|
-
.catch((e) => {
|
|
81
|
-
throw e;
|
|
82
|
-
});
|
|
83
|
-
},
|
|
84
|
-
Promise.resolve(),
|
|
85
|
-
focusMethods
|
|
86
|
-
);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
55
|
startLongPressTimeout() {
|
|
90
56
|
this.longPressTimeout = setTimeout(() => {
|
|
91
57
|
this.longPress(null);
|
|
@@ -102,26 +68,6 @@ class Focusable extends BaseFocusable<Props> {
|
|
|
102
68
|
}
|
|
103
69
|
}
|
|
104
70
|
|
|
105
|
-
/**
|
|
106
|
-
* will invoke the underlying component's focus method
|
|
107
|
-
* @param {Object} scrollDirection
|
|
108
|
-
* @returns {Promise}
|
|
109
|
-
*/
|
|
110
|
-
focus(scrollDirection) {
|
|
111
|
-
return this.onFocus(this, scrollDirection); // invokeComponentMethod(this, "onFocus", scrollDirection);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* will invoke the underlying component's blur method
|
|
116
|
-
* @param {Object} scrollDirection
|
|
117
|
-
* @returns {Promise}
|
|
118
|
-
*/
|
|
119
|
-
blur(
|
|
120
|
-
scrollDirection?: FocusManager.Web.Direction | FocusManager.IOS.Direction
|
|
121
|
-
) {
|
|
122
|
-
return this.onBlur(this, scrollDirection);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
71
|
/**
|
|
126
72
|
* will invoke the underlying component's press method
|
|
127
73
|
* @param {Object} keyEvent
|
|
@@ -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> {
|
|
@@ -90,7 +94,7 @@ export class Focusable extends BaseFocusable<Props> {
|
|
|
90
94
|
onBlur(nativeEvent);
|
|
91
95
|
}
|
|
92
96
|
|
|
93
|
-
setFocus(
|
|
97
|
+
setFocus(_direction, callback) {
|
|
94
98
|
const focusMethods = [
|
|
95
99
|
{ method: this.willReceiveFocus },
|
|
96
100
|
{ method: this.focus, args: [callback] },
|
|
@@ -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) {
|
|
@@ -87,8 +89,8 @@ function FocusableListComponent<ItemT>(props: Props<ItemT>, ref) {
|
|
|
87
89
|
initialFocusDirection = "down",
|
|
88
90
|
data = [],
|
|
89
91
|
// eslint-disable-next-line unused-imports/no-unused-vars
|
|
90
|
-
onAllComponentsLoaded, // TODO: re-implement it
|
|
91
92
|
omitPropsPropagation = [],
|
|
93
|
+
useScrollView = false,
|
|
92
94
|
} = props;
|
|
93
95
|
|
|
94
96
|
useCheckItemIdsForUnique({ componentId: props.id, items: data });
|
|
@@ -274,6 +276,7 @@ function FocusableListComponent<ItemT>(props: Props<ItemT>, ref) {
|
|
|
274
276
|
"initialFocusDirection",
|
|
275
277
|
"withStateMemory",
|
|
276
278
|
"useSequentialLoading",
|
|
279
|
+
"useScrollView",
|
|
277
280
|
...omitPropsPropagation,
|
|
278
281
|
],
|
|
279
282
|
R.__
|
|
@@ -296,15 +299,27 @@ function FocusableListComponent<ItemT>(props: Props<ItemT>, ref) {
|
|
|
296
299
|
return (
|
|
297
300
|
// @ts-ignore
|
|
298
301
|
<ChildrenFocusDeactivatorView flex={1}>
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
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
|
+
focusable={false}
|
|
315
|
+
scrollEnabled={false}
|
|
316
|
+
ref={ref}
|
|
317
|
+
{...getFlatListProps(props)}
|
|
318
|
+
renderItem={renderItem}
|
|
319
|
+
onEndReached={onEndReached}
|
|
320
|
+
initialNumToRender={initialNumToRender}
|
|
321
|
+
/>
|
|
322
|
+
)}
|
|
308
323
|
</ChildrenFocusDeactivatorView>
|
|
309
324
|
);
|
|
310
325
|
}
|
|
@@ -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(() => {
|
|
@@ -50,7 +50,7 @@ function Image({
|
|
|
50
50
|
// @ts-ignore
|
|
51
51
|
<MemoizedImage
|
|
52
52
|
style={style as ImageStyle}
|
|
53
|
-
onError={() => setErrorState(true)}
|
|
53
|
+
onError={React.useCallback(() => setErrorState(true), [])}
|
|
54
54
|
source={
|
|
55
55
|
shouldRenderImg ? withDimensions(source) : defaultPlaceHolderImage
|
|
56
56
|
}
|
|
@@ -8,7 +8,7 @@ import { MasterCellAsyncRenderManager } from "./MasterCellAsyncRenderManager";
|
|
|
8
8
|
const layoutMeasure = (viewRef, onMeasure) => {
|
|
9
9
|
viewRef?.measureLayout?.(
|
|
10
10
|
findNodeHandle(viewRef),
|
|
11
|
-
(
|
|
11
|
+
(_x, _y, width, height) => {
|
|
12
12
|
onMeasure({ width, height });
|
|
13
13
|
},
|
|
14
14
|
noop
|
|
@@ -16,7 +16,7 @@ const layoutMeasure = (viewRef, onMeasure) => {
|
|
|
16
16
|
};
|
|
17
17
|
|
|
18
18
|
const regularMeasure = (viewRef, onMeasure) => {
|
|
19
|
-
viewRef?.measure?.((
|
|
19
|
+
viewRef?.measure?.((_x, _y, width, height) => {
|
|
20
20
|
onMeasure({ width, height });
|
|
21
21
|
});
|
|
22
22
|
};
|
|
@@ -380,7 +380,7 @@ const PlayerContainerComponent = (props: Props) => {
|
|
|
380
380
|
}
|
|
381
381
|
};
|
|
382
382
|
|
|
383
|
-
const playerRemoteHandler = (
|
|
383
|
+
const playerRemoteHandler = (event, isLanguageOverlayVisible) => {
|
|
384
384
|
const { eventType } = event;
|
|
385
385
|
|
|
386
386
|
if (!isLanguageOverlayVisible && eventType === "menu") {
|
|
@@ -623,12 +623,8 @@ const PlayerContainerComponent = (props: Props) => {
|
|
|
623
623
|
<PlayerContainerContext.Consumer>
|
|
624
624
|
{(context) => (
|
|
625
625
|
<TVEventHandlerComponent
|
|
626
|
-
tvEventHandler={(
|
|
627
|
-
playerRemoteHandler(
|
|
628
|
-
component,
|
|
629
|
-
event,
|
|
630
|
-
context.isLanguageOverlayVisible
|
|
631
|
-
)
|
|
626
|
+
tvEventHandler={(_component, event) =>
|
|
627
|
+
playerRemoteHandler(event, context.isLanguageOverlayVisible)
|
|
632
628
|
}
|
|
633
629
|
>
|
|
634
630
|
<FocusableGroup
|
|
@@ -9,11 +9,14 @@ type ContextProps = {
|
|
|
9
9
|
setIsLanguageOverlayVisible: (isVisible) => void;
|
|
10
10
|
setShowComponentsContainer: (isVisible) => void;
|
|
11
11
|
showComponentsContainer: boolean;
|
|
12
|
+
setIsSeekBarTouch: (isTouch) => void;
|
|
13
|
+
isSeekBarTouch: boolean;
|
|
12
14
|
};
|
|
13
15
|
|
|
14
16
|
export const PlayerContainerContext = createContext({
|
|
15
17
|
ignoreOffsetContainer: false,
|
|
16
18
|
isLanguageOverlayVisible: false,
|
|
19
|
+
isSeekBarTouch: false,
|
|
17
20
|
} as ContextProps);
|
|
18
21
|
|
|
19
22
|
type Props = {
|
|
@@ -40,6 +43,8 @@ export const PlayerContainerContextProvider = ({
|
|
|
40
43
|
const [showComponentsContainer, setShowComponentsContainer] =
|
|
41
44
|
React.useState(true);
|
|
42
45
|
|
|
46
|
+
const [isSeekBarTouch, setIsSeekBarTouch] = React.useState(false);
|
|
47
|
+
|
|
43
48
|
const value = React.useMemo(
|
|
44
49
|
() => ({
|
|
45
50
|
ignoreOffsetContainer,
|
|
@@ -49,6 +54,8 @@ export const PlayerContainerContextProvider = ({
|
|
|
49
54
|
setIsLanguageOverlayVisible,
|
|
50
55
|
showComponentsContainer,
|
|
51
56
|
setShowComponentsContainer: inline ? setShowComponentsContainer : null,
|
|
57
|
+
isSeekBarTouch,
|
|
58
|
+
setIsSeekBarTouch,
|
|
52
59
|
}),
|
|
53
60
|
[
|
|
54
61
|
ignoreOffsetContainer,
|
|
@@ -58,6 +65,8 @@ export const PlayerContainerContextProvider = ({
|
|
|
58
65
|
setIsLanguageOverlayVisible,
|
|
59
66
|
showComponentsContainer,
|
|
60
67
|
setShowComponentsContainer,
|
|
68
|
+
isSeekBarTouch,
|
|
69
|
+
setIsSeekBarTouch,
|
|
61
70
|
]
|
|
62
71
|
);
|
|
63
72
|
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
+
import { compose } from "ramda";
|
|
1
2
|
import { River as RiverComponent } from "./River";
|
|
2
3
|
import { withTvEventHandler } from "./withTVEventHandler";
|
|
4
|
+
import { withComponentsMapOffsetContext } from "../../../Contexts/ComponentsMapOffsetContext";
|
|
3
5
|
|
|
4
|
-
export const River =
|
|
6
|
+
export const River = compose(
|
|
7
|
+
withTvEventHandler,
|
|
8
|
+
withComponentsMapOffsetContext
|
|
9
|
+
)(RiverComponent);
|
|
@@ -62,7 +62,10 @@ export const AnimatedScrollModalComponent = ({ children }: Props) => {
|
|
|
62
62
|
} = useModalAnimationContext();
|
|
63
63
|
|
|
64
64
|
const [enableGesture, setIEnableGesture] = React.useState<boolean>(true);
|
|
65
|
-
|
|
65
|
+
|
|
66
|
+
const { isLanguageOverlayVisible, isSeekBarTouch } = React.useContext(
|
|
67
|
+
PlayerContainerContext
|
|
68
|
+
);
|
|
66
69
|
|
|
67
70
|
const { maximiseVideoModal, minimiseVideoModal, videoModalState } =
|
|
68
71
|
useNavigation();
|
|
@@ -80,6 +83,7 @@ export const AnimatedScrollModalComponent = ({ children }: Props) => {
|
|
|
80
83
|
enableGesture &&
|
|
81
84
|
!isLanguageOverlayVisible &&
|
|
82
85
|
isNotMinimizeMaximazeAnimation &&
|
|
86
|
+
!isSeekBarTouch &&
|
|
83
87
|
(isMaximazedModal || isMinimizedModal);
|
|
84
88
|
|
|
85
89
|
const isAudioItem = React.useMemo(
|
|
@@ -41,7 +41,10 @@ export const AnimatedVideoPlayer = ({ children }: Props) => {
|
|
|
41
41
|
videoModalState: { mode: videoModalMode },
|
|
42
42
|
} = useNavigation();
|
|
43
43
|
|
|
44
|
-
const { isLanguageOverlayVisible } = React.useContext(
|
|
44
|
+
const { isLanguageOverlayVisible, isSeekBarTouch } = React.useContext(
|
|
45
|
+
PlayerContainerContext
|
|
46
|
+
);
|
|
47
|
+
|
|
45
48
|
const isMaximazedModal = videoModalMode === "MAXIMIZED";
|
|
46
49
|
const isMinimizedModal = videoModalMode === "MINIMIZED";
|
|
47
50
|
|
|
@@ -52,6 +55,7 @@ export const AnimatedVideoPlayer = ({ children }: Props) => {
|
|
|
52
55
|
const isEnablePanGesture =
|
|
53
56
|
!isLanguageOverlayVisible &&
|
|
54
57
|
isNotMinimizeMaximazeAnimation &&
|
|
58
|
+
!isSeekBarTouch &&
|
|
55
59
|
(isMaximazedModal || isMinimizedModal);
|
|
56
60
|
|
|
57
61
|
const onGestureEvent = Animated.event(
|
|
@@ -77,7 +77,7 @@ export const AnimationView = ({
|
|
|
77
77
|
const measureView = React.useCallback(() => {
|
|
78
78
|
if (isTabletLandscape && tabletLandscapePlayerTopPosition === 0) {
|
|
79
79
|
animationComponentRef.current.measure(
|
|
80
|
-
(
|
|
80
|
+
(_x, _y, _width, _height, _pageX, pageY) => {
|
|
81
81
|
setTabletLandscapePlayerTopPosition(pageY - 20);
|
|
82
82
|
}
|
|
83
83
|
);
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import React, { PropsWithChildren } from "react";
|
|
2
|
+
|
|
3
|
+
export const ComponentsMapOffsetContext = React.createContext({
|
|
4
|
+
setComponentsMapOffset: (_offset: number) => void 0,
|
|
5
|
+
componentsMapOffset: 0,
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
export const ComponentsMapOffsetContextProvider = ({
|
|
9
|
+
children,
|
|
10
|
+
}: PropsWithChildren<{}>) => {
|
|
11
|
+
const [componentsMapOffset, setComponentsMapOffset] = React.useState(0);
|
|
12
|
+
|
|
13
|
+
const contextValue = {
|
|
14
|
+
componentsMapOffset,
|
|
15
|
+
setComponentsMapOffset,
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<ComponentsMapOffsetContext.Provider value={contextValue}>
|
|
20
|
+
{children}
|
|
21
|
+
</ComponentsMapOffsetContext.Provider>
|
|
22
|
+
);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const withComponentsMapOffsetContext = (Component: any) =>
|
|
26
|
+
function ComponentsMapOffsetContextProviderWrapper(props: {}) {
|
|
27
|
+
return (
|
|
28
|
+
<ComponentsMapOffsetContextProvider>
|
|
29
|
+
<Component {...props} />
|
|
30
|
+
</ComponentsMapOffsetContextProvider>
|
|
31
|
+
);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export const withComponentsMapOffsetContextConsumer = (Component: any) =>
|
|
35
|
+
function ComponentsMapOffsetContextConsumerWrapper(props: {}) {
|
|
36
|
+
const { componentsMapOffset } = React.useContext(
|
|
37
|
+
ComponentsMapOffsetContext
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
return <Component {...props} componentsMapOffset={componentsMapOffset} />;
|
|
41
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@applicaster/zapp-react-native-ui-components",
|
|
3
|
-
"version": "13.0.0-alpha.
|
|
3
|
+
"version": "13.0.0-alpha.7222542422",
|
|
4
4
|
"description": "Applicaster Zapp React Native ui components for the Quick Brick App",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -34,20 +34,26 @@
|
|
|
34
34
|
"redux-mock-store": "^1.5.3"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@applicaster/applicaster-types": "13.0.0-alpha.
|
|
38
|
-
"@applicaster/zapp-react-native-bridge": "13.0.0-alpha.
|
|
39
|
-
"@applicaster/zapp-react-native-redux": "13.0.0-alpha.
|
|
40
|
-
"@applicaster/zapp-react-native-utils": "13.0.0-alpha.
|
|
37
|
+
"@applicaster/applicaster-types": "13.0.0-alpha.7222542422",
|
|
38
|
+
"@applicaster/zapp-react-native-bridge": "13.0.0-alpha.7222542422",
|
|
39
|
+
"@applicaster/zapp-react-native-redux": "13.0.0-alpha.7222542422",
|
|
40
|
+
"@applicaster/zapp-react-native-utils": "13.0.0-alpha.7222542422",
|
|
41
41
|
"promise": "^8.3.0",
|
|
42
42
|
"react-router-native": "^5.1.2",
|
|
43
43
|
"url": "^0.11.0",
|
|
44
44
|
"uuid": "^3.3.2"
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|
|
47
|
+
"@applicaster/zapp-pipes-v2-client": "*",
|
|
47
48
|
"@react-native-community/netinfo": "*",
|
|
49
|
+
"@types/node": "*",
|
|
50
|
+
"immer": "*",
|
|
48
51
|
"react": "17.0.2",
|
|
49
52
|
"react-native": "0.68.6",
|
|
50
53
|
"react-native-safe-area-context": "*",
|
|
51
|
-
"react-native-svg": "*"
|
|
54
|
+
"react-native-svg": "*",
|
|
55
|
+
"uglify-js": "*",
|
|
56
|
+
"validate-color": "*",
|
|
57
|
+
"zustand": "*"
|
|
52
58
|
}
|
|
53
59
|
}
|