@applicaster/zapp-react-native-ui-components 13.0.0-rc.99 → 14.0.0-alpha.1235043154

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 (73) hide show
  1. package/Components/AnimatedInOut/index.tsx +5 -3
  2. package/Components/AudioPlayer/index.tsx +15 -0
  3. package/Components/AudioPlayer/mobile/Layout.tsx +66 -0
  4. package/Components/AudioPlayer/{__tests__/__snapshots__/audioPlayer.test.js.snap → mobile/__tests__/__snapshots__/audioPlayerMobileLayout.test.js.snap} +2 -2
  5. package/Components/AudioPlayer/mobile/__tests__/audioPlayerMobileLayout.test.js +18 -0
  6. package/Components/AudioPlayer/mobile/index.tsx +18 -0
  7. package/Components/AudioPlayer/tv/Artwork.tsx +40 -0
  8. package/Components/AudioPlayer/{AudioPlayerLayout.tsx → tv/Layout.tsx} +37 -79
  9. package/Components/AudioPlayer/{Runtime.tsx → tv/Runtime.tsx} +2 -1
  10. package/Components/AudioPlayer/{Summary.tsx → tv/Summary.tsx} +12 -9
  11. package/Components/AudioPlayer/{Title.tsx → tv/Title.tsx} +12 -9
  12. package/Components/AudioPlayer/{__tests__ → tv/__tests__}/__snapshots__/artWork.test.js.snap +9 -4
  13. package/Components/AudioPlayer/tv/__tests__/__snapshots__/audioPlayer.test.js.snap +170 -0
  14. package/Components/AudioPlayer/{__tests__ → tv/__tests__}/__snapshots__/summary.test.js.snap +1 -1
  15. package/Components/AudioPlayer/{__tests__ → tv/__tests__}/__snapshots__/title.test.js.snap +1 -1
  16. package/Components/AudioPlayer/{__tests__ → tv/__tests__}/audioPlayer.test.js +7 -3
  17. package/Components/AudioPlayer/{helpers.tsx → tv/helpers.tsx} +3 -6
  18. package/Components/AudioPlayer/{AudioPlayer.tsx → tv/index.tsx} +14 -70
  19. package/Components/AudioPlayer/types.ts +40 -0
  20. package/Components/Focusable/FocusableTvOS.tsx +3 -3
  21. package/Components/GeneralContentScreen/GeneralContentScreen.tsx +3 -2
  22. package/Components/GeneralContentScreen/utils/useCurationAPI.ts +4 -2
  23. package/Components/GeneralContentScreen/utils/useEventAlerts.ts +30 -0
  24. package/Components/HandlePlayable/HandlePlayable.tsx +14 -8
  25. package/Components/MasterCell/DefaultComponents/Text/index.tsx +1 -0
  26. package/Components/MasterCell/elementMapper.tsx +1 -2
  27. package/Components/ModalComponent/BottomSheetModalContent.tsx +32 -46
  28. package/Components/ModalComponent/Button/index.tsx +25 -29
  29. package/Components/ModalComponent/Header/index.tsx +9 -8
  30. package/Components/OfflineHandler/NotificationView/__tests__/index.test.tsx +13 -18
  31. package/Components/OfflineHandler/__tests__/__snapshots__/index.test.tsx.snap +9 -0
  32. package/Components/PlayerContainer/PlayerContainer.tsx +26 -43
  33. package/Components/PlayerImageBackground/index.tsx +1 -1
  34. package/Components/River/ComponentsMap/ComponentsMap.tsx +7 -4
  35. package/Components/River/RiverItem.tsx +8 -4
  36. package/Components/River/TV/River.tsx +0 -3
  37. package/Components/River/TV/withTVEventHandler.tsx +1 -1
  38. package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +3 -1
  39. package/Components/RouteManager/TestId.tsx +1 -5
  40. package/Components/RouteManager/__tests__/__snapshots__/routeManager.test.js.snap +0 -1
  41. package/Components/RouteManager/__tests__/testId.test.js +0 -4
  42. package/Components/Screen/TV/__tests__/index.web.test.tsx +26 -0
  43. package/Components/Screen/__tests__/Screen.test.tsx +22 -14
  44. package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +2 -2
  45. package/Components/TopMarginApplicator/TopMarginApplicator.tsx +16 -15
  46. package/Components/Touchable/__tests__/__snapshots__/touchable.test.tsx.snap +34 -0
  47. package/Components/Transitioner/__tests__/__snapshots__/Scene.test.js.snap +15 -9
  48. package/Components/VideoLive/animationUtils.ts +3 -3
  49. package/Components/VideoModal/ModalAnimation/AnimationComponent.tsx +1 -2
  50. package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +1 -0
  51. package/Components/VideoModal/ModalAnimation/utils.ts +3 -9
  52. package/Components/VideoModal/PlayerWrapper.tsx +9 -19
  53. package/Components/VideoModal/__tests__/__snapshots__/PlayerWrapper.test.tsx.snap +60 -0
  54. package/Components/VideoModal/hooks/__tests__/useDelayedPlayerDetails.test.ts +17 -55
  55. package/Components/VideoModal/hooks/useDelayedPlayerDetails.ts +15 -26
  56. package/Components/Viewport/ViewportAware/__tests__/viewportAware.test.js +12 -16
  57. package/Components/Viewport/ViewportTracker/__tests__/viewportTracker.test.js +84 -24
  58. package/Decorators/ConfigurationWrapper/withConfigurationProvider.tsx +2 -2
  59. package/index.d.ts +0 -1
  60. package/package.json +5 -7
  61. package/Components/AudioPlayer/Artwork.tsx +0 -35
  62. package/Components/AudioPlayer/__tests__/__snapshots__/audioPlayerLayout.test.js.snap +0 -66
  63. package/Components/AudioPlayer/__tests__/audioPlayerLayout.test.js +0 -26
  64. package/Components/AudioPlayer/index.ts +0 -1
  65. package/Decorators/Navigator/__tests__/react-router-native-mock.js +0 -11
  66. package/Components/AudioPlayer/{Channel.tsx → tv/Channel.tsx} +0 -0
  67. package/Components/AudioPlayer/{__tests__ → tv/__tests__}/Runtime.test.js +0 -0
  68. package/Components/AudioPlayer/{__tests__ → tv/__tests__}/__snapshots__/Runtime.test.js.snap +2 -2
  69. /package/Components/AudioPlayer/{__tests__ → tv/__tests__}/__snapshots__/channel.test.js.snap +0 -0
  70. /package/Components/AudioPlayer/{__tests__ → tv/__tests__}/artWork.test.js +0 -0
  71. /package/Components/AudioPlayer/{__tests__ → tv/__tests__}/channel.test.js +0 -0
  72. /package/Components/AudioPlayer/{__tests__ → tv/__tests__}/summary.test.js +0 -0
  73. /package/Components/AudioPlayer/{__tests__ → tv/__tests__}/title.test.js +0 -0
