@applicaster/zapp-react-native-ui-components 12.0.0-alpha.5035843303 → 12.0.0-alpha.5050333009

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 (150) hide show
  1. package/Components/AnimatedInOut/index.tsx +18 -10
  2. package/Components/AudioPlayer/AudioPlayer.tsx +9 -1
  3. package/Components/AudioPlayer/__tests__/__snapshots__/audioPlayer.test.js.snap +5 -0
  4. package/Components/AudioPlayer/helpers.tsx +1 -0
  5. package/Components/BaseFocusable/index.tsx +3 -3
  6. package/Components/Cell/Cell.tsx +16 -15
  7. package/Components/Cell/CellWithFocusable.tsx +18 -9
  8. package/Components/Cell/TvOSCellComponent.tsx +5 -5
  9. package/Components/ErrorScreen/index.tsx +1 -1
  10. package/Components/Focusable/Focusable.tsx +6 -1
  11. package/Components/FocusableCell/index.tsx +7 -7
  12. package/Components/FocusableGroup/index.tsx +11 -2
  13. package/Components/FocusableList/FocusableChild.ts +24 -14
  14. package/Components/FocusableList/FocusableItem.tsx +1 -1
  15. package/Components/FocusableList/FocusableListItemWrapper.tsx +76 -0
  16. package/Components/FocusableList/hooks/useCellState.android.ts +3 -1
  17. package/Components/FocusableList/index.tsx +26 -25
  18. package/Components/GeneralContentScreen/GeneralContentScreen.tsx +11 -8
  19. package/Components/GeneralContentScreen/utils/__tests__/useCurationAPI.test.js +13 -6
  20. package/Components/GeneralContentScreen/utils/useCurationAPI.ts +1 -1
  21. package/Components/HandlePlayable/HandlePlayable.tsx +4 -1
  22. package/Components/HookRenderer/index.tsx +1 -6
  23. package/Components/Image/Image.android.tsx +11 -2
  24. package/Components/Image/Image.tsx +18 -2
  25. package/Components/Image/utils/index.ts +39 -0
  26. package/Components/Layout/TV/ScreenContainer.tsx +79 -5
  27. package/Components/Layout/TV/ScreenLayoutContextProvider.tsx +2 -2
  28. package/Components/Layout/TV/__tests__/ScreenContainer.test.tsx +38 -28
  29. package/Components/Layout/TV/__tests__/__snapshots__/ScreenContainer.test.tsx.snap +25 -13
  30. package/Components/Layout/TV/__tests__/__snapshots__/index.test.tsx.snap +10 -50
  31. package/Components/Layout/TV/__tests__/index.test.tsx +20 -108
  32. package/Components/Layout/TV/index.tsx +15 -84
  33. package/Components/Layout/TV/index.web.tsx +33 -0
  34. package/Components/Layout/index.tsx +15 -11
  35. package/Components/MasterCell/DefaultComponents/ActionButton.tsx +2 -0
  36. package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +88 -113
  37. package/Components/MasterCell/DefaultComponents/CellBadge/index.tsx +21 -8
  38. package/Components/MasterCell/DefaultComponents/DynamicBadge.tsx +4 -2
  39. package/Components/MasterCell/DefaultComponents/Image/Image.android.tsx +9 -26
  40. package/Components/MasterCell/DefaultComponents/Image/Image.ios.tsx +5 -7
  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 +37 -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/SharedUI/CollapsibleTextContainer/index.ts +6 -10
  50. package/Components/MasterCell/hooks/useAsyncRendering/index.ts +1 -1
  51. package/Components/MasterCell/utils/index.ts +33 -35
  52. package/Components/MeasurmentsPortal/BasicMeasurementsPortal.tsx +11 -7
  53. package/Components/MeasurmentsPortal/MeasurementsPortal.tsx +10 -1
  54. package/Components/ModalComponent/BottomSheetModalContent.tsx +121 -0
  55. package/Components/ModalComponent/Button/Item.tsx +82 -0
  56. package/Components/ModalComponent/Button/ItemIcon.tsx +60 -0
  57. package/Components/ModalComponent/Button/ItemLabel.tsx +101 -0
  58. package/Components/ModalComponent/Button/assets.ts +4 -0
  59. package/Components/ModalComponent/Button/index.tsx +111 -0
  60. package/Components/ModalComponent/Header/CloseButton.tsx +59 -0
  61. package/Components/ModalComponent/Header/Title.tsx +70 -0
  62. package/Components/ModalComponent/Header/assets.ts +4 -0
  63. package/Components/ModalComponent/Header/index.tsx +71 -0
  64. package/Components/ModalComponent/Header/utils.ts +79 -0
  65. package/Components/ModalComponent/index.tsx +38 -0
  66. package/Components/ModalComponent/utils.ts +126 -0
  67. package/Components/NativeFocusables/{index.js → index.ts} +4 -2
  68. package/Components/Navigator/StackNavigator.tsx +57 -0
  69. package/Components/Navigator/index.ts +1 -0
  70. package/Components/PlayerContainer/ErrorDisplay/ErrorDisplay.tsx +4 -7
  71. package/Components/PlayerContainer/PlayerContainer.tsx +66 -31
  72. package/Components/PlayerContainer/ProgramInfo/index.tsx +3 -1
  73. package/Components/PlayerImageBackground/index.tsx +34 -8
  74. package/Components/River/ComponentsMap/ComponentsMap.tsx +97 -2
  75. package/Components/River/RefreshControl.tsx +55 -1
  76. package/Components/River/River.tsx +8 -5
  77. package/Components/River/TV/River.tsx +2 -25
  78. package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +1 -1
  79. package/Components/River/__tests__/componentsMap.test.js +7 -0
  80. package/Components/River/useScreenConfiguration.ts +5 -6
  81. package/Components/RouteManager/__tests__/__snapshots__/routeManager.test.js.snap +1 -0
  82. package/Components/RouteManager/__tests__/routeManager.test.js +9 -2
  83. package/Components/RouteManager/index.tsx +4 -2
  84. package/Components/Screen/TV/hooks/index.ts +3 -0
  85. package/Components/Screen/TV/hooks/useInitialFocus.ts +70 -0
  86. package/Components/Screen/TV/hooks/useRestoreFocus.ts +22 -0
  87. package/Components/Screen/TV/index.web.tsx +165 -0
  88. package/Components/Screen/__tests__/Screen.test.tsx +51 -36
  89. package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +24 -6
  90. package/Components/Screen/__tests__/navigationHandler.test.ts +1 -178
  91. package/Components/Screen/index.tsx +110 -54
  92. package/Components/Screen/navigationHandler.ts +5 -82
  93. package/Components/Screen/orientationHandler.ts +1 -1
  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 +41 -29
  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 +66 -0
  106. package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +355 -0
  107. package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.tsx +161 -0
  108. package/Components/VideoModal/ModalAnimation/AnimationComponent.tsx +456 -0
  109. package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +182 -0
  110. package/Components/VideoModal/ModalAnimation/index.ts +15 -0
  111. package/Components/VideoModal/ModalAnimation/utils.ts +302 -0
  112. package/Components/VideoModal/PlayerDetails.tsx +6 -3
  113. package/Components/VideoModal/PlayerWrapper.tsx +133 -100
  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 +15 -3
  117. package/Components/VideoModal/__tests__/__snapshots__/PlayerWrapper.test.tsx.snap +262 -170
  118. package/Components/VideoModal/hooks/useModalSize.ts +23 -16
  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/Analytics/{index.js → index.tsx} +11 -11
  140. package/Decorators/ComponentsMapWrapper/withComponentsMapProvider.tsx +7 -2
  141. package/Decorators/RiverResolver/index.tsx +0 -4
  142. package/Decorators/index.tsx +0 -4
  143. package/README.md +1 -13
  144. package/events/index.ts +1 -0
  145. package/package.json +5 -6
  146. package/Components/Layout/TV/TransitionOverlay.tsx +0 -64
  147. package/Components/VideoModal/hooks/usePosition.ts +0 -50
  148. /package/Components/FocusableGroup/{index.android.js → index.android.ts} +0 -0
  149. /package/Components/FocusableGroup/{index.ios.js → index.ios.ts} +0 -0
  150. /package/Contexts/{index.js → index.ts} +0 -0
