@momo-kits/foundation 0.169.5-beta.1 → 0.169.5
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.hooks.ts +8 -8
- package/Application/BottomSheet.parts.tsx +44 -34
- package/Application/BottomSheet.tsx +40 -37
- package/Application/BottomTab/ActiveIndicator.tsx +15 -14
- package/Application/BottomTab/BottomTabBar.tsx +43 -34
- package/Application/BottomTab/BottomTabInset.tsx +8 -4
- package/Application/BottomTab/CurvedContainer.tsx +14 -12
- package/Application/BottomTab/CustomBottomTabItem.tsx +10 -6
- package/Application/BottomTab/DuoBarCompression.tsx +13 -19
- package/Application/BottomTab/DuoRailTabBar.tsx +13 -13
- package/Application/BottomTab/DuoTabBar.tsx +8 -9
- package/Application/BottomTab/TabBarIcon.tsx +13 -8
- package/Application/BottomTab/TabScreen.tsx +1 -5
- package/Application/BottomTab/index.tsx +38 -30
- package/Application/BottomTab/metrics.ts +21 -20
- package/Application/BottomTab/tabItems.ts +1 -1
- package/Application/BottomTab/usePressLift.ts +2 -2
- package/Application/Components/BackgroundImageView.tsx +4 -0
- package/Application/Components/DuoGlassBackground.tsx +5 -4
- package/Application/Components/HeaderAnimated.tsx +4 -0
- package/Application/Components/HeaderBackground.tsx +4 -0
- package/Application/Components/HeaderExtendHeader.tsx +11 -10
- package/Application/Components/HeaderLeft.tsx +2 -1
- package/Application/Components/HeaderRight.tsx +2 -1
- package/Application/Components/HeaderTitle.shared.ts +11 -9
- package/Application/Components/HeaderTitle.tsx +4 -3
- package/Application/Components/HeaderToolkitAction.tsx +17 -20
- package/Application/Components/NavigationButton.tsx +2 -1
- package/Application/Components/SearchHeader.tsx +17 -10
- package/Application/Components/TitleJourney.tsx +2 -2
- package/Application/Components/TitleLocation.tsx +2 -2
- package/Application/Components/TitleUser.tsx +2 -2
- package/Application/Components/useToolkitActions.tsx +17 -32
- package/Application/Localize.ts +7 -0
- package/Application/ModalScreen.tsx +10 -10
- package/Application/Navigation.ts +14 -16
- package/Application/NavigationContainer.tsx +19 -18
- package/Application/Navigator.ts +20 -48
- package/Application/StackScreen.tracking.ts +11 -26
- package/Application/StackScreen.tsx +9 -39
- package/Application/TooltipPortal.tsx +12 -5
- package/Application/WidgetContainer.tsx +10 -4
- package/Application/modalLayout.ts +13 -14
- package/Application/navigationOptions.tsx +21 -17
- package/Badge/Badge.tsx +5 -0
- package/Badge/BadgeDot.tsx +1 -0
- package/Badge/BadgeDotAnimation.tsx +15 -7
- package/Badge/BadgeRibbon.tsx +46 -38
- package/Badge/Shape.tsx +2 -0
- package/Badge/types.ts +2 -2
- package/Button/index.tsx +5 -16
- package/Button/utils.ts +9 -15
- package/CheckBox/index.tsx +4 -0
- package/Consts/theme.ts +10 -10
- package/Context/index.ts +1 -0
- package/Divider/DashDivider.tsx +1 -0
- package/Divider/index.tsx +4 -0
- package/FoundationList/index.tsx +4 -0
- package/Icon/index.tsx +6 -6
- package/Icon/types.ts +1 -1
- package/IconButton/index.tsx +4 -12
- package/Image/index.tsx +9 -7
- package/Input/Input.tsx +8 -9
- package/Input/InputDropDown.tsx +10 -12
- package/Input/InputMoney.tsx +12 -8
- package/Input/InputMoneyView.tsx +14 -7
- package/Input/InputOTP.tsx +35 -33
- package/Input/InputPhoneNumber.tsx +8 -9
- package/Input/InputSearch.tsx +4 -6
- package/Input/InputTextArea.tsx +4 -0
- package/Input/SearchTextTyping.tsx +4 -0
- package/Input/SystemTextInput.tsx +7 -2
- package/Input/TextTyping.tsx +4 -0
- package/Input/common.tsx +26 -12
- package/Input/index.tsx +17 -20
- package/Input/moneyUtils.ts +10 -4
- package/Input/styles.ts +0 -5
- package/Input/utils.ts +14 -12
- package/Layout/Card.tsx +7 -4
- package/Layout/FloatingButton.tsx +4 -0
- package/Layout/Item.tsx +6 -2
- package/Layout/ItemList.tsx +9 -7
- package/Layout/ItemSectionList.tsx +2 -1
- package/Layout/ReservedRegions.tsx +25 -25
- package/Layout/Screen.tsx +56 -58
- package/Layout/Section.tsx +8 -5
- package/Layout/TrackingScope.tsx +1 -0
- package/Layout/screenContent.tsx +4 -2
- package/Layout/screenFooter.tsx +28 -18
- package/Layout/screenHeader.tsx +34 -31
- package/Layout/screenQuickReturn.tsx +17 -20
- package/Layout/screenScroll.ts +4 -7
- package/Layout/screenTypes.ts +3 -2
- package/Layout/types.ts +2 -4
- package/Layout/utils.ts +31 -37
- package/Loader/DotLoader.tsx +4 -0
- package/Loader/Loader.tsx +4 -0
- package/Loader/ProgressBar.tsx +1 -0
- package/Loader/Spinner.tsx +4 -0
- package/Loader/utils.ts +11 -7
- package/Pagination/Dot.tsx +4 -0
- package/Pagination/PaginationDot.tsx +1 -0
- package/Pagination/PaginationNumber.tsx +5 -3
- package/Pagination/PaginationScroll.tsx +5 -0
- package/Pagination/PaginationWhiteDot.tsx +1 -0
- package/Pagination/index.tsx +4 -0
- package/Pagination/types.ts +2 -2
- package/Popup/PopupNotify.tsx +44 -48
- package/Popup/PopupPromotion.tsx +4 -7
- package/Popup/types.ts +4 -4
- package/Popup/utils.ts +4 -0
- package/Radio/index.tsx +4 -0
- package/Skeleton/index.tsx +8 -0
- package/Switch/index.tsx +1 -0
- package/Tag/index.tsx +5 -1
- package/Tag/types.ts +3 -3
- package/Text/index.tsx +14 -3
- package/Text/styles.ts +1 -1
- package/Text/types.ts +1 -6
- package/Text/utils.ts +9 -0
- package/Title/index.tsx +41 -32
- package/ViewBoundary/index.tsx +4 -2
- package/index.ts +0 -6
- package/package.json +1 -1
- package/Application/KitAdoption/AdoptionBadge.tsx +0 -119
- package/Application/KitAdoption/AdoptionProbe.tsx +0 -145
- package/Application/KitAdoption/AdoptionStore.ts +0 -80
- package/Application/KitAdoption/Authorship/utils.ts +0 -137
- package/Application/KitAdoption/constants.ts +0 -118
- package/Application/KitAdoption/index.ts +0 -5
- package/Application/KitAdoption/styles.ts +0 -55
- package/Application/KitAdoption/types.ts +0 -48
- package/Application/KitAdoption/utils.ts +0 -375
- package/Application/__tests__/KitAdoption.integration.test.tsx +0 -235
- package/Application/__tests__/KitAdoption.test.ts +0 -283
- package/Application/__tests__/KitAdoptionAuthorship.test.ts +0 -132
- package/Application/__tests__/KitAdoptionStore.test.ts +0 -110
- package/Application/__tests__/modalLayout.test.ts +0 -128
|
@@ -34,7 +34,8 @@ import { Image } from '../Image';
|
|
|
34
34
|
import { Typography } from '../Text/types';
|
|
35
35
|
|
|
36
36
|
/**
|
|
37
|
-
*
|
|
37
|
+
* Vietnamese phone number input with a fixed flag and `+84` prefix, number pad
|
|
38
|
+
* by default, plus clear and trailing icons.
|
|
38
39
|
*/
|
|
39
40
|
const InputPhoneNumber = forwardRef(
|
|
40
41
|
(
|
|
@@ -146,9 +147,6 @@ const InputPhoneNumber = forwardRef(
|
|
|
146
147
|
typography = 'header_m_bold';
|
|
147
148
|
}
|
|
148
149
|
|
|
149
|
-
/**
|
|
150
|
-
* Render input view
|
|
151
|
-
*/
|
|
152
150
|
const renderInputView = () => {
|
|
153
151
|
const disabledColor = theme.colors.text.disable;
|
|
154
152
|
const secure = secureTextEntry;
|
|
@@ -156,6 +154,11 @@ const InputPhoneNumber = forwardRef(
|
|
|
156
154
|
let placeholderColor = theme.colors.text.hint;
|
|
157
155
|
let iconTintColor = trailingColor ?? theme.colors.text.default;
|
|
158
156
|
const borderWidth = focused ? 1.5 : 1;
|
|
157
|
+
let secureTextStyle = styles.secureTextLarge;
|
|
158
|
+
|
|
159
|
+
if (size === 'small') {
|
|
160
|
+
secureTextStyle = styles.secureTextSmall;
|
|
161
|
+
}
|
|
159
162
|
|
|
160
163
|
if (disabled) {
|
|
161
164
|
textColor = disabledColor;
|
|
@@ -214,11 +217,7 @@ const InputPhoneNumber = forwardRef(
|
|
|
214
217
|
color: textColor,
|
|
215
218
|
},
|
|
216
219
|
inputTextStyles,
|
|
217
|
-
secure &&
|
|
218
|
-
haveValue &&
|
|
219
|
-
(size === 'small'
|
|
220
|
-
? styles.secureTextSmall
|
|
221
|
-
: styles.secureTextLarge),
|
|
220
|
+
secure && haveValue && secureTextStyle,
|
|
222
221
|
]}
|
|
223
222
|
defaultValue={defaultValue}
|
|
224
223
|
allowFontScaling={false}
|
package/Input/InputSearch.tsx
CHANGED
|
@@ -26,6 +26,10 @@ import styles from './styles';
|
|
|
26
26
|
import { Styles } from '../Consts';
|
|
27
27
|
import SystemTextInput from './SystemTextInput';
|
|
28
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Search field with search icon, clear icon, optional trailing icon and a
|
|
31
|
+
* cancel button; with `typingData` it shows an animated placeholder instead.
|
|
32
|
+
*/
|
|
29
33
|
const InputSearch: ForwardRefRenderFunction<InputRef, InputSearchProps> = (
|
|
30
34
|
{
|
|
31
35
|
placeholder,
|
|
@@ -106,9 +110,6 @@ const InputSearch: ForwardRefRenderFunction<InputRef, InputSearchProps> = (
|
|
|
106
110
|
};
|
|
107
111
|
});
|
|
108
112
|
|
|
109
|
-
/**
|
|
110
|
-
* Render the input view
|
|
111
|
-
*/
|
|
112
113
|
const renderInputView = () => {
|
|
113
114
|
if (typingData && typingData?.length > 0) {
|
|
114
115
|
return (
|
|
@@ -149,9 +150,6 @@ const InputSearch: ForwardRefRenderFunction<InputRef, InputSearchProps> = (
|
|
|
149
150
|
);
|
|
150
151
|
};
|
|
151
152
|
|
|
152
|
-
/**
|
|
153
|
-
* Render trailing icon or text
|
|
154
|
-
*/
|
|
155
153
|
const renderTrailing = () => {
|
|
156
154
|
return (
|
|
157
155
|
<View style={Styles.row}>
|
package/Input/InputTextArea.tsx
CHANGED
|
@@ -21,6 +21,10 @@ import styles from './styles';
|
|
|
21
21
|
import SystemTextInput from './SystemTextInput';
|
|
22
22
|
import { setAutomationID, useComponentId } from '../Application';
|
|
23
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Multiline input with floating label, clear icon and a `length/maxLength`
|
|
26
|
+
* counter; the ref exposes clear, focus, blur and setText.
|
|
27
|
+
*/
|
|
24
28
|
const InputTextArea = forwardRef(
|
|
25
29
|
(
|
|
26
30
|
{
|
|
@@ -4,6 +4,10 @@ import { TextInput } from 'react-native';
|
|
|
4
4
|
import { useScaleSize, exportFontFamily } from '../Text';
|
|
5
5
|
import styles from './styles';
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Animated search placeholder: types each string of `data` char by char, then
|
|
9
|
+
* erases it, looping forever. Writes via setNativeProps to avoid re-renders.
|
|
10
|
+
*/
|
|
7
11
|
export const TextTyping: FC<any> = ({
|
|
8
12
|
data = [],
|
|
9
13
|
timeDelayEnd = 2000,
|
|
@@ -11,11 +11,16 @@ interface SystemTextInputProps extends TextInputProps {
|
|
|
11
11
|
ref?: React.RefObject<TextInput | null>;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* TextInput that still fires onPressIn/onPressOut when not editable on
|
|
16
|
+
* Android, by moving them to a wrapping touchable.
|
|
17
|
+
*/
|
|
14
18
|
const SystemTextInput: FC<SystemTextInputProps> = props => {
|
|
15
19
|
const { editable, onPressIn, onPressOut, ref } = props;
|
|
16
20
|
|
|
17
|
-
const
|
|
18
|
-
|
|
21
|
+
const isAndroidReadOnly = Platform.OS === 'android' && !editable;
|
|
22
|
+
const hasPressHandler = !!(onPressIn || onPressOut);
|
|
23
|
+
const needsAndroidWrapper = isAndroidReadOnly && hasPressHandler;
|
|
19
24
|
|
|
20
25
|
const noPressHandlers = { onPressIn: undefined, onPressOut: undefined };
|
|
21
26
|
const inputProps: TextInputProps = {
|
package/Input/TextTyping.tsx
CHANGED
|
@@ -11,6 +11,10 @@ export interface TextTypingProps {
|
|
|
11
11
|
inputStyle?: TextStyle;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Typing placeholder: types each string of `data` char by char, then erases
|
|
16
|
+
* it, looping forever. Writes via setNativeProps to avoid re-renders.
|
|
17
|
+
*/
|
|
14
18
|
const TextTyping: FC<TextTypingProps> = ({
|
|
15
19
|
data = [],
|
|
16
20
|
timeDelayEnd = 2000,
|
package/Input/common.tsx
CHANGED
|
@@ -35,6 +35,7 @@ type TrailingProps = {
|
|
|
35
35
|
|
|
36
36
|
export const MAX_LENGTH = 300;
|
|
37
37
|
|
|
38
|
+
/** Border color by state; priority is disabled > error > focused > default. */
|
|
38
39
|
export const getBorderColor = (
|
|
39
40
|
theme: Theme,
|
|
40
41
|
focused: boolean,
|
|
@@ -58,6 +59,10 @@ export const getBorderColor = (
|
|
|
58
59
|
return { borderColor };
|
|
59
60
|
};
|
|
60
61
|
|
|
62
|
+
/**
|
|
63
|
+
* Container style for `size`; multiline uses `height` as minHeight so the
|
|
64
|
+
* field can grow with its content.
|
|
65
|
+
*/
|
|
61
66
|
export const getSizeStyle = (
|
|
62
67
|
size?: 'small' | 'large',
|
|
63
68
|
multiline: boolean = false,
|
|
@@ -68,13 +73,14 @@ export const getSizeStyle = (
|
|
|
68
73
|
scaleStyle = { height };
|
|
69
74
|
}
|
|
70
75
|
|
|
71
|
-
if (multiline)
|
|
76
|
+
if (multiline) {
|
|
72
77
|
return [
|
|
73
78
|
styles.multilineContainer,
|
|
74
79
|
{
|
|
75
80
|
minHeight: height,
|
|
76
81
|
},
|
|
77
82
|
];
|
|
83
|
+
}
|
|
78
84
|
|
|
79
85
|
if (size === 'small') {
|
|
80
86
|
return [styles.smallContainer, scaleStyle];
|
|
@@ -82,6 +88,10 @@ export const getSizeStyle = (
|
|
|
82
88
|
return [styles.container, scaleStyle];
|
|
83
89
|
};
|
|
84
90
|
|
|
91
|
+
/**
|
|
92
|
+
* Error or hint row under an input (error wins); with neither, `errorSpacing`
|
|
93
|
+
* keeps an empty row so the layout doesn't jump when an error appears.
|
|
94
|
+
*/
|
|
85
95
|
export const ErrorView: FC<{
|
|
86
96
|
errorMessage?: string;
|
|
87
97
|
errorSpacing?: boolean;
|
|
@@ -95,21 +105,17 @@ export const ErrorView: FC<{
|
|
|
95
105
|
const scaledIconSize = useScaleSize(16);
|
|
96
106
|
|
|
97
107
|
if (errorMessage || hintText) {
|
|
108
|
+
const messageColor = errorMessage ? errorColor : hintColor;
|
|
109
|
+
const messageType = errorMessage ? 'error' : 'hint';
|
|
98
110
|
return (
|
|
99
111
|
<View style={styles.errorView}>
|
|
100
112
|
<View style={styles.errorIcon}>
|
|
101
|
-
<Icon
|
|
102
|
-
color={errorMessage ? errorColor : hintColor}
|
|
103
|
-
source="ic_error"
|
|
104
|
-
size={scaledIconSize}
|
|
105
|
-
/>
|
|
113
|
+
<Icon color={messageColor} source="ic_error" size={scaledIconSize} />
|
|
106
114
|
</View>
|
|
107
115
|
<Text
|
|
108
|
-
accessibilityLabel={`${componentId}|${
|
|
109
|
-
errorMessage ? 'error' : 'hint'
|
|
110
|
-
}-text`}
|
|
116
|
+
accessibilityLabel={`${componentId}|${messageType}-text`}
|
|
111
117
|
style={Styles.flex}
|
|
112
|
-
color={
|
|
118
|
+
color={messageColor}
|
|
113
119
|
typography={'description_default_regular'}
|
|
114
120
|
>
|
|
115
121
|
{errorMessage ?? hintTextDefault}
|
|
@@ -123,6 +129,10 @@ export const ErrorView: FC<{
|
|
|
123
129
|
return <View />;
|
|
124
130
|
};
|
|
125
131
|
|
|
132
|
+
/**
|
|
133
|
+
* Floating label on the input's top border with a required `*` and an
|
|
134
|
+
* optional pressable icon; renders nothing without `floatingValue`.
|
|
135
|
+
*/
|
|
126
136
|
export const FloatingView: FC<FloatingViewProps> = ({
|
|
127
137
|
floatingValue,
|
|
128
138
|
floatingIconColor,
|
|
@@ -191,6 +201,10 @@ export const FloatingView: FC<FloatingViewProps> = ({
|
|
|
191
201
|
return null;
|
|
192
202
|
};
|
|
193
203
|
|
|
204
|
+
/**
|
|
205
|
+
* Trailing slot: spinner while `loading`, an icon when `icon` is a known icon
|
|
206
|
+
* name or URL, otherwise `icon` as a text action (first 15 chars).
|
|
207
|
+
*/
|
|
194
208
|
export const RenderTrailing: FC<TrailingProps> = ({
|
|
195
209
|
loading,
|
|
196
210
|
color,
|
|
@@ -204,8 +218,8 @@ export const RenderTrailing: FC<TrailingProps> = ({
|
|
|
204
218
|
}
|
|
205
219
|
|
|
206
220
|
if (icon) {
|
|
207
|
-
const
|
|
208
|
-
|
|
221
|
+
const isIconName = !!(IconSources as any)?.[icon]?.uri;
|
|
222
|
+
const isIcon = isIconName || icon.includes('http');
|
|
209
223
|
const renderIconTouchable = (content: ReactNode) => {
|
|
210
224
|
return (
|
|
211
225
|
<TouchableOpacity
|
package/Input/index.tsx
CHANGED
|
@@ -36,7 +36,7 @@ export interface InputProps extends TextInputProps {
|
|
|
36
36
|
errorMessage?: string;
|
|
37
37
|
|
|
38
38
|
/**
|
|
39
|
-
* Optional.
|
|
39
|
+
* Optional. Trailing icon name or URL; any other string is shown as a text action.
|
|
40
40
|
*/
|
|
41
41
|
trailing?: string;
|
|
42
42
|
|
|
@@ -69,19 +69,18 @@ export interface InputProps extends TextInputProps {
|
|
|
69
69
|
required?: boolean;
|
|
70
70
|
|
|
71
71
|
/**
|
|
72
|
-
* Optional. If `true`,
|
|
73
|
-
* include spacing when Input not have error message.
|
|
72
|
+
* Optional. If `true`, keeps an empty error row when there is no error or hint.
|
|
74
73
|
* Defaults to `false` if not provided.
|
|
75
74
|
*/
|
|
76
75
|
errorSpacing?: boolean;
|
|
77
76
|
|
|
78
77
|
/**
|
|
79
|
-
*
|
|
78
|
+
* Optional. If `true`, shows a loading spinner in place of the trailing icon.
|
|
80
79
|
*/
|
|
81
80
|
loading?: boolean;
|
|
82
81
|
|
|
83
82
|
/**
|
|
84
|
-
*
|
|
83
|
+
* Optional. Represents the leading icon in the Input component.
|
|
85
84
|
*/
|
|
86
85
|
leadingIcon?: string;
|
|
87
86
|
|
|
@@ -111,12 +110,13 @@ export interface InputProps extends TextInputProps {
|
|
|
111
110
|
hintText?: string;
|
|
112
111
|
|
|
113
112
|
/**
|
|
114
|
-
* Optional.
|
|
113
|
+
* Optional. Callback function to be called when the floating icon is pressed.
|
|
115
114
|
*/
|
|
116
115
|
onPressFloatingIcon?: () => void;
|
|
117
116
|
|
|
118
117
|
/**
|
|
119
|
-
* Optional.
|
|
118
|
+
* Optional. If `true`, shows a clear icon while focused with a value.
|
|
119
|
+
* Defaults to `true` if not provided.
|
|
120
120
|
*/
|
|
121
121
|
showClearIcon?: boolean;
|
|
122
122
|
}
|
|
@@ -137,12 +137,12 @@ export interface InputSearchProps extends TextInputProps {
|
|
|
137
137
|
|
|
138
138
|
/**
|
|
139
139
|
* Optional. If `true`, the text on the search button will be displayed.
|
|
140
|
-
* Defaults to `
|
|
140
|
+
* Defaults to `true` if not provided.
|
|
141
141
|
*/
|
|
142
142
|
showButtonText?: boolean;
|
|
143
143
|
|
|
144
144
|
/**
|
|
145
|
-
* Optional.
|
|
145
|
+
* Optional. Callback function to be called when the search button is pressed.
|
|
146
146
|
*/
|
|
147
147
|
onPressButtonText?: () => void;
|
|
148
148
|
|
|
@@ -180,7 +180,7 @@ export interface InputSearchProps extends TextInputProps {
|
|
|
180
180
|
onPressTrailing?: () => void;
|
|
181
181
|
|
|
182
182
|
/**
|
|
183
|
-
* Optional.
|
|
183
|
+
* Optional. Background color of the search field. Defaults to the surface color.
|
|
184
184
|
*/
|
|
185
185
|
backgroundColor?: string;
|
|
186
186
|
|
|
@@ -195,8 +195,7 @@ export interface InputSearchProps extends TextInputProps {
|
|
|
195
195
|
typingData?: string[];
|
|
196
196
|
|
|
197
197
|
/**
|
|
198
|
-
* Optional.
|
|
199
|
-
* @param e
|
|
198
|
+
* Optional. Called when the field is pressed while showing `typingData`.
|
|
200
199
|
*/
|
|
201
200
|
onPress?: (e: GestureResponderEvent) => void;
|
|
202
201
|
}
|
|
@@ -259,7 +258,7 @@ export interface InputDropDownProps extends TouchableOpacityProps {
|
|
|
259
258
|
placeholder?: string;
|
|
260
259
|
|
|
261
260
|
/**
|
|
262
|
-
* Optional.
|
|
261
|
+
* Optional. If `true`, the value wraps to multiple lines and the chevron aligns to the top.
|
|
263
262
|
*/
|
|
264
263
|
multiline?: boolean;
|
|
265
264
|
|
|
@@ -303,19 +302,18 @@ export interface InputDropDownProps extends TouchableOpacityProps {
|
|
|
303
302
|
required?: boolean;
|
|
304
303
|
|
|
305
304
|
/**
|
|
306
|
-
* Optional. If `true`,
|
|
307
|
-
* include spacing when Input not have error message.
|
|
305
|
+
* Optional. If `true`, keeps an empty error row when there is no error or hint.
|
|
308
306
|
* Defaults to `false` if not provided.
|
|
309
307
|
*/
|
|
310
308
|
errorSpacing?: boolean;
|
|
311
309
|
|
|
312
310
|
/**
|
|
313
|
-
*
|
|
311
|
+
* Optional. If `true`, shows a loading spinner in place of the trailing icon.
|
|
314
312
|
*/
|
|
315
313
|
loading?: boolean;
|
|
316
314
|
|
|
317
315
|
/**
|
|
318
|
-
*
|
|
316
|
+
* Optional. Represents the leading icon in the Input component.
|
|
319
317
|
*/
|
|
320
318
|
leadingIcon?: string;
|
|
321
319
|
|
|
@@ -340,15 +338,14 @@ export interface InputDropDownProps extends TouchableOpacityProps {
|
|
|
340
338
|
hintText?: string;
|
|
341
339
|
|
|
342
340
|
/**
|
|
343
|
-
* Optional.
|
|
341
|
+
* Optional. Callback function to be called when the floating icon is pressed.
|
|
344
342
|
*/
|
|
345
343
|
onPressFloatingIcon?: () => void;
|
|
346
344
|
}
|
|
347
345
|
|
|
348
346
|
export interface InputPhoneNumberProps extends InputProps {
|
|
349
347
|
/**
|
|
350
|
-
* Optional.
|
|
351
|
-
* It should be in the format of a string, e.g., "+1" for the United States.
|
|
348
|
+
* Optional. Currently unused: the displayed prefix is always `+84`.
|
|
352
349
|
*/
|
|
353
350
|
countryCode?: string;
|
|
354
351
|
}
|
package/Input/moneyUtils.ts
CHANGED
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
export const moneyFormatter = new Intl.NumberFormat('vi-VN');
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Locates the single edited span between `old` and `text`: common prefix
|
|
5
|
+
* length (`start`), common suffix length (`suffix`) and removed + inserted
|
|
6
|
+
* char count (`size`).
|
|
7
|
+
*/
|
|
3
8
|
export const findEdit = (old: string, text: string) => {
|
|
4
9
|
let start = 0;
|
|
5
10
|
while (start < text.length && old[start] === text[start]) {
|
|
6
11
|
start += 1;
|
|
7
12
|
}
|
|
13
|
+
const maxSuffix = Math.min(text.length, old.length) - start;
|
|
8
14
|
let suffix = 0;
|
|
9
|
-
while (
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
15
|
+
while (suffix < maxSuffix) {
|
|
16
|
+
if (old[old.length - 1 - suffix] !== text[text.length - 1 - suffix]) {
|
|
17
|
+
break;
|
|
18
|
+
}
|
|
13
19
|
suffix += 1;
|
|
14
20
|
}
|
|
15
21
|
const size = old.length + text.length - 2 * (start + suffix);
|
package/Input/styles.ts
CHANGED
|
@@ -2,7 +2,6 @@ import { Platform, StyleSheet } from 'react-native';
|
|
|
2
2
|
import { Colors, Radius, Spacing } from '../Consts';
|
|
3
3
|
|
|
4
4
|
export default StyleSheet.create({
|
|
5
|
-
//input style
|
|
6
5
|
input: {
|
|
7
6
|
flex: 1,
|
|
8
7
|
paddingLeft: Spacing.M,
|
|
@@ -65,7 +64,6 @@ export default StyleSheet.create({
|
|
|
65
64
|
marginTop: Spacing.S,
|
|
66
65
|
},
|
|
67
66
|
|
|
68
|
-
//text area style
|
|
69
67
|
textAreaView: {
|
|
70
68
|
flex: 1,
|
|
71
69
|
},
|
|
@@ -94,7 +92,6 @@ export default StyleSheet.create({
|
|
|
94
92
|
marginBottom: Spacing.XS,
|
|
95
93
|
},
|
|
96
94
|
|
|
97
|
-
//searchInput
|
|
98
95
|
searchInputWrapper: {
|
|
99
96
|
flexDirection: 'row',
|
|
100
97
|
borderRadius: Radius.XL,
|
|
@@ -136,7 +133,6 @@ export default StyleSheet.create({
|
|
|
136
133
|
ios: 36,
|
|
137
134
|
}),
|
|
138
135
|
},
|
|
139
|
-
//OTP
|
|
140
136
|
otpWrapper: {
|
|
141
137
|
width: '100%',
|
|
142
138
|
marginTop: Spacing.M,
|
|
@@ -183,7 +179,6 @@ export default StyleSheet.create({
|
|
|
183
179
|
opacity: 0.02,
|
|
184
180
|
letterSpacing: Spacing.XXL,
|
|
185
181
|
},
|
|
186
|
-
//DropDown
|
|
187
182
|
inputDropDownWrapper: {
|
|
188
183
|
flexDirection: 'row',
|
|
189
184
|
marginTop: Spacing.S,
|
package/Input/utils.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { Localize } from '../Application';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Formats a number Vietnamese-style (`.` thousands, `,` decimals) with
|
|
5
|
+
* `currency` appended; 0, NaN or a falsy value gives `0<currency>`.
|
|
6
|
+
*/
|
|
3
7
|
const formatNumberToMoney = (number: number, currency = '') => {
|
|
4
8
|
if (!number || isNaN(number) || Number(number) === 0) {
|
|
5
9
|
return `0${currency}`;
|
|
@@ -41,6 +45,10 @@ const formatNumberToMoney = (number: number, currency = '') => {
|
|
|
41
45
|
return isNegative ? `-${result}${currency}` : `${result}${currency}`;
|
|
42
46
|
};
|
|
43
47
|
|
|
48
|
+
/**
|
|
49
|
+
* Parses a formatted money string back to a number by stripping
|
|
50
|
+
* `currencyUnit`, separators, `đ` and spaces; unparsable text gives 0.
|
|
51
|
+
*/
|
|
44
52
|
const formatMoneyToNumber = (money: string, currencyUnit: string) => {
|
|
45
53
|
if (money && money.length > 0) {
|
|
46
54
|
const moneyString = money
|
|
@@ -59,26 +67,20 @@ const formatMoneyToNumber = (money: string, currencyUnit: string) => {
|
|
|
59
67
|
return Number(money);
|
|
60
68
|
};
|
|
61
69
|
|
|
62
|
-
// --- PHONE VALIDATION ---
|
|
63
70
|
function formatPhoneNumber(phone: string): string {
|
|
64
71
|
return phone.replace(/\D/g, '');
|
|
65
72
|
}
|
|
66
73
|
|
|
74
|
+
/**
|
|
75
|
+
* Keeps the digits of `phone` (prepending the `0` a 9-char local number lacks)
|
|
76
|
+
* and returns a localized error when empty or not 10-11 digits.
|
|
77
|
+
*/
|
|
67
78
|
function checkValidPhoneNumber(phone: string): {
|
|
68
79
|
phoneFormatted: string;
|
|
69
80
|
error?: string;
|
|
70
81
|
} {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
// if not starting with 0 and has 9 digits => prepend 0
|
|
74
|
-
if (
|
|
75
|
-
phoneNumber.length > 0 &&
|
|
76
|
-
phoneNumber[0] !== '0' &&
|
|
77
|
-
/^\d$/.test(phoneNumber[0]) &&
|
|
78
|
-
phoneNumber.length === 9
|
|
79
|
-
) {
|
|
80
|
-
phoneNumber = '0' + phoneNumber;
|
|
81
|
-
}
|
|
82
|
+
const isMissingLeadingZero = phone.length === 9 && /^[1-9]/.test(phone);
|
|
83
|
+
const phoneNumber = isMissingLeadingZero ? `0${phone}` : phone;
|
|
82
84
|
|
|
83
85
|
const phoneFormatted = formatPhoneNumber(phoneNumber);
|
|
84
86
|
const localize = new Localize({ vi: {}, en: {} });
|
package/Layout/Card.tsx
CHANGED
|
@@ -20,6 +20,10 @@ export interface CardProps extends ViewProps {
|
|
|
20
20
|
useShadow?: boolean;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
/**
|
|
24
|
+
* Surface card across the content width minus 12pt margins, with its own grid for child
|
|
25
|
+
* Items; optional background image and light shadow.
|
|
26
|
+
*/
|
|
23
27
|
const Card: React.FC<CardProps> = ({
|
|
24
28
|
children,
|
|
25
29
|
style,
|
|
@@ -34,10 +38,9 @@ const Card: React.FC<CardProps> = ({
|
|
|
34
38
|
const gutterSize = 8;
|
|
35
39
|
const margin = 12;
|
|
36
40
|
const widthSection = contentWidth - margin * 2;
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
1 / numberOfColumns;
|
|
41
|
+
const innerWidth = widthSection - margin * 2;
|
|
42
|
+
const columnsWidth = innerWidth - gutterSize * (numberOfColumns - 1);
|
|
43
|
+
const sizePerSpan = columnsWidth / numberOfColumns - 1 / numberOfColumns;
|
|
41
44
|
|
|
42
45
|
const gridContext: GridContextProps = {
|
|
43
46
|
numberOfColumns,
|
|
@@ -31,6 +31,10 @@ export interface FloatingButtonProps {
|
|
|
31
31
|
renderComponent?: () => React.ReactNode;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
/**
|
|
35
|
+
* Floating action button above the screen bottom. With a label and a scroll value it
|
|
36
|
+
* shrinks to its icon while scrolling down and expands again on scroll up.
|
|
37
|
+
*/
|
|
34
38
|
export const FloatingButton: React.FC<FloatingButtonProps> = ({
|
|
35
39
|
renderComponent,
|
|
36
40
|
label = '',
|
package/Layout/Item.tsx
CHANGED
|
@@ -7,16 +7,20 @@ import { SpanNumber } from './types';
|
|
|
7
7
|
|
|
8
8
|
export interface ItemProps extends ViewProps {
|
|
9
9
|
/**
|
|
10
|
-
*
|
|
10
|
+
* Width in grid columns (1-12). Defaults to the full row.
|
|
11
11
|
*/
|
|
12
12
|
widthSpan?: SpanNumber;
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
|
-
* Optional.
|
|
15
|
+
* Optional. Height in grid spans (1-12). Unset keeps the content height.
|
|
16
16
|
*/
|
|
17
17
|
heightSpan?: SpanNumber;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Grid cell sized from the enclosing GridContext; outlined when the mini app turns on
|
|
22
|
+
* the baseline debug feature.
|
|
23
|
+
*/
|
|
20
24
|
const Item: React.FC<ItemProps> = ({
|
|
21
25
|
widthSpan,
|
|
22
26
|
heightSpan,
|
package/Layout/ItemList.tsx
CHANGED
|
@@ -7,12 +7,16 @@ import { SpanNumber } from './types';
|
|
|
7
7
|
|
|
8
8
|
export interface ItemListProps extends FlatListProps<any> {
|
|
9
9
|
/**
|
|
10
|
-
*
|
|
10
|
+
* Width of each item in grid columns (1-12). Defaults to the full row.
|
|
11
11
|
*/
|
|
12
12
|
widthSpan?: SpanNumber;
|
|
13
13
|
data: any[];
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
/**
|
|
17
|
+
* FlatList on the grid: `widthSpan` sets how many items share a row, and every item but
|
|
18
|
+
* the first of a row gets the gutter on its left.
|
|
19
|
+
*/
|
|
16
20
|
const ItemList = forwardRef(
|
|
17
21
|
(
|
|
18
22
|
{
|
|
@@ -30,6 +34,9 @@ const ItemList = forwardRef(
|
|
|
30
34
|
const indexBegin = props?.data?.map(
|
|
31
35
|
(_i: any, index: number) => index * numColumns,
|
|
32
36
|
);
|
|
37
|
+
const defaultKeyExtractor = (item: any, index: number) => {
|
|
38
|
+
return (item?.id ?? item?.key ?? index).toString();
|
|
39
|
+
};
|
|
33
40
|
const _renderItem = (item: any) => {
|
|
34
41
|
if (indexBegin?.includes(item?.index)) {
|
|
35
42
|
return <Item widthSpan={widthItem}>{renderItem?.(item)}</Item>;
|
|
@@ -46,12 +53,7 @@ const ItemList = forwardRef(
|
|
|
46
53
|
ref={ref as React.RefObject<FlatList>}
|
|
47
54
|
{...props}
|
|
48
55
|
key={`ItemList-${numColumns}`}
|
|
49
|
-
keyExtractor={
|
|
50
|
-
// prefer caller's keyExtractor; fallback to common id/key/index
|
|
51
|
-
(props as any)?.keyExtractor ??
|
|
52
|
-
((item: any, index: number) =>
|
|
53
|
-
(item?.id ?? item?.key ?? index).toString())
|
|
54
|
-
}
|
|
56
|
+
keyExtractor={(props as any)?.keyExtractor ?? defaultKeyExtractor}
|
|
55
57
|
numColumns={numColumns}
|
|
56
58
|
renderItem={_renderItem}
|
|
57
59
|
horizontal={false}
|
|
@@ -7,11 +7,12 @@ import { SpanNumber } from './types';
|
|
|
7
7
|
|
|
8
8
|
export interface ItemSectionListProps extends SectionListProps<any> {
|
|
9
9
|
/**
|
|
10
|
-
*
|
|
10
|
+
* Width of each item in grid columns (1-12). Defaults to the full row.
|
|
11
11
|
*/
|
|
12
12
|
widthSpan?: SpanNumber;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
/** SectionList whose items sit on the grid, `widthSpan` columns wide, a gutter apart. */
|
|
15
16
|
const ItemSectionList: FC<ItemSectionListProps> = ({
|
|
16
17
|
renderItem,
|
|
17
18
|
widthSpan,
|