@applicaster/zapp-react-native-ui-components 16.0.0-rc.57 → 16.0.0-rc.58

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 (29) hide show
  1. package/Components/ModalComponent/AudioPlayer/Components/Action.tsx +0 -18
  2. package/Components/ModalComponent/AudioPlayer/Components/Button.tsx +17 -24
  3. package/Components/ModalComponent/AudioPlayer/Components/Header.tsx +25 -73
  4. package/Components/ModalComponent/AudioPlayer/Components/Input.tsx +0 -18
  5. package/Components/ModalComponent/AudioPlayer/Components/Item.tsx +75 -79
  6. package/Components/ModalComponent/AudioPlayer/Components/NowPlayingHeaderSection.tsx +89 -0
  7. package/Components/ModalComponent/AudioPlayer/Components/index.ts +3 -1
  8. package/Components/ModalComponent/AudioPlayer/utils/mergeStyles.ts +199 -0
  9. package/Components/ModalComponent/BottomSheetModalContent.tsx +382 -61
  10. package/Components/ModalComponent/Button/index.tsx +5 -3
  11. package/Components/ModalComponent/Header/index.tsx +20 -7
  12. package/Components/ModalComponent/Header/utils.ts +1 -1
  13. package/Components/ModalComponent/ModalBlocksStyleContext.tsx +10 -0
  14. package/Components/ModalComponent/SortableList.tsx +142 -0
  15. package/Components/ModalComponent/TextInputContent.tsx +100 -0
  16. package/Components/ModalComponent/__tests__/BottomSheetModalContent.test.tsx +238 -0
  17. package/Components/ModalComponent/__tests__/TextInputContent.test.tsx +120 -0
  18. package/Components/ModalComponent/__tests__/showTextInput.test.ts +95 -0
  19. package/Components/ModalComponent/index.tsx +11 -0
  20. package/Components/ModalComponent/presets/__tests__/getCell.test.ts +66 -0
  21. package/Components/ModalComponent/presets/dynamicCollectionCell.tsx +78 -0
  22. package/Components/ModalComponent/presets/index.ts +27 -0
  23. package/Components/ModalComponent/presets/selectableCell.tsx +28 -0
  24. package/Components/ModalComponent/presets/standardCell.tsx +41 -0
  25. package/Components/ModalComponent/presets/types.ts +35 -0
  26. package/Components/ModalComponent/showTextInput.ts +43 -0
  27. package/Components/ModalComponent/utils.ts +76 -20
  28. package/package.json +6 -5
  29. /package/Decorators/ZappPipesDataConnector/__tests__/{Hero.js → Hero.tsx} +0 -0
@@ -1,16 +1,54 @@
1
1
  import React, { useCallback, useEffect, useMemo, useRef } from "react";
2
- import { View, ScrollView } from "react-native";
2
+ import {
3
+ ActivityIndicator,
4
+ ScrollView,
5
+ StyleSheet,
6
+ Text,
7
+ View,
8
+ } from "react-native";
3
9
  import { Button } from "./Button";
4
10
  import { ItemIconProps } from "./Button/ItemIcon";
5
11
  import { ItemProps } from "./Button/Item";
6
12
  import { ItemLabelProps } from "./Button/ItemLabel";
7
13
  import { getItemIconProps, getItemLabelProps, getItemProps } from "./utils";
8
14
  import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
15
+ import {
16
+ ContentViewModel,
17
+ OPERATIONS,
18
+ hasOperation,
19
+ useBottomSheetContent,
20
+ } from "@applicaster/zapp-react-native-utils/modalState";
21
+ import { getCell } from "./presets";
9
22
  import type { PluginConfiguration } from "./";
10
-
11
23
  import { ModalHeader as DefaultModalHeader } from "./Header";
12
24
  import { useSafeAreaInsets } from "react-native-safe-area-context";
25
+ import { SortableList } from "./SortableList";
26
+ import {
27
+ AudioPlayerButton,
28
+ NowPlayingHeaderSection,
29
+ VARIANT_WITH_ICON,
30
+ } from "./AudioPlayer/Components";
13
31
  import { BottomSheetDragArea } from "./BottomSheetDragArea";
