@momo-kits/foundation 0.81.1-beta.6 → 0.81.1-optimize.10
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/Application/BottomSheet.tsx +225 -0
- package/Application/BottomTab/BottomTabBar.tsx +291 -0
- package/Application/BottomTab/index.tsx +138 -0
- package/Application/Components.tsx +274 -211
- package/Application/ModalScreen.tsx +10 -226
- package/Application/Navigation.ts +17 -6
- package/Application/NavigationContainer.tsx +66 -65
- package/Application/Navigator.ts +106 -53
- package/Application/StackScreen.tsx +123 -28
- package/Application/index.ts +15 -9
- package/Application/types.ts +55 -31
- package/Application/utils.tsx +36 -6
- package/Assets/icon.json +83 -5
- package/Assets/icon_bank.json +506 -0
- package/Assets/language.json +3 -1
- package/Badge/Badge.tsx +56 -0
- package/Badge/BadgeDot.tsx +11 -0
- package/Badge/BadgeRibbon.tsx +125 -0
- package/Badge/Shape.tsx +29 -0
- package/Badge/index.tsx +7 -0
- package/Badge/styles.ts +58 -0
- package/Badge/types.ts +63 -0
- package/Button/index.tsx +60 -24
- package/Button/styles.ts +19 -0
- package/CheckBox/index.tsx +37 -27
- package/CheckBox/types.ts +5 -3
- package/Consts/colors+spacing+radius.ts +17 -3
- package/Icon/index.tsx +14 -9
- package/Icon/types.ts +4 -3
- package/IconButton/index.tsx +24 -15
- package/Image/index.tsx +61 -55
- package/Image/styles.ts +1 -9
- package/Image/types.ts +5 -1
- package/Input/Input.tsx +102 -25
- package/Input/InputDropDown.tsx +32 -23
- package/Input/InputMoney.tsx +67 -18
- package/Input/InputOTP.tsx +194 -148
- package/Input/InputSearch.tsx +72 -36
- package/Input/InputTextArea.tsx +33 -18
- package/Input/common.tsx +25 -16
- package/Input/index.tsx +62 -16
- package/Input/styles.ts +5 -8
- package/Layout/Card.tsx +20 -11
- package/Layout/FloatingButton.tsx +168 -0
- package/Layout/GridSystem.tsx +1 -2
- package/Layout/Item.tsx +0 -1
- package/Layout/ItemList.tsx +39 -40
- package/Layout/ItemSectionList.tsx +39 -0
- package/Layout/Screen.tsx +151 -65
- package/Layout/Section.tsx +17 -19
- package/Layout/index.ts +2 -1
- package/Layout/styles.ts +20 -15
- package/Layout/types.ts +2 -5
- package/Layout/utils.ts +2 -2
- package/Pagination/PaginationNumber.tsx +1 -1
- package/Pagination/PaginationScroll.tsx +0 -1
- package/Popup/PopupNotify.tsx +105 -37
- package/Popup/PopupPromotion.tsx +44 -9
- package/Popup/types.ts +12 -9
- package/Radio/index.tsx +35 -21
- package/Radio/types.ts +5 -3
- package/Skeleton/index.tsx +1 -1
- package/Switch/index.tsx +32 -19
- package/Switch/types.ts +5 -3
- package/Tag/index.tsx +2 -4
- package/Text/index.tsx +26 -6
- package/Text/styles.ts +38 -0
- package/Text/types.ts +25 -13
- package/Title/index.tsx +172 -63
- package/Title/styles.ts +48 -15
- package/Title/types.ts +16 -47
- package/code-scanner.js +131 -39
- package/index.ts +9 -0
- package/migrate_typo.js +56 -0
- package/package.json +8 -8
- package/verify.js +24 -6
- package/Application/BottomTab.tsx +0 -65
package/Input/InputTextArea.tsx
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, {
|
|
2
|
+
forwardRef,
|
|
3
|
+
useContext,
|
|
4
|
+
useImperativeHandle,
|
|
5
|
+
useRef,
|
|
6
|
+
useState,
|
|
7
|
+
} from 'react';
|
|
2
8
|
import {
|
|
3
9
|
NativeSyntheticEvent,
|
|
4
10
|
TextInput,
|
|
@@ -6,7 +12,8 @@ import {
|
|
|
6
12
|
TouchableOpacity,
|
|
7
13
|
View,
|
|
8
14
|
} from 'react-native';
|
|
9
|
-
import
|
|
15
|
+
import {ApplicationContext} from '../Application';
|
|
16
|
+
import {Icon} from '../Icon';
|
|
10
17
|
import {Text} from '../Text';
|
|
11
18
|
import {
|
|
12
19
|
DEFAULT_HEIGHT,
|
|
@@ -16,8 +23,7 @@ import {
|
|
|
16
23
|
MAX_LENGTH,
|
|
17
24
|
} from './common';
|
|
18
25
|
import {InputTextAreaProps} from './index';
|
|
19
|
-
import
|
|
20
|
-
import {ApplicationContext} from '../Application';
|
|
26
|
+
import styles from './styles';
|
|
21
27
|
|
|
22
28
|
const InputTextArea = forwardRef(
|
|
23
29
|
(
|
|
@@ -29,15 +35,17 @@ const InputTextArea = forwardRef(
|
|
|
29
35
|
floatingValue,
|
|
30
36
|
onFocus,
|
|
31
37
|
onBlur,
|
|
32
|
-
disabled,
|
|
38
|
+
disabled = false,
|
|
33
39
|
value,
|
|
34
40
|
defaultValue,
|
|
35
41
|
height,
|
|
36
42
|
placeholder,
|
|
37
|
-
maxLength,
|
|
43
|
+
maxLength = MAX_LENGTH,
|
|
38
44
|
required,
|
|
39
45
|
errorSpacing,
|
|
40
46
|
style,
|
|
47
|
+
accessibilityLabel,
|
|
48
|
+
hintText,
|
|
41
49
|
...props
|
|
42
50
|
}: InputTextAreaProps,
|
|
43
51
|
ref,
|
|
@@ -45,8 +53,7 @@ const InputTextArea = forwardRef(
|
|
|
45
53
|
const {theme} = useContext(ApplicationContext);
|
|
46
54
|
const [focused, setFocused] = useState(false);
|
|
47
55
|
const [inputValue, setInputValue] = useState(defaultValue ?? '');
|
|
48
|
-
const inputRef =
|
|
49
|
-
const accessibilityLabel = `InputTextArea/${floatingValue ?? placeholder}`;
|
|
56
|
+
const inputRef = useRef<TextInput | null>(null);
|
|
50
57
|
|
|
51
58
|
const onClearText = () => {
|
|
52
59
|
inputRef?.current?.clear();
|
|
@@ -68,10 +75,21 @@ const InputTextArea = forwardRef(
|
|
|
68
75
|
onBlur?.(e);
|
|
69
76
|
};
|
|
70
77
|
|
|
78
|
+
useImperativeHandle(ref, () => {
|
|
79
|
+
return {
|
|
80
|
+
clear: onClearText,
|
|
81
|
+
focus: () => inputRef.current?.focus(),
|
|
82
|
+
blur: () => inputRef.current?.blur(),
|
|
83
|
+
setText: (text: string) => _onChangeText(text),
|
|
84
|
+
};
|
|
85
|
+
});
|
|
86
|
+
|
|
71
87
|
const renderCountingView = () => {
|
|
72
88
|
return (
|
|
73
89
|
<View style={styles.countingView}>
|
|
74
|
-
<Text
|
|
90
|
+
<Text
|
|
91
|
+
color={theme.colors.text.hint}
|
|
92
|
+
typography={'description_xs_regular'}>
|
|
75
93
|
{`${inputValue.length}/${maxLength}`}
|
|
76
94
|
</Text>
|
|
77
95
|
</View>
|
|
@@ -108,7 +126,6 @@ const InputTextArea = forwardRef(
|
|
|
108
126
|
<View style={styles.textAreaView}>
|
|
109
127
|
<TextInput
|
|
110
128
|
{...props}
|
|
111
|
-
accessibilityLabel={accessibilityLabel}
|
|
112
129
|
ref={inputRef}
|
|
113
130
|
editable={!disabled}
|
|
114
131
|
textAlignVertical="top"
|
|
@@ -148,18 +165,16 @@ const InputTextArea = forwardRef(
|
|
|
148
165
|
};
|
|
149
166
|
|
|
150
167
|
return (
|
|
151
|
-
<View
|
|
152
|
-
style={[style, styles.wrapper]}
|
|
153
|
-
accessibilityLabel={accessibilityLabel}>
|
|
168
|
+
<View style={[style, styles.wrapper]}>
|
|
154
169
|
{renderInputView()}
|
|
155
|
-
<ErrorView
|
|
170
|
+
<ErrorView
|
|
171
|
+
errorMessage={errorMessage}
|
|
172
|
+
errorSpacing={errorSpacing}
|
|
173
|
+
hintText={hintText}
|
|
174
|
+
/>
|
|
156
175
|
</View>
|
|
157
176
|
);
|
|
158
177
|
},
|
|
159
178
|
);
|
|
160
179
|
|
|
161
|
-
InputTextArea.defaultProps = {
|
|
162
|
-
maxLength: MAX_LENGTH,
|
|
163
|
-
};
|
|
164
|
-
|
|
165
180
|
export default InputTextArea;
|
package/Input/common.tsx
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {View, ViewStyle} from 'react-native';
|
|
2
|
-
import styles from './styles';
|
|
3
|
-
import {scaleSize, Text} from '../Text';
|
|
4
1
|
import React, {FC, useContext} from 'react';
|
|
2
|
+
import {View, ViewStyle} from 'react-native';
|
|
5
3
|
import {ApplicationContext} from '../Application';
|
|
6
|
-
import {Icon} from '../Icon';
|
|
7
|
-
import {Styles} from '../Consts';
|
|
8
4
|
import {Theme} from '../Application/types';
|
|
5
|
+
import {Styles} from '../Consts';
|
|
6
|
+
import {Icon} from '../Icon';
|
|
7
|
+
import {scaleSize, Text} from '../Text';
|
|
8
|
+
import styles from './styles';
|
|
9
9
|
|
|
10
10
|
type FloatingViewProps = {
|
|
11
11
|
floatingValue?: string;
|
|
@@ -48,24 +48,31 @@ export const getSizeStyle = (size?: 'small' | 'large') => {
|
|
|
48
48
|
}
|
|
49
49
|
return styles.container;
|
|
50
50
|
};
|
|
51
|
-
export const ErrorView: FC<{
|
|
52
|
-
errorMessage
|
|
53
|
-
errorSpacing
|
|
54
|
-
|
|
51
|
+
export const ErrorView: FC<{
|
|
52
|
+
errorMessage?: string;
|
|
53
|
+
errorSpacing?: boolean;
|
|
54
|
+
hintText?: string;
|
|
55
|
+
}> = ({errorMessage, errorSpacing, hintText}) => {
|
|
55
56
|
const {theme} = useContext(ApplicationContext);
|
|
56
57
|
const errorColor = theme.colors.error.primary;
|
|
57
|
-
|
|
58
|
+
const hintColor = theme.colors.text.hint;
|
|
59
|
+
const hintTextDefault = hintText ?? 'Không thể chỉnh sửa';
|
|
60
|
+
if (errorMessage || hintText) {
|
|
58
61
|
return (
|
|
59
62
|
<View style={styles.errorView}>
|
|
60
63
|
<View style={styles.errorIcon}>
|
|
61
|
-
<Icon
|
|
64
|
+
<Icon
|
|
65
|
+
color={errorMessage ? errorColor : hintColor}
|
|
66
|
+
source="ic_error"
|
|
67
|
+
size={16}
|
|
68
|
+
/>
|
|
62
69
|
</View>
|
|
63
70
|
<Text
|
|
64
71
|
style={Styles.flex}
|
|
65
|
-
color={errorColor}
|
|
72
|
+
color={errorMessage ? errorColor : hintColor}
|
|
66
73
|
numberOfLines={2}
|
|
67
|
-
typography={'
|
|
68
|
-
{errorMessage}
|
|
74
|
+
typography={'description_default_regular'}>
|
|
75
|
+
{errorMessage ?? hintTextDefault}
|
|
69
76
|
</Text>
|
|
70
77
|
</View>
|
|
71
78
|
);
|
|
@@ -104,10 +111,12 @@ export const FloatingView: FC<FloatingViewProps> = ({
|
|
|
104
111
|
},
|
|
105
112
|
style,
|
|
106
113
|
]}>
|
|
107
|
-
<Text color={floatingTextColor} typography={'
|
|
114
|
+
<Text color={floatingTextColor} typography={'label_s_medium'}>
|
|
108
115
|
{floatingValue}
|
|
109
116
|
{required && (
|
|
110
|
-
<Text
|
|
117
|
+
<Text
|
|
118
|
+
typography={'label_s_medium'}
|
|
119
|
+
color={theme.colors.error.primary}>
|
|
111
120
|
*
|
|
112
121
|
</Text>
|
|
113
122
|
)}
|
package/Input/index.tsx
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
|
+
import {TextInputProps, ViewStyle} from 'react-native';
|
|
1
2
|
import Input from './Input';
|
|
2
|
-
import
|
|
3
|
-
import InputSearch from './InputSearch';
|
|
3
|
+
import InputDropDown from './InputDropDown';
|
|
4
4
|
import InputMoney from './InputMoney';
|
|
5
5
|
import InputOTP from './InputOTP';
|
|
6
|
-
import
|
|
7
|
-
import
|
|
6
|
+
import InputSearch from './InputSearch';
|
|
7
|
+
import InputTextArea from './InputTextArea';
|
|
8
|
+
|
|
9
|
+
export interface InputRef {
|
|
10
|
+
focus: () => void;
|
|
11
|
+
blur: () => void;
|
|
12
|
+
clear: () => void;
|
|
13
|
+
setText: (text: string) => void;
|
|
14
|
+
}
|
|
8
15
|
|
|
9
16
|
export interface InputProps extends TextInputProps {
|
|
10
17
|
/**
|
|
@@ -76,6 +83,26 @@ export interface InputProps extends TextInputProps {
|
|
|
76
83
|
* Optional. Represents the color of the leading icon in the Input component.
|
|
77
84
|
*/
|
|
78
85
|
leadingIconColor?: string;
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Optional. Represents the font weight of the text in the Input component.
|
|
89
|
+
*/
|
|
90
|
+
fontWeight?: 'Regular' | 'Bold';
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Optional. callback function to be called when the icon is pressed.
|
|
94
|
+
*/
|
|
95
|
+
onPressIcon?: () => void;
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Optional. params for element tracking.
|
|
99
|
+
*/
|
|
100
|
+
params?: any;
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Optional. Represents text below the Input component.
|
|
104
|
+
*/
|
|
105
|
+
hintText?: string;
|
|
79
106
|
}
|
|
80
107
|
|
|
81
108
|
type InputPropsWithoutSizeAndIcon = Omit<
|
|
@@ -83,10 +110,6 @@ type InputPropsWithoutSizeAndIcon = Omit<
|
|
|
83
110
|
'size' | 'icon' | 'iconColor'
|
|
84
111
|
>;
|
|
85
112
|
|
|
86
|
-
type InputPropsOmitForSearch = Omit<
|
|
87
|
-
InputProps,
|
|
88
|
-
'required' | 'size' | 'errorMessage' | 'disabled'
|
|
89
|
-
>;
|
|
90
113
|
type InputPropsWithoutPlaceholder = Omit<InputProps, 'placeholder'>;
|
|
91
114
|
|
|
92
115
|
export interface InputTextAreaProps extends InputPropsWithoutSizeAndIcon {
|
|
@@ -97,7 +120,7 @@ export interface InputTextAreaProps extends InputPropsWithoutSizeAndIcon {
|
|
|
97
120
|
height?: number;
|
|
98
121
|
}
|
|
99
122
|
|
|
100
|
-
export interface InputSearchProps extends
|
|
123
|
+
export interface InputSearchProps extends TextInputProps {
|
|
101
124
|
/**
|
|
102
125
|
* Optional. Represents the text to be displayed on the search button within the InputSearch component.
|
|
103
126
|
*/
|
|
@@ -109,13 +132,24 @@ export interface InputSearchProps extends InputPropsOmitForSearch {
|
|
|
109
132
|
*/
|
|
110
133
|
showButtonText?: boolean;
|
|
111
134
|
|
|
135
|
+
onPressButtonText?: () => void;
|
|
136
|
+
|
|
112
137
|
/**
|
|
113
|
-
* Optional.
|
|
114
|
-
* Defaults to `true` if not provided.
|
|
138
|
+
* Optional. Represents the color of the icon in the Input component.
|
|
115
139
|
*/
|
|
116
|
-
|
|
140
|
+
iconColor?: string;
|
|
117
141
|
|
|
118
|
-
|
|
142
|
+
/**
|
|
143
|
+
* Optional. Represents the name or key of the icon to be displayed in the Input component.
|
|
144
|
+
*/
|
|
145
|
+
icon?: string;
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
*
|
|
149
|
+
*/
|
|
150
|
+
hasColorBG?: boolean;
|
|
151
|
+
|
|
152
|
+
params?: any;
|
|
119
153
|
}
|
|
120
154
|
|
|
121
155
|
export interface InputMoneyProps extends InputPropsWithoutPlaceholder {}
|
|
@@ -135,6 +169,8 @@ export interface InputOTPProps extends InputPropsWithoutSizeAndIcon {
|
|
|
135
169
|
* 'number' - The OTP input will be treated as a number.
|
|
136
170
|
*/
|
|
137
171
|
dataType?: 'string' | 'number';
|
|
172
|
+
|
|
173
|
+
floatingValue: string;
|
|
138
174
|
}
|
|
139
175
|
|
|
140
176
|
export type CaretProps = {
|
|
@@ -151,7 +187,7 @@ export type CaretProps = {
|
|
|
151
187
|
length?: number;
|
|
152
188
|
};
|
|
153
189
|
|
|
154
|
-
export interface InputDropDownProps {
|
|
190
|
+
export interface InputDropDownProps extends InputProps {
|
|
155
191
|
/**
|
|
156
192
|
* Optional. Defines the size of the InputDropDown component.
|
|
157
193
|
* 'small' - A smaller, less prominent input.
|
|
@@ -239,7 +275,17 @@ export interface InputDropDownProps {
|
|
|
239
275
|
*/
|
|
240
276
|
leadingIconColor?: string;
|
|
241
277
|
|
|
242
|
-
|
|
278
|
+
/**
|
|
279
|
+
* Optional. params for element tracking.
|
|
280
|
+
*/
|
|
281
|
+
params?: {
|
|
282
|
+
[key: string]: any;
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Optional. Represents the style of the InputDropDown component.
|
|
287
|
+
*/
|
|
288
|
+
style?: ViewStyle | ViewStyle[];
|
|
243
289
|
}
|
|
244
290
|
|
|
245
|
-
export {Input,
|
|
291
|
+
export {Input, InputDropDown, InputMoney, InputOTP, InputSearch, InputTextArea};
|
package/Input/styles.ts
CHANGED
|
@@ -7,8 +7,7 @@ export default StyleSheet.create({
|
|
|
7
7
|
input: {
|
|
8
8
|
flex: 1,
|
|
9
9
|
paddingLeft: Spacing.M,
|
|
10
|
-
|
|
11
|
-
fontSize: scaleSize(15),
|
|
10
|
+
fontSize: scaleSize(14),
|
|
12
11
|
},
|
|
13
12
|
wrapper: {
|
|
14
13
|
width: '100%',
|
|
@@ -54,8 +53,7 @@ export default StyleSheet.create({
|
|
|
54
53
|
marginRight: Spacing.M,
|
|
55
54
|
},
|
|
56
55
|
icon: {
|
|
57
|
-
|
|
58
|
-
height: 24,
|
|
56
|
+
marginLeft: Spacing.S,
|
|
59
57
|
},
|
|
60
58
|
iconWrapper: {
|
|
61
59
|
width: 24,
|
|
@@ -77,7 +75,7 @@ export default StyleSheet.create({
|
|
|
77
75
|
flex: 1,
|
|
78
76
|
paddingRight: Spacing.XS,
|
|
79
77
|
paddingVertical: Spacing.XS,
|
|
80
|
-
fontSize: scaleSize(
|
|
78
|
+
fontSize: scaleSize(14),
|
|
81
79
|
},
|
|
82
80
|
textAreaWrapper: {
|
|
83
81
|
borderWidth: 1,
|
|
@@ -102,7 +100,6 @@ export default StyleSheet.create({
|
|
|
102
100
|
searchInputWrapper: {
|
|
103
101
|
flexDirection: 'row',
|
|
104
102
|
borderRadius: Radius.XL,
|
|
105
|
-
borderWidth: 1,
|
|
106
103
|
paddingHorizontal: Spacing.S,
|
|
107
104
|
alignItems: 'center',
|
|
108
105
|
flex: 1,
|
|
@@ -111,7 +108,7 @@ export default StyleSheet.create({
|
|
|
111
108
|
height: 36,
|
|
112
109
|
marginLeft: Spacing.S,
|
|
113
110
|
flex: 1,
|
|
114
|
-
fontSize: scaleSize(
|
|
111
|
+
fontSize: scaleSize(14),
|
|
115
112
|
paddingVertical: Spacing.XS,
|
|
116
113
|
},
|
|
117
114
|
searchInputView: {
|
|
@@ -148,7 +145,7 @@ export default StyleSheet.create({
|
|
|
148
145
|
},
|
|
149
146
|
otpFloatingView: {
|
|
150
147
|
position: 'absolute',
|
|
151
|
-
top: -Spacing.
|
|
148
|
+
top: scaleSize(-Spacing.M + 2),
|
|
152
149
|
alignSelf: 'center',
|
|
153
150
|
paddingHorizontal: Spacing.S,
|
|
154
151
|
},
|
package/Layout/Card.tsx
CHANGED
|
@@ -7,7 +7,7 @@ import Item from './Item';
|
|
|
7
7
|
import {Image} from '../Image';
|
|
8
8
|
import {ApplicationContext} from '../Application';
|
|
9
9
|
import styles from './styles';
|
|
10
|
-
import {Colors, Shadow, Spacing} from '../Consts';
|
|
10
|
+
import {Colors, Radius, Shadow, Spacing} from '../Consts';
|
|
11
11
|
import ItemList from './ItemList';
|
|
12
12
|
|
|
13
13
|
export interface CardProps extends ViewProps {
|
|
@@ -27,8 +27,9 @@ const Card: React.FC<CardProps> = ({
|
|
|
27
27
|
style,
|
|
28
28
|
backgroundImage,
|
|
29
29
|
useShadow = false,
|
|
30
|
+
...props
|
|
30
31
|
}) => {
|
|
31
|
-
const {showGrid} = useContext(ApplicationContext);
|
|
32
|
+
const {showGrid, theme} = useContext(ApplicationContext);
|
|
32
33
|
const {numberOfColumns} = useGridSystem();
|
|
33
34
|
|
|
34
35
|
const gutterSize = 8;
|
|
@@ -41,12 +42,12 @@ const Card: React.FC<CardProps> = ({
|
|
|
41
42
|
const gridContext: GridContextProps = {
|
|
42
43
|
numberOfColumns,
|
|
43
44
|
gutterSize,
|
|
44
|
-
margin,
|
|
45
45
|
sizePerSpan,
|
|
46
46
|
getSizeSpan: span => {
|
|
47
47
|
return span * sizePerSpan + (span - 1) * gutterSize;
|
|
48
48
|
},
|
|
49
49
|
};
|
|
50
|
+
|
|
50
51
|
/**
|
|
51
52
|
* render overlay only dev mode
|
|
52
53
|
*/
|
|
@@ -107,8 +108,9 @@ const Card: React.FC<CardProps> = ({
|
|
|
107
108
|
if (Array.isArray(children)) {
|
|
108
109
|
let cursor = 0;
|
|
109
110
|
let rows: any[] = [];
|
|
110
|
-
|
|
111
|
-
|
|
111
|
+
const arrayChild = children.filter(item => !!item);
|
|
112
|
+
for (let i = 0; i < arrayChild.length; ++i) {
|
|
113
|
+
const item = arrayChild[i];
|
|
112
114
|
const totalSpan = rows
|
|
113
115
|
.concat([item])
|
|
114
116
|
.map(i => i.props?.widthSpan)
|
|
@@ -124,7 +126,7 @@ const Card: React.FC<CardProps> = ({
|
|
|
124
126
|
rows = [item];
|
|
125
127
|
}
|
|
126
128
|
|
|
127
|
-
if (i ==
|
|
129
|
+
if (i == arrayChild.length - 1) {
|
|
128
130
|
cursor += 1;
|
|
129
131
|
element.push(addRowView(rows, cursor, true));
|
|
130
132
|
}
|
|
@@ -138,25 +140,32 @@ const Card: React.FC<CardProps> = ({
|
|
|
138
140
|
return element;
|
|
139
141
|
};
|
|
140
142
|
|
|
141
|
-
const {theme} = useContext(ApplicationContext);
|
|
142
143
|
return (
|
|
143
144
|
<GridContext.Provider value={gridContext}>
|
|
144
145
|
<View
|
|
145
|
-
|
|
146
|
+
{...props}
|
|
146
147
|
style={[
|
|
148
|
+
{backgroundColor: theme.colors.background.surface},
|
|
147
149
|
style,
|
|
150
|
+
styles.protectedStyle,
|
|
148
151
|
styles.card,
|
|
149
152
|
{
|
|
150
153
|
width: widthSection,
|
|
151
|
-
marginHorizontal:
|
|
152
|
-
backgroundColor: theme.colors.background.surface,
|
|
154
|
+
marginHorizontal: margin,
|
|
153
155
|
},
|
|
156
|
+
!!backgroundImage && {backgroundColor: undefined},
|
|
154
157
|
useShadow && Shadow.Light,
|
|
155
158
|
]}>
|
|
156
159
|
{!!backgroundImage && (
|
|
157
160
|
<Image
|
|
158
161
|
source={{uri: backgroundImage}}
|
|
159
|
-
style={
|
|
162
|
+
style={[
|
|
163
|
+
styles.imageBackground,
|
|
164
|
+
{
|
|
165
|
+
width: widthSection,
|
|
166
|
+
borderRadius: Radius.M,
|
|
167
|
+
},
|
|
168
|
+
]}
|
|
160
169
|
/>
|
|
161
170
|
)}
|
|
162
171
|
{renderView(validateChildren(children, [Item, ItemList]))}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import {default as React, useContext, useEffect, useRef, useState} from 'react';
|
|
2
|
+
import {
|
|
3
|
+
Animated,
|
|
4
|
+
LayoutChangeEvent,
|
|
5
|
+
StyleSheet,
|
|
6
|
+
TouchableOpacity,
|
|
7
|
+
View,
|
|
8
|
+
} from 'react-native';
|
|
9
|
+
import {ApplicationContext} from '../Application';
|
|
10
|
+
import {Icon} from '../Icon';
|
|
11
|
+
|
|
12
|
+
export interface FloatingButtonProps {
|
|
13
|
+
label?: string;
|
|
14
|
+
onPress?: () => void;
|
|
15
|
+
position?: 'center' | 'right';
|
|
16
|
+
icon?: string;
|
|
17
|
+
iconColor?: string;
|
|
18
|
+
size?: 'small' | 'large';
|
|
19
|
+
animatedValue?: Animated.Value;
|
|
20
|
+
bottom?: number;
|
|
21
|
+
renderComponent?: () => React.ReactNode;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const FloatingButton: React.FC<FloatingButtonProps> = ({
|
|
25
|
+
renderComponent,
|
|
26
|
+
label = '',
|
|
27
|
+
onPress = () => {},
|
|
28
|
+
position = 'right',
|
|
29
|
+
icon = '',
|
|
30
|
+
iconColor = '',
|
|
31
|
+
size = 'small',
|
|
32
|
+
animatedValue,
|
|
33
|
+
bottom = 12,
|
|
34
|
+
}: FloatingButtonProps) => {
|
|
35
|
+
const {theme} = useContext(ApplicationContext);
|
|
36
|
+
const maxWidth = useRef(0);
|
|
37
|
+
const minWidth = size == 'small' ? 36 : 48;
|
|
38
|
+
const [opacityAnimated] = useState(new Animated.Value(0)); // Initial opacity set to 0
|
|
39
|
+
const [widthAnimated, setWidthAnimated] = useState<Animated.Value>();
|
|
40
|
+
const lastOffset = useRef(0);
|
|
41
|
+
const lastDirection = useRef<string>();
|
|
42
|
+
const [showText, setShowText] = React.useState(true);
|
|
43
|
+
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
if (!label) return;
|
|
46
|
+
|
|
47
|
+
Animated.timing(opacityAnimated, {
|
|
48
|
+
toValue: 1,
|
|
49
|
+
duration: 100,
|
|
50
|
+
useNativeDriver: true,
|
|
51
|
+
}).start();
|
|
52
|
+
|
|
53
|
+
const listener = animatedValue?.addListener(({value}) => {
|
|
54
|
+
if (value !== lastOffset.current && value > 0) {
|
|
55
|
+
const direction = value > lastOffset.current ? 'down' : 'up';
|
|
56
|
+
lastOffset.current = value;
|
|
57
|
+
if (lastDirection.current !== direction) {
|
|
58
|
+
lastDirection.current = direction;
|
|
59
|
+
if (direction === 'down') {
|
|
60
|
+
Animated.timing(opacityAnimated, {
|
|
61
|
+
toValue: 0,
|
|
62
|
+
duration: 100,
|
|
63
|
+
useNativeDriver: true,
|
|
64
|
+
}).start();
|
|
65
|
+
Animated.timing(widthAnimated!, {
|
|
66
|
+
toValue: minWidth,
|
|
67
|
+
duration: 100,
|
|
68
|
+
useNativeDriver: false,
|
|
69
|
+
}).start(() => setShowText(false));
|
|
70
|
+
} else {
|
|
71
|
+
Animated.timing(opacityAnimated, {
|
|
72
|
+
toValue: 1,
|
|
73
|
+
duration: 100,
|
|
74
|
+
useNativeDriver: true,
|
|
75
|
+
}).start();
|
|
76
|
+
Animated.timing(widthAnimated!, {
|
|
77
|
+
toValue: maxWidth.current,
|
|
78
|
+
duration: 100,
|
|
79
|
+
useNativeDriver: false,
|
|
80
|
+
}).start(() => setShowText(true));
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
return () => {
|
|
87
|
+
if (listener != null) {
|
|
88
|
+
animatedValue?.removeListener(listener);
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
}, [label, widthAnimated]);
|
|
92
|
+
|
|
93
|
+
const handleLayout = (event: LayoutChangeEvent) => {
|
|
94
|
+
const layout = event.nativeEvent.layout;
|
|
95
|
+
if (widthAnimated) return;
|
|
96
|
+
maxWidth.current = layout.width;
|
|
97
|
+
setWidthAnimated(new Animated.Value(layout.width));
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
if (renderComponent) {
|
|
101
|
+
return (
|
|
102
|
+
<View
|
|
103
|
+
onLayout={handleLayout}
|
|
104
|
+
style={[
|
|
105
|
+
styles.container,
|
|
106
|
+
{
|
|
107
|
+
right: position === 'right' ? 12 : undefined,
|
|
108
|
+
alignSelf: position === 'center' ? 'center' : 'flex-end',
|
|
109
|
+
bottom,
|
|
110
|
+
},
|
|
111
|
+
]}>
|
|
112
|
+
{renderComponent()}
|
|
113
|
+
</View>
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return (
|
|
118
|
+
<Animated.View
|
|
119
|
+
onLayout={handleLayout}
|
|
120
|
+
style={[
|
|
121
|
+
styles.container,
|
|
122
|
+
{
|
|
123
|
+
right: position === 'right' ? 12 : undefined,
|
|
124
|
+
alignSelf: position === 'center' ? 'center' : 'flex-end',
|
|
125
|
+
width: widthAnimated,
|
|
126
|
+
height: size == 'small' ? 36 : 48,
|
|
127
|
+
backgroundColor: theme.colors.primary,
|
|
128
|
+
bottom,
|
|
129
|
+
},
|
|
130
|
+
]}>
|
|
131
|
+
<TouchableOpacity
|
|
132
|
+
style={{flexDirection: 'row', alignItems: 'center'}}
|
|
133
|
+
onPress={onPress}>
|
|
134
|
+
<Icon
|
|
135
|
+
source={icon}
|
|
136
|
+
size={size === 'small' ? 12 : 24}
|
|
137
|
+
color={iconColor}
|
|
138
|
+
/>
|
|
139
|
+
{showText && !!label && (
|
|
140
|
+
<Animated.Text
|
|
141
|
+
style={[styles.label, {opacity: opacityAnimated, color: 'white'}]}
|
|
142
|
+
numberOfLines={1}>
|
|
143
|
+
{label}
|
|
144
|
+
</Animated.Text>
|
|
145
|
+
)}
|
|
146
|
+
</TouchableOpacity>
|
|
147
|
+
</Animated.View>
|
|
148
|
+
);
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
const styles = StyleSheet.create({
|
|
152
|
+
container: {
|
|
153
|
+
flexDirection: 'row',
|
|
154
|
+
alignContent: 'center',
|
|
155
|
+
alignItems: 'center',
|
|
156
|
+
position: 'absolute',
|
|
157
|
+
borderRadius: 100,
|
|
158
|
+
paddingHorizontal: 12,
|
|
159
|
+
flexWrap: 'wrap',
|
|
160
|
+
alignSelf: 'flex-end',
|
|
161
|
+
},
|
|
162
|
+
label: {
|
|
163
|
+
marginLeft: 8,
|
|
164
|
+
fontSize: 16,
|
|
165
|
+
lineHeight: 22,
|
|
166
|
+
fontWeight: '700',
|
|
167
|
+
},
|
|
168
|
+
});
|
package/Layout/GridSystem.tsx
CHANGED
|
@@ -24,7 +24,6 @@ const GridSystem: React.FC<any> = ({
|
|
|
24
24
|
const grid: GridContextProps = {
|
|
25
25
|
numberOfColumns,
|
|
26
26
|
gutterSize,
|
|
27
|
-
margin,
|
|
28
27
|
sizePerSpan,
|
|
29
28
|
getSizeSpan: span => {
|
|
30
29
|
return span * sizePerSpan + (span - 1) * gutterSize;
|
|
@@ -46,7 +45,7 @@ const GridSystem: React.FC<any> = ({
|
|
|
46
45
|
screenGrid && insets,
|
|
47
46
|
{
|
|
48
47
|
borderColor: theme.colors.error.primary,
|
|
49
|
-
marginHorizontal:
|
|
48
|
+
marginHorizontal: margin,
|
|
50
49
|
top: Spacing.M,
|
|
51
50
|
bottom: Spacing.M,
|
|
52
51
|
},
|