@applicaster/zapp-react-native-ui-components 15.1.0-rc.9 → 16.0.0-alpha.1833861900

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 (155) hide show
  1. package/Components/BackgroundImage/BackgroundImage.tsx +42 -0
  2. package/Components/BackgroundImage/BackgroundImage.tv.android.tsx +28 -0
  3. package/Components/BackgroundImage/BackgroundImage.tv.ios.tsx +24 -0
  4. package/Components/BackgroundImage/index.ts +1 -0
  5. package/Components/BaseFocusable/index.ios.ts +12 -2
  6. package/Components/Cell/FocusableWrapper.tsx +3 -0
  7. package/Components/Cell/TvOSCellComponent.tsx +6 -3
  8. package/Components/Focusable/Focusable.tsx +4 -2
  9. package/Components/Focusable/FocusableTvOS.tsx +18 -1
  10. package/Components/Focusable/__tests__/__snapshots__/FocusableTvOS.test.tsx.snap +1 -0
  11. package/Components/FocusableGroup/FocusableTvOS.tsx +41 -8
  12. package/Components/GeneralContentScreen/utils/__tests__/useCurationAPI.test.js +1 -1
  13. package/Components/HandlePlayable/HandlePlayable.tsx +13 -8
  14. package/Components/Layout/TV/NavBarContainer.tsx +1 -10
  15. package/Components/Layout/TV/ScreenContainer.tsx +2 -6
  16. package/Components/Layout/TV/__tests__/__snapshots__/NavBarContainer.test.tsx.snap +7 -12
  17. package/Components/Layout/TV/__tests__/__snapshots__/ScreenContainer.test.tsx.snap +7 -12
  18. package/Components/Layout/TV/__tests__/__snapshots__/index.test.tsx.snap +0 -1
  19. package/Components/Layout/TV/index.tsx +5 -7
  20. package/Components/Layout/TV/index.web.tsx +5 -7
  21. package/Components/LinkHandler/LinkHandler.tsx +2 -2
  22. package/Components/MasterCell/CONFIG_BUILDER_TO_REACT_COMPONENT.md +144 -0
  23. package/Components/MasterCell/DefaultComponents/ActionButton.tsx +16 -5
  24. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/model.test.ts +80 -0
  25. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/placement.test.ts +187 -0
  26. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/selectors.test.ts +45 -0
  27. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/style.test.ts +49 -0
  28. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/ActionButtonController.tsx +165 -0
  29. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/__tests__/ActionButtonController.test.tsx +405 -0
  30. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/index.ts +1 -0
  31. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/model.ts +47 -0
  32. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/placement.ts +170 -0
  33. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/selectors.ts +26 -0
  34. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/style.ts +29 -0
  35. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/types.ts +37 -0
  36. package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +4 -10
  37. package/Components/MasterCell/DefaultComponents/Button.tsx +0 -15
  38. package/Components/MasterCell/DefaultComponents/ButtonContainerView/components/HorizontalSeparator.tsx +8 -0
  39. package/Components/MasterCell/DefaultComponents/ButtonContainerView/index.tsx +15 -0
  40. package/Components/MasterCell/DefaultComponents/ButtonContainerView/index.tv.android.tsx +58 -0
  41. package/Components/MasterCell/DefaultComponents/{tv/ButtonContainerView/index.tsx → ButtonContainerView/index.tv.tsx} +3 -11
  42. package/Components/MasterCell/DefaultComponents/ButtonContainerView/index.web.ts +1 -0
  43. package/Components/MasterCell/DefaultComponents/ButtonContainerView/types.ts +40 -0
  44. package/Components/MasterCell/DefaultComponents/DataProvider/index.tsx +163 -0
  45. package/Components/MasterCell/DefaultComponents/FocusableView/index.android.tsx +2 -23
  46. package/Components/MasterCell/DefaultComponents/FocusableView/index.tsx +4 -22
  47. package/Components/MasterCell/DefaultComponents/Image/Image.android.tsx +8 -2
  48. package/Components/MasterCell/DefaultComponents/Image/Image.ios.tsx +11 -3
  49. package/Components/MasterCell/DefaultComponents/Image/Image.web.tsx +9 -1
  50. package/Components/MasterCell/DefaultComponents/Image/hooks/useImage.ts +15 -14
  51. package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +1 -2
  52. package/Components/MasterCell/DefaultComponents/PressableView.tsx +34 -0
  53. package/Components/MasterCell/DefaultComponents/SecondaryImage/hooks/__tests__/useGetImageDimensions.test.ts +7 -6
  54. package/Components/MasterCell/DefaultComponents/Text/hooks/useText.ts +11 -0
  55. package/Components/MasterCell/DefaultComponents/__tests__/DataProvider.test.tsx +141 -0
  56. package/Components/MasterCell/DefaultComponents/index.ts +9 -3
  57. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/ActionButton.tsx +135 -0
  58. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Asset.ts +33 -0
  59. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/AssetComponent.tsx +22 -0
  60. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Button.ts +125 -0
  61. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Spacer.ts +16 -0
  62. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/TextLabel.ts +67 -0
  63. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/TextLabelsContainer.ts +37 -0
  64. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/PressableView.test.tsx +393 -0
  65. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/builders.test.ts +141 -0
  66. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/index.test.ts +343 -0
  67. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/helpers.ts +105 -0
  68. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/index.ts +122 -0
  69. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/__tests__/insertButtons.test.ts +118 -0
  70. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/index.ts +238 -0
  71. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/Asset.ts +4 -18
  72. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/Button.ts +24 -73
  73. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/TextLabelsContainer.ts +37 -18
  74. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/TvActionButton.tsx +27 -0
  75. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/__tests__/index.test.ts +89 -0
  76. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/__tests__/renderedTree.test.tsx +231 -0
  77. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/index.ts +47 -48
  78. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/__tests__/getPluginIdentifier.test.ts +115 -29
  79. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/index.ts +101 -144
  80. package/Components/MasterCell/MappingFunctions/index.js +3 -2
  81. package/Components/MasterCell/README.md +4 -0
  82. package/Components/MasterCell/__tests__/__snapshots__/dataAdapter.test.js.snap +24 -0
  83. package/Components/MasterCell/__tests__/configInflater.test.js +1 -0
  84. package/Components/MasterCell/__tests__/elementMapper.test.js +46 -0
  85. package/Components/MasterCell/dataAdapter.ts +4 -1
  86. package/Components/MasterCell/elementMapper.tsx +52 -7
  87. package/Components/MasterCell/utils/__tests__/cloneChildrenWithIds.test.tsx +43 -0
  88. package/Components/MasterCell/utils/__tests__/useFilterChildren.test.tsx +80 -0
  89. package/Components/MasterCell/utils/index.ts +85 -15
  90. package/Components/OfflineHandler/NotificationView/NotificationView.tsx +2 -2
  91. package/Components/OfflineHandler/NotificationView/__tests__/index.test.tsx +17 -18
  92. package/Components/OfflineHandler/__tests__/index.test.tsx +27 -18
  93. package/Components/PlayerContainer/PlayerContainer.tsx +8 -5
  94. package/Components/PlayerContainer/__tests__/PlayerContainer.test.tsx +284 -0
  95. package/Components/River/ComponentsMap/ComponentsMap.tsx +6 -19
  96. package/Components/River/ComponentsMap/hooks/__tests__/useLoadingState.test.ts +1 -1
  97. package/Components/River/RefreshControl.tsx +19 -88
  98. package/Components/River/River.tsx +9 -82
  99. package/Components/River/TV/River.tsx +31 -14
  100. package/Components/River/TV/index.tsx +8 -4
  101. package/Components/River/TV/utils/__tests__/toStringOrEmpty.test.ts +30 -0
  102. package/Components/River/TV/utils/index.ts +4 -0
  103. package/Components/River/TV/withFocusableGroupForContent.tsx +71 -0
  104. package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +1 -0
  105. package/Components/River/__tests__/componentsMap.test.js +38 -0
  106. package/Components/River/hooks/__tests__/usePullToRefresh.test.ts +132 -0
  107. package/Components/River/hooks/index.ts +1 -0
  108. package/Components/River/hooks/usePullToRefresh.ts +51 -0
  109. package/Components/Screen/TV/index.web.tsx +4 -2
  110. package/Components/Screen/__tests__/Screen.test.tsx +65 -42
  111. package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +68 -44
  112. package/Components/Screen/hooks.ts +2 -3
  113. package/Components/Screen/index.tsx +2 -3
  114. package/Components/Screen/orientationHandler.ts +3 -3
  115. package/Components/ScreenResolver/index.tsx +9 -5
  116. package/Components/ScreenRevealManager/Overlay.tsx +34 -0
  117. package/Components/ScreenRevealManager/ScreenRevealManager.ts +40 -8
  118. package/Components/ScreenRevealManager/__tests__/Overlay.test.tsx +88 -0
  119. package/Components/ScreenRevealManager/__tests__/ScreenRevealManager.test.ts +86 -69
  120. package/Components/ScreenRevealManager/withScreenRevealManager.tsx +9 -20
  121. package/Components/Tabs/TabContent.tsx +7 -4
  122. package/Components/TopCutoffOverlay/__tests__/TopCutoffOverlay.test.tsx +201 -0
  123. package/Components/TopCutoffOverlay/hooks/__tests__/useMarginTop.test.ts +130 -0
  124. package/Components/TopCutoffOverlay/hooks/index.ts +1 -0
  125. package/Components/TopCutoffOverlay/hooks/useMarginTop.ts +59 -0
  126. package/Components/TopCutoffOverlay/index.tsx +55 -0
  127. package/Components/Transitioner/index.js +3 -3
  128. package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +5 -5
  129. package/Components/VideoModal/hooks/__tests__/useDelayedPlayerDetails.test.ts +15 -7
  130. package/Components/VideoModal/utils.ts +12 -10
  131. package/Components/Viewport/ViewportAware/__tests__/viewportAware.test.js +0 -2
  132. package/Components/Viewport/ViewportAware/index.tsx +16 -7
  133. package/Components/Viewport/ViewportEvents/__tests__/viewportEvents.test.js +1 -1
  134. package/Components/ZappFrameworkComponents/BarView/BarView.tsx +4 -6
  135. package/Components/ZappFrameworkComponents/BarView/__tests__/BarView.test.tsx +2 -2
  136. package/Components/default-cell-renderer/viewTrees/mobile/index.ts +0 -3
  137. package/Contexts/ScreenContext/index.tsx +25 -18
  138. package/Contexts/ScreenTrackedViewPositionsContext/__tests__/index.test.tsx +1 -1
  139. package/Decorators/Analytics/index.tsx +6 -5
  140. package/Decorators/ConfigurationWrapper/__tests__/__snapshots__/withConfigurationProvider.test.tsx.snap +1 -0
  141. package/Decorators/ConfigurationWrapper/const.ts +1 -0
  142. package/Decorators/ZappPipesDataConnector/__tests__/UrlFeedResolver.test.tsx +39 -21
  143. package/Decorators/ZappPipesDataConnector/__tests__/zappPipesDataConnector.test.js +1 -1
  144. package/Decorators/ZappPipesDataConnector/index.tsx +2 -2
  145. package/Decorators/ZappPipesDataConnector/resolvers/StaticFeedResolver.tsx +1 -1
  146. package/Decorators/ZappPipesDataConnector/resolvers/UrlFeedResolver.tsx +18 -7
  147. package/Helpers/DataSourceHelper/__tests__/itemLimitForData.test.ts +80 -0
  148. package/Helpers/DataSourceHelper/index.ts +19 -0
  149. package/Helpers/index.js +7 -1
  150. package/package.json +5 -5
  151. package/Components/Layout/TV/LayoutBackground.tsx +0 -28
  152. package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.android.tsx +0 -135
  153. package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/types.ts +0 -25
  154. package/Components/River/TV/withTVEventHandler.tsx +0 -36
  155. package/Helpers/DataSourceHelper/index.js +0 -19