@@ -1,6 +1,7 @@
1
1
  import * as React from "react";
2
2
  import { Animated, EasingFunction, StyleProp, ViewStyle } from "react-native";
3
-
3
+ import { usePrevious } from "@applicaster/zapp-react-native-utils/reactHooks/utils";
4
+ import { toBooleanWithDefaultFalse } from "@applicaster/zapp-react-native-utils/booleanUtils";
4
5
  import { noop } from "@applicaster/zapp-react-native-utils/functionUtils";
5
6
 
6
7
  type AnimatedInterpolatedStyle =
@@ -35,11 +36,16 @@ function getAnimation(config: AnimatedInConfig, phase: "in" | "out") {
35
36
  return config[phase] || config;
36
37
  }
37
38
 
38
- export function AnimatedInOut(props: Props) {
39
- const { visible, animationConfig, children } = props;
40
-
39
+ export function AnimatedInOut({
40
+ visible,
41
+ animationConfig,
42
+ staticStyles = {},
43
+ children,
44
+ }: Props) {
41
45
  const [animatedValue] = React.useState(new Animated.Value(visible ? 1 : 0));
42
- const [animating, setAnimating] = React.useState(null);
46
+ const [animating, setAnimating] = React.useState(undefined);
47
+
48
+ const previousVisible = usePrevious(toBooleanWithDefaultFalse(visible));
43
49
 
44
50
  function startAnimation(toValue, config) {
45
51
  if (animating) {
@@ -55,7 +61,7 @@ export function AnimatedInOut(props: Props) {
55
61
  delay,
56
62
  useNativeDriver: true,
57
63
  }).start(() => {
58
- setAnimating(null);
64
+ setAnimating(undefined);
59
65
  onAnimationEnd();
60
66
  });
61
67
 
@@ -63,12 +69,14 @@ export function AnimatedInOut(props: Props) {
63
69
  }
64
70
 
65
71
  React.useEffect(() => {
66
- if (visible) {
72
+ if (!previousVisible && visible) {
67
73
  startAnimation(1.0, getAnimation(animationConfig, "in"));
68
- } else {
74
+ }
75
+
76
+ if (previousVisible && !visible) {
69
77
  startAnimation(0.0, getAnimation(animationConfig, "out"));
70
78
  }
71
- }, [visible, animating]);
79
+ }, [visible, previousVisible]);
72
80
 
73
81
  const styles = visible
74
82
  ? getAnimation(animationConfig, "in").styles
@@ -77,7 +85,7 @@ export function AnimatedInOut(props: Props) {
77
85
  return (
78
86
  <Animated.View
79
87
  renderToHardwareTextureAndroid={animating}
80
- style={[styles(animatedValue), props?.staticStyles || {}]}
88
+ style={[styles(animatedValue), staticStyles]}
81
89
  >
82
90
  {children}
83
91
  </Animated.View>
@@ -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,12 +11,10 @@ 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
- item: {
17
- id: string;
18
- title: string;
19
- };
17
+ item: ZappEntry;
20
18
  index: number;
21
19
  shouldScrollHorizontally: (arg1: [any]) => boolean | null | undefined;
22
20
  shouldScrollVertically: (...args: any) => boolean | null | undefined;
@@ -32,10 +30,9 @@ type Props = {
32
30
  id: string;
33
31
  };
34
32
  selected?: boolean;
35
- CellRenderer: React.ComponentType<{
36
- item: any;
37
- state: string;
38
- }> & { hasFocusableInside: (item: Record<string, string>) => boolean };
33
+ CellRenderer: React.FunctionComponent<any> & {
34
+ hasFocusableInside: (item: ZappEntry) => boolean;
35
+ };
39
36
  preferredFocus: boolean;
40
37
  navigator: {
41
38
  push: (arg1: Record<any, any>) => void;
@@ -105,8 +102,8 @@ export class CellComponent extends React.Component<Props> {
105
102
  onPress(item, index);
106
103
  } else {
107
104
  const { item, navigator, component, appData } = this.props;
108
- const itemTargetScreen = R.path(["screen_type"], item);
109
- const componentTargetScreen = R.path(["data", "target"], component);
105
+ const itemTargetScreen = path(["screen_type"], item);
106
+ const componentTargetScreen = path(["data", "target"], component);
110
107
  const itemType = getItemType(item, appData?.layoutVersion);
111
108
 
112
109
  sendSelectCellEvent(item, component, item.title, index);
@@ -116,7 +113,7 @@ export class CellComponent extends React.Component<Props> {
116
113
  componentTargetScreen &&
117
114
  !itemTargetScreen
118
115
  ) {
119
- navigator.push(R.merge(item, { screen_type: componentTargetScreen }));
116
+ navigator.push(merge(item, { screen_type: componentTargetScreen }));
120
117
 
121
118
  return;
122
119
  }
@@ -139,6 +136,10 @@ export class CellComponent extends React.Component<Props> {
139
136
  shouldScrollVertically?.(mouse, focusable, id, title)
140
137
  ) {
141
138
  this.scrollVertically(focusable);
139
+
140
+ const accessibilityManager = AccessibilityManager.getInstance();
141
+ // Text from DSP should be localized already
142
+ accessibilityManager.readText({ text: title.toString() });
142
143
  }
143
144
  }
144
145
  }
@@ -202,12 +203,12 @@ export class CellComponent extends React.Component<Props> {
202
203
  groupId,
203
204
  isFocusable = true,
204
205
  skipFocusManagerRegistration,
205
- behavior
206
+ behavior,
206
207
  } = this.props;
207
208
 
208
209
  const { id } = item;
209
210
 
210
- const focusableId = R.join("-", [component?.id, id, index]);
211
+ const focusableId = join("-", [component?.id, id, index]);
211
212
 
212
213
  const handleFocus = (focusable, mouse) => {
213
214
  const focusFn = onFocus || noop;
@@ -263,7 +264,7 @@ export class CellComponent extends React.Component<Props> {
263
264
  item,
264
265
  focused: this.isCellFocused(focused),
265
266
  scrollTo: this.scrollToIndex(event),
266
- behavior
267
+ behavior,
267
268
  }}
268
269
  />
269
270
  );
@@ -8,12 +8,20 @@ import { toBooleanWithDefaultFalse } from "@applicaster/zapp-react-native-utils/
8
8
  import { isAppleTV } from "../../Helpers/Platform";
9
9
  import { useCellState } from "../MasterCell/utils";
10
10
 
11
- const useCellFocusedState = (groupId: string, id: string) => {
11
+ const useCellFocusedState = (
12
+ skipFocusManagerRegistration: boolean,
13
+ groupId: string,
14
+ id: string
15
+ ) => {
12
16
  const [currentCellFocused, setCurrentCellFocused] = React.useState(false);
13
17
 
14
18
  React.useEffect(() => {
15
- const isGroupItemFocused = () =>
16
- setCurrentCellFocused(focusManager.isGroupItemFocused(groupId, id));
19
+ const isGroupItemFocused = () => {
20
+ if (!skipFocusManagerRegistration) {
21
+ const isFocused = focusManager.isGroupItemFocused(groupId, id);
22
+ setCurrentCellFocused(isFocused);
23
+ }
24
+ };
17
25
 
18
26
  const handler = () => {
19
27
  // tvOS hack for properly checking focus
@@ -31,14 +39,14 @@ const useCellFocusedState = (groupId: string, id: string) => {
31
39
  return () => {
32
40
  focusManager.removeHandler(FOCUS_EVENTS.FOCUS, handler);
33
41
  };
34
- }, [groupId]);
42
+ }, [groupId, skipFocusManagerRegistration]);
35
43
 
36
44
  return currentCellFocused;
37
45
  };
38
46
 
39
47
  type Props = {
40
48
  item: ZappEntry;
41
- CellRenderer: React.ComponentType<{ item: Object; state: string }>;
49
+ CellRenderer: React.FunctionComponent<any>;
42
50
  id: string;
43
51
  groupId: string;
44
52
  onFocus: Function;
@@ -48,7 +56,6 @@ type Props = {
48
56
  skipFocusManagerRegistration?: boolean;
49
57
  isFocusable?: boolean;
50
58
  behavior: Behavior;
51
- selected?: boolean;
52
59
  focused?: boolean;
53
60
  };
54
61
 
@@ -65,17 +72,19 @@ export function CellWithFocusable(props: Props) {
65
72
  skipFocusManagerRegistration,
66
73
  isFocusable,
67
74
  behavior,
68
- selected,
69
75
  focused,
70
76
  } = props;
71
77
 
72
- const isFocused = useCellFocusedState(groupId, id);
78
+ const isFocused = useCellFocusedState(
79
+ skipFocusManagerRegistration,
80
+ groupId,
81
+ id
82
+ );
73
83
 
74
84
  const state = useCellState({
75
85
  id: item.id,
76
86
  behavior,
77
87
  focused: isFocused || toBooleanWithDefaultFalse(focused),
78
- selected,
79
88
  });
80
89
 
81
90
  const [focusedButtonId, setFocusedButtonId] = React.useState(undefined);
@@ -36,10 +36,9 @@ type Props = {
36
36
  component_type: string;
37
37
  };
38
38
  selected: boolean;
39
- CellRenderer: React.ComponentType<{
40
- item: any;
41
- state: string;
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;
@@ -226,7 +225,7 @@ export class TvOSCellComponent extends React.Component<Props, State> {
226
225
 
227
226
  hasReceivedFocus() {}
228
227
 
229
- scrollTo(index) {}
228
+ scrollTo() {}
230
229
 
231
230
  render() {
232
231
  const {
@@ -275,6 +274,7 @@ export class TvOSCellComponent extends React.Component<Props, State> {
275
274
  preferredFocus={preferredFocus}
276
275
  focused={this.props.focused}
277
276
  behavior={behavior}
277
+ isFocusable={isFocusable}
278
278
  />
279
279
  </View>
280
280
  );
@@ -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) => {