@applicaster/zapp-react-native-ui-components 15.0.0-rc.129 → 15.0.0-rc.131

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 (30) hide show
  1. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/model.test.ts +80 -0
  2. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/placement.test.ts +187 -0
  3. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/selectors.test.ts +45 -0
  4. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/style.test.ts +49 -0
  5. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/model.ts +47 -0
  6. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/placement.ts +170 -0
  7. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/selectors.ts +26 -0
  8. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/style.ts +29 -0
  9. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/types.ts +37 -0
  10. package/Components/MasterCell/DefaultComponents/PressableView.tsx +196 -0
  11. package/Components/MasterCell/DefaultComponents/index.ts +2 -0
  12. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Asset.ts +46 -0
  13. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Button.ts +126 -0
  14. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/ButtonContainerView.ts +23 -0
  15. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Spacer.ts +16 -0
  16. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/TextLabel.ts +67 -0
  17. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/TextLabelsContainer.ts +32 -0
  18. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/PressableView.test.tsx +191 -0
  19. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/builders.test.ts +140 -0
  20. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/index.test.ts +222 -0
  21. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/helpers.ts +105 -0
  22. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/index.ts +104 -0
  23. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/__tests__/insertButtons.test.ts +118 -0
  24. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/index.ts +73 -0
  25. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/__tests__/index.test.ts +86 -0
  26. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/index.ts +35 -52
  27. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/__tests__/getPluginIdentifier.test.ts +35 -171
  28. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/index.ts +36 -145
  29. package/Components/MasterCell/elementMapper.tsx +1 -0
  30. package/package.json +5 -5
