@applicaster/zapp-react-native-ui-components 12.0.0-alpha.4534459584 → 12.0.0-alpha.5062263581

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 (58) hide show
  1. package/Components/AnimatedInOut/index.tsx +18 -10
  2. package/Components/AudioPlayer/AudioPlayer.tsx +9 -1
  3. package/Components/AudioPlayer/__tests__/__snapshots__/audioPlayer.test.js.snap +5 -0
  4. package/Components/AudioPlayer/helpers.tsx +1 -0
  5. package/Components/GeneralContentScreen/GeneralContentScreen.tsx +5 -5
  6. package/Components/HandlePlayable/HandlePlayable.tsx +1 -1
  7. package/Components/HookRenderer/index.tsx +1 -6
  8. package/Components/Image/Image.android.tsx +4 -1
  9. package/Components/Layout/TV/__tests__/__snapshots__/index.test.tsx.snap +1 -41
  10. package/Components/Layout/TV/__tests__/index.test.tsx +1 -34
  11. package/Components/Layout/TV/index.tsx +20 -36
  12. package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +0 -1
  13. package/Components/MasterCell/DefaultComponents/CellBadge/index.tsx +21 -8
  14. package/Components/MasterCell/SharedUI/CollapsibleTextContainer/index.ts +8 -10
  15. package/Components/ModalComponent/BottomSheetModalContent.tsx +121 -0
  16. package/Components/ModalComponent/Button/Item.tsx +82 -0
  17. package/Components/ModalComponent/Button/ItemIcon.tsx +60 -0
  18. package/Components/ModalComponent/Button/ItemLabel.tsx +101 -0
  19. package/Components/ModalComponent/Button/assets.ts +4 -0
  20. package/Components/ModalComponent/Button/index.tsx +111 -0
  21. package/Components/ModalComponent/Header/CloseButton.tsx +59 -0
  22. package/Components/ModalComponent/Header/Title.tsx +70 -0
  23. package/Components/ModalComponent/Header/assets.ts +4 -0
  24. package/Components/ModalComponent/Header/index.tsx +71 -0
  25. package/Components/ModalComponent/Header/utils.ts +79 -0
  26. package/Components/ModalComponent/index.tsx +38 -0
  27. package/Components/ModalComponent/utils.ts +126 -0
  28. package/Components/PlayerContainer/ErrorDisplay/ErrorDisplay.tsx +4 -7
  29. package/Components/River/ComponentsMap/ComponentsMap.tsx +25 -0
  30. package/Components/River/River.tsx +8 -5
  31. package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +1 -1
  32. package/Components/River/useScreenConfiguration.ts +3 -4
  33. package/Components/RouteManager/__tests__/__snapshots__/routeManager.test.js.snap +1 -0
  34. package/Components/RouteManager/__tests__/routeManager.test.js +9 -2
  35. package/Components/RouteManager/index.tsx +4 -2
  36. package/Components/Screen/__tests__/Screen.test.tsx +43 -35
  37. package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +10 -6
  38. package/Components/Screen/__tests__/navigationHandler.test.ts +1 -178
  39. package/Components/Screen/index.tsx +84 -45
  40. package/Components/Screen/navigationHandler.ts +0 -82
  41. package/Components/Screen/orientationHandler.ts +1 -1
  42. package/Components/ScreenResolver/index.tsx +1 -1
  43. package/Components/TrackedView/index.tsx +6 -3
  44. package/Components/VideoLive/LiveImageManager.ts +42 -13
  45. package/Components/VideoLive/PlayerLiveImageComponent.tsx +79 -73
  46. package/Components/VideoModal/PlayerDetails.tsx +3 -0
  47. package/Components/VideoModal/PlayerWrapper.tsx +66 -65
  48. package/Components/VideoModal/__tests__/PlayerDetails.test.tsx +0 -2
  49. package/Components/VideoModal/__tests__/PlayerWrapper.test.tsx +4 -4
  50. package/Components/VideoModal/__tests__/__snapshots__/PlayerWrapper.test.tsx.snap +206 -162
  51. package/Components/VideoModal/hooks/useModalSize.ts +13 -4
  52. package/Components/Viewport/ViewportAware/__tests__/viewportAware.test.js +26 -21
  53. package/Components/Viewport/ViewportTracker/__tests__/viewportTracker.test.js +4 -5
  54. package/Components/index.js +2 -0
  55. package/Contexts/ScreenContext/index.tsx +1 -1
  56. package/Decorators/RiverResolver/index.tsx +0 -4
  57. package/Decorators/index.tsx +0 -4
  58. package/package.json +5 -6
