@applicaster/zapp-react-native-ui-components 16.0.0-alpha.5402127570 → 16.0.0-alpha.5439705823

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 (28) hide show
  1. package/Components/CellRendererResolver/index.ts +1 -1
  2. package/Components/Layout/FullWidthRow.tsx +38 -0
  3. package/Components/MasterCell/DefaultComponents/PressableView.tsx +7 -27
  4. package/Components/MasterCell/DefaultComponents/Text/hooks/useText.ts +0 -4
  5. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/ActionButton.tsx +100 -154
  6. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/TextLabelsContainer.ts +1 -0
  7. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/PressableView.test.tsx +21 -32
  8. package/Components/MasterCell/dataAdapter.ts +15 -5
  9. package/Components/MasterCell/hooks/index.ts +0 -2
  10. package/Components/MasterCell/index.tsx +30 -5
  11. package/Components/MasterCell/utils/__tests__/resolveColor.test.js +169 -132
  12. package/Components/MasterCell/utils/__tests__/resolveColorForProp.test.js +92 -0
  13. package/Components/MasterCell/utils/index.ts +85 -46
  14. package/Components/PlayerContainer/PlayerContainer.tsx +7 -3
  15. package/Components/River/ComponentsMap/ComponentsMap.tsx +2 -2
  16. package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +3 -15
  17. package/Components/Screen/index.tsx +8 -4
  18. package/Components/ZappFrameworkComponents/BarView/BarView.tsx +12 -5
  19. package/Components/ZappFrameworkComponents/BarView/__tests__/BarView.test.tsx +2 -2
  20. package/Contexts/CachedDimensionsContext/__tests__/index.test.ts +154 -0
  21. package/Contexts/CachedDimensionsContext/index.ts +17 -2
  22. package/package.json +5 -5
  23. package/Components/MasterCell/contexts/PressedStateContext.ts +0 -3
  24. package/Components/MasterCell/hooks/usePressedState.ts +0 -4
  25. package/Components/Screen/utils.ts +0 -16
  26. /package/Components/BackgroundImage/{BackgroundImage.tv.android.tsx → BackgroundImage.android.tv.tsx} +0 -0
  27. /package/Components/BackgroundImage/{BackgroundImage.tv.ios.tsx → BackgroundImage.ios.tv.tsx} +0 -0
  28. /package/Components/MasterCell/DefaultComponents/ButtonContainerView/{index.tv.android.tsx → index.android.tv.tsx} +0 -0
@@ -92,7 +92,7 @@ export function CellRendererResolver({
92
92
  component,
93
93
  styles,
94
94
  cellStyles,
95
- cellOptions,
95
+ cellOptions: { ...cellOptions, component },
96
96
  ...cellOptions, // fallback for existing plugins
97
97
  });
98
98
  }
@@ -0,0 +1,38 @@
1
+ import * as React from "react";
2
+ import { StyleSheet, View } from "react-native";
3
+
4
+ const styles = StyleSheet.create({
5
+ row: { flexDirection: "row" },
6
+ basis: {
7
+ maxWidth: "100%",
8
+ flexBasis: "100%",
9
+ },
10
+ });
11
+
12
+ type Props = {
13
+ children: React.ReactNode;
14
+ };
15
+
16
+ /**
17
+ * Constrains its child to exactly the available parent width.
18
+ *
19
+ * UI components such as Hero and List render their items inside a parent
20
+ * `ScrollView` (e.g. when wrapped by tabs). A child placed directly in a
21
+ * scroll container can size itself to its own content rather than to the
22
+ * viewport, which causes items to be measured at a stale width and resize
23
+ * unexpectedly after layout changes such as device rotation.
24
+ *
25
+ * Wrapping the content in a `flexDirection: "row"` parent with a
26
+ * `flexBasis: "100%"` / `maxWidth: "100%"` child forces the child to
27
+ * re-derive its width from the parent on every layout pass instead of
28
+ * caching a measured pixel width, keeping it full-width and stable.
29
+ *
30
+ * @param children - The content to render at full parent width.
31
+ */
32
+ export function FullWidthRow({ children }: Props) {
33
+ return (
34
+ <View style={styles.row}>
35
+ <View style={styles.basis}>{children}</View>
36
+ </View>
37
+ );
38
+ }
@@ -1,16 +1,13 @@
1
- import React, { useCallback } from "react";
2
- import { Pressable } from "react-native";
3
- import { PressedStateContext } from "../contexts/PressedStateContext";
1
+ import React from "react";
2
+ import { TouchableOpacity } from "react-native";
4
3
 
