|
1 | 1 | import type { FC } from 'react'; |
| 2 | +import React from 'react'; |
| 3 | +import { useTranslation } from 'react-i18next'; |
| 4 | +import { Box, Flex } from 'react-native-flex-layout'; |
2 | 5 | import type { ModalProps } from 'react-native-paper'; |
3 | | -import { Modal, useTheme } from 'react-native-paper'; |
| 6 | +import { |
| 7 | + Button, |
| 8 | + Icon, |
| 9 | + IconButton, |
| 10 | + Modal, |
| 11 | + Text, |
| 12 | + useTheme, |
| 13 | +} from 'react-native-paper'; |
4 | 14 |
|
5 | 15 | import { Platform } from 'react-native'; |
6 | 16 |
|
7 | 17 | import { spacing } from '@/constants'; |
8 | 18 |
|
9 | | -export interface BaseModalProps extends ModalProps { |
10 | | - backgroundColor?: string; |
11 | | - disableSidePadding?: boolean; |
12 | | - disableSideMargin?: boolean; |
13 | | - isScreen?: boolean; |
| 19 | +export interface ExtendableModalProps { |
| 20 | + visible: boolean; |
| 21 | + onDismiss: () => void; |
| 22 | +} |
| 23 | + |
| 24 | +export interface BaseModalProps extends ExtendableModalProps { |
| 25 | + title: string; |
| 26 | + dismissButton: |
| 27 | + | false |
| 28 | + | 'dismiss' |
| 29 | + | 'cancel' |
| 30 | + | { |
| 31 | + label: string; |
| 32 | + onPress: () => void; |
| 33 | + variant?: 'text' | 'outlined' | 'contained'; |
| 34 | + disabled?: boolean; |
| 35 | + textColor?: string; |
| 36 | + }; |
| 37 | + dismissButtonDisabled?: boolean; |
| 38 | + actions?: { |
| 39 | + label: string; |
| 40 | + onPress: () => void; |
| 41 | + variant?: 'text' | 'outlined' | 'contained'; |
| 42 | + disabled?: boolean; |
| 43 | + buttonColor?: string; |
| 44 | + textColor?: string; |
| 45 | + loading?: boolean; |
| 46 | + }[]; |
| 47 | + description?: string; |
| 48 | + icon?: string; |
| 49 | + modalProps?: Omit<ModalProps, 'visible' | 'onDismiss' | 'children'>; |
| 50 | + fullscreen?: boolean; |
| 51 | + hideBottomPadding?: boolean; |
| 52 | + legacy?: boolean; |
| 53 | + children?: React.ReactNode; |
14 | 54 | } |
15 | 55 |
|
16 | 56 | const BaseModal: FC<BaseModalProps> = ({ |
| 57 | + visible, |
| 58 | + onDismiss, |
| 59 | + title, |
| 60 | + description, |
| 61 | + fullscreen, |
17 | 62 | children, |
18 | | - backgroundColor, |
19 | | - disableSidePadding, |
20 | | - disableSideMargin, |
21 | | - isScreen, |
| 63 | + dismissButton = 'dismiss', |
| 64 | + dismissButtonDisabled, |
| 65 | + actions, |
| 66 | + icon, |
| 67 | + hideBottomPadding, |
| 68 | + legacy, |
22 | 69 | ...rest |
23 | 70 | }) => { |
24 | 71 | const theme = useTheme(); |
25 | | - |
26 | | - if (isScreen) { |
27 | | - disableSideMargin = true; |
28 | | - disableSidePadding = true; |
29 | | - } |
30 | | - |
31 | | - const { visible } = rest; |
| 72 | + const { t } = useTranslation(); |
32 | 73 |
|
33 | 74 | return ( |
34 | 75 | <Modal |
35 | | - {...rest} |
| 76 | + visible={visible} |
| 77 | + onDismiss={onDismiss} |
| 78 | + dismissableBackButton |
| 79 | + dismissable |
| 80 | + {...rest.modalProps} |
36 | 81 | style={{ |
37 | 82 | ...(Platform.OS !== 'android' |
38 | 83 | ? { height: '100%', paddingBottom: spacing * 10 } |
39 | 84 | : {}), |
40 | | - backgroundColor: visible ? 'rgba(0, 0, 0, 0.5)' : undefined, |
41 | 85 | alignItems: 'center', |
42 | 86 | justifyContent: 'center', |
43 | 87 | flexDirection: 'row', |
44 | 88 | }} |
45 | 89 | contentContainerStyle={{ |
46 | | - backgroundColor: backgroundColor ?? theme.colors.elevation.level4, |
47 | | - ...(disableSidePadding |
48 | | - ? { paddingTop: 8, paddingBottom: 8 } |
49 | | - : { padding: 8 }), |
50 | | - borderRadius: isScreen ? 0 : 28, |
51 | | - marginVertical: isScreen ? 0 : 8, |
52 | | - marginHorizontal: disableSideMargin ? 0 : 20, |
53 | | - maxWidth: 450, |
| 90 | + backgroundColor: theme.colors.surfaceVariant, |
| 91 | + padding: fullscreen ? 8 : 24, |
| 92 | + paddingBottom: 0, |
| 93 | + borderRadius: fullscreen ? 0 : 28, |
| 94 | + marginVertical: fullscreen ? 0 : 8, |
| 95 | + marginHorizontal: fullscreen ? 0 : 32, |
| 96 | + maxWidth: fullscreen ? undefined : 450, |
| 97 | + height: fullscreen ? '100%' : undefined, |
54 | 98 | flex: 1, |
| 99 | + justifyContent: 'space-between', |
55 | 100 | }} |
56 | 101 | > |
57 | | - {children} |
| 102 | + <Box style={{ flex: fullscreen ? 1 : undefined }}> |
| 103 | + {!legacy ? ( |
| 104 | + <> |
| 105 | + <Flex direction="column" style={{ gap: 16 }}> |
| 106 | + {icon && !fullscreen ? ( |
| 107 | + <Flex direction="row" center> |
| 108 | + <Icon |
| 109 | + source={icon} |
| 110 | + color={theme.colors.secondary} |
| 111 | + size={24} |
| 112 | + /> |
| 113 | + </Flex> |
| 114 | + ) : null} |
| 115 | + <Flex direction="row" justify="between" items="center"> |
| 116 | + <Flex |
| 117 | + direction="row" |
| 118 | + justify="start" |
| 119 | + items="center" |
| 120 | + style={{ gap: 8, flex: 1 }} |
| 121 | + > |
| 122 | + {fullscreen && dismissButton ? ( |
| 123 | + <IconButton icon="close" onPress={onDismiss} /> |
| 124 | + ) : null} |
| 125 | + <Text |
| 126 | + variant="headlineSmall" |
| 127 | + style={{ |
| 128 | + textAlign: icon ? 'center' : undefined, |
| 129 | + color: theme.colors.onSurface, |
| 130 | + }} |
| 131 | + > |
| 132 | + {title} |
| 133 | + </Text> |
| 134 | + </Flex> |
| 135 | + </Flex> |
| 136 | + {actions?.length && actions.length > 1 && fullscreen ? ( |
| 137 | + <Button |
| 138 | + onPress={actions[0].onPress} |
| 139 | + disabled={actions[0].disabled} |
| 140 | + mode={actions[0].variant ? actions[0].variant : 'text'} |
| 141 | + buttonColor={actions[0].buttonColor} |
| 142 | + textColor={actions[0].textColor} |
| 143 | + style={{ |
| 144 | + paddingVertical: actions[0].variant !== 'text' ? 0 : 8, |
| 145 | + minWidth: 100, |
| 146 | + alignSelf: 'flex-start', |
| 147 | + }} |
| 148 | + loading={actions[0].loading} |
| 149 | + > |
| 150 | + {actions[0].label} |
| 151 | + </Button> |
| 152 | + ) : null} |
| 153 | + {description && !fullscreen ? ( |
| 154 | + <Text |
| 155 | + variant="bodyMedium" |
| 156 | + style={{ color: theme.colors.onSurface }} |
| 157 | + > |
| 158 | + {description} |
| 159 | + </Text> |
| 160 | + ) : null} |
| 161 | + </Flex> |
| 162 | + </> |
| 163 | + ) : null} |
| 164 | + {children ? ( |
| 165 | + <Box |
| 166 | + pt={fullscreen || legacy ? 0 : 20} |
| 167 | + style={{ flex: fullscreen ? 1 : 0 }} |
| 168 | + > |
| 169 | + {children} |
| 170 | + </Box> |
| 171 | + ) : null} |
| 172 | + </Box> |
| 173 | + {fullscreen ? null : ( |
| 174 | + <Box pv={hideBottomPadding ? 12 : 20}> |
| 175 | + <Flex |
| 176 | + direction="row" |
| 177 | + justify="end" |
| 178 | + items="center" |
| 179 | + style={{ gap: 8 }} |
| 180 | + wrap="wrap" |
| 181 | + > |
| 182 | + {dismissButton ? ( |
| 183 | + <Button |
| 184 | + onPress={ |
| 185 | + typeof dismissButton === 'object' |
| 186 | + ? dismissButton.onPress |
| 187 | + : onDismiss |
| 188 | + } |
| 189 | + disabled={ |
| 190 | + dismissButtonDisabled |
| 191 | + ? dismissButtonDisabled |
| 192 | + : typeof dismissButton === 'object' && |
| 193 | + dismissButton.disabled |
| 194 | + ? dismissButton.disabled |
| 195 | + : false |
| 196 | + } |
| 197 | + mode={ |
| 198 | + typeof dismissButton === 'object' && dismissButton.variant |
| 199 | + ? dismissButton.variant |
| 200 | + : 'text' |
| 201 | + } |
| 202 | + textColor={ |
| 203 | + typeof dismissButton === 'object' && dismissButton.textColor |
| 204 | + ? dismissButton.textColor |
| 205 | + : undefined |
| 206 | + } |
| 207 | + > |
| 208 | + {typeof dismissButton === 'object' |
| 209 | + ? dismissButton.label |
| 210 | + : dismissButton === 'cancel' |
| 211 | + ? t('cancel') |
| 212 | + : t('dismiss')} |
| 213 | + </Button> |
| 214 | + ) : null} |
| 215 | + {actions?.map((action, index) => ( |
| 216 | + <Button |
| 217 | + key={`action-${index}`} |
| 218 | + onPress={action.onPress} |
| 219 | + disabled={action.disabled} |
| 220 | + mode={action.variant ? action.variant : 'text'} |
| 221 | + buttonColor={action.buttonColor} |
| 222 | + textColor={action.textColor} |
| 223 | + style={{ |
| 224 | + paddingVertical: action.variant !== 'text' ? 0 : 8, |
| 225 | + minWidth: 100, |
| 226 | + }} |
| 227 | + loading={action.loading} |
| 228 | + > |
| 229 | + {action.label} |
| 230 | + </Button> |
| 231 | + ))} |
| 232 | + </Flex> |
| 233 | + </Box> |
| 234 | + )} |
58 | 235 | </Modal> |
59 | 236 | ); |
60 | 237 | }; |
|
0 commit comments