@janiscommerce/ui-native 1.16.2 → 1.18.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.
- package/dist/components/atoms/Icon/index.d.ts +1 -1
- package/dist/components/atoms/Image/index.d.ts +7 -4
- package/dist/components/atoms/Image/index.js +27 -10
- package/dist/components/atoms/Modal/index.d.ts +15 -0
- package/dist/components/atoms/Modal/index.js +84 -0
- package/dist/components/organisms/ErrorBoundary/components/ErrorFallback/index.d.ts +9 -0
- package/dist/components/organisms/ErrorBoundary/components/ErrorFallback/index.js +81 -0
- package/dist/components/organisms/ErrorBoundary/index.d.ts +22 -0
- package/dist/components/organisms/ErrorBoundary/index.js +37 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3 -1
- package/package.json +3 -1
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
import { Props as NativeIconProps } from '../Icon';
|
|
3
|
+
import { ViewStyle } from 'react-native';
|
|
4
|
+
import { FastImageProps } from 'react-native-fast-image';
|
|
5
|
+
interface ImageProps extends FastImageProps {
|
|
6
|
+
iconProps?: NativeIconProps;
|
|
7
|
+
iconBackgroundStyle?: ViewStyle;
|
|
5
8
|
}
|
|
6
|
-
declare const Image: ({ source, ...props }: ImageProps) => React.JSX.Element
|
|
9
|
+
declare const Image: ({ source, iconProps, iconBackgroundStyle, onError, ...props }: ImageProps) => React.JSX.Element;
|
|
7
10
|
export default Image;
|
|
@@ -1,13 +1,30 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import Icon from '../Icon';
|
|
3
|
+
import { palette } from '../../../theme/palette';
|
|
4
|
+
import { View, StyleSheet } from 'react-native';
|
|
5
|
+
import FastImage from 'react-native-fast-image';
|
|
6
|
+
const Image = ({ source, iconProps, iconBackgroundStyle = {}, onError, ...props }) => {
|
|
7
|
+
const [showPlaceholderImage, setShowPlaceholderImage] = useState(false);
|
|
8
|
+
const styles = StyleSheet.create({
|
|
9
|
+
iconBackground: {
|
|
10
|
+
backgroundColor: palette.white.light,
|
|
11
|
+
display: 'flex',
|
|
12
|
+
alignItems: 'center',
|
|
13
|
+
justifyContent: 'center',
|
|
14
|
+
},
|
|
15
|
+
});
|
|
16
|
+
const placeholderStyle = [styles.iconBackground, iconBackgroundStyle, props.style].filter(Boolean);
|
|
17
|
+
const handleError = () => {
|
|
18
|
+
if (onError) {
|
|
19
|
+
onError();
|
|
20
|
+
}
|
|
21
|
+
setShowPlaceholderImage(true);
|
|
22
|
+
};
|
|
23
|
+
if (showPlaceholderImage || !source) {
|
|
24
|
+
return (<View style={placeholderStyle}>
|
|
25
|
+
<Icon name="exclamation_circle" color={palette.white.dark} size={36} {...iconProps}/>
|
|
26
|
+
</View>);
|
|
6
27
|
}
|
|
7
|
-
|
|
8
|
-
if (!sourceKeys.includes('uri')) {
|
|
9
|
-
return null;
|
|
10
|
-
}
|
|
11
|
-
return <ImageComp source={source} {...props}/>;
|
|
28
|
+
return <FastImage onError={handleError} source={source} {...props}/>;
|
|
12
29
|
};
|
|
13
30
|
export default Image;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { ModalProps as NativeModalProps, ViewStyle } from 'react-native';
|
|
3
|
+
export interface UIModalProps extends NativeModalProps {
|
|
4
|
+
oncloseCallback?: () => void;
|
|
5
|
+
showCloseButton?: boolean;
|
|
6
|
+
fullScreen?: boolean;
|
|
7
|
+
modalContainerStyle?: ViewStyle;
|
|
8
|
+
canClose?: boolean;
|
|
9
|
+
}
|
|
10
|
+
interface RefProps {
|
|
11
|
+
open?: () => void;
|
|
12
|
+
close?: () => void;
|
|
13
|
+
}
|
|
14
|
+
declare const Modal: React.ForwardRefExoticComponent<UIModalProps & React.RefAttributes<RefProps>>;
|
|
15
|
+
export default Modal;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import Icon from '../Icon';
|
|
2
|
+
import React, { useState, forwardRef, useImperativeHandle } from 'react';
|
|
3
|
+
import { Modal as NativeModal, Pressable, StyleSheet, View, } from 'react-native';
|
|
4
|
+
import { horizontalScale } from '../../../scale';
|
|
5
|
+
import { verticalScale, moderateScale } from '../../../scale';
|
|
6
|
+
import { palette } from '../../../theme/palette';
|
|
7
|
+
const styles = StyleSheet.create({
|
|
8
|
+
Overlay: {
|
|
9
|
+
flex: 1,
|
|
10
|
+
backgroundColor: 'rgba(0,0,0,0.6)',
|
|
11
|
+
justifyContent: 'center',
|
|
12
|
+
alignItems: 'center',
|
|
13
|
+
},
|
|
14
|
+
ModalWrapper: {
|
|
15
|
+
backgroundColor: palette.base.white,
|
|
16
|
+
alignItems: 'center',
|
|
17
|
+
justifyContent: 'center',
|
|
18
|
+
elevation: 12,
|
|
19
|
+
minWidth: horizontalScale(50),
|
|
20
|
+
width: horizontalScale(280),
|
|
21
|
+
marginHorizontal: horizontalScale(20),
|
|
22
|
+
borderRadius: verticalScale(18),
|
|
23
|
+
zIndex: 1,
|
|
24
|
+
},
|
|
25
|
+
Shadow: {
|
|
26
|
+
shadowOffset: {
|
|
27
|
+
width: 0,
|
|
28
|
+
height: 2,
|
|
29
|
+
},
|
|
30
|
+
shadowOpacity: 0.25,
|
|
31
|
+
},
|
|
32
|
+
FullScreen: {
|
|
33
|
+
position: 'absolute',
|
|
34
|
+
top: 0,
|
|
35
|
+
bottom: 0,
|
|
36
|
+
left: 0,
|
|
37
|
+
right: 0,
|
|
38
|
+
backgroundColor: palette.base.white,
|
|
39
|
+
zIndex: 2,
|
|
40
|
+
},
|
|
41
|
+
HeaderWrapper: {
|
|
42
|
+
flexDirection: 'row',
|
|
43
|
+
justifyContent: 'flex-end',
|
|
44
|
+
alignItems: 'center',
|
|
45
|
+
zIndex: 3,
|
|
46
|
+
minHeight: verticalScale(52),
|
|
47
|
+
backgroundColor: palette.base.white,
|
|
48
|
+
},
|
|
49
|
+
CloseButton: {
|
|
50
|
+
position: 'absolute',
|
|
51
|
+
top: moderateScale(12),
|
|
52
|
+
right: moderateScale(12),
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
const Modal = forwardRef(({ children = null, oncloseCallback = undefined, showCloseButton = false, canClose = true, animationType = 'fade', transparent = true, fullScreen = false, modalContainerStyle = {}, ...props }, ref) => {
|
|
56
|
+
const [isVisible, setIsVisible] = useState(false);
|
|
57
|
+
const renderCloseButton = fullScreen && showCloseButton;
|
|
58
|
+
const handleClose = () => {
|
|
59
|
+
if (oncloseCallback) {
|
|
60
|
+
oncloseCallback();
|
|
61
|
+
}
|
|
62
|
+
setIsVisible(false);
|
|
63
|
+
};
|
|
64
|
+
useImperativeHandle(ref, () => ({
|
|
65
|
+
open: () => setIsVisible(true),
|
|
66
|
+
close: () => setIsVisible(false),
|
|
67
|
+
}));
|
|
68
|
+
return (<NativeModal visible={isVisible} transparent={transparent} animationType={animationType} {...props} {...(canClose && {
|
|
69
|
+
onRequestClose: handleClose,
|
|
70
|
+
})}>
|
|
71
|
+
<View style={styles.Overlay}>
|
|
72
|
+
{!fullScreen && (<Pressable style={StyleSheet.absoluteFill} disabled={!canClose} onPress={handleClose}/>)}
|
|
73
|
+
<View style={fullScreen
|
|
74
|
+
? styles.FullScreen
|
|
75
|
+
: [styles.ModalWrapper, styles.Shadow, modalContainerStyle]}>
|
|
76
|
+
{renderCloseButton && canClose && (<Pressable onPress={handleClose} style={styles.HeaderWrapper} accessibilityLabel="Close modal" accessibilityRole="button">
|
|
77
|
+
<Icon name="cross_light" size={24} color={palette.black.main} style={styles.CloseButton}/>
|
|
78
|
+
</Pressable>)}
|
|
79
|
+
{children}
|
|
80
|
+
</View>
|
|
81
|
+
</View>
|
|
82
|
+
</NativeModal>);
|
|
83
|
+
});
|
|
84
|
+
export default Modal;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import Collapsible from '../../../../atoms/Collapsible';
|
|
3
|
+
import Typography from '../../../../atoms/Typography';
|
|
4
|
+
import { StyleSheet, TouchableOpacity, View } from 'react-native';
|
|
5
|
+
import { palette } from '../../../../../theme/palette';
|
|
6
|
+
const styles = StyleSheet.create({
|
|
7
|
+
container: {
|
|
8
|
+
flex: 1,
|
|
9
|
+
justifyContent: 'center',
|
|
10
|
+
alignItems: 'center',
|
|
11
|
+
backgroundColor: palette.white.light,
|
|
12
|
+
padding: 30,
|
|
13
|
+
borderRadius: 12,
|
|
14
|
+
margin: 16,
|
|
15
|
+
shadowColor: palette.base.black,
|
|
16
|
+
shadowOffset: { width: 0, height: 2 },
|
|
17
|
+
shadowOpacity: 0.2,
|
|
18
|
+
shadowRadius: 4,
|
|
19
|
+
elevation: 5,
|
|
20
|
+
},
|
|
21
|
+
errorMessageText: {
|
|
22
|
+
textAlign: 'center',
|
|
23
|
+
color: palette.error.main,
|
|
24
|
+
},
|
|
25
|
+
showErrorButton: {
|
|
26
|
+
paddingVertical: 10,
|
|
27
|
+
paddingHorizontal: 20,
|
|
28
|
+
justifyContent: 'center',
|
|
29
|
+
alignItems: 'center',
|
|
30
|
+
backgroundColor: palette.error.main,
|
|
31
|
+
borderRadius: 6,
|
|
32
|
+
marginVertical: 12,
|
|
33
|
+
},
|
|
34
|
+
collapsibleWrapper: {
|
|
35
|
+
marginBottom: 35,
|
|
36
|
+
},
|
|
37
|
+
errorDetails: {
|
|
38
|
+
padding: 10,
|
|
39
|
+
backgroundColor: palette.black.main,
|
|
40
|
+
borderRadius: 8,
|
|
41
|
+
marginVertical: 10,
|
|
42
|
+
},
|
|
43
|
+
heading: {
|
|
44
|
+
marginBottom: 12,
|
|
45
|
+
},
|
|
46
|
+
goBackButtonWrapper: {
|
|
47
|
+
marginTop: 20,
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
const ShowErrorDetailsButton = () => {
|
|
51
|
+
return (<View style={styles.showErrorButton}>
|
|
52
|
+
<Typography color={palette.base.white}>Show error details</Typography>
|
|
53
|
+
</View>);
|
|
54
|
+
};
|
|
55
|
+
const ErrorDetails = ({ errorDetails }) => {
|
|
56
|
+
return (<View style={styles.errorDetails}>
|
|
57
|
+
<Typography color={palette.error.main}>{errorDetails}</Typography>
|
|
58
|
+
</View>);
|
|
59
|
+
};
|
|
60
|
+
const ErrorFallback = ({ error, errorDetails, isDebug, errorMessage, }) => {
|
|
61
|
+
const data = [
|
|
62
|
+
{
|
|
63
|
+
errorDetails,
|
|
64
|
+
},
|
|
65
|
+
];
|
|
66
|
+
if (errorMessage) {
|
|
67
|
+
return (<View style={styles.container}>
|
|
68
|
+
<Typography type="heading" size="large" style={styles.errorMessageText}>
|
|
69
|
+
{errorMessage}
|
|
70
|
+
</Typography>
|
|
71
|
+
</View>);
|
|
72
|
+
}
|
|
73
|
+
return (<View style={styles.container}>
|
|
74
|
+
<Typography type="heading" size="large" style={styles.heading}>
|
|
75
|
+
Oops! Something went wrong.
|
|
76
|
+
</Typography>
|
|
77
|
+
{error && <Typography color={palette.error.main}>{error}</Typography>}
|
|
78
|
+
{isDebug && (<Collapsible wrapperStyle={styles.collapsibleWrapper} header={ShowErrorDetailsButton} content={ErrorDetails} data={data} pressableComponent={TouchableOpacity}/>)}
|
|
79
|
+
</View>);
|
|
80
|
+
};
|
|
81
|
+
export default ErrorFallback;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import React, { ReactNode } from 'react';
|
|
2
|
+
interface ErrorBoundaryProps {
|
|
3
|
+
children: ReactNode;
|
|
4
|
+
isDebug?: boolean;
|
|
5
|
+
errorMessage?: string;
|
|
6
|
+
renderErrorComponent?: (errorMessage: string) => ReactNode;
|
|
7
|
+
onError?: (error: Error, errorInfo: React.ErrorInfo) => void;
|
|
8
|
+
onMount?: () => void;
|
|
9
|
+
}
|
|
10
|
+
interface ErrorBoundaryState {
|
|
11
|
+
hasError: boolean;
|
|
12
|
+
error: Error | null;
|
|
13
|
+
errorInfo: React.ErrorInfo | null;
|
|
14
|
+
}
|
|
15
|
+
declare class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundaryState> {
|
|
16
|
+
constructor(props: ErrorBoundaryProps);
|
|
17
|
+
static getDerivedStateFromError(error: Error): Partial<ErrorBoundaryState>;
|
|
18
|
+
componentDidMount(): void;
|
|
19
|
+
componentDidCatch(error: Error, errorInfo: React.ErrorInfo): void;
|
|
20
|
+
render(): React.ReactNode;
|
|
21
|
+
}
|
|
22
|
+
export default ErrorBoundary;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import ErrorFallback from '../ErrorBoundary/components/ErrorFallback';
|
|
3
|
+
class ErrorBoundary extends React.Component {
|
|
4
|
+
constructor(props) {
|
|
5
|
+
super(props);
|
|
6
|
+
this.state = {
|
|
7
|
+
hasError: false,
|
|
8
|
+
error: null,
|
|
9
|
+
errorInfo: null,
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
static getDerivedStateFromError(error) {
|
|
13
|
+
return { hasError: true, error };
|
|
14
|
+
}
|
|
15
|
+
componentDidMount() {
|
|
16
|
+
if (this.props.onMount) {
|
|
17
|
+
this.props.onMount();
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
componentDidCatch(error, errorInfo) {
|
|
21
|
+
this.setState({ errorInfo });
|
|
22
|
+
if (this.props.onError) {
|
|
23
|
+
this.props.onError(error, errorInfo);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
render() {
|
|
27
|
+
if (this.state.hasError) {
|
|
28
|
+
const { renderErrorComponent } = this.props;
|
|
29
|
+
if (typeof renderErrorComponent === 'function') {
|
|
30
|
+
return renderErrorComponent(this.state.error?.message || '');
|
|
31
|
+
}
|
|
32
|
+
return (<ErrorFallback isDebug={this.props.isDebug} errorMessage={this.props.errorMessage} error={this.state.error?.message} errorDetails={this.state.errorInfo?.componentStack}/>);
|
|
33
|
+
}
|
|
34
|
+
return this.props.children;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export default ErrorBoundary;
|
package/dist/index.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ import Text from './components/atoms/Text';
|
|
|
13
13
|
import BaseInput from './components/atoms/BaseInput';
|
|
14
14
|
import Typography from './components/atoms/Typography';
|
|
15
15
|
import Collapsible from './components/atoms/Collapsible';
|
|
16
|
+
import Modal from './components/atoms/Modal';
|
|
16
17
|
import Avatar from './components/molecules/Avatar';
|
|
17
18
|
import Button from './components/molecules/Button';
|
|
18
19
|
import Carousel from './components/molecules/Carousel';
|
|
@@ -28,6 +29,7 @@ import Input from './components/molecules/Input';
|
|
|
28
29
|
import LoadingFullScreen from './components/organisms/LoadingFullScreen';
|
|
29
30
|
import FullScreenMessage from './components/organisms/FullScreenMessage';
|
|
30
31
|
import SwipeItemSelectionList from './components/organisms/SwipeItemSelectionList';
|
|
32
|
+
import ErrorBoundary from './components/organisms/ErrorBoundary';
|
|
31
33
|
import { palette } from './theme/palette';
|
|
32
34
|
import * as getScale from './scale';
|
|
33
|
-
export { Text, Avatar, CheckBox, Icon, Image, Input, Loading, Svg, StatusChip, palette, LoadingFullScreen, RadioButton, Select, SwipeUp, SwipeUpFlatList, SwipeUpScrollView, SwipeUpView, Carousel, ProgressBar, List, BaseButton, Button, getScale, LayoutWithBottomButtons, FullScreenMessage, Toast, configToast, SwipeList, ItemSelectionButton, SwipeItemSelectionList, MainCardList, BaseInput, Typography, Collapsible, };
|
|
35
|
+
export { Text, Avatar, CheckBox, Icon, Image, Input, Loading, Svg, StatusChip, palette, LoadingFullScreen, RadioButton, Select, SwipeUp, SwipeUpFlatList, SwipeUpScrollView, SwipeUpView, Carousel, ProgressBar, List, BaseButton, Button, getScale, LayoutWithBottomButtons, FullScreenMessage, Toast, configToast, SwipeList, ItemSelectionButton, SwipeItemSelectionList, MainCardList, BaseInput, Typography, Collapsible, ErrorBoundary, Modal, };
|
package/dist/index.js
CHANGED
|
@@ -14,6 +14,7 @@ import Text from './components/atoms/Text';
|
|
|
14
14
|
import BaseInput from './components/atoms/BaseInput';
|
|
15
15
|
import Typography from './components/atoms/Typography';
|
|
16
16
|
import Collapsible from './components/atoms/Collapsible';
|
|
17
|
+
import Modal from './components/atoms/Modal';
|
|
17
18
|
// Molecules
|
|
18
19
|
import Avatar from './components/molecules/Avatar';
|
|
19
20
|
import Button from './components/molecules/Button';
|
|
@@ -31,7 +32,8 @@ import Input from './components/molecules/Input';
|
|
|
31
32
|
import LoadingFullScreen from './components/organisms/LoadingFullScreen';
|
|
32
33
|
import FullScreenMessage from './components/organisms/FullScreenMessage';
|
|
33
34
|
import SwipeItemSelectionList from './components/organisms/SwipeItemSelectionList';
|
|
35
|
+
import ErrorBoundary from './components/organisms/ErrorBoundary';
|
|
34
36
|
// Misc
|
|
35
37
|
import { palette } from './theme/palette';
|
|
36
38
|
import * as getScale from './scale';
|
|
37
|
-
export { Text, Avatar, CheckBox, Icon, Image, Input, Loading, Svg, StatusChip, palette, LoadingFullScreen, RadioButton, Select, SwipeUp, SwipeUpFlatList, SwipeUpScrollView, SwipeUpView, Carousel, ProgressBar, List, BaseButton, Button, getScale, LayoutWithBottomButtons, FullScreenMessage, Toast, configToast, SwipeList, ItemSelectionButton, SwipeItemSelectionList, MainCardList, BaseInput, Typography, Collapsible, };
|
|
39
|
+
export { Text, Avatar, CheckBox, Icon, Image, Input, Loading, Svg, StatusChip, palette, LoadingFullScreen, RadioButton, Select, SwipeUp, SwipeUpFlatList, SwipeUpScrollView, SwipeUpView, Carousel, ProgressBar, List, BaseButton, Button, getScale, LayoutWithBottomButtons, FullScreenMessage, Toast, configToast, SwipeList, ItemSelectionButton, SwipeItemSelectionList, MainCardList, BaseInput, Typography, Collapsible, ErrorBoundary, Modal, };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@janiscommerce/ui-native",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.18.0",
|
|
4
4
|
"description": "components library for Janis app",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"typings": "dist/index.d.ts",
|
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
"react": ">=17.0.2",
|
|
48
48
|
"react-native": ">=0.67.5",
|
|
49
49
|
"react-native-gesture-handler": ">=2.9.0",
|
|
50
|
+
"react-native-fast-image": "^8.5.11",
|
|
50
51
|
"react-native-reanimated": "2.17.0",
|
|
51
52
|
"react-native-toast-message": ">=1.6.0"
|
|
52
53
|
},
|
|
@@ -96,6 +97,7 @@
|
|
|
96
97
|
"react": "17.0.2",
|
|
97
98
|
"react-dom": "^17.0.2",
|
|
98
99
|
"react-native": "0.67.5",
|
|
100
|
+
"react-native-fast-image": "^8.5.11",
|
|
99
101
|
"react-native-svg-transformer": "^1.0.0",
|
|
100
102
|
"react-test-renderer": "17.0.2",
|
|
101
103
|
"setup-env": "^2.0.0",
|