@applicaster/zapp-react-native-ui-components 13.0.0-alpha.2833001702 → 13.0.0-alpha.2886020207

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 (88) hide show
  1. package/Components/AudioPlayer/AudioPlayer.tsx +7 -4
  2. package/Components/AudioPlayer/helpers.tsx +1 -1
  3. package/Components/Cell/Cell.tsx +3 -13
  4. package/Components/Cell/CellWithFocusable.tsx +21 -15
  5. package/Components/Cell/CellWrapper.tsx +18 -0
  6. package/Components/Cell/styles.ts +17 -0
  7. package/Components/CellRendererResolver/index.ts +2 -1
  8. package/Components/ErrorScreen/index.tsx +8 -0
  9. package/Components/FocusableCell/index.tsx +1 -1
  10. package/Components/FocusableGroup/FocusableTvOS.tsx +34 -86
  11. package/Components/FocusableGroup/index.tsx +0 -3
  12. package/Components/GeneralContentScreen/GeneralContentScreen.tsx +3 -0
  13. package/Components/GeneralContentScreen/utils/useCurationAPI.ts +4 -2
  14. package/Components/GeneralContentScreen/utils/useEventAlerts.ts +30 -0
  15. package/Components/HandlePlayable/HandlePlayable.tsx +21 -27
  16. package/Components/Layout/TV/LayoutBackground.tsx +28 -0
  17. package/Components/Layout/TV/NavBarContainer.tsx +2 -1
  18. package/Components/Layout/TV/ScreenContainer.tsx +8 -2
  19. package/Components/Layout/TV/ScreenLayoutContextProvider.tsx +5 -0
  20. package/Components/Layout/TV/__tests__/ScreenContainer.test.tsx +2 -1
  21. package/Components/Layout/TV/__tests__/__snapshots__/NavBarContainer.test.tsx.snap +1 -0
  22. package/Components/Layout/TV/__tests__/__snapshots__/ScreenContainer.test.tsx.snap +1 -0
  23. package/Components/Layout/TV/__tests__/__snapshots__/index.test.tsx.snap +15 -10
  24. package/Components/Layout/TV/__tests__/index.test.tsx +8 -2
  25. package/Components/Layout/TV/index.tsx +6 -20
  26. package/Components/Layout/TV/index.web.tsx +4 -1
  27. package/Components/MasterCell/DefaultComponents/ActionButton.tsx +2 -3
  28. package/Components/MasterCell/DefaultComponents/Image/Image.ios.tsx +4 -3
  29. package/Components/MasterCell/DefaultComponents/Image/hooks/useImage.ts +11 -7
  30. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/const.ts +3 -0
  31. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/index.ts +6 -6
  32. package/Components/MasterCell/SharedUI/ProgressBar/hooks/useFillInPercent.ts +2 -8
  33. package/Components/MasterCell/utils/behaviorProvider.ts +136 -0
  34. package/Components/MasterCell/utils/index.ts +8 -134
  35. package/Components/ModalComponent/BottomSheetModalContent.tsx +59 -43
  36. package/Components/ModalComponent/Button/Item.tsx +6 -5
  37. package/Components/ModalComponent/Button/assets.ts +1 -1
  38. package/Components/ModalComponent/Button/index.tsx +45 -62
  39. package/Components/ModalComponent/Header/index.tsx +9 -8
  40. package/Components/ModalComponent/utils.ts +55 -7
  41. package/Components/OfflineHandler/utils/index.ts +1 -1
  42. package/Components/PlayerContainer/PlayerContainer.tsx +34 -7
  43. package/Components/PlayerImageBackground/index.tsx +1 -1
  44. package/Components/River/ComponentsMap/ComponentsMap.tsx +14 -3
  45. package/Components/River/ComponentsMap/hooks/useLoadingState.ts +3 -3
  46. package/Components/River/RiverItem.tsx +8 -3
  47. package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +7 -1
  48. package/Components/Screen/TV/__tests__/index.web.test.tsx +26 -0
  49. package/Components/Screen/TV/index.web.tsx +5 -6
  50. package/Components/Screen/__tests__/Screen.test.tsx +22 -14
  51. package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +2 -2
  52. package/Components/Screen/hooks.ts +3 -6
  53. package/Components/TopMarginApplicator/TopMarginApplicator.tsx +2 -3
  54. package/Components/TrackedView/index.tsx +1 -0
  55. package/Components/Transitioner/AnimationManager.js +15 -15
  56. package/Components/VideoLive/PlayerLiveImageComponent.tsx +4 -0
  57. package/Components/VideoLive/__tests__/__snapshots__/PlayerLiveImageComponent.test.tsx.snap +1 -0
  58. package/Components/VideoModal/ModalAnimation/AnimatedPlayerModalWrapper.tsx +1 -1
  59. package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +35 -19
  60. package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.tsx +3 -3
  61. package/Components/VideoModal/ModalAnimation/AnimationComponent.tsx +38 -14
  62. package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +10 -3
  63. package/Components/VideoModal/ModalAnimation/__tests__/getMoveUpValue.test.ts +108 -0
  64. package/Components/VideoModal/ModalAnimation/const.ts +5 -0
  65. package/Components/VideoModal/ModalAnimation/index.ts +2 -0
  66. package/Components/VideoModal/ModalAnimation/utils.ts +30 -0
  67. package/Components/VideoModal/PlayerDetails.tsx +13 -5
  68. package/Components/VideoModal/PlayerWrapper.tsx +35 -22
  69. package/Components/VideoModal/VideoModal.tsx +19 -4
  70. package/Components/VideoModal/__tests__/__snapshots__/PlayerDetails.test.tsx.snap +2 -40
  71. package/Components/VideoModal/utils.ts +3 -4
  72. package/Components/ZappUIComponent/index.tsx +4 -4
  73. package/Contexts/FocusableGroupContext/withFocusableContext.tsx +4 -10
  74. package/Contexts/HeaderOffsetContext/index.tsx +4 -6
  75. package/Contexts/ScreenContext/index.tsx +3 -10
  76. package/Contexts/ScreenLayoutContext/index.tsx +5 -3
  77. package/Decorators/RiverResolver/index.tsx +5 -7
  78. package/Decorators/ZappPipesDataConnector/index.tsx +4 -30
  79. package/package.json +5 -9
  80. package/tsconfig.json +2 -3
  81. package/.babelrc +0 -8
  82. package/Components/Cell/CellStyles/FallbackCellStyle/index.js +0 -157
  83. package/Components/Cell/CellStyles/Hero/index.js +0 -111
  84. package/Components/Cell/CellStyles/ScreenSelector/index.js +0 -68
  85. package/Components/Cell/CellStyles/cellStylesResolver.ts +0 -19
  86. package/Components/Cell/CellStyles/colors.js +0 -40
  87. package/Components/Cell/CellStyles/index.js +0 -15
  88. package/Components/Cell/CellWithFocusable.ios.tsx +0 -126