32
+ import { ModalBlocksStyleContext } from "./ModalBlocksStyleContext";
33
+ import { usePlugins } from "@applicaster/zapp-react-native-redux/hooks";
34
+ import {
35
+ ModalBlocksConfig,
36
+ parseModalBlocksConfiguration,
37
+ } from "@applicaster/zapp-react-native-utils/configurationUtils/modalBlocksParser";
38
+ import { useLocalizedStrings } from "@applicaster/zapp-react-native-utils/localizationUtils";
39
+
40
+ const styles = StyleSheet.create({
41
+ centered: {
42
+ justifyContent: "center",
43
+ alignItems: "center",
44
+ padding: 16,
45
+ },
46
+ errorText: {
47
+ color: "#ff453a",
48
+ fontSize: 14,
49
+ textAlign: "center",
50
+ },
51
+ });
14
52
 
15
53
  type ModalComponentProps = {
16
54
  items: any[];
@@ -22,13 +60,8 @@ type ModalComponentProps = {
22
60
  title?: string;
23
61
  maxHeight?: number;
24
62
  dismiss: () => void;
25
- headerComponent?: React.ComponentType;
26
-
27
- scrollViewContent?: {
28
- renderItem: (item: any, index: number) => React.ReactNode;
29
- items: any[];
30
- } | null;
31
-
63
+ headerComponent?: React.ComponentType<any>;
64
+ headerComponentProps?: Record<string, any>;
32
65
  buttonComponent?: React.ComponentType;
33
66
  iconProps?: ItemIconProps | ((theme: PluginConfiguration) => ItemIconProps);
34
67
  itemProps?:
@@ -37,13 +70,18 @@ type ModalComponentProps = {
37
70
  labelProps?:
38
71
  | ItemLabelProps
39
72
  | ((theme: PluginConfiguration, width: number) => ItemLabelProps);
40
- getSelectedItemIcon: (
73
+ getSelectedItemIcon?: (
41
74
  theme: PluginConfiguration
42
75
  ) => ItemIconProps["asset"] | null;
43
- getDefaultItemIcon: (
76
+ getDefaultItemIcon?: (
44
77
  theme: PluginConfiguration
45
78
  ) => ItemIconProps["asset"] | null;
46
79
  iconPlacement?: "left" | "right";
80
+ viewModel?: ContentViewModel;
81
+ /** Optional body that replaces the items list (header stays DefaultModalHeader). */
82
+ contentComponent?: React.ComponentType<any>;
83
+ contentComponentProps?: Record<string, any>;
84
+ styleOverrides?: ModalBlocksConfig | Record<string, any>;
47
85
  };
48
86
 
49
87
  export function BottomSheetModalContent(props: ModalComponentProps) {
@@ -55,15 +93,18 @@ export function BottomSheetModalContent(props: ModalComponentProps) {
55
93
  dismiss,
56
94
  summary,
57
95
  title,
58
- headerComponent = DefaultModalHeader,
59
- scrollViewContent = null,
60
-
96
+ headerComponent,
97
+ headerComponentProps,
98
+ viewModel = null,
61
99
  buttonComponent: ButtonComponent = Button,
62
100
  getSelectedItemIcon = ({
63
101
  modal_bottom_sheet_item_selected_icon,
64
102
  }: BaseThemePropertiesMobile) => modal_bottom_sheet_item_selected_icon,
65
103
  getDefaultItemIcon = () => null,
66
104
  iconPlacement,
105
+ contentComponent: ContentComponent,
106
+ contentComponentProps = {},
107
+ styleOverrides: propStyleOverrides,
67
108
  } = props;
68
109
 
69
110
  const route = useRef(currentRoute);
@@ -71,7 +112,67 @@ export function BottomSheetModalContent(props: ModalComponentProps) {
71
112
  const paddingTop = Number(theme.modal_bottom_sheet_padding_top);
72
113
  const paddingBottom = Number(theme.modal_bottom_sheet_padding_bottom);
73
114
 
74
- const Header = headerComponent;
115
+ const {
116
+ items: fetchedItems,
117
+ showCentralLoading,
118
+ error,
119
+ handleItemPress,
120
+ role,
121
+ behavior,
122
+ editableCollection,
123
+ themePluginId,
124
+ } = useBottomSheetContent({
125
+ onPress,
126
+ viewModel,
127
+ });
128
+
129
+ const plugins = usePlugins();
130
+
131
+ const plugin = useMemo(
132
+ () => plugins?.find((p) => p.identifier === themePluginId),
133
+ [plugins, themePluginId]
134
+ );
135
+
136
+ const styleOverrides = useMemo(() => {
137
+ if (propStyleOverrides) {
138
+ return propStyleOverrides;
139
+ }
140
+
141
+ const configuration =
142
+ plugin?.configuration || (plugin as any)?.configuration_json;
143
+
144
+ if (!configuration || !themePluginId) return null;
145
+
146
+ return parseModalBlocksConfiguration(configuration, themePluginId);
147
+ }, [propStyleOverrides, plugin, themePluginId]);
148
+
149
+ const pluginLocalizations = useLocalizedStrings({
150
+ localizations:
151
+ plugin?.configuration?.localizations ||
152
+ (plugin as any)?.configuration_json?.localizations,
153
+ });
154
+
155
+ const normalizedPluginId = themePluginId?.replace(/-/g, "_");
156
+
157
+ const localizedHeaderTitle = normalizedPluginId
158
+ ? pluginLocalizations?.[`${normalizedPluginId}_header_title`]
159
+ : undefined;
160
+
161
+ const createPlaylistTitle =
162
+ pluginLocalizations?.create_new_playlist_title || "Create New Playlist";
163
+
164
+ const resolvedTitle = props.title || localizedHeaderTitle || title;
165
+
166
+ const itemsToRender = viewModel ? fetchedItems : items;
167
+ const operations = editableCollection?.operations || [];
168
+ const isSortable = hasOperation(operations, OPERATIONS.REORDER);
169
+
170
+ const cell = getCell(
171
+ viewModel ? role : undefined,
172
+ viewModel ? behavior : undefined
173
+ );
174
+
175
+ const Header = headerComponent ?? DefaultModalHeader;
75
176
 
76
177
  useEffect(() => {
77
178
  if (currentRoute !== route.current) {
@@ -84,14 +185,24 @@ export function BottomSheetModalContent(props: ModalComponentProps) {
84
185
  }, [theme, props.iconProps]);
85
186
 
86
187
  const itemBaseProps = useMemo<ItemProps>(() => {
87
- return getItemProps(theme, props.width, props.itemProps);
88
- }, [theme, props.width, props.itemProps]);
188
+ return getItemProps(
189
+ theme,
190
+ props.width,
191
+ props.itemProps,
192
+ styleOverrides?.item
193
+ );
194
+ }, [theme, props.width, props.itemProps, styleOverrides?.item]);
89
195
 
90
196
  const labelBaseProps = useMemo<ItemLabelProps>(() => {
91
- return getItemLabelProps(theme, props.width, props.labelProps);
92
- }, [theme, props.width, props.labelProps]);
197
+ return getItemLabelProps(
198
+ theme,
199
+ props.width,
200
+ props.labelProps,
201
+ styleOverrides?.item
202
+ );
203
+ }, [theme, props.width, props.labelProps, styleOverrides?.item]);
93
204
 
94
- const handlePress = useCallback(
205
+ const handleStaticPress = useCallback(
95
206
  (item: any) => {
96
207
  onPress(item);
97
208
  dismiss();
@@ -99,58 +210,268 @@ export function BottomSheetModalContent(props: ModalComponentProps) {
99
210
  [onPress, dismiss]
100
211
  );
101
212
 
213
+ const onItemPress = viewModel ? handleItemPress : handleStaticPress;
214
+
215
+ const handleDragEnd = useCallback(
216
+ (params: any) => {
217
+ const from = params?.fromIndex ?? params?.from;
218
+ const to = params?.toIndex ?? params?.to;
219
+
220
+ const reorderedItemIds = Array.isArray(params?.data)
221
+ ? params.data
222
+ .filter((item: any) => !item?.isHeader)
223
+ .map((item: any) => item?.id)
224
+ .filter(Boolean)
225
+ : undefined;
226
+
227
+ if (
228
+ (reorderedItemIds?.length ||
229
+ (typeof from === "number" &&
230
+ typeof to === "number" &&
231
+ from !== to)) &&
232
+ editableCollection?.move
233
+ ) {
234
+ editableCollection.move(reorderedItemIds || []);
235
+ }
236
+ },
237
+ [editableCollection]
238
+ );
239
+
240
+ const renderContext = useMemo(
241
+ () => ({
242
+ theme,
243
+ ButtonComponent,
244
+ current_selection,
245
+ iconBaseProps,
246
+ itemBaseProps,
247
+ labelBaseProps,
248
+ getSelectedItemIcon,
249
+ getDefaultItemIcon,
250
+ iconPlacement,
251
+ editableCollection,
252
+ behavior,
253
+ }),
254
+ [
255
+ theme,
256
+ ButtonComponent,
257
+ current_selection,
258
+ iconBaseProps,
259
+ itemBaseProps,
260
+ labelBaseProps,
261
+ getSelectedItemIcon,
262
+ getDefaultItemIcon,
263
+ iconPlacement,
264
+ editableCollection,
265
+ behavior,
266
+ ]
267
+ );
268
+
102
269
  const bottomInset = useSafeAreaInsets().bottom;
103
270
 
104
- return (
105
- <View
106
- style={{
107
- maxHeight: props.maxHeight,
108
- paddingTop: paddingTop,
109
- }}
110
- >
111
- {Header ? (
112
- <BottomSheetDragArea>
113
- <Header
271
+ const hasNowPlaying = hasOperation(operations, OPERATIONS.NOW_PLAYING);
272
+ const hasClearAll = hasOperation(operations, OPERATIONS.CLEAR_ALL);
273
+ const hasAdd = hasOperation(operations, OPERATIONS.ADD);
274
+
275
+ const handleDeleteCollection = useCallback(() => {
276
+ editableCollection?.deleteCollection?.();
277
+ }, [editableCollection]);
278
+
279
+ const handleCreatePlaylist = useCallback(() => {
280
+ editableCollection?.add?.({
281
+ id: "create_new_playlist",
282
+ title: createPlaylistTitle,
283
+ });
284
+ }, [editableCollection, createPlaylistTitle]);
285
+
286
+ const headers = useMemo(() => {
287
+ const list: Array<{ index: number; component: () => React.ReactElement }> =
288
+ [];
289
+
290
+ if (hasNowPlaying) {
291
+ list.push({
292
+ index: list.length,
293
+ component: () => (
294
+ <NowPlayingHeaderSection
295
+ onClear={hasClearAll ? handleDeleteCollection : undefined}
296
+ />
297
+ ),
298
+ });
299
+ }
300
+
301
+ if (hasAdd) {
302
+ list.push({
303
+ index: list.length,
304
+ component: () => (
305
+ <View
306
+ style={{ paddingHorizontal: 20, paddingTop: 8, paddingBottom: 12 }}
307
+ >
308
+ <AudioPlayerButton
309
+ configuration={VARIANT_WITH_ICON}
310
+ data={{ title: createPlaylistTitle }}
311
+ onPress={handleCreatePlaylist}
312
+ />
313
+ </View>
314
+ ),
315
+ });
316
+ }
317
+
318
+ return list.length > 0 ? list : undefined;
319
+ }, [
320
+ hasNowPlaying,
321
+ hasClearAll,
322
+ handleDeleteCollection,
323
+ hasAdd,
324
+ handleCreatePlaylist,
325
+ ]);
326
+
327
+ const renderBody = () => {
328
+ if (ContentComponent) {
329
+ const shouldWrapInScrollView =
330
+ !contentComponentProps?.disableScrollViewWrap &&
331
+ !(ContentComponent as any)?.disableScrollViewWrap;
332
+
333
+ if (shouldWrapInScrollView) {
334
+ return (
335
+ <ScrollView
336
+ contentContainerStyle={{
337
+ paddingBottom: paddingBottom + bottomInset,
338
+ }}
339
+ >
340
+ <ContentComponent
341
+ {...contentComponentProps}
342
+ dismiss={dismiss}
343
+ width={props.width}
344
+ maxHeight={props.maxHeight}
345
+ />
346
+ </ScrollView>
347
+ );
348
+ }
349
+
350
+ return (
351
+ <View
352
+ style={{
353
+ flexShrink: 1,
354
+ paddingBottom: paddingBottom + bottomInset,
355
+ }}
356
+ >
357
+ <ContentComponent
358
+ {...contentComponentProps}
114
359
  dismiss={dismiss}
115
- configuration={theme}
116
- summary={summary}
117
- title={title}
360
+ width={props.width}
361
+ maxHeight={props.maxHeight}
118
362
  />
119
- </BottomSheetDragArea>
120
- ) : null}
363
+ </View>
364
+ );
365
+ }
366
+
367
+ if (showCentralLoading) {
368
+ const loaderColor =
369
+ (theme as any)?.modal_bottom_sheet_activity_indicator_color ||
370
+ (theme as any)?.cell_title_color ||
371
+ "#ffffff";
372
+
373
+ return (
374
+ <View
375
+ style={[
376
+ styles.centered,
377
+ { backgroundColor: theme.modal_bottom_sheet_background_color },
378
+ ]}
379
+ >
380
+ <ActivityIndicator color={loaderColor} />
381
+ </View>
382
+ );
383
+ }
384
+
385
+ if (error) {
386
+ return (
387
+ <View style={styles.centered}>
388
+ <Text style={styles.errorText}>
389
+ {typeof error === "string" ? error : error.message}
390
+ </Text>
391
+ </View>
392
+ );
393
+ }
394
+
395
+ if (isSortable) {
396
+ return (
397
+ <SortableList
398
+ sortableData={itemsToRender}
399
+ headers={headers}
400
+ scrollViewStyle={{
401
+ flexShrink: 1,
402
+ }}
403
+ contentContainerStyle={{
404
+ paddingBottom: paddingBottom + bottomInset,
405
+ }}
406
+ onDragEnd={handleDragEnd}
407
+ renderItem={({ item, index, renderHandle }) =>
408
+ cell.renderItem({
409
+ item,
410
+ index,
411
+ onPress: onItemPress,
412
+ context: renderContext,
413
+ renderHandle,
414
+ })
415
+ }
416
+ />
417
+ );
418
+ }
419
+
420
+ return (
121
421
  <ScrollView
122
422
  overScrollMode="never"
123
423
  bounces={false}
124
- showsVerticalScrollIndicator={false}
125
424
  contentContainerStyle={{
126
425
  paddingBottom: paddingBottom + bottomInset,
127
426
  }}
128
427
  >
129
- {scrollViewContent
130
- ? scrollViewContent.items.map((item, index) =>
131
- scrollViewContent.renderItem(item, index)
132
- )
133
- : null}
134
- {!scrollViewContent &&
135
- items.map((item, index) =>
136
- item ? (
137
- <ButtonComponent
138
- key={index}
139
- configuration={theme}
140
- selectedItem={current_selection}
141
- item={item}
142
- onPress={handlePress}
143
- label={theme[item?.label] ?? item?.label}
144
- iconBaseProps={iconBaseProps}
145
- itemBaseProps={itemBaseProps}
146
- labelBaseProps={labelBaseProps}
147
- selectedItemIcon={getSelectedItemIcon(theme)}
148
- defaultItemIcon={getDefaultItemIcon(theme)}
149
- iconPlacement={iconPlacement}
150
- />
151
- ) : null
152
- )}
428
+ {hasNowPlaying ? (
429
+ <NowPlayingHeaderSection
430
+ onClear={hasClearAll ? handleDeleteCollection : undefined}
431
+ />
432
+ ) : null}
433
+ {hasAdd ? (
434
+ <View
435
+ style={{ paddingHorizontal: 20, paddingTop: 8, paddingBottom: 12 }}
436
+ >
437
+ <AudioPlayerButton
438
+ configuration={VARIANT_WITH_ICON}
439
+ data={{ title: createPlaylistTitle }}
440
+ onPress={handleCreatePlaylist}
441
+ />
442
+ </View>
443
+ ) : null}
444
+ {(itemsToRender || []).map((item, index) =>
445
+ cell.renderItem({
446
+ item,
447
+ index,
448
+ onPress: onItemPress,
449
+ context: renderContext,
450
+ })
451
+ )}
153
452
  </ScrollView>
154
- </View>
453
+ );
454
+ };
455
+
456
+ return (
457
+ <ModalBlocksStyleContext.Provider value={styleOverrides as any}>
458
+ <View
459
+ style={{
460
+ maxHeight: props.maxHeight,
461
+ paddingTop: paddingTop,
462
+ }}
463
+ >
464
+ <BottomSheetDragArea>
465
+ <Header
466
+ {...(headerComponentProps || {})}
467
+ dismiss={dismiss}
468
+ configuration={theme}
469
+ summary={summary}
470
+ title={resolvedTitle}
471
+ />
472
+ </BottomSheetDragArea>
473
+ {renderBody()}
474
+ </View>
475
+ </ModalBlocksStyleContext.Provider>
155
476
  );
156
477
  }
@@ -43,11 +43,13 @@ export function Button({
43
43
  }: ButtonProps) {
44
44
  const [focused, setFocused] = useState(false);
45
45
 
46
- const selected = selectedItem && item.value === selectedItem?.value;
46
+ const selected = selectedItem && item?.value === selectedItem?.value;
47
47
 
48
48
  const itemIconPropsAssets = useMemo<ItemIconProps["asset"]>(
49
- () => configuration[item.asset] ?? item.asset ?? defaultItemIcon,
50
- [item.asset, defaultItemIcon]
49
+ () =>
50
+ (item ? (configuration[item.asset] ?? item.asset) : undefined) ??
51
+ defaultItemIcon,
52
+ [item?.asset, defaultItemIcon]
51
53
  );
52
54
 
53
55
  const selectedItemIconPropsAssets = useMemo<ItemIconProps["asset"]>(
@@ -4,6 +4,7 @@ import { Title, TitleProps } from "./Title";
4
4
  import { CloseButton, CloseButtonProps } from "./CloseButton";
5
5
  import { getCloseButtonProps, getTitleProps } from "./utils";
6
6
  import { PluginConfiguration } from "../index";
7
+ import { useModalBlocksStyle } from "../ModalBlocksStyleContext";
7
8
 
8
9
  type Props = {
9
10
  dismiss: () => void;
@@ -28,6 +29,14 @@ const styles = StyleSheet.create({
28
29
 
29
30
  export function ModalHeader(props: Props) {
30
31
  const { configuration, dismiss, onLayout, summary, title } = props;
32
+ const modalBlocksStyle = useModalBlocksStyle();
33
+ const headerBlock = modalBlocksStyle?.header;
34
+
35
+ const containerStyle = useMemo(() => {
36
+ const bg = headerBlock?.container?.default?.backgroundColor;
37
+
38
+ return [styles.container, bg ? { backgroundColor: bg } : null];
39
+ }, [headerBlock?.container?.default?.backgroundColor]);
31
40
 
32
41
  const closeButtonProps = useMemo<CloseButtonProps>(
33
42
  () => ({
@@ -37,13 +46,17 @@ export function ModalHeader(props: Props) {
37
46
  [configuration, dismiss]
38
47
  );
39
48
 
40
- const titleProps = useMemo<TitleProps>(
41
- () => ({
42
- ...getTitleProps(configuration, "title"),
49
+ const titleProps = useMemo<TitleProps>(() => {
50
+ const baseProps = getTitleProps(configuration, "title");
51
+ const titleOverride = headerBlock?.title?.default;
52
+
53
+ return {
54
+ ...baseProps,
43
55
  text: title,
44
- }),
45
- [configuration, title]
46
- );
56
+ ...(titleOverride?.fontColor ? { color: titleOverride.fontColor } : {}),
57
+ ...(titleOverride?.fontSize ? { fontSize: titleOverride.fontSize } : {}),
58
+ };
59
+ }, [configuration, title, headerBlock?.title?.default]);
47
60
 
48
61
  const summaryProps = useMemo<TitleProps>(
49
62
  () => ({
@@ -59,7 +72,7 @@ export function ModalHeader(props: Props) {
59
72
  configuration.modal_bottom_sheet_close_margin_right;
60
73
 
61
74
  return (
62
- <View onLayout={onLayout} style={styles.container}>
75
+ <View onLayout={onLayout} style={containerStyle}>
63
76
  <View style={styles.flex}>
64
77
  {title ? <Title {...titleProps} /> : null}
65
78
  {summary ? <Title {...summaryProps} /> : null}
@@ -18,7 +18,7 @@ export function getCloseButtonProps(config: PluginConfiguration) {
18
18
  return {
19
19
  asset: modal_bottom_sheet_close_asset,
20
20
  assetFocused: modal_bottom_sheet_close_focus_asset,
21
- enabled: modal_bottom_sheet_close_switch,
21
+ enabled: modal_bottom_sheet_close_switch !== false,
22
22
  width: modal_bottom_sheet_close_width,
23
23
  height: modal_bottom_sheet_close_height,
24
24
  marginTop: modal_bottom_sheet_close_margin_top,
@@ -0,0 +1,10 @@
1
+ import React, { useContext } from "react";
2
+ import type { ModalBlocksConfig } from "@applicaster/zapp-react-native-utils/configurationUtils/modalBlocksParser";
3
+
4
+ export const ModalBlocksStyleContext = React.createContext<
5
+ ModalBlocksConfig | Record<string, any> | undefined
6
+ >(undefined);
7
+
8
+ export const useModalBlocksStyle = () => {
9
+ return useContext(ModalBlocksStyleContext);
10
+ };