@jobber/components-native 0.93.0 → 0.95.0

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 (32) hide show
  1. package/dist/package.json +4 -2
  2. package/dist/src/BottomSheet/BottomSheet.js +58 -32
  3. package/dist/src/BottomSheet/BottomSheet.style.js +8 -9
  4. package/dist/src/BottomSheet/hooks/useBottomSheetBackHandler.js +26 -0
  5. package/dist/src/InputText/InputText.js +2 -2
  6. package/dist/src/ProgressBar/ProgressBar.js +10 -6
  7. package/dist/src/ProgressBar/ProgressBarInner.js +3 -12
  8. package/dist/src/ProgressBar/ProgressBarStepped.js +7 -2
  9. package/dist/tsconfig.build.tsbuildinfo +1 -1
  10. package/dist/types/src/BottomSheet/BottomSheet.d.ts +7 -3
  11. package/dist/types/src/BottomSheet/BottomSheet.style.d.ts +7 -14
  12. package/dist/types/src/BottomSheet/hooks/useBottomSheetBackHandler.d.ts +8 -0
  13. package/dist/types/src/InputText/InputText.d.ts +1 -1
  14. package/dist/types/src/ProgressBar/ProgressBar.d.ts +1 -1
  15. package/dist/types/src/ProgressBar/ProgressBarInner.d.ts +4 -1
  16. package/dist/types/src/ProgressBar/ProgressBarStepped.d.ts +3 -1
  17. package/dist/types/src/ProgressBar/types.d.ts +20 -0
  18. package/package.json +4 -2
  19. package/src/BottomSheet/BottomSheet.stories.tsx +128 -0
  20. package/src/BottomSheet/BottomSheet.style.ts +7 -14
  21. package/src/BottomSheet/BottomSheet.test.tsx +19 -24
  22. package/src/BottomSheet/BottomSheet.tsx +112 -93
  23. package/src/BottomSheet/hooks/useBottomSheetBackHandler.test.ts +90 -0
  24. package/src/BottomSheet/hooks/useBottomSheetBackHandler.ts +41 -0
  25. package/src/InputText/InputText.tsx +3 -3
  26. package/src/ProgressBar/ProgressBar.test.tsx +109 -0
  27. package/src/ProgressBar/ProgressBar.tsx +17 -1
  28. package/src/ProgressBar/ProgressBarInner.tsx +7 -10
  29. package/src/ProgressBar/ProgressBarStepped.tsx +12 -1
  30. package/src/ProgressBar/__snapshots__/ProgressBar.test.tsx.snap +14 -0
  31. package/src/ProgressBar/types.ts +22 -0
  32. package/src/ThumbnailList/__snapshots__/ThumbnailList.test.tsx.snap +199 -1
@@ -1,6 +1,5 @@
1
1
  import type { ReactNode } from "react";
2
2
  import React from "react";
3
- import type { Modalize } from "react-native-modalize";
4
3
  export interface BottomSheetProps {
5
4
  readonly children: ReactNode;
6
5
  /**
@@ -24,5 +23,10 @@ export interface BottomSheetProps {
24
23
  */
25
24
  readonly onClose?: () => void;
26
25
  }