@@ -1,16 +1,13 @@
1
- import React, { useEffect, useMemo } from "react";
1
+ import React, { useMemo } from "react";
2
2
  import * as R from "ramda";
3
3
  import validateColor from "validate-color";
4
4
  import { platformSelect } from "@applicaster/zapp-react-native-utils/reactUtils";
5
5
  import { useActions } from "@applicaster/zapp-react-native-utils/reactHooks/actions";
6
6
 
7
7
  import { masterCellLogger } from "../logger";
8
- import { playerManager } from "@applicaster/zapp-react-native-utils/appUtils";
9
- import { usePlayer } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/usePlayer";
10
- import { PushTopicManager } from "@applicaster/zapp-react-native-bridge/PushNotifications/PushTopicManager";
11
- import { StorageMultiSelectProvider } from "@applicaster/zapp-react-native-bridge/ZappStorage/StorageMultiSelectProvider";
12
8
  import { getCellState } from "../../Cell/utils";
13
9
  import { getColorFromData } from "@applicaster/zapp-react-native-utils/cellUtils";
10
+ import { isCellSelected, useBehaviorUpdate } from "./behaviorProvider";
14
11
 
15
12
  const hasElementSpecificViewType = (viewType) => (element) => {
16
13
  if (R.isNil(element)) {
@@ -193,147 +190,24 @@ export const getFocusedButtonId = (focusable) => {
193
190
  });
194
191
  };
195
192
 
