@applicaster/zapp-react-native-ui-components 12.0.3-rc.2 → 13.0.0-alpha.1338463240
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 +28 -65
- package/Components/Cell/Cell.tsx +4 -8
- package/Components/Cell/CellWithFocusable.tsx +1 -1
- package/Components/Cell/TvOSCellComponent.tsx +7 -8
- package/Components/Focusable/Focusable.tsx +54 -0
- package/Components/Focusable/FocusableTvOS.tsx +4 -0
- package/Components/FocusableList/index.tsx +25 -9
- package/Components/FocusableScrollView/index.tsx +39 -12
- package/Components/NativeFocusables/{index.js → index.ts} +4 -2
- package/Decorators/Analytics/{index.js → index.tsx} +11 -11
- package/README.md +1 -13
- package/package.json +5 -5
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import * as R from "ramda";
|
|
3
2
|
|
|
4
3
|
import {
|
|
5
4
|
BaseFocusable as BaseFocusableInterface,
|
|
@@ -8,6 +7,7 @@ import {
|
|
|
8
7
|
|
|
9
8
|
import { createReactRef } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
10
9
|
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,6 +131,12 @@ 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
|
+
);
|
|
134
140
|
};
|
|
135
141
|
|
|
136
142
|
willReceiveFocus: FocusManager.FocusEventCB = (
|
|
@@ -139,6 +145,12 @@ export class BaseFocusable<
|
|
|
139
145
|
) => {
|
|
140
146
|
const { willReceiveFocus = noop } = this.props;
|
|
141
147
|
willReceiveFocus(focusable, scrollDirection);
|
|
148
|
+
|
|
149
|
+
focusManager.invokeHandler?.(
|
|
150
|
+
FOCUS_EVENTS.WILL_RECEIVE_FOCUS,
|
|
151
|
+
this,
|
|
152
|
+
scrollDirection
|
|
153
|
+
);
|
|
142
154
|
};
|
|
143
155
|
|
|
144
156
|
/**
|
|
@@ -151,6 +163,8 @@ export class BaseFocusable<
|
|
|
151
163
|
const { onFocus = noop } = this.props;
|
|
152
164
|
this.setFocusedState(true);
|
|
153
165
|
onFocus(focusable, scrollDirection);
|
|
166
|
+
|
|
167
|
+
focusManager.invokeHandler?.(FOCUS_EVENTS.FOCUS, this, scrollDirection);
|
|
154
168
|
};
|
|
155
169
|
|
|
156
170
|
/**
|
|
@@ -165,6 +179,12 @@ export class BaseFocusable<
|
|
|
165
179
|
) => {
|
|
166
180
|
const { hasReceivedFocus = noop } = this.props;
|
|
167
181
|
hasReceivedFocus(focusable, scrollDirection);
|
|
182
|
+
|
|
183
|
+
focusManager.invokeHandler?.(
|
|
184
|
+
FOCUS_EVENTS.HAS_RECEIVED_FOCUS,
|
|
185
|
+
this,
|
|
186
|
+
scrollDirection
|
|
187
|
+
);
|
|
168
188
|
};
|
|
169
189
|
|
|
170
190
|
/**
|
|
@@ -176,6 +196,12 @@ export class BaseFocusable<
|
|
|
176
196
|
hasLostFocus: FocusManager.FocusEventCB = (focusable, scrollDirection) => {
|
|
177
197
|
const { hasLostFocus = noop } = this.props;
|
|
178
198
|
hasLostFocus(focusable, scrollDirection);
|
|
199
|
+
|
|
200
|
+
focusManager.invokeHandler?.(
|
|
201
|
+
FOCUS_EVENTS.HAS_LOST_FOCUS,
|
|
202
|
+
this,
|
|
203
|
+
scrollDirection
|
|
204
|
+
);
|
|
179
205
|
};
|
|
180
206
|
|
|
181
207
|
/**
|
|
@@ -202,6 +228,7 @@ export class BaseFocusable<
|
|
|
202
228
|
const { onBlur = noop } = this.props;
|
|
203
229
|
this.setFocusedState(false);
|
|
204
230
|
onBlur(focusable, scrollDirection);
|
|
231
|
+
focusManager.invokeHandler?.(FOCUS_EVENTS.BLUR, this, scrollDirection);
|
|
205
232
|
};
|
|
206
233
|
|
|
207
234
|
onPress = (keyEvent?: Nullable<React.MouseEvent>) => {
|
|
@@ -242,68 +269,4 @@ export class BaseFocusable<
|
|
|
242
269
|
this.setState({ focused });
|
|
243
270
|
}
|
|
244
271
|
};
|
|
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
|
-
}
|
|
309
272
|
}
|
package/Components/Cell/Cell.tsx
CHANGED
|
@@ -14,10 +14,7 @@ import { BaseFocusable } from "../BaseFocusable";
|
|
|
14
14
|
import { AccessibilityManager } from "@applicaster/zapp-react-native-utils/appUtils/accessibilityManager";
|
|
15
15
|
|
|
16
16
|
type Props = {
|
|
17
|
-
item:
|
|
18
|
-
id: string;
|
|
19
|
-
title: string;
|
|
20
|
-
};
|
|
17
|
+
item: ZappEntry;
|
|
21
18
|
index: number;
|
|
22
19
|
shouldScrollHorizontally: (arg1: [any]) => boolean | null | undefined;
|
|
23
20
|
shouldScrollVertically: (...args: any) => boolean | null | undefined;
|
|
@@ -33,10 +30,9 @@ type Props = {
|
|
|
33
30
|
id: string;
|
|
34
31
|
};
|
|
35
32
|
selected?: boolean;
|
|
36
|
-
CellRenderer: React.
|
|
37
|
-
item:
|
|
38
|
-
|
|
39
|
-
}> & { hasFocusableInside: (item: Record<string, string>) => boolean };
|
|
33
|
+
CellRenderer: React.FunctionComponent<any> & {
|
|
34
|
+
hasFocusableInside: (item: ZappEntry) => boolean;
|
|
35
|
+
};
|
|
40
36
|
preferredFocus: boolean;
|
|
41
37
|
navigator: {
|
|
42
38
|
push: (arg1: Record<any, any>) => void;
|
|
@@ -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";
|
|
@@ -36,10 +36,9 @@ type Props = {
|
|
|
36
36
|
component_type: string;
|
|
37
37
|
};
|
|
38
38
|
selected: boolean;
|
|
39
|
-
CellRenderer: React.
|
|
40
|
-
item: any;
|
|
41
|
-
|
|
42
|
-
}> & { hasFocusableInside: (item: any) => boolean };
|
|
39
|
+
CellRenderer: React.FunctionComponent<any> & {
|
|
40
|
+
hasFocusableInside: (item: any) => boolean;
|
|
41
|
+
};
|
|
43
42
|
preferredFocus: boolean;
|
|
44
43
|
navigator: {
|
|
45
44
|
push: (arg1: Record<any, any>) => void;
|
|
@@ -252,7 +251,7 @@ export class TvOSCellComponent extends React.Component<Props, State> {
|
|
|
252
251
|
|
|
253
252
|
const focusableId = R.join("-", [component?.id, id, index]);
|
|
254
253
|
|
|
255
|
-
const handleFocus = (arg1: any, index
|
|
254
|
+
const handleFocus = (arg1: any, index?: number) => {
|
|
256
255
|
const focusFn = onFocus || noop;
|
|
257
256
|
focusFn(arg1, index);
|
|
258
257
|
|
|
@@ -285,7 +284,7 @@ export class TvOSCellComponent extends React.Component<Props, State> {
|
|
|
285
284
|
<View onLayout={this.onLayout} style={styles.container}>
|
|
286
285
|
<Focusable
|
|
287
286
|
id={focusableId}
|
|
288
|
-
groupId={groupId || component?.id}
|
|
287
|
+
groupId={(groupId || component?.id).toString()}
|
|
289
288
|
onFocus={handleFocus}
|
|
290
289
|
onBlur={onBlur || this.onBlur}
|
|
291
290
|
isParallaxDisabled={this.layout?.width > 1740}
|
|
@@ -304,7 +303,7 @@ export class TvOSCellComponent extends React.Component<Props, State> {
|
|
|
304
303
|
index,
|
|
305
304
|
CellRenderer,
|
|
306
305
|
item,
|
|
307
|
-
focused: this.props.focused || selectedCell || focused,
|
|
306
|
+
focused: !!(this.props.focused || selectedCell || focused),
|
|
308
307
|
scrollTo: this.scrollTo,
|
|
309
308
|
behavior,
|
|
310
309
|
}}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
+
import * as R from "ramda";
|
|
2
3
|
|
|
3
4
|
import { BaseFocusable } from "../BaseFocusable";
|
|
4
5
|
import { focusManager } from "@applicaster/zapp-react-native-utils/appUtils/focusManager";
|
|
@@ -41,6 +42,8 @@ class Focusable extends BaseFocusable<Props> {
|
|
|
41
42
|
this.resetLongPressTimeout = this.resetLongPressTimeout.bind(this);
|
|
42
43
|
this.longPress = this.longPress.bind(this);
|
|
43
44
|
this.press = this.press.bind(this);
|
|
45
|
+
this.focus = this.focus.bind(this);
|
|
46
|
+
this.blur = this.blur.bind(this);
|
|
44
47
|
}
|
|
45
48
|
|
|
46
49
|
/**
|
|
@@ -52,6 +55,37 @@ class Focusable extends BaseFocusable<Props> {
|
|
|
52
55
|
return this.preferredFocus();
|
|
53
56
|
}
|
|
54
57
|
|
|
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
|
+
|
|
55
89
|
startLongPressTimeout() {
|
|
56
90
|
this.longPressTimeout = setTimeout(() => {
|
|
57
91
|
this.longPress(null);
|
|
@@ -68,6 +102,26 @@ class Focusable extends BaseFocusable<Props> {
|
|
|
68
102
|
}
|
|
69
103
|
}
|
|
70
104
|
|
|
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
|
+
|
|
71
125
|
/**
|
|
72
126
|
* will invoke the underlying component's press method
|
|
73
127
|
* @param {Object} keyEvent
|
|
@@ -16,6 +16,8 @@ function noop() {}
|
|
|
16
16
|
type Props = {
|
|
17
17
|
id: string;
|
|
18
18
|
groupId: string;
|
|
19
|
+
willReceiveFocus?: (nativeEvent: React.SyntheticEvent) => void;
|
|
20
|
+
hasReceivedFocus?: (nativeEvent: React.SyntheticEvent) => void;
|
|
19
21
|
onPress?: (nativeEvent: React.SyntheticEvent) => void;
|
|
20
22
|
onFocus?: (nativeEvent: React.SyntheticEvent) => void;
|
|
21
23
|
onBlur?: (nativeEvent: React.SyntheticEvent) => void;
|
|
@@ -35,6 +37,8 @@ type Props = {
|
|
|
35
37
|
forceFocus?: boolean;
|
|
36
38
|
initialFocus?: boolean;
|
|
37
39
|
onLayout?: (e: any) => void;
|
|
40
|
+
offsetUpdater: (arg1: string, arg2: number) => number;
|
|
41
|
+
style: any;
|
|
38
42
|
};
|
|
39
43
|
|
|
40
44
|
export class Focusable extends BaseFocusable<Props> {
|
|
@@ -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) {
|
|
@@ -89,6 +91,7 @@ function FocusableListComponent<ItemT>(props: Props<ItemT>, ref) {
|
|
|
89
91
|
// eslint-disable-next-line unused-imports/no-unused-vars
|
|
90
92
|
onAllComponentsLoaded, // TODO: re-implement it
|
|
91
93
|
omitPropsPropagation = [],
|
|
94
|
+
useScrollView = false,
|
|
92
95
|
} = props;
|
|
93
96
|
|
|
94
97
|
useCheckItemIdsForUnique({ componentId: props.id, items: data });
|
|
@@ -274,6 +277,7 @@ function FocusableListComponent<ItemT>(props: Props<ItemT>, ref) {
|
|
|
274
277
|
"initialFocusDirection",
|
|
275
278
|
"withStateMemory",
|
|
276
279
|
"useSequentialLoading",
|
|
280
|
+
"useScrollView",
|
|
277
281
|
...omitPropsPropagation,
|
|
278
282
|
],
|
|
279
283
|
R.__
|
|
@@ -296,15 +300,27 @@ function FocusableListComponent<ItemT>(props: Props<ItemT>, ref) {
|
|
|
296
300
|
return (
|
|
297
301
|
// @ts-ignore
|
|
298
302
|
<ChildrenFocusDeactivatorView flex={1}>
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
303
|
+
{useScrollView ? (
|
|
304
|
+
<FocusableScrollView
|
|
305
|
+
ref={ref}
|
|
306
|
+
{...getFlatListProps(props)}
|
|
307
|
+
onEndReached={onEndReached}
|
|
308
|
+
initialNumToRender={initialNumToRender}
|
|
309
|
+
renderItem={renderItem}
|
|
310
|
+
focused={focused}
|
|
311
|
+
data={data}
|
|
312
|
+
/>
|
|
313
|
+
) : (
|
|
314
|
+
<FlatList
|
|
315
|
+
focusable={false}
|
|
316
|
+
scrollEnabled={false}
|
|
317
|
+
ref={ref}
|
|
318
|
+
{...getFlatListProps(props)}
|
|
319
|
+
renderItem={renderItem}
|
|
320
|
+
onEndReached={onEndReached}
|
|
321
|
+
initialNumToRender={initialNumToRender}
|
|
322
|
+
/>
|
|
323
|
+
)}
|
|
308
324
|
</ChildrenFocusDeactivatorView>
|
|
309
325
|
);
|
|
310
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,10 +1,12 @@
|
|
|
1
1
|
import { requireNativeComponent, View } from "react-native";
|
|
2
2
|
import { isAppleTV } from "@applicaster/zapp-react-native-ui-components/Helpers/Platform";
|
|
3
3
|
|
|
4
|
+
// @TODO define the types
|
|
4
5
|
export const FocusableGroupNative = isAppleTV()
|
|
5
|
-
? requireNativeComponent("FocusableGroupViewModule"
|
|
6
|
+
? requireNativeComponent<any>("FocusableGroupViewModule")
|
|
6
7
|
: View;
|
|
7
8
|
|
|
9
|
+
// @TODO define the types
|
|
8
10
|
export const FocusableItemNative = isAppleTV()
|
|
9
|
-
? requireNativeComponent("FocusableViewModule"
|
|
11
|
+
? requireNativeComponent<any>("FocusableViewModule")
|
|
10
12
|
: View;
|
|
@@ -5,18 +5,18 @@ import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
|
|
|
5
5
|
|
|
6
6
|
type ComponentProps = {
|
|
7
7
|
component: {
|
|
8
|
-
id: string
|
|
9
|
-
rules: { item_limit: number
|
|
10
|
-
component_type: string
|
|
8
|
+
id: string;
|
|
9
|
+
rules: { item_limit: number; component_cells_selectable: boolean };
|
|
10
|
+
component_type: string;
|
|
11
11
|
styles: {
|
|
12
|
-
cell_style: string
|
|
13
|
-
header: { visible: boolean }
|
|
14
|
-
component_cells_selectable: boolean
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
zappPipesData: { url: string
|
|
18
|
-
selected: boolean
|
|
19
|
-
assets: { menu_button: string }
|
|
12
|
+
cell_style: string;
|
|
13
|
+
header: { visible: boolean };
|
|
14
|
+
component_cells_selectable: boolean;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
zappPipesData: { url: string; data: { title: string } };
|
|
18
|
+
selected: boolean;
|
|
19
|
+
assets: { menu_button: string };
|
|
20
20
|
};
|
|
21
21
|
|
|
22
22
|
function withAnalytics(Component) {
|
package/README.md
CHANGED
|
@@ -78,17 +78,6 @@ export default MyAwesomeComponent;
|
|
|
78
78
|
export { MyAwesomeComponent } from "./MyAwesomeComponent";
|
|
79
79
|
```
|
|
80
80
|
|
|
81
|
-
We provide flow types for component props - please use it to make sure your component will be compliant
|
|
82
|
-
|
|
83
|
-
```javascript
|
|
84
|
-
import * as React from "react";
|
|
85
|
-
import type { ZappComponentProps } from "../../flow-types"; // in the root of the project
|
|
86
|
-
|
|
87
|
-
export class MyComponent extends React.Component<ZappComponentProps> {
|
|
88
|
-
/* ... */
|
|
89
|
-
}
|
|
90
|
-
```
|
|
91
|
-
|
|
92
81
|
## Connecting my component to Zapp Pipes
|
|
93
82
|
|
|
94
83
|
Zapp App components rely on [Zapp-Pipes](https://github.com/applicaster/zapp-pipes) to load data.
|
|
@@ -100,10 +89,9 @@ In order to connect your component to ZappPipes, you simply need to use the `Zap
|
|
|
100
89
|
import * as React from "react";
|
|
101
90
|
import { View, Text } from "react-native";
|
|
102
91
|
import { zappPipesDataConnector } from "../ZappPipesDataConnector";
|
|
103
|
-
import type { ZappComponentProps } from "../../flow-types";
|
|
104
92
|
|
|
105
93
|
@zappPipesDataConnector
|
|
106
|
-
export class MyComponent extends React.Component
|
|
94
|
+
export class MyComponent extends React.Component {
|
|
107
95
|
renderComponent = (data) => {
|
|
108
96
|
return (
|
|
109
97
|
<View>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@applicaster/zapp-react-native-ui-components",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "13.0.0-alpha.1338463240",
|
|
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,10 +34,10 @@
|
|
|
34
34
|
"redux-mock-store": "^1.5.3"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@applicaster/applicaster-types": "
|
|
38
|
-
"@applicaster/zapp-react-native-bridge": "
|
|
39
|
-
"@applicaster/zapp-react-native-redux": "
|
|
40
|
-
"@applicaster/zapp-react-native-utils": "
|
|
37
|
+
"@applicaster/applicaster-types": "13.0.0-alpha.1338463240",
|
|
38
|
+
"@applicaster/zapp-react-native-bridge": "13.0.0-alpha.1338463240",
|
|
39
|
+
"@applicaster/zapp-react-native-redux": "13.0.0-alpha.1338463240",
|
|
40
|
+
"@applicaster/zapp-react-native-utils": "13.0.0-alpha.1338463240",
|
|
41
41
|
"promise": "^8.3.0",
|
|
42
42
|
"react-router-native": "^5.1.2",
|
|
43
43
|
"url": "^0.11.0",
|