@@ -1,7 +1,11 @@
1
1
  import React from "react";
2
2
  import { render } from "@testing-library/react-native";
3
3
 
4
- import { AudioPlayer } from "../AudioPlayer";
4
+ import { AudioPlayerTV } from "..";
5
+
6
+ jest.mock("@applicaster/zapp-react-native-utils/audioPlayerUtils", () => ({
7
+ useArtworkImage: jest.fn(() => "artwork_url"),
8
+ }));
5
9
 
6
10
  const audioPlayerProps = {
7
11
  audio_item: {
@@ -45,9 +49,9 @@ const audioPlayerProps = {
45
49
  styles: {},
46
50
  };
47
51
 
48
- describe("<AudioPlayer />", () => {
52
+ describe("<AudioPlayerTV />", () => {
49
53
  it("renders correctly", () => {
50
- const { toJSON } = render(<AudioPlayer {...audioPlayerProps} />);
54
+ const { toJSON } = render(<AudioPlayerTV {...audioPlayerProps} />);
51
55
  expect(toJSON()).toMatchSnapshot();
52
56
  });
53
57
  });
@@ -2,11 +2,8 @@ const defaults = {
2
2
  audio_player_title_color: "white",
3
3
  audio_player_summary_color: "white",
4
4
  audio_player_background_color: "black",
5
- audio_player_artwork_aspect_ratio: "1:1",
5
+ audio_player_background_image: undefined,
6
6
  audio_player_rtl: false,
7
- magic_background: false,
8
- audio_player_background_image_query: "",
9
- audio_player_background_image_default_color: "",
10
7
  };
11
8
 
12
9
  export function getPropertyFromEntryOrConfig({ entry, plugin_configuration }) {
@@ -28,14 +25,14 @@ export function getPropertyFromEntryOrConfig({ entry, plugin_configuration }) {
28
25
  const LTR = {
29
26
  flexDirection: "row",
30
27
  justifyContent: "flex-start",
31
- textAlign: "right",
28
+ textAlign: "left",
32
29
  alignItems: "flex-end",
33
30
  };
34
31
 
35
32
  const RTL = {
36
33
  flexDirection: "row-reverse",
37
34
  justifyContent: "flex-end",
38
- textAlign: "left",
35
+ textAlign: "right",
39
36
  alignItems: "flex-start",
40
37
  };
41
38
 
@@ -1,64 +1,24 @@
1
1
  import React, { useCallback, useMemo } from "react";
2
2
 
3
3
  import { platformSelect } from "@applicaster/zapp-react-native-utils/reactUtils";
4
+ import { useArtworkImage } from "@applicaster/zapp-react-native-utils/audioPlayerUtils";
4
5
 
5
- import { imageSrcFromMediaItem } from "@applicaster/zapp-react-native-utils/configurationUtils";
6
+ import { AudioPlayerTVLayout } from "./Layout";
6
7
 
7
- import { AudioPlayerLayout } from "./AudioPlayerLayout";
8
8
  import { Channel } from "./Channel";
9
9
  import { Title } from "./Title";
10
10
  import { Summary } from "./Summary";
11
11
  import { Runtime } from "./Runtime";
12
12
  import { getPropertyFromEntryOrConfig } from "./helpers";
13
- import { ViewStyle } from "react-native";
14
-
15
- type Props = {
16
- audio_item: ZappEntry & {
17
- extensions?: {
18
- audio_player_artwork_aspect_ratio?: string;
19
- audio_player_background_image?: string;
20
- audio_player_background_color?: string;
21
- audio_player_channel_icon?: string;
22
- audio_player_title_color?: string;
23
- audio_player_summary_color?: string;
24
- audio_player_rtl?: boolean;
25
- magic_background?: boolean;
26
- audio_player_background_image_query?: string;
27
- audio_player_background_image_default_color?: string;
28
- start_time?: string;
29
- end_time?: string;
30
- };
31
- };
32
- plugin_configuration: {
33
- audio_player_background_color?: string;
34
- audio_player_title_color?: string;
35
- audio_player_summary_color?: string;
36
- audio_player_rtl?: string;
37
- magic_background?: string;
38
- audio_player_background_image_query?: string;
39
- audio_player_background_image_default_color?: string;
40
- audio_player_background_image?: string;
41
- audio_player_artwork_aspect_ratio?: string;
42
- lg_tv_audio_player_title_font_family?: string;
43
- lg_tv_audio_player_title_font_size?: number;
44
- lg_tv_audio_player_summary_font_family?: string;
45
- lg_tv_audio_player_summary_font_size?: number;
46
- samsung_tv_audio_player_title_font_family?: string;
47
- samsung_tv_audio_player_title_font_size?: number;
48
- samsung_tv_audio_player_summary_font_family?: string;
49
- samsung_tv_audio_player_summary_font_size?: number;
50
- tv_os_audio_player_title_font_family?: string;
51
- tv_os_audio_player_title_font_size?: number;
52
- tv_os_audio_player_summary_font_family?: string;
53
- tv_os_audio_player_summary_font_size?: number;
54
- };
55
- style?: ViewStyle;
56
- };
57
-
58
- export function AudioPlayer(props: Props) {
59
- const { audio_item, plugin_configuration, style } = props;
13
+
14
+ import { Props } from "../types";
15
+
16
+ export function AudioPlayerTV(props: Props) {
17
+ const { audio_item, plugin_configuration, style = {} } = props;
60
18
  const { extensions, title, summary } = audio_item;
61
19
 
20
+ const artwork = useArtworkImage(audio_item);
21
+
62
22
  const getProp = useCallback(
63
23
  getPropertyFromEntryOrConfig({
64
24
  entry: audio_item,
@@ -68,23 +28,14 @@ export function AudioPlayer(props: Props) {
68
28
  );
69
29
 
70
30
  const config = useMemo(() => {
71
- // Checking if we are recieving items from the DSP
31
+ // Checking if we are receiving items from the DSP
72
32
  const titleColor = getProp("audio_player_title_color");
73
33
  const summaryColor = getProp("audio_player_summary_color");
74
34
  const backgroundColor = getProp("audio_player_background_color");
75
35
  const backgroundImage = getProp("audio_player_background_image");
76
- const artworkAspectRatio = getProp("audio_player_artwork_aspect_ratio");
77
36
  const channelIcon = getProp("audio_player_channel_icon");
78
37
  const rtlFlag = getProp("audio_player_rtl");
79
- const magicBackground = getProp("magic_background");
80
-
81
- const audioPlayerBackgroundImageQuery = getProp(
82
- "audio_player_background_image_query"
83
- );
84
-
85
- const audioPlayerBackgroundImageDefaultColor = getProp(
86
- "audio_player_background_image_default_color"
87
- );
38
+ const artworkBorderRadius = getProp("audio_player_artwork_border_radius");
88
39
 
89
40
  const isRTL = rtlFlag === "1" || rtlFlag === "true" || rtlFlag === true;
90
41
 
@@ -160,24 +111,17 @@ export function AudioPlayer(props: Props) {
160
111
  summaryFontSize,
161
112
  runTimeFontFamily,
162
113
  runTimeFontSize,
163
- artworkAspectRatio,
164
114
  channelIcon,
165
- magicBackground,
166
- audioPlayerBackgroundImageQuery,
167
- audioPlayerBackgroundImageDefaultColor,
115
+ artworkBorderRadius,
168
116
  };
169
117
  }, [getProp]);
170
118
 
171
- const artwork = imageSrcFromMediaItem(audio_item, [
172
- config?.artworkAspectRatio,
173
- ]);
174
-
175
119
  return (
176
- <AudioPlayerLayout artwork={artwork} config={config} style={style || {}}>
120
+ <AudioPlayerTVLayout artwork={artwork} config={config} style={style}>
177
121
  <Channel srcImage={config?.channelIcon} config={config} />
178
122
  <Title title={title} config={config} />
179
123
  <Summary summary={summary} config={config} />
180
124
  <Runtime {...extensions} config={config} />
181
- </AudioPlayerLayout>
125
+ </AudioPlayerTVLayout>
182
126
  );
183
127
  }
@@ -0,0 +1,40 @@
1
+ import { ViewStyle } from "react-native";
2
+
3
+ export type Props = {
4
+ audio_item: ZappEntry & {
5
+ extensions?: {
6
+ audio_player_artwork_aspect_ratio?: string;
7
+ audio_player_background_image?: string;
8
+ audio_player_background_color?: string;
9
+ audio_player_channel_icon?: string;
10
+ audio_player_title_color?: string;
11
+ audio_player_summary_color?: string;
12
+ audio_player_rtl?: boolean;
13
+ audio_player_background_image_default_color?: string;
14
+ start_time?: string;
15
+ end_time?: string;
16
+ };
17
+ };
18
+ plugin_configuration: {
19
+ audio_player_background_color?: string;
20
+ audio_player_title_color?: string;
21
+ audio_player_summary_color?: string;
22
+ audio_player_rtl?: string;
23
+ audio_player_background_image_default_color?: string;
24
+ audio_player_background_image?: string;
25
+ audio_player_artwork_aspect_ratio?: string;
26
+ lg_tv_audio_player_title_font_family?: string;
27
+ lg_tv_audio_player_title_font_size?: number;
28
+ lg_tv_audio_player_summary_font_family?: string;
29
+ lg_tv_audio_player_summary_font_size?: number;
30
+ samsung_tv_audio_player_title_font_family?: string;
31
+ samsung_tv_audio_player_title_font_size?: number;
32
+ samsung_tv_audio_player_summary_font_family?: string;
33
+ samsung_tv_audio_player_summary_font_size?: number;
34
+ tv_os_audio_player_title_font_family?: string;
35
+ tv_os_audio_player_title_font_size?: number;
36
+ tv_os_audio_player_summary_font_family?: string;
37
+ tv_os_audio_player_summary_font_size?: number;
38
+ };
39
+ style?: ViewStyle;
40
+ };
@@ -16,9 +16,9 @@ function noop() {}
16
16
  type Props = {
17
17
  id: string;
18
18
  groupId: string;
19
- onPress?: (nativeEvent: React.SyntheticEvent) => void;
20
- onFocus?: (nativeEvent: React.SyntheticEvent) => void;
21
- onBlur?: (nativeEvent: React.SyntheticEvent) => void;
19
+ onPress?: (nativeEvent: any) => void;
20
+ onFocus?: (nativeEvent: any) => void;
21
+ onBlur?: (nativeEvent: any) => void;
22
22
  children: (focused?: boolean) => React.ReactNode;
23
23
  isParallaxDisabled: boolean;
24
24
  preferredFocus?: boolean;
@@ -11,6 +11,7 @@ import { allSettled } from "promise";
11
11
  import { createLogger } from "@applicaster/zapp-react-native-utils/logger";
12
12
  import { isNilOrEmpty } from "@applicaster/zapp-react-native-utils/reactUtils/helpers";
13
13
  import { ScreenTrackedViewPositionsContext } from "@applicaster/zapp-react-native-ui-components/Contexts/ScreenTrackedViewPositionsContext";
14
+ import { useEventAlerts } from "./utils/useEventAlerts";
14
15
 
15
16
  const { log_info } = createLogger({
16
17
  category: "ScreenContainer",
@@ -29,7 +30,6 @@ export const GeneralContentScreen = ({
29
30
  isScreenWrappedInContainer,
30
31
  componentsMapExtraProps = {},
31
32
  focused,
32
- extraOffset,
33
33
  parentFocus,
34
34
  containerHeight,
35
35
  preferredFocus = false,
@@ -103,6 +103,8 @@ export const GeneralContentScreen = ({
103
103
  [typeof cellTapAction === "function" ? cellTapAction : onCellTapAction]
104
104
  );
105
105
 
106
+ useEventAlerts(screenData);
107
+
106
108
  if (!isReady || isNilOrEmpty(components || uiComponents)) return null;
107
109
 
108
110
  return (
@@ -119,7 +121,6 @@ export const GeneralContentScreen = ({
119
121
  isScreenWrappedInContainer={isScreenWrappedInContainer}
120
122
  parentFocus={parentFocus}
121
123
  focused={focused}
122
- extraOffset={extraOffset}
123
124
  containerHeight={containerHeight}
124
125
  preferredFocus={preferredFocus}
125
126
  {...componentsMapExtraProps}
@@ -3,8 +3,10 @@ import { all, equals, path, prop, isEmpty, pluck, values } from "ramda";
3
3
  import { useEffect, useMemo } from "react";
4
4
  import { useDispatch } from "react-redux";
5
5
 
6
- import { useLayoutPresets } from "@applicaster/zapp-react-native-redux/hooks/useLayoutPresets";
7
- import { useZappPipesFeeds } from "@applicaster/zapp-react-native-redux/hooks/useZappPipesFeeds";
6
+ import {
7
+ useLayoutPresets,
8
+ useZappPipesFeeds,
9
+ } from "@applicaster/zapp-react-native-redux/hooks";
8
10
  import { loadPipesData } from "@applicaster/zapp-react-native-redux/ZappPipes";
9
11
  import { isEmptyOrNil } from "@applicaster/zapp-react-native-utils/cellUtils";
10
12
  import { Categories } from "./logger";
@@ -0,0 +1,30 @@
1
+ import React from "react";
2
+ import { showAlertDialog } from "@applicaster/zapp-react-native-utils/alertUtils";
3
+ import { TOGGLE_FLAG_MAX_ITEMS_REACHED_EVENT } from "@applicaster/zapp-react-native-utils/actionsExecutor/consts";
4
+ import { useLocalizedStrings } from "@applicaster/zapp-react-native-utils/localizationUtils";
5
+ import { useIsScreenActive } from "@applicaster/zapp-react-native-utils/reactHooks";
6
+ import { useSubscriberFor } from "@applicaster/zapp-react-native-utils/reactHooks/useSubscriberFor";
7
+
8
+ export const useEventAlerts = (screenData: ZappRiver) => {
9
+ const localizations = useLocalizedStrings({
10
+ localizations: screenData?.localizations || {},
11
+ });
12
+
13
+ const isActive = useIsScreenActive();
14
+
15
+ const onMaxTagsReached = React.useCallback(() => {
16
+ // We can't skip subscribe hook call, so we have to check.
17
+ if (!isActive || !localizations?.msg_maximum_selection_reached_message) {
18
+ return;
19
+ }
20
+
21
+ showAlertDialog({
22
+ title: "",
23
+ message: localizations.msg_maximum_selection_reached_message,
24
+ okButtonText:
25
+ localizations.msg_maximum_selection_reached_message_ok_button || "OK",
26
+ });
27
+ }, [localizations, isActive]);
28
+
29
+ useSubscriberFor(TOGGLE_FLAG_MAX_ITEMS_REACHED_EVENT, onMaxTagsReached);
30
+ };
@@ -13,6 +13,7 @@ import {
13
13
  import { BufferAnimation } from "../PlayerContainer/BufferAnimation";
14
14
  import { PlayerContainer } from "../PlayerContainer";
15
15
  import { useModalSize } from "../VideoModal/hooks";
16
+ import { ViewStyle } from "react-native";
16
17
 
17
18
  type Props = {
18
19
  item: ZappEntry;
@@ -140,14 +141,19 @@ export function HandlePlayable({
140
141
  const modalSize = useModalSize();
141
142
 
142
143
  const style = React.useMemo(
143
- () => ({
144
- width: isModal ? modalSize.width : mode === "PIP" ? "100%" : screenWidth,
145
- height: isModal
146
- ? modalSize.height
147
- : mode === "PIP"
148
- ? "100%"
149
- : screenHeight,
150
- }),
144
+ () =>
145
+ ({
146
+ width: isModal
147
+ ? modalSize.width
148
+ : mode === "PIP"
149
+ ? "100%"
150
+ : screenWidth,
151
+ height: isModal
152
+ ? modalSize.height
153
+ : mode === "PIP"
154
+ ? "100%"
155
+ : screenHeight,
156
+ }) as ViewStyle,
151
157
  [screenWidth, screenHeight, modalSize, isModal, mode]
152
158
  );
153
159
 
@@ -52,6 +52,7 @@ const _Text = ({
52
52
  withScaledLineHeight(withFocusedStyles({ style, otherProps })),
53
53
  { height },
54
54
  ]}
55
+ allowFontScaling={false}
55
56
  {...withoutLabel(otherProps)}
56
57
  >
57
58
  {dateTransformEnabled
@@ -73,7 +73,6 @@ export function elementMapper(
73
73
  : {};
74
74
 
75
75
  const componentProps = {
76
- key,
77
76
  style,
78
77
  skipButtons: otherProps?.skipButtons,
79
78
  emitAsyncElementRegistrate: otherProps?.emitAsyncElementRegistrate,
@@ -91,7 +90,7 @@ export function elementMapper(
91
90
  const fn = mapElementWithKey(elementMapper(components, otherProps));
92
91
 
93
92
  return (
94
- <Component {...componentProps}>
93
+ <Component key={key} {...componentProps}>
95
94
  {focusableTypes.has(type) && elements.length > 0
96
95
  ? elements.map(fn)
97
96
  : null}
@@ -1,11 +1,5 @@
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 } from "react-native";
9
3
  import { Button } from "./Button";
10
4
  import { ItemIconProps } from "./Button/ItemIcon";
11
5
  import { ItemProps } from "./Button/Item";
@@ -15,7 +9,8 @@ import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
15
9
 
16
10
  import type { PluginConfiguration } from "./";
17
11
 
18
- import { ModalHeader } from "./Header";
12
+ import { ModalHeader as DefaultModalHeader } from "./Header";
13
+ import { useSafeAreaInsets } from "react-native-safe-area-context";
19
14
 
20
15
  type ModalComponentProps = {
21
16
  items: any[];
@@ -27,6 +22,7 @@ type ModalComponentProps = {
27
22
  title?: string;
28
23
  maxHeight?: number;
29
24
  dismiss: () => void;
25
+ headerComponent?: React.ComponentType;
30
26
  buttonComponent?: React.ComponentType;
31
27
  iconProps?: ItemIconProps | ((theme: PluginConfiguration) => ItemIconProps);
32
28
  itemProps?:
@@ -48,33 +44,25 @@ export function BottomSheetModalContent(props: ModalComponentProps) {
48
44
  const {
49
45
  items,
50
46
  currentRoute,
51
- maxHeight,
52
47
  current_selection = null,
53
48
  onPress,
54
49
  dismiss,
55
50
  summary,
56
51
  title,
52
+ headerComponent: ModalHeader = DefaultModalHeader,
57
53
  buttonComponent: ButtonComponent = Button,
58
- getSelectedItemIcon = (theme) =>
59
- theme.modal_bottom_sheet_item_selected_icon,
54
+ getSelectedItemIcon = ({
55
+ modal_bottom_sheet_item_selected_icon,
56
+ }: BaseThemePropertiesMobile) => modal_bottom_sheet_item_selected_icon,
60
57
  getDefaultItemIcon = () => null,
61
58
  iconPlacement,
62
59
  } = props;
63
60
 
64
- const [headerHeight, setHeaderHeight] = useState(0);
65
61
  const route = useRef(currentRoute);
66
62
  const theme = useTheme<BaseThemePropertiesMobile>();
67
63
  const paddingTop = Number(theme.modal_bottom_sheet_padding_top);
68
64
  const paddingBottom = Number(theme.modal_bottom_sheet_padding_bottom);
69
65
 
70
- const maxContentHeight = maxHeight
71
- ? maxHeight - headerHeight - paddingTop
72
- : undefined;
73
-
74
- const onHeaderLayout = useCallback((event: LayoutChangeEvent) => {
75
- setHeaderHeight(event.nativeEvent.layout.height);
76
- }, []);
77
-
78
66
  useEffect(() => {
79
67
  if (currentRoute !== route.current) {
80
68
  props.dismiss();
@@ -101,46 +89,44 @@ export function BottomSheetModalContent(props: ModalComponentProps) {
101
89
  [onPress, dismiss]
102
90
  );
103
91
 
92
+ const bottomInset = useSafeAreaInsets().bottom;
93
+
104
94
  return (
105
95
  <View
106
96
  style={{
107
- maxWidth: props.width,
108
- paddingTop,
97
+ maxHeight: props.maxHeight,
98
+ paddingTop: paddingTop,
109
99
  }}
110
100
  >
111
101
  <ModalHeader
112
- width={props.width}
113
102
  dismiss={dismiss}
114
103
  configuration={theme}
115
- onLayout={onHeaderLayout}
116
104
  summary={summary}
117
105
  title={title}
118
106
  />
119
107
  <ScrollView
120
- bounces={false}
121
- style={{ maxHeight: maxContentHeight }}
122
108
  contentContainerStyle={{
123
- paddingBottom,
124
- paddingTop,
109
+ paddingBottom: paddingBottom + bottomInset,
125
110
  }}
126
111
  >
127
- {items.map((item, index) => (
128
- <ButtonComponent
129
- key={index}
130
- width={props.width}
131
- configuration={theme}
132
- selectedItem={current_selection}
133
- item={item}
134
- onPress={handlePress}
135
- label={theme[item?.label] ?? item?.label}
136
- iconBaseProps={iconBaseProps}
137
- itemBaseProps={itemBaseProps}
138
- labelBaseProps={labelBaseProps}
139
- selectedItemIcon={getSelectedItemIcon(theme)}
140
- defaultItemIcon={getDefaultItemIcon(theme)}
141
- iconPlacement={iconPlacement}
142
- />
143
- ))}
112
+ {items.map((item, index) =>
113
+ item ? (
114
+ <ButtonComponent
115
+ key={index}
116
+ configuration={theme}
117
+ selectedItem={current_selection}
118
+ item={item}
119
+ onPress={handlePress}
120
+ label={theme[item?.label] ?? item?.label}
121
+ iconBaseProps={iconBaseProps}
122
+ itemBaseProps={itemBaseProps}
123
+ labelBaseProps={labelBaseProps}
124
+ selectedItemIcon={getSelectedItemIcon(theme)}
125
+ defaultItemIcon={getDefaultItemIcon(theme)}
126
+ iconPlacement={iconPlacement}
127
+ />
128
+ ) : null
129
+ )}
144
130
  </ScrollView>
145
131
  </View>
146
132
  );
@@ -7,7 +7,6 @@ import { defaultSelectedAsset } from "./assets";
7
7
 
8
8
  type ButtonProps = {
9
9
  configuration: any;
10
- width: number;
11
10
  selectedItem?: any;
12
11
  item: any; // Adjust type as needed
13
12
  onPress: (item: any) => void; // Adjust type as needed
@@ -33,7 +32,6 @@ export function Button({
33
32
  item,
34
33
  onPress,
35
34
  configuration,
36
- width,
37
35
  iconBaseProps,
38
36
  itemBaseProps: itemProps,
39
37
  labelBaseProps,
@@ -65,34 +63,32 @@ export function Button({
65
63
  if (disabled) return null;
66
64
 
67
65
  return (
68
- <View style={{ maxWidth: width }}>
69
- <TouchableOpacity
70
- activeOpacity={1}
71
- onPress={() => onPress(item)}
72
- onPressIn={() => setFocused(true)}
73
- onPressOut={() => setFocused(false)}
74
- >
75
- <Item {...itemProps} focused={focused} selected={selected}>
76
- <View style={styles.label_icon_container}>
77
- {iconPlacement === "left" && renderItemIcon}
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}
78
75
 
79
- {label ? (
80
- <ItemLabel
81
- {...labelBaseProps}
82
- label={label ?? null}
83
- focused={focused}
84
- selected={selected}
85
- />
86
- ) : null}
87
- </View>
76
+ {label ? (
77
+ <ItemLabel
78
+ {...labelBaseProps}
79
+ label={label ?? null}
80
+ focused={focused}
81
+ selected={selected}
82
+ />
83
+ ) : null}
84
+ </View>
88
85
 
89
- {selected ? (
90
- <ItemIcon {...iconBaseProps} asset={selectedItemIconPropsAssets} />
91
- ) : (
92
- iconPlacement === "right" && renderItemIcon
93
- )}
94
- </Item>
95
- </TouchableOpacity>
96
- </View>
86
+ {selected ? (
87
+ <ItemIcon {...iconBaseProps} asset={selectedItemIconPropsAssets} />
88
+ ) : (
89
+ iconPlacement === "right" && renderItemIcon
90
+ )}
91
+ </Item>
92
+ </TouchableOpacity>
97
93
  );
98
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>