196
- export const isSelected = (id: string | number, behavior?: Behavior) => {
197
- if (!behavior) {
198
- return false;
199
- }
200
-
201
- if (behavior?.selection_source === "now_playing") {
202
- const player = playerManager.getActivePlayer();
203
-
204
- if (player?.entry?.id === id) {
205
- return true;
206
- }
207
- }
208
-
209
- if (behavior?.select_mode === "single") {
210
- return behavior.current_selection === id;
211
- }
212
-
213
- if (behavior?.select_mode === "multi") {
214
- // TODO: Use generic resolver source
215
-
216
- if (behavior.selection_source === "@{push/topics}") {
217
- const tags = PushTopicManager.getInstance().getRegisteredTags();
218
-
219
- return tags.includes(String(id));
220
- }
221
-
222
- if (Array.isArray(behavior.current_selection)) {
223
- return behavior.current_selection.includes(id);
224
- }
225
-
226
- const currentSelection = String(behavior.current_selection);
227
-
228
- if (currentSelection?.startsWith("@{ctx/")) {
229
- const keyWithoutCtx = currentSelection.substring(
230
- "@{ctx/".length,
231
- currentSelection.length - 1
232
- );
233
-
234
- const selectedItems =
235
- StorageMultiSelectProvider.getProvider(
236
- keyWithoutCtx
237
- )?.getSelectedItems();
238
-
239
- return selectedItems?.includes(String(id));
240
- }
241
- }
242
-
243
- return false;
244
- };
245
-
246
- export const useBehaviorUpdate = (behavior: Behavior) => {
247
- const [lastUpdate, setLastUpdate] = React.useState(null);
248
-
249
- const player = usePlayer();
250
-
251
- const triggerUpdate = () => {
252
- setLastUpdate(Date.now());
253
- };
254
-
255
- // TODO: Create generic RX to state update
256
- useEffect(() => {
257
- // TODO: Use generic resolver source
258
- if (!behavior) {
259
- return;
260
- }
261
-
262
- const currentSelection = String(behavior.current_selection);
263
-
264
- if (currentSelection?.startsWith("@{ctx/")) {
265
- const keyWithoutCtx = currentSelection.substring(
266
- "@{ctx/".length,
267
- currentSelection.length - 1
268
- );
269
-
270
- if (keyWithoutCtx) {
271
- const subscription = StorageMultiSelectProvider.getProvider(
272
- keyWithoutCtx
273
- )
274
- .getObservable()
275
- .subscribe(() => {
276
- triggerUpdate();
277
- });
278
-
279
- return () => {
280
- subscription.unsubscribe();
281
- };
282
- }
283
- }
284
- }, [behavior]);
285
-
286
- useEffect(() => {
287
- if (!behavior) {
288
- return;
289
- }
290
-
291
- if (behavior?.selection_source === "@{push/topics}") {
292
- const subscription = PushTopicManager.getInstance()
293
- .getEntryObservable()
294
- .subscribe(() => {
295
- triggerUpdate();
296
- });
297
-
298
- return () => {
299
- subscription.unsubscribe();
300
- };
301
- }
302
- }, [behavior]);
303
-
304
- useEffect(() => {
305
- if (!behavior) {
306
- return;
307
- }
308
-
309
- if (behavior?.selection_source === "now_playing" && player) {
310
- const subscription = player.getEntryObservable().subscribe(() => {
311
- triggerUpdate();
312
- });
313
-
314
- return () => {
315
- subscription.unsubscribe();
316
- };
317
- }
318
- }, [behavior, player]);
319
-
320
- return lastUpdate;
193
+ export const isSelected = (item: ZappEntry, behavior?: Behavior) => {
194
+ return isCellSelected(item, behavior);
321
195
  };
322
196
 
323
197
  export const useCellState = ({
324
- id,
198
+ item,
325
199
  behavior,
326
200
  focused,
327
201
  }: {
328
- id: string | number;
202
+ item: ZappEntry;
329
203
  behavior: Behavior;
330
204
  focused: boolean;
331
205
  }): CellState => {
332
206
  const lastUpdate = useBehaviorUpdate(behavior);
333
207
 
334
208
  const _isSelected = useMemo(
335
- () => isSelected(id, behavior),
336
- [behavior, id, lastUpdate]
209
+ () => isSelected(item, behavior),
210
+ [behavior, item, lastUpdate]
337
211
  );
338
212
 
339
213
  return getCellState({ focused, selected: _isSelected });
@@ -1,23 +1,16 @@
1
- import React, {
2
- useCallback,
3
- useEffect,
4
- useMemo,
5
- useRef,
6
- useState,
7
- } from "react";
8
- import { View, LayoutChangeEvent, ScrollView } from "react-native";
1
+ import React, { useCallback, useEffect, useMemo, useRef } from "react";
2
+ import { View, ScrollView, StyleSheet, Platform } from "react-native";
9
3
  import { Button } from "./Button";
10
4
  import { ItemIconProps } from "./Button/ItemIcon";
11
5
  import { ItemProps } from "./Button/Item";
12
6
  import { ItemLabelProps } from "./Button/ItemLabel";
13
- import {
14
- defaultItemIconProps,
15
- defaultItemLabelProps,
16
- defaultItemProps,
17
- } from "./utils";
7
+ import { getItemIconProps, getItemLabelProps, getItemProps } from "./utils";
18
8
  import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
19
9
 
10
+ import type { PluginConfiguration } from "./";
11
+
20
12
  import { ModalHeader } from "./Header";
13
+ import { useSafeAreaInsets } from "react-native-safe-area-context";
21
14
 
22
15
  type ModalComponentProps = {
23
16
  items: any[];
@@ -30,32 +23,48 @@ type ModalComponentProps = {
30
23
  maxHeight?: number;
31
24
  dismiss: () => void;
32
25
  buttonComponent?: React.ComponentType;
26
+ iconProps?: ItemIconProps | ((theme: PluginConfiguration) => ItemIconProps);
27
+ itemProps?:
28
+ | ItemProps
29
+ | ((theme: PluginConfiguration, width: number) => ItemProps);
30
+ labelProps?:
31
+ | ItemLabelProps
32
+ | ((theme: PluginConfiguration, width: number) => ItemLabelProps);
33
+ getSelectedItemIcon: (
34
+ theme: PluginConfiguration
35
+ ) => ItemIconProps["asset"] | null;
36
+ getDefaultItemIcon: (
37
+ theme: PluginConfiguration
38
+ ) => ItemIconProps["asset"] | null;
39
+ iconPlacement?: "left" | "right";
33
40
  };
34
41
 
42
+ const styles = StyleSheet.create({
43
+ container: {
44
+ maxHeight: "100%",
45
+ },
46
+ });
47
+
35
48
  export function BottomSheetModalContent(props: ModalComponentProps) {
36
49
  const {
37
50
  items,
38
51
  currentRoute,
39
- maxHeight,
40
52
  current_selection = null,
41
53
  onPress,
42
54
  dismiss,
43
55
  summary,
44
56
  title,
45
57
  buttonComponent: ButtonComponent = Button,
58
+ getSelectedItemIcon = (theme) =>
59
+ theme.modal_bottom_sheet_item_selected_icon,
60
+ getDefaultItemIcon = () => null,
61
+ iconPlacement,
46
62
  } = props;
47
63
 
48
- const [headerHeight, setHeaderHeight] = useState(0);
49
64
  const route = useRef(currentRoute);
50
65
  const theme = useTheme<BaseThemePropertiesMobile>();
51
-
52
- const maxContentHeight = maxHeight
53
- ? maxHeight - headerHeight - Number(theme.modal_bottom_sheet_padding_top)
54
- : undefined;
55
-
56
- const onHeaderLayout = useCallback((event: LayoutChangeEvent) => {
57
- setHeaderHeight(event.nativeEvent.layout.height);
58
- }, []);
66
+ const paddingTop = Number(theme.modal_bottom_sheet_padding_top);
67
+ const paddingBottom = Number(theme.modal_bottom_sheet_padding_bottom);
59
68
 
60
69
  useEffect(() => {
61
70
  if (currentRoute !== route.current) {
@@ -64,55 +73,62 @@ export function BottomSheetModalContent(props: ModalComponentProps) {
64
73
  }, [currentRoute]);
65
74
 
66
75
  const iconBaseProps = useMemo<ItemIconProps>(() => {
67
- return defaultItemIconProps(theme);
68
- }, [theme]);
76
+ return getItemIconProps(theme, props.iconProps);
77
+ }, [theme, props.iconProps]);
69
78
 
70
79
  const itemBaseProps = useMemo<ItemProps>(() => {
71
- return defaultItemProps(theme, props.width);
72
- }, [theme, props.width]);
80
+ return getItemProps(theme, props.width, props.itemProps);
81
+ }, [theme, props.width, props.itemProps]);
73
82
 
74
83
  const labelBaseProps = useMemo<ItemLabelProps>(() => {
75
- return defaultItemLabelProps(theme, props.width);
76
- }, [theme, props.width]);
84
+ return getItemLabelProps(theme, props.width, props.labelProps);
85
+ }, [theme, props.width, props.labelProps]);
86
+
87
+ const handlePress = useCallback(
88
+ (item: any) => {
89
+ onPress(item);
90
+ dismiss();
91
+ },
92
+ [onPress, dismiss]
93
+ );
77
94
 