@@ -0,0 +1,191 @@
1
+ import React from "react";
2
+ import { render, fireEvent } from "@testing-library/react-native";
3
+ import { useActions } from "@applicaster/zapp-react-native-utils/reactHooks/actions";
4
+
5
+ import { elementMapper } from "../../../../elementMapper";
6
+ import { defaultComponents } from "../../../index";
7
+
8
+ jest.mock("@applicaster/zapp-react-native-utils/reactHooks/actions", () => ({
9
+ useActions: jest.fn(),
10
+ }));
11
+
12
+ jest.mock("@applicaster/zapp-react-native-utils/theme", () => ({
13
+ useTheme: () => ({}),
14
+ }));
15
+
16
+ jest.mock("@applicaster/zapp-react-native-utils/localizationUtils", () => ({
17
+ useIsRTL: jest.fn(() => false),
18
+ }));
19
+
20
+ jest.mock(
21
+ "@applicaster/zapp-react-native-utils/reactHooks/navigation/useNavigation",
22
+ () => ({
23
+ useNavigation: jest.fn(() => ({
24
+ currentRoute: "home",
25
+ videoModalState: {
26
+ visible: false,
27
+ },
28
+ })),
29
+ })
30
+ );
31
+
32
+ jest.mock(
33
+ "@applicaster/zapp-react-native-utils/reactHooks/navigation/usePathname",
34
+ () => ({
35
+ usePathname: jest.fn(() => "home"),
36
+ })
37
+ );
38
+
39
+ jest.mock(
40
+ "@applicaster/zapp-react-native-utils/appUtils/accessibilityManager/hooks",
41
+ () => ({
42
+ useAccessibilityManager: jest.fn(() => ({
43
+ addHeading: jest.fn(),
44
+ })),
45
+ })
46
+ );
47
+
48
+ const mockUseActions = useActions as jest.Mock;
49
+
50
+ const item = {
51
+ id: "entry-1",
52
+ } as ZappEntry;
53
+
54
+ const buildActionContext = (entryState = {}) => ({
55
+ initialEntryState: jest.fn(() => ({
56
+ asset: {
57
+ inactive: "https://example.com/image-inactive.png",
58
+ active: "https://example.com/image-active.png",
59
+ },
60
+ label: {
61
+ label_1: "Play",
62
+ },
63
+ ...entryState,
64
+ })),
65
+ invokeAction: jest.fn(),
66
+ addListener: jest.fn(() => jest.fn()),
67
+ isActionAvailable: jest.fn(() => true),
68
+ });
69
+
70
+ const baseNode = {
71
+ type: "PressableView",
72
+ style: {
73
+ backgroundColor: "rgba(1,1,1,1)",
74
+ borderColor: "rgba(2,2,2,1)",
75
+ borderWidth: 1,
76
+ },
77
+ props: {
78
+ item,
79
+ action: {
80
+ identifier: "navigation_action",
81
+ },
82
+ focusedStyles: {
83
+ backgroundColor: "rgba(3,3,3,1)",
84
+ borderColor: "rgba(4,4,4,1)",
85
+ },
86
+ testID: "mobile-action-button",
87
+ },
88
+ elements: [
89
+ {
90
+ type: "Image",
91
+ style: {
92
+ width: 24,
93
+ height: 24,
94
+ },
95
+ props: {
96
+ testID: "mobile-action-button-asset",
97
+ source: {
98
+ context: "navigation_action",
99
+ },
100
+ mobileActionRole: "asset",
101
+ },
102
+ },
103
+ {
104
+ type: "View",
105
+ style: {
106
+ flexDirection: "column",
107
+ },
108
+ props: {
109
+ mobileActionRole: "label_container",
110
+ },
111
+ elements: [
112
+ {
113
+ type: "Text",
114
+ style: {
115
+ fontSize: 15,
116
+ },
117
+ props: {
118
+ testID: "mobile-action-button-label",
119
+ label: {
120
+ context: "navigation_action",
121
+ name: "label_1",
122
+ },
123
+ focusedStyles: {
124
+ color: "rgba(20,20,20,1)",
125
+ },
126
+ normalStyles: {
127
+ color: "rgba(10,10,10,1)",
128
+ },
129
+ mobileActionRole: "label",
130
+ transformText: "default",
131
+ },
132
+ },
133
+ ],
134
+ },
135
+ ],
136
+ };
137
+
138
+ describe("PressableView", () => {
139
+ beforeEach(() => {
140
+ jest.clearAllMocks();
141
+ });
142
+
143
+ const renderNode = (node = baseNode) =>
144
+ render(
145
+ <React.Fragment>
146
+ {elementMapper(defaultComponents)(node as never)}
147
+ </React.Fragment>
148
+ );
149
+
150
+ it("renders nested image and text children through elementMapper", () => {
151
+ mockUseActions.mockReturnValue(buildActionContext());
152
+
153
+ const { getByText, getByTestId } = renderNode();
154
+
155
+ expect(getByTestId("mobile-action-button")).toBeTruthy();
156
+ expect(getByTestId("mobile-action-button-asset")).toBeTruthy();
157
+ expect(getByText("Play")).toBeTruthy();
158
+ });
159
+
160
+ it("applies focused styles when action entry state is active", () => {
161
+ mockUseActions.mockReturnValue(buildActionContext({ active: true }));
162
+
163
+ const { getByTestId, getByText } = renderNode();
164
+
165
+ expect(
166
+ getByTestId("mobile-action-button").props.style.backgroundColor
167
+ ).toBe("rgba(3,3,3,1)");
168
+
169
+ expect(getByText("Play").props.style).toEqual(
170
+ expect.arrayContaining([
171
+ expect.objectContaining({ color: "rgba(20,20,20,1)" }),
172
+ ])
173
+ );
174
+ });
175
+
176
+ it("invokes action on press", () => {
177
+ const actionContext = buildActionContext();
178
+ mockUseActions.mockReturnValue(actionContext);
179
+
180
+ const { getByTestId } = renderNode();
181
+
182
+ fireEvent.press(getByTestId("mobile-action-button"));
183
+
184
+ expect(actionContext.invokeAction).toHaveBeenCalledWith(
185
+ item,
186
+ expect.objectContaining({
187
+ updateState: expect.any(Function),
188
+ })
189
+ );
190
+ });
191
+ });
@@ -0,0 +1,140 @@
1
+ import { Asset } from "../Asset";
2
+ import { Button } from "../Button";
3
+ import { ButtonContainerView } from "../ButtonContainerView";
4
+ import { Spacer } from "../Spacer";
5
+ import { TextLabel } from "../TextLabel";
6
+ import { TextLabelsContainer } from "../TextLabelsContainer";
7
+
8
+ describe("mobile action button builders", () => {
9
+ const configuration = {
10
+ mobile_buttons_container_align: "left",
11
+ mobile_buttons_container_margin_top: 0,
12
+ mobile_buttons_container_margin_right: 12,
13
+ mobile_buttons_container_margin_bottom: 0,
14
+ mobile_buttons_container_margin_left: 6,
15
+ mobile_buttons_container_stacking: "horizontal",
16
+ mobile_button_1_button_enabled: true,
17
+ mobile_button_1_assign_action: "navigation_action",
18
+ mobile_button_1_display_mode: "dynamic",
19
+ mobile_button_1_contents_alignment: "center",
20
+ mobile_button_1_background_color: "rgba(1,1,1,1)",
21
+ mobile_button_1_border_color: "rgba(0,0,0,0)",
22
+ mobile_button_1_border_size: 0,
23
+ mobile_button_1_corner_radius: 8,
24
+ mobile_button_1_padding_top: 10,
25
+ mobile_button_1_padding_right: 10,
26
+ mobile_button_1_padding_bottom: 10,
27
+ mobile_button_1_padding_left: 10,
28
+ mobile_button_1_asset_enabled: true,
29
+ mobile_button_1_action_asset_flavour: "flavour_1",
30
+ mobile_button_1_asset_alignment: "left",
31
+ mobile_button_1_asset_width: 24,
32
+ mobile_button_1_asset_height: 24,
33
+ mobile_button_1_asset_margin_top: 1,
34
+ mobile_button_1_asset_margin_right: 2,
35
+ mobile_button_1_asset_margin_bottom: 3,
36
+ mobile_button_1_asset_margin_left: 4,
37
+ mobile_button_1_label_enabled: true,
38
+ mobile_button_1_font_color: "rgba(255,255,255,1)",
39
+ mobile_button_1_focused_font_color: "rgba(255,0,0,1)",
40
+ mobile_button_1_ios_font_family: "Ubuntu-Bold",
41
+ mobile_button_1_android_font_family: "Ubuntu-Bold",
42
+ mobile_button_1_font_size: 15,
43
+ mobile_button_1_line_height: 24,
44
+ mobile_button_1_ios_letter_spacing: -0.2,
45
+ mobile_button_1_android_letter_spacing: -0.2,
46
+ mobile_button_1_text_transform: "default",
47
+ };
48
+
49
+ const value = (key) => configuration[key];
50
+
51
+ it("builds the container node with content style in additionalProps", () => {
52
+ const result = ButtonContainerView({
53
+ style: { position: "absolute" },
54
+ contentStyle: {
55
+ flexDirection: "row",
56
+ },
57
+ elements: [{ type: "PressableView" }],
58
+ });
59
+
60
+ expect(result).toEqual({
61
+ type: "View",
62
+ style: { position: "absolute" },
63
+ elements: [
64
+ {
65
+ type: "View",
66
+ style: {
67
+ flexDirection: "row",
68
+ },
69
+ elements: [{ type: "PressableView" }],
70
+ },
71
+ ],
72
+ });
73
+ });
74
+
75
+ it("builds the button node with asset and text label children", () => {
76
+ const result = Button({
77
+ index: 0,
78
+ value,
79
+ stylePrefix: "mobile_button_1",
80
+ specificPrefix: "mobile_button_1",
81
+ spacingStyle: { marginRight: 8 },
82
+ });
83
+
84
+ expect(result.type).toBe("PressableView");
85
+ expect(result.additionalProps.action.identifier).toBe("navigation_action");
86
+
87
+ expect(result.elements).toEqual(
88
+ expect.arrayContaining([
89
+ expect.objectContaining({ type: "Image" }),
90
+ expect.objectContaining({ type: "View" }),
91
+ ])
92
+ );
93
+ });
94
+
95
+ it("builds the asset, label container, label and spacer nodes", () => {
96
+ expect(
97
+ Asset({
98
+ prefix: "mobile_button_1",
99
+ value,
100
+ actionIdentifier: "navigation_action",
101
+ })
102
+ ).toEqual(
103
+ expect.objectContaining({
104
+ type: "Image",
105
+ })
106
+ );
107
+
108
+ expect(
109
+ TextLabelsContainer({
110
+ prefix: "mobile_button_1",
111
+ value,
112
+ actionIdentifier: "navigation_action",
113
+ })
114
+ ).toEqual(
115
+ expect.objectContaining({
116
+ type: "View",
117
+ elements: [expect.objectContaining({ type: "Text" })],
118
+ })
119
+ );
120
+
121
+ expect(
122
+ TextLabel({
123
+ prefix: "mobile_button_1",
124
+ value,
125
+ actionIdentifier: "navigation_action",
126
+ })
127
+ ).toEqual(
128
+ expect.objectContaining({
129
+ type: "Text",
130
+ })
131
+ );
132
+
133
+ expect(Spacer({ enabled: true })).toEqual({
134
+ type: "View",
135
+ style: {
136
+ flex: 1,
137
+ },
138
+ });
139
+ });
140
+ });
@@ -0,0 +1,222 @@
1
+ import { MobileActionButtons } from "..";
2
+
3
+ describe("MobileActionButtons", () => {
4
+ const configuration = {
5
+ mobile_buttons_container_buttons_enabled: true,
6
+ mobile_buttons_container_position: "over_image",
7
+ mobile_buttons_container_align: "left",
8
+ mobile_buttons_container_margin_top: 0,
9
+ mobile_buttons_container_margin_right: 0,
10
+ mobile_buttons_container_margin_bottom: 0,
11
+ mobile_buttons_container_margin_left: 0,
12
+ mobile_buttons_container_stacking: "horizontal",
13
+ mobile_buttons_container_horizontal_gutter: 8,
14
+ mobile_buttons_container_vertical_gutter: 8,
15
+ mobile_buttons_container_independent_styles: true,
16
+ mobile_buttons_container_over_image_position: "top_right",
17
+ mobile_button_1_button_enabled: true,
18
+ mobile_button_1_assign_action: "navigation_action",
19
+ mobile_button_1_display_mode: "dynamic",
20
+ mobile_button_1_contents_alignment: "center",
21
+ mobile_button_1_background_color: "rgba(1,1,1,1)",
22
+ mobile_button_1_border_color: "rgba(0,0,0,0)",
23
+ mobile_button_1_border_size: 0,
24
+ mobile_button_1_corner_radius: 8,
25
+ mobile_button_1_padding_top: 10,
26
+ mobile_button_1_padding_right: 10,
27
+ mobile_button_1_padding_bottom: 10,
28
+ mobile_button_1_padding_left: 10,
29
+ mobile_button_1_asset_width: 24,
30
+ mobile_button_1_asset_height: 24,
31
+ mobile_button_1_asset_margin_top: 0,
32
+ mobile_button_1_asset_margin_right: 0,
33
+ mobile_button_1_asset_margin_bottom: 0,
34
+ mobile_button_1_asset_margin_left: 0,
35
+ mobile_button_1_asset_enabled: true,
36
+ mobile_button_1_label_enabled: true,
37
+ mobile_button_1_font_color: "rgba(255,255,255,1)",
38
+ mobile_button_1_focused_font_color: "rgba(255,0,0,1)",
39
+ };
40
+
41
+ const value = (key) => configuration[key];
42
+
43
+ it("renders over-image buttons only for over_image placement", () => {
44
+ const result = MobileActionButtons({
45
+ value,
46
+ configuration,
47
+ placement: "over_image",
48
+ });
49
+
50
+ expect(result).toBeTruthy();
51
+ expect(result.type).toBe("View");
52
+ expect(result.style.position).toBe("absolute");
53
+ expect(result.style.top).toBe(0);
54
+ expect(result.style.right).toBe(0);
55
+ expect(result.elements).toHaveLength(1);
56
+ expect(result.elements[0].type).toBe("View");
57
+ expect(result.elements[0].style.flexDirection).toBe("row");
58
+ expect(result.elements[0].elements[0].type).toBe("PressableView");
59
+
60
+ expect(result.elements[0].elements[0].elements).toEqual(
61
+ expect.arrayContaining([
62
+ expect.objectContaining({ type: "Image" }),
63
+ expect.objectContaining({ type: "View" }),
64
+ ])
65
+ );
66
+ });
67
+
68
+ it("does not render label placement when position is over_image", () => {
69
+ const result = MobileActionButtons({
70
+ value,
71
+ configuration,
72
+ placement: "labels",
73
+ });
74
+
75
+ expect(result).toBeNull();
76
+ });
77
+
78
+ it("renders button 1 when only button 1 is enabled", () => {
79
+ const configurationWithSingleButton = {
80
+ ...configuration,
81
+ mobile_button_1_button_enabled: true,
82
+ mobile_button_2_button_enabled: false,
83
+ mobile_button_3_button_enabled: false,
84
+ };
85
+
86
+ const singleButtonValue = (key) => configurationWithSingleButton[key];
87
+
88
+ const result = MobileActionButtons({
89
+ value: singleButtonValue,
90
+ configuration: configurationWithSingleButton,
91
+ placement: "over_image",
92
+ });
93
+
94
+ expect(result?.elements?.[0]?.elements).toHaveLength(1);
95
+ expect(result?.elements?.[0]?.elements?.[0]?.type).toBe("PressableView");
96
+ });
97
+
98
+ it("renders button 2 when only button 2 is enabled", () => {
99
+ const configurationWithSingleButton = {
100
+ ...configuration,
101
+ mobile_button_1_button_enabled: false,
102
+ mobile_button_2_button_enabled: true,
103
+ mobile_button_3_button_enabled: false,
104
+ };
105
+
106
+ const singleButtonValue = (key) => configurationWithSingleButton[key];
107
+
108
+ const result = MobileActionButtons({
109
+ value: singleButtonValue,
110
+ configuration: configurationWithSingleButton,
111
+ placement: "over_image",
112
+ });
113
+
114
+ expect(result?.elements?.[0]?.elements).toHaveLength(1);
115
+ expect(result?.elements?.[0]?.elements?.[0]?.type).toBe("PressableView");
116
+
117
+ expect(
118
+ result?.elements?.[0]?.elements?.[0]?.additionalProps?.action?.identifier
119
+ ).toBe(configurationWithSingleButton.mobile_button_2_assign_action);
120
+ });
121
+
122
+ it("renders button 3 when only button 3 is enabled", () => {
123
+ const configurationWithSingleButton = {
124
+ ...configuration,
125
+ mobile_button_1_button_enabled: false,
126
+ mobile_button_2_button_enabled: false,
127
+ mobile_button_3_button_enabled: true,
128
+ mobile_button_3_assign_action: "local_storage_favourites_action",
129
+ };
130
+
131
+ const singleButtonValue = (key) => configurationWithSingleButton[key];
132
+
133
+ const result = MobileActionButtons({
134
+ value: singleButtonValue,
135
+ configuration: configurationWithSingleButton,
136
+ placement: "over_image",
137
+ });
138
+
139
+ expect(result?.elements?.[0]?.elements).toHaveLength(1);
140
+ expect(result?.elements?.[0]?.elements?.[0]?.type).toBe("PressableView");
141
+
142
+ expect(
143
+ result?.elements?.[0]?.elements?.[0]?.additionalProps?.action?.identifier
144
+ ).toBe(configurationWithSingleButton.mobile_button_3_assign_action);
145
+ });
146
+
147
+ it("renders sparse slots contiguously while preserving slot-specific actions", () => {
148
+ const configurationWithSparseButtons = {
149
+ ...configuration,
150
+ mobile_button_1_button_enabled: true,
151
+ mobile_button_1_assign_action: "action_1",
152
+ mobile_button_2_button_enabled: false,
153
+ mobile_button_3_button_enabled: true,
154
+ mobile_button_3_assign_action: "action_3",
155
+ };
156
+
157
+ const sparseValue = (key) => configurationWithSparseButtons[key];
158
+
159
+ const result = MobileActionButtons({
160
+ value: sparseValue,
161
+ configuration: configurationWithSparseButtons,
162
+ placement: "over_image",
163
+ });
164
+
165
+ expect(result?.elements?.[0]?.elements).toHaveLength(2);
166
+
167
+ expect(
168
+ result?.elements?.[0]?.elements?.[0]?.additionalProps?.action?.identifier
169
+ ).toBe("action_1");
170
+
171
+ expect(
172
+ result?.elements?.[0]?.elements?.[1]?.additionalProps?.action?.identifier
173
+ ).toBe("action_3");
174
+
175
+ expect(result?.elements?.[0]?.elements?.[0]?.additionalProps?.testID).toBe(
176
+ "mobile_action_button_1"
177
+ );
178
+
179
+ expect(result?.elements?.[0]?.elements?.[1]?.additionalProps?.testID).toBe(
180
+ "mobile_action_button_2"
181
+ );
182
+ });
183
+
184
+ it("maps shared semantic layout data into content styles", () => {
185
+ const configurationWithLayout = {
186
+ ...configuration,
187
+ mobile_buttons_container_align: "middle",
188
+ mobile_buttons_container_margin_top: 5,
189
+ mobile_buttons_container_margin_right: 6,
190
+ mobile_buttons_container_margin_bottom: 7,
191
+ mobile_buttons_container_margin_left: 8,
192
+ mobile_buttons_container_stacking: "vertical",
193
+ };
194
+
195
+ const layoutValue = (key) => configurationWithLayout[key];
196
+
197
+ const result = MobileActionButtons({
198
+ value: layoutValue,
199
+ configuration: configurationWithLayout,
200
+ placement: "labels",
201
+ });
202
+
203
+ expect(result).toBeNull();
204
+
205
+ const overImageResult = MobileActionButtons({
206
+ value: layoutValue,
207
+ configuration: configurationWithLayout,
208
+ placement: "over_image",
209
+ });
210
+
211
+ expect(overImageResult?.elements?.[0]?.style).toMatchObject({
212
+ flexDirection: "column",
213
+ alignItems: "center",
214
+ marginTop: 5,
215
+ marginRight: 6,
216
+ marginBottom: 7,
217
+ marginLeft: 8,
218
+ });
219
+
220
+ expect(overImageResult?.elements?.[0]?.style.alignSelf).toBeUndefined();
221
+ });
222
+ });
@@ -0,0 +1,105 @@
1
+ import { Platform } from "react-native";
2
+
3
+ export function isStringAsset(asset) {
4
+ return typeof asset === "string" || Array.isArray(asset);
5
+ }
6
+
7
+ export function getAssetValue(asset, flavour, fallbackAsset = null) {
8
+ if (!asset) {
9
+ return fallbackAsset;
10
+ }
11
+
12
+ if (typeof asset === "string") {
13
+ return asset;
14
+ }
15
+
16
+ if (Array.isArray(asset)) {
17
+ const flavourIndex = Number(String(flavour || "").replace("flavour_", ""));
18
+
19
+ if (!Number.isNaN(flavourIndex) && flavourIndex > 0) {
20
+ return asset[flavourIndex - 1];
21
+ }
22
+
23
+ return asset[0];
24
+ }
25
+
26
+ return asset.src || fallbackAsset;
27
+ }
28
+
29
+ export function getContentDirection(alignment = "left") {
30
+ switch (alignment) {
31
+ case "right":
32
+ return "row-reverse";
33
+ case "above":
34
+ return "column";
35
+ case "below":
36
+ return "column-reverse";
37
+ case "left":
38
+ default:
39
+ return "row";
40
+ }
41
+ }
42
+
43
+ export function getContentsAlignment(alignment = "center") {
44
+ switch (alignment) {
45
+ case "left":
46
+ return "flex-start";
47
+ case "right":
48
+ return "flex-end";
49
+ case "center":
50
+ default:
51
+ return "center";
52
+ }
53
+ }
54
+
55
+ export function resolveLabelText(label) {
56
+ if (typeof label === "string") {
57
+ return label;
58
+ }
59
+
60
+ return label?.label_1 || "";
61
+ }
62
+
63
+ export function resolveIsActive(actionState, fallbackSelected = false) {
64
+ if (actionState == null) {
65
+ return fallbackSelected;
66
+ }
67
+
68
+ return Boolean(
69
+ actionState?.active ??
70
+ actionState?.isActive ??
71
+ actionState?.selected ??
72
+ actionState?.isSelected ??
73
+ fallbackSelected
74
+ );
75
+ }
76
+
77
+ export function buildLegacySelection(item, actionContext) {
78
+ const defaultIsSelected = (actionContext?.state || []).includes(item);
79
+
80
+ return actionContext?.masterCell?.isSelected
81
+ ? actionContext?.masterCell?.isSelected(item)
82
+ : defaultIsSelected;
83
+ }
84
+
85
+ export function buildLabelStyle(label) {
86
+ if (!label?.enabled) {
87
+ return {};
88
+ }
89
+
90
+ const platformFontFamily =
91
+ Platform.OS === "ios"
92
+ ? label?.iosFontFamily || label?.normalStyle?.fontFamily
93
+ : label?.androidFontFamily || label?.normalStyle?.fontFamily;
94
+
95
+ const platformLetterSpacing =
96
+ Platform.OS === "ios"
97
+ ? label?.iosLetterSpacing
98
+ : label?.androidLetterSpacing;
99
+
100
+ return {
101
+ ...label?.normalStyle,
102
+ fontFamily: platformFontFamily,
103
+ letterSpacing: platformLetterSpacing,
104
+ };
105
+ }