27
- export declare const BottomSheet: React.ForwardRefExoticComponent<BottomSheetProps & React.RefAttributes<import("react-native-modalize/lib/options").IHandles | undefined>>;
28
- export type BottomSheetRef = Modalize | undefined;
26
+ export interface BottomSheetRef {
27
+ open: () => void;
28
+ close: () => void;
29
+ }
30
+ export declare function BottomSheet({ children, showCancel, loading, heading, onOpen, onClose, ref, }: BottomSheetProps & {
31
+ readonly ref?: React.Ref<BottomSheetRef>;
32
+ }): React.JSX.Element;
@@ -1,23 +1,13 @@
1
1
  export declare const useStyles: () => {
2
- overlayModalize: {
2
+ backdrop: {
3
3
  backgroundColor: string;
4
4
  };
5
- overlay: {
6
- backgroundColor: string;
7
- height: number;
8
- position: "absolute";
9
- left: 0;
10
- right: 0;
11
- top: 0;
12
- bottom: 0;
13
- };
14
- modal: {
5
+ background: {
15
6
  borderTopLeftRadius: number;
16
7
  borderTopRightRadius: number;
17
- paddingTop: number;
18
8
  };
19
- children: {
20
- paddingBottom: number;
9
+ footerContainer: {
10
+ backgroundColor: string;
21
11
  };
22
12
  header: {
23
13
  paddingHorizontal: number;
@@ -29,4 +19,7 @@ export declare const useStyles: () => {
29
19
  marginTop: number;
30
20
  marginBottom: number;
31
21
  };
22
+ handle: {
23
+ display: "none";
24
+ };
32
25
  };
@@ -0,0 +1,8 @@
1
+ import type BottomSheet from "@gorhom/bottom-sheet";
2
+ /**
3
+ * Hook that closes the bottom sheet on the hardware back button press if it is visible
4
+ * @param bottomSheetRef ref to the bottom sheet component
5
+ */
6
+ export declare function useBottomSheetBackHandler(bottomSheetRef: React.RefObject<BottomSheet | null>): {
7
+ handleSheetPositionChange: (index: number) => void;
8
+ };
@@ -77,7 +77,7 @@ export interface InputTextProps extends Pick<InputFieldWrapperProps, "toolbar" |
77
77
  /**
78
78
  * Callback that is called when the text input is blurred
79
79
  */
80
- readonly onBlur?: () => void;
80
+ readonly onBlur?: (event?: FocusEvent) => void;
81
81
  /**
82
82
  * VoiceOver will read this string when a user selects the associated element
83
83
  */
@@ -1,3 +1,3 @@
1
1
  import React from "react";
2
2
  import type { ProgressBarProps } from "./types";
3
- export declare function ProgressBar({ loading, total, current, inProgress, reverseTheme, header, variation, size, }: ProgressBarProps): React.JSX.Element;
3
+ export declare function ProgressBar({ loading, total, current, inProgress, reverseTheme, header, variation, size, UNSAFE_style, }: ProgressBarProps): React.JSX.Element;
@@ -1,9 +1,12 @@
1
1
  import React from "react";
2
+ import type { StyleProp, ViewStyle } from "react-native";
2
3
  interface ProgressBarInnerProps {
3
4
  readonly width: number;
4
5
  readonly animationDuration?: number;
5
6
  readonly color?: string;
7
+ readonly style?: StyleProp<ViewStyle>;
8
+ readonly testID?: string;
6
9
  }
7
- export declare function ProgressBarInner({ width, color, }: ProgressBarInnerProps): React.JSX.Element;
10
+ export declare function ProgressBarInner({ width, color, style, testID, }: ProgressBarInnerProps): React.JSX.Element;
8
11
  export declare function calculateWidth(total: number, current: number): number;
9
12
  export {};
@@ -1,10 +1,12 @@
1
1
  import React from "react";
2
+ import type { ProgressBarUnsafeStyle } from "./types";
2
3
  interface ProgressBarSteppedProps {
3
4
  readonly total: number;
4
5
  readonly current: number;
5
6
  readonly color?: string;
6
7
  readonly loading?: boolean;
7
8
  readonly inProgress?: number;
9
+ readonly UNSAFE_style?: ProgressBarUnsafeStyle;
8
10
  }
9
- export declare function ProgressBarStepped({ total, current, color, loading, inProgress, }: ProgressBarSteppedProps): React.JSX.Element;
11
+ export declare function ProgressBarStepped({ total, current, color, loading, inProgress, UNSAFE_style, }: ProgressBarSteppedProps): React.JSX.Element;
10
12
  export {};
@@ -1,4 +1,5 @@
1
1
  import type { ReactNode } from "react";
2
+ import type { StyleProp, ViewStyle } from "react-native";
2
3
  export interface ProgressBarProps {
3
4
  /**
4
5
  * The total number of items to be completed
@@ -36,4 +37,23 @@ export interface ProgressBarProps {
36
37
  * @default base
37
38
  */
38
39
  readonly size?: "smaller" | "small" | "base";
40
+ /** **Use at your own risk:** Custom style for specific elements. This should only be used as a
41
+ * **last resort**. Using this may result in unexpected side effects.
42
+ * More information in the [Customizing components Guide](https://atlantis.getjobber.com/guides/customizing-components).
43
+ */
44
+ readonly UNSAFE_style?: ProgressBarUnsafeStyle;
45
+ }
46
+ export interface ProgressBarUnsafeStyle {
47
+ /** Styles applied to the outer accessible wrapper (role="progressbar"). */
48
+ container?: StyleProp<ViewStyle>;
49
+ /** Styles applied to the inner bar container (track container) – controls bar height/rounding. */
50
+ progressBarContainer?: StyleProp<ViewStyle>;
51
+ /** Styles applied to each step block in the stepped variation. */
52
+ step?: StyleProp<ViewStyle>;
53
+ /** Track/background bar in 'progress' variation (full-width inner) */
54
+ track?: StyleProp<ViewStyle>;
55
+ /** Filled/completed portion in 'progress' variation */
56
+ fill?: StyleProp<ViewStyle>;
57
+ /** In-progress overlay portion in 'progress' variation */
58
+ inProgressFill?: StyleProp<ViewStyle>;
39
59
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jobber/components-native",
3
- "version": "0.93.0",
3
+ "version": "0.95.0",
4
4
  "license": "MIT",
5
5
  "description": "React Native implementation of Atlantis",
6
6
  "repository": {
@@ -53,6 +53,7 @@
53
53
  "ts-xor": "^1.1.0"
54
54
  },
55
55
  "devDependencies": {
56
+ "@gorhom/bottom-sheet": "^5.2.8",
56
57
  "@react-native-community/datetimepicker": "^8.4.5",
57
58
  "@react-native/babel-preset": "^0.82.1",
58
59
  "@storybook/addon-a11y": "^9.1.2",
@@ -78,6 +79,7 @@
78
79
  },
79
80
  "peerDependencies": {
80
81
  "@babel/core": "^7.4.5",
82
+ "@gorhom/bottom-sheet": "^5.2.8",
81
83
  "@jobber/design": "*",
82
84
  "@react-native-community/datetimepicker": ">=6.7.0",
83
85
  "date-fns": "^2.30.0",
@@ -94,5 +96,5 @@
94
96
  "react-native-safe-area-context": "^5.4.0",
95
97
  "react-native-svg": ">=12.0.0"
96
98
  },
97
- "gitHead": "a84b7d122f13b650fd7f77cda794d71f1d25317b"
99
+ "gitHead": "ea9cbb709f05442a469763a4f12a332a59cb6350"
98
100
  }
@@ -0,0 +1,128 @@
1
+ import React, { useRef } from "react";
2
+ import type { Meta, StoryObj } from "@storybook/react-native-web-vite";
3
+ import { View } from "react-native";
4
+ import { SafeAreaProvider } from "react-native-safe-area-context";
5
+ import { Button, Heading, Text } from "@jobber/components-native";
6
+ import { BottomSheet } from "./BottomSheet";
7
+ import type { BottomSheetRef } from "./BottomSheet";
8
+ import { BottomSheetOption } from "./components/BottomSheetOption";
9
+
10
+ const meta = {
11
+ title: "Components/Selections/BottomSheet",
12
+ component: BottomSheet,
13
+ } satisfies Meta<typeof BottomSheet>;
14
+
15
+ export default meta;
16
+ type Story = StoryObj<typeof meta>;
17
+
18
+ const BasicTemplate = () => {
19
+ const bottomSheetRef = useRef<BottomSheetRef>(null);
20
+
21
+ const openBottomSheet = () => {
22
+ bottomSheetRef.current?.open();
23
+ };
24
+
25
+ const closeBottomSheet = () => {
26
+ bottomSheetRef.current?.close();
27
+ };
28
+
29
+ return (
30
+ <SafeAreaProvider>
31
+ <View style={{ display: "flex", flexDirection: "column", gap: 16 }}>
32
+ <Heading>Basic BottomSheet</Heading>
33
+ <Text>
34
+ Note that due to the differences between React Native Web and React
35
+ Native, this does not render 100% properly
36
+ </Text>
37
+ <Button label="Open Bottom Sheet" onPress={openBottomSheet} />
38
+ <Button label="Close Bottom Sheet" onPress={closeBottomSheet} />
39
+ </View>
40
+ <BottomSheet
41
+ ref={bottomSheetRef}
42
+ onClose={() => console.log("closed bottom sheet")}
43
+ onOpen={() => console.log("opened bottom sheet")}
44
+ >
45
+ <BottomSheetOption
46
+ icon="sendMessage"
47
+ iconColor="greyBlue"
48
+ text="Send message"
49
+ onPress={() => alert("send message")}
50
+ />
51
+ <BottomSheetOption
52
+ icon="phone"
53
+ iconColor="greyBlue"
54
+ text="Call a friend"
55
+ onPress={() => alert("Calling a friend")}
56
+ />
57
+ <BottomSheetOption
58
+ destructive={true}
59
+ icon="trash"
60
+ text="Remove"
61
+ onPress={() => alert("Removed")}
62
+ />
63
+ </BottomSheet>
64
+ </SafeAreaProvider>
65
+ );
66
+ };
67
+
68
+ const HeaderFooterInputTextTemplate = () => {
69
+ const bottomSheetRef = useRef<BottomSheetRef>(null);
70
+
71
+ const openBottomSheet = () => {
72
+ bottomSheetRef.current?.open();
73
+ };
74
+
75
+ const closeBottomSheet = () => {
76
+ bottomSheetRef.current?.close();
77
+ };
78
+
79
+ return (
80
+ <SafeAreaProvider>
81
+ <View style={{ display: "flex", flexDirection: "column", gap: 16 }}>
82
+ <Heading>Basic BottomSheet</Heading>
83
+ <Text>
84
+ Note that due to the differences between React Native Web and React
85
+ Native, this does not render 100% properly
86
+ </Text>
87
+ <Button label="Open Bottom Sheet" onPress={openBottomSheet} />
88
+ <Button label="Close Bottom Sheet" onPress={closeBottomSheet} />
89
+ </View>
90
+ <BottomSheet
91
+ ref={bottomSheetRef}
92
+ showCancel={true}
93
+ heading="BottomSheet Header"
94
+ onClose={() => console.log("closed bottom sheet")}
95
+ onOpen={() => console.log("opened bottom sheet")}
96
+ >
97
+ <BottomSheetOption
98
+ icon="sendMessage"
99
+ iconColor="greyBlue"
100
+ text="Send message"
101
+ onPress={() => alert("send message")}
102
+ />
103
+ <BottomSheetOption
104
+ icon="phone"
105
+ iconColor="greyBlue"
106
+ text="Call a friend"
107
+ onPress={() => alert("Calling a friend")}
108
+ />
109
+ <BottomSheetOption
110
+ destructive={true}
111
+ icon="trash"
112
+ text="Remove"
113
+ onPress={() => alert("Removed")}
114
+ />
115
+ </BottomSheet>
116
+ </SafeAreaProvider>
117
+ );
118
+ };
119
+
120
+ export const Basic: Story = {
121
+ render: BasicTemplate,
122
+ args: {} as Story["args"],
123
+ };
124
+
125
+ export const HeaderFooterInputText: Story = {
126
+ render: HeaderFooterInputTextTemplate,
127
+ args: {} as Story["args"],
128
+ };
@@ -1,28 +1,18 @@
1
- import { Dimensions, StyleSheet } from "react-native";
2
1
  import { buildThemedStyles } from "../AtlantisThemeContext";
3
2
 
4
- const { height } = Dimensions.get("window");
5
-
6
3
  export const useStyles = buildThemedStyles(tokens => {
7
4
  const modalBorderRadius = tokens["radius-larger"];
8
5
 
9
6
  return {
10
- overlayModalize: {
11
- backgroundColor: "transparent",
12
- },
13
-
14
- overlay: {
15
- ...StyleSheet.absoluteFillObject,
7
+ backdrop: {
16
8
  backgroundColor: tokens["color-overlay"],
17
- height,
18
9
  },
19
- modal: {
10
+ background: {
20
11
  borderTopLeftRadius: modalBorderRadius,
21
12
  borderTopRightRadius: modalBorderRadius,
22
- paddingTop: tokens["space-small"],
23
13
  },
24
- children: {
25
- paddingBottom: tokens["space-small"],
14
+ footerContainer: {
15
+ backgroundColor: tokens["color-surface"],
26
16
  },
27
17
  header: {
28
18
  paddingHorizontal: tokens["space-base"],
@@ -34,5 +24,8 @@ export const useStyles = buildThemedStyles(tokens => {
34
24
  marginTop: tokens["space-small"],
35
25
  marginBottom: tokens["space-smaller"],
36
26
  },
27
+ handle: {
28
+ display: "none",
29
+ },
37
30
  };
38
31
  });
@@ -1,7 +1,6 @@
1
1
  import React, { createRef } from "react";
2
- import { act, fireEvent, render, waitFor } from "@testing-library/react-native";
2
+ import { act, render, userEvent, waitFor } from "@testing-library/react-native";
3
3
  import { AccessibilityInfo, View } from "react-native";
4
- import { Host, Portal } from "react-native-portalize";
5
4
  import { BottomSheet } from ".";
6
5
  import type { BottomSheetRef } from "./BottomSheet";
7
6
  import { waitForUntestableRender } from "../utils/test/wait";
@@ -23,22 +22,18 @@ function setup({
23
22
  loading?: boolean;
24
23
  }) {
25
24
  return render(
26
- <Host>
27
- <Portal>
28
- <BottomSheet
29
- ref={ref}
30
- heading={heading}
31
- showCancel={showCancel}
32
- loading={loading}
33
- onClose={mockOnClose}
34
- onOpen={mockOnOpen}
35
- >
36
- <View>
37
- <Text>BottomSheet</Text>
38
- </View>
39
- </BottomSheet>
40
- </Portal>
41
- </Host>,
25
+ <BottomSheet
26
+ ref={ref}
27
+ heading={heading}
28
+ showCancel={showCancel}
29
+ loading={loading}
30
+ onClose={mockOnClose}
31
+ onOpen={mockOnOpen}
32
+ >
33
+ <View>
34
+ <Text>BottomSheet</Text>
35
+ </View>
36
+ </BottomSheet>,
42
37
  );
43
38
  }
44
39
 
@@ -78,7 +73,8 @@ it("BottomSheet can be closed with the cancel action", async () => {
78
73
  ref.current?.open();
79
74
  });
80
75
 
81
- fireEvent.press(await findByLabelText("Cancel"));
76
+ const cancelButton = await findByLabelText("Cancel");
77
+ await userEvent.press(cancelButton);
82
78
 
83
79
  await waitFor(() => {
84
80
  expect(queryByText("BottomSheet")).toBeNull();
@@ -134,15 +130,14 @@ describe("when there is a screen reader enabled", () => {
134
130
 
135
131
  const { findByLabelText, queryByText } = setup({});
136
132
 
137
- await waitForUntestableRender(
138
- "Wait for AccessibilityInfo.isScreenReaderEnabled to resolve",
139
- );
140
-
141
133
  await act(async () => {
142
134
  ref.current?.open();
143
135
  });
144
136
 
145
- fireEvent.press(await findByLabelText("Cancel"));
137
+ const cancelButton = await findByLabelText("Cancel");
138
+ expect(cancelButton).toBeDefined();
139
+
140
+ await userEvent.press(cancelButton);
146
141
 
147
142
  await waitFor(() => {
148
143
  expect(queryByText("BottomSheet")).toBeNull();
@@ -1,14 +1,19 @@
1
- import type { ReactNode, Ref, RefObject } from "react";
2
- import React, { forwardRef, useState } from "react";
3
- import type { Modalize } from "react-native-modalize";
4
- import { useSafeAreaInsets } from "react-native-safe-area-context";
1
+ import type { ReactNode } from "react";
2
+ import React, { useCallback, useImperativeHandle, useRef } from "react";
5
3
  import { Keyboard, View } from "react-native";
6
- import { BottomSheetOption } from "./components/BottomSheetOption";
4
+ import { useSafeAreaInsets } from "react-native-safe-area-context";
5
+ import RNBottomSheet, {
6
+ BottomSheetBackdrop,
7
+ BottomSheetView,
8
+ } from "@gorhom/bottom-sheet";
9
+ import type { BottomSheetBackdropProps } from "@gorhom/bottom-sheet";
10
+ import { tokens } from "@jobber/design";
7
11
  import { useStyles } from "./BottomSheet.style";
8
- import { UNSAFE_WrappedModalize } from "../ContentOverlay/UNSAFE_WrappedModalize";
9
- import { useIsScreenReaderEnabled } from "../hooks";
12
+ import { BottomSheetOption } from "./components/BottomSheetOption";
13
+ import { useBottomSheetBackHandler } from "./hooks/useBottomSheetBackHandler";
10
14
  import { Divider } from "../Divider";
11
15
  import { Heading } from "../Heading";
16
+ import { useIsScreenReaderEnabled } from "../hooks";
12
17
  import { useAtlantisI18n } from "../hooks/useAtlantisI18n";
13
18
 
14
19
  export interface BottomSheetProps {
@@ -40,71 +45,89 @@ export interface BottomSheetProps {
40
45
  readonly onClose?: () => void;
41
46
  }
42
47
 
43
- export const BottomSheet = forwardRef(BottomSheetInternal);
44
-
45
- export type BottomSheetRef = Modalize | undefined;
46
-
47
- function BottomSheetInternal(
48
- {
49
- children,
50
- showCancel,
51
- loading = false,
52
- heading,
53
- onOpen,
54
- onClose,
55
- }: BottomSheetProps,
56
- ref: Ref<BottomSheetRef>,
57
- ) {
58
- const isScreenReaderEnabled = useIsScreenReaderEnabled();
59
- const [open, setOpen] = useState<boolean>(false);
48
+ export interface BottomSheetRef {
49
+ open: () => void;
50
+ close: () => void;
51
+ }
52
+
53
+ export function BottomSheet({
54
+ children,
55
+ showCancel,
56
+ loading = false,
57
+ heading,
58
+ onOpen,
59
+ onClose,
60
+ ref,
61
+ }: BottomSheetProps & { readonly ref?: React.Ref<BottomSheetRef> }) {
60
62
  const styles = useStyles();
63
+ const isScreenReaderEnabled = useIsScreenReaderEnabled();
64
+
65
+ const cancellable = (showCancel && !loading) || isScreenReaderEnabled;
66
+
67
+ const { t } = useAtlantisI18n();
68
+ const insets = useSafeAreaInsets();
69
+ const previousIndexRef = useRef(-1);
70
+ const bottomSheetRef = useRef<RNBottomSheet>(null);
71
+ const { handleSheetPositionChange } =
72
+ useBottomSheetBackHandler(bottomSheetRef);
73
+
74
+ useImperativeHandle(ref, () => ({
75
+ open: () => {
76
+ bottomSheetRef.current?.expand();
77
+ },
78
+ close: () => {
79
+ close();
80
+ },
81
+ }));
82
+
83
+ const close = useCallback(() => {
84
+ bottomSheetRef.current?.close();
85
+ }, []);
86
+
87
+ const handleChange = (index: number) => {
88
+ // Handle Android back button
89
+ handleSheetPositionChange(index);
90
+
91
+ const previousIndex = previousIndexRef.current;
92
+
93
+ if (previousIndex === -1 && index >= 0) {
94
+ // Transitioned from closed to open
95
+ dismissKeyboard();
96
+ onOpen?.();
97
+ } else if (previousIndex >= 0 && index === -1) {
98
+ // Transitioned from open to closed
99
+ dismissKeyboard();
100
+ onClose?.();
101
+ }
102
+
103
+ previousIndexRef.current = index;
104
+ };
61
105
 
62
106
  return (
63
- <>
64
- {open && <Overlay styles={styles} />}
65
- <UNSAFE_WrappedModalize
66
- ref={ref}
67
- adjustToContentHeight={true}
68
- modalStyle={styles.modal}
69
- overlayStyle={styles.overlayModalize}
70
- HeaderComponent={
71
- heading && <Header heading={heading} styles={styles} />
72
- }
73
- FooterComponent={
74
- <Footer
75
- cancellable={(showCancel && !loading) || isScreenReaderEnabled}
76
- onCancel={() => {
77
- (ref as RefObject<BottomSheetRef>)?.current?.close();
78
- }}
79
- styles={styles}
80
- />
81
- }
82
- withHandle={false}
83
- withReactModal={isScreenReaderEnabled}
84
- onOpen={openModal}
85
- onClose={closeModal}
107
+ <RNBottomSheet
108
+ ref={bottomSheetRef}
109
+ index={-1}
110
+ backdropComponent={Backdrop}
111
+ backgroundStyle={styles.background}
112
+ enablePanDownToClose={true}
113
+ onChange={handleChange}
114
+ keyboardBlurBehavior="restore"
115
+ handleStyle={styles.handle}
116
+ >
117
+ <BottomSheetView
118
+ style={{
119
+ paddingBottom: insets.bottom + tokens["space-small"],
120
+ paddingTop: tokens["space-small"],
121
+ }}
86
122
  >
87
- <View
88
- style={
89
- !showCancel && !isScreenReaderEnabled ? styles.children : undefined
90
- }
91
- >
92
- {children}
93
- </View>
94
- </UNSAFE_WrappedModalize>
95
- </>
123
+ {heading && <Header heading={heading} styles={styles} />}
124
+ {children}
125
+ {cancellable && (
126
+ <Footer styles={styles} close={close} cancelLabel={t("cancel")} />
127
+ )}
128
+ </BottomSheetView>
129
+ </RNBottomSheet>
96
130
  );
97
-
98
- function openModal() {
99
- onOpen?.();
100
- setOpen(true);
101
- dismissKeyboard();
102
- }
103
-
104
- function closeModal() {
105
- onClose?.();
106
- setOpen(false);
107
- }
108
131
  }
109
132
 
110
133
  function Header({
@@ -122,31 +145,21 @@ function Header({
122
145
  }
123
146
 
124
147
  function Footer({
125
- cancellable,
126
- onCancel,
127
148
  styles,
149
+ close,
150
+ cancelLabel,
128
151
  }: {
129
- readonly cancellable: boolean;
130
- readonly onCancel: () => void;
131
152
  readonly styles: ReturnType<typeof useStyles>;
153
+ readonly close: () => void;
154
+ readonly cancelLabel: string;
132
155
  }) {
133
- const insets = useSafeAreaInsets();
134
- const { t } = useAtlantisI18n();
135
-
136
156
  return (
137
- <View style={{ marginBottom: insets.bottom }}>
138
- {cancellable && (
139
- <View style={styles.children}>
140
- <View style={styles.footerDivider}>
141
- <Divider />
142
- </View>
143
- <BottomSheetOption
144
- text={t("cancel")}
145
- icon={"remove"}
146
- onPress={onCancel}
147
- />
148
- </View>
149
- )}
157
+ <View>
158
+ <View style={styles.footerDivider}>
159
+ <Divider />
160
+ </View>
161
+
162
+ <BottomSheetOption text={cancelLabel} icon="remove" onPress={close} />
150
163
  </View>
151
164
  );
152
165
  }
@@ -157,10 +170,16 @@ function dismissKeyboard() {
157
170
  Keyboard.dismiss();
158
171
  }
159
172
 
160
- function Overlay({
161
- styles,
162
- }: {
163
- readonly styles: ReturnType<typeof useStyles>;
164
- }) {
165
- return <View style={styles.overlay} />;
173
+ function Backdrop(bottomSheetBackdropProps: BottomSheetBackdropProps) {
174
+ const styles = useStyles();
175
+
176
+ return (
177
+ <BottomSheetBackdrop
178
+ {...bottomSheetBackdropProps}
179
+ appearsOnIndex={0}
180
+ disappearsOnIndex={-1}
181
+ style={styles.backdrop}
182
+ opacity={1}
183
+ />
184
+ );
166
185
  }