@utilitywarehouse/hearth-react-native 0.15.0 → 0.15.2
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/.turbo/turbo-build.log +1 -1
- package/.turbo/turbo-lint.log +1 -1
- package/CHANGELOG.md +14 -0
- package/build/components/BottomSheet/index.d.ts +5 -5
- package/build/components/BottomSheet/index.js +4 -4
- package/build/components/CurrencyInput/CurrencyInput.d.ts +1 -1
- package/build/components/CurrencyInput/CurrencyInput.js +2 -2
- package/build/components/CurrencyInput/CurrencyInput.props.d.ts +3 -1
- package/build/components/Modal/Modal.d.ts +1 -1
- package/build/components/Modal/Modal.js +49 -4
- package/build/components/Modal/Modal.props.d.ts +1 -0
- package/package.json +1 -1
- package/src/components/BottomSheet/index.ts +7 -5
- package/src/components/CurrencyInput/CurrencyInput.props.ts +3 -1
- package/src/components/CurrencyInput/CurrencyInput.tsx +2 -1
- package/src/components/Modal/Modal.props.ts +1 -0
- package/src/components/Modal/Modal.tsx +86 -23
package/.turbo/turbo-build.log
CHANGED
package/.turbo/turbo-lint.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @utilitywarehouse/hearth-react-native@0.15.
|
|
2
|
+
> @utilitywarehouse/hearth-react-native@0.15.2 lint /home/runner/work/hearth/hearth/packages/react-native
|
|
3
3
|
> TIMING=1 eslint --max-warnings 0
|
|
4
4
|
|
|
5
5
|
Rule | Time (ms) | Relative
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# @utilitywarehouse/hearth-react-native
|
|
2
2
|
|
|
3
|
+
## 0.15.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#771](https://github.com/utilitywarehouse/hearth/pull/771) [`5320be5`](https://github.com/utilitywarehouse/hearth/commit/5320be544992c137e201be7750d30ea098a7d399) Thanks [@jordmccord](https://github.com/jordmccord)! - 🐛 [FIX]: Adds missing `ref` prop to the `CurrencyInput` component
|
|
8
|
+
|
|
9
|
+
## 0.15.1
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- [#768](https://github.com/utilitywarehouse/hearth/pull/768) [`570f240`](https://github.com/utilitywarehouse/hearth/commit/570f240a448eae546b893ed3ad69235213ee5fac) Thanks [@jordmccord](https://github.com/jordmccord)! - 🐛 [FIX]: Makes `Modal` footer sticky by default
|
|
14
|
+
|
|
15
|
+
This change updates the `Modal` component to have a sticky footer by default, enhancing user experience by keeping action buttons accessible. The `stickyFooter` prop has been added to allow developers to disable this behavior if needed.
|
|
16
|
+
|
|
3
17
|
## 0.15.0
|
|
4
18
|
|
|
5
19
|
### Minor Changes
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
export { BottomSheetFlashList, BottomSheetFooter, BottomSheetModalProvider, BottomSheetSectionList, BottomSheetVirtualizedList, useBottomSheet, useBottomSheetModal, type BottomSheetBackdropProps, type BottomSheetBackgroundProps, type BottomSheetHandleProps, type BottomSheetModalProps, } from '@gorhom/bottom-sheet';
|
|
1
2
|
export { default as BottomSheet } from './BottomSheet';
|
|
2
|
-
export { default as BottomSheetView } from './BottomSheetView';
|
|
3
|
-
export { default as BottomSheetScrollView } from './BottomSheetScrollView';
|
|
4
|
-
export { default as BottomSheetModal } from './BottomSheetModal';
|
|
5
|
-
export { default as BottomSheetFlatList } from './BottomSheetFlatList';
|
|
6
|
-
export { BottomSheetModalProvider, BottomSheetSectionList, BottomSheetVirtualizedList, useBottomSheet, useBottomSheetModal, type BottomSheetBackdropProps, type BottomSheetBackgroundProps, type BottomSheetHandleProps, type BottomSheetModalProps, } from '@gorhom/bottom-sheet';
|
|
7
3
|
export type { default as BottomSheetProps } from './BottomSheet.props';
|
|
4
|
+
export { default as BottomSheetFlatList } from './BottomSheetFlatList';
|
|
5
|
+
export { default as BottomSheetModal } from './BottomSheetModal';
|
|
6
|
+
export { default as BottomSheetScrollView } from './BottomSheetScrollView';
|
|
7
|
+
export { default as BottomSheetView } from './BottomSheetView';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
export { BottomSheetFlashList, BottomSheetFooter, BottomSheetModalProvider, BottomSheetSectionList, BottomSheetVirtualizedList, useBottomSheet, useBottomSheetModal, } from '@gorhom/bottom-sheet';
|
|
1
2
|
export { default as BottomSheet } from './BottomSheet';
|
|
2
|
-
export { default as BottomSheetView } from './BottomSheetView';
|
|
3
|
-
export { default as BottomSheetScrollView } from './BottomSheetScrollView';
|
|
4
|
-
export { default as BottomSheetModal } from './BottomSheetModal';
|
|
5
3
|
export { default as BottomSheetFlatList } from './BottomSheetFlatList';
|
|
6
|
-
export {
|
|
4
|
+
export { default as BottomSheetModal } from './BottomSheetModal';
|
|
5
|
+
export { default as BottomSheetScrollView } from './BottomSheetScrollView';
|
|
6
|
+
export { default as BottomSheetView } from './BottomSheetView';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type CurrencyInputProps from './CurrencyInput.props';
|
|
2
2
|
declare const CurrencyInput: {
|
|
3
|
-
({ validationStatus, disabled, focused, readonly, placeholder, inBottomSheet, required, disableGroupSeparator, value, onChangeText, ...rest }: CurrencyInputProps): import("react/jsx-runtime").JSX.Element;
|
|
3
|
+
({ validationStatus, disabled, focused, readonly, placeholder, inBottomSheet, required, disableGroupSeparator, value, onChangeText, ref, ...rest }: CurrencyInputProps): import("react/jsx-runtime").JSX.Element;
|
|
4
4
|
displayName: string;
|
|
5
5
|
};
|
|
6
6
|
export default CurrencyInput;
|
|
@@ -5,7 +5,7 @@ import { formatThousands } from '../../utils';
|
|
|
5
5
|
import { DetailText } from '../DetailText';
|
|
6
6
|
import { useFormFieldContext } from '../FormField';
|
|
7
7
|
import { Input, InputField, InputSlot } from '../Input';
|
|
8
|
-
const CurrencyInput = ({ validationStatus = 'initial', disabled, focused, readonly, placeholder, inBottomSheet = false, required, disableGroupSeparator = false, value, onChangeText, ...rest }) => {
|
|
8
|
+
const CurrencyInput = ({ validationStatus = 'initial', disabled, focused, readonly, placeholder, inBottomSheet = false, required, disableGroupSeparator = false, value, onChangeText, ref, ...rest }) => {
|
|
9
9
|
const formFieldContext = useFormFieldContext();
|
|
10
10
|
const { disabled: formFieldDisabled } = formFieldContext;
|
|
11
11
|
const validationStatusFromContext = formFieldContext?.validationStatus ?? validationStatus;
|
|
@@ -21,7 +21,7 @@ const CurrencyInput = ({ validationStatus = 'initial', disabled, focused, readon
|
|
|
21
21
|
}
|
|
22
22
|
};
|
|
23
23
|
const displayValue = !disableGroupSeparator && typeof value === 'string' ? formatThousands(value) : value;
|
|
24
|
-
return (_jsxs(Input, { validationStatus: validationStatusFromContext, disabled: formFieldDisabled ?? disabled, readonly: readonly, focused: focused, style: styles.wrap, children: [_jsx(InputSlot, { children: _jsx(DetailText, { size: "4xl", style: styles.text, accessible: false, children: "\u00A3" }) }), _jsx(InputField, { inputMode: "decimal", inBottomSheet: inBottomSheet, accessibilityHint: 'Enter the amount in pounds and pence, for example "10.99"', ...rest, placeholder: getPlaceholder, keyboardType: "decimal-pad", style: styles.input, value: displayValue, onChangeText: handleChangeText })] }));
|
|
24
|
+
return (_jsxs(Input, { validationStatus: validationStatusFromContext, disabled: formFieldDisabled ?? disabled, readonly: readonly, focused: focused, style: styles.wrap, children: [_jsx(InputSlot, { children: _jsx(DetailText, { size: "4xl", style: styles.text, accessible: false, children: "\u00A3" }) }), _jsx(InputField, { ref: ref, inputMode: "decimal", inBottomSheet: inBottomSheet, accessibilityHint: 'Enter the amount in pounds and pence, for example "10.99"', ...rest, placeholder: getPlaceholder, keyboardType: "decimal-pad", style: styles.input, value: displayValue, onChangeText: handleChangeText })] }));
|
|
25
25
|
};
|
|
26
26
|
CurrencyInput.displayName = 'CurrencyInput';
|
|
27
27
|
const styles = StyleSheet.create(theme => ({
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Ref } from 'react';
|
|
2
|
+
import type { TextInput, TextInputProps, ViewProps } from 'react-native';
|
|
2
3
|
export interface CurrencyInputBaseProps {
|
|
4
|
+
ref?: Ref<TextInput>;
|
|
3
5
|
disabled?: boolean;
|
|
4
6
|
validationStatus?: 'initial' | 'valid' | 'invalid';
|
|
5
7
|
readonly?: boolean;
|
|
@@ -3,5 +3,5 @@ import ModalProps from './Modal.props';
|
|
|
3
3
|
type Modal<T = any> = BottomSheetModalMethods<T> & {
|
|
4
4
|
triggerCloseAnimation?: () => void;
|
|
5
5
|
};
|
|
6
|
-
declare const Modal: ({ ref, children, heading, description, showCloseButton, primaryButtonText, secondaryButtonText, onPressPrimaryButton, onPressCloseButton, onPressSecondaryButton, closeOnPrimaryButtonPress, closeOnSecondaryButtonPress, loading, fullscreen, image, primaryButtonProps, secondaryButtonProps, closeButtonProps, inNavModal, ...props }: ModalProps) => import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
declare const Modal: ({ ref, children, heading, description, showCloseButton, primaryButtonText, secondaryButtonText, onPressPrimaryButton, onPressCloseButton, onPressSecondaryButton, closeOnPrimaryButtonPress, closeOnSecondaryButtonPress, loading, fullscreen, image, primaryButtonProps, secondaryButtonProps, closeButtonProps, inNavModal, stickyFooter, ...props }: ModalProps) => import("react/jsx-runtime").JSX.Element;
|
|
7
7
|
export default Modal;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { BottomSheetFooter, } from '@gorhom/bottom-sheet';
|
|
2
3
|
import { CloseMediumIcon } from '@utilitywarehouse/hearth-react-native-icons';
|
|
3
4
|
import { useCallback, useEffect, useImperativeHandle, useRef } from 'react';
|
|
4
5
|
import { AccessibilityInfo, Platform, View, findNodeHandle } from 'react-native';
|
|
@@ -12,7 +13,7 @@ import { Button } from '../Button';
|
|
|
12
13
|
import { Heading } from '../Heading';
|
|
13
14
|
import { Spinner } from '../Spinner';
|
|
14
15
|
import { UnstyledIconButton } from '../UnstyledIconButton';
|
|
15
|
-
const Modal = ({ ref, children, heading, description, showCloseButton = true, primaryButtonText, secondaryButtonText, onPressPrimaryButton, onPressCloseButton, onPressSecondaryButton, closeOnPrimaryButtonPress = true, closeOnSecondaryButtonPress = true, loading, fullscreen = false, image, primaryButtonProps, secondaryButtonProps, closeButtonProps, inNavModal = false, ...props }) => {
|
|
16
|
+
const Modal = ({ ref, children, heading, description, showCloseButton = true, primaryButtonText, secondaryButtonText, onPressPrimaryButton, onPressCloseButton, onPressSecondaryButton, closeOnPrimaryButtonPress = true, closeOnSecondaryButtonPress = true, loading, fullscreen = false, image, primaryButtonProps, secondaryButtonProps, closeButtonProps, inNavModal = false, stickyFooter = true, ...props }) => {
|
|
16
17
|
const bottomSheetModalRef = useRef(null);
|
|
17
18
|
const viewRef = useRef(null);
|
|
18
19
|
const scrollViewRef = useRef(null);
|
|
@@ -97,9 +98,24 @@ const Modal = ({ ref, children, heading, description, showCloseButton = true, pr
|
|
|
97
98
|
bottomSheetModalRef.current?.dismiss();
|
|
98
99
|
}
|
|
99
100
|
};
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
101
|
+
const noButtons = !onPressPrimaryButton && !onPressSecondaryButton;
|
|
102
|
+
styles.useVariants({
|
|
103
|
+
loading,
|
|
104
|
+
bothButtons: !!(onPressPrimaryButton && onPressSecondaryButton),
|
|
105
|
+
noButtons,
|
|
106
|
+
stickyFooter,
|
|
107
|
+
});
|
|
108
|
+
const footer = (_jsxs(View, { style: styles.footer, children: [onPressPrimaryButton && primaryButtonText ? (_jsx(Button, { onPress: handlePrimaryButtonPress, text: primaryButtonText, ...primaryButtonProps, variant: primaryButtonProps?.variant ?? 'solid', colorScheme: primaryButtonProps?.colorScheme ?? 'highlight' })) : null, onPressSecondaryButton && secondaryButtonText ? (_jsx(Button, { onPress: handleSecondaryButtonPress, text: secondaryButtonText, ...secondaryButtonProps, variant: secondaryButtonProps?.variant ?? 'outline', colorScheme: secondaryButtonProps?.colorScheme ?? 'functional' })) : null] }));
|
|
109
|
+
const content = (_jsx(_Fragment, { children: loading ? (_jsxs(View, { style: styles.loadingContainer, accessible: Platform.OS === 'android' ? true : undefined, accessibilityLabel: Platform.OS === 'android' ? 'Loading' : undefined, screenReaderFocusable: true, ref: viewRef, children: [_jsx(Spinner, { size: "lg" }), _jsx(Heading, { size: "lg", textAlign: "center", children: "Loading..." })] })) : (_jsxs(View, { style: styles.container, accessible: Platform.OS === 'android' ? true : undefined, accessibilityLabel: Platform.OS === 'android' ? 'Modal content' : undefined, screenReaderFocusable: true, ref: viewRef, children: [_jsxs(View, { style: styles.header, children: [_jsxs(View, { style: styles.headerTextContent, children: [heading && !image ? (_jsx(Heading, { size: "lg", accessible: true, children: heading })) : null, description && !image ? _jsx(BodyText, { accessible: true, children: description }) : null] }), showCloseButton ? (_jsx(UnstyledIconButton, { icon: CloseMediumIcon, onPress: handleCloseButtonPress, accessibilityLabel: "Close modal", ...closeButtonProps })) : null] }), image ? (_jsxs(View, { style: styles.imageContainer, children: [image, _jsxs(View, { style: styles.textContent, children: [heading ? (_jsx(Heading, { size: "lg", textAlign: "center", accessible: true, children: heading })) : null, description ? (_jsx(BodyText, { textAlign: "center", accessible: true, children: description })) : null] })] })) : null, children, !stickyFooter && !noButtons ? footer : null] })) }));
|
|
110
|
+
const renderFooter = useCallback((props) => (_jsx(BottomSheetFooter, { ...props, children: _jsx(View, { style: styles.footerWrap, children: footer }) })), [
|
|
111
|
+
onPressPrimaryButton,
|
|
112
|
+
primaryButtonText,
|
|
113
|
+
onPressSecondaryButton,
|
|
114
|
+
secondaryButtonText,
|
|
115
|
+
primaryButtonProps,
|
|
116
|
+
secondaryButtonProps,
|
|
117
|
+
]);
|
|
118
|
+
return inNavModal ? (_jsxs(View, { style: { flex: 1, backgroundColor: theme.color.background.primary }, children: [Platform.OS === 'android' ? (_jsx(Animated.View, { style: [styles.androidContainer, animatedBackgroundStyle], children: _jsx(Animated.View, { style: [styles.pretendContent, animatedPretendContentStyle] }) })) : null, _jsx(Animated.View, { style: [styles.inNavModalContainer, Platform.OS === 'android' && animatedInNavModalStyle], children: _jsx(View, { style: styles.inNavModalContent, children: content }) })] })) : (_jsxs(BottomSheetModal, { ref: bottomSheetModalRef, enableDynamicSizing: true, snapPoints: image || fullscreen ? ['90%'] : props.snapPoints, showHandle: typeof loading !== 'undefined' && loading ? false : props.showHandle, accessible: false, style: styles.modal, footerComponent: stickyFooter && !noButtons ? renderFooter : undefined, ...props, onChange: handleChange, children: [loading ? _jsx(View, { style: styles.loadingTop }) : null, _jsx(BottomSheetScrollView, { contentContainerStyle: styles.scrollView, ref: scrollViewRef, children: content })] }));
|
|
103
119
|
};
|
|
104
120
|
const styles = StyleSheet.create((theme, rt) => ({
|
|
105
121
|
modal: {
|
|
@@ -116,6 +132,30 @@ const styles = StyleSheet.create((theme, rt) => ({
|
|
|
116
132
|
},
|
|
117
133
|
},
|
|
118
134
|
},
|
|
135
|
+
scrollView: {
|
|
136
|
+
flex: 1,
|
|
137
|
+
variants: {
|
|
138
|
+
bothButtons: {
|
|
139
|
+
true: {
|
|
140
|
+
paddingBottom: 166 + rt.insets.bottom - theme.components.modal.padding,
|
|
141
|
+
},
|
|
142
|
+
false: {
|
|
143
|
+
paddingBottom: 102 + rt.insets.bottom - theme.components.modal.padding,
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
noButtons: {
|
|
147
|
+
true: {
|
|
148
|
+
paddingBottom: rt.insets.bottom + theme.components.modal.padding,
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
stickyFooter: {
|
|
152
|
+
true: {},
|
|
153
|
+
false: {
|
|
154
|
+
paddingBottom: rt.insets.bottom + theme.components.modal.padding,
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
},
|
|
119
159
|
header: {
|
|
120
160
|
flexDirection: 'row',
|
|
121
161
|
gap: theme.components.modal.gap,
|
|
@@ -149,6 +189,11 @@ const styles = StyleSheet.create((theme, rt) => ({
|
|
|
149
189
|
footer: {
|
|
150
190
|
gap: theme.components.modal.action.gap,
|
|
151
191
|
},
|
|
192
|
+
footerWrap: {
|
|
193
|
+
backgroundColor: theme.color.surface.neutral.strong,
|
|
194
|
+
paddingHorizontal: theme.components.bottomSheet.padding,
|
|
195
|
+
paddingBottom: theme.components.bottomSheet.padding + rt.insets.bottom,
|
|
196
|
+
},
|
|
152
197
|
inNavModalContainer: {
|
|
153
198
|
flex: 1,
|
|
154
199
|
...(Platform.OS === 'ios' ? { backgroundColor: theme.components.overlay.backgroundColor } : {}),
|
|
@@ -11,6 +11,7 @@ interface ModalProps extends Omit<BottomSheetProps, 'children'> {
|
|
|
11
11
|
description?: string;
|
|
12
12
|
inNavModal?: boolean;
|
|
13
13
|
fullscreen?: boolean;
|
|
14
|
+
stickyFooter?: boolean;
|
|
14
15
|
children?: ViewProps['children'];
|
|
15
16
|
onPressPrimaryButton?: () => void;
|
|
16
17
|
primaryButtonText?: string;
|
package/package.json
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
export { default as BottomSheet } from './BottomSheet';
|
|
2
|
-
export { default as BottomSheetView } from './BottomSheetView';
|
|
3
|
-
export { default as BottomSheetScrollView } from './BottomSheetScrollView';
|
|
4
|
-
export { default as BottomSheetModal } from './BottomSheetModal';
|
|
5
|
-
export { default as BottomSheetFlatList } from './BottomSheetFlatList';
|
|
6
1
|
export {
|
|
2
|
+
BottomSheetFlashList,
|
|
3
|
+
BottomSheetFooter,
|
|
7
4
|
BottomSheetModalProvider,
|
|
8
5
|
BottomSheetSectionList,
|
|
9
6
|
BottomSheetVirtualizedList,
|
|
@@ -14,4 +11,9 @@ export {
|
|
|
14
11
|
type BottomSheetHandleProps,
|
|
15
12
|
type BottomSheetModalProps,
|
|
16
13
|
} from '@gorhom/bottom-sheet';
|
|
14
|
+
export { default as BottomSheet } from './BottomSheet';
|
|
17
15
|
export type { default as BottomSheetProps } from './BottomSheet.props';
|
|
16
|
+
export { default as BottomSheetFlatList } from './BottomSheetFlatList';
|
|
17
|
+
export { default as BottomSheetModal } from './BottomSheetModal';
|
|
18
|
+
export { default as BottomSheetScrollView } from './BottomSheetScrollView';
|
|
19
|
+
export { default as BottomSheetView } from './BottomSheetView';
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Ref } from 'react';
|
|
2
|
+
import type { TextInput, TextInputProps, ViewProps } from 'react-native';
|
|
2
3
|
|
|
3
4
|
export interface CurrencyInputBaseProps {
|
|
5
|
+
ref?: Ref<TextInput>;
|
|
4
6
|
disabled?: boolean;
|
|
5
7
|
validationStatus?: 'initial' | 'valid' | 'invalid';
|
|
6
8
|
readonly?: boolean;
|
|
@@ -17,6 +17,7 @@ const CurrencyInput = ({
|
|
|
17
17
|
disableGroupSeparator = false,
|
|
18
18
|
value,
|
|
19
19
|
onChangeText,
|
|
20
|
+
ref,
|
|
20
21
|
...rest
|
|
21
22
|
}: CurrencyInputProps) => {
|
|
22
23
|
const formFieldContext = useFormFieldContext();
|
|
@@ -52,6 +53,7 @@ const CurrencyInput = ({
|
|
|
52
53
|
</DetailText>
|
|
53
54
|
</InputSlot>
|
|
54
55
|
<InputField
|
|
56
|
+
ref={ref as any}
|
|
55
57
|
inputMode="decimal"
|
|
56
58
|
inBottomSheet={inBottomSheet}
|
|
57
59
|
accessibilityHint='Enter the amount in pounds and pence, for example "10.99"'
|
|
@@ -67,7 +69,6 @@ const CurrencyInput = ({
|
|
|
67
69
|
};
|
|
68
70
|
|
|
69
71
|
CurrencyInput.displayName = 'CurrencyInput';
|
|
70
|
-
|
|
71
72
|
const styles = StyleSheet.create(theme => ({
|
|
72
73
|
wrap: {
|
|
73
74
|
height: theme.components.input.currency.height,
|
|
@@ -12,6 +12,7 @@ interface ModalProps extends Omit<BottomSheetProps, 'children'> {
|
|
|
12
12
|
description?: string;
|
|
13
13
|
inNavModal?: boolean;
|
|
14
14
|
fullscreen?: boolean;
|
|
15
|
+
stickyFooter?: boolean;
|
|
15
16
|
children?: ViewProps['children'];
|
|
16
17
|
onPressPrimaryButton?: () => void;
|
|
17
18
|
primaryButtonText?: string;
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
BottomSheetFooter,
|
|
3
|
+
BottomSheetFooterProps,
|
|
4
|
+
BottomSheetScrollViewMethods,
|
|
5
|
+
SNAP_POINT_TYPE,
|
|
6
|
+
} from '@gorhom/bottom-sheet';
|
|
2
7
|
import { BottomSheetModalMethods } from '@gorhom/bottom-sheet/lib/typescript/types';
|
|
3
8
|
import { CloseMediumIcon } from '@utilitywarehouse/hearth-react-native-icons';
|
|
4
9
|
import { useCallback, useEffect, useImperativeHandle, useRef } from 'react';
|
|
@@ -43,6 +48,7 @@ const Modal = ({
|
|
|
43
48
|
secondaryButtonProps,
|
|
44
49
|
closeButtonProps,
|
|
45
50
|
inNavModal = false,
|
|
51
|
+
stickyFooter = true,
|
|
46
52
|
...props
|
|
47
53
|
}: ModalProps) => {
|
|
48
54
|
const bottomSheetModalRef = useRef<BottomSheetModal>(null);
|
|
@@ -155,7 +161,37 @@ const Modal = ({
|
|
|
155
161
|
}
|
|
156
162
|
};
|
|
157
163
|
|
|
158
|
-
|
|
164
|
+
const noButtons = !onPressPrimaryButton && !onPressSecondaryButton;
|
|
165
|
+
|
|
166
|
+
styles.useVariants({
|
|
167
|
+
loading,
|
|
168
|
+
bothButtons: !!(onPressPrimaryButton && onPressSecondaryButton),
|
|
169
|
+
noButtons,
|
|
170
|
+
stickyFooter,
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
const footer = (
|
|
174
|
+
<View style={styles.footer}>
|
|
175
|
+
{onPressPrimaryButton && primaryButtonText ? (
|
|
176
|
+
<Button
|
|
177
|
+
onPress={handlePrimaryButtonPress}
|
|
178
|
+
text={primaryButtonText}
|
|
179
|
+
{...primaryButtonProps}
|
|
180
|
+
variant={(primaryButtonProps?.variant as 'solid') ?? 'solid'}
|
|
181
|
+
colorScheme={(primaryButtonProps?.colorScheme as 'highlight') ?? 'highlight'}
|
|
182
|
+
/>
|
|
183
|
+
) : null}
|
|
184
|
+
{onPressSecondaryButton && secondaryButtonText ? (
|
|
185
|
+
<Button
|
|
186
|
+
onPress={handleSecondaryButtonPress}
|
|
187
|
+
text={secondaryButtonText}
|
|
188
|
+
{...secondaryButtonProps}
|
|
189
|
+
variant={(secondaryButtonProps?.variant as 'outline') ?? 'outline'}
|
|
190
|
+
colorScheme={(secondaryButtonProps?.colorScheme as 'functional') ?? 'functional'}
|
|
191
|
+
/>
|
|
192
|
+
) : null}
|
|
193
|
+
</View>
|
|
194
|
+
);
|
|
159
195
|
|
|
160
196
|
const content = (
|
|
161
197
|
<>
|
|
@@ -216,31 +252,28 @@ const Modal = ({
|
|
|
216
252
|
</View>
|
|
217
253
|
) : null}
|
|
218
254
|
{children}
|
|
219
|
-
|
|
220
|
-
{onPressPrimaryButton && primaryButtonText ? (
|
|
221
|
-
<Button
|
|
222
|
-
onPress={handlePrimaryButtonPress}
|
|
223
|
-
text={primaryButtonText}
|
|
224
|
-
{...primaryButtonProps}
|
|
225
|
-
variant={(primaryButtonProps?.variant as 'solid') ?? 'solid'}
|
|
226
|
-
colorScheme={(primaryButtonProps?.colorScheme as 'highlight') ?? 'highlight'}
|
|
227
|
-
/>
|
|
228
|
-
) : null}
|
|
229
|
-
{onPressSecondaryButton && secondaryButtonText ? (
|
|
230
|
-
<Button
|
|
231
|
-
onPress={handleSecondaryButtonPress}
|
|
232
|
-
text={secondaryButtonText}
|
|
233
|
-
{...secondaryButtonProps}
|
|
234
|
-
variant={(secondaryButtonProps?.variant as 'outline') ?? 'outline'}
|
|
235
|
-
colorScheme={(secondaryButtonProps?.colorScheme as 'functional') ?? 'functional'}
|
|
236
|
-
/>
|
|
237
|
-
) : null}
|
|
238
|
-
</View>
|
|
255
|
+
{!stickyFooter && !noButtons ? footer : null}
|
|
239
256
|
</View>
|
|
240
257
|
)}
|
|
241
258
|
</>
|
|
242
259
|
);
|
|
243
260
|
|
|
261
|
+
const renderFooter = useCallback(
|
|
262
|
+
(props: BottomSheetFooterProps) => (
|
|
263
|
+
<BottomSheetFooter {...props}>
|
|
264
|
+
<View style={styles.footerWrap}>{footer}</View>
|
|
265
|
+
</BottomSheetFooter>
|
|
266
|
+
),
|
|
267
|
+
[
|
|
268
|
+
onPressPrimaryButton,
|
|
269
|
+
primaryButtonText,
|
|
270
|
+
onPressSecondaryButton,
|
|
271
|
+
secondaryButtonText,
|
|
272
|
+
primaryButtonProps,
|
|
273
|
+
secondaryButtonProps,
|
|
274
|
+
]
|
|
275
|
+
);
|
|
276
|
+
|
|
244
277
|
return inNavModal ? (
|
|
245
278
|
<View style={{ flex: 1, backgroundColor: theme.color.background.primary }}>
|
|
246
279
|
{Platform.OS === 'android' ? (
|
|
@@ -262,11 +295,12 @@ const Modal = ({
|
|
|
262
295
|
showHandle={typeof loading !== 'undefined' && loading ? false : props.showHandle}
|
|
263
296
|
accessible={false}
|
|
264
297
|
style={styles.modal}
|
|
298
|
+
footerComponent={stickyFooter && !noButtons ? renderFooter : undefined}
|
|
265
299
|
{...props}
|
|
266
300
|
onChange={handleChange}
|
|
267
301
|
>
|
|
268
302
|
{loading ? <View style={styles.loadingTop} /> : null}
|
|
269
|
-
<BottomSheetScrollView contentContainerStyle={styles.
|
|
303
|
+
<BottomSheetScrollView contentContainerStyle={styles.scrollView} ref={scrollViewRef}>
|
|
270
304
|
{content}
|
|
271
305
|
</BottomSheetScrollView>
|
|
272
306
|
</BottomSheetModal>
|
|
@@ -288,6 +322,30 @@ const styles = StyleSheet.create((theme, rt) => ({
|
|
|
288
322
|
},
|
|
289
323
|
},
|
|
290
324
|
},
|
|
325
|
+
scrollView: {
|
|
326
|
+
flex: 1,
|
|
327
|
+
variants: {
|
|
328
|
+
bothButtons: {
|
|
329
|
+
true: {
|
|
330
|
+
paddingBottom: 166 + rt.insets.bottom - theme.components.modal.padding,
|
|
331
|
+
},
|
|
332
|
+
false: {
|
|
333
|
+
paddingBottom: 102 + rt.insets.bottom - theme.components.modal.padding,
|
|
334
|
+
},
|
|
335
|
+
},
|
|
336
|
+
noButtons: {
|
|
337
|
+
true: {
|
|
338
|
+
paddingBottom: rt.insets.bottom + theme.components.modal.padding,
|
|
339
|
+
},
|
|
340
|
+
},
|
|
341
|
+
stickyFooter: {
|
|
342
|
+
true: {},
|
|
343
|
+
false: {
|
|
344
|
+
paddingBottom: rt.insets.bottom + theme.components.modal.padding,
|
|
345
|
+
},
|
|
346
|
+
},
|
|
347
|
+
},
|
|
348
|
+
},
|
|
291
349
|
header: {
|
|
292
350
|
flexDirection: 'row',
|
|
293
351
|
gap: theme.components.modal.gap,
|
|
@@ -321,6 +379,11 @@ const styles = StyleSheet.create((theme, rt) => ({
|
|
|
321
379
|
footer: {
|
|
322
380
|
gap: theme.components.modal.action.gap,
|
|
323
381
|
},
|
|
382
|
+
footerWrap: {
|
|
383
|
+
backgroundColor: theme.color.surface.neutral.strong,
|
|
384
|
+
paddingHorizontal: theme.components.bottomSheet.padding,
|
|
385
|
+
paddingBottom: theme.components.bottomSheet.padding + rt.insets.bottom,
|
|
386
|
+
},
|
|
324
387
|
inNavModalContainer: {
|
|
325
388
|
flex: 1,
|
|
326
389
|
...(Platform.OS === 'ios' ? { backgroundColor: theme.components.overlay.backgroundColor } : {}),
|