@utilitywarehouse/hearth-react-native 0.28.6 → 0.29.1
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.
- package/.storybook/preview.tsx +7 -4
- package/.turbo/turbo-build.log +1 -1
- package/.turbo/turbo-lint.log +15 -18
- package/CHANGELOG.md +59 -0
- package/build/components/Combobox/Combobox.js +1 -1
- package/build/components/Modal/Modal.d.ts +1 -1
- package/build/components/Modal/Modal.js +6 -95
- package/build/components/Modal/Modal.props.d.ts +2 -31
- package/build/components/Modal/Modal.shared.types.d.ts +22 -0
- package/build/components/Modal/Modal.web.d.ts +1 -1
- package/build/components/Modal/Modal.web.js +6 -71
- package/build/components/NavModal/NavModal.d.ts +3 -0
- package/build/components/NavModal/NavModal.js +190 -0
- package/build/components/NavModal/NavModal.props.d.ts +15 -0
- package/build/components/NavModal/NavModal.props.js +1 -0
- package/build/components/NavModal/index.d.ts +2 -0
- package/build/components/NavModal/index.js +1 -0
- package/build/components/Select/Select.js +1 -1
- package/build/components/index.d.ts +2 -1
- package/build/components/index.js +2 -1
- package/docs/changelog.mdx +34 -0
- package/docs/components/AllComponents.web.tsx +709 -689
- package/package.json +5 -3
- package/src/components/Combobox/Combobox.tsx +1 -1
- package/src/components/Modal/Modal.docs.mdx +5 -122
- package/src/components/Modal/Modal.props.ts +2 -34
- package/src/components/Modal/Modal.shared.types.ts +23 -0
- package/src/components/Modal/Modal.stories.tsx +0 -1
- package/src/components/Modal/Modal.tsx +11 -174
- package/src/components/Modal/Modal.web.tsx +29 -127
- package/src/components/NavModal/NavModal.docs.mdx +178 -0
- package/src/components/NavModal/NavModal.figma.tsx +13 -0
- package/src/components/NavModal/NavModal.props.ts +23 -0
- package/src/components/NavModal/NavModal.stories.tsx +131 -0
- package/src/components/NavModal/NavModal.tsx +375 -0
- package/src/components/NavModal/index.ts +2 -0
- package/src/components/Select/Select.tsx +1 -1
- package/src/components/index.ts +3 -1
- package/build/components/SafeAreaView/SafeAreaView.d.ts +0 -5
- package/build/components/SafeAreaView/SafeAreaView.js +0 -117
- package/build/components/SafeAreaView/SafeAreaView.props.d.ts +0 -17
- package/build/components/SafeAreaView/index.d.ts +0 -2
- package/build/components/SafeAreaView/index.js +0 -1
- package/src/components/SafeAreaView/SafeAreaView.props.ts +0 -20
- package/src/components/SafeAreaView/SafeAreaView.tsx +0 -173
- package/src/components/SafeAreaView/index.ts +0 -2
- /package/build/components/{SafeAreaView/SafeAreaView.props.js → Modal/Modal.shared.types.js} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@utilitywarehouse/hearth-react-native",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.29.1",
|
|
4
4
|
"description": "Utility Warehouse React Native UI library",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"types": "build/index.d.ts",
|
|
@@ -46,6 +46,7 @@
|
|
|
46
46
|
"react-native-gesture-handler": "2.28.0",
|
|
47
47
|
"react-native-nitro-modules": "0.31.4",
|
|
48
48
|
"react-native-reanimated": "4.1.3",
|
|
49
|
+
"react-native-safe-area-context": "5.6.1",
|
|
49
50
|
"react-native-svg": "^15.12.1",
|
|
50
51
|
"react-native-unistyles": "3.0.17",
|
|
51
52
|
"react-native-web": "^0.20.0",
|
|
@@ -59,8 +60,8 @@
|
|
|
59
60
|
"@utilitywarehouse/hearth-fonts": "^0.0.4",
|
|
60
61
|
"@utilitywarehouse/hearth-react-icons": "^0.8.0",
|
|
61
62
|
"@utilitywarehouse/hearth-react-native-icons": "^0.8.0",
|
|
62
|
-
"@utilitywarehouse/hearth-
|
|
63
|
-
"@utilitywarehouse/hearth-
|
|
63
|
+
"@utilitywarehouse/hearth-tokens": "^0.2.4",
|
|
64
|
+
"@utilitywarehouse/hearth-svg-assets": "^0.5.0"
|
|
64
65
|
},
|
|
65
66
|
"peerDependencies": {
|
|
66
67
|
"@gorhom/bottom-sheet": ">=5.0.0",
|
|
@@ -69,6 +70,7 @@
|
|
|
69
70
|
"react-native": ">=0.77",
|
|
70
71
|
"react-native-gesture-handler": ">=2",
|
|
71
72
|
"react-native-reanimated": "3.x || ^4.x",
|
|
73
|
+
"react-native-safe-area-context": "5.x",
|
|
72
74
|
"react-native-svg": ">=13.4.0",
|
|
73
75
|
"react-native-unistyles": ">=3.0.0",
|
|
74
76
|
"react-native-web": ">=0.19"
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { CloseSmallIcon, SearchMediumIcon } from '@utilitywarehouse/hearth-react-native-icons';
|
|
2
2
|
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
3
3
|
import { GestureResponderEvent, Pressable, TextInput, View } from 'react-native';
|
|
4
|
+
import { SafeAreaView } from 'react-native-safe-area-context';
|
|
4
5
|
import { StyleSheet } from 'react-native-unistyles';
|
|
5
6
|
import { useTheme } from '../../hooks';
|
|
6
7
|
import { BodyText } from '../BodyText';
|
|
@@ -9,7 +10,6 @@ import { DetailText } from '../DetailText';
|
|
|
9
10
|
import { FormField, useFormFieldContext } from '../FormField';
|
|
10
11
|
import { Icon } from '../Icon';
|
|
11
12
|
import { Input } from '../Input';
|
|
12
|
-
import { SafeAreaView } from '../SafeAreaView';
|
|
13
13
|
import { Spinner } from '../Spinner';
|
|
14
14
|
import { UnstyledIconButton } from '../UnstyledIconButton';
|
|
15
15
|
import { ComboboxContext, ComboboxSelection } from './Combobox.context';
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { Canvas, Controls, Meta, Story } from '@storybook/addon-docs/blocks';
|
|
2
2
|
import { BodyText, BottomSheetModal, Box, Button, Center, Heading, Modal } from '../../';
|
|
3
3
|
import StorybookLink from '../../../../../shared/storybook/StorybookLink';
|
|
4
|
-
import modalAndroidVideo from '../../../docs/assets/modal-android.mp4';
|
|
5
|
-
import modaliOSVideo from '../../../docs/assets/modal-ios.mp4';
|
|
6
4
|
import { BackToTopButton, UsageWrap, ViewFigmaButton } from '../../../docs/components';
|
|
7
5
|
import * as Stories from './Modal.stories';
|
|
8
6
|
|
|
@@ -18,6 +16,8 @@ The `Modal` component provides a versatile dialog interface that slides up from
|
|
|
18
16
|
|
|
19
17
|
The Modal component is ideal for displaying important information, collecting user input, or presenting choices that require user attention without navigating away from the current screen.
|
|
20
18
|
|
|
19
|
+
If you need a modal layout inside a React Navigation modal screen, use <StorybookLink to="components-navmodal">`NavModal`</StorybookLink> instead.
|
|
20
|
+
|
|
21
21
|
- [Playground](#playground)
|
|
22
22
|
- [Usage](#usage)
|
|
23
23
|
- [Props](#props)
|
|
@@ -31,7 +31,6 @@ The Modal component is ideal for displaying important information, collecting us
|
|
|
31
31
|
- [Loading State](#loading-state)
|
|
32
32
|
- [Without Close Button](#without-close-button)
|
|
33
33
|
- [Single Action Modal](#single-action-modal)
|
|
34
|
-
- [Modal in Navigation Modal](#modal-in-navigation-modal)
|
|
35
34
|
- [Integration Notes](#integration-notes)
|
|
36
35
|
- [External Resources](#external-resources)
|
|
37
36
|
|
|
@@ -108,10 +107,7 @@ The Modal component extends the `BottomSheetModal` component and accepts all of
|
|
|
108
107
|
| `primaryButtonProps` | `Omit<ButtonWithoutChildrenProps, 'children'>` | Additional props to pass to the primary button (colorScheme defaults to 'highlight', variant to 'solid') | - |
|
|
109
108
|
| `secondaryButtonProps` | `Omit<ButtonWithoutChildrenProps, 'children'>` | Additional props to pass to the secondary button (colorScheme defaults to 'functional', variant to 'outline') | - |
|
|
110
109
|
| `closeButtonProps` | `Omit<UnstyledIconButtonProps, 'children'>` | Additional props to pass to the close button | - |
|
|
111
|
-
| `fullscreen` | `boolean` | Whether the modal should take up the full screen height
|
|
112
|
-
| `inNavModal` | `boolean` | Renders the modal correctly when used inside a navigation modal | `false` |
|
|
113
|
-
| `background` | `'default' \| 'brand'` | Sets the modal background. Only applies when `inNavModal` is `true` | `'default'` |
|
|
114
|
-
| `scrollable` | `boolean` | Whether the modal's content should be placed in a `ScrollView`. Only applies when `inNavModal` is `true` | `true` |
|
|
110
|
+
| `fullscreen` | `boolean` | Whether the modal should take up the full screen height | `false` |
|
|
115
111
|
|
|
116
112
|
\* use this to detect if the modal has been opened or closed, index 0 indicates open state and -1 indicates closed state
|
|
117
113
|
|
|
@@ -462,122 +458,9 @@ const AlertModal = () => {
|
|
|
462
458
|
};
|
|
463
459
|
```
|
|
464
460
|
|
|
465
|
-
###
|
|
466
|
-
|
|
467
|
-
When wanting to use the Modal component in a navigation context using [React Navigation](https://reactnavigation.org/docs/modal), you can set `inNavModal` to `true` to make it behave like a standard modal screen.
|
|
468
|
-
|
|
469
|
-
Within React Navigation, you can set `presentation: 'modal'` in the screen's settings to have the Modal look and behave like a standard modal/bottom sheet, or you can set `presentation: 'fullScreenModal'` to have the Modal fill the entire screen.
|
|
470
|
-
|
|
471
|
-
When using `inNavModal`, by default the content will be rendered inside a `ScrollView` to ensure it is scrollable, especially on smaller devices or smaller modals. You can disable this by setting `scrollable={false}` if, for example, you need to center your content or add some custom content.
|
|
472
|
-
|
|
473
|
-
Here's an example of how to implement this with custom close animations for Android:
|
|
474
|
-
|
|
475
|
-
```tsx
|
|
476
|
-
import { useNavigation } from '@react-navigation/native';
|
|
477
|
-
import { useCallback, useEffect, useRef } from 'react';
|
|
478
|
-
import { Platform, StyleSheet, View } from 'react-native';
|
|
479
|
-
|
|
480
|
-
import { BodyText, Heading, InlineLink, Modal } from '@utilitywarehouse/hearth-react-native';
|
|
481
|
-
import type { NavigationAction } from '@react-navigation/native';
|
|
482
|
-
|
|
483
|
-
export default function ModalScreen({ onClose }: { onClose?: () => void }) {
|
|
484
|
-
const modalRef = useRef<Modal>(null);
|
|
485
|
-
const navigation = useNavigation();
|
|
486
|
-
const isClosingRef = useRef(false);
|
|
487
|
-
|
|
488
|
-
const handleClose = useCallback(
|
|
489
|
-
(action?: NavigationAction) => {
|
|
490
|
-
if (Platform.OS === 'ios') {
|
|
491
|
-
if (onClose) {
|
|
492
|
-
onClose();
|
|
493
|
-
} else {
|
|
494
|
-
navigation.goBack();
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
return;
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
if (isClosingRef.current) {
|
|
501
|
-
return;
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
isClosingRef.current = true;
|
|
505
|
-
// Trigger our custom animation first
|
|
506
|
-
modalRef.current?.triggerCloseAnimation?.();
|
|
507
|
-
|
|
508
|
-
// Delay the actual navigation to allow our animation to play
|
|
509
|
-
setTimeout(() => {
|
|
510
|
-
if (onClose) {
|
|
511
|
-
onClose();
|
|
512
|
-
} else if (action) {
|
|
513
|
-
// Dispatch the original action (e.g. popToTop, replace, reset)
|
|
514
|
-
// so we don't override the caller's intended navigation behaviour
|
|
515
|
-
navigation.dispatch(action);
|
|
516
|
-
} else {
|
|
517
|
-
navigation.goBack();
|
|
518
|
-
}
|
|
519
|
-
}, 100); // Match Modal animation duration
|
|
520
|
-
},
|
|
521
|
-
[navigation, onClose]
|
|
522
|
-
);
|
|
523
|
-
|
|
524
|
-
useEffect(() => {
|
|
525
|
-
if (Platform.OS === 'android') {
|
|
526
|
-
const unsubscribe = navigation.addListener('beforeRemove', e => {
|
|
527
|
-
if (!isClosingRef.current) {
|
|
528
|
-
// Prevent default behavior of the navigation action so we can
|
|
529
|
-
// play the close animation before letting navigation proceed
|
|
530
|
-
e.preventDefault();
|
|
531
|
-
handleClose(e.data.action);
|
|
532
|
-
}
|
|
533
|
-
});
|
|
534
|
-
|
|
535
|
-
return unsubscribe;
|
|
536
|
-
}
|
|
537
|
-
}, [navigation, handleClose]);
|
|
538
|
-
|
|
539
|
-
return (
|
|
540
|
-
<Modal
|
|
541
|
-
ref={modalRef}
|
|
542
|
-
inNavModal
|
|
543
|
-
onPressCloseButton={handleClose}
|
|
544
|
-
primaryButtonText="Action"
|
|
545
|
-
onPressPrimaryButton={handleClose}
|
|
546
|
-
secondaryButtonText="Cancel"
|
|
547
|
-
onPressSecondaryButton={handleClose}
|
|
548
|
-
>
|
|
549
|
-
<View style={styles.container}>
|
|
550
|
-
<Heading size="xl">This is a modal</Heading>
|
|
551
|
-
<BodyText>
|
|
552
|
-
<InlineLink onPress={handleClose} style={styles.link}>
|
|
553
|
-
Go to home screen
|
|
554
|
-
</InlineLink>
|
|
555
|
-
</BodyText>
|
|
556
|
-
</View>
|
|
557
|
-
</Modal>
|
|
558
|
-
);
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
const styles = StyleSheet.create({
|
|
562
|
-
container: {
|
|
563
|
-
flex: 1,
|
|
564
|
-
alignItems: 'center',
|
|
565
|
-
justifyContent: 'center',
|
|
566
|
-
padding: 20,
|
|
567
|
-
},
|
|
568
|
-
link: {
|
|
569
|
-
marginTop: 15,
|
|
570
|
-
paddingVertical: 15,
|
|
571
|
-
},
|
|
572
|
-
});
|
|
573
|
-
```
|
|
574
|
-
|
|
575
|
-
The above example demonstrates how to create a fullscreen modal that integrates with navigation.
|
|
576
|
-
It includes custom close animations for Android to enhance the user experience. See the videos below for platform-specific behavior:
|
|
461
|
+
### Navigation Modals
|
|
577
462
|
|
|
578
|
-
|
|
579
|
-
| ------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------- |
|
|
580
|
-
| <video src={modaliOSVideo} width={300} height="auto" controls loop autoPlay /> | <video src={modalAndroidVideo} width={300} height="auto" controls loop autoPlay /> |
|
|
463
|
+
For React Navigation modal screens, use <StorybookLink to="components-navmodal">`NavModal`</StorybookLink>. It contains the extracted screen-based modal layout, background variants, scrollable content handling, and the Android `triggerCloseAnimation()` ref used during navigation dismissal.
|
|
581
464
|
|
|
582
465
|
## Integration Notes
|
|
583
466
|
|
|
@@ -1,42 +1,10 @@
|
|
|
1
|
-
import { ReactNode } from 'react';
|
|
2
|
-
import { ViewProps } from 'react-native';
|
|
3
1
|
import { BottomSheetProps } from '../BottomSheet';
|
|
4
|
-
import {
|
|
5
|
-
import { UnstyledIconButtonProps } from '../UnstyledIconButton';
|
|
2
|
+
import { ModalCommonProps } from './Modal.shared.types';
|
|
6
3
|
|
|
7
|
-
interface
|
|
8
|
-
loading?: boolean;
|
|
9
|
-
image?: ReactNode;
|
|
10
|
-
showCloseButton?: boolean;
|
|
11
|
-
heading?: string;
|
|
12
|
-
loadingHeading?: string;
|
|
13
|
-
description?: string;
|
|
4
|
+
interface ModalProps extends Omit<BottomSheetProps, 'children'>, ModalCommonProps {
|
|
14
5
|
fullscreen?: boolean;
|
|
15
|
-
stickyFooter?: boolean;
|
|
16
|
-
children?: ViewProps['children'];
|
|
17
|
-
onPressPrimaryButton?: () => void;
|
|
18
|
-
primaryButtonText?: string;
|
|
19
|
-
onPressSecondaryButton?: () => void;
|
|
20
6
|
closeOnPrimaryButtonPress?: boolean;
|
|
21
|
-
secondaryButtonText?: string;
|
|
22
|
-
onPressCloseButton?: () => void;
|
|
23
7
|
closeOnSecondaryButtonPress?: boolean;
|
|
24
|
-
primaryButtonProps?: Omit<ButtonWithoutChildrenProps, 'children'>;
|
|
25
|
-
secondaryButtonProps?: Omit<ButtonWithoutChildrenProps, 'children'>;
|
|
26
|
-
closeButtonProps?: Omit<UnstyledIconButtonProps, 'children'>;
|
|
27
8
|
}
|
|
28
9
|
|
|
29
|
-
type ModalProps =
|
|
30
|
-
| (ModalPropsBase & {
|
|
31
|
-
inNavModal?: false | undefined;
|
|
32
|
-
scrollable?: never;
|
|
33
|
-
background?: never;
|
|
34
|
-
})
|
|
35
|
-
| (ModalPropsBase & {
|
|
36
|
-
inNavModal: true;
|
|
37
|
-
fullscreen?: never;
|
|
38
|
-
scrollable?: boolean;
|
|
39
|
-
background?: 'default' | 'brand';
|
|
40
|
-
});
|
|
41
|
-
|
|
42
10
|
export default ModalProps;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { ViewProps } from 'react-native';
|
|
3
|
+
import { ButtonWithoutChildrenProps } from '../Button/Button.props';
|
|
4
|
+
import { UnstyledIconButtonProps } from '../UnstyledIconButton';
|
|
5
|
+
|
|
6
|
+
export interface ModalCommonProps {
|
|
7
|
+
loading?: boolean;
|
|
8
|
+
image?: ReactNode;
|
|
9
|
+
showCloseButton?: boolean;
|
|
10
|
+
heading?: string;
|
|
11
|
+
loadingHeading?: string;
|
|
12
|
+
description?: string;
|
|
13
|
+
stickyFooter?: boolean;
|
|
14
|
+
children?: ViewProps['children'];
|
|
15
|
+
onPressPrimaryButton?: () => void;
|
|
16
|
+
primaryButtonText?: string;
|
|
17
|
+
onPressSecondaryButton?: () => void;
|
|
18
|
+
secondaryButtonText?: string;
|
|
19
|
+
onPressCloseButton?: () => void;
|
|
20
|
+
primaryButtonProps?: Omit<ButtonWithoutChildrenProps, 'children'>;
|
|
21
|
+
secondaryButtonProps?: Omit<ButtonWithoutChildrenProps, 'children'>;
|
|
22
|
+
closeButtonProps?: Omit<UnstyledIconButtonProps, 'children'>;
|
|
23
|
+
}
|
|
@@ -6,23 +6,13 @@ import {
|
|
|
6
6
|
} from '@gorhom/bottom-sheet';
|
|
7
7
|
import { BottomSheetModalMethods } from '@gorhom/bottom-sheet/lib/typescript/types';
|
|
8
8
|
import { CloseMediumIcon } from '@utilitywarehouse/hearth-react-native-icons';
|
|
9
|
-
import { useCallback,
|
|
10
|
-
import { AccessibilityInfo, Platform,
|
|
11
|
-
import Animated, {
|
|
12
|
-
Easing,
|
|
13
|
-
useAnimatedStyle,
|
|
14
|
-
useSharedValue,
|
|
15
|
-
withDelay,
|
|
16
|
-
withTiming,
|
|
17
|
-
} from 'react-native-reanimated';
|
|
9
|
+
import { useCallback, useImperativeHandle, useMemo, useRef } from 'react';
|
|
10
|
+
import { AccessibilityInfo, Platform, View, findNodeHandle } from 'react-native';
|
|
18
11
|
import { StyleSheet } from 'react-native-unistyles';
|
|
19
|
-
import { useTheme } from '../../hooks';
|
|
20
|
-
import { hexWithOpacity } from '../../utils';
|
|
21
12
|
import { BodyText } from '../BodyText';
|
|
22
13
|
import { BottomSheetModal, BottomSheetScrollView } from '../BottomSheet';
|
|
23
14
|
import { Button } from '../Button';
|
|
24
15
|
import { Heading } from '../Heading';
|
|
25
|
-
import { SafeAreaView } from '../SafeAreaView';
|
|
26
16
|
import { Spinner } from '../Spinner';
|
|
27
17
|
import { UnstyledIconButton } from '../UnstyledIconButton';
|
|
28
18
|
import ModalProps from './Modal.props';
|
|
@@ -49,74 +39,15 @@ const Modal = ({
|
|
|
49
39
|
primaryButtonProps,
|
|
50
40
|
secondaryButtonProps,
|
|
51
41
|
closeButtonProps,
|
|
52
|
-
inNavModal = false,
|
|
53
42
|
stickyFooter = true,
|
|
54
|
-
background = 'default',
|
|
55
|
-
scrollable = true,
|
|
56
43
|
...props
|
|
57
44
|
}: ModalProps) => {
|
|
58
45
|
const bottomSheetModalRef = useRef<BottomSheetModal>(null);
|
|
59
46
|
const viewRef = useRef<View>(null);
|
|
60
47
|
const scrollViewRef = useRef<BottomSheetScrollViewMethods>(null);
|
|
61
|
-
const theme = useTheme();
|
|
62
|
-
const backgroundOpacity = useSharedValue(0);
|
|
63
|
-
const pretendContentTranslateY = useSharedValue(20);
|
|
64
|
-
const isBrandBackground = background === 'brand';
|
|
65
|
-
|
|
66
|
-
const triggerCloseAnimation = useCallback(() => {
|
|
67
|
-
if (Platform.OS === 'android' && inNavModal) {
|
|
68
|
-
pretendContentTranslateY.value = withTiming(20, {
|
|
69
|
-
duration: 50,
|
|
70
|
-
easing: Easing.in(Easing.quad),
|
|
71
|
-
});
|
|
72
|
-
backgroundOpacity.value = withTiming(0, {
|
|
73
|
-
duration: 100,
|
|
74
|
-
easing: Easing.in(Easing.quad),
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
}, [inNavModal, pretendContentTranslateY, backgroundOpacity]);
|
|
78
48
|
|
|
79
49
|
useImperativeHandle(ref, () => ({
|
|
80
50
|
...(bottomSheetModalRef.current as BottomSheetModal),
|
|
81
|
-
triggerCloseAnimation,
|
|
82
|
-
}));
|
|
83
|
-
|
|
84
|
-
// Trigger animations on render for inNavModal Android modal
|
|
85
|
-
useEffect(() => {
|
|
86
|
-
if (Platform.OS === 'android' && inNavModal) {
|
|
87
|
-
backgroundOpacity.value = withDelay(
|
|
88
|
-
300,
|
|
89
|
-
withTiming(1, {
|
|
90
|
-
duration: 200,
|
|
91
|
-
easing: Easing.out(Easing.quad),
|
|
92
|
-
})
|
|
93
|
-
);
|
|
94
|
-
pretendContentTranslateY.value = withDelay(
|
|
95
|
-
500,
|
|
96
|
-
withTiming(0, {
|
|
97
|
-
duration: 300,
|
|
98
|
-
easing: Easing.out(Easing.quad),
|
|
99
|
-
})
|
|
100
|
-
);
|
|
101
|
-
}
|
|
102
|
-
}, [inNavModal, backgroundOpacity, pretendContentTranslateY]);
|
|
103
|
-
|
|
104
|
-
const animatedBackgroundStyle = useAnimatedStyle(() => ({
|
|
105
|
-
backgroundColor: hexWithOpacity(
|
|
106
|
-
theme.components.overlay.backgroundColor,
|
|
107
|
-
backgroundOpacity.value * (theme.components.overlay.opacity / 100)
|
|
108
|
-
),
|
|
109
|
-
}));
|
|
110
|
-
|
|
111
|
-
const animatedInNavModalStyle = useAnimatedStyle(() => ({
|
|
112
|
-
backgroundColor: hexWithOpacity(
|
|
113
|
-
theme.components.overlay.backgroundColor,
|
|
114
|
-
backgroundOpacity.value * (theme.components.overlay.opacity / 100)
|
|
115
|
-
),
|
|
116
|
-
}));
|
|
117
|
-
|
|
118
|
-
const animatedPretendContentStyle = useAnimatedStyle(() => ({
|
|
119
|
-
transform: [{ translateY: pretendContentTranslateY.value }],
|
|
120
51
|
}));
|
|
121
52
|
|
|
122
53
|
const handleChange = (index: number, position: number, type: SNAP_POINT_TYPE) => {
|
|
@@ -174,7 +105,6 @@ const Modal = ({
|
|
|
174
105
|
noButtons,
|
|
175
106
|
stickyFooter,
|
|
176
107
|
showHandle: props.showHandle,
|
|
177
|
-
background: isBrandBackground ? 'brand' : 'primary',
|
|
178
108
|
});
|
|
179
109
|
|
|
180
110
|
const footer = useMemo(
|
|
@@ -184,7 +114,6 @@ const Modal = ({
|
|
|
184
114
|
<Button
|
|
185
115
|
onPress={handlePrimaryButtonPress}
|
|
186
116
|
text={primaryButtonText}
|
|
187
|
-
inverted={isBrandBackground && inNavModal}
|
|
188
117
|
{...primaryButtonProps}
|
|
189
118
|
variant={(primaryButtonProps?.variant as 'solid') ?? 'solid'}
|
|
190
119
|
colorScheme={(primaryButtonProps?.colorScheme as 'highlight') ?? 'highlight'}
|
|
@@ -194,7 +123,6 @@ const Modal = ({
|
|
|
194
123
|
<Button
|
|
195
124
|
onPress={handleSecondaryButtonPress}
|
|
196
125
|
text={secondaryButtonText}
|
|
197
|
-
inverted={isBrandBackground && inNavModal}
|
|
198
126
|
{...secondaryButtonProps}
|
|
199
127
|
variant={(secondaryButtonProps?.variant as 'outline') ?? 'outline'}
|
|
200
128
|
colorScheme={(secondaryButtonProps?.colorScheme as 'functional') ?? 'functional'}
|
|
@@ -205,8 +133,6 @@ const Modal = ({
|
|
|
205
133
|
[
|
|
206
134
|
handlePrimaryButtonPress,
|
|
207
135
|
handleSecondaryButtonPress,
|
|
208
|
-
inNavModal,
|
|
209
|
-
isBrandBackground,
|
|
210
136
|
onPressPrimaryButton,
|
|
211
137
|
onPressSecondaryButton,
|
|
212
138
|
primaryButtonProps,
|
|
@@ -216,8 +142,6 @@ const Modal = ({
|
|
|
216
142
|
]
|
|
217
143
|
);
|
|
218
144
|
|
|
219
|
-
const InNavModalContainer = scrollable ? ScrollView : View;
|
|
220
|
-
|
|
221
145
|
const content = (
|
|
222
146
|
<>
|
|
223
147
|
{loading ? (
|
|
@@ -228,11 +152,8 @@ const Modal = ({
|
|
|
228
152
|
screenReaderFocusable
|
|
229
153
|
ref={viewRef}
|
|
230
154
|
>
|
|
231
|
-
<Spinner
|
|
232
|
-
|
|
233
|
-
color={isBrandBackground && inNavModal ? theme.color.icon.inverted : undefined}
|
|
234
|
-
/>
|
|
235
|
-
<Heading size="lg" textAlign="center" inverted={isBrandBackground && inNavModal}>
|
|
155
|
+
<Spinner size="lg" />
|
|
156
|
+
<Heading size="lg" textAlign="center">
|
|
236
157
|
{loadingHeading}
|
|
237
158
|
</Heading>
|
|
238
159
|
</View>
|
|
@@ -247,22 +168,17 @@ const Modal = ({
|
|
|
247
168
|
<View style={styles.header}>
|
|
248
169
|
<View style={styles.headerTextContent}>
|
|
249
170
|
{heading && !image ? (
|
|
250
|
-
<Heading size="lg" accessible
|
|
171
|
+
<Heading size="lg" accessible>
|
|
251
172
|
{heading}
|
|
252
173
|
</Heading>
|
|
253
174
|
) : null}
|
|
254
|
-
{description && !image ?
|
|
255
|
-
<BodyText accessible inverted={isBrandBackground && inNavModal}>
|
|
256
|
-
{description}
|
|
257
|
-
</BodyText>
|
|
258
|
-
) : null}
|
|
175
|
+
{description && !image ? <BodyText accessible>{description}</BodyText> : null}
|
|
259
176
|
</View>
|
|
260
177
|
{showCloseButton ? (
|
|
261
178
|
<UnstyledIconButton
|
|
262
179
|
icon={CloseMediumIcon}
|
|
263
180
|
onPress={handleCloseButtonPress}
|
|
264
181
|
accessibilityLabel="Close modal"
|
|
265
|
-
inverted={isBrandBackground && inNavModal}
|
|
266
182
|
{...closeButtonProps}
|
|
267
183
|
/>
|
|
268
184
|
) : null}
|
|
@@ -272,45 +188,20 @@ const Modal = ({
|
|
|
272
188
|
{image}
|
|
273
189
|
<View style={styles.textContent}>
|
|
274
190
|
{heading ? (
|
|
275
|
-
<Heading
|
|
276
|
-
size="lg"
|
|
277
|
-
textAlign="center"
|
|
278
|
-
accessible
|
|
279
|
-
inverted={isBrandBackground && inNavModal}
|
|
280
|
-
>
|
|
191
|
+
<Heading size="lg" textAlign="center" accessible>
|
|
281
192
|
{heading}
|
|
282
193
|
</Heading>
|
|
283
194
|
) : null}
|
|
284
195
|
{description ? (
|
|
285
|
-
<BodyText
|
|
286
|
-
textAlign="center"
|
|
287
|
-
accessible
|
|
288
|
-
inverted={isBrandBackground && inNavModal}
|
|
289
|
-
>
|
|
196
|
+
<BodyText textAlign="center" accessible>
|
|
290
197
|
{description}
|
|
291
198
|
</BodyText>
|
|
292
199
|
) : null}
|
|
293
200
|
</View>
|
|
294
201
|
</View>
|
|
295
202
|
) : null}
|
|
296
|
-
{
|
|
297
|
-
|
|
298
|
-
style={{
|
|
299
|
-
flex: stickyFooter ? 1 : 0,
|
|
300
|
-
...(scrollable ? { marginHorizontal: -1 } : {}),
|
|
301
|
-
}}
|
|
302
|
-
{...(scrollable ? { contentContainerStyle: { paddingHorizontal: 1 } } : {})}
|
|
303
|
-
>
|
|
304
|
-
{children}
|
|
305
|
-
{!stickyFooter ? (
|
|
306
|
-
<View style={styles.inNavModalFooterContainer}>{footer}</View>
|
|
307
|
-
) : null}
|
|
308
|
-
</InNavModalContainer>
|
|
309
|
-
)}
|
|
310
|
-
{!inNavModal && children}
|
|
311
|
-
{((!stickyFooter && !inNavModal) || (inNavModal && stickyFooter)) && !noButtons
|
|
312
|
-
? footer
|
|
313
|
-
: null}
|
|
203
|
+
{children}
|
|
204
|
+
{!stickyFooter && !noButtons ? footer : null}
|
|
314
205
|
</View>
|
|
315
206
|
)}
|
|
316
207
|
</>
|
|
@@ -325,27 +216,7 @@ const Modal = ({
|
|
|
325
216
|
[footer]
|
|
326
217
|
);
|
|
327
218
|
|
|
328
|
-
return
|
|
329
|
-
<View
|
|
330
|
-
style={{
|
|
331
|
-
flex: 1,
|
|
332
|
-
backgroundColor: theme.color.background[isBrandBackground ? 'brand' : 'primary'],
|
|
333
|
-
}}
|
|
334
|
-
>
|
|
335
|
-
{Platform.OS === 'android' ? (
|
|
336
|
-
<Animated.View style={[styles.androidContainer, animatedBackgroundStyle]}>
|
|
337
|
-
<Animated.View style={[styles.pretendContent, animatedPretendContentStyle]} />
|
|
338
|
-
</Animated.View>
|
|
339
|
-
) : null}
|
|
340
|
-
<Animated.View
|
|
341
|
-
style={[styles.inNavModalContainer, Platform.OS === 'android' && animatedInNavModalStyle]}
|
|
342
|
-
>
|
|
343
|
-
<SafeAreaView edges={['top', 'bottom']} style={styles.inNavModalContent}>
|
|
344
|
-
{content}
|
|
345
|
-
</SafeAreaView>
|
|
346
|
-
</Animated.View>
|
|
347
|
-
</View>
|
|
348
|
-
) : (
|
|
219
|
+
return (
|
|
349
220
|
<BottomSheetModal
|
|
350
221
|
ref={bottomSheetModalRef}
|
|
351
222
|
enableDynamicSizing={true}
|
|
@@ -464,40 +335,6 @@ const styles = StyleSheet.create((theme, rt) => ({
|
|
|
464
335
|
paddingHorizontal: theme.components.bottomSheet.padding,
|
|
465
336
|
paddingBottom: theme.components.bottomSheet.padding + rt.insets.bottom,
|
|
466
337
|
},
|
|
467
|
-
inNavModalContainer: {
|
|
468
|
-
flex: 1,
|
|
469
|
-
...(Platform.OS === 'ios' ? { backgroundColor: theme.components.overlay.backgroundColor } : {}),
|
|
470
|
-
},
|
|
471
|
-
inNavModalContent: {
|
|
472
|
-
flex: 1,
|
|
473
|
-
borderTopLeftRadius: theme.components.modal.borderRadius,
|
|
474
|
-
borderTopRightRadius: theme.components.modal.borderRadius,
|
|
475
|
-
backgroundColor: theme.color.surface.neutral.strong,
|
|
476
|
-
padding: theme.components.bottomSheet.padding,
|
|
477
|
-
variants: {
|
|
478
|
-
background: {
|
|
479
|
-
primary: {},
|
|
480
|
-
brand: {
|
|
481
|
-
backgroundColor: theme.color.background.brand,
|
|
482
|
-
},
|
|
483
|
-
},
|
|
484
|
-
},
|
|
485
|
-
},
|
|
486
|
-
inNavModalFooterContainer: {
|
|
487
|
-
paddingTop: theme.components.bottomSheet.padding,
|
|
488
|
-
},
|
|
489
|
-
androidContainer: {
|
|
490
|
-
height: rt.insets.top + 18,
|
|
491
|
-
paddingLeft: theme.components.bottomSheet.padding,
|
|
492
|
-
paddingRight: theme.components.bottomSheet.padding,
|
|
493
|
-
justifyContent: 'flex-end',
|
|
494
|
-
},
|
|
495
|
-
pretendContent: {
|
|
496
|
-
borderTopLeftRadius: theme.components.modal.borderRadius,
|
|
497
|
-
borderTopRightRadius: theme.components.modal.borderRadius,
|
|
498
|
-
height: 12,
|
|
499
|
-
backgroundColor: theme.components.parts.modalStack.backgroundColorCardTop,
|
|
500
|
-
},
|
|
501
338
|
}));
|
|
502
339
|
|
|
503
340
|
export default Modal;
|