@momo-kits/foundation 0.169.7-beta.3 → 0.169.7-beta.4
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 +34 -44
- package/Application/BottomSheet.tsx +37 -40
- package/Application/BottomTab/ActiveIndicator.tsx +14 -15
- package/Application/BottomTab/BottomTabBar.tsx +62 -49
- package/Application/BottomTab/BottomTabInset.tsx +4 -8
- package/Application/BottomTab/CurvedContainer.tsx +12 -14
- package/Application/BottomTab/CustomBottomTabItem.tsx +13 -13
- package/Application/BottomTab/DuoBarCompression.tsx +19 -13
- package/Application/BottomTab/DuoRailTabBar.tsx +24 -20
- package/Application/BottomTab/DuoTabBar.tsx +10 -8
- package/Application/BottomTab/TabBarIcon.tsx +29 -15
- package/Application/BottomTab/TabScreen.tsx +5 -1
- package/Application/BottomTab/index.tsx +30 -33
- package/Application/BottomTab/metrics.ts +23 -23
- package/Application/BottomTab/tabItems.ts +1 -1
- package/Application/BottomTab/usePressLift.ts +2 -2
- package/Application/Components/BackgroundImageView.tsx +0 -4
- package/Application/Components/DuoGlassBackground.tsx +4 -5
- package/Application/Components/HeaderAnimated.tsx +0 -4
- package/Application/Components/HeaderBackground.tsx +0 -4
- package/Application/Components/HeaderExtendHeader.tsx +10 -11
- package/Application/Components/HeaderLeft.tsx +1 -2
- package/Application/Components/HeaderRight.tsx +1 -2
- package/Application/Components/HeaderTitle.shared.ts +9 -11
- package/Application/Components/HeaderTitle.tsx +3 -4
- package/Application/Components/HeaderToolkitAction.tsx +20 -17
- package/Application/Components/NavigationButton.tsx +1 -2
- package/Application/Components/SearchHeader.tsx +10 -17
- 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 +32 -17
- package/Application/KitAdoption/AdoptionBadge.tsx +119 -0
- package/Application/KitAdoption/AdoptionProbe.tsx +166 -0
- package/Application/KitAdoption/AdoptionStore.ts +80 -0
- package/Application/KitAdoption/Authorship/utils.ts +137 -0
- package/Application/KitAdoption/Slots/utils.ts +68 -0
- package/Application/KitAdoption/constants.ts +120 -0
- package/Application/KitAdoption/index.ts +12 -0
- package/Application/KitAdoption/styles.ts +55 -0
- package/Application/KitAdoption/types.ts +61 -0
- package/Application/KitAdoption/utils.ts +376 -0
- package/Application/Localize.ts +0 -7
- package/Application/ModalScreen.tsx +10 -10
- package/Application/Navigation.ts +16 -14
- package/Application/NavigationContainer.tsx +18 -19
- package/Application/Navigator.ts +48 -20
- package/Application/ScaleSizeProvider.tsx +6 -13
- package/Application/StackScreen.tracking.ts +33 -11
- package/Application/StackScreen.tsx +54 -10
- package/Application/TooltipPortal.tsx +5 -12
- package/Application/WidgetContainer.tsx +4 -10
- package/Application/__tests__/KitAdoption.integration.test.tsx +253 -0
- package/Application/__tests__/KitAdoption.test.ts +283 -0
- package/Application/__tests__/KitAdoptionAuthorship.test.ts +132 -0
- package/Application/__tests__/KitAdoptionStore.test.ts +110 -0
- package/Application/__tests__/KitAdoptionStructural.test.ts +193 -0
- package/Application/__tests__/modalLayout.test.ts +128 -0
- package/Application/modalLayout.ts +14 -16
- package/Application/navigationOptions.tsx +17 -21
- package/Badge/Badge.tsx +0 -5
- package/Badge/BadgeDot.tsx +0 -1
- package/Badge/BadgeDotAnimation.tsx +7 -15
- package/Badge/BadgeRibbon.tsx +38 -46
- package/Badge/Shape.tsx +0 -2
- package/Badge/types.ts +2 -2
- package/Button/index.tsx +16 -5
- package/Button/utils.ts +15 -9
- package/CheckBox/index.tsx +0 -4
- package/Consts/theme.ts +10 -10
- package/Context/index.ts +1 -5
- package/Divider/DashDivider.tsx +0 -1
- package/Divider/index.tsx +0 -4
- package/FoundationList/index.tsx +0 -4
- package/Icon/index.tsx +6 -6
- package/Icon/types.ts +1 -1
- package/IconButton/index.tsx +12 -4
- package/Image/index.tsx +7 -9
- package/Input/Input.tsx +9 -8
- package/Input/InputDropDown.tsx +12 -10
- package/Input/InputMoney.tsx +8 -12
- package/Input/InputMoneyView.tsx +7 -14
- package/Input/InputOTP.tsx +33 -35
- package/Input/InputPhoneNumber.tsx +9 -8
- package/Input/InputSearch.tsx +6 -4
- package/Input/InputTextArea.tsx +0 -4
- package/Input/SearchTextTyping.tsx +0 -4
- package/Input/SystemTextInput.tsx +2 -7
- package/Input/TextTyping.tsx +0 -4
- package/Input/common.tsx +12 -26
- package/Input/index.tsx +20 -17
- package/Input/moneyUtils.ts +4 -10
- package/Input/styles.ts +5 -0
- package/Input/utils.ts +12 -14
- package/Layout/Card.tsx +4 -7
- package/Layout/FloatingButton.tsx +0 -4
- package/Layout/Item.tsx +2 -6
- package/Layout/ItemList.tsx +7 -9
- package/Layout/ItemSectionList.tsx +1 -2
- package/Layout/ReservedRegions.tsx +25 -25
- package/Layout/Screen.tsx +58 -56
- package/Layout/Section.tsx +5 -8
- package/Layout/TrackingScope.tsx +0 -1
- package/Layout/screenContent.tsx +2 -4
- package/Layout/screenFooter.tsx +18 -28
- package/Layout/screenHeader.tsx +31 -34
- package/Layout/screenQuickReturn.tsx +20 -17
- package/Layout/screenScroll.ts +7 -4
- package/Layout/screenTypes.ts +2 -3
- package/Layout/types.ts +4 -2
- package/Layout/utils.ts +37 -31
- package/Loader/DotLoader.tsx +0 -4
- package/Loader/Loader.tsx +0 -4
- package/Loader/ProgressBar.tsx +0 -1
- package/Loader/Spinner.tsx +0 -4
- package/Loader/utils.ts +7 -11
- package/Pagination/Dot.tsx +0 -4
- package/Pagination/PaginationDot.tsx +0 -1
- package/Pagination/PaginationNumber.tsx +3 -5
- package/Pagination/PaginationScroll.tsx +0 -5
- package/Pagination/PaginationWhiteDot.tsx +0 -1
- package/Pagination/index.tsx +0 -4
- package/Pagination/types.ts +2 -2
- package/Popup/PopupNotify.tsx +49 -45
- package/Popup/PopupPromotion.tsx +7 -4
- package/Popup/types.ts +4 -4
- package/Popup/utils.ts +4 -11
- package/Radio/index.tsx +0 -4
- package/Skeleton/index.tsx +0 -8
- package/Switch/index.tsx +0 -1
- package/Tag/index.tsx +1 -5
- package/Tag/types.ts +3 -3
- package/Text/index.tsx +3 -14
- package/Text/styles.ts +1 -1
- package/Text/types.ts +6 -1
- package/Text/utils.ts +4 -20
- package/Title/index.tsx +32 -41
- package/ViewBoundary/index.tsx +2 -4
- package/index.ts +6 -0
- package/package.json +1 -1
package/Input/InputMoneyView.tsx
CHANGED
|
@@ -52,10 +52,6 @@ type InputMoneyViewProps = Pick<
|
|
|
52
52
|
inputProps: TextInputProps;
|
|
53
53
|
};
|
|
54
54
|
|
|
55
|
-
/**
|
|
56
|
-
* Presentational body of InputMoney: bordered field, currency suffix (kept
|
|
57
|
-
* after a placeholder that ends with it) and the clear/trailing icons.
|
|
58
|
-
*/
|
|
59
55
|
export const InputMoneyView = ({
|
|
60
56
|
size,
|
|
61
57
|
errorMessage,
|
|
@@ -102,12 +98,9 @@ export const InputMoneyView = ({
|
|
|
102
98
|
fontFamily: inputFontFamily,
|
|
103
99
|
};
|
|
104
100
|
const placeholderHasCurrency = placeholder.endsWith(currency);
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
if (placeholderHasCurrency) {
|
|
109
|
-
inputPlaceholder = placeholder.slice(0, -currency.length);
|
|
110
|
-
}
|
|
101
|
+
const inputPlaceholder = placeholderHasCurrency
|
|
102
|
+
? placeholder.slice(0, -currency.length)
|
|
103
|
+
: placeholder;
|
|
111
104
|
return (
|
|
112
105
|
<View
|
|
113
106
|
style={[
|
|
@@ -154,7 +147,7 @@ export const InputMoneyView = ({
|
|
|
154
147
|
keyboardType={'numeric'}
|
|
155
148
|
maxLength={undefined}
|
|
156
149
|
/>
|
|
157
|
-
{
|
|
150
|
+
{displayValue || placeholderHasCurrency ? (
|
|
158
151
|
<Text
|
|
159
152
|
style={[
|
|
160
153
|
textStyle,
|
|
@@ -164,10 +157,10 @@ export const InputMoneyView = ({
|
|
|
164
157
|
>
|
|
165
158
|
{currency}
|
|
166
159
|
</Text>
|
|
167
|
-
)}
|
|
160
|
+
) : null}
|
|
168
161
|
</Pressable>
|
|
169
162
|
<View style={styles.iconView}>
|
|
170
|
-
{
|
|
163
|
+
{showClearIcon && focused && displayValue ? (
|
|
171
164
|
<TouchableOpacity
|
|
172
165
|
accessibilityLabel={`${componentId}|clear-icon-touch`}
|
|
173
166
|
style={styles.iconWrapper}
|
|
@@ -180,7 +173,7 @@ export const InputMoneyView = ({
|
|
|
180
173
|
accessibilityLabel={`${componentId}|clear-icon`}
|
|
181
174
|
/>
|
|
182
175
|
</TouchableOpacity>
|
|
183
|
-
)}
|
|
176
|
+
) : null}
|
|
184
177
|
<RenderTrailing
|
|
185
178
|
color={iconTintColor}
|
|
186
179
|
icon={trailing}
|
package/Input/InputOTP.tsx
CHANGED
|
@@ -38,7 +38,6 @@ import {
|
|
|
38
38
|
MiniAppContext,
|
|
39
39
|
} from '../Context';
|
|
40
40
|
|
|
41
|
-
/** Blinking caret shown in the OTP slot being typed. */
|
|
42
41
|
const OTPCaret: FC<CaretProps> = ({ index, length }) => {
|
|
43
42
|
const DURATION = 300;
|
|
44
43
|
const { theme } = useContext(ApplicationContext);
|
|
@@ -61,7 +60,9 @@ const OTPCaret: FC<CaretProps> = ({ index, length }) => {
|
|
|
61
60
|
};
|
|
62
61
|
}, [opacity]);
|
|
63
62
|
|
|
64
|
-
const animatedStyle = useAnimatedStyle(() => ({
|
|
63
|
+
const animatedStyle = useAnimatedStyle(() => ({
|
|
64
|
+
opacity: opacity.value,
|
|
65
|
+
}));
|
|
65
66
|
|
|
66
67
|
const hasNextInput = !isNaN(Number(length)) && index !== Number(length) - 1;
|
|
67
68
|
const spacingStyle = hasNextInput && { marginRight: Spacing.L };
|
|
@@ -85,10 +86,6 @@ const OTPCaret: FC<CaretProps> = ({ index, length }) => {
|
|
|
85
86
|
);
|
|
86
87
|
};
|
|
87
88
|
|
|
88
|
-
/**
|
|
89
|
-
* OTP input: a hidden TextInput drives `length` slots (or a free-length row
|
|
90
|
-
* when `length` is unset) with a blinking caret; accepts at most 10 chars.
|
|
91
|
-
*/
|
|
92
89
|
const InputOTP = forwardRef(
|
|
93
90
|
(
|
|
94
91
|
{
|
|
@@ -124,16 +121,14 @@ const InputOTP = forwardRef(
|
|
|
124
121
|
props.accessibilityLabel,
|
|
125
122
|
);
|
|
126
123
|
|
|
127
|
-
useImperativeHandle(ref, () => {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
};
|
|
136
|
-
});
|
|
124
|
+
useImperativeHandle(ref, () => ({
|
|
125
|
+
onChangeText: (text: string) => {
|
|
126
|
+
_onChangeText(text);
|
|
127
|
+
},
|
|
128
|
+
focus: () => inputRef.current?.focus(),
|
|
129
|
+
blur: () => inputRef.current?.blur(),
|
|
130
|
+
setText: (text: string) => _onChangeText(text),
|
|
131
|
+
}));
|
|
137
132
|
|
|
138
133
|
const onFocusInput = (e: TextInputFocusEvent) => {
|
|
139
134
|
setFocused(true);
|
|
@@ -147,10 +142,13 @@ const InputOTP = forwardRef(
|
|
|
147
142
|
|
|
148
143
|
const _onChangeText = (text: string) => {
|
|
149
144
|
if (text.length > MAX_LENGTH) return;
|
|
150
|
-
|
|
151
|
-
if (
|
|
152
|
-
|
|
153
|
-
|
|
145
|
+
if (dataType === 'number' && isNaN(Number(text))) return;
|
|
146
|
+
if (
|
|
147
|
+
!isNaN(Number(text)) &&
|
|
148
|
+
value.length >= Number(length) &&
|
|
149
|
+
text.length >= value.length
|
|
150
|
+
)
|
|
151
|
+
return;
|
|
154
152
|
|
|
155
153
|
setValue(text);
|
|
156
154
|
onChangeText?.(text);
|
|
@@ -166,21 +164,21 @@ const InputOTP = forwardRef(
|
|
|
166
164
|
const TextInputs: React.ReactNode[] = [];
|
|
167
165
|
const textColors = theme.colors.text;
|
|
168
166
|
for (let i = 0; i < inputLength; i++) {
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
167
|
+
TextInputs.push(
|
|
168
|
+
<View key={`otp-input-${i}`}>
|
|
169
|
+
{focused && value.length === i ? (
|
|
170
|
+
<OTPCaret index={i} length={inputLength} />
|
|
171
|
+
) : (
|
|
172
|
+
<Text
|
|
173
|
+
style={i !== inputLength - 1 && { marginRight: Spacing.L }}
|
|
174
|
+
color={value[i] ? textColors.default : textColors.hint}
|
|
175
|
+
typography={value[i] ? 'header_m_bold' : 'body_default_regular'}
|
|
176
|
+
>
|
|
177
|
+
{value[i] || '-'}
|
|
178
|
+
</Text>
|
|
179
|
+
)}
|
|
180
|
+
</View>,
|
|
181
|
+
);
|
|
184
182
|
}
|
|
185
183
|
return TextInputs;
|
|
186
184
|
};
|
|
@@ -34,8 +34,7 @@ import { Image } from '../Image';
|
|
|
34
34
|
import { Typography } from '../Text/types';
|
|
35
35
|
|
|
36
36
|
/**
|
|
37
|
-
*
|
|
38
|
-
* by default, plus clear and trailing icons.
|
|
37
|
+
* Input default component
|
|
39
38
|
*/
|
|
40
39
|
const InputPhoneNumber = forwardRef(
|
|
41
40
|
(
|
|
@@ -147,6 +146,9 @@ const InputPhoneNumber = forwardRef(
|
|
|
147
146
|
typography = 'header_m_bold';
|
|
148
147
|
}
|
|
149
148
|
|
|
149
|
+
/**
|
|
150
|
+
* Render input view
|
|
151
|
+
*/
|
|
150
152
|
const renderInputView = () => {
|
|
151
153
|
const disabledColor = theme.colors.text.disable;
|
|
152
154
|
const secure = secureTextEntry;
|
|
@@ -154,11 +156,6 @@ const InputPhoneNumber = forwardRef(
|
|
|
154
156
|
let placeholderColor = theme.colors.text.hint;
|
|
155
157
|
let iconTintColor = trailingColor ?? theme.colors.text.default;
|
|
156
158
|
const borderWidth = focused ? 1.5 : 1;
|
|
157
|
-
let secureTextStyle = styles.secureTextLarge;
|
|
158
|
-
|
|
159
|
-
if (size === 'small') {
|
|
160
|
-
secureTextStyle = styles.secureTextSmall;
|
|
161
|
-
}
|
|
162
159
|
|
|
163
160
|
if (disabled) {
|
|
164
161
|
textColor = disabledColor;
|
|
@@ -217,7 +214,11 @@ const InputPhoneNumber = forwardRef(
|
|
|
217
214
|
color: textColor,
|
|
218
215
|
},
|
|
219
216
|
inputTextStyles,
|
|
220
|
-
secure &&
|
|
217
|
+
secure &&
|
|
218
|
+
haveValue &&
|
|
219
|
+
(size === 'small'
|
|
220
|
+
? styles.secureTextSmall
|
|
221
|
+
: styles.secureTextLarge),
|
|
221
222
|
]}
|
|
222
223
|
defaultValue={defaultValue}
|
|
223
224
|
allowFontScaling={false}
|
package/Input/InputSearch.tsx
CHANGED
|
@@ -26,10 +26,6 @@ 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
|
-
*/
|
|
33
29
|
const InputSearch: ForwardRefRenderFunction<InputRef, InputSearchProps> = (
|
|
34
30
|
{
|
|
35
31
|
placeholder,
|
|
@@ -110,6 +106,9 @@ const InputSearch: ForwardRefRenderFunction<InputRef, InputSearchProps> = (
|
|
|
110
106
|
};
|
|
111
107
|
});
|
|
112
108
|
|
|
109
|
+
/**
|
|
110
|
+
* Render the input view
|
|
111
|
+
*/
|
|
113
112
|
const renderInputView = () => {
|
|
114
113
|
if (typingData && typingData?.length > 0) {
|
|
115
114
|
return (
|
|
@@ -150,6 +149,9 @@ const InputSearch: ForwardRefRenderFunction<InputRef, InputSearchProps> = (
|
|
|
150
149
|
);
|
|
151
150
|
};
|
|
152
151
|
|
|
152
|
+
/**
|
|
153
|
+
* Render trailing icon or text
|
|
154
|
+
*/
|
|
153
155
|
const renderTrailing = () => {
|
|
154
156
|
return (
|
|
155
157
|
<View style={Styles.row}>
|
package/Input/InputTextArea.tsx
CHANGED
|
@@ -21,10 +21,6 @@ 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
|
-
*/
|
|
28
24
|
const InputTextArea = forwardRef(
|
|
29
25
|
(
|
|
30
26
|
{
|
|
@@ -4,10 +4,6 @@ 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
|
-
*/
|
|
11
7
|
export const TextTyping: FC<any> = ({
|
|
12
8
|
data = [],
|
|
13
9
|
timeDelayEnd = 2000,
|
|
@@ -11,16 +11,11 @@ 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
|
-
*/
|
|
18
14
|
const SystemTextInput: FC<SystemTextInputProps> = props => {
|
|
19
15
|
const { editable, onPressIn, onPressOut, ref } = props;
|
|
20
16
|
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
const needsAndroidWrapper = isAndroidReadOnly && hasPressHandler;
|
|
17
|
+
const needsAndroidWrapper =
|
|
18
|
+
Platform.OS === 'android' && !editable && (onPressIn || onPressOut);
|
|
24
19
|
|
|
25
20
|
const noPressHandlers = { onPressIn: undefined, onPressOut: undefined };
|
|
26
21
|
const inputProps: TextInputProps = {
|
package/Input/TextTyping.tsx
CHANGED
|
@@ -11,10 +11,6 @@ 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
|
-
*/
|
|
18
14
|
const TextTyping: FC<TextTypingProps> = ({
|
|
19
15
|
data = [],
|
|
20
16
|
timeDelayEnd = 2000,
|
package/Input/common.tsx
CHANGED
|
@@ -35,7 +35,6 @@ type TrailingProps = {
|
|
|
35
35
|
|
|
36
36
|
export const MAX_LENGTH = 300;
|
|
37
37
|
|
|
38
|
-
/** Border color by state; priority is disabled > error > focused > default. */
|
|
39
38
|
export const getBorderColor = (
|
|
40
39
|
theme: Theme,
|
|
41
40
|
focused: boolean,
|
|
@@ -59,10 +58,6 @@ export const getBorderColor = (
|
|
|
59
58
|
return { borderColor };
|
|
60
59
|
};
|
|
61
60
|
|
|
62
|
-
/**
|
|
63
|
-
* Container style for `size`; multiline uses `height` as minHeight so the
|
|
64
|
-
* field can grow with its content.
|
|
65
|
-
*/
|
|
66
61
|
export const getSizeStyle = (
|
|
67
62
|
size?: 'small' | 'large',
|
|
68
63
|
multiline: boolean = false,
|
|
@@ -73,14 +68,13 @@ export const getSizeStyle = (
|
|
|
73
68
|
scaleStyle = { height };
|
|
74
69
|
}
|
|
75
70
|
|
|
76
|
-
if (multiline)
|
|
71
|
+
if (multiline)
|
|
77
72
|
return [
|
|
78
73
|
styles.multilineContainer,
|
|
79
74
|
{
|
|
80
75
|
minHeight: height,
|
|
81
76
|
},
|
|
82
77
|
];
|
|
83
|
-
}
|
|
84
78
|
|
|
85
79
|
if (size === 'small') {
|
|
86
80
|
return [styles.smallContainer, scaleStyle];
|
|
@@ -88,10 +82,6 @@ export const getSizeStyle = (
|
|
|
88
82
|
return [styles.container, scaleStyle];
|
|
89
83
|
};
|
|
90
84
|
|
|
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
|
-
*/
|
|
95
85
|
export const ErrorView: FC<{
|
|
96
86
|
errorMessage?: string;
|
|
97
87
|
errorSpacing?: boolean;
|
|
@@ -105,17 +95,21 @@ export const ErrorView: FC<{
|
|
|
105
95
|
const scaledIconSize = useScaleSize(16);
|
|
106
96
|
|
|
107
97
|
if (errorMessage || hintText) {
|
|
108
|
-
const messageColor = errorMessage ? errorColor : hintColor;
|
|
109
|
-
const messageType = errorMessage ? 'error' : 'hint';
|
|
110
98
|
return (
|
|
111
99
|
<View style={styles.errorView}>
|
|
112
100
|
<View style={styles.errorIcon}>
|
|
113
|
-
<Icon
|
|
101
|
+
<Icon
|
|
102
|
+
color={errorMessage ? errorColor : hintColor}
|
|
103
|
+
source="ic_error"
|
|
104
|
+
size={scaledIconSize}
|
|
105
|
+
/>
|
|
114
106
|
</View>
|
|
115
107
|
<Text
|
|
116
|
-
accessibilityLabel={`${componentId}|${
|
|
108
|
+
accessibilityLabel={`${componentId}|${
|
|
109
|
+
errorMessage ? 'error' : 'hint'
|
|
110
|
+
}-text`}
|
|
117
111
|
style={Styles.flex}
|
|
118
|
-
color={
|
|
112
|
+
color={errorMessage ? errorColor : hintColor}
|
|
119
113
|
typography={'description_default_regular'}
|
|
120
114
|
>
|
|
121
115
|
{errorMessage ?? hintTextDefault}
|
|
@@ -129,10 +123,6 @@ export const ErrorView: FC<{
|
|
|
129
123
|
return <View />;
|
|
130
124
|
};
|
|
131
125
|
|
|
132
|
-
/**
|
|
133
|
-
* Floating label on the input's top border with a required `*` and an
|
|
134
|
-
* optional pressable icon; renders nothing without `floatingValue`.
|
|
135
|
-
*/
|
|
136
126
|
export const FloatingView: FC<FloatingViewProps> = ({
|
|
137
127
|
floatingValue,
|
|
138
128
|
floatingIconColor,
|
|
@@ -201,10 +191,6 @@ export const FloatingView: FC<FloatingViewProps> = ({
|
|
|
201
191
|
return null;
|
|
202
192
|
};
|
|
203
193
|
|
|
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
|
-
*/
|
|
208
194
|
export const RenderTrailing: FC<TrailingProps> = ({
|
|
209
195
|
loading,
|
|
210
196
|
color,
|
|
@@ -218,8 +204,8 @@ export const RenderTrailing: FC<TrailingProps> = ({
|
|
|
218
204
|
}
|
|
219
205
|
|
|
220
206
|
if (icon) {
|
|
221
|
-
const
|
|
222
|
-
|
|
207
|
+
const isIcon =
|
|
208
|
+
(IconSources as any)?.[icon as string]?.uri || icon?.includes('http');
|
|
223
209
|
const renderIconTouchable = (content: ReactNode) => {
|
|
224
210
|
return (
|
|
225
211
|
<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. Represents the name or key of the icon to be displayed in the Input component.
|
|
40
40
|
*/
|
|
41
41
|
trailing?: string;
|
|
42
42
|
|
|
@@ -69,18 +69,19 @@ export interface InputProps extends TextInputProps {
|
|
|
69
69
|
required?: boolean;
|
|
70
70
|
|
|
71
71
|
/**
|
|
72
|
-
* Optional. If `true`,
|
|
72
|
+
* Optional. If `true`,
|
|
73
|
+
* include spacing when Input not have error message.
|
|
73
74
|
* Defaults to `false` if not provided.
|
|
74
75
|
*/
|
|
75
76
|
errorSpacing?: boolean;
|
|
76
77
|
|
|
77
78
|
/**
|
|
78
|
-
*
|
|
79
|
+
* If true, the icon of input will use be show indicator loading.
|
|
79
80
|
*/
|
|
80
81
|
loading?: boolean;
|
|
81
82
|
|
|
82
83
|
/**
|
|
83
|
-
*
|
|
84
|
+
* Optional. Represents the leading icon in the Input component.
|
|
84
85
|
*/
|
|
85
86
|
leadingIcon?: string;
|
|
86
87
|
|
|
@@ -110,13 +111,12 @@ export interface InputProps extends TextInputProps {
|
|
|
110
111
|
hintText?: string;
|
|
111
112
|
|
|
112
113
|
/**
|
|
113
|
-
* Optional.
|
|
114
|
+
* Optional. Represents the style of the Input component.
|
|
114
115
|
*/
|
|
115
116
|
onPressFloatingIcon?: () => void;
|
|
116
117
|
|
|
117
118
|
/**
|
|
118
|
-
* Optional.
|
|
119
|
-
* Defaults to `true` if not provided.
|
|
119
|
+
* Optional. Represents the style of the Input component.
|
|
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 `false` if not provided.
|
|
141
141
|
*/
|
|
142
142
|
showButtonText?: boolean;
|
|
143
143
|
|
|
144
144
|
/**
|
|
145
|
-
* Optional.
|
|
145
|
+
* Optional. Represents the value for the floating title in the InputSearch component.
|
|
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. If `true`, the user won't be able to interact with the Input component.
|
|
184
184
|
*/
|
|
185
185
|
backgroundColor?: string;
|
|
186
186
|
|
|
@@ -195,7 +195,8 @@ export interface InputSearchProps extends TextInputProps {
|
|
|
195
195
|
typingData?: string[];
|
|
196
196
|
|
|
197
197
|
/**
|
|
198
|
-
* Optional.
|
|
198
|
+
* Optional. Represents the callback function to be called when the search button is pressed.
|
|
199
|
+
* @param e
|
|
199
200
|
*/
|
|
200
201
|
onPress?: (e: GestureResponderEvent) => void;
|
|
201
202
|
}
|
|
@@ -258,7 +259,7 @@ export interface InputDropDownProps extends TouchableOpacityProps {
|
|
|
258
259
|
placeholder?: string;
|
|
259
260
|
|
|
260
261
|
/**
|
|
261
|
-
* Optional.
|
|
262
|
+
* Optional. Represents the data for the dropdown.
|
|
262
263
|
*/
|
|
263
264
|
multiline?: boolean;
|
|
264
265
|
|
|
@@ -302,18 +303,19 @@ export interface InputDropDownProps extends TouchableOpacityProps {
|
|
|
302
303
|
required?: boolean;
|
|
303
304
|
|
|
304
305
|
/**
|
|
305
|
-
* Optional. If `true`,
|
|
306
|
+
* Optional. If `true`,
|
|
307
|
+
* include spacing when Input not have error message.
|
|
306
308
|
* Defaults to `false` if not provided.
|
|
307
309
|
*/
|
|
308
310
|
errorSpacing?: boolean;
|
|
309
311
|
|
|
310
312
|
/**
|
|
311
|
-
*
|
|
313
|
+
* If true, the icon of input will use be show indicator loading.
|
|
312
314
|
*/
|
|
313
315
|
loading?: boolean;
|
|
314
316
|
|
|
315
317
|
/**
|
|
316
|
-
*
|
|
318
|
+
* Optional. Represents the leading icon in the Input component.
|
|
317
319
|
*/
|
|
318
320
|
leadingIcon?: string;
|
|
319
321
|
|
|
@@ -338,14 +340,15 @@ export interface InputDropDownProps extends TouchableOpacityProps {
|
|
|
338
340
|
hintText?: string;
|
|
339
341
|
|
|
340
342
|
/**
|
|
341
|
-
* Optional.
|
|
343
|
+
* Optional. Represents the style of the Input component.
|
|
342
344
|
*/
|
|
343
345
|
onPressFloatingIcon?: () => void;
|
|
344
346
|
}
|
|
345
347
|
|
|
346
348
|
export interface InputPhoneNumberProps extends InputProps {
|
|
347
349
|
/**
|
|
348
|
-
* Optional.
|
|
350
|
+
* Optional. Represents the country code to be displayed in the InputPhoneNumber component.
|
|
351
|
+
* It should be in the format of a string, e.g., "+1" for the United States.
|
|
349
352
|
*/
|
|
350
353
|
countryCode?: string;
|
|
351
354
|
}
|
package/Input/moneyUtils.ts
CHANGED
|
@@ -1,21 +1,15 @@
|
|
|
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
|
-
*/
|
|
8
3
|
export const findEdit = (old: string, text: string) => {
|
|
9
4
|
let start = 0;
|
|
10
5
|
while (start < text.length && old[start] === text[start]) {
|
|
11
6
|
start += 1;
|
|
12
7
|
}
|
|
13
|
-
const maxSuffix = Math.min(text.length, old.length) - start;
|
|
14
8
|
let suffix = 0;
|
|
15
|
-
while (
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
9
|
+
while (
|
|
10
|
+
suffix < Math.min(text.length, old.length) - start &&
|
|
11
|
+
old[old.length - 1 - suffix] === text[text.length - 1 - suffix]
|
|
12
|
+
) {
|
|
19
13
|
suffix += 1;
|
|
20
14
|
}
|
|
21
15
|
const size = old.length + text.length - 2 * (start + suffix);
|
package/Input/styles.ts
CHANGED
|
@@ -2,6 +2,7 @@ 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
|
|
5
6
|
input: {
|
|
6
7
|
flex: 1,
|
|
7
8
|
paddingLeft: Spacing.M,
|
|
@@ -64,6 +65,7 @@ export default StyleSheet.create({
|
|
|
64
65
|
marginTop: Spacing.S,
|
|
65
66
|
},
|
|
66
67
|
|
|
68
|
+
//text area style
|
|
67
69
|
textAreaView: {
|
|
68
70
|
flex: 1,
|
|
69
71
|
},
|
|
@@ -92,6 +94,7 @@ export default StyleSheet.create({
|
|
|
92
94
|
marginBottom: Spacing.XS,
|
|
93
95
|
},
|
|
94
96
|
|
|
97
|
+
//searchInput
|
|
95
98
|
searchInputWrapper: {
|
|
96
99
|
flexDirection: 'row',
|
|
97
100
|
borderRadius: Radius.XL,
|
|
@@ -133,6 +136,7 @@ export default StyleSheet.create({
|
|
|
133
136
|
ios: 36,
|
|
134
137
|
}),
|
|
135
138
|
},
|
|
139
|
+
//OTP
|
|
136
140
|
otpWrapper: {
|
|
137
141
|
width: '100%',
|
|
138
142
|
marginTop: Spacing.M,
|
|
@@ -179,6 +183,7 @@ export default StyleSheet.create({
|
|
|
179
183
|
opacity: 0.02,
|
|
180
184
|
letterSpacing: Spacing.XXL,
|
|
181
185
|
},
|
|
186
|
+
//DropDown
|
|
182
187
|
inputDropDownWrapper: {
|
|
183
188
|
flexDirection: 'row',
|
|
184
189
|
marginTop: Spacing.S,
|
package/Input/utils.ts
CHANGED
|
@@ -1,9 +1,5 @@
|
|
|
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
|
-
*/
|
|
7
3
|
const formatNumberToMoney = (number: number, currency = '') => {
|
|
8
4
|
if (!number || isNaN(number) || Number(number) === 0) {
|
|
9
5
|
return `0${currency}`;
|
|
@@ -45,10 +41,6 @@ const formatNumberToMoney = (number: number, currency = '') => {
|
|
|
45
41
|
return isNegative ? `-${result}${currency}` : `${result}${currency}`;
|
|
46
42
|
};
|
|
47
43
|
|
|
48
|
-
/**
|
|
49
|
-
* Parses a formatted money string back to a number by stripping
|
|
50
|
-
* `currencyUnit`, separators, `đ` and spaces; unparsable text gives 0.
|
|
51
|
-
*/
|
|
52
44
|
const formatMoneyToNumber = (money: string, currencyUnit: string) => {
|
|
53
45
|
if (money && money.length > 0) {
|
|
54
46
|
const moneyString = money
|
|
@@ -67,20 +59,26 @@ const formatMoneyToNumber = (money: string, currencyUnit: string) => {
|
|
|
67
59
|
return Number(money);
|
|
68
60
|
};
|
|
69
61
|
|
|
62
|
+
// --- PHONE VALIDATION ---
|
|
70
63
|
function formatPhoneNumber(phone: string): string {
|
|
71
64
|
return phone.replace(/\D/g, '');
|
|
72
65
|
}
|
|
73
66
|
|
|
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
|
-
*/
|
|
78
67
|
function checkValidPhoneNumber(phone: string): {
|
|
79
68
|
phoneFormatted: string;
|
|
80
69
|
error?: string;
|
|
81
70
|
} {
|
|
82
|
-
|
|
83
|
-
|
|
71
|
+
let phoneNumber = phone;
|
|
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
|
+
}
|
|
84
82
|
|
|
85
83
|
const phoneFormatted = formatPhoneNumber(phoneNumber);
|
|
86
84
|
const localize = new Localize({ vi: {}, en: {} });
|
package/Layout/Card.tsx
CHANGED
|
@@ -20,10 +20,6 @@ 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
|
-
*/
|
|
27
23
|
const Card: React.FC<CardProps> = ({
|
|
28
24
|
children,
|
|
29
25
|
style,
|
|
@@ -38,9 +34,10 @@ const Card: React.FC<CardProps> = ({
|
|
|
38
34
|
const gutterSize = 8;
|
|
39
35
|
const margin = 12;
|
|
40
36
|
const widthSection = contentWidth - margin * 2;
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
37
|
+
let sizePerSpan =
|
|
38
|
+
(widthSection - margin * 2 - gutterSize * (numberOfColumns - 1)) /
|
|
39
|
+
numberOfColumns -
|
|
40
|
+
1 / numberOfColumns;
|
|
44
41
|
|
|
45
42
|
const gridContext: GridContextProps = {
|
|
46
43
|
numberOfColumns,
|