5
4
  type Props = {
6
5
  children?: React.ReactNode;
7
6
  style?: Record<string, unknown>;
8
- pressedStyle?: Record<string, unknown>;
9
7
  testID?: string;
10
8
  accessibilityLabel?: string;
11
9
  accessibilityHint?: string;
12
10
  onPress?: () => void;
13
- testOnly_pressed?: boolean;
14
11
  };
15
12
 
16
13
  export function PressableView({
@@ -19,36 +16,19 @@ export function PressableView({
19
16
  testID,
20
17
  accessibilityLabel,
21
18
  accessibilityHint,
22
- pressedStyle,
23
19
  onPress,
24
- testOnly_pressed = false,
25
20
  }: Props) {
26
21
  return (
27
- <Pressable
28
- testOnly_pressed={testOnly_pressed}
22
+ <TouchableOpacity
23
+ activeOpacity={1}
29
24
  onPress={onPress}
30
25
  testID={testID}
31
26
  accessibilityLabel={accessibilityLabel}
32
27
  accessibilityHint={accessibilityHint}
33
28
  accessible={!!(testID || accessibilityLabel)}
34
- style={useCallback(
35
- ({ pressed }) => [
36
- style,
37
- pressed && {
38
- opacity: 1,
39
- },
40
- pressed && pressedStyle,
41
- ],
42
- [style, pressedStyle]
43
- )}
29
+ style={style}
44
30
  >
45
- {({ pressed }) => {
46
- return (
47
- <PressedStateContext.Provider value={pressed}>
48
- {children}
49
- </PressedStateContext.Provider>
50
- );
51
- }}
52
- </Pressable>
31
+ {children}
32
+ </TouchableOpacity>
53
33
  );
54
34
  }
@@ -83,10 +83,6 @@ export const useTextLabel = ({ label, entry }): string => {
83
83
  return prepareHebrewText(label, isRTL);
84
84
  };
85
85
 
86
- /**
87
- * use `otherProps.state` to set focused or default styles (focusedStyles or normalStyles) to the text component
88
- * for mobile buttons state using usePressedState() and passing isPressed to `state`
89
- * */
90
86
  export const withFocusedStyles = ({ style, otherProps }) => {
91
87
  const state = R.path(["state"], otherProps);
92
88
 
@@ -1,6 +1,5 @@
1
- import React, { useCallback } from "react";
1
+ import React from "react";
2
2
  import { PressableView } from "../../PressableView";
3
- import { usePressedState } from "../../../hooks";
4
3
 
5
4
  import { ActionButtonController } from "../../ActionButtonsCore/components";
6
5
 
@@ -21,137 +20,7 @@ const isValidElement = (
21
20
  ): child is React.ReactElement<ChildElementProps> =>
22
21
  React.isValidElement(child);
23
22
 
24
- type ActionButtonContentProps = {
25
- style: any;
26
- children: React.ReactNode;
27
- action: any;
28
- focusedStyles: any;
29
- entry: any;
30
- props: Record<string, any>;
31
- actionContext: any;
32
- actionState: any;
33
- isActive: boolean;
34
- onPress: () => any;
35
- };
36
-
37
- const ActionButtonContent = ({
38
- children,
39
- action,
40
- entry,
41
- actionContext,
42
- actionState,
43
- isActive,
44
- }: ActionButtonContentProps) => {
45
- const isPressed = usePressedState();
46
-
47
- const supportsEntryState =
48
- typeof actionContext?.initialEntryState === "function";
49
-
50
- const shouldRenderAsset = Boolean(
51
- supportsEntryState ? actionState?.mobileButtonAssets : false
52
- );
53
-
54
- const shouldRenderLabel = Boolean(
55
- supportsEntryState && resolveLabelText(actionState?.label)
56
- );
57
-
58
- const _cloneChildrenWithState = useCallback(
59
- (nodes?: React.ReactNode): React.ReactNode => {
60
- return React.Children.map(nodes, (child) => {
61
- if (!isValidElement(child)) {
62
- return child;
63
- }
64
-
65
- const role = child.props.mobileActionRole;
66
-
67
- const nextChildren = _cloneChildrenWithState(child.props.children);
68
-
69
- // Prevents of the asset rendering when the action doesn't provide asset for current entry state
70
- if (role === "asset" && !shouldRenderAsset) {
71
- return null;
72
- }
73
-
74
- // Prevents of the label rendering when the action doesn't provide label for current entry state.
75
- // Also prevents of the label container when it doesn't have children to avoid unnecessary empty space.
76
- if (role === "label" && !shouldRenderLabel) {
77
- return null;
78
- }
79
-
80
- // Prevents of the label container rendering when it doesn't have children to avoid unnecessary empty space.
81
- if (
82
- role === "label_container" &&
83
- React.Children.count(nextChildren) === 0
84
- ) {
85
- return null;
86
- }
87
-
88
- const nextProps: Partial<ChildElementProps> = {};
89
-
90
- // Inject asset or uri to Asset component with role asset
91
- if (role === "asset") {
92
- const resolvedAsset = selectByAssetFlavour(
93
- actionState,
94
- action.flavour,
95
- isActive
96
- );
97
-
98
- if (typeof resolvedAsset === "function") {
99
- const AssetComponent = resolvedAsset as CellActionAssetComponent;
100
-
101
- nextProps.asset = (
102
- <AssetComponent
103
- flavour={action.flavour || "flavour_1"}
104
- width={action.width}
105
- height={action.height}
106
- />
107
- );
108
- } else {
109
- nextProps.uri = resolvedAsset;
110
- }
111
- }
112
-
113
- // Inject state and entry to Text component with role label
114
- // Use isPressed state for mobile buttons to set focused state on mobile
115
- if (role === "label") {
116
- nextProps.state = isActive || isPressed ? "focused" : "default";
117
- nextProps.entry = entry;
118
- }
119
-
120
- if (nextChildren !== child.props.children) {
121
- nextProps.children = nextChildren;
122
- }
123
-
124
- return React.cloneElement(child, nextProps);
125
- });
126
- },
127
- [
128
- shouldRenderAsset,
129
- shouldRenderLabel,
130
- actionState,
131
- action.flavour,
132
- action.width,
133
- action.height,
134
- isActive,
135
- isPressed,
136
- entry,
137
- ]
138
- );
139
-
140
- if (!shouldRenderAsset && !shouldRenderLabel) {
141
- return null;
142
- }
143
-
144
- return _cloneChildrenWithState(children);
145
- };
146
-
147
- export const ActionButton = ({
148
- style,
149
- children,
150
- action,
151
- focusedStyles,
152
- entry,
153
- ...props
154
- }) => {
23
+ export const ActionButton = ({ style, children, action, entry, ...props }) => {
155
24
  return (
156
25
  <ActionButtonController
157
26
  action={action}
@@ -162,28 +31,105 @@ export const ActionButton = ({
162
31
  );
163
32
  }}
164
33
  >
165
- {(renderProps) => (
166
- <PressableView
167
- testOnly_pressed={props.testOnly_pressed}
168
- testID={props.testID || `${entry?.id}`}
169
- style={style}
170
- pressedStyle={focusedStyles}
171
- onPress={renderProps.onPress}
172
- accessibilityLabel={props.accessibilityLabel || `${entry?.id}`}
173
- accessibilityHint={props.accessibilityHint}
174
- >
175
- <ActionButtonContent
176
- style={style}
177
- action={action}
178
- focusedStyles={focusedStyles}
179
- entry={entry}
180
- props={props}
181
- {...renderProps}
34
+ {({ actionContext, actionState, isActive, onPress }) => {
35
+ const supportsEntryState =
36
+ typeof actionContext?.initialEntryState === "function";
37
+
38
+ const shouldRenderAsset = Boolean(
39
+ supportsEntryState ? actionState?.mobileButtonAssets : false
40
+ );
41
+
42
+ const shouldRenderLabel = Boolean(
43
+ supportsEntryState && resolveLabelText(actionState?.label)
44
+ );
45
+
46
+ const _cloneChildrenWithState = (
47
+ nodes?: React.ReactNode
48
+ ): React.ReactNode => {
49
+ return React.Children.map(nodes, (child) => {
50
+ if (!isValidElement(child)) {
51
+ return child;
52
+ }
53
+
54
+ const role = child.props.mobileActionRole;
55
+
56
+ const nextChildren = _cloneChildrenWithState(child.props.children);
57
+
58
+ // Prevents of the asset rendering when the action doesn't provide asset for current entry state
59
+ if (role === "asset" && !shouldRenderAsset) {
60
+ return null;
61
+ }
62
+
63
+ // Prevents of the label rendering when the action doesn't provide label for current entry state.
64
+ // Also prevents of the label container when it doesn't have children to avoid unnecessary empty space.
65
+ if (role === "label" && !shouldRenderLabel) {
66
+ return null;
67
+ }
68
+
69
+ // Prevents of the label container rendering when it doesn't have children to avoid unnecessary empty space.
70
+ if (
71
+ role === "label_container" &&
72
+ React.Children.count(nextChildren) === 0
73
+ ) {
74
+ return null;
75
+ }
76
+
77
+ const nextProps: Partial<ChildElementProps> = {};
78
+
79
+ // Inject asset or uri to Asset component with role asset
80
+ if (role === "asset") {
81
+ const resolvedAsset = selectByAssetFlavour(
82
+ actionState,
83
+ action.flavour,
84
+ isActive
85
+ );
86
+
87
+ if (typeof resolvedAsset === "function") {
88
+ const AssetComponent =
89
+ resolvedAsset as CellActionAssetComponent;
90
+
91
+ nextProps.asset = (
92
+ <AssetComponent
93
+ flavour={action.flavour || "flavour_1"}
94
+ width={action.width}
95
+ height={action.height}
96
+ />
97
+ );
98
+ } else {
99
+ nextProps.uri = resolvedAsset;
100
+ }
101
+ }
102
+
103
+ // Inject state and entry to Text component with role label
104
+ if (role === "label") {
105
+ nextProps.state = isActive ? "focused" : "default";
106
+ nextProps.entry = entry;
107
+ }
108
+
109
+ if (nextChildren !== child.props.children) {
110
+ nextProps.children = nextChildren;
111
+ }
112
+
113
+ return React.cloneElement(child, nextProps);
114
+ });
115
+ };
116
+
117
+ if (!shouldRenderAsset && !shouldRenderLabel) {
118
+ return null;
119
+ }
120
+
121
+ return (
122
+ <PressableView
123
+ testID={props.testID || `${entry?.id}`}
124
+ style={isActive ? { ...style, ...props.focusedStyles } : style}
125
+ onPress={onPress}
126
+ accessibilityLabel={props.accessibilityLabel || `${entry?.id}`}
127
+ accessibilityHint={props.accessibilityHint}
182
128
  >
183
- {children}
184
- </ActionButtonContent>
185
- </PressableView>
186
- )}
129
+ {_cloneChildrenWithState(children)}
130
+ </PressableView>
131
+ );
132
+ }}
187
133
  </ActionButtonController>
188
134
  );
189
135
  };
@@ -26,6 +26,7 @@ export const TextLabelsContainer = ({
26
26
  context: actionIdentifier,
27
27
  name: "label_1",
28
28
  },
29
+ state: "default",
29
30
  mobileActionRole: "label",
30
31
  testID: testID ? `${testID}-label` : undefined,
31
32
  ...extraProps,
@@ -1,5 +1,5 @@
1
1
  import React from "react";
2
- import { Pressable, Text as RNText, View } from "react-native";
2
+ import { Text as RNText, TouchableOpacity, View } from "react-native";
3
3
  import { render, fireEvent } from "@testing-library/react-native";
4
4
  import { useActions } from "@applicaster/zapp-react-native-utils/reactHooks/actions";
5
5
 
@@ -8,11 +8,6 @@ import { elementMapper } from "../../../../elementMapper";
8
8
  import { defaultComponents } from "../../../index";
9
9
  import { MobileActionButtons } from "..";
10
10
  import { AssetComponent } from "../AssetComponent";
11
- import { usePressedState } from "../../../../hooks/usePressedState";
12
-
13
- jest.mock("../../../../hooks/usePressedState", () => ({
14
- usePressedState: jest.fn(),
15
- }));
16
11
 
17
12
  jest.mock("@applicaster/zapp-react-native-utils/reactHooks/actions", () => ({
18
13
  useActions: jest.fn(),
@@ -141,7 +136,6 @@ const baseNode = {
141
136
  borderColor: "rgba(4,4,4,1)",
142
137
  },
143
138
  testID: "mobile-action-button",
144
- testOnly_pressed: false,
145
139
  },
146
140
  elements: [
147
141
  {
@@ -196,20 +190,14 @@ const baseNode = {
196
190
  describe("MobileActionButton", () => {
197
191
  beforeEach(() => {
198
192
  jest.clearAllMocks();
199
- (usePressedState as jest.Mock).mockReturnValue(false);
200
193
  });
201
194
 
202
- const renderNode = ({ node = baseNode, pressed = false } = {}) => {
203
- if (pressed) {
204
- node.props.testOnly_pressed = true;
205
- }
206
-
207
- return render(
195
+ const renderNode = (node = baseNode) =>
196
+ render(
208
197
  <React.Fragment>
209
198
  {elementMapper(defaultComponents)(node as never)}
210
199
  </React.Fragment>
211
200
  );
212
- };
213
201
 
214
202
  it("renders nested image and text children through elementMapper", () => {
215
203
  mockUseActions.mockReturnValue(buildActionContext());
@@ -222,13 +210,12 @@ describe("MobileActionButton", () => {
222
210
  });
223
211
 
224
212
  it("applies focused styles when action entry state is active", () => {
225
- (usePressedState as jest.Mock).mockReturnValue(true);
226
213
  mockUseActions.mockReturnValue(buildActionContext({ active: true }));
227
214
 
228
- const { getByTestId, getByText } = renderNode({ pressed: true });
215
+ const { getByTestId, getByText } = renderNode();
229
216
 
230
217
  expect(
231
- getByTestId("mobile-action-button").props.style[2].backgroundColor
218
+ getByTestId("mobile-action-button").props.style.backgroundColor
232
219
  ).toBe("rgba(3,3,3,1)");
233
220
 
234
221
  expect(getByText("Play").props.style).toEqual(
@@ -266,8 +253,6 @@ describe("MobileActionButton", () => {
266
253
  })
267
254
  );
268
255
 
269
- node.props.testOnly_pressed = false;
270
-
271
256
  const { UNSAFE_getAllByType, UNSAFE_getByType, getByTestId, getByText } =
272
257
  render(
273
258
  <React.Fragment>
@@ -275,14 +260,14 @@ describe("MobileActionButton", () => {
275
260
  </React.Fragment>
276
261
  );
277
262
 
278
- const pressable = UNSAFE_getByType(Pressable);
263
+ const pressable = UNSAFE_getByType(TouchableOpacity);
279
264
  const label = getByText("Play");
280
265
  const asset = getByTestId("mobile_action_button_1-asset");
281
266
 
282
267
  expect(pressable.props.testID).toBe("mobile_action_button_1");
283
268
  expect(pressable.props.accessibilityLabel).toBe("entry-1");
284
269
 
285
- expect(pressable.props.style({ pressed: false })[0]).toMatchObject({
270
+ expect(pressable.props.style).toMatchObject({
286
271
  backgroundColor: "rgba(1,1,1,1)",
287
272
  borderColor: "rgba(2,2,2,1)",
288
273
  borderWidth: 1,
@@ -293,11 +278,15 @@ describe("MobileActionButton", () => {
293
278
  height: 24,
294
279
  });
295
280
 
296
- expect(label.props.style[0]).toMatchObject({
297
- color: "rgba(10,10,10,1)",
298
- fontSize: 15,
299
- lineHeight: 24,
300
- });
281
+ expect(label.props.style).toEqual(
282
+ expect.arrayContaining([
283
+ expect.objectContaining({
284
+ color: "rgba(10,10,10,1)",
285
+ fontSize: 15,
286
+ lineHeight: 24,
287
+ }),
288
+ ])
289
+ );
301
290
 
302
291
  expect(UNSAFE_getAllByType(View).length).toBeGreaterThan(0);
303
292
  expect(UNSAFE_getAllByType(RNText).length).toBeGreaterThan(0);
@@ -335,7 +324,7 @@ describe("MobileActionButton", () => {
335
324
  buildActionContext({ mobileButtonAssets: MockAssetComponent })
336
325
  );
337
326
 
338
- const { getByTestId } = renderNode({ node: nodeWithFlavour1 });
327
+ const { getByTestId } = renderNode(nodeWithFlavour1);
339
328
 
340
329
  expect(MockAssetComponent).toHaveBeenCalled();
341
330
  expect(getByTestId("mobile-action-button-asset")).toBeTruthy();
@@ -350,7 +339,7 @@ describe("MobileActionButton", () => {
350
339
  buildActionContext({ mobileButtonAssets: MockAssetComponent })
351
340
  );
352
341
 
353
- renderNode({ node: nodeWithFlavour1 });
342
+ renderNode(nodeWithFlavour1);
354
343
 
355
344
  expect(MockAssetComponent).toHaveBeenCalledWith(
356
345
  expect.objectContaining({ flavour: "flavour_1" }),
@@ -367,7 +356,7 @@ describe("MobileActionButton", () => {
367
356
  buildActionContext({ mobileButtonAssets: MockAssetComponent })
368
357
  );
369
358
 
370
- renderNode({ node: nodeWithFlavour2 });
359
+ renderNode(nodeWithFlavour2);
371
360
 
372
361
  expect(MockAssetComponent).toHaveBeenCalledWith(
373
362
  expect.objectContaining({ flavour: "flavour_2" }),
@@ -380,7 +369,7 @@ describe("MobileActionButton", () => {
380
369
  buildActionContext({ mobileButtonAssets: undefined })
381
370
  );
382
371
 
383
- const { queryByTestId } = renderNode({ node: nodeWithFlavour1 });
372
+ const { queryByTestId } = renderNode(nodeWithFlavour1);
384
373
 
385
374
  expect(queryByTestId("mobile-action-button-asset")).toBeNull();
386
375
  });
@@ -396,7 +385,7 @@ describe("MobileActionButton", () => {
396
385
  })
397
386
  );
398
387
 
399
- const { getByTestId } = renderNode({ node: nodeWithFlavour1 });
388
+ const { getByTestId } = renderNode(nodeWithFlavour1);
400
389
 
401
390
  expect(getByTestId("mobile-action-button-asset")).toBeTruthy();
402
391
  });
@@ -1,5 +1,6 @@
1
1
  import * as R from "ramda";
2
2
  import { isFunction } from "@applicaster/zapp-react-native-utils/functionUtils";
3
+ import { toBooleanWithDefaultFalse } from "@applicaster/zapp-react-native-utils/booleanUtils";
3
4
 
4
5
  import { functionForName } from "./MappingFunctions";
5
6
  import { resolveColor } from "./utils";
@@ -52,7 +53,8 @@ export function configInflater(
52
53
  additionalProps?: Record<string, any>;
53
54
  data?: Array<{ propName: string; func: Function; args: any[] }>;
54
55
  elements?: any[];
55
- }
56
+ },
57
+ allowDynamicColorsOutsideExtensions: boolean
56
58
  ) {
57
59
  const props = data.reduce(
58
60
  (acc, curr) => {
@@ -68,13 +70,13 @@ export function configInflater(
68
70
 
69
71
  if (Array.isArray(elements)) {
70
72
  adjustedElements = elements.map((element) =>
71
- configInflater(entry, element as any)
73
+ configInflater(entry, element as any, allowDynamicColorsOutsideExtensions)
72
74
  );
73
75
  }
74
76
 
75
77
  return {
76
78
  type,
77
- style: resolveColor(entry, style),
79
+ style: resolveColor(entry, style, allowDynamicColorsOutsideExtensions),
78
80
  props,
79
81
  elements: adjustedElements,
80
82
  };
@@ -98,9 +100,17 @@ function resolveElementsNode(entry, state, elements) {
98
100
  }
99
101
 
100
102
  export function defaultDataAdapter(elements) {
101
- return function elementsBuilder({ entry, state = "default" }) {
103
+ return function elementsBuilder({
104
+ entry,
105
+ state = "default",
106
+ allowDynamicColorsOutsideExtensions,
107
+ }) {
102
108
  return resolveElementsNode(entry, state, elements).map((element) =>
103
- configInflater(entry, element)
109
+ configInflater(
110
+ entry,
111
+ element,
112
+ toBooleanWithDefaultFalse(allowDynamicColorsOutsideExtensions)
113
+ )
104
114
  );
105
115
  };
106
116
  }
@@ -1,3 +1 @@
1
1
  export { useAsyncRendering } from "./useAsyncRendering";
2
-
3
- export { usePressedState } from "./usePressedState";
@@ -2,7 +2,10 @@ import * as R from "ramda";
2
2
  import * as React from "react";
3
3
  import { v4 as uuid } from "uuid";
4
4
 
5
+ import { isHorizontalList } from "@applicaster/zapp-react-native-utils/componentsUtils";
6
+ import { isVideoPreviewEnabled } from "@applicaster/zapp-react-native-ui-components/Components/MasterCell/utils";
5
7
  import { useRoute } from "@applicaster/zapp-react-native-utils/reactHooks/navigation";
8
+ import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
6
9
  import { useScreenState } from "@applicaster/zapp-react-native-utils/screenState";
7
10
  import { defaultComponents } from "./DefaultComponents";
8
11
  import { defaultDataAdapter } from "./dataAdapter";
@@ -34,8 +37,8 @@ type MasterCellBuilderOptions = {
34
37
  containerStyle: any;
35
38
  cellOptions?: {
36
39
  isRTL?: boolean;
37
- generateId?: () => string;
38
40
  skipButtons?: boolean;
41
+ component?: ZappComponent;
39
42
  };
40
43
  };
41
44
 
@@ -46,7 +49,7 @@ export function masterCellBuilder({
46
49
  containerStyle = defaultContainerStyle,
47
50
  cellOptions = {},
48
51
  }: MasterCellBuilderOptions) {
49
- const { generateId = uuid, skipButtons = false } = cellOptions;
52
+ const { skipButtons = false, component } = cellOptions;
50
53
 
51
54
  const componentsMap = R.merge(defaultComponents, components);
52
55
  const { View } = componentsMap;
@@ -65,6 +68,7 @@ export function masterCellBuilder({
65
68
  */
66
69
  function MasterCell({ item, state, ...otherProps }: Props) {
67
70
  const { screenData } = useRoute();
71
+ const theme = useTheme();
68
72
 
69
73
  const screenId =
70
74
  screenData && "targetScreen" in screenData
@@ -85,14 +89,35 @@ export function masterCellBuilder({
85
89
  elementsBuilder({
86
90
  entry: item,
87
91
  state: getEntryState(state, entryIsSelected),
92
+ allowDynamicColorsOutsideExtensions:
93
+ theme?.allow_dynamic_colors_outside_extensions,
88
94
  }),
89
- [state, item, entryIsSelected] // Assuming that item won't mutate
95
+ [
96
+ state,
97
+ item,
98
+ entryIsSelected,
99
+ theme?.allow_dynamic_colors_outside_extensions,
100
+ ] // Assuming that item won't mutate
90
101
  );
91
102
 
92
103
  const wrapperRef = React.useRef(null);
93
104
 
94
- const cellUUID = React.useMemo<string>(generateId, [
95
- generateId,
105
+ const cellUUID = React.useMemo<string>(() => {
106
+ const isLiveImage = isVideoPreviewEnabled({
107
+ enable_video_preview: component?.rules?.enable_video_preview,
108
+ player_screen_id: component?.rules?.player_screen_id,
109
+ });
110
+
111
+ const isHL = isHorizontalList(component);
112
+
113
+ return isHL && isLiveImage
114
+ ? `${component?.id}-${component?.styles?.cell_plugin_configuration_id}`
115
+ : uuid();
116
+ }, [
117
+ component?.id,
118
+ component?.styles?.cell_plugin_configuration_id,
119
+ component?.rules?.enable_video_preview,
120
+ component?.rules?.player_screen_id,
96
121
  screenId,
97
122
  item?.id,
98
123
  ]);