@applicaster/zapp-react-native-ui-components 15.1.4 → 16.0.0-alpha.1042942411

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 (202) hide show
  1. package/Components/BackgroundImage/index.ts +1 -8
  2. package/Components/BaseFocusable/index.ios.ts +12 -2
  3. package/Components/Cell/FocusableWrapper.tsx +3 -0
  4. package/Components/Cell/TvOSCellComponent.tsx +6 -3
  5. package/Components/CellRendererResolver/index.ts +1 -1
  6. package/Components/Focusable/Focusable.tsx +4 -2
  7. package/Components/Focusable/FocusableTvOS.tsx +18 -1
  8. package/Components/Focusable/__tests__/__snapshots__/FocusableTvOS.test.tsx.snap +1 -0
  9. package/Components/FocusableGroup/FocusableTvOS.tsx +30 -1
  10. package/Components/GeneralContentScreen/GeneralContentScreenHookAdapter.tsx +39 -0
  11. package/Components/GeneralContentScreen/__tests__/GeneralContentScreenHookAdapter.test.tsx +64 -0
  12. package/Components/GeneralContentScreen/__tests__/HookContentFocusGroup.web.test.tsx +91 -0
  13. package/Components/GeneralContentScreen/hookAdapter/__tests__/networkService.test.ts +74 -0
  14. package/Components/GeneralContentScreen/hookAdapter/__tests__/runInBackground.test.ts +139 -0
  15. package/Components/GeneralContentScreen/hookAdapter/__tests__/validationHelper.test.ts +124 -0
  16. package/Components/GeneralContentScreen/hookAdapter/logger.ts +6 -0
  17. package/Components/GeneralContentScreen/hookAdapter/networkService.ts +53 -0
  18. package/Components/GeneralContentScreen/hookAdapter/runInBackground.ts +48 -0
  19. package/Components/GeneralContentScreen/hookAdapter/validationHelper.ts +72 -0
  20. package/Components/GeneralContentScreen/hookFocus/index.tsx +13 -0
  21. package/Components/GeneralContentScreen/hookFocus/index.web.tsx +69 -0
  22. package/Components/GeneralContentScreen/index.ts +2 -0
  23. package/Components/GeneralContentScreen/utils/__tests__/useCurationAPI.test.js +1 -1
  24. package/Components/HandlePlayable/HandlePlayable.tsx +13 -8
  25. package/Components/Layout/TV/NavBarContainer.tsx +1 -10
  26. package/Components/Layout/TV/ScreenContainer.tsx +2 -6
  27. package/Components/Layout/TV/__tests__/__snapshots__/NavBarContainer.test.tsx.snap +7 -12
  28. package/Components/Layout/TV/__tests__/__snapshots__/ScreenContainer.test.tsx.snap +7 -12
  29. package/Components/Layout/TV/index.tsx +3 -4
  30. package/Components/Layout/TV/index.web.tsx +3 -4
  31. package/Components/LinkHandler/LinkHandler.tsx +2 -2
  32. package/Components/MasterCell/CONFIG_BUILDER_TO_REACT_COMPONENT.md +144 -0
  33. package/Components/MasterCell/DefaultComponents/ActionButton.tsx +27 -48
  34. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/model.test.ts +80 -0
  35. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/placement.test.ts +213 -0
  36. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/selectors.test.ts +45 -0
  37. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/style.test.ts +49 -0
  38. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/ActionButtonController.tsx +165 -0
  39. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/__tests__/ActionButtonController.test.tsx +405 -0
  40. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/index.ts +1 -0
  41. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/model.ts +47 -0
  42. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/placement.ts +198 -0
  43. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/selectors.ts +26 -0
  44. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/style.ts +29 -0
  45. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/types.ts +37 -0
  46. package/Components/MasterCell/DefaultComponents/BorderContainerView/__tests__/index.test.tsx +217 -46
  47. package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +50 -119
  48. package/Components/MasterCell/DefaultComponents/Button.tsx +50 -58
  49. package/Components/MasterCell/DefaultComponents/ButtonContainerView/components/HorizontalSeparator.tsx +8 -0
  50. package/Components/MasterCell/DefaultComponents/ButtonContainerView/index.android.tv.tsx +58 -0
  51. package/Components/MasterCell/DefaultComponents/ButtonContainerView/index.tsx +15 -0
  52. package/Components/MasterCell/DefaultComponents/{tv/ButtonContainerView/index.tsx → ButtonContainerView/index.tv.tsx} +3 -11
  53. package/Components/MasterCell/DefaultComponents/ButtonContainerView/index.web.ts +1 -0
  54. package/Components/MasterCell/DefaultComponents/ButtonContainerView/types.ts +40 -0
  55. package/Components/MasterCell/DefaultComponents/CellBadge/__tests__/__snapshots__/index.test.tsx.snap +18 -0
  56. package/Components/MasterCell/DefaultComponents/CellBadge/__tests__/index.test.tsx +90 -0
  57. package/Components/MasterCell/DefaultComponents/CellBadge/index.tsx +13 -14
  58. package/Components/MasterCell/DefaultComponents/DataProvider/index.tsx +163 -0
  59. package/Components/MasterCell/DefaultComponents/FocusableView/index.android.tsx +2 -23
  60. package/Components/MasterCell/DefaultComponents/FocusableView/index.tsx +4 -22
  61. package/Components/MasterCell/DefaultComponents/Image/Image.android.tsx +8 -2
  62. package/Components/MasterCell/DefaultComponents/Image/Image.ios.tsx +11 -3
  63. package/Components/MasterCell/DefaultComponents/Image/Image.web.tsx +9 -1
  64. package/Components/MasterCell/DefaultComponents/Image/hooks/useImage.ts +15 -14
  65. package/Components/MasterCell/DefaultComponents/ImageContainer/index.tsx +5 -3
  66. package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +1 -2
  67. package/Components/MasterCell/DefaultComponents/PressableView.tsx +54 -0
  68. package/Components/MasterCell/DefaultComponents/SecondaryImage/Image.tsx +46 -50
  69. package/Components/MasterCell/DefaultComponents/SecondaryImage/__tests__/__snapshots__/Image.test.tsx.snap +47 -39
  70. package/Components/MasterCell/DefaultComponents/SecondaryImage/hooks/__tests__/useGetImageDimensions.test.ts +7 -6
  71. package/Components/MasterCell/DefaultComponents/SecondaryImage/utils.ts +19 -6
  72. package/Components/MasterCell/DefaultComponents/Text/hooks/useText.ts +15 -0
  73. package/Components/MasterCell/DefaultComponents/Text/index.tsx +45 -15
  74. package/Components/MasterCell/DefaultComponents/UIKitAdapters/Badge.tsx +49 -0
  75. package/Components/MasterCell/DefaultComponents/__tests__/ActionButton.test.tsx +122 -0
  76. package/Components/MasterCell/DefaultComponents/__tests__/Button.test.tsx +186 -0
  77. package/Components/MasterCell/DefaultComponents/__tests__/DataProvider.test.tsx +141 -0
  78. package/Components/MasterCell/DefaultComponents/__tests__/DynamicBadge.test.tsx +156 -0
  79. package/Components/MasterCell/DefaultComponents/__tests__/__snapshots__/ActionButton.test.tsx.snap +60 -0
  80. package/Components/MasterCell/DefaultComponents/__tests__/text.test.tsx +196 -67
  81. package/Components/MasterCell/DefaultComponents/index.ts +13 -3
  82. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/ActionButton.tsx +189 -0
  83. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Asset.ts +33 -0
  84. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/AssetComponent.tsx +33 -0
  85. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Button.ts +159 -0
  86. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Spacer.ts +18 -0
  87. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/TextLabel.ts +67 -0
  88. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/TextLabelsContainer.ts +39 -0
  89. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/PressableView.test.tsx +402 -0
  90. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/builders.test.ts +141 -0
  91. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/index.test.ts +354 -0
  92. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/helpers.ts +105 -0
  93. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/index.ts +129 -0
  94. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/__tests__/insertButtons.test.ts +123 -0
  95. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/index.ts +238 -0
  96. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/Asset.ts +4 -18
  97. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/Button.ts +24 -73
  98. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/TextLabelsContainer.ts +37 -18
  99. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/TvActionButton.tsx +27 -0
  100. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/__tests__/index.test.ts +89 -0
  101. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/__tests__/renderedTree.test.tsx +231 -0
  102. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/index.ts +47 -48
  103. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/__tests__/getPluginIdentifier.test.ts +115 -29
  104. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/__tests__/insertButtonsBetweenLabels.test.ts +45 -13
  105. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/index.ts +101 -144
  106. package/Components/MasterCell/MappingFunctions/index.js +3 -2
  107. package/Components/MasterCell/README.md +4 -0
  108. package/Components/MasterCell/__tests__/__snapshots__/dataAdapter.test.js.snap +24 -0
  109. package/Components/MasterCell/__tests__/configInflater.test.js +1 -0
  110. package/Components/MasterCell/__tests__/elementMapper.test.js +46 -0
  111. package/Components/MasterCell/contexts/PressedStateContext.ts +3 -0
  112. package/Components/MasterCell/dataAdapter.ts +4 -1
  113. package/Components/MasterCell/elementMapper.tsx +54 -7
  114. package/Components/MasterCell/hooks/index.ts +2 -0
  115. package/Components/MasterCell/hooks/usePressedState.ts +4 -0
  116. package/Components/MasterCell/index.tsx +20 -4
  117. package/Components/MasterCell/utils/__tests__/cloneChildrenWithIds.test.tsx +43 -0
  118. package/Components/MasterCell/utils/__tests__/useFilterChildren.test.tsx +80 -0
  119. package/Components/MasterCell/utils/index.ts +82 -16
  120. package/Components/ModalComponent/AudioPlayer/Components/Action.tsx +33 -69
  121. package/Components/ModalComponent/AudioPlayer/Components/Button.tsx +25 -47
  122. package/Components/ModalComponent/AudioPlayer/Components/Header.tsx +122 -39
  123. package/Components/ModalComponent/AudioPlayer/Components/Input.tsx +671 -0
  124. package/Components/ModalComponent/AudioPlayer/Components/Item.tsx +257 -88
  125. package/Components/ModalComponent/AudioPlayer/Components/index.ts +6 -4
  126. package/Components/ModalComponent/BottomSheetDragArea.tsx +33 -0
  127. package/Components/ModalComponent/BottomSheetModalContent.tsx +17 -10
  128. package/Components/ModalComponent/__tests__/BottomSheetDragArea.test.tsx +74 -0
  129. package/Components/OfflineHandler/__tests__/__snapshots__/index.test.tsx.snap +4 -85
  130. package/Components/OfflineHandler/__tests__/index.test.tsx +101 -32
  131. package/Components/OfflineHandler/hooks.ts +123 -0
  132. package/Components/OfflineHandler/index.tsx +51 -73
  133. package/Components/OfflineHandler/utils/index.ts +1 -13
  134. package/Components/PlayerContainer/PlayerContainer.tsx +6 -5
  135. package/Components/PlayerContainer/__tests__/PlayerContainer.test.tsx +2 -4
  136. package/Components/PreloaderWrapper/index.tsx +1 -1
  137. package/Components/River/ComponentsMap/ComponentsMap.tsx +8 -21
  138. package/Components/River/ComponentsMap/hooks/__tests__/useLoadingState.test.ts +1 -1
  139. package/Components/River/RefreshControl.tsx +19 -105
  140. package/Components/River/River.tsx +9 -82
  141. package/Components/River/TV/River.tsx +31 -14
  142. package/Components/River/TV/index.tsx +8 -4
  143. package/Components/River/TV/utils/__tests__/toStringOrEmpty.test.ts +30 -0
  144. package/Components/River/TV/utils/index.ts +4 -0
  145. package/Components/River/TV/withFocusableGroupForContent.tsx +71 -0
  146. package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +4 -0
  147. package/Components/River/__tests__/componentsMap.test.js +38 -0
  148. package/Components/River/hooks/__tests__/usePullToRefresh.test.ts +132 -0
  149. package/Components/River/hooks/index.ts +1 -0
  150. package/Components/River/hooks/usePullToRefresh.ts +51 -0
  151. package/Components/Screen/TV/index.web.tsx +4 -2
  152. package/Components/Screen/__tests__/Screen.test.tsx +65 -42
  153. package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +68 -44
  154. package/Components/Screen/hooks.ts +2 -3
  155. package/Components/Screen/index.tsx +2 -3
  156. package/Components/Screen/orientationHandler.ts +3 -3
  157. package/Components/ScreenFeedLoader/ScreenFeedLoader.tsx +5 -3
  158. package/Components/ScreenResolver/index.tsx +9 -5
  159. package/Components/ScreenResolverFeedProvider/ScreenResolverFeedProvider.tsx +1 -1
  160. package/Components/ScreenRevealManager/ScreenRevealManager.ts +40 -8
  161. package/Components/ScreenRevealManager/__tests__/ScreenRevealManager.test.ts +86 -69
  162. package/Components/ScreenRevealManager/withScreenRevealManager.tsx +1 -1
  163. package/Components/Tabs/TabContent.tsx +7 -4
  164. package/Components/TopCutoffOverlay/__tests__/TopCutoffOverlay.test.tsx +201 -0
  165. package/Components/TopCutoffOverlay/hooks/__tests__/useMarginTop.test.ts +130 -0
  166. package/Components/TopCutoffOverlay/hooks/index.ts +1 -0
  167. package/Components/TopCutoffOverlay/hooks/useMarginTop.ts +59 -0
  168. package/Components/TopCutoffOverlay/index.tsx +55 -0
  169. package/Components/Transitioner/index.js +3 -3
  170. package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +5 -5
  171. package/Components/VideoModal/hooks/__tests__/useDelayedPlayerDetails.test.ts +15 -7
  172. package/Components/VideoModal/utils.ts +12 -10
  173. package/Components/Viewport/ViewportAware/__tests__/viewportAware.test.js +0 -2
  174. package/Components/Viewport/ViewportAware/index.tsx +16 -7
  175. package/Components/Viewport/ViewportEvents/__tests__/viewportEvents.test.js +1 -1
  176. package/Components/default-cell-renderer/viewTrees/mobile/index.ts +0 -3
  177. package/Contexts/CachedDimensionsContext/__tests__/index.test.ts +154 -0
  178. package/Contexts/ScreenContext/index.tsx +25 -18
  179. package/Contexts/ScreenTrackedViewPositionsContext/__tests__/index.test.tsx +1 -1
  180. package/Decorators/Analytics/index.tsx +6 -5
  181. package/Decorators/ConfigurationWrapper/__tests__/__snapshots__/withConfigurationProvider.test.tsx.snap +1 -0
  182. package/Decorators/ConfigurationWrapper/const.ts +1 -0
  183. package/Decorators/ZappPipesDataConnector/__tests__/UrlFeedResolver.test.tsx +39 -21
  184. package/Decorators/ZappPipesDataConnector/__tests__/zappPipesDataConnector.test.js +1 -1
  185. package/Decorators/ZappPipesDataConnector/index.tsx +2 -2
  186. package/Decorators/ZappPipesDataConnector/resolvers/StaticFeedResolver.tsx +1 -1
  187. package/Decorators/ZappPipesDataConnector/resolvers/UrlFeedResolver.tsx +18 -7
  188. package/Helpers/DataSourceHelper/__tests__/itemLimitForData.test.ts +80 -0
  189. package/Helpers/DataSourceHelper/index.ts +19 -0
  190. package/package.json +5 -5
  191. package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.android.tsx +0 -135
  192. package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/types.ts +0 -25
  193. package/Components/OfflineHandler/NotificationView/NotificationView.lg.tsx +0 -112
  194. package/Components/OfflineHandler/NotificationView/NotificationView.samsung.tsx +0 -107
  195. package/Components/OfflineHandler/NotificationView/NotificationView.tsx +0 -153
  196. package/Components/OfflineHandler/NotificationView/__tests__/index.test.tsx +0 -67
  197. package/Components/OfflineHandler/NotificationView/utils.ts +0 -34
  198. package/Components/River/TV/withTVEventHandler.tsx +0 -36
  199. package/Helpers/DataSourceHelper/index.js +0 -19
  200. /package/Components/BackgroundImage/{BackgroundImage.tv.android.tsx → BackgroundImage.android.tv.tsx} +0 -0
  201. /package/Components/BackgroundImage/{BackgroundImage.tv.ios.tsx → BackgroundImage.ios.tv.tsx} +0 -0
  202. /package/Components/BackgroundImage/{BackgroundImage.tv.web.tsx → BackgroundImage.tsx} +0 -0