@@ -0,0 +1,238 @@
1
+ import {
2
+ insertBetweenLabelContainers,
3
+ insertBetweenLabels,
4
+ } from "../../../ActionButtonsCore/placement";
5
+ import { toNumberWithDefaultZero } from "@applicaster/zapp-react-native-utils/numberUtils";
6
+ import { Platform } from "react-native";
7
+
8
+ export const insertButtonsBetweenLabels = (
9
+ configuration: Record<string, unknown>,
10
+ buttons,
11
+ labels = []
12
+ ) =>
13
+ insertBetweenLabels(
14
+ {
15
+ position: configuration?.mobile_buttons_container_position as
16
+ | string
17
+ | undefined,
18
+ allowOnTop: false,
19
+ appendWhenMissing: false,
20
+ },
21
+ buttons,
22
+ labels // "text_label_1", "text_label_2", "text_label_3", "text_label_4"
23
+ );
24
+
25
+ export const insertButtonsBetweenLabelContainers = (
26
+ configuration: Record<string, unknown>,
27
+ buttons,
28
+ labelContainers = []
29
+ ) =>
30
+ insertBetweenLabelContainers(
31
+ {
32
+ position: configuration?.mobile_buttons_container_position as
33
+ | string
34
+ | undefined,
35
+ allowOnTop: false,
36
+ appendWhenMissing: true,
37
+ },
38
+ buttons,
39
+ labelContainers // top_label_1, top_label_2, bottom_label_1, bottom_label_2, etc.
40
+ );
41
+
42
+ export const mobileOverImagePositionStyles = (position: string) => {
43
+ switch (position) {
44
+ case "top_left":
45
+ return {
46
+ justifyContent: "flex-start",
47
+ alignItems: "flex-start",
48
+ };
49
+ case "top_right":
50
+ return {
51
+ justifyContent: "flex-start",
52
+ alignItems: "flex-end",
53
+ };
54
+ case "bottom_left":
55
+ return {
56
+ justifyContent: "flex-end",
57
+ alignItems: "flex-start",
58
+ };
59
+ case "bottom_right":
60
+ return {
61
+ justifyContent: "flex-end",
62
+ alignItems: "flex-end",
63
+ };
64
+ case "center":
65
+ default:
66
+ return {
67
+ justifyContent: "center",
68
+ alignItems: "center",
69
+ top: 0,
70
+ left: 0,
71
+ right: 0,
72
+ bottom: 0,
73
+ };
74
+ }
75
+ };
76
+
77
+ export function getContentDirection(alignment = "left") {
78
+ switch (alignment) {
79
+ case "right":
80
+ return "row-reverse";
81
+ case "above":
82
+ return "column";
83
+ case "below":
84
+ return "column-reverse";
85
+ case "left":
86
+ default:
87
+ return "row";
88
+ }
89
+ }
90
+
91
+ export function getContentsAlignment(alignment = "center", direction = "left") {
92
+ switch (alignment) {
93
+ case "left":
94
+ return direction === "left" ? "flex-start" : "flex-end";
95
+ case "right":
96
+ return direction === "left" ? "flex-end" : "flex-start";
97
+ case "center":
98
+ default:
99
+ return "center";
100
+ }
101
+ }
102
+
103
+ export function resolveLabelText(label) {
104
+ if (typeof label === "string") {
105
+ return label;
106
+ }
107
+
108
+ return label?.label_1 || "";
109
+ }
110
+
111
+ export function buildLegacySelection(item, actionContext) {
112
+ // Some state are not array. In this case we fallback to the default value provided by the action or false
113
+ const defaultIsSelected = Array.isArray(actionContext?.state)
114
+ ? (actionContext?.state || []).includes(item)
115
+ : false;
116
+
117
+ return actionContext?.masterCell?.isSelected
118
+ ? actionContext?.masterCell?.isSelected(item)
119
+ : defaultIsSelected;
120
+ }
121
+
122
+ export const getMarginStyles = (value) => ({
123
+ marginTop: toNumberWithDefaultZero(value("margin_top")),
124
+ marginRight: toNumberWithDefaultZero(value("margin_right")),
125
+ marginBottom: toNumberWithDefaultZero(value("margin_bottom")),
126
+ marginLeft: toNumberWithDefaultZero(value("margin_left")),
127
+ });
128
+
129
+ export const getPaddingStyles = (value) => ({
130
+ paddingTop: toNumberWithDefaultZero(value("padding_top")),
131
+ paddingRight: toNumberWithDefaultZero(value("padding_right")),
132
+ paddingBottom: toNumberWithDefaultZero(value("padding_bottom")),
133
+ paddingLeft: toNumberWithDefaultZero(value("padding_left")),
134
+ });
135
+
136
+ export const getBorderStyles = (value) => ({
137
+ borderWidth: toNumberWithDefaultZero(value("border_size")),
138
+ borderRadius: toNumberWithDefaultZero(value("corner_radius")),
139
+ borderColor: value("border_color"),
140
+ });
141
+
142
+ export const getPressableStyles = (value) => ({
143
+ ...getMarginStyles(value),
144
+ ...getPaddingStyles(value),
145
+ ...getBorderStyles(value),
146
+ backgroundColor: value("background_color"),
147
+ });
148
+
149
+ export const getAssetStyles = (value) => ({
150
+ ...getMarginStyles((suffix) => value(`asset_${suffix}`)),
151
+ width: toNumberWithDefaultZero(value("asset_width")),
152
+ height: toNumberWithDefaultZero(value("asset_height")),
153
+ backgroundColor: "transparent",
154
+ });
155
+
156
+ export const getTextLabelStyles = (value) => ({
157
+ color: value("font_color"),
158
+ fontSize: toNumberWithDefaultZero(value("font_size")),
159
+ lineHeight: toNumberWithDefaultZero(value("line_height")),
160
+ ...getMarginStyles(value),
161
+ fontFamily:
162
+ Platform.OS === "ios"
163
+ ? value("ios_font_family")
164
+ : value("android_font_family"),
165
+ letterSpacing: toNumberWithDefaultZero(
166
+ Platform.OS === "ios"
167
+ ? value("ios_letter_spacing")
168
+ : value("android_letter_spacing")
169
+ ),
170
+ });
171
+
172
+ /** retrieves asset uri for a given flavour,
173
+ * if flavour is not provided, returns the default asset from `asset` or selected state asset (if available)
174
+ * asset can be:
175
+ * provided as asset path,
176
+ * provided as [default || flavour_1, alternative || flavour_2] array.
177
+ * mobileButtonAssets asset can be:
178
+ * provided as asset path,
179
+ * provided as [default || flavour_1, alternative || flavour_2] array,
180
+ * provided as [[] as flavour_1, [] as flavour_2] array for multiple flavours, where each flavour can be either a path or [default, alternative] array,
181
+ * provided as a React component accepting flavour as prop.
182
+ *
183
+ * isActive reflect the state of the action and can be used to render different asset for active/inactive state if asset is provided as array
184
+ *
185
+ * */
186
+ export const selectByAssetFlavour = (
187
+ actionState: {
188
+ asset?: string | [string, string] | CellActionAssetComponent;
189
+ mobileButtonAssets?:
190
+ | [string, string]
191
+ | [string, string][]
192
+ | CellActionAssetComponent;
193
+ },
194
+ flavour?: "flavour_1" | "flavour_2",
195
+ isActive?: boolean
196
+ ): string | CellActionAssetComponent => {
197
+ if (actionState.mobileButtonAssets) {
198
+ if (typeof actionState.mobileButtonAssets === "function") {
199
+ return actionState.mobileButtonAssets;
200
+ }
201
+
202
+ if (flavour) {
203
+ if (flavour === "flavour_1") {
204
+ if (typeof actionState.mobileButtonAssets[0] === "string") {
205
+ return actionState.mobileButtonAssets[0];
206
+ }
207
+
208
+ if (Array.isArray(actionState.mobileButtonAssets[0])) {
209
+ return actionState.mobileButtonAssets[0][isActive ? 1 : 0];
210
+ }
211
+
212
+ return actionState.mobileButtonAssets[0];
213
+ } else if (flavour === "flavour_2") {
214
+ if (typeof actionState.mobileButtonAssets[1] === "string") {
215
+ return actionState.mobileButtonAssets[1];
216
+ }
217
+
218
+ if (Array.isArray(actionState.mobileButtonAssets[1])) {
219
+ return actionState.mobileButtonAssets[1][isActive ? 1 : 0];
220
+ }
221
+
222
+ return actionState.mobileButtonAssets[1];
223
+ }
224
+ }
225
+
226
+ return Array.isArray(actionState.mobileButtonAssets[0])
227
+ ? actionState.mobileButtonAssets[0][isActive ? 1 : 0]
228
+ : actionState.mobileButtonAssets[0];
229
+ } else {
230
+ if (typeof actionState?.asset === "function") {
231
+ return actionState.asset;
232
+ }
233
+
234
+ return typeof actionState?.asset === "string"
235
+ ? actionState.asset
236
+ : actionState.asset[isActive ? 1 : 0];
237
+ }
238
+ };
@@ -1,31 +1,17 @@
1
- import * as R from "ramda";
2
- import { toNumber } from "@applicaster/zapp-react-native-utils/numberUtils";
3
-
4
1
  const Image = "Image";