@@ -1,6 +1,7 @@
1
1
  import * as React from "react";
2
2
  import { Animated, EasingFunction, StyleProp, ViewStyle } from "react-native";
3
-
3
+ import { usePrevious } from "@applicaster/zapp-react-native-utils/reactHooks/utils";
4
+ import { toBooleanWithDefaultFalse } from "@applicaster/zapp-react-native-utils/booleanUtils";
4
5
  import { noop } from "@applicaster/zapp-react-native-utils/functionUtils";
5
6
 
6
7
  type AnimatedInterpolatedStyle =
@@ -35,11 +36,16 @@ function getAnimation(config: AnimatedInConfig, phase: "in" | "out") {
35
36
  return config[phase] || config;
36
37
  }
37
38
 
38
- export function AnimatedInOut(props: Props) {
39
- const { visible, animationConfig, children } = props;
40
-
39
+ export function AnimatedInOut({
40
+ visible,
41
+ animationConfig,
42
+ staticStyles = {},
43
+ children,
44
+ }: Props) {
41
45
  const [animatedValue] = React.useState(new Animated.Value(visible ? 1 : 0));
42
- const [animating, setAnimating] = React.useState(null);
46
+ const [animating, setAnimating] = React.useState(undefined);
47
+
48
+ const previousVisible = usePrevious(toBooleanWithDefaultFalse(visible));
43
49
 
44
50
  function startAnimation(toValue, config) {
45
51
  if (animating) {
@@ -55,7 +61,7 @@ export function AnimatedInOut(props: Props) {
55
61
  delay,
56
62
  useNativeDriver: true,
57
63
  }).start(() => {
58
- setAnimating(null);
64
+ setAnimating(undefined);
59
65
  onAnimationEnd();
60
66
  });
61
67
 
@@ -63,12 +69,14 @@ export function AnimatedInOut(props: Props) {
63
69
  }
64
70
 
65
71
  React.useEffect(() => {
66
- if (visible) {
72
+ if (!previousVisible && visible) {
67
73
  startAnimation(1.0, getAnimation(animationConfig, "in"));
68
- } else {
74
+ }
75
+
76
+ if (previousVisible && !visible) {
69
77
  startAnimation(0.0, getAnimation(animationConfig, "out"));
70
78
  }
71
- }, [visible, animating]);
79
+ }, [visible, previousVisible]);
72
80
 
73
81
  const styles = visible
74
82
  ? getAnimation(animationConfig, "in").styles
@@ -77,7 +85,7 @@ export function AnimatedInOut(props: Props) {
77
85
  return (
78
86
  <Animated.View
79
87
  renderToHardwareTextureAndroid={animating}
80
- style={[styles(animatedValue), props?.staticStyles || {}]}
88
+ style={[styles(animatedValue), staticStyles]}
81
89
  >
82
90
  {children}
83
91
  </Animated.View>
@@ -23,6 +23,7 @@ type Props = {
23
23
  audio_player_summary_color?: string;
24
24
  audio_player_rtl?: boolean;
25
25
  magic_background?: boolean;
26
+ audio_player_background_image_query?: string;
26
27
  start_time?: string;
27
28
  end_time?: string;
28
29
  };
@@ -33,6 +34,7 @@ type Props = {
33
34
  audio_player_summary_color?: string;
34
35
  audio_player_rtl?: string;
35
36
  magic_background?: string;
37
+ audio_player_background_image_query?: string;
36
38
  audio_player_background_image?: string;
37
39
  audio_player_artwork_aspect_ratio?: string;
38
40
  lg_tv_audio_player_title_font_family?: string;
@@ -73,6 +75,11 @@ export function AudioPlayer(props: Props) {
73
75
  const channelIcon = getProp("audio_player_channel_icon");
74
76
  const rtlFlag = getProp("audio_player_rtl");
75
77
  const magicBackground = getProp("magic_background");
78
+
79
+ const audioPlayerBackgroundImageQuery = getProp(
80
+ "audio_player_background_image_query"
81
+ );
82
+
76
83
  const isRTL = rtlFlag === "1" || rtlFlag === "true" || rtlFlag === true;
77
84
 
78
85
  const titleFontFamily = getProp(
@@ -149,7 +156,8 @@ export function AudioPlayer(props: Props) {
149
156
  runTimeFontSize,
150
157
  artworkAspectRatio,
151
158
  channelIcon,
152
- magicBackground
159
+ magicBackground,
160
+ audioPlayerBackgroundImageQuery,
153
161
  };
154
162
  }, [getProp]);
155
163
 
@@ -5,6 +5,7 @@ exports[`<AudioPlayer /> renders correctly 1`] = `
5
5
  config={
6
6
  Object {
7
7
  "artworkAspectRatio": "1:1",
8
+ "audioPlayerBackgroundImageQuery": "",
8
9
  "backgroundColor": "black",
9
10
  "backgroundImage": "https://example.com",
10
11
  "channelIcon": "https://example.com",
@@ -26,6 +27,7 @@ exports[`<AudioPlayer /> renders correctly 1`] = `
26
27
  config={
27
28
  Object {
28
29
  "artworkAspectRatio": "1:1",
30
+ "audioPlayerBackgroundImageQuery": "",
29
31
  "backgroundColor": "black",
30
32
  "backgroundImage": "https://example.com",
31
33
  "channelIcon": "https://example.com",
@@ -47,6 +49,7 @@ exports[`<AudioPlayer /> renders correctly 1`] = `
47
49
  config={
48
50
  Object {
49
51
  "artworkAspectRatio": "1:1",
52
+ "audioPlayerBackgroundImageQuery": "",
50
53
  "backgroundColor": "black",
51
54
  "backgroundImage": "https://example.com",
52
55
  "channelIcon": "https://example.com",
@@ -68,6 +71,7 @@ exports[`<AudioPlayer /> renders correctly 1`] = `
68
71
  config={
69
72
  Object {
70
73
  "artworkAspectRatio": "1:1",
74
+ "audioPlayerBackgroundImageQuery": "",
71
75
  "backgroundColor": "black",
72
76
  "backgroundImage": "https://example.com",
73
77
  "channelIcon": "https://example.com",
@@ -96,6 +100,7 @@ exports[`<AudioPlayer /> renders correctly 1`] = `
96
100
  config={
97
101
  Object {
98
102
  "artworkAspectRatio": "1:1",
103
+ "audioPlayerBackgroundImageQuery": "",
99
104
  "backgroundColor": "black",
100
105
  "backgroundImage": "https://example.com",
101
106
  "channelIcon": "https://example.com",
@@ -5,6 +5,7 @@ const defaults = {
5
5
  audio_player_artwork_aspect_ratio: "1:1",
6
6
  audio_player_rtl: false,
7
7
  magic_background: false,
8
+ audio_player_background_image_query: "",
8
9
  };
9
10
 
10
11
  export function getPropertyFromEntryOrConfig({ entry, plugin_configuration }) {
@@ -52,13 +52,13 @@ export const GeneralContentScreen = ({
52
52
  React.useState(actionsInitialStateSetters.length === 0);
53
53
 
54
54
  useEffect(() => {
55
- log_info(
56
- `ScreenContainer: starting to check river action provider ready, current status: ${riverActionProvidersReady}`,
57
- { actionsInitialStateSetters }
58
- );
59
-
60
55
  if (!riverActionProvidersReady) {
61
56
  if (actionsInitialStateSetters.length > 0) {
57
+ log_info(
58
+ "ScreenContainer: starting to check river action providers to initialize",
59
+ { actionsInitialStateSetters }
60
+ );
61
+
62
62
  allSettled(actionsInitialStateSetters).finally(() => {
63
63
  log_info(
64
64
  "ScreenContainer: action provider ready, completed. Starting to present screen"
@@ -104,7 +104,7 @@ export function HandlePlayable({
104
104
  const [Player, playerModuleProperties] = getPlayer(item, state);
105
105
 
106
106
  const { module: CastPlugin } =
107
- findPluginByIdentifier(CHROMECAST_PLUGIN_ID, state.plugins) || {};
107
+ findPluginByIdentifier(CHROMECAST_PLUGIN_ID, state.plugins, true) || {};
108
108
 
109
109
  const [playable, setPlayable] =
110
110
  React.useState<Nullable<PlayableComponent>>(null);
@@ -1,6 +1 @@
1
- import * as R from "ramda";
2
-
3
- import { withNavigator } from "@applicaster/zapp-react-native-ui-components/Decorators/Navigator/";
4
- import { HookRenderer as HookRendererComponent } from "./HookRenderer";
5
-
6
- export const HookRenderer = R.compose(withNavigator)(HookRendererComponent);
1
+ export { HookRenderer } from "./HookRenderer";
@@ -1,5 +1,4 @@
1
1
  import React from "react";
2
-
3
2
  import FastImage, {
4
3
  FastImageProps,
5
4
  } from "@applicaster/react-native-fast-image";
@@ -15,6 +14,8 @@ export const QBImage = (props: FastImageProps) => {
15
14
  headers: props.source.headers,
16
15
  priority: props.source.priority,
17
16
  cache: props.source.cache,
17
+ maxWidth: props.source.maxWidth,
18
+ maxHeight: props.source.maxHeight,
18
19
  };
19
20
  }, [
20
21
  ...(typeof props.source === "object"
@@ -23,6 +24,8 @@ export const QBImage = (props: FastImageProps) => {
23
24
  props.source?.priority,
24
25
  props.source?.cache,
25
26
  props.source?.headers,
27
+ props.source?.maxWidth,
28
+ props.source?.maxHeight,
26
29
  ]
27
30
  : []),
28
31
  ]);
@@ -14,46 +14,6 @@ exports[`Layout TV renders 1`] = `
14
14
  <View
15
15
  backgroundImageUrl="http://applicaster.com"
16
16
  testID="background-component"
17
- >
18
- <View
19
- style={
20
- Array [
21
- Object {
22
- "position": "absolute",
23
- "top": 0,
24
- "width": 1920,
25
- "zIndex": 10,
26
- },
27
- Object {
28
- "maxHeight": 10,
29
- },
30
- ]
31
- }
32
- testID="nav-bar-container"
33
- >
34
- <View
35
- isVisible={true}
36
- menuPlugin={
37
- Object {
38
- "module": [Function],
39
- "name": "resolved-menu-plug",
40
- "type": "menu",
41
- }
42
- }
43
- navigationProps={
44
- Object {
45
- "assets": undefined,
46
- "home": undefined,
47
- "nav_items": undefined,
48
- "rules": undefined,
49
- "selected": undefined,
50
- "styles": undefined,
51
- "title": "",
52
- }
53
- }
54
- testID="nav-bar-component"
55
- />
56
- </View>
57
- </View>
17
+ />
58
18
  </View>
59
19
  `;
@@ -12,19 +12,7 @@ jest.mock("@applicaster/quick-brick-core/App/ActionSetters", () => ({
12
12
  useRiverInitialState: () => ({}),
13
13
  }));
14
14
 
15
- jest.mock("@applicaster/zapp-react-native-utils/reactHooks/screen", () => ({
16
- useCurrentScreenData: jest.fn(() => ({
17
- navigations: [
18
- {
19
- category: "menu",
20
- navigation_type: "menu",
21
- styles: {
22
- hide_secondary_level_menu: false,
23
- },
24
- },
25
- ],
26
- })),
27
- }));
15
+ jest.mock("@applicaster/zapp-react-native-utils/reactHooks/screen");
28
16
 
29
17
  const Layout = require("..").default;
30
18
 
@@ -125,25 +113,4 @@ describe("Layout TV", () => {
125
113
  expect(bgComponent.props.testProp).toBe(true);
126
114
  });
127
115
  });
128
-
129
- describe("props passed to navbar component", () => {
130
- it("passes correct props", () => {
131
- const { getByTestId } = render(
132
- <Layout
133
- Components={Components}
134
- ComponentsExtraProps={{
135
- NavBar: { testProp: true },
136
- }}
137
- >
138
- <Screen />
139
- </Layout>,
140
- { wrapper }
141
- );
142
-
143
- const navbarComponent = getByTestId("nav-bar-component");
144
- expect(navbarComponent.props.isVisible).toBe(isNavBarVisible);
145
- expect(navbarComponent.props.menuPlugin).toBe(plugins[0]);
146
- expect(navbarComponent.props.testProp).toBe(true);
147
- });
148
- });
149
116
  });
@@ -1,10 +1,7 @@
1
1
  import * as React from "react";
2
2
  import * as R from "ramda";
3
3
  import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
4
- import {
5
- useIsNavBarVisible,
6
- useNavigation,
7
- } from "@applicaster/zapp-react-native-utils/reactHooks/navigation";
4
+ import { useNavigation } from "@applicaster/zapp-react-native-utils/reactHooks/navigation";
8
5
  import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
9
6
 
10
7
  import {
@@ -43,7 +40,7 @@ type Props = {
43
40
  const Layout = ({ Components, ComponentsExtraProps, children }: Props) => {
44
41
  const navigator = useNavigation();
45
42
  const theme = useTheme();
46
- const { title } = useNavbarState();
43
+ const { title, visible } = useNavbarState();
47
44
  const { activeRiver, currentRoute } = navigator;
48
45
 
49
46
  const {
@@ -80,10 +77,22 @@ const Layout = ({ Components, ComponentsExtraProps, children }: Props) => {
80
77
  []
81
78
  );
82
79
 
83
- const navBarVisible = useIsNavBarVisible();
84
-
85
80
  const [navBarReady, setNavBarReady] = React.useState(false);
86
81
 
82
+ const navBarContainer = (
83
+ <NavBarContainer isVisible={visible} onReady={setNavBarReady}>
84
+ <NavBar
85
+ ref={ComponentsExtraProps?.NavBar?.ref}
86
+ isVisible={visible && !routeIsPlayerScreen(currentRoute)}
87
+ menuPlugin={menuPlugin}
88
+ navigationProps={navigationProps}
89
+ {...R.omit(["ref"])(ComponentsExtraProps?.NavBar)}
90
+ />
91
+ </NavBarContainer>
92
+ );
93
+
94
+ const isTVOS = isApplePlatform();
95
+
87
96
  if (!appReady) {
88
97
  return null;
89
98
  }
@@ -96,18 +105,7 @@ const Layout = ({ Components, ComponentsExtraProps, children }: Props) => {
96
105
  backgroundImageUrl={backgroundImageUrl}
97
106
  {...R.omit(["ref"])(ComponentsExtraProps?.Background)}
98
107
  >
99
- {!isApplePlatform() ? (
100
- <NavBarContainer isVisible={navBarVisible} onReady={setNavBarReady}>
101
- <NavBar
102
- ref={ComponentsExtraProps?.NavBar?.ref}
103
- isVisible={navBarVisible && !routeIsPlayerScreen(currentRoute)}
104
- menuPlugin={menuPlugin}
105
- navigationProps={navigationProps}
106
- {...R.omit(["ref"])(ComponentsExtraProps?.NavBar)}
107
- />
108
- </NavBarContainer>
109
- ) : null}
110
-
108
+ {!isTVOS ? navBarContainer : null}
111
109
  {navBarReady ? (
112
110
  /**
113
111
  Need to re-mount this component on each navigation event
@@ -118,23 +116,9 @@ const Layout = ({ Components, ComponentsExtraProps, children }: Props) => {
118
116
  <ScreenContainer>{children}</ScreenContainer>
119
117
  </PathnameContext.Provider>
120
118
  ) : null}
121
-
122
- {isApplePlatform() ? (
123
- /**
124
- Render NavBarContainer over ScreenContainer to let tvOS native
125
- focus manager works correctly to focus nav bar items first
126
- **/
127
- <NavBarContainer isVisible={navBarVisible} onReady={setNavBarReady}>
128
- <NavBar
129
- ref={ComponentsExtraProps?.NavBar?.ref}
130
- isVisible={navBarVisible && !routeIsPlayerScreen(currentRoute)}
131
- menuPlugin={menuPlugin}
132
- navigationProps={navigationProps}
133
- {...R.omit(["ref"])(ComponentsExtraProps?.NavBar)}
134
- />
135
- </NavBarContainer>
136
- ) : null}
137
-
119
+ {/** Render NavBarContainer over ScreenContainer to let tvOS native
120
+ focus manager works correctly to focus nav bar items first **/}
121
+ {!isTVOS ? navBarContainer : null}
138
122
  <TransitionOverlay />
139
123
  </Components.Background>
140
124
  </ScreenLayoutContextProvider>
@@ -152,7 +152,6 @@ export const BorderContainerView = (props: Props) => {
152
152
  ...updatedPaddings,
153
153
  width: updatedWidth,
154
154
  height: updatedHeight,
155
- ...borderStyleForMissingBorder,
156
155
  };
157
156
 
158
157
  return (
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  compact,
3
3
  ifElseFocusedSelected,
4
+ ifElseSelected,
4
5
  } from "@applicaster/zapp-react-native-utils/cellUtils";
5
6
 
6
7
  import { getPositionStyles } from "./utils";
@@ -12,9 +13,15 @@ type Props = {
12
13
  value: Function;
13
14
  state: string;
14
15
  visible?: Boolean;
16
+ isMobile?: Boolean;
15
17
  };
16
18
 
17
- export const CellBadge = ({ value, state, visible = true }: Props) => {
19
+ export const CellBadge = ({
20
+ value,
21
+ state,
22
+ visible = true,
23
+ isMobile = false,
24
+ }: Props) => {
18
25
  if (!value("cell_badge_switch") || !visible) {
19
26
  return null;
20
27
  }
@@ -45,13 +52,19 @@ export const CellBadge = ({ value, state, visible = true }: Props) => {
45
52
  data: [
46
53
  {
47
54
  func: () => {
48
- return ifElseFocusedSelected(
49
- state,
50
- value("cell_badge_focused"),
51
- value("cell_badge_selected"),
52
- value("cell_badge_focused_selected"),
53
- value("cell_badge_default")
54
- );
55
+ return isMobile
56
+ ? ifElseSelected(
57
+ state,
58
+ value("cell_badge_selected"),
59
+ value("cell_badge_default")
60
+ )
61
+ : ifElseFocusedSelected(
62
+ state,
63
+ value("cell_badge_focused"),
64
+ value("cell_badge_selected"),
65
+ value("cell_badge_focused_selected"),
66
+ value("cell_badge_default")
67
+ );
55
68
  },
56
69
  args: [],
57
70
  propName: "uri",
@@ -1,6 +1,7 @@
1
- import { path, pathOr } from "ramda";
1
+ import { path } from "ramda";
2
2
  import { isNilOrEmpty } from "@applicaster/zapp-react-native-utils/reactUtils/helpers";
3
3
  import {
4
+ getColorFromData,
4
5
  getKeyForLabel,
5
6
  mapSelfAlignment,
6
7
  } from "@applicaster/zapp-react-native-utils/cellUtils";
@@ -48,15 +49,12 @@ export const CollapsibleTextContainer = ({
48
49
  ]),
49
50
  {
50
51
  func: (data) => {
51
- const value = getValue("background_color");
52
-
53
- // check if value is a reference to an extension
54
- if (value.startsWith("extensions")) {
55
- return pathOr(value, value.split("."), data);
56
- } else {
57
- // return default value
58
- return additionalStyles.backgroundColor;
59
- }
52
+ return getColorFromData({
53
+ data,
54
+ getValue,
55
+ key: "background_color",
56
+ defaultColor: additionalStyles.backgroundColor,
57
+ });
60
58
  },
61
59
  propName: "backgroundColor",
62
60
  },
@@ -0,0 +1,121 @@
1
+ import React, {
2
+ useCallback,
3
+ useEffect,
4
+ useMemo,
5
+ useRef,
6
+ useState,
7
+ } from "react";
8
+ import { View, LayoutChangeEvent, ScrollView } from "react-native";
9
+ import { Button } from "./Button";
10
+ import { ItemIconProps } from "./Button/ItemIcon";
11
+ import { ItemProps } from "./Button/Item";
12
+ import { ItemLabelProps } from "./Button/ItemLabel";
13
+ import {
14
+ defaultItemIconProps,
15
+ defaultItemLabelProps,
16
+ defaultItemProps,
17
+ } from "./utils";
18
+ import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
19
+
20
+ import { ModalHeader } from "./Header";
21
+
22
+ type ModalComponentProps = {
23
+ items: any[];
24
+ current_selection?: any;
25
+ onPress: (item: any) => void;
26
+ width: number;
27
+ currentRoute: boolean;
28
+ summary?: string;
29
+ title?: string;
30
+ maxHeight?: number;
31
+ dismiss: () => void;
32
+ buttonComponent?: React.ComponentType;
33
+ };
34
+
35
+ export function BottomSheetModalContent(props: ModalComponentProps) {
36
+ const {
37
+ items,
38
+ currentRoute,
39
+ maxHeight,
40
+ current_selection = null,
41
+ onPress,
42
+ dismiss,
43
+ summary,
44
+ title,
45
+ buttonComponent: ButtonComponent = Button,
46
+ } = props;
47
+
48
+ const [headerHeight, setHeaderHeight] = useState(0);
49
+ const route = useRef(currentRoute);
50
+ 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
+ }, []);
59
+
60
+ useEffect(() => {
61
+ if (currentRoute !== route.current) {
62
+ props.dismiss();
63
+ }
64
+ }, [currentRoute]);
65
+
66
+ const iconBaseProps = useMemo<ItemIconProps>(() => {
67
+ return defaultItemIconProps(theme);
68
+ }, [theme]);
69
+
70
+ const itemBaseProps = useMemo<ItemProps>(() => {
71
+ return defaultItemProps(theme, props.width);
72
+ }, [theme, props.width]);
73
+
74
+ const labelBaseProps = useMemo<ItemLabelProps>(() => {
75
+ return defaultItemLabelProps(theme, props.width);
76
+ }, [theme, props.width]);
77
+
78
+ const handlePress = (item: any) => {
79
+ onPress(item);
80
+ dismiss();
81
+ };
82
+
83
+ return (
84
+ <View
85
+ style={{
86
+ paddingTop: Number(theme.modal_bottom_sheet_padding_top),
87
+ }}
88
+ >
89
+ <ModalHeader
90
+ width={props.width}
91
+ dismiss={dismiss}
92
+ configuration={theme}
93
+ onLayout={onHeaderLayout}
94
+ summary={summary}
95
+ title={title}
96
+ />
97
+ <ScrollView
98
+ bounces={false}
99
+ style={{ maxHeight: maxContentHeight }}
100
+ contentContainerStyle={{
101
+ paddingBottom: Number(theme.modal_bottom_sheet_padding_bottom),
102
+ paddingTop: Number(theme.modal_bottom_sheet_padding_top),
103
+ }}
104
+ >
105
+ {items.map((item, index) => (
106
+ <ButtonComponent
107
+ key={index}
108
+ width={props.width}
109
+ configuration={theme}
110
+ selectedItem={current_selection}
111
+ item={item}
112
+ onPress={handlePress}
113
+ iconBaseProps={iconBaseProps}
114
+ itemBaseProps={itemBaseProps}
115
+ labelBaseProps={labelBaseProps}
116
+ />
117
+ ))}
118
+ </ScrollView>
119
+ </View>
120
+ );
121
+ }