78
- const handlePress = (item: any) => {
79
- onPress(item);
80
- dismiss();
81
- };
95
+ const bottomInset = useSafeAreaInsets().bottom;
82
96
 
83
97
  return (
84
98
  <View
85
- style={{
86
- paddingTop: Number(theme.modal_bottom_sheet_padding_top),
87
- }}
99
+ style={[
100
+ styles.container,
101
+ {
102
+ paddingTop: paddingTop,
103
+ },
104
+ ]}
88
105
  >
89
106
  <ModalHeader
90
- width={props.width}
91
107
  dismiss={dismiss}
92
108
  configuration={theme}
93
- onLayout={onHeaderLayout}
94
109
  summary={summary}
95
110
  title={title}
96
111
  />
97
112
  <ScrollView
98
- bounces={false}
99
- style={{ maxHeight: maxContentHeight }}
100
113
  contentContainerStyle={{
101
- paddingBottom: Number(theme.modal_bottom_sheet_padding_bottom),
102
- paddingTop: Number(theme.modal_bottom_sheet_padding_top),
114
+ paddingBottom:
115
+ Platform.OS === "ios" ? paddingBottom + bottomInset : paddingBottom,
103
116
  }}
104
117
  >
105
118
  {items.map((item, index) => (
106
119
  <ButtonComponent
107
120
  key={index}
108
- width={props.width}
109
121
  configuration={theme}
110
122
  selectedItem={current_selection}
111
123
  item={item}
112
124
  onPress={handlePress}
125
+ label={theme[item?.label] ?? item?.label}
113
126
  iconBaseProps={iconBaseProps}
114
127
  itemBaseProps={itemBaseProps}
115
128
  labelBaseProps={labelBaseProps}
129
+ selectedItemIcon={getSelectedItemIcon(theme)}
130
+ defaultItemIcon={getDefaultItemIcon(theme)}
131
+ iconPlacement={iconPlacement}
116
132
  />
117
133
  ))}
118
134
  </ScrollView>
@@ -5,7 +5,7 @@ export type ItemProps = {
5
5
  backgroundColor: string;
6
6
  focusedBackgroundColor: string;
7
7
  selectedBackgroundColor?: string;
8
- focusectedBackgroundColor?: string;
8
+ focusedSelectedBackgroundColor?: string;
9
9
  borderRadius: number;
10
10
  marginBottom: number;
11
11
  marginLeft: number;
@@ -15,6 +15,7 @@ export type ItemProps = {
15
15
  paddingBottom: number;
16
16
  paddingRight: number;
17
17
  paddingLeft: number;
18
+ maxWidth: number;
18
19
  focused?: boolean;
19
20
  selected?: boolean;
20
21
  children?: ReactChild[];
@@ -33,11 +34,11 @@ function getBackgroundColor({
33
34
  backgroundColor,
34
35
  focusedBackgroundColor,
35
36
  selectedBackgroundColor,
36
- focusectedBackgroundColor,
37
+ focusedSelectedBackgroundColor,
37
38
  }) {
38
39
  switch (true) {
39
40
  case selected && focused:
40
- return focusectedBackgroundColor;
41
+ return focusedSelectedBackgroundColor;
41
42
  case selected && !focused:
42
43
  return selectedBackgroundColor;
43
44
  case !selected && focused:
@@ -51,7 +52,7 @@ export function Item(props: ItemProps) {
51
52
  const {
52
53
  backgroundColor,
53
54
  focusedBackgroundColor,
54
- focusectedBackgroundColor,
55
+ focusedSelectedBackgroundColor,
55
56
  selectedBackgroundColor,
56
57
  children,
57
58
  focused,
@@ -68,7 +69,7 @@ export function Item(props: ItemProps) {
68
69
  backgroundColor: getBackgroundColor({
69
70
  selected,
70
71
  focused,
71
- focusectedBackgroundColor,
72
+ focusedSelectedBackgroundColor,
72
73
  focusedBackgroundColor,
73
74
  selectedBackgroundColor,
74
75
  backgroundColor,
@@ -1,4 +1,4 @@
1
1
  /* eslint-disable max-len */
2
2
 
3
3
  export const defaultSelectedAsset: string =
4
- "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAYAAABV7bNHAAAACXBIWXMAACE4AAAhOAFFljFgAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAGsSURBVHgB7drdaYRAFIbhk2yuBTuwlXSQDrKdrOnErSSlpATBe0nOBxEMEXV+nDnjfg8MwiK4vKjMDIoQERERERERERHRY+j7vtHxJvTfb5wvHd/DMLyLpyc5IcTRw6eOZvrtcrlcq6q6i6PTBVqKM/GJdKpAa3EmrpGe5ST2xIFxHF/FwSnuoL1x1L2u66s4KD7QkXGg6EBHx4FiA6WIA0UGShUHiguUMg4UFSh1HCgmUI44UESgXHEgaCatf/wWslLeeY1GMsWBF/GEOHpodeouGkl8Vso7rtFIxjjg9YhNcea/+W4nrFyjkcxxwDnQUpxJrEhW4oBToLU4k9BIluKA60t6M6i+kzrfF7e1OODziLV6uG2d53onWYwDvi/pViJGshoHvCeKsSJZjgNBM+nQSNbjQPBSwzdSCXEgylrMNVIpcSDaYnVvJPWhA9OAZuO87HEg6mreIdIWE3Eg+nZHhEhm4sAh+0EBkUzFgcM2zDwimYsDh+4oOkQyGQcO33LdEclsHEiyJ70SyXScpBAJX3vNRif01yxSJ7SMH1USEREREREREREV7Ad//SBSU+GIIAAAAABJRU5ErkJggg==";
4
+ "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAYAAABV7bNHAAAACXBIWXMAACE4AAAhOAFFljFgAAAA+0lEQVR4nO3a3QnCQBBF4ZRiSXZgKVqKnVnCQBpQwb0QJA752ZjszPlg3/JwOU8b2K4DAAAAAAAAkIOZnczsvPeOQypxHmb27Pv+sveeQxnG0SFSMRaHSIUXJ32kKXHKue+99e+I4yCOgzgO4jiI4yCOgzgO4jiajfMedd366t50nK3/b0LE2SpSqDi1I4WMUytSs3G6z/jbhOGLIzUdR7aKFCKO1I4UKo7UihQyjqyNFDqOLI2UIo7MjZQqjkyNVL7LFUdmRMoXRypEihtHVkSKH0cWRMoTR2ZEyhdHJkTKG0ecSMSRkUjE+TaIRJxfeFQJAAAAAAAANOwFT0Mt2UgcaMoAAAAASUVORK5CYII=";
@@ -3,12 +3,10 @@ import { StyleSheet, TouchableOpacity, View } from "react-native";
3
3
  import { Item, ItemProps } from "./Item";
4
4
  import { ItemIcon, ItemIconProps } from "./ItemIcon";
5
5
  import { ItemLabel, ItemLabelProps } from "./ItemLabel";
6
- import * as assets from "./assets";
7
6
  import { defaultSelectedAsset } from "./assets";
8
7
 
9
8
  type ButtonProps = {
10
9
  configuration: any;
11
- width: number;
12
10
  selectedItem?: any;
13
11
  item: any; // Adjust type as needed
14
12
  onPress: (item: any) => void; // Adjust type as needed
@@ -17,6 +15,9 @@ type ButtonProps = {
17
15
  labelBaseProps?: ItemLabelProps;
18
16
  disabled?: boolean;
19
17
  label?: string | Record<string, string>;
18
+ iconPlacement?: "left" | "right";
19
+ selectedItemIcon?: ItemIconProps["asset"];
20
+ defaultItemIcon?: ItemIconProps["asset"];
20
21
  };
21
22
 
22
23
  const styles = StyleSheet.create({
@@ -31,81 +32,63 @@ export function Button({
31
32
  item,
32
33
  onPress,
33
34
  configuration,
34
- width,
35
35
  iconBaseProps,
36
- itemBaseProps,
36
+ itemBaseProps: itemProps,
37
37
  labelBaseProps,
38
38
  label,
39
+ iconPlacement = "left",
40
+ defaultItemIcon,
41
+ selectedItemIcon,
39
42
  disabled = false,
40
43
  }: ButtonProps) {
41
44
  const [focused, setFocused] = useState(false);
42
45
 
43
- const selected = useMemo(
44
- () => selectedItem && item.value === selectedItem?.value,
45
- [selectedItem, selectedItem]
46
- );
47
-
48
- const itemProps = useMemo<ItemProps>(
49
- () => ({
50
- ...itemBaseProps,
51
- width,
52
- }),
53
- [configuration, width]
54
- );
46
+ const selected = selectedItem && item.value === selectedItem?.value;
55
47
 
56
- const itemIconProps = useMemo<ItemIconProps>(
57
- () => ({
58
- ...iconBaseProps,
59
- asset: configuration[item.asset] ?? assets[item.asset] ?? item.asset,
60
- }),
61
- [configuration, item.asset]
48
+ const itemIconPropsAssets = useMemo<ItemIconProps["asset"]>(
49
+ () => configuration[item.asset] ?? item.asset ?? defaultItemIcon,
50
+ [item.asset, defaultItemIcon]
62
51
  );
63
52
 
64
- const selectedItemIconProps = useMemo<ItemIconProps>(
65
- () => ({
66
- ...iconBaseProps,
67
- asset:
68
- configuration["modal_bottom_sheet_item_selected_icon"] ||
69
- defaultSelectedAsset,
70
- marginRight: 10,
71
- }),
72
- [configuration]
53
+ const selectedItemIconPropsAssets = useMemo<ItemIconProps["asset"]>(
54
+ () => selectedItemIcon || defaultSelectedAsset,
55
+ [selectedItemIcon]
73
56
  );
74
57
 
75
- const itemLabelProps = useMemo<ItemLabelProps>(
76
- () => ({
77
- ...labelBaseProps,
78
- label: label ?? configuration[item?.label] ?? item?.label ?? null,
79
- }),
80
- [configuration, item?.label]
81
- );
58
+ const renderItemIcon =
59
+ itemIconPropsAssets && itemIconPropsAssets.length > 0 ? (
60
+ <ItemIcon {...iconBaseProps} asset={itemIconPropsAssets} />
61
+ ) : null;
82
62
 
83
63
  if (disabled) return null;
84
64
 
85
65
  return (
86
- <View style={{ maxWidth: width }}>
87
- <TouchableOpacity
88
- activeOpacity={1}
89
- onPress={() => onPress(item)}
90
- onPressIn={() => setFocused(true)}
91
- onPressOut={() => setFocused(false)}
92
- >
93
- <Item {...itemProps} focused={focused} selected={selected}>
94
- <View style={styles.label_icon_container}>
95
- {itemIconProps.asset && itemIconProps.asset.length > 0 ? (
96
- <ItemIcon {...itemIconProps} />
97
- ) : null}
98
- {itemLabelProps.label ? (
99
- <ItemLabel
100
- {...itemLabelProps}
101
- focused={focused}
102
- selected={selected}
103
- />
104
- ) : null}
105
- </View>
106
- {selected ? <ItemIcon {...selectedItemIconProps} /> : null}
107
- </Item>
108
- </TouchableOpacity>
109
- </View>
66
+ <TouchableOpacity
67
+ activeOpacity={1}
68
+ onPress={() => onPress(item)}
69
+ onPressIn={() => setFocused(true)}
70
+ onPressOut={() => setFocused(false)}
71
+ >
72
+ <Item {...itemProps} focused={focused} selected={selected}>
73
+ <View style={styles.label_icon_container}>
74
+ {iconPlacement === "left" && renderItemIcon}
75
+
76
+ {label ? (
77
+ <ItemLabel
78
+ {...labelBaseProps}
79
+ label={label ?? null}
80
+ focused={focused}
81
+ selected={selected}
82
+ />
83
+ ) : null}
84
+ </View>
85
+
86
+ {selected ? (
87
+ <ItemIcon {...iconBaseProps} asset={selectedItemIconPropsAssets} />
88
+ ) : (
89
+ iconPlacement === "right" && renderItemIcon
90
+ )}
91
+ </Item>
92
+ </TouchableOpacity>
110
93
  );
111
94
  }
@@ -8,13 +8,18 @@ import { PluginConfiguration } from "../index";
8
8
  type Props = {
9
9
  dismiss: () => void;
10
10
  configuration: PluginConfiguration;
11
- width: number;
12
11
  onLayout?: (event: LayoutChangeEvent) => void;
13
12
  summary?: string;
14
13
  title?: string;
15
14
  };
16
15
 
17
16
  const styles = StyleSheet.create({
17
+ noFlex: {
18
+ flex: 0,
19
+ },
20
+ flex: {
21
+ flex: 1,
22
+ },
18
23
  container: {
19
24
  flexDirection: "row",
20
25
  alignItems: "center",
@@ -22,7 +27,7 @@ const styles = StyleSheet.create({
22
27
  });
23
28
 
24
29
  export function ModalHeader(props: Props) {
25
- const { configuration, dismiss, width, onLayout, summary, title } = props;
30
+ const { configuration, dismiss, onLayout, summary, title } = props;
26
31
 
27
32
  const closeButtonProps = useMemo<CloseButtonProps>(
28
33
  () => ({
@@ -55,15 +60,11 @@ export function ModalHeader(props: Props) {
55
60
 
56
61
  return (
57
62
  <View onLayout={onLayout} style={styles.container}>
58
- <View
59
- style={{
60
- width: width - buttonsContainerWidth,
61
- }}
62
- >
63
+ <View style={styles.flex}>
63
64
  {title ? <Title {...titleProps} /> : null}
64
65
  {summary ? <Title {...summaryProps} /> : null}
65
66
  </View>
66
- <View style={{ width: buttonsContainerWidth }}>
67
+ <View style={[styles.noFlex, { width: buttonsContainerWidth }]}>
67
68
  <CloseButton {...closeButtonProps} />
68
69
  </View>
69
70
  </View>
@@ -1,15 +1,18 @@
1
1
  import { platformSelect } from "@applicaster/zapp-react-native-utils/reactUtils";
2
- import { PluginConfiguration } from "./index";
2
+ import { PluginConfiguration } from "./";
3
+ import { ItemIconProps } from "./Button/ItemIcon";
4
+ import { ItemLabelProps } from "./Button/ItemLabel";
5
+ import { ItemProps } from "./Button/Item";
3
6
 
4
7
  export function defaultItemProps(
5
8
  config: PluginConfiguration,
6
9
  maxWidth: number
7
- ) {
10
+ ): ItemProps {
8
11
  const {
9
12
  modal_bottom_sheet_item_background_color,
10
13
  modal_bottom_sheet_item_focus_background_color,
11
14
  modal_bottom_sheet_item_selected_background_color,
12
- modal_bottom_sheet_item_focusected_background_color,
15
+ modal_bottom_sheet_item_focused_selected_background_color,
13
16
  modal_bottom_sheet_item_corner_radius,
14
17
  modal_bottom_sheet_item_margin_bottom,
15
18
  modal_bottom_sheet_item_margin_left,
@@ -25,8 +28,8 @@ export function defaultItemProps(
25
28
  backgroundColor: modal_bottom_sheet_item_background_color,
26
29
  focusedBackgroundColor: modal_bottom_sheet_item_focus_background_color,
27
30
  selectedBackgroundColor: modal_bottom_sheet_item_selected_background_color,
28
- focusectedBackgroundColor:
29
- modal_bottom_sheet_item_focusected_background_color,
31
+ focusedSelectedBackgroundColor:
32
+ modal_bottom_sheet_item_focused_selected_background_color,
30
33
  borderRadius: modal_bottom_sheet_item_corner_radius,
31
34
  marginBottom: modal_bottom_sheet_item_margin_bottom,
32
35
  marginTop: modal_bottom_sheet_item_margin_top,
@@ -43,7 +46,7 @@ export function defaultItemProps(
43
46
  export function defaultItemLabelProps(
44
47
  config: PluginConfiguration,
45
48
  sheetWidth: number
46
- ) {
49
+ ): ItemLabelProps {
47
50
  const {
48
51
  modal_bottom_sheet_item_label_android_letter_spacing,
49
52
  modal_bottom_sheet_item_label_ios_letter_spacing,
@@ -103,7 +106,9 @@ export function defaultItemLabelProps(
103
106
  };
104
107
  }
105
108
 
106
- export function defaultItemIconProps(config: PluginConfiguration) {
109
+ export function defaultItemIconProps(
110
+ config: PluginConfiguration
111
+ ): ItemIconProps {
107
112
  const {
108
113
  modal_bottom_sheet_item_icon_height,
109
114
  modal_bottom_sheet_item_icon_width,
@@ -124,3 +129,46 @@ export function defaultItemIconProps(config: PluginConfiguration) {
124
129
  marginRight: modal_bottom_sheet_item_icon_margin_right,
125
130
  };
126
131
  }
132
+
133
+ export function getItemIconProps(
134
+ theme: PluginConfiguration,
135
+ iconProps?: ((theme: PluginConfiguration) => ItemIconProps) | ItemIconProps
136
+ ) {
137
+ if (iconProps) {
138
+ return typeof iconProps === "function" ? iconProps(theme) : iconProps;
139
+ }
140
+
141
+ return defaultItemIconProps(theme);
142
+ }
143
+
144
+ export function getItemLabelProps(
145
+ theme: PluginConfiguration,
146
+ width: number,
147
+ labelProps?:
148
+ | ((theme: PluginConfiguration, width: number) => ItemLabelProps)
149
+ | ItemLabelProps
150
+ ) {
151
+ if (labelProps) {
152
+ return typeof labelProps === "function"
153
+ ? labelProps(theme, width)
154
+ : labelProps;
155
+ }
156
+
157
+ return defaultItemLabelProps(theme, width);
158
+ }
159
+
160
+ export function getItemProps(
161
+ theme: PluginConfiguration,
162
+ width: number,
163
+ itemProps?:
164
+ | ((theme: PluginConfiguration, width: number) => ItemProps)
165
+ | ItemProps
166
+ ) {
167
+ if (itemProps) {
168
+ return typeof itemProps === "function"
169
+ ? itemProps(theme, width)
170
+ : itemProps;
171
+ }
172
+
173
+ return defaultItemProps(theme, width);
174
+ }