@jobber/components-native 0.89.4 → 0.89.5-JOB-140604-4c8f8f2.41
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/dist/package.json +4 -2
- package/dist/src/BottomSheet/BottomSheet.js +55 -35
- package/dist/src/BottomSheet/BottomSheet.style.js +10 -8
- package/dist/src/BottomSheet/components/BottomSheetInputText/BottomSheetInputText.js +45 -0
- package/dist/src/BottomSheet/components/BottomSheetInputText/BottomSheetInputText.styles.js +8 -0
- package/dist/src/ButtonGroup/ButtonGroup.js +1 -1
- package/dist/src/InputText/InputText.js +2 -2
- package/dist/src/utils/meta/meta.json +1 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/types/src/BottomSheet/BottomSheet.d.ts +13 -4
- package/dist/types/src/BottomSheet/BottomSheet.style.d.ts +9 -7
- package/dist/types/src/BottomSheet/components/BottomSheetInputText/BottomSheetInputText.d.ts +9 -0
- package/dist/types/src/BottomSheet/components/BottomSheetInputText/BottomSheetInputText.styles.d.ts +5 -0
- package/dist/types/src/InputText/InputText.d.ts +1 -1
- package/package.json +4 -2
- package/src/BottomSheet/BottomSheet.stories.tsx +129 -0
- package/src/BottomSheet/BottomSheet.style.ts +10 -11
- package/src/BottomSheet/BottomSheet.test.tsx +19 -24
- package/src/BottomSheet/BottomSheet.tsx +125 -103
- package/src/BottomSheet/components/BottomSheetInputText/BottomSheetInputText.styles.ts +9 -0
- package/src/BottomSheet/components/BottomSheetInputText/BottomSheetInputText.tsx +89 -0
- package/src/ButtonGroup/ButtonGroup.tsx +1 -1
- package/src/InputText/InputText.tsx +3 -3
- package/src/ThumbnailList/__snapshots__/ThumbnailList.test.tsx.snap +211 -1
- package/src/utils/meta/meta.json +1 -0
package/dist/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jobber/components-native",
|
|
3
|
-
"version": "0.89.
|
|
3
|
+
"version": "0.89.5-JOB-140604-4c8f8f2.41+4c8f8f2c3",
|
|
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.6",
|
|
56
57
|
"@react-native-community/datetimepicker": "^8.4.5",
|
|
57
58
|
"@react-native/babel-preset": "^0.81.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.6",
|
|
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": "
|
|
99
|
+
"gitHead": "4c8f8f2c37844355a5d654999ce62550aeeba2d8"
|
|
98
100
|
}
|
|
@@ -1,52 +1,72 @@
|
|
|
1
|
-
import React, {
|
|
2
|
-
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
1
|
+
import React, { useCallback, useImperativeHandle, useRef } from "react";
|
|
3
2
|
import { Keyboard, View } from "react-native";
|
|
4
|
-
import {
|
|
3
|
+
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
4
|
+
import RNBottomSheet, { BottomSheetBackdrop, BottomSheetFooter, BottomSheetView, } from "@gorhom/bottom-sheet";
|
|
5
5
|
import { useStyles } from "./BottomSheet.style";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
6
|
+
import { BottomSheetOption } from "./components/BottomSheetOption";
|
|
7
|
+
import { BottomSheetInputText } from "./components/BottomSheetInputText/BottomSheetInputText";
|
|
8
8
|
import { Divider } from "../Divider";
|
|
9
9
|
import { Heading } from "../Heading";
|
|
10
|
+
import { useIsScreenReaderEnabled } from "../hooks";
|
|
10
11
|
import { useAtlantisI18n } from "../hooks/useAtlantisI18n";
|
|
11
|
-
export
|
|
12
|
-
function BottomSheetInternal({ children, showCancel, loading = false, heading, onOpen, onClose, }, ref) {
|
|
13
|
-
const isScreenReaderEnabled = useIsScreenReaderEnabled();
|
|
14
|
-
const [open, setOpen] = useState(false);
|
|
12
|
+
export function BottomSheet({ children, showCancel, loading = false, heading, onOpen, onClose, ref, }) {
|
|
15
13
|
const styles = useStyles();
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
14
|
+
const isScreenReaderEnabled = useIsScreenReaderEnabled();
|
|
15
|
+
const cancellable = (showCancel && !loading) || isScreenReaderEnabled;
|
|
16
|
+
const { t } = useAtlantisI18n();
|
|
17
|
+
const insets = useSafeAreaInsets();
|
|
18
|
+
const previousIndexRef = useRef(-1);
|
|
19
|
+
const bottomSheetRef = useRef(null);
|
|
20
|
+
useImperativeHandle(ref, () => ({
|
|
21
|
+
open: () => {
|
|
22
|
+
var _a;
|
|
23
|
+
(_a = bottomSheetRef.current) === null || _a === void 0 ? void 0 : _a.expand();
|
|
24
|
+
},
|
|
25
|
+
close: () => {
|
|
26
|
+
var _a;
|
|
27
|
+
(_a = bottomSheetRef.current) === null || _a === void 0 ? void 0 : _a.close();
|
|
28
|
+
},
|
|
29
|
+
}));
|
|
30
|
+
const handleChange = (index) => {
|
|
31
|
+
const previousIndex = previousIndexRef.current;
|
|
32
|
+
if (previousIndex === -1 && index >= 0) {
|
|
33
|
+
// Transitioned from closed to open
|
|
34
|
+
dismissKeyboard();
|
|
35
|
+
onOpen === null || onOpen === void 0 ? void 0 : onOpen();
|
|
36
|
+
}
|
|
37
|
+
else if (previousIndex >= 0 && index === -1) {
|
|
38
|
+
// Transitioned from open to closed
|
|
39
|
+
dismissKeyboard();
|
|
40
|
+
onClose === null || onClose === void 0 ? void 0 : onClose();
|
|
41
|
+
}
|
|
42
|
+
previousIndexRef.current = index;
|
|
43
|
+
};
|
|
44
|
+
const renderFooter = useCallback((bottomSheetFooterProps) => {
|
|
45
|
+
return (React.createElement(BottomSheetFooter, Object.assign({}, bottomSheetFooterProps),
|
|
46
|
+
React.createElement(View, { style: [styles.footerContainer, { paddingBottom: insets.bottom }] }, cancellable && (React.createElement(View, { style: styles.footer },
|
|
47
|
+
React.createElement(View, { style: styles.footerDivider },
|
|
48
|
+
React.createElement(Divider, null)),
|
|
49
|
+
React.createElement(BottomSheetOption, { text: t("cancel"), icon: "remove", onPress: () => {
|
|
50
|
+
var _a;
|
|
51
|
+
(_a = bottomSheetRef.current) === null || _a === void 0 ? void 0 : _a.close();
|
|
52
|
+
} }))))));
|
|
53
|
+
}, [cancellable]);
|
|
54
|
+
return (React.createElement(RNBottomSheet, { ref: bottomSheetRef, index: -1, backdropComponent: Backdrop, backgroundStyle: styles.background, footerComponent: renderFooter, enablePanDownToClose: true, onChange: handleChange, keyboardBlurBehavior: "restore" },
|
|
55
|
+
React.createElement(BottomSheetView, { style: styles.content, enableFooterMarginAdjustment: true },
|
|
56
|
+
heading && React.createElement(Header, { heading: heading, styles: styles }),
|
|
57
|
+
children)));
|
|
32
58
|
}
|
|
33
59
|
function Header({ heading, styles, }) {
|
|
34
60
|
return (React.createElement(View, { style: styles.header },
|
|
35
61
|
React.createElement(Heading, { level: "subtitle" }, heading)));
|
|
36
62
|
}
|
|
37
|
-
function Footer({ cancellable, onCancel, styles, }) {
|
|
38
|
-
const insets = useSafeAreaInsets();
|
|
39
|
-
const { t } = useAtlantisI18n();
|
|
40
|
-
return (React.createElement(View, { style: { marginBottom: insets.bottom } }, cancellable && (React.createElement(View, { style: styles.children },
|
|
41
|
-
React.createElement(View, { style: styles.footerDivider },
|
|
42
|
-
React.createElement(Divider, null)),
|
|
43
|
-
React.createElement(BottomSheetOption, { text: t("cancel"), icon: "remove", onPress: onCancel })))));
|
|
44
|
-
}
|
|
45
63
|
function dismissKeyboard() {
|
|
46
64
|
//Dismisses the keyboard before opening the bottom sheet.
|
|
47
65
|
//In the case where an input text field is focused we don't want to show the bottom sheet behind or above keyboard
|
|
48
66
|
Keyboard.dismiss();
|
|
49
67
|
}
|
|
50
|
-
function
|
|
51
|
-
|
|
68
|
+
function Backdrop(bottomSheetBackdropProps) {
|
|
69
|
+
const styles = useStyles();
|
|
70
|
+
return (React.createElement(BottomSheetBackdrop, Object.assign({}, bottomSheetBackdropProps, { appearsOnIndex: 0, disappearsOnIndex: -1, style: styles.backdrop, opacity: 1 })));
|
|
52
71
|
}
|
|
72
|
+
BottomSheet.InputText = BottomSheetInputText;
|
|
@@ -1,21 +1,23 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { StyleSheet } from "react-native";
|
|
2
2
|
import { buildThemedStyles } from "../AtlantisThemeContext";
|
|
3
|
-
const { height } = Dimensions.get("window");
|
|
4
3
|
export const useStyles = buildThemedStyles(tokens => {
|
|
5
4
|
const modalBorderRadius = tokens["radius-larger"];
|
|
6
5
|
return {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
},
|
|
10
|
-
overlay: Object.assign(Object.assign({}, StyleSheet.absoluteFillObject), { backgroundColor: tokens["color-overlay"], height }),
|
|
11
|
-
modal: {
|
|
6
|
+
backdrop: Object.assign(Object.assign({}, StyleSheet.absoluteFillObject), { backgroundColor: tokens["color-overlay"] }),
|
|
7
|
+
background: {
|
|
12
8
|
borderTopLeftRadius: modalBorderRadius,
|
|
13
9
|
borderTopRightRadius: modalBorderRadius,
|
|
14
10
|
paddingTop: tokens["space-small"],
|
|
15
11
|
},
|
|
16
|
-
|
|
12
|
+
content: {
|
|
13
|
+
paddingBottom: tokens["space-small"],
|
|
14
|
+
},
|
|
15
|
+
footer: {
|
|
17
16
|
paddingBottom: tokens["space-small"],
|
|
18
17
|
},
|
|
18
|
+
footerContainer: {
|
|
19
|
+
backgroundColor: tokens["color-surface"],
|
|
20
|
+
},
|
|
19
21
|
header: {
|
|
20
22
|
paddingHorizontal: tokens["space-base"],
|
|
21
23
|
paddingTop: tokens["space-small"],
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import React, { forwardRef, useCallback } from "react";
|
|
2
|
+
import { TextInput, View, findNodeHandle } from "react-native";
|
|
3
|
+
import { useBottomSheetInternal } from "@gorhom/bottom-sheet";
|
|
4
|
+
import { useStyles } from "./BottomSheetInputText.styles";
|
|
5
|
+
import { InputText } from "../../../InputText/InputText";
|
|
6
|
+
/**
|
|
7
|
+
* BottomSheetInputText is a wrapper around InputText that provides
|
|
8
|
+
* bottom sheet keyboard handling. It implements the handleOnFocus and
|
|
9
|
+
* handleOnBlur logic from BottomSheetTextInput to ensure proper keyboard
|
|
10
|
+
* positioning within bottom sheets.
|
|
11
|
+
*/
|
|
12
|
+
export const BottomSheetInputText = forwardRef(function BottomSheetInputText(props, ref) {
|
|
13
|
+
const styles = useStyles();
|
|
14
|
+
const { onFocus, onBlur } = props;
|
|
15
|
+
const { animatedKeyboardState, textInputNodesRef } = useBottomSheetInternal();
|
|
16
|
+
const handleOnFocus = useCallback((event) => {
|
|
17
|
+
animatedKeyboardState.set((state) => (Object.assign(Object.assign({}, state), { target: event === null || event === void 0 ? void 0 : event.nativeEvent.target })));
|
|
18
|
+
onFocus === null || onFocus === void 0 ? void 0 : onFocus(event);
|
|
19
|
+
}, [animatedKeyboardState, onFocus]);
|
|
20
|
+
const handleOnBlur = useCallback((event) => {
|
|
21
|
+
const keyboardState = animatedKeyboardState.get();
|
|
22
|
+
const currentlyFocusedInput = TextInput.State.currentlyFocusedInput();
|
|
23
|
+
const currentFocusedInput = currentlyFocusedInput !== null
|
|
24
|
+
? findNodeHandle(
|
|
25
|
+
// @ts-expect-error - TextInput.State.currentlyFocusedInput() returns NativeMethods
|
|
26
|
+
// which is not directly assignable to findNodeHandle's expected type,
|
|
27
|
+
// but it works at runtime. This is a known type limitation in React Native.
|
|
28
|
+
currentlyFocusedInput)
|
|
29
|
+
: null;
|
|
30
|
+
/**
|
|
31
|
+
* we need to make sure that we only remove the target
|
|
32
|
+
* if the target belong to the current component and
|
|
33
|
+
* if the currently focused input is not in the targets set.
|
|
34
|
+
*/
|
|
35
|
+
const shouldRemoveCurrentTarget = keyboardState.target === (event === null || event === void 0 ? void 0 : event.nativeEvent.target);
|
|
36
|
+
const shouldIgnoreBlurEvent = currentFocusedInput &&
|
|
37
|
+
textInputNodesRef.current.has(currentFocusedInput);
|
|
38
|
+
if (shouldRemoveCurrentTarget && !shouldIgnoreBlurEvent) {
|
|
39
|
+
animatedKeyboardState.set((state) => (Object.assign(Object.assign({}, state), { target: undefined })));
|
|
40
|
+
}
|
|
41
|
+
onBlur === null || onBlur === void 0 ? void 0 : onBlur(event);
|
|
42
|
+
}, [animatedKeyboardState, textInputNodesRef, onBlur]);
|
|
43
|
+
return (React.createElement(View, { style: styles.inputText },
|
|
44
|
+
React.createElement(InputText, Object.assign({}, props, { ref: ref, onFocus: handleOnFocus, onBlur: handleOnBlur }))));
|
|
45
|
+
});
|
|
@@ -9,7 +9,7 @@ import { useAtlantisI18n } from "../hooks/useAtlantisI18n";
|
|
|
9
9
|
export function ButtonGroup({ children, showCancelInBottomSheet, bottomSheetHeading, onOpenBottomSheet, onCloseBottomSheet, allowTapWhenOffline = false, }) {
|
|
10
10
|
const { t } = useAtlantisI18n();
|
|
11
11
|
const { handlePress } = usePreventTapWhenOffline();
|
|
12
|
-
const secondaryActionsRef = useRef();
|
|
12
|
+
const secondaryActionsRef = useRef(null);
|
|
13
13
|
const { primaryActions, secondaryActions } = getActions(children);
|
|
14
14
|
const styles = useStyles();
|
|
15
15
|
return (React.createElement(View, { style: styles.buttonGroup },
|
|
@@ -89,10 +89,10 @@ function InputTextInternal({ invalid, disabled, readonly = false, name, placehol
|
|
|
89
89
|
_name && setFocusedInput(_name);
|
|
90
90
|
setFocused(true);
|
|
91
91
|
onFocus === null || onFocus === void 0 ? void 0 : onFocus(event);
|
|
92
|
-
}, onBlur:
|
|
92
|
+
}, onBlur: event => {
|
|
93
93
|
_name && setFocusedInput("");
|
|
94
94
|
setFocused(false);
|
|
95
|
-
onBlur === null || onBlur === void 0 ? void 0 : onBlur();
|
|
95
|
+
onBlur === null || onBlur === void 0 ? void 0 : onBlur(event);
|
|
96
96
|
field.onBlur();
|
|
97
97
|
trimWhitespace(inputTransform(field.value), updateFormAndState);
|
|
98
98
|
}, ref: (instance) => {
|