5
2
 
6
3
  type Props = {
7
- prefix: string;
8
- value: Function;
9
4
  pluginIdentifier: string;
5
+ style?: Record<string, unknown>;
10
6
  };
11
7
 
12
- export const Asset = ({ prefix, value, pluginIdentifier }: Props) => {
13
- if (!value(`${prefix}_asset_enabled`)) return null;
14
-
8
+ export const Asset = ({ pluginIdentifier, style }: Props) => {
15
9
  return {
16
10
  type: Image,
17
- style: {
18
- marginTop: value(`${prefix}_asset_margin_top`),
19
- marginRight: value(`${prefix}_asset_margin_right`),
20
- marginBottom: value(`${prefix}_asset_margin_bottom`),
21
- marginLeft: value(`${prefix}_asset_margin_left`),
22
-
23
- width: toNumber(value(`${prefix}_asset_width`)) || 40,
24
- height: toNumber(value(`${prefix}_asset_height`)) || 40,
25
- },
11
+ style: style,
26
12
  data: [
27
13
  {
28
- func: R.identity,
14
+ func: "identity",
29
15
  args: [],
30
16
  propName: "entry",
31
17
  },
@@ -1,13 +1,14 @@
1
- import {
2
- toNumberWithDefaultZero,
3
- toNumberWithDefault,
4
- } from "@applicaster/zapp-react-native-utils/numberUtils";
5
-
6
1
  import { compact } from "@applicaster/zapp-react-native-utils/cellUtils";
7
2
 
8
3
  import { TextLabelsContainer } from "./TextLabelsContainer";
9
4
  import { Asset } from "./Asset";
10
5
  import { Spacer } from "./Spacer";
6
+ import {
7
+ getAssetStyles,
8
+ getBorderStyles,
9
+ getDisplayModeStyles,
10
+ getPaddingStyles,
11
+ } from "./utils";
11
12
 
12
13
  const compactAndSort = ({ value, prefix, asset, labels, spacer }) => {
13
14
  const assetAlignment = value(`${prefix}_asset_alignment`) || "left";
@@ -19,31 +20,6 @@ const compactAndSort = ({ value, prefix, asset, labels, spacer }) => {
19
20
  return compact([asset, labels]);
20
21
  };
21
22
 
22
- const displayMode = ({ value, prefix }) => {
23
- const mode = value(`${prefix}_display_mode`) || "dynamic";
24
-
25
- const width = toNumberWithDefault(
26
- 240,
27
- value(`${prefix}_fixed_and_fixed_center_width`)
28
- );
29
-
30
- if (mode === "fixed") {
31
- return {
32
- width,
33
- };
34
- }
35
-
36
- if (mode === "fixed_center") {
37
- return {
38
- width,
39
- justifyContent: "center",
40
- };
41
- }
42
-
43
- // dynamic mode
44
- return {};
45
- };
46
-
47
23
  type Props = {
48
24
  prefix: string;
49
25
  value: Function;
@@ -63,55 +39,30 @@ export const Button = ({
63
39
  }: Props) => {
64
40
  if (!value(`${suffixId}_button_enabled`)) return null;
65
41
 
42
+ const getValue = (suffix: string) => value(`${prefix}_${suffix}`);
43
+
66
44
  return {
67
- type: "FocusableView", // container
45
+ type: "TvActionButton",
68
46
  style: {
69
47
  flexDirection: "row",
70
48
  alignItems: "center",
71
49
  display: "flex",
72
-
73
- paddingTop: toNumberWithDefaultZero(
74
- value(`${prefix}_background_padding_top`)
75
- ),
76
- paddingRight: toNumberWithDefaultZero(
77
- value(`${prefix}_background_padding_right`)
78
- ),
79
- paddingBottom: toNumberWithDefaultZero(
80
- value(`${prefix}_background_padding_bottom`)
81
- ),
82
- paddingLeft: toNumberWithDefaultZero(
83
- value(`${prefix}_background_padding_left`)
84
- ),
85
-
86
- // BORDER
87
- borderRadius: toNumberWithDefaultZero(
88
- value(`${prefix}_background_corner_radius`)
89
- ),
90
- borderWidth: value(`${prefix}_background_border_thickness`),
91
- borderStyle: "solid",
92
- // BORDER
93
-
94
- ...displayMode({ value, prefix }),
50
+ ...getPaddingStyles((suffix) => getValue(`background_${suffix}`)),
51
+ ...getBorderStyles((suffix) => getValue(`background_${suffix}`)),
52
+ ...getDisplayModeStyles(getValue),
95
53
  },
96
- data: [
97
- {
98
- func: (x) => x,
99
- args: [],
100
- propName: "item",
101
- },
102
- ],
103
54
  elements: compactAndSort({
104
55
  prefix,
105
56
  value,
106
- asset: Asset({
107
- prefix,
108
- value,
109
- pluginIdentifier,
110
- }),
57
+ asset: getValue("asset_enabled")
58
+ ? Asset({
59
+ style: getAssetStyles((suffix) => getValue(`asset_${suffix}`)),
60
+ pluginIdentifier,
61
+ })
62
+ : null,
111
63
  labels: TextLabelsContainer({
112
- prefix,
113
- value,
114
- platformValue,
64
+ value: getValue,
65
+ platformValue: (suffix: string) => platformValue(`${prefix}_${suffix}`),
115
66
  pluginIdentifier,
116
67
  }),
117
68
  spacer: Spacer(),
@@ -119,12 +70,12 @@ export const Button = ({
119
70
  additionalProps: {
120
71
  suffixId,
121
72
  focusedStyles: {
122
- backgroundColor: value(`${prefix}_focused_background_color`),
123
- borderColor: value(`${prefix}_focused_background_border_color`),
73
+ backgroundColor: getValue("focused_background_color"),
74
+ borderColor: getValue("focused_background_border_color"),
124
75
  },
125
76
  normalStyles: {
126
- backgroundColor: value(`${prefix}_background_color`),
127
- borderColor: value(`${prefix}_background_border_color`),
77
+ backgroundColor: getValue("background_color"),
78
+ borderColor: getValue("background_border_color"),
128
79
  },
129
80
  pluginIdentifier,
130
81
  preferredFocus,
@@ -1,10 +1,10 @@
1
- import { TextLabel } from "./TextLabel";
2
1
  import { compact } from "@applicaster/zapp-react-native-utils/cellUtils";
2
+ import { getTextLabelStyles } from "./utils";
3
3
 
4
4
  const View = "View";
5
+ const Text = "Text";
5
6
 
6
7
  export const TextLabelsContainer = ({
7
- prefix,
8
8
  value,
9
9
  platformValue,
10
10
  pluginIdentifier,
@@ -14,21 +14,40 @@ export const TextLabelsContainer = ({
14
14
  style: {
15
15
  flexDirection: "column",
16
16
  },
17
- elements: compact([
18
- TextLabel({
19
- prefix: `${prefix}_label_1`,
20
- value,
21
- platformValue,
22
- pluginIdentifier,
23
- name: "label_1",
24
- }),
25
- TextLabel({
26
- prefix: `${prefix}_label_2`,
27
- value,
28
- platformValue,
29
- pluginIdentifier,
30
- name: "label_2",
31
- }),
32
- ]),
17
+ elements: compact(
18
+ ["label_1", "label_2"].map((name) => {
19
+ const getValue = (suffix: string) => value(`${name}_${suffix}`);
20
+
21
+ const getPlatformValue = (suffix: string) =>
22
+ platformValue(`${name}_${suffix}`);
23
+
24
+ return getValue("toggle")
25
+ ? {
26
+ type: Text,
27
+ style: getTextLabelStyles(getValue, getPlatformValue),
28
+ data: [
29
+ {
30
+ func: "identity",
31
+ args: [],
32
+ propName: "entry",
33
+ },
34
+ ],
35
+ additionalProps: {
36
+ label: { context: pluginIdentifier, name },
37
+ numberOfLines: getValue("number_of_lines"),
38
+ transformText: getValue("text_transform"),
39
+ focusedStyles: {
40
+ backgroundColor: getValue("focused_background_color"),
41
+ color: getValue("focused_font_color"),
42
+ },
43
+ normalStyles: {
44
+ backgroundColor: getValue("background_color"),
45
+ color: getValue("font_color"),
46
+ },
47
+ },
48
+ }
49
+ : null;
50
+ })
51
+ ),
33
52
  };
34
53
  };
@@ -0,0 +1,27 @@
1
+ import React from "react";
2
+ import { ActionButtonController } from "../../ActionButtonsCore/components";
3
+ import { FocusableView } from "../../FocusableView";
4
+
5
+ type Props = Record<string, any> & {
6
+ children?: React.ReactNode;
7
+ style?: object;
8
+ entry?: any;
9
+ };
10
+
11
+ export function TvActionButton({
12
+ children,
13
+ pluginIdentifier,
14
+ style,
15
+ entry,
16
+ ...props
17
+ }: Props) {
18
+ return (
19
+ <ActionButtonController pluginIdentifier={pluginIdentifier} entry={entry}>
20
+ {({ onPress }) => (
21
+ <FocusableView {...props} style={style} onPress={onPress}>
22
+ {children}
23
+ </FocusableView>
24
+ )}
25
+ </ActionButtonController>
26
+ );
27
+ }
@@ -0,0 +1,89 @@
1
+ import { TvActionButtons } from "..";
2
+
3
+ describe("TvActionButtons", () => {
4
+ const baseConfiguration = {
5
+ tv_buttons_container_buttons_enabled: true,
6
+ tv_buttons_container_align: "middle",
7
+ tv_buttons_container_margin_top: 1,
8
+ tv_buttons_container_margin_right: 2,
9
+ tv_buttons_container_margin_bottom: 3,
10
+ tv_buttons_container_margin_left: 4,
11
+ tv_buttons_container_horizontal_gutter: 10,
12
+ tv_buttons_container_independent_styles: false,
13
+ tv_buttons_button_1_button_enabled: true,
14
+ tv_buttons_button_1_assign_action: "action_1",
15
+ tv_buttons_button_1_background_padding_top: 11,
16
+ tv_buttons_button_1_background_padding_right: 12,
17
+ tv_buttons_button_1_background_padding_bottom: 13,
18
+ tv_buttons_button_1_background_padding_left: 14,
19
+ tv_buttons_button_3_button_enabled: true,
20
+ tv_buttons_button_3_assign_action: "action_3",
21
+ tv_buttons_button_3_background_padding_top: 31,
22
+ tv_buttons_button_3_background_padding_right: 32,
23
+ tv_buttons_button_3_background_padding_bottom: 33,
24
+ tv_buttons_button_3_background_padding_left: 34,
25
+ };
26
+
27
+ const platformValue = jest.fn();
28
+
29
+ it("renders sparse enabled slots with slot-based action lookup and ids", () => {
30
+ const value = (key) => baseConfiguration[key];
31
+
32
+ const result = TvActionButtons({
33
+ value,
34
+ platformValue,
35
+ configuration: baseConfiguration,
36
+ state: "focused",
37
+ skipButtons: false,
38
+ });
39
+
40
+ expect(result.type).toBe("ButtonContainerView");
41
+ expect(result.style.justifyContent).toBe("center");
42
+ expect(result.additionalProps.buttonsCount).toBe(2);
43
+ expect(result.elements).toHaveLength(2);
44
+ expect(result.elements[0].type).toBe("DataProvider");
45
+
46
+ expect(result.elements[0].data).toEqual([
47
+ {
48
+ func: "identity",
49
+ args: [],
50
+ propName: "entry",
51
+ },
52
+ ]);
53
+
54
+ const firstButton = result.elements[0].elements[0];
55
+ const secondButton = result.elements[1].elements[0];
56
+
57
+ expect(firstButton.additionalProps.pluginIdentifier).toBe("action_1");
58
+
59
+ expect(firstButton.additionalProps.suffixId).toBe("tv_buttons_button_1");
60
+
61
+ expect(firstButton.additionalProps.preferredFocus).toBe(true);
62
+
63
+ expect(secondButton.additionalProps.pluginIdentifier).toBe("action_3");
64
+
65
+ expect(secondButton.additionalProps.suffixId).toBe("tv_buttons_button_3");
66
+
67
+ expect(secondButton.additionalProps.preferredFocus).toBe(false);
68
+ });
69
+
70
+ it("keeps shared button styles from slot 1 while preserving slot 3 identity", () => {
71
+ const value = (key) => baseConfiguration[key];
72
+
73
+ const result = TvActionButtons({
74
+ value,
75
+ platformValue,
76
+ configuration: baseConfiguration,
77
+ state: "focused",
78
+ skipButtons: false,
79
+ });
80
+
81
+ const secondButton = result.elements[1].elements[0];
82
+
83
+ expect(secondButton.style.paddingTop).toBe(11);
84
+
85
+ expect(secondButton.additionalProps.pluginIdentifier).toBe("action_3");
86
+
87
+ expect(secondButton.additionalProps.suffixId).toBe("tv_buttons_button_3");
88
+ });
89
+ });