@applicaster/zapp-react-native-ui-components 12.0.0-alpha.7539319320 → 12.0.0-alpha.7604714363

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.
Files changed (148) hide show
  1. package/Components/AudioPlayer/AudioPlayer.tsx +9 -1
  2. package/Components/AudioPlayer/__tests__/__snapshots__/audioPlayer.test.js.snap +5 -0
  3. package/Components/AudioPlayer/helpers.tsx +1 -0
  4. package/Components/BaseFocusable/index.tsx +3 -3
  5. package/Components/Cell/Cell.tsx +12 -7
  6. package/Components/Cell/CellWithFocusable.tsx +40 -19
  7. package/Components/Cell/TvOSCellComponent.tsx +5 -9
  8. package/Components/ErrorScreen/index.tsx +1 -1
  9. package/Components/Focusable/Focusable.tsx +6 -1
  10. package/Components/FocusableCell/index.tsx +7 -7
  11. package/Components/FocusableGroup/index.tsx +11 -2
  12. package/Components/FocusableList/FocusableChild.ts +24 -14
  13. package/Components/FocusableList/FocusableItem.tsx +1 -1
  14. package/Components/FocusableList/FocusableListItemWrapper.tsx +76 -0
  15. package/Components/FocusableList/hooks/useCellState.android.ts +3 -1
  16. package/Components/FocusableList/index.tsx +26 -25
  17. package/Components/GeneralContentScreen/GeneralContentScreen.tsx +11 -8
  18. package/Components/GeneralContentScreen/utils/__tests__/useCurationAPI.test.js +13 -6
  19. package/Components/GeneralContentScreen/utils/useCurationAPI.ts +1 -1
  20. package/Components/HandlePlayable/HandlePlayable.tsx +4 -1
  21. package/Components/HookRenderer/index.tsx +1 -6
  22. package/Components/Image/Image.android.tsx +3 -26
  23. package/Components/Image/Image.tsx +18 -2
  24. package/Components/Image/utils/index.ts +39 -0
  25. package/Components/Layout/TV/ScreenContainer.tsx +79 -5
  26. package/Components/Layout/TV/ScreenLayoutContextProvider.tsx +2 -2
  27. package/Components/Layout/TV/__tests__/ScreenContainer.test.tsx +38 -28
  28. package/Components/Layout/TV/__tests__/__snapshots__/ScreenContainer.test.tsx.snap +25 -13
  29. package/Components/Layout/TV/__tests__/__snapshots__/index.test.tsx.snap +10 -50
  30. package/Components/Layout/TV/__tests__/index.test.tsx +20 -108
  31. package/Components/Layout/TV/index.tsx +15 -84
  32. package/Components/Layout/TV/index.web.tsx +33 -0
  33. package/Components/Layout/index.tsx +15 -11
  34. package/Components/MasterCell/DefaultComponents/ActionButton.tsx +2 -0
  35. package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +88 -113
  36. package/Components/MasterCell/DefaultComponents/CellBadge/index.tsx +21 -8
  37. package/Components/MasterCell/DefaultComponents/DynamicBadge.tsx +4 -2
  38. package/Components/MasterCell/DefaultComponents/FocusableView/index.tsx +4 -1
  39. package/Components/MasterCell/DefaultComponents/Image/Image.android.tsx +9 -26
  40. package/Components/MasterCell/DefaultComponents/Image/Image.ios.tsx +5 -3
  41. package/Components/MasterCell/DefaultComponents/Image/hooks/useImageSize.ts +65 -0
  42. package/Components/MasterCell/DefaultComponents/ImageContainer/index.tsx +5 -1
  43. package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +17 -2
  44. package/Components/MasterCell/DefaultComponents/SecondaryImage/Image.tsx +3 -2
  45. package/Components/MasterCell/DefaultComponents/Text/index.tsx +14 -2
  46. package/Components/MasterCell/DefaultComponents/Toggle.tsx +5 -1
  47. package/Components/MasterCell/DefaultComponents/__tests__/image.test.js +1 -1
  48. package/Components/MasterCell/DefaultComponents/__tests__/text.test.tsx +24 -9
  49. package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.android.tsx +4 -1
  50. package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.tsx +10 -2
  51. package/Components/MasterCell/SharedUI/CollapsibleTextContainer/index.ts +6 -10
  52. package/Components/MasterCell/hooks/useAsyncRendering/index.ts +1 -1
  53. package/Components/MasterCell/utils/__tests__/insertBetween.test.js +9 -0
  54. package/Components/MasterCell/utils/index.ts +33 -35
  55. package/Components/MeasurmentsPortal/BasicMeasurementsPortal.tsx +11 -7
  56. package/Components/MeasurmentsPortal/MeasurementsPortal.tsx +10 -1
  57. package/Components/ModalComponent/BottomSheetModalContent.tsx +121 -0
  58. package/Components/ModalComponent/Button/Item.tsx +82 -0
  59. package/Components/ModalComponent/Button/ItemIcon.tsx +60 -0
  60. package/Components/ModalComponent/Button/ItemLabel.tsx +101 -0
  61. package/Components/ModalComponent/Button/assets.ts +4 -0
  62. package/Components/ModalComponent/Button/index.tsx +111 -0
  63. package/Components/ModalComponent/Header/CloseButton.tsx +59 -0
  64. package/Components/ModalComponent/Header/Title.tsx +70 -0
  65. package/Components/ModalComponent/Header/assets.ts +4 -0
  66. package/Components/ModalComponent/Header/index.tsx +71 -0
  67. package/Components/ModalComponent/Header/utils.ts +79 -0
  68. package/Components/ModalComponent/index.tsx +38 -0
  69. package/Components/ModalComponent/utils.ts +126 -0
  70. package/Components/Navigator/StackNavigator.tsx +57 -0
  71. package/Components/Navigator/index.ts +1 -0
  72. package/Components/PlayerContainer/PlayerContainer.tsx +66 -31
  73. package/Components/PlayerContainer/ProgramInfo/index.tsx +3 -1
  74. package/Components/PlayerImageBackground/index.tsx +34 -8
  75. package/Components/River/ComponentsMap/ComponentsMap.tsx +97 -2
  76. package/Components/River/RefreshControl.tsx +55 -1
  77. package/Components/River/River.tsx +8 -5
  78. package/Components/River/TV/River.tsx +2 -25
  79. package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +1 -1
  80. package/Components/River/__tests__/componentsMap.test.js +7 -0
  81. package/Components/River/useScreenConfiguration.ts +2 -2
  82. package/Components/RouteManager/__tests__/__snapshots__/routeManager.test.js.snap +1 -0
  83. package/Components/RouteManager/__tests__/routeManager.test.js +9 -2
  84. package/Components/RouteManager/index.tsx +4 -2
  85. package/Components/Screen/TV/hooks/index.ts +3 -0
  86. package/Components/Screen/TV/hooks/useInitialFocus.ts +70 -0
  87. package/Components/Screen/TV/hooks/useRestoreFocus.ts +22 -0
  88. package/Components/Screen/TV/index.web.tsx +165 -0
  89. package/Components/Screen/__tests__/Screen.test.tsx +91 -74
  90. package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +24 -6
  91. package/Components/Screen/__tests__/navigationHandler.test.ts +1 -178
  92. package/Components/Screen/index.tsx +110 -54
  93. package/Components/Screen/navigationHandler.ts +5 -82
  94. package/Components/ScreenResolver/index.tsx +4 -2
  95. package/Components/Tabs/TV/styles.ts +1 -1
  96. package/Components/TrackedView/index.tsx +6 -3
  97. package/Components/Transitioner/Scene.tsx +32 -26
  98. package/Components/Transitioner/Transitioner.tsx +21 -28
  99. package/Components/Transitioner/__tests__/__snapshots__/Scene.test.js.snap +12 -4
  100. package/Components/Transitioner/__tests__/__snapshots__/transitioner.test.js.snap +1 -0
  101. package/Components/VideoLive/LiveImageManager.ts +70 -14
  102. package/Components/VideoLive/PlayerLiveImageComponent.tsx +81 -72
  103. package/Components/VideoLive/__tests__/PlayerLiveImageComponent.test.tsx +45 -3
  104. package/Components/VideoLive/__tests__/__snapshots__/PlayerLiveImageComponent.test.tsx.snap +18 -28
  105. package/Components/VideoModal/ModalAnimation/AnimatedPlayerModalWrapper.tsx +78 -0
  106. package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +354 -0
  107. package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.tsx +161 -0
  108. package/Components/VideoModal/ModalAnimation/AnimationComponent.tsx +455 -0
  109. package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +162 -0
  110. package/Components/VideoModal/ModalAnimation/index.ts +15 -0
  111. package/Components/VideoModal/ModalAnimation/utils.ts +301 -0
  112. package/Components/VideoModal/PlayerDetails.tsx +6 -3
  113. package/Components/VideoModal/PlayerWrapper.tsx +81 -49
  114. package/Components/VideoModal/VideoModal.tsx +23 -40
  115. package/Components/VideoModal/__tests__/PlayerDetails.test.tsx +0 -2
  116. package/Components/VideoModal/__tests__/PlayerWrapper.test.tsx +12 -1
  117. package/Components/VideoModal/__tests__/__snapshots__/PlayerWrapper.test.tsx.snap +266 -166
  118. package/Components/VideoModal/hooks/useModalSize.ts +10 -12
  119. package/Components/VideoModal/utils.ts +8 -5
  120. package/Components/Viewport/ViewportAware/__tests__/viewportAware.test.js +26 -21
  121. package/Components/Viewport/ViewportTracker/ViewportTracker.tsx +31 -40
  122. package/Components/Viewport/ViewportTracker/__tests__/viewportTracker.test.js +4 -5
  123. package/Components/Viewport/ViewportTracker/index.tsx +1 -0
  124. package/Components/Viewport/utils.js +1 -1
  125. package/Components/ZappUIComponent/index.tsx +22 -12
  126. package/Components/index.js +2 -0
  127. package/Contexts/FocusableGroupContext/FocusableGroupContext.ts +3 -0
  128. package/Contexts/FocusableGroupContext/FocusableGroupContextProvider.tsx +16 -0
  129. package/Contexts/FocusableGroupContext/__tests__/FocusableGroupContextProvider.test.tsx +22 -0
  130. package/Contexts/FocusableGroupContext/__tests__/withFocusableContext.test.tsx +26 -0
  131. package/Contexts/FocusableGroupContext/index.ts +7 -0
  132. package/Contexts/FocusableGroupContext/withFocusableContext.tsx +17 -0
  133. package/Contexts/MenuToggleContext/{index.js → index.ts} +16 -0
  134. package/Contexts/ScreenContext/__tests__/__snapshots__/index.test.tsx.snap +3 -0
  135. package/Contexts/ScreenContext/__tests__/index.test.tsx +27 -0
  136. package/Contexts/ScreenContext/index.tsx +95 -29
  137. package/Contexts/ScreenDataContext/index.tsx +20 -0
  138. package/Contexts/ZappPipesContext/ZappPipesEntryContext.ts +1 -1
  139. package/Decorators/ComponentsMapWrapper/withComponentsMapProvider.tsx +7 -2
  140. package/Decorators/RiverResolver/index.tsx +0 -4
  141. package/Decorators/index.tsx +0 -4
  142. package/events/index.ts +1 -0
  143. package/package.json +5 -6
  144. package/Components/Layout/TV/TransitionOverlay.tsx +0 -64
  145. package/Components/VideoModal/hooks/usePosition.ts +0 -50
  146. /package/Components/FocusableGroup/{index.android.js → index.android.ts} +0 -0
  147. /package/Components/FocusableGroup/{index.ios.js → index.ios.ts} +0 -0
  148. /package/Contexts/{index.js → index.ts} +0 -0