@@ -0,0 +1,129 @@
1
+ import { compact } from "@applicaster/zapp-react-native-utils/cellUtils";
2
+ import {
3
+ insertButtonsBetweenLabels,
4
+ insertButtonsBetweenLabelContainers,
5
+ mobileOverImagePositionStyles,
6
+ } from "./utils";
7
+ import { Button } from "./Button";
8
+ import { buildActionButtonsModel } from "../../ActionButtonsCore/model";
9
+ import { ViewStyle } from "react-native";
10
+
11
+ const CONTAINER_PREFIX = "mobile_buttons_container";
12
+ const BUTTON_PREFIX = "mobile_button";
13
+
14
+ type MobileActionButtonsProps = {
15
+ value: (key: string) => unknown;
16
+ configuration: Record<string, unknown>;
17
+ placement: "labels" | "over_image"; // Indicates where the component was placed, to apply specific display rules
18
+ };
19
+
20
+ export const MobileActionButtons = ({
21
+ value,
22
+ configuration,
23
+ placement,
24
+ }: MobileActionButtonsProps) => {
25
+ const position = value(`${CONTAINER_PREFIX}_position`);
26
+
27
+ if (placement === "labels" && position === "over_image") {
28
+ return null;
29
+ }
30
+
31
+ if (placement === "over_image" && position !== "over_image") {
32
+ return null;
33
+ }
34
+
35
+ const model = buildActionButtonsModel({
36
+ configuration,
37
+ value,
38
+ containerPrefix: CONTAINER_PREFIX,
39
+ buttonPrefix: BUTTON_PREFIX,
40
+ });
41
+
42
+ if (!model) {
43
+ return null;
44
+ }
45
+
46
+ const {
47
+ container: { stacking, verticalGutter, horizontalGutter },
48
+ } = model;
49
+
50
+ const isVertical = stacking === "vertical";
51
+ const isOverImage = position === "over_image";
52
+ const gutter = isVertical ? verticalGutter : horizontalGutter;
53
+
54
+ const containerPaddings = {
55
+ paddingTop: model.container.margins.top,
56
+ paddingRight: model.container.margins.right,
57
+ paddingBottom: model.container.margins.bottom,
58
+ paddingLeft: model.container.margins.left,
59
+ };
60
+
61
+ const style = {
62
+ alignItems: "center",
63
+ ...(isOverImage
64
+ ? {
65
+ position: "absolute",
66
+ zIndex: 10,
67
+ top: 0,
68
+ left: 0,
69
+ right: 0,
70
+ bottom: 0,
71
+ ...mobileOverImagePositionStyles(
72
+ value(`${CONTAINER_PREFIX}_over_image_position`) as string
73
+ ),
74
+ }
75
+ : {}),
76
+ };
77
+
78
+ const contentStyle = {
79
+ ...containerPaddings,
80
+ flexDirection: isVertical ? "column" : "row",
81
+ width: isVertical ? "100%" : undefined,
82
+ alignSelf: !isOverImage ? model.container.horizontalAlign : undefined,
83
+ alignItems: model.container.horizontalAlign,
84
+ };
85
+
86
+ const elements = compact(
87
+ model.buttons.map(({ renderIndex, specificPrefix, stylePrefix }) => {
88
+ const isNotLast = renderIndex < model.buttons.length - 1;
89
+
90
+ const spacingStyle: ViewStyle = {
91
+ [isVertical ? "marginBottom" : "marginRight"]: isNotLast ? gutter : 0,
92
+ };
93
+
94
+ const button = Button({
95
+ index: renderIndex,
96
+ value,
97
+ stylePrefix,
98
+ specificPrefix,
99
+ spacingStyle,
100
+ isVertical,
101
+ });
102
+
103
+ if (!button) return null;
104
+
105
+ return {
106
+ type: "DataProvider",
107
+ data: [
108
+ {
109
+ func: "identity",
110
+ args: [],
111
+ propName: "entry",
112
+ },
113
+ ],
114
+ elements: [button],
115
+ };
116
+ })
117
+ );
118
+
119
+ return {
120
+ type: "ButtonContainerView",
121
+ style: style,
122
+ additionalProps: {
123
+ contentStyle,
124
+ },
125
+ elements,
126
+ };
127
+ };
128
+
129
+ export { insertButtonsBetweenLabels, insertButtonsBetweenLabelContainers };
@@ -0,0 +1,123 @@
1
+ import {
2
+ insertButtonsBetweenLabelContainers,
3
+ insertButtonsBetweenLabels,
4
+ mobileOverImagePositionStyles,
5
+ } from "..";
6
+
7
+ describe("mobile action insertion helpers", () => {
8
+ const slots = [
9
+ { name: "text_label_1", element: { type: "View", id: "text_label_1" } },
10
+ { name: "text_label_2", element: { type: "View", id: "text_label_2" } },
11
+ ];
12
+
13
+ const labels = slots.map((slot) => slot.element);
14
+ const buttons = { type: "View", name: "buttons" };
15
+
16
+ it("inserts label buttons below target label", () => {
17
+ const result = insertButtonsBetweenLabels(
18
+ { mobile_buttons_container_position: "below_text_label_1" },
19
+ buttons,
20
+ slots
21
+ );
22
+
23
+ expect(result).toEqual([labels[0], buttons, labels[1]]);
24
+ });
25
+
26
+ it("returns labels unchanged when the flat-label target is unknown", () => {
27
+ const result = insertButtonsBetweenLabels(
28
+ { mobile_buttons_container_position: "unknown" },
29
+ buttons,
30
+ slots
31
+ );
32
+
33
+ expect(result).toEqual([labels[0], labels[1]]);
34
+ });
35
+
36
+ it("inserts label-container buttons below nested target", () => {
37
+ const labelContainers = [
38
+ {
39
+ elements: [{ elements: [{ name: "top_text_label_1" }] }],
40
+ },
41
+ ];
42
+
43
+ const result = insertButtonsBetweenLabelContainers(
44
+ { mobile_buttons_container_position: "top_text_label_1" },
45
+ buttons,
46
+ labelContainers
47
+ );
48
+
49
+ expect(result).toEqual([
50
+ {
51
+ elements: [{ elements: [{ name: "top_text_label_1" }, buttons] }],
52
+ },
53
+ ]);
54
+ });
55
+
56
+ it("inserts label-container buttons below direct target in two-level structure", () => {
57
+ const labelContainers = [
58
+ {
59
+ elements: [{ name: "top_text_label_1" }],
60
+ },
61
+ ];
62
+
63
+ const result = insertButtonsBetweenLabelContainers(
64
+ { mobile_buttons_container_position: "top_text_label_1" },
65
+ buttons,
66
+ labelContainers
67
+ );
68
+
69
+ expect(result).toEqual([
70
+ {
71
+ elements: [{ name: "top_text_label_1" }, buttons],
72
+ },
73
+ ]);
74
+ });
75
+
76
+ it("appends container buttons into the last container when target is unknown", () => {
77
+ const labelContainers = [
78
+ {
79
+ elements: [{ elements: [{ name: "top_text_label_1" }] }],
80
+ },
81
+ {
82
+ elements: [{ elements: [{ name: "bottom_text_label_1" }] }],
83
+ },
84
+ ];
85
+
86
+ const result = insertButtonsBetweenLabelContainers(
87
+ { mobile_buttons_container_position: "unknown" },
88
+ buttons,
89
+ labelContainers
90
+ );
91
+
92
+ expect(result).toEqual([
93
+ labelContainers[0],
94
+ {
95
+ elements: [...labelContainers[1].elements, buttons],
96
+ },
97
+ ]);
98
+ });
99
+ });
100
+
101
+ describe("mobileOverImagePositionStyles", () => {
102
+ it("maps bottom left anchor to absolute positioning", () => {
103
+ const result = mobileOverImagePositionStyles("bottom_left");
104
+
105
+ expect(result).toEqual({
106
+ justifyContent: "flex-end",
107
+ alignItems: "flex-start",
108
+ });
109
+ });
110
+
111
+ it("maps center anchor to absolute center positioning", () => {
112
+ const result = mobileOverImagePositionStyles("center");
113
+
114
+ expect(result).toEqual({
115
+ justifyContent: "center",
116
+ alignItems: "center",
117
+ top: 0,
118
+ left: 0,
119
+ right: 0,
120
+ bottom: 0,
121
+ });
122
+ });
123
+ });
@@ -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
+ slots = [] // { name, element } per canonical text label slot
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
+ slots
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,