@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
package/Button/index.tsx
CHANGED
|
@@ -63,6 +63,11 @@ export interface ButtonProps extends TouchableOpacityProps {
|
|
|
63
63
|
params?: any;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
/**
|
|
67
|
+
* Design-system button: `type` sets the colors, `size` the height and text,
|
|
68
|
+
* and `loading` swaps an icon slot for a spinner. Primary buttons use the mini
|
|
69
|
+
* app gradient from `config.navigationBar.buttonColors` when set.
|
|
70
|
+
*/
|
|
66
71
|
const Button: FC<ButtonProps> = ({
|
|
67
72
|
type = 'primary',
|
|
68
73
|
size = 'large',
|
|
@@ -90,33 +95,21 @@ const Button: FC<ButtonProps> = ({
|
|
|
90
95
|
rest.accessibilityLabel,
|
|
91
96
|
);
|
|
92
97
|
|
|
93
|
-
/**
|
|
94
|
-
* animate press in (Reanimated)
|
|
95
|
-
*/
|
|
96
98
|
const animateIn = (event: GestureResponderEvent) => {
|
|
97
99
|
rest?.onPressIn?.(event);
|
|
98
100
|
pressAnim.value = withTiming(1, { duration: 100 });
|
|
99
101
|
};
|
|
100
102
|
|
|
101
|
-
/**
|
|
102
|
-
* animate press out (Reanimated)
|
|
103
|
-
*/
|
|
104
103
|
const animateOut = (event: GestureResponderEvent) => {
|
|
105
104
|
rest?.onPressOut?.(event);
|
|
106
105
|
pressAnim.value = withTiming(0, { duration: 100 });
|
|
107
106
|
};
|
|
108
107
|
|
|
109
|
-
/**
|
|
110
|
-
* export size style
|
|
111
|
-
*/
|
|
112
108
|
const getSizeStyle = () => {
|
|
113
109
|
const styleSheet: { [key: string]: any } = styles;
|
|
114
110
|
return styleSheet[size ?? 'small'];
|
|
115
111
|
};
|
|
116
112
|
|
|
117
|
-
/**
|
|
118
|
-
* render title
|
|
119
|
-
*/
|
|
120
113
|
const renderTitle = () => {
|
|
121
114
|
const typography = getTypography(size);
|
|
122
115
|
const textColor = getTextColor(type, theme, color);
|
|
@@ -127,10 +120,6 @@ const Button: FC<ButtonProps> = ({
|
|
|
127
120
|
);
|
|
128
121
|
};
|
|
129
122
|
|
|
130
|
-
/**
|
|
131
|
-
* render icon
|
|
132
|
-
* @param position
|
|
133
|
-
*/
|
|
134
123
|
const renderIcon = (position: 'left' | 'right') => {
|
|
135
124
|
const iconSize = getIconSize(size);
|
|
136
125
|
const space = getIconSpace(size);
|
package/Button/utils.ts
CHANGED
|
@@ -4,10 +4,8 @@ import { Typography } from '../Text/types';
|
|
|
4
4
|
import { ButtonProps } from './index';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* @param right
|
|
10
|
-
* @param isLeft
|
|
7
|
+
* Whether the loading spinner belongs in the `isLeft` icon slot: it takes the
|
|
8
|
+
* right slot when there is a right icon, otherwise the left one.
|
|
11
9
|
*/
|
|
12
10
|
export const shouldShowLoading = (
|
|
13
11
|
left?: string,
|
|
@@ -23,7 +21,8 @@ export const shouldShowLoading = (
|
|
|
23
21
|
};
|
|
24
22
|
|
|
25
23
|
/**
|
|
26
|
-
*
|
|
24
|
+
* Background and border of a button `type`; `color` (mini app button color)
|
|
25
|
+
* overrides the outline border.
|
|
27
26
|
*/
|
|
28
27
|
export const getTypeStyle = (
|
|
29
28
|
type: ButtonProps['type'],
|
|
@@ -64,9 +63,7 @@ export const getTypeStyle = (
|
|
|
64
63
|
}
|
|
65
64
|
};
|
|
66
65
|
|
|
67
|
-
/**
|
|
68
|
-
* export icon size
|
|
69
|
-
*/
|
|
66
|
+
/** Icon size for a button `size`. */
|
|
70
67
|
export const getIconSize = (size: ButtonProps['size']) => {
|
|
71
68
|
switch (size) {
|
|
72
69
|
case 'large':
|
|
@@ -79,9 +76,7 @@ export const getIconSize = (size: ButtonProps['size']) => {
|
|
|
79
76
|
}
|
|
80
77
|
};
|
|
81
78
|
|
|
82
|
-
/**
|
|
83
|
-
* export icon space
|
|
84
|
-
*/
|
|
79
|
+
/** Gap between icon and title for a button `size`. */
|
|
85
80
|
export const getIconSpace = (size: ButtonProps['size']) => {
|
|
86
81
|
switch (size) {
|
|
87
82
|
case 'large':
|
|
@@ -94,9 +89,7 @@ export const getIconSpace = (size: ButtonProps['size']) => {
|
|
|
94
89
|
}
|
|
95
90
|
};
|
|
96
91
|
|
|
97
|
-
/**
|
|
98
|
-
* export typography style
|
|
99
|
-
*/
|
|
92
|
+
/** Title typography for a button `size`. */
|
|
100
93
|
export const getTypography = (size: ButtonProps['size']): Typography => {
|
|
101
94
|
switch (size) {
|
|
102
95
|
case 'large':
|
|
@@ -111,7 +104,8 @@ export const getTypography = (size: ButtonProps['size']): Typography => {
|
|
|
111
104
|
};
|
|
112
105
|
|
|
113
106
|
/**
|
|
114
|
-
*
|
|
107
|
+
* Title and icon color of a button `type`; `color` (mini app button color)
|
|
108
|
+
* overrides the outline and text types.
|
|
115
109
|
*/
|
|
116
110
|
export const getTextColor = (
|
|
117
111
|
type: ButtonProps['type'],
|
package/CheckBox/index.tsx
CHANGED
|
@@ -11,6 +11,10 @@ import {
|
|
|
11
11
|
import { Text } from '../Text';
|
|
12
12
|
import { Icon } from '../Icon';
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Checkbox with an optional label. `indeterminate` shows a minus icon and is
|
|
16
|
+
* styled and announced as checked.
|
|
17
|
+
*/
|
|
14
18
|
const CheckBox: FC<CheckBoxProps> = ({
|
|
15
19
|
value,
|
|
16
20
|
disabled = false,
|
package/Consts/theme.ts
CHANGED
|
@@ -62,22 +62,22 @@ const defaultDarkTheme: Theme = {
|
|
|
62
62
|
secondary: Colors.pink_08,
|
|
63
63
|
gradient: '#FDCADE',
|
|
64
64
|
background: {
|
|
65
|
-
default: '#121212',
|
|
66
|
-
surface: '#1e1e1e',
|
|
65
|
+
default: '#121212',
|
|
66
|
+
surface: '#1e1e1e',
|
|
67
67
|
tonal: Colors.pink_10,
|
|
68
|
-
pressed: '#1a1a1a',
|
|
68
|
+
pressed: '#1a1a1a',
|
|
69
69
|
selected: Colors.pink_11,
|
|
70
|
-
disable: '#303030',
|
|
70
|
+
disable: '#303030',
|
|
71
71
|
},
|
|
72
72
|
text: {
|
|
73
|
-
default: '#ffffff',
|
|
74
|
-
secondary: '#b0b0b0',
|
|
75
|
-
hint: '#727272',
|
|
76
|
-
disable: '#505050',
|
|
73
|
+
default: '#ffffff',
|
|
74
|
+
secondary: '#b0b0b0',
|
|
75
|
+
hint: '#727272',
|
|
76
|
+
disable: '#505050',
|
|
77
77
|
},
|
|
78
78
|
border: {
|
|
79
|
-
default: '#2a2a2a',
|
|
80
|
-
disable: '#242424',
|
|
79
|
+
default: '#2a2a2a',
|
|
80
|
+
disable: '#242424',
|
|
81
81
|
},
|
|
82
82
|
success: {
|
|
83
83
|
primary: Colors.green_03,
|
package/Context/index.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { defaultContext } from '../Consts';
|
|
|
5
5
|
const Context = createContext({});
|
|
6
6
|
const ApplicationContext = createContext(defaultContext);
|
|
7
7
|
|
|
8
|
+
// Shared by the host app via `Platform`; standalone falls back to a local one.
|
|
8
9
|
const MiniAppContext = (Platform as any).MiniAppContext ?? Context;
|
|
9
10
|
const ScreenContext = (Platform as any).ScreenContext ?? Context;
|
|
10
11
|
const ComponentContext = (Platform as any).ComponentContext ?? Context;
|
package/Divider/DashDivider.tsx
CHANGED
|
@@ -11,6 +11,7 @@ export interface DashDividerProps {
|
|
|
11
11
|
style?: ViewStyle;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
/** 1px dashed horizontal line in the default border color. */
|
|
14
15
|
const DashDivider: React.FC<DashDividerProps> = ({ style }) => {
|
|
15
16
|
const { theme } = useContext(ApplicationContext);
|
|
16
17
|
const borderColor = theme.colors.border.default;
|
package/Divider/index.tsx
CHANGED
|
@@ -18,6 +18,10 @@ export interface DividerProps {
|
|
|
18
18
|
type?: 'dash' | 'default';
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
/**
|
|
22
|
+
* 1px horizontal line in the default border color. `type='dash'` renders
|
|
23
|
+
* `DashDivider`, which always has a 4px vertical margin.
|
|
24
|
+
*/
|
|
21
25
|
const Divider: React.FC<DividerProps> = ({
|
|
22
26
|
style,
|
|
23
27
|
useMargin = true,
|
package/FoundationList/index.tsx
CHANGED
|
@@ -5,6 +5,10 @@ import { FlatListProps } from 'react-native';
|
|
|
5
5
|
type CombinedListProps<ItemT> = FlatListProps<ItemT> &
|
|
6
6
|
Partial<FlashListProps<ItemT>>;
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
* FlashList typed to also accept FlatList props, so FlatList call sites can
|
|
10
|
+
* switch to it without type changes.
|
|
11
|
+
*/
|
|
8
12
|
const FoundationList = <ItemT,>(props: CombinedListProps<ItemT>) => {
|
|
9
13
|
return <FlashList {...props} />;
|
|
10
14
|
};
|
package/Icon/index.tsx
CHANGED
|
@@ -7,6 +7,10 @@ import { Image } from '../Image';
|
|
|
7
7
|
import { StyleSheet } from 'react-native';
|
|
8
8
|
import { Colors } from '../Consts';
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Tinted icon from the bundled icon map (unknown names fall back to
|
|
12
|
+
* `ic_warning`) or from a URL. `color={null}` keeps the image's own colors.
|
|
13
|
+
*/
|
|
10
14
|
const Icon: React.FC<IconProps> = ({
|
|
11
15
|
source = 'ic_back',
|
|
12
16
|
size = 24,
|
|
@@ -19,9 +23,6 @@ const Icon: React.FC<IconProps> = ({
|
|
|
19
23
|
|
|
20
24
|
const showBaseLineDebug = context?.features?.showBaseLineDebug ?? false;
|
|
21
25
|
|
|
22
|
-
/**
|
|
23
|
-
* get icon source maps or remote http
|
|
24
|
-
*/
|
|
25
26
|
const getIconSource = (): any => {
|
|
26
27
|
if (source && !source.includes('http')) {
|
|
27
28
|
let icon: { [key: string]: { uri: string } } = IconSources;
|
|
@@ -30,9 +31,8 @@ const Icon: React.FC<IconProps> = ({
|
|
|
30
31
|
return { uri: source };
|
|
31
32
|
};
|
|
32
33
|
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
};
|
|
34
|
+
const tintColor = color ?? theme.colors.text.default;
|
|
35
|
+
const tintColorProps = color !== null && { tintColor };
|
|
36
36
|
|
|
37
37
|
return (
|
|
38
38
|
<Image
|
package/Icon/types.ts
CHANGED
package/IconButton/index.tsx
CHANGED
|
@@ -41,6 +41,10 @@ export interface IconButtonProps extends TouchableOpacityProps {
|
|
|
41
41
|
params?: any;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
/**
|
|
45
|
+
* Icon-only button. `type` sets the background and icon color; `'disabled'`
|
|
46
|
+
* also blocks presses.
|
|
47
|
+
*/
|
|
44
48
|
const IconButton: React.FC<IconButtonProps> = ({
|
|
45
49
|
type = 'primary',
|
|
46
50
|
icon,
|
|
@@ -54,9 +58,6 @@ const IconButton: React.FC<IconButtonProps> = ({
|
|
|
54
58
|
|
|
55
59
|
const showBaseLineDebug = context?.features?.showBaseLineDebug ?? false;
|
|
56
60
|
|
|
57
|
-
/**
|
|
58
|
-
* get size icon button
|
|
59
|
-
*/
|
|
60
61
|
const getSizeStyle = () => {
|
|
61
62
|
switch (size) {
|
|
62
63
|
case 'small':
|
|
@@ -68,16 +69,10 @@ const IconButton: React.FC<IconButtonProps> = ({
|
|
|
68
69
|
}
|
|
69
70
|
};
|
|
70
71
|
|
|
71
|
-
/**
|
|
72
|
-
* get shape icon button
|
|
73
|
-
*/
|
|
74
72
|
const getShapeStyle = () => {
|
|
75
73
|
return shape === 'square' ? styles.square : styles.circle;
|
|
76
74
|
};
|
|
77
75
|
|
|
78
|
-
/**
|
|
79
|
-
* get style for icon button by type
|
|
80
|
-
*/
|
|
81
76
|
const getTypeStyle = () => {
|
|
82
77
|
switch (type) {
|
|
83
78
|
case 'disabled':
|
|
@@ -111,9 +106,6 @@ const IconButton: React.FC<IconButtonProps> = ({
|
|
|
111
106
|
}
|
|
112
107
|
};
|
|
113
108
|
|
|
114
|
-
/**
|
|
115
|
-
* get color for icon
|
|
116
|
-
*/
|
|
117
109
|
const getIconColor = () => {
|
|
118
110
|
switch (type) {
|
|
119
111
|
case 'disabled':
|
package/Image/index.tsx
CHANGED
|
@@ -15,6 +15,7 @@ import { ImageProps } from './types';
|
|
|
15
15
|
|
|
16
16
|
type Status = 'success' | 'loading' | 'error';
|
|
17
17
|
|
|
18
|
+
/** Last path segment of a `{ uri }` source, used in QA labels; '' otherwise. */
|
|
18
19
|
export const getImageName = (source: any): string => {
|
|
19
20
|
const parts = source?.uri?.split?.('/');
|
|
20
21
|
if (parts?.length > 0) {
|
|
@@ -23,6 +24,10 @@ export const getImageName = (source: any): string => {
|
|
|
23
24
|
return '';
|
|
24
25
|
};
|
|
25
26
|
|
|
27
|
+
/**
|
|
28
|
+
* FastImage that shows a skeleton while loading (if `loading`) or inside a
|
|
29
|
+
* loading `SkeletonProvider`, and a fail icon on error.
|
|
30
|
+
*/
|
|
26
31
|
const Image: React.FC<ImageProps> = ({
|
|
27
32
|
style,
|
|
28
33
|
source,
|
|
@@ -44,15 +49,12 @@ const Image: React.FC<ImageProps> = ({
|
|
|
44
49
|
|
|
45
50
|
const showBaseLineDebug = context?.features?.showBaseLineDebug ?? false;
|
|
46
51
|
|
|
47
|
-
|
|
52
|
+
const imageName = getImageName(source);
|
|
53
|
+
let accessibilityLabel: any = `img|${imageName}`;
|
|
48
54
|
if (component?.componentId) {
|
|
49
|
-
accessibilityLabel = `${component?.componentId}|img|${
|
|
50
|
-
source,
|
|
51
|
-
)}`;
|
|
55
|
+
accessibilityLabel = `${component?.componentId}|img|${imageName}`;
|
|
52
56
|
}
|
|
53
|
-
|
|
54
|
-
* render content loading | fail | rendered
|
|
55
|
-
*/
|
|
57
|
+
|
|
56
58
|
const renderContent = () => {
|
|
57
59
|
if (status === 'loading' || status === 'error') {
|
|
58
60
|
let content = <Skeleton />;
|
package/Input/Input.tsx
CHANGED
|
@@ -31,7 +31,8 @@ import {
|
|
|
31
31
|
} from '../Context';
|
|
32
32
|
|
|
33
33
|
/**
|
|
34
|
-
*
|
|
34
|
+
* Text input with floating label, leading/trailing/clear icons and an error or
|
|
35
|
+
* hint row; controlled via `value` or uncontrolled via `defaultValue`.
|
|
35
36
|
*/
|
|
36
37
|
const Input = forwardRef(
|
|
37
38
|
(
|
|
@@ -131,15 +132,17 @@ const Input = forwardRef(
|
|
|
131
132
|
};
|
|
132
133
|
});
|
|
133
134
|
|
|
134
|
-
/**
|
|
135
|
-
* Render input view
|
|
136
|
-
*/
|
|
137
135
|
const renderInputView = () => {
|
|
138
136
|
const disabledColor = theme.colors.text.disable;
|
|
139
137
|
const secure = secureTextEntry;
|
|
140
138
|
let textColor = theme.colors.text.default;
|
|
141
139
|
let placeholderColor = theme.colors.text.hint;
|
|
142
140
|
let iconTintColor = trailingColor;
|
|
141
|
+
let secureTextStyle = styles.secureTextLarge;
|
|
142
|
+
|
|
143
|
+
if (size === 'small') {
|
|
144
|
+
secureTextStyle = styles.secureTextSmall;
|
|
145
|
+
}
|
|
143
146
|
|
|
144
147
|
if (disabled) {
|
|
145
148
|
textColor = disabledColor;
|
|
@@ -199,11 +202,7 @@ const Input = forwardRef(
|
|
|
199
202
|
color: textColor,
|
|
200
203
|
fontFamily: exportFontFamily(fontWeight ?? 'regular'),
|
|
201
204
|
},
|
|
202
|
-
secure &&
|
|
203
|
-
haveValue &&
|
|
204
|
-
(size === 'small'
|
|
205
|
-
? styles.secureTextSmall
|
|
206
|
-
: styles.secureTextLarge),
|
|
205
|
+
secure && haveValue && secureTextStyle,
|
|
207
206
|
]}
|
|
208
207
|
defaultValue={defaultValue}
|
|
209
208
|
allowFontScaling={false}
|
package/Input/InputDropDown.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { useContext } from 'react';
|
|
2
|
-
import { TouchableOpacity, View } from 'react-native';
|
|
2
|
+
import { TouchableOpacity, View, type ViewStyle } from 'react-native';
|
|
3
3
|
import { useComponentId } from '../Application';
|
|
4
4
|
import {
|
|
5
5
|
ApplicationContext,
|
|
@@ -18,6 +18,10 @@ import { Text, useScaleSize } from '../Text';
|
|
|
18
18
|
import styles from './styles';
|
|
19
19
|
import { Spacing } from '../Consts';
|
|
20
20
|
|
|
21
|
+
/**
|
|
22
|
+
* Pressable field styled like an input with a chevron; shows `value` or
|
|
23
|
+
* `placeholder`. Opening the picker is left to `onPress`.
|
|
24
|
+
*/
|
|
21
25
|
const InputDropDown = ({
|
|
22
26
|
value,
|
|
23
27
|
floatingValue,
|
|
@@ -50,18 +54,19 @@ const InputDropDown = ({
|
|
|
50
54
|
props.accessibilityLabel,
|
|
51
55
|
);
|
|
52
56
|
|
|
53
|
-
/**
|
|
54
|
-
* Render the input view
|
|
55
|
-
*/
|
|
56
57
|
const renderInputView = () => {
|
|
57
58
|
const disabledColor = theme.colors.text.disable;
|
|
58
59
|
let textColor = theme.colors.text.default;
|
|
59
60
|
let placeholderColor = theme.colors.text.hint;
|
|
61
|
+
let chevronStyle: ViewStyle = styles.iconViewDropDownSingle;
|
|
60
62
|
|
|
61
63
|
if (disabled) {
|
|
62
64
|
textColor = disabledColor;
|
|
63
65
|
placeholderColor = disabledColor;
|
|
64
66
|
}
|
|
67
|
+
if (multiline) {
|
|
68
|
+
chevronStyle = styles.iconViewDropDownMultiline;
|
|
69
|
+
}
|
|
65
70
|
|
|
66
71
|
return (
|
|
67
72
|
<View
|
|
@@ -111,14 +116,7 @@ const InputDropDown = ({
|
|
|
111
116
|
</Text>
|
|
112
117
|
</View>
|
|
113
118
|
</View>
|
|
114
|
-
<View
|
|
115
|
-
style={[
|
|
116
|
-
styles.iconViewDropDown,
|
|
117
|
-
multiline
|
|
118
|
-
? styles.iconViewDropDownMultiline
|
|
119
|
-
: styles.iconViewDropDownSingle,
|
|
120
|
-
]}
|
|
121
|
-
>
|
|
119
|
+
<View style={[styles.iconViewDropDown, chevronStyle]}>
|
|
122
120
|
<Icon source={'arrow_chevron_down_small'} />
|
|
123
121
|
</View>
|
|
124
122
|
</View>
|
package/Input/InputMoney.tsx
CHANGED
|
@@ -16,6 +16,11 @@ import { InputMoneyView } from './InputMoneyView';
|
|
|
16
16
|
import { findEdit, moneyFormatter } from './moneyUtils';
|
|
17
17
|
import styles from './styles';
|
|
18
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Money input: keeps digits only, shows them grouped with a currency suffix and
|
|
21
|
+
* reports raw digits to `onChangeText`. On Android it restores the caret by the
|
|
22
|
+
* count of digits after it, since regrouping shifts positions.
|
|
23
|
+
*/
|
|
19
24
|
const InputMoney = forwardRef(
|
|
20
25
|
(
|
|
21
26
|
{
|
|
@@ -88,9 +93,9 @@ const InputMoney = forwardRef(
|
|
|
88
93
|
|
|
89
94
|
useEffect(() => {
|
|
90
95
|
if (!caretSelection) return;
|
|
91
|
-
setCaretSelection(current =>
|
|
92
|
-
current === caretSelection ? undefined : current
|
|
93
|
-
);
|
|
96
|
+
setCaretSelection(current => {
|
|
97
|
+
return current === caretSelection ? undefined : current;
|
|
98
|
+
});
|
|
94
99
|
}, [caretSelection]);
|
|
95
100
|
|
|
96
101
|
const updateDisplayValue = (numeric: string) => {
|
|
@@ -142,11 +147,10 @@ const InputMoney = forwardRef(
|
|
|
142
147
|
lastRawText.current = text;
|
|
143
148
|
const formattedEdit = findEdit(lastDisplayValue, text);
|
|
144
149
|
const rawEdit = findEdit(raw, text);
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
).length;
|
|
150
|
+
const isRawEditSmaller = !!raw && rawEdit.size < formattedEdit.size;
|
|
151
|
+
const { old, start, suffix } = isRawEditSmaller ? rawEdit : formattedEdit;
|
|
152
|
+
const textAfterEdit = text.slice(text.length - suffix);
|
|
153
|
+
caretDigitsAfter.current = parseInputText(textAfterEdit).length;
|
|
150
154
|
if (text.length < old.length && digits === numericValue) {
|
|
151
155
|
const index = parseInputText(text.slice(0, start)).length - 1;
|
|
152
156
|
if (index >= 0) {
|
package/Input/InputMoneyView.tsx
CHANGED
|
@@ -52,6 +52,10 @@ 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
|
+
*/
|
|
55
59
|
export const InputMoneyView = ({
|
|
56
60
|
size,
|
|
57
61
|
errorMessage,
|
|
@@ -98,9 +102,12 @@ export const InputMoneyView = ({
|
|
|
98
102
|
fontFamily: inputFontFamily,
|
|
99
103
|
};
|
|
100
104
|
const placeholderHasCurrency = placeholder.endsWith(currency);
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
105
|
+
const shouldShowCurrency = !!displayValue || placeholderHasCurrency;
|
|
106
|
+
const shouldShowClearIcon = !!(showClearIcon && focused && displayValue);
|
|
107
|
+
let inputPlaceholder = placeholder;
|
|
108
|
+
if (placeholderHasCurrency) {
|
|
109
|
+
inputPlaceholder = placeholder.slice(0, -currency.length);
|
|
110
|
+
}
|
|
104
111
|
return (
|
|
105
112
|
<View
|
|
106
113
|
style={[
|
|
@@ -147,7 +154,7 @@ export const InputMoneyView = ({
|
|
|
147
154
|
keyboardType={'numeric'}
|
|
148
155
|
maxLength={undefined}
|
|
149
156
|
/>
|
|
150
|
-
{
|
|
157
|
+
{shouldShowCurrency && (
|
|
151
158
|
<Text
|
|
152
159
|
style={[
|
|
153
160
|
textStyle,
|
|
@@ -157,10 +164,10 @@ export const InputMoneyView = ({
|
|
|
157
164
|
>
|
|
158
165
|
{currency}
|
|
159
166
|
</Text>
|
|
160
|
-
)
|
|
167
|
+
)}
|
|
161
168
|
</Pressable>
|
|
162
169
|
<View style={styles.iconView}>
|
|
163
|
-
{
|
|
170
|
+
{shouldShowClearIcon && (
|
|
164
171
|
<TouchableOpacity
|
|
165
172
|
accessibilityLabel={`${componentId}|clear-icon-touch`}
|
|
166
173
|
style={styles.iconWrapper}
|
|
@@ -173,7 +180,7 @@ export const InputMoneyView = ({
|
|
|
173
180
|
accessibilityLabel={`${componentId}|clear-icon`}
|
|
174
181
|
/>
|
|
175
182
|
</TouchableOpacity>
|
|
176
|
-
)
|
|
183
|
+
)}
|
|
177
184
|
<RenderTrailing
|
|
178
185
|
color={iconTintColor}
|
|
179
186
|
icon={trailing}
|
package/Input/InputOTP.tsx
CHANGED
|
@@ -38,6 +38,7 @@ import {
|
|
|
38
38
|
MiniAppContext,
|
|
39
39
|
} from '../Context';
|
|
40
40
|
|
|
41
|
+
/** Blinking caret shown in the OTP slot being typed. */
|
|
41
42
|
const OTPCaret: FC<CaretProps> = ({ index, length }) => {
|
|
42
43
|
const DURATION = 300;
|
|
43
44
|
const { theme } = useContext(ApplicationContext);
|
|
@@ -60,9 +61,7 @@ const OTPCaret: FC<CaretProps> = ({ index, length }) => {
|
|
|
60
61
|
};
|
|
61
62
|
}, [opacity]);
|
|
62
63
|
|
|
63
|
-
const animatedStyle = useAnimatedStyle(() => ({
|
|
64
|
-
opacity: opacity.value,
|
|
65
|
-
}));
|
|
64
|
+
const animatedStyle = useAnimatedStyle(() => ({ opacity: opacity.value }));
|
|
66
65
|
|
|
67
66
|
const hasNextInput = !isNaN(Number(length)) && index !== Number(length) - 1;
|
|
68
67
|
const spacingStyle = hasNextInput && { marginRight: Spacing.L };
|
|
@@ -86,6 +85,10 @@ const OTPCaret: FC<CaretProps> = ({ index, length }) => {
|
|
|
86
85
|
);
|
|
87
86
|
};
|
|
88
87
|
|
|
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
|
+
*/
|
|
89
92
|
const InputOTP = forwardRef(
|
|
90
93
|
(
|
|
91
94
|
{
|
|
@@ -121,14 +124,16 @@ const InputOTP = forwardRef(
|
|
|
121
124
|
props.accessibilityLabel,
|
|
122
125
|
);
|
|
123
126
|
|
|
124
|
-
useImperativeHandle(ref, () =>
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
127
|
+
useImperativeHandle(ref, () => {
|
|
128
|
+
return {
|
|
129
|
+
onChangeText: (text: string) => {
|
|
130
|
+
_onChangeText(text);
|
|
131
|
+
},
|
|
132
|
+
focus: () => inputRef.current?.focus(),
|
|
133
|
+
blur: () => inputRef.current?.blur(),
|
|
134
|
+
setText: (text: string) => _onChangeText(text),
|
|
135
|
+
};
|
|
136
|
+
});
|
|
132
137
|
|
|
133
138
|
const onFocusInput = (e: TextInputFocusEvent) => {
|
|
134
139
|
setFocused(true);
|
|
@@ -142,13 +147,10 @@ const InputOTP = forwardRef(
|
|
|
142
147
|
|
|
143
148
|
const _onChangeText = (text: string) => {
|
|
144
149
|
if (text.length > MAX_LENGTH) return;
|
|
145
|
-
|
|
146
|
-
if (
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
text.length >= value.length
|
|
150
|
-
)
|
|
151
|
-
return;
|
|
150
|
+
const isNumeric = !isNaN(Number(text));
|
|
151
|
+
if (dataType === 'number' && !isNumeric) return;
|
|
152
|
+
const isFull = value.length >= Number(length);
|
|
153
|
+
if (isNumeric && isFull && text.length >= value.length) return;
|
|
152
154
|
|
|
153
155
|
setValue(text);
|
|
154
156
|
onChangeText?.(text);
|
|
@@ -164,21 +166,21 @@ const InputOTP = forwardRef(
|
|
|
164
166
|
const TextInputs: React.ReactNode[] = [];
|
|
165
167
|
const textColors = theme.colors.text;
|
|
166
168
|
for (let i = 0; i < inputLength; i++) {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
);
|
|
169
|
+
let slot: React.ReactNode;
|
|
170
|
+
if (focused && value.length === i) {
|
|
171
|
+
slot = <OTPCaret index={i} length={inputLength} />;
|
|
172
|
+
} else {
|
|
173
|
+
slot = (
|
|
174
|
+
<Text
|
|
175
|
+
style={i !== inputLength - 1 && { marginRight: Spacing.L }}
|
|
176
|
+
color={value[i] ? textColors.default : textColors.hint}
|
|
177
|
+
typography={value[i] ? 'header_m_bold' : 'body_default_regular'}
|
|
178
|
+
>
|
|
179
|
+
{value[i] || '-'}
|
|
180
|
+
</Text>
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
TextInputs.push(<View key={`otp-input-${i}`}>{slot}</View>);
|
|
182
184
|
}
|
|
183
185
|
return TextInputs;
|
|
184
186
|
};
|