@@ -23,6 +23,7 @@ type Props = {
23
23
  audio_player_summary_color?: string;
24
24
  audio_player_rtl?: boolean;
25
25
  magic_background?: boolean;
26
+ audio_player_background_image_query?: string;
26
27
  start_time?: string;
27
28
  end_time?: string;
28
29
  };
@@ -33,6 +34,7 @@ type Props = {
33
34
  audio_player_summary_color?: string;
34
35
  audio_player_rtl?: string;
35
36
  magic_background?: string;
37
+ audio_player_background_image_query?: string;
36
38
  audio_player_background_image?: string;
37
39
  audio_player_artwork_aspect_ratio?: string;
38
40
  lg_tv_audio_player_title_font_family?: string;
@@ -73,6 +75,11 @@ export function AudioPlayer(props: Props) {
73
75
  const channelIcon = getProp("audio_player_channel_icon");
74
76
  const rtlFlag = getProp("audio_player_rtl");
75
77
  const magicBackground = getProp("magic_background");
78
+
79
+ const audioPlayerBackgroundImageQuery = getProp(
80
+ "audio_player_background_image_query"
81
+ );
82
+
76
83
  const isRTL = rtlFlag === "1" || rtlFlag === "true" || rtlFlag === true;
77
84
 
78
85
  const titleFontFamily = getProp(
@@ -149,7 +156,8 @@ export function AudioPlayer(props: Props) {
149
156
  runTimeFontSize,
150
157
  artworkAspectRatio,
151
158
  channelIcon,
152
- magicBackground
159
+ magicBackground,
160
+ audioPlayerBackgroundImageQuery,
153
161
  };
154
162
  }, [getProp]);
155
163
 
@@ -5,6 +5,7 @@ exports[`<AudioPlayer /> renders correctly 1`] = `
5
5
  config={
6
6
  Object {
7
7
  "artworkAspectRatio": "1:1",
8
+ "audioPlayerBackgroundImageQuery": "",
8
9
  "backgroundColor": "black",
9
10
  "backgroundImage": "https://example.com",
10
11
  "channelIcon": "https://example.com",
@@ -26,6 +27,7 @@ exports[`<AudioPlayer /> renders correctly 1`] = `
26
27
  config={
27
28
  Object {
28
29
  "artworkAspectRatio": "1:1",
30
+ "audioPlayerBackgroundImageQuery": "",
29
31
  "backgroundColor": "black",
30
32
  "backgroundImage": "https://example.com",
31
33
  "channelIcon": "https://example.com",
@@ -47,6 +49,7 @@ exports[`<AudioPlayer /> renders correctly 1`] = `
47
49
  config={
48
50
  Object {
49
51
  "artworkAspectRatio": "1:1",
52
+ "audioPlayerBackgroundImageQuery": "",
50
53
  "backgroundColor": "black",
51
54
  "backgroundImage": "https://example.com",
52
55
  "channelIcon": "https://example.com",
@@ -68,6 +71,7 @@ exports[`<AudioPlayer /> renders correctly 1`] = `
68
71
  config={
69
72
  Object {
70
73
  "artworkAspectRatio": "1:1",
74
+ "audioPlayerBackgroundImageQuery": "",
71
75
  "backgroundColor": "black",
72
76
  "backgroundImage": "https://example.com",
73
77
  "channelIcon": "https://example.com",
@@ -96,6 +100,7 @@ exports[`<AudioPlayer /> renders correctly 1`] = `
96
100
  config={
97
101
  Object {
98
102
  "artworkAspectRatio": "1:1",
103
+ "audioPlayerBackgroundImageQuery": "",
99
104
  "backgroundColor": "black",
100
105
  "backgroundImage": "https://example.com",
101
106
  "channelIcon": "https://example.com",
@@ -5,6 +5,7 @@ const defaults = {
5
5
  audio_player_artwork_aspect_ratio: "1:1",
6
6
  audio_player_rtl: false,
7
7
  magic_background: false,
8
+ audio_player_background_image_query: "",
8
9
  };
9
10
 
10
11
  export function getPropertyFromEntryOrConfig({ entry, plugin_configuration }) {
@@ -66,14 +66,14 @@ export class BaseFocusable<
66
66
  const { initialFocus } = this.props;
67
67
 
68
68
  if (initialFocus) {
69
- focusManager.setupInitialFocus(this.getId());
69
+ focusManager.setFocus(this.getId());
70
70
  }
71
71
  }
72
72
 
73
73
  getId(props?: { id: string; groupId: string }) {
74
- const { id, groupId = null } = props || this.props;
74
+ const { id } = props || this.props;
75
75
 
76
- return groupId ? `${groupId}-${id}` : id;
76
+ return id;
77
77
  }
78
78
 
79
79
  getRect() {
@@ -1,5 +1,5 @@
1
1
  import * as React from "react";
2
- import * as R from "ramda";
2
+ import { join, path, merge } from "ramda";
3
3
  import { View } from "react-native";
4
4
 
5
5
  import { Focusable } from "@applicaster/zapp-react-native-ui-components/Components/Focusable";
@@ -11,6 +11,7 @@ import { noop } from "@applicaster/zapp-react-native-utils/functionUtils";
11
11
 
12
12
  import { CellWithFocusable } from "./CellWithFocusable";
13
13
  import { BaseFocusable } from "../BaseFocusable";
14
+ import { AccessibilityManager } from "@applicaster/zapp-react-native-utils/appUtils/accessibilityManager";
14
15
 
15
16
  type Props = {
16
17
  item: {
@@ -105,8 +106,8 @@ export class CellComponent extends React.Component<Props> {
105
106
  onPress(item, index);
106
107
  } else {
107
108
  const { item, navigator, component, appData } = this.props;
108
- const itemTargetScreen = R.path(["screen_type"], item);
109
- const componentTargetScreen = R.path(["data", "target"], component);
109
+ const itemTargetScreen = path(["screen_type"], item);
110
+ const componentTargetScreen = path(["data", "target"], component);
110
111
  const itemType = getItemType(item, appData?.layoutVersion);
111
112
 
112
113
  sendSelectCellEvent(item, component, item.title, index);
@@ -116,7 +117,7 @@ export class CellComponent extends React.Component<Props> {
116
117
  componentTargetScreen &&
117
118
  !itemTargetScreen
118
119
  ) {
119
- navigator.push(R.merge(item, { screen_type: componentTargetScreen }));
120
+ navigator.push(merge(item, { screen_type: componentTargetScreen }));
120
121
 
121
122
  return;
122
123
  }
@@ -139,6 +140,10 @@ export class CellComponent extends React.Component<Props> {
139
140
  shouldScrollVertically?.(mouse, focusable, id, title)
140
141
  ) {
141
142
  this.scrollVertically(focusable);
143
+
144
+ const accessibilityManager = AccessibilityManager.getInstance();
145
+ // Text from DSP should be localized already
146
+ accessibilityManager.readText({ text: title });
142
147
  }
143
148
  }
144
149
  }
@@ -202,12 +207,12 @@ export class CellComponent extends React.Component<Props> {
202
207
  groupId,
203
208
  isFocusable = true,
204
209
  skipFocusManagerRegistration,
205
- behavior
210
+ behavior,
206
211
  } = this.props;
207
212
 
208
213
  const { id } = item;
209
214
 
210
- const focusableId = R.join("-", [component?.id, id, index]);
215
+ const focusableId = join("-", [component?.id, id, index]);
211
216
 
212
217
  const handleFocus = (focusable, mouse) => {
213
218
  const focusFn = onFocus || noop;
@@ -263,7 +268,7 @@ export class CellComponent extends React.Component<Props> {
263
268
  item,
264
269
  focused: this.isCellFocused(focused),
265
270
  scrollTo: this.scrollToIndex(event),
266
- behavior
271
+ behavior,
267
272
  }}
268
273
  />
269
274
  );
@@ -3,15 +3,25 @@ import * as React from "react";
3
3
  import { focusManager } from "@applicaster/zapp-react-native-utils/appUtils/focusManager";
4
4
  import * as FOCUS_EVENTS from "@applicaster/zapp-react-native-utils/appUtils/focusManager/events";
5
5
  import { noop } from "@applicaster/zapp-react-native-utils/functionUtils";
6
+ import { toBooleanWithDefaultFalse } from "@applicaster/zapp-react-native-utils/booleanUtils";
7
+
6
8
  import { isAppleTV } from "../../Helpers/Platform";
7
9
  import { useCellState } from "../MasterCell/utils";
8
10
 
9
- const useCellFocusedState = (groupId: string, id: string) => {
11
+ const useCellFocusedState = (
12
+ skipFocusManagerRegistration: boolean,
13
+ groupId: string,
14
+ id: string
15
+ ) => {
10
16
  const [currentCellFocused, setCurrentCellFocused] = React.useState(false);
11
17
 
12
18
  React.useEffect(() => {
13
- const isGroupItemFocused = () =>
14
- setCurrentCellFocused(focusManager.isGroupItemFocused(groupId, id));
19
+ const isGroupItemFocused = () => {
20
+ if (!skipFocusManagerRegistration) {
21
+ const isFocused = focusManager.isGroupItemFocused(groupId, id);
22
+ setCurrentCellFocused(isFocused);
23
+ }
24
+ };
15
25
 
16
26
  const handler = () => {
17
27
  // tvOS hack for properly checking focus
@@ -29,24 +39,24 @@ const useCellFocusedState = (groupId: string, id: string) => {
29
39
  return () => {
30
40
  focusManager.removeHandler(FOCUS_EVENTS.FOCUS, handler);
31
41
  };
32
- }, [groupId]);
42
+ }, [groupId, skipFocusManagerRegistration]);
33
43
 
34
44
  return currentCellFocused;
35
45
  };
36
46
 
37
47
  type Props = {
38
- item: ZappEntry,
39
- CellRenderer: React.ComponentType<{ item: Object, state: string }>,
40
- id: string,
41
- groupId: string,
42
- onFocus: Function,
43
- index: number,
44
- scrollTo: Function,
45
- preferredFocus?: boolean,
46
- skipFocusManagerRegistration?: boolean,
47
- isFocusable?: boolean,
48
- behavior: Behavior,
49
- selected?: boolean,
48
+ item: ZappEntry;
49
+ CellRenderer: React.ComponentType<{ item: Object; state: string }>;
50
+ id: string;
51
+ groupId: string;
52
+ onFocus: Function;
53
+ index: number;
54
+ scrollTo: Function;
55
+ preferredFocus?: boolean;
56
+ skipFocusManagerRegistration?: boolean;
57
+ isFocusable?: boolean;
58
+ behavior: Behavior;
59
+ focused?: boolean;
50
60
  };
51
61
 
52
62
  export function CellWithFocusable(props: Props) {
@@ -62,11 +72,21 @@ export function CellWithFocusable(props: Props) {
62
72
  skipFocusManagerRegistration,
63
73
  isFocusable,
64
74
  behavior,
65
- selected
75
+ focused,
66
76
  } = props;
67
77
 
68
- const isFocused = useCellFocusedState(groupId, id);
69
- const state = useCellState({ id: item.id, behavior, focused: isFocused, selected });
78
+ const isFocused = useCellFocusedState(
79
+ skipFocusManagerRegistration,
80
+ groupId,
81
+ id
82
+ );
83
+
84
+ const state = useCellState({
85
+ id: item.id,
86
+ behavior,
87
+ focused: isFocused || toBooleanWithDefaultFalse(focused),
88
+ });
89
+
70
90
  const [focusedButtonId, setFocusedButtonId] = React.useState(undefined);
71
91
 
72
92
  // for horizontal scrolling
@@ -95,6 +115,7 @@ export function CellWithFocusable(props: Props) {
95
115
  preferredFocus={preferredFocus}
96
116
  skipFocusManagerRegistration={skipFocusManagerRegistration}
97
117
  isFocusable={isFocusable}
118
+ focused={focused}
98
119
  />
99
120
  );
100
121
  }
@@ -226,13 +226,7 @@ export class TvOSCellComponent extends React.Component<Props, State> {
226
226
 
227
227
  hasReceivedFocus() {}
228
228
 
229
- scrollTo(index) {
230
- // Workaround for HL autoscroll/autoloop feature, we scroll manually on tvOS.
231
- // Should be removed after refactoring autoscroll/autoloop feature
232
- if (this.props.component?.component_type !== "horizontal_list_qb") {
233
- this.props?.scrollToIndex?.(index);
234
- }
235
- }
229
+ scrollTo() {}
236
230
 
237
231
  render() {
238
232
  const {
@@ -249,7 +243,7 @@ export class TvOSCellComponent extends React.Component<Props, State> {
249
243
  hasReceivedFocus,
250
244
  groupId,
251
245
  isFocusable,
252
- behavior
246
+ behavior,
253
247
  } = this.props;
254
248
 
255
249
  const { id } = item;
@@ -279,7 +273,9 @@ export class TvOSCellComponent extends React.Component<Props, State> {
279
273
  index={index}
280
274
  scrollTo={this.scrollTo}
281
275
  preferredFocus={preferredFocus}
276
+ focused={this.props.focused}
282
277
  behavior={behavior}
278
+ isFocusable={isFocusable}
283
279
  />
284
280
  </View>
285
281
  );
@@ -310,7 +306,7 @@ export class TvOSCellComponent extends React.Component<Props, State> {
310
306
  item,
311
307
  focused: this.props.focused || selectedCell || focused,
312
308
  scrollTo: this.scrollTo,
313
- behavior
309
+ behavior,
314
310
  }}
315
311
  />
316
312
  );
@@ -3,7 +3,6 @@ import * as R from "ramda";
3
3
  import {
4
4
  View,
5
5
  StyleSheet,
6
- Image,
7
6
  Text,
8
7
  ViewStyle,
9
8
  TextStyle,
@@ -13,6 +12,7 @@ import {
13
12
  import { defaultAsset } from "./defaultAsset";
14
13
  import { platformSelect } from "@applicaster/zapp-react-native-utils/reactUtils";
15
14
  import { useNavigation } from "@applicaster/zapp-react-native-utils/reactHooks";
15
+ import { QBImage as Image } from "@applicaster/zapp-react-native-ui-components/Components/Image";
16
16
  import { Button } from "./Button";
17
17
 
18
18
  type ButtonProp = {
@@ -4,6 +4,7 @@ import * as R from "ramda";
4
4
  import { BaseFocusable } from "../BaseFocusable";
5
5
  import { focusManager } from "@applicaster/zapp-react-native-utils/appUtils/focusManager";
6
6
  import { LONG_KEY_PRESS_TIMEOUT } from "@applicaster/quick-brick-core/const";
7
+ import { withFocusableContext } from "../../Contexts/FocusableGroupContext/withFocusableContext";
7
8
 
8
9
  type Props = {
9
10
  initialFocus?: boolean;
@@ -23,7 +24,7 @@ type Props = {
23
24
  style?: React.CSSProperties;
24
25
  };
25
26
 
26
- export class Focusable extends BaseFocusable<Props> {
27
+ class Focusable extends BaseFocusable<Props> {
27
28
  isGroup: boolean;
28
29
  mouse: boolean;
29
30
  longPressTimeout = null;
@@ -198,3 +199,7 @@ export class Focusable extends BaseFocusable<Props> {
198
199
  );
199
200
  }
200
201
  }
202
+
203
+ const FocusableWithFocusableContext = withFocusableContext(Focusable);
204
+
205
+ export { FocusableWithFocusableContext as Focusable };
@@ -3,12 +3,12 @@ import { Platform } from "react-native";
3
3
  import { useCellState } from "../MasterCell/utils";
4
4
 
5
5
  type Props = {
6
- index: number,
7
- item: ZappEntry,
8
- focused: boolean,
9
- scrollTo?: (index: number) => void,
10
- CellRenderer: React.ComponentType<{ item: Object, state: string }>,
11
- behavior: Behavior
6
+ index: number;
7
+ item: ZappEntry;
8
+ focused: boolean;
9
+ scrollTo?: (index: number) => void;
10
+ CellRenderer: React.ComponentType<{ item: Object; state: string }>;
11
+ behavior: Behavior;
12
12
  };
13
13
 
14
14
  const isAndroid = Platform.OS === "android";
@@ -20,7 +20,7 @@ export function FocusableCell(props: Props) {
20
20
  CellRenderer,
21
21
  focused,
22
22
  scrollTo = null,
23
- behavior
23
+ behavior,
24
24
  } = props;
25
25
 
26
26
  const state = useCellState({ id: item.id, behavior, focused });
@@ -1,6 +1,8 @@
1
1
  import * as React from "react";
2
2
 
3
3
  import { BaseFocusable } from "../BaseFocusable";
4
+ import { FocusableGroupContextProvider } from "../../Contexts/FocusableGroupContext";
5
+ import { withFocusableContext } from "../../Contexts/FocusableGroupContext/withFocusableContext";
4
6
 
5
7
  type Props = {
6
8
  testID?: string;
@@ -26,9 +28,10 @@ type Props = {
26
28
  nextFocusLeft?: any;
27
29
  nextFocusRight?: any;
28
30
  onLayout?: any;
31
+ excludeFromFocusSearching?: boolean;
29
32
  };
30
33
 
31
- export class FocusableGroup extends BaseFocusable<Props> {
34
+ class FocusableGroup extends BaseFocusable<Props> {
32
35
  constructor(props) {
33
36
  super(props);
34
37
  this.isGroup = true;
@@ -81,8 +84,14 @@ export class FocusableGroup extends BaseFocusable<Props> {
81
84
  style={style}
82
85
  ref={this.ref}
83
86
  >
84
- {children}
87
+ <FocusableGroupContextProvider id={id}>
88
+ {children}
89
+ </FocusableGroupContextProvider>
85
90
  </div>
86
91
  );
87
92
  }
88
93
  }
94
+
95
+ const FocusableGroupWithFocusableContext = withFocusableContext(FocusableGroup);
96
+
97
+ export { FocusableGroupWithFocusableContext as FocusableGroup };
@@ -1,28 +1,38 @@
1
1
  import * as React from "react";
2
2
  import { isEqual } from "@applicaster/zapp-react-native-utils/equalUtils";
3
- import { FocusableContext } from "../Focusable/index.android";
3
+ import { useFocusable } from "../Focusable/index.android";
4
4
  import { useCellState } from "./hooks/useCellState.android";
5
5
 
6
- function FocusableChildComponent(props) {
7
- const context = React.useContext(FocusableContext);
8
-
9
- const { setIsFocusable } = context || {};
10
-
11
- const nextFocusDown = props.nextFocusDown ?? context.nextFocusDown;
12
- const nextFocusUp = props.nextFocusUp ?? context.nextFocusUp;
13
- const nextFocusRight = props.nextFocusRight ?? context.nextFocusRight;
14
- const nextFocusLeft = props.nextFocusLeft ?? context.nextFocusLeft;
15
-
16
- const parentFocus = React.useMemo(
6
+ /**
7
+ * returns parent focus from prop or from context
8
+ * */
9
+ const useParentFocus = (
10
+ { nextFocusDown, nextFocusUp, nextFocusRight, nextFocusLeft },
11
+ context
12
+ ) =>
13
+ React.useMemo(
17
14
  () => ({
15
+ nextFocusRight: nextFocusRight ?? context.nextFocusRight,
16
+ nextFocusDown: nextFocusDown ?? context.nextFocusDown,
17
+ nextFocusLeft: nextFocusLeft ?? context.nextFocusLeft,
18
+ nextFocusUp: nextFocusUp ?? context.nextFocusUp,
19
+ }),
20
+ [
18
21
  nextFocusRight,
19
22
  nextFocusDown,
20
23
  nextFocusLeft,
21
24
  nextFocusUp,
22
- }),
23
- [nextFocusRight, nextFocusDown, nextFocusLeft, nextFocusUp]
25
+ context.nextFocusLeft,
26
+ context.nextFocusRight,
27
+ context.nextFocusUp,
28
+ context.nextFocusDown,
29
+ ]
24
30
  );
25
31
 
32
+ function FocusableChildComponent(props) {
33
+ const { setIsFocusable, ...focusableContext } = useFocusable();
34
+ const parentFocus = useParentFocus(props, focusableContext);
35
+
26
36
  const onLoadFailed = React.useCallback(() => {
27
37
  setIsFocusable(false);
28
38
  }, []);
@@ -1,5 +1,5 @@
1
1
  import * as React from "react";
2
- import { Focusable } from "../Focusable";
2
+ import { Focusable } from "../Focusable/index.android";
3
3
  import { FocusableChild } from "./FocusableChild";
4
4
  import { noop } from "@applicaster/zapp-react-native-utils/functionUtils";
5
5
  import { IListRenderItem } from "./index";
@@ -0,0 +1,76 @@
1
+ import * as React from "react";
2
+ import { ListRenderItemInfo as IListRenderItemInfo } from "react-native";
3
+ import { FocusableItem } from "./FocusableItem";
4
+
5
+ export type IListRenderItem<ItemT> = (
6
+ info: IListRenderItemInfo<ItemT> & {
7
+ focused: boolean;
8
+ onLoadFinished: () => void;
9
+ onLoadFailed: () => void;
10
+ }
11
+ ) => React.ReactElement | null;
12
+
13
+ type Item = ZappEntry | ZappUIComponent | any;
14
+
15
+ type Props = {
16
+ item: Item;
17
+ onFocus: (
18
+ element: FocusManager.FocusableRef,
19
+ renderArgs: { item: Item; index: number },
20
+ direction: FocusManager.Android.FocusNavigationDirections
21
+ ) => void;
22
+ onListElementBlur?: (any, RenderItemProps, Direction) => void;
23
+ onListElementPress?: (any, RenderItemProps) => void;
24
+ onListElementLongPress?: (any, RenderItemProps) => void;
25
+ onListElementPressOut?: (any, RenderItemProps) => void;
26
+ focusableItemProps: any;
27
+ id: string;
28
+ index: number;
29
+ extraData: any;
30
+ extraChildrenData: any;
31
+ renderItem: IListRenderItem<any> | null | undefined;
32
+ getNextFocus: (index: number) => ParentFocus;
33
+ } & ParentFocus;
34
+
35
+ export const FocusableListItemWrapper = React.memo(
36
+ ({
37
+ onFocus,
38
+ onListElementBlur,
39
+ onListElementPress,
40
+ onListElementLongPress,
41
+ onListElementPressOut,
42
+ focusableItemProps,
43
+ id,
44
+ getNextFocus,
45
+ item,
46
+ index,
47
+ extraData,
48
+ extraChildrenData,
49
+ renderItem,
50
+ }: Props) => {
51
+ const nextFocus = getNextFocus(index);
52
+
53
+ return (
54
+ <FocusableItem
55
+ nextFocusDown={nextFocus.nextFocusDown}
56
+ nextFocusRight={nextFocus.nextFocusRight}
57
+ nextFocusLeft={nextFocus.nextFocusLeft}
58
+ nextFocusUp={nextFocus.nextFocusUp}
59
+ id={id}
60
+ focusableItemProps={focusableItemProps}
61
+ onFocus={onFocus}
62
+ item={item}
63
+ index={index}
64
+ renderItem={renderItem}
65
+ extraChildrenData={extraChildrenData}
66
+ extraData={extraData}
67
+ onListElementBlur={onListElementBlur}
68
+ onListElementPress={onListElementPress}
69
+ onListElementLongPress={onListElementLongPress}
70
+ onListElementPressOut={onListElementPressOut}
71
+ />
72
+ );
73
+ }
74
+ );
75
+
76
+ FocusableListItemWrapper.displayName = "FocusableListItemWrapper";
@@ -5,7 +5,9 @@ import { useFocusManager } from "@applicaster/zapp-react-native-utils/focusManag
5
5
  export const useCellState = (id: string) => {
6
6
  const focusManager = useFocusManager();
7
7
 
8
- const [currentCellFocused, setCurrentCellFocused] = React.useState(false);
8
+ const [currentCellFocused, setCurrentCellFocused] = React.useState(
9
+ focusManager.isFocused(id)
10
+ );
9
11
 
10
12
  React.useEffect(() => {
11
13
  const handler = (focusable) => {