@momo-kits/foundation 0.81.49 → 0.81.51-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Application/BottomSheet.tsx +216 -0
- package/Application/BottomTab/BottomTabBar.tsx +278 -0
- package/Application/{BottomTab.tsx → BottomTab/index.tsx} +42 -18
- package/Application/Components.tsx +356 -91
- package/Application/Localize.ts +44 -0
- package/Application/ModalScreen.tsx +14 -185
- package/Application/Navigation.ts +5 -19
- package/Application/NavigationContainer.tsx +62 -72
- package/Application/Navigator.ts +154 -48
- package/Application/StackScreen.tsx +289 -32
- package/Application/constants.ts +9 -0
- package/Application/index.ts +13 -15
- package/Application/types.ts +81 -35
- package/Application/utils.tsx +76 -30
- package/Assets/icon_bank.json +1 -1
- package/Badge/Badge.tsx +1 -1
- package/Badge/BadgeDotAnimation.tsx +95 -0
- package/Badge/index.tsx +2 -1
- package/Badge/styles.ts +50 -6
- package/Button/index.tsx +44 -26
- package/CheckBox/index.tsx +37 -26
- package/CheckBox/types.ts +5 -3
- package/Consts/colors+spacing+radius.ts +14 -0
- package/Icon/index.tsx +9 -8
- package/Icon/types.ts +2 -2
- package/IconButton/index.tsx +24 -14
- package/Image/index.tsx +23 -8
- package/Image/types.ts +2 -0
- package/Input/Input.tsx +116 -26
- package/Input/InputDropDown.tsx +66 -51
- package/Input/InputMoney.tsx +106 -15
- package/Input/InputOTP.tsx +49 -26
- package/Input/InputSearch.tsx +252 -99
- package/Input/InputTextArea.tsx +26 -6
- package/Input/TextTyping.tsx +113 -0
- package/Input/common.tsx +48 -22
- package/Input/index.tsx +112 -77
- package/Input/styles.ts +48 -11
- package/Input/utils.ts +1 -1
- package/Layout/Card.tsx +4 -3
- package/Layout/FloatingButton.tsx +168 -0
- package/Layout/GridSystem.tsx +1 -1
- package/Layout/ItemList.tsx +3 -2
- package/Layout/Screen.tsx +242 -121
- package/Layout/Section.tsx +4 -3
- package/Layout/styles.ts +11 -13
- package/Layout/types.ts +1 -1
- package/Layout/utils.ts +4 -4
- package/Loader/index.tsx +2 -0
- package/Pagination/PaginationScroll.tsx +2 -2
- package/Pagination/styles.ts +1 -1
- package/Popup/PopupNotify.tsx +57 -46
- package/Popup/PopupPromotion.tsx +6 -8
- package/Popup/types.ts +1 -7
- package/Radio/index.tsx +35 -20
- package/Radio/types.ts +5 -3
- package/Switch/index.tsx +32 -18
- package/Switch/types.ts +5 -3
- package/Tag/index.tsx +9 -2
- package/Tag/styles.ts +3 -2
- package/Tag/types.ts +12 -0
- package/Text/index.tsx +24 -5
- package/Text/styles.ts +8 -8
- package/Text/types.ts +19 -1
- package/Title/index.tsx +170 -103
- package/Title/styles.ts +36 -9
- package/Title/types.ts +16 -48
- package/code-scanner.js +125 -16
- package/index.ts +7 -0
- package/migrate_typo.js +56 -0
- package/package.json +10 -9
- package/publish.sh +16 -21
- package/tsconfig.json +1 -1
package/Application/index.ts
CHANGED
|
@@ -1,41 +1,39 @@
|
|
|
1
|
+
import {Platform} from 'react-native';
|
|
2
|
+
import {createContext} from 'react';
|
|
1
3
|
import {NavigationContainer} from './NavigationContainer';
|
|
4
|
+
import Localize from './Localize';
|
|
2
5
|
import Screen from '../Layout/Screen';
|
|
3
6
|
import {
|
|
4
7
|
HeaderAnimated,
|
|
5
8
|
HeaderBackground,
|
|
6
9
|
HeaderCustom,
|
|
7
|
-
HeaderLeft,
|
|
8
|
-
HeaderRightAction,
|
|
9
10
|
HeaderTitle,
|
|
10
|
-
HeaderToolkitAction,
|
|
11
11
|
NavigationButton,
|
|
12
|
+
HeaderRightAction,
|
|
12
13
|
} from './Components';
|
|
13
14
|
import BottomTab from './BottomTab';
|
|
14
|
-
import {createContext} from 'react';
|
|
15
15
|
import {defaultContext} from '../Consts';
|
|
16
|
-
import {MiniAppInfo} from './types';
|
|
17
16
|
|
|
17
|
+
const Context = createContext({});
|
|
18
18
|
const ApplicationContext = createContext(defaultContext);
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
toolkitConfig: {},
|
|
24
|
-
fromCode: '',
|
|
25
|
-
});
|
|
19
|
+
|
|
20
|
+
const MiniAppContext = (Platform as any).MiniAppContext ?? createContext({});
|
|
21
|
+
const ScreenContext = (Platform as any).ScreenContext ?? createContext({});
|
|
22
|
+
const ComponentContext = (Platform as any).ComponentContext ?? Context;
|
|
26
23
|
|
|
27
24
|
export {
|
|
28
25
|
ApplicationContext,
|
|
29
26
|
MiniAppContext,
|
|
27
|
+
ScreenContext,
|
|
28
|
+
ComponentContext,
|
|
30
29
|
NavigationContainer,
|
|
30
|
+
Localize,
|
|
31
31
|
Screen,
|
|
32
32
|
BottomTab,
|
|
33
33
|
NavigationButton,
|
|
34
|
+
HeaderRightAction,
|
|
34
35
|
HeaderTitle,
|
|
35
|
-
HeaderLeft,
|
|
36
36
|
HeaderBackground,
|
|
37
|
-
HeaderRightAction,
|
|
38
|
-
HeaderToolkitAction,
|
|
39
37
|
HeaderCustom,
|
|
40
38
|
HeaderAnimated,
|
|
41
39
|
};
|
package/Application/types.ts
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {EventArg} from '@react-navigation/core';
|
|
2
2
|
import {StackNavigationOptions} from '@react-navigation/stack';
|
|
3
3
|
import React from 'react';
|
|
4
|
-
import
|
|
4
|
+
import {Animated, ViewProps} from 'react-native';
|
|
5
5
|
import {PopupNotifyProps} from '../Popup/types';
|
|
6
|
+
import Localize from './Localize';
|
|
6
7
|
import Navigation from './Navigation';
|
|
8
|
+
import Navigator from './Navigator';
|
|
9
|
+
import {
|
|
10
|
+
NavigationActionCallbackErrorEnum,
|
|
11
|
+
NavigationActionCallbackStatusEnum,
|
|
12
|
+
} from './constants';
|
|
7
13
|
|
|
8
14
|
type Screen = React.ComponentType<NavigationScreenProps>;
|
|
9
15
|
|
|
@@ -70,16 +76,6 @@ export type Context = {
|
|
|
70
76
|
translate?: (key: string) => string;
|
|
71
77
|
};
|
|
72
78
|
|
|
73
|
-
export type MiniAppInfo = {
|
|
74
|
-
[key: string]: any;
|
|
75
|
-
code: string;
|
|
76
|
-
appId: string;
|
|
77
|
-
buildNumber: number;
|
|
78
|
-
flowPartnerId?: string;
|
|
79
|
-
toolkitConfig?: any;
|
|
80
|
-
fromCode?: string;
|
|
81
|
-
};
|
|
82
|
-
|
|
83
79
|
export type LocalizationObject = {
|
|
84
80
|
vi: {
|
|
85
81
|
[key: string]: string;
|
|
@@ -94,7 +90,8 @@ export type NavigationContainerProps = {
|
|
|
94
90
|
options?: NavigationOptions;
|
|
95
91
|
theme: Theme;
|
|
96
92
|
maxApi: any;
|
|
97
|
-
|
|
93
|
+
initialParams?: any;
|
|
94
|
+
localize: Localize;
|
|
98
95
|
};
|
|
99
96
|
|
|
100
97
|
export type NavigationScreenProps = {
|
|
@@ -109,32 +106,43 @@ export type ScreenParams = {
|
|
|
109
106
|
options?: NavigationOptions;
|
|
110
107
|
};
|
|
111
108
|
|
|
112
|
-
export
|
|
109
|
+
export type ModalParams = {
|
|
113
110
|
[key: string]: any;
|
|
111
|
+
screen: React.ComponentType;
|
|
114
112
|
onDismiss?: () => void;
|
|
115
113
|
barrierDismissible?: boolean;
|
|
116
|
-
}
|
|
114
|
+
};
|
|
117
115
|
|
|
118
|
-
export
|
|
116
|
+
export type BottomSheetParams = {
|
|
119
117
|
[key: string]: any;
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
118
|
+
screen: React.ComponentType;
|
|
119
|
+
options: {
|
|
120
|
+
title: string;
|
|
121
|
+
surface?: boolean;
|
|
122
|
+
};
|
|
123
|
+
useNativeModal?: boolean;
|
|
124
|
+
surface?: boolean;
|
|
125
|
+
onDismiss?: (type?: string) => void;
|
|
126
|
+
barrierDismissible?: boolean;
|
|
127
|
+
draggable?: boolean;
|
|
128
|
+
useBottomInset?: boolean;
|
|
129
|
+
keyboardVerticalOffset?: number;
|
|
130
|
+
};
|
|
124
131
|
|
|
125
132
|
export type NavigationButtonProps = {
|
|
126
133
|
icon: string;
|
|
127
134
|
tintColor?: string;
|
|
128
135
|
useBorder?: boolean;
|
|
129
136
|
onPress: () => void;
|
|
130
|
-
badgeType?: 'dot' | 'number';
|
|
137
|
+
badgeType?: 'dot' | 'number' | 'dot-animation';
|
|
131
138
|
badgeValue?: number;
|
|
139
|
+
accessibilityLabel?: string;
|
|
132
140
|
};
|
|
133
141
|
|
|
134
142
|
export type PinnedToolType = {
|
|
135
143
|
key: string;
|
|
136
144
|
badgeValue?: number;
|
|
137
|
-
badgeType?: 'number' | 'dot';
|
|
145
|
+
badgeType?: 'number' | 'dot' | 'dot-animation';
|
|
138
146
|
};
|
|
139
147
|
|
|
140
148
|
export type RuntimeToolType = {
|
|
@@ -143,18 +151,29 @@ export type RuntimeToolType = {
|
|
|
143
151
|
key: string;
|
|
144
152
|
};
|
|
145
153
|
|
|
146
|
-
export interface NavigationOptions
|
|
154
|
+
export interface NavigationOptions
|
|
155
|
+
extends Omit<StackNavigationOptions, 'headerRight'> {
|
|
147
156
|
preventBack?: PopupNotifyProps;
|
|
148
157
|
onPressLeftHeader?: () => void;
|
|
149
158
|
surface?: boolean;
|
|
150
159
|
hiddenBack?: boolean;
|
|
151
160
|
customTitle?: TitleCustomProps;
|
|
152
|
-
|
|
153
|
-
pinnedTool?: PinnedToolType;
|
|
154
|
-
runtimeTools?: RuntimeToolType[];
|
|
155
|
-
};
|
|
161
|
+
headerRight?: HeaderRightToolkit | HeaderRightActions | any;
|
|
156
162
|
}
|
|
157
163
|
|
|
164
|
+
export type HeaderRightToolkit = {
|
|
165
|
+
type?: 'toolkit';
|
|
166
|
+
pinnedTool?: PinnedToolType;
|
|
167
|
+
runtimeTools?: RuntimeToolType[];
|
|
168
|
+
preventClose?: PopupNotifyProps;
|
|
169
|
+
useMore?: boolean;
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
export type HeaderRightActions = {
|
|
173
|
+
type?: 'icon';
|
|
174
|
+
buttons: NavigationButtonProps[];
|
|
175
|
+
};
|
|
176
|
+
|
|
158
177
|
export interface HeaderBackProps extends NavigationButtonProps {
|
|
159
178
|
preventBack?: PopupNotifyProps;
|
|
160
179
|
onPressLeftHeader?: () => void;
|
|
@@ -162,9 +181,8 @@ export interface HeaderBackProps extends NavigationButtonProps {
|
|
|
162
181
|
|
|
163
182
|
export type HeaderBackgroundProps = {
|
|
164
183
|
image?: string | null;
|
|
165
|
-
backgroundColor?: string | null;
|
|
166
184
|
animatedValue?: Animated.Value;
|
|
167
|
-
|
|
185
|
+
useSurface?: boolean;
|
|
168
186
|
};
|
|
169
187
|
|
|
170
188
|
export type TitleCustomProps = {
|
|
@@ -183,20 +201,25 @@ export type HeaderToolkitProps = {
|
|
|
183
201
|
configs: any[];
|
|
184
202
|
};
|
|
185
203
|
|
|
186
|
-
export
|
|
204
|
+
export interface HeaderAnimatedProps extends ViewProps {
|
|
187
205
|
animatedValue: Animated.Value;
|
|
188
206
|
image: string;
|
|
189
|
-
|
|
190
|
-
};
|
|
207
|
+
}
|
|
191
208
|
|
|
192
209
|
export type BottomTabItemProps = {
|
|
193
210
|
name: string;
|
|
194
211
|
label: string;
|
|
195
212
|
icon: string;
|
|
196
213
|
showDot?: boolean;
|
|
197
|
-
badgeLabel?: string;
|
|
214
|
+
badgeLabel?: string | number;
|
|
198
215
|
screen: Screen;
|
|
199
216
|
options: NavigationOptions;
|
|
217
|
+
initialParams?: any;
|
|
218
|
+
listeners?: {
|
|
219
|
+
tabPress?: (e: EventArg<'tabPress', true, undefined>) => void;
|
|
220
|
+
focus?: (e: EventArg<'focus', false, unknown>) => void;
|
|
221
|
+
blur?: (e: EventArg<'blur', false, unknown>) => void;
|
|
222
|
+
};
|
|
200
223
|
};
|
|
201
224
|
|
|
202
225
|
export type BottomTabProps = {
|
|
@@ -205,12 +228,35 @@ export type BottomTabProps = {
|
|
|
205
228
|
initialRouteName?: string;
|
|
206
229
|
navigation: Navigation;
|
|
207
230
|
listeners?: {
|
|
208
|
-
tabPress?:
|
|
209
|
-
focus?:
|
|
231
|
+
tabPress?: (e: EventArg<'tabPress', true, undefined>) => void;
|
|
232
|
+
focus?: (e: EventArg<'focus', false, unknown>) => void;
|
|
233
|
+
blur?: (e: EventArg<'blur', false, unknown>) => void;
|
|
210
234
|
};
|
|
235
|
+
floatingButton?: FloatingButtonProps;
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
export type FloatingButtonProps = {
|
|
239
|
+
container?: React.ReactNode;
|
|
240
|
+
icon: string | React.ReactNode;
|
|
241
|
+
label: string;
|
|
242
|
+
onPress: () => void;
|
|
211
243
|
};
|
|
212
244
|
|
|
213
245
|
export type AnimatedHeader = {
|
|
214
246
|
type: 'default' | 'surface';
|
|
215
247
|
component?: (props?: any) => React.ReactElement;
|
|
248
|
+
headerTitle?: (props?: any) => React.ReactElement;
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
type NavigatorActionSuccessCallbackType = {
|
|
252
|
+
status: NavigationActionCallbackStatusEnum.Success;
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
type NavigatorActionFailCallbackType = {
|
|
256
|
+
status: NavigationActionCallbackStatusEnum.Failed;
|
|
257
|
+
type: NavigationActionCallbackErrorEnum;
|
|
216
258
|
};
|
|
259
|
+
|
|
260
|
+
export type NavigatorCallbackParamsType =
|
|
261
|
+
| NavigatorActionSuccessCallbackType
|
|
262
|
+
| NavigatorActionFailCallbackType;
|
package/Application/utils.tsx
CHANGED
|
@@ -7,11 +7,13 @@ import {
|
|
|
7
7
|
HeaderBackground,
|
|
8
8
|
HeaderCustom,
|
|
9
9
|
HeaderLeft,
|
|
10
|
+
HeaderRight,
|
|
10
11
|
HeaderTitle,
|
|
11
|
-
HeaderToolkitAction,
|
|
12
12
|
} from './Components';
|
|
13
|
-
import {NavigationOptions
|
|
13
|
+
import {NavigationOptions} from './types';
|
|
14
14
|
import {Colors} from '../Consts';
|
|
15
|
+
import Navigation from './Navigation';
|
|
16
|
+
import {LayoutChangeEvent} from 'react-native';
|
|
15
17
|
|
|
16
18
|
/**
|
|
17
19
|
* default options for stack screen
|
|
@@ -22,7 +24,7 @@ const getStackOptions = (): StackNavigationOptions => {
|
|
|
22
24
|
headerTitle: HeaderTitle,
|
|
23
25
|
headerBackground: (props: any) => <HeaderBackground {...props} />,
|
|
24
26
|
headerLeft: (props: any) => <HeaderLeft {...props} />,
|
|
25
|
-
headerRight: (props: any) => <
|
|
27
|
+
headerRight: (props: any) => <HeaderRight {...props} />,
|
|
26
28
|
headerTintColor: Colors.black_01,
|
|
27
29
|
};
|
|
28
30
|
};
|
|
@@ -72,39 +74,44 @@ const getModalOptions = (): StackNavigationOptions => {
|
|
|
72
74
|
/**
|
|
73
75
|
* build react-navigation options
|
|
74
76
|
*/
|
|
75
|
-
const getOptions = (params: NavigationOptions,
|
|
77
|
+
const getOptions = (params: NavigationOptions, navigation?: Navigation) => {
|
|
76
78
|
let options = {};
|
|
77
79
|
|
|
80
|
+
/**
|
|
81
|
+
* left header factory
|
|
82
|
+
*/
|
|
78
83
|
if (
|
|
79
84
|
typeof params.onPressLeftHeader === 'function' ||
|
|
80
85
|
params.preventBack !== undefined ||
|
|
81
86
|
typeof params.hiddenBack === 'boolean'
|
|
82
87
|
) {
|
|
88
|
+
let headerLeft: any = (props: any) => (
|
|
89
|
+
<HeaderLeft
|
|
90
|
+
{...props}
|
|
91
|
+
preventBack={params.preventBack}
|
|
92
|
+
onPressLeftHeader={params.onPressLeftHeader}
|
|
93
|
+
/>
|
|
94
|
+
);
|
|
95
|
+
if (params.hiddenBack) {
|
|
96
|
+
headerLeft = null;
|
|
97
|
+
}
|
|
83
98
|
options = {
|
|
84
99
|
...options,
|
|
85
|
-
headerLeft
|
|
86
|
-
? null
|
|
87
|
-
: (props: any) => (
|
|
88
|
-
<HeaderLeft
|
|
89
|
-
{...props}
|
|
90
|
-
preventBack={params.preventBack}
|
|
91
|
-
onPressLeftHeader={params.onPressLeftHeader}
|
|
92
|
-
/>
|
|
93
|
-
),
|
|
100
|
+
headerLeft,
|
|
94
101
|
};
|
|
95
102
|
}
|
|
96
103
|
|
|
97
|
-
|
|
104
|
+
/**
|
|
105
|
+
* surface header style
|
|
106
|
+
*/
|
|
107
|
+
if (typeof params.surface === 'boolean') {
|
|
108
|
+
console.warn('surface options is deprecated, headerType instead in screen');
|
|
98
109
|
if (params.surface) {
|
|
99
110
|
options = {
|
|
100
111
|
...options,
|
|
101
|
-
headerTintColor:
|
|
112
|
+
headerTintColor: Colors.black_17,
|
|
102
113
|
headerBackground: () => (
|
|
103
|
-
<HeaderBackground
|
|
104
|
-
image={null}
|
|
105
|
-
backgroundColor={theme.colors.background.surface}
|
|
106
|
-
useBorder={true}
|
|
107
|
-
/>
|
|
114
|
+
<HeaderBackground image={null} useSurface={true} />
|
|
108
115
|
),
|
|
109
116
|
};
|
|
110
117
|
} else {
|
|
@@ -116,7 +123,13 @@ const getOptions = (params: NavigationOptions, theme: Theme) => {
|
|
|
116
123
|
}
|
|
117
124
|
}
|
|
118
125
|
|
|
126
|
+
/**
|
|
127
|
+
* custom title
|
|
128
|
+
*/
|
|
119
129
|
if (params.customTitle) {
|
|
130
|
+
console.warn(
|
|
131
|
+
'customTitle options is deprecated, please used headerTitle instead'
|
|
132
|
+
);
|
|
120
133
|
options = {
|
|
121
134
|
...options,
|
|
122
135
|
headerTitleAlign: 'left',
|
|
@@ -126,19 +139,39 @@ const getOptions = (params: NavigationOptions, theme: Theme) => {
|
|
|
126
139
|
};
|
|
127
140
|
}
|
|
128
141
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
142
|
+
/**
|
|
143
|
+
* header right
|
|
144
|
+
*/
|
|
145
|
+
if (params.headerRight) {
|
|
146
|
+
let headerRight: any = (props: any) => {
|
|
147
|
+
return (
|
|
148
|
+
<HeaderRight
|
|
149
|
+
{...props}
|
|
150
|
+
{...params.headerRight}
|
|
151
|
+
onLayout={(e: LayoutChangeEvent) => {
|
|
152
|
+
navigation?.onHeaderRightChange?.(e.nativeEvent.layout.width);
|
|
153
|
+
}}
|
|
154
|
+
/>
|
|
155
|
+
);
|
|
156
|
+
};
|
|
157
|
+
if (typeof params.headerRight === 'function') {
|
|
158
|
+
headerRight = (props: any) => {
|
|
132
159
|
return (
|
|
133
|
-
<
|
|
134
|
-
preventBack={params.preventBack}
|
|
135
|
-
pinnedTool={params.toolkitParams?.pinnedTool}
|
|
136
|
-
runtimeTools={params.toolkitParams?.runtimeTools}
|
|
160
|
+
<HeaderRight
|
|
137
161
|
{...props}
|
|
138
|
-
|
|
162
|
+
onLayout={(e: LayoutChangeEvent) => {
|
|
163
|
+
navigation?.onHeaderRightChange?.(e.nativeEvent.layout.width);
|
|
164
|
+
}}>
|
|
165
|
+
{params.headerRight(props)}
|
|
166
|
+
</HeaderRight>
|
|
139
167
|
);
|
|
140
|
-
}
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
options = {
|
|
171
|
+
...options,
|
|
172
|
+
headerRight,
|
|
141
173
|
};
|
|
174
|
+
}
|
|
142
175
|
|
|
143
176
|
return {
|
|
144
177
|
...params,
|
|
@@ -146,4 +179,17 @@ const getOptions = (params: NavigationOptions, theme: Theme) => {
|
|
|
146
179
|
};
|
|
147
180
|
};
|
|
148
181
|
|
|
149
|
-
|
|
182
|
+
const formatNameLength = (name = '') => {
|
|
183
|
+
let formattedName = name;
|
|
184
|
+
if (formattedName.length > 100) {
|
|
185
|
+
formattedName = formattedName.substring(0, 100);
|
|
186
|
+
}
|
|
187
|
+
return formattedName;
|
|
188
|
+
};
|
|
189
|
+
export {
|
|
190
|
+
getStackOptions,
|
|
191
|
+
getDialogOptions,
|
|
192
|
+
getModalOptions,
|
|
193
|
+
getOptions,
|
|
194
|
+
formatNameLength,
|
|
195
|
+
};
|
package/Assets/icon_bank.json
CHANGED
package/Badge/Badge.tsx
CHANGED
|
@@ -46,7 +46,7 @@ const Badge: FC<BadgeProps> = ({label = 'Label', style, backgroundColor}) => {
|
|
|
46
46
|
|
|
47
47
|
return (
|
|
48
48
|
<View style={[style, styles.badge, {backgroundColor: badgeColor}]}>
|
|
49
|
-
<Text color={Colors.black_01} typography={'
|
|
49
|
+
<Text color={Colors.black_01} typography={'action_xxs_bold'}>
|
|
50
50
|
{formatTitle()}
|
|
51
51
|
</Text>
|
|
52
52
|
</View>
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import React, {useEffect, useRef} from 'react';
|
|
2
|
+
import {Animated, View} from 'react-native';
|
|
3
|
+
import {BadgeDotProps} from './types';
|
|
4
|
+
import styles from './styles';
|
|
5
|
+
|
|
6
|
+
const DURATION = 500;
|
|
7
|
+
|
|
8
|
+
const BadgeDotAnimation = ({size, style}: BadgeDotProps) => {
|
|
9
|
+
// Refs for animated values
|
|
10
|
+
const scaleAnim = useRef(new Animated.Value(1)).current;
|
|
11
|
+
const waveScaleAnim = useRef(new Animated.Value(1)).current;
|
|
12
|
+
const waveOpacityAnim = useRef(new Animated.Value(0)).current;
|
|
13
|
+
|
|
14
|
+
const dotStyle =
|
|
15
|
+
size === 'small' ? styles.dotAnimationSmall : styles.dotAnimation;
|
|
16
|
+
const waveStyle = size === 'small' ? styles.waveSmall : styles.wave;
|
|
17
|
+
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
// Infinite loop animation for the scale and wave effect
|
|
20
|
+
const animation = Animated.loop(
|
|
21
|
+
Animated.parallel([
|
|
22
|
+
// Dot pulse animation
|
|
23
|
+
Animated.sequence([
|
|
24
|
+
Animated.spring(scaleAnim, {
|
|
25
|
+
toValue: 1, // Scale up slightly
|
|
26
|
+
friction: 5, // Controls the "bounciness" of the spring
|
|
27
|
+
tension: 30, // Controls the "stiffness" of the spring
|
|
28
|
+
useNativeDriver: true,
|
|
29
|
+
}),
|
|
30
|
+
Animated.spring(scaleAnim, {
|
|
31
|
+
toValue: 1.1,
|
|
32
|
+
friction: 5,
|
|
33
|
+
tension: 30,
|
|
34
|
+
useNativeDriver: true,
|
|
35
|
+
}),
|
|
36
|
+
]), // Wave animation
|
|
37
|
+
Animated.sequence([
|
|
38
|
+
Animated.timing(waveScaleAnim, {
|
|
39
|
+
toValue: 2.5,
|
|
40
|
+
duration: DURATION * 3,
|
|
41
|
+
useNativeDriver: true,
|
|
42
|
+
}),
|
|
43
|
+
Animated.timing(waveScaleAnim, {
|
|
44
|
+
toValue: 1, // Reset wave size
|
|
45
|
+
duration: 0,
|
|
46
|
+
useNativeDriver: true,
|
|
47
|
+
}),
|
|
48
|
+
]), // Wave opacity animation
|
|
49
|
+
Animated.sequence([
|
|
50
|
+
Animated.timing(waveOpacityAnim, {
|
|
51
|
+
toValue: 0.3, // Wave becomes visible
|
|
52
|
+
duration: DURATION * 2,
|
|
53
|
+
useNativeDriver: true,
|
|
54
|
+
}),
|
|
55
|
+
Animated.timing(waveOpacityAnim, {
|
|
56
|
+
toValue: 0, // Wave fades out
|
|
57
|
+
duration: DURATION,
|
|
58
|
+
useNativeDriver: true,
|
|
59
|
+
}),
|
|
60
|
+
]),
|
|
61
|
+
])
|
|
62
|
+
);
|
|
63
|
+
animation.start();
|
|
64
|
+
|
|
65
|
+
return () => {
|
|
66
|
+
animation.stop();
|
|
67
|
+
};
|
|
68
|
+
}, []);
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
<View style={[styles.dotAnimationContainer, style]}>
|
|
72
|
+
{/* Wave Animation */}
|
|
73
|
+
<Animated.View
|
|
74
|
+
style={[
|
|
75
|
+
waveStyle,
|
|
76
|
+
{
|
|
77
|
+
transform: [{scale: waveScaleAnim}],
|
|
78
|
+
opacity: waveOpacityAnim,
|
|
79
|
+
},
|
|
80
|
+
]}
|
|
81
|
+
/>
|
|
82
|
+
{/* Dot Animation */}
|
|
83
|
+
<Animated.View
|
|
84
|
+
style={[
|
|
85
|
+
dotStyle,
|
|
86
|
+
{
|
|
87
|
+
transform: [{scale: scaleAnim}],
|
|
88
|
+
},
|
|
89
|
+
]}
|
|
90
|
+
/>
|
|
91
|
+
</View>
|
|
92
|
+
);
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
export default BadgeDotAnimation;
|
package/Badge/index.tsx
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import Badge from './Badge';
|
|
2
2
|
import BadgeDot from './BadgeDot';
|
|
3
3
|
import BadgeRibbon from './BadgeRibbon';
|
|
4
|
+
import BadgeDotAnimation from './BadgeDotAnimation';
|
|
4
5
|
import {BadgeProps, BadgeDotProps, BadgeRibbonProps} from './types';
|
|
5
6
|
|
|
6
|
-
export {Badge, BadgeRibbon, BadgeDot};
|
|
7
|
+
export {Badge, BadgeRibbon, BadgeDot, BadgeDotAnimation};
|
|
7
8
|
export type {BadgeProps, BadgeDotProps, BadgeRibbonProps};
|
package/Badge/styles.ts
CHANGED
|
@@ -2,6 +2,10 @@ import {StyleSheet} from 'react-native';
|
|
|
2
2
|
import {Colors, Radius, Spacing} from '../Consts';
|
|
3
3
|
import {scaleSize} from '../Text';
|
|
4
4
|
|
|
5
|
+
const DOT_SIZE = 16;
|
|
6
|
+
|
|
7
|
+
const DOT_SMALL_SIZE = 10;
|
|
8
|
+
|
|
5
9
|
export default StyleSheet.create({
|
|
6
10
|
badge: {
|
|
7
11
|
paddingHorizontal: Spacing.XS,
|
|
@@ -15,21 +19,61 @@ export default StyleSheet.create({
|
|
|
15
19
|
borderColor: Colors.black_01,
|
|
16
20
|
alignSelf: 'baseline',
|
|
17
21
|
},
|
|
22
|
+
dotAnimationContainer: {
|
|
23
|
+
position: 'relative',
|
|
24
|
+
alignItems: 'center',
|
|
25
|
+
justifyContent: 'center',
|
|
26
|
+
},
|
|
18
27
|
dot: {
|
|
19
|
-
width:
|
|
20
|
-
height:
|
|
28
|
+
width: DOT_SIZE,
|
|
29
|
+
height: DOT_SIZE,
|
|
21
30
|
borderWidth: 2,
|
|
22
31
|
borderColor: Colors.black_01,
|
|
23
32
|
backgroundColor: Colors.red_03,
|
|
24
|
-
borderRadius:
|
|
33
|
+
borderRadius: DOT_SIZE / 2,
|
|
25
34
|
},
|
|
26
35
|
dotSmall: {
|
|
27
|
-
width:
|
|
28
|
-
height:
|
|
36
|
+
width: DOT_SMALL_SIZE,
|
|
37
|
+
height: DOT_SMALL_SIZE,
|
|
29
38
|
borderWidth: 1,
|
|
30
39
|
borderColor: Colors.black_01,
|
|
31
40
|
backgroundColor: Colors.red_03,
|
|
32
|
-
borderRadius:
|
|
41
|
+
borderRadius: DOT_SMALL_SIZE / 2,
|
|
42
|
+
},
|
|
43
|
+
dotAnimation: {
|
|
44
|
+
width: DOT_SIZE,
|
|
45
|
+
height: DOT_SIZE,
|
|
46
|
+
borderColor: Colors.black_01,
|
|
47
|
+
backgroundColor: Colors.red_03,
|
|
48
|
+
borderRadius: DOT_SIZE / 2,
|
|
49
|
+
borderWidth: 0,
|
|
50
|
+
},
|
|
51
|
+
dotAnimationSmall: {
|
|
52
|
+
width: DOT_SMALL_SIZE,
|
|
53
|
+
height: DOT_SMALL_SIZE,
|
|
54
|
+
borderColor: Colors.black_01,
|
|
55
|
+
backgroundColor: Colors.red_03,
|
|
56
|
+
borderRadius: DOT_SMALL_SIZE / 2,
|
|
57
|
+
borderWidth: 0,
|
|
58
|
+
},
|
|
59
|
+
wave: {
|
|
60
|
+
width: DOT_SIZE,
|
|
61
|
+
height: DOT_SIZE,
|
|
62
|
+
borderColor: Colors.black_01,
|
|
63
|
+
backgroundColor: Colors.red_03,
|
|
64
|
+
borderRadius: DOT_SIZE / 2,
|
|
65
|
+
opacity: 0,
|
|
66
|
+
position: 'absolute',
|
|
67
|
+
borderWidth: 0,
|
|
68
|
+
},
|
|
69
|
+
waveSmall: {
|
|
70
|
+
width: DOT_SMALL_SIZE,
|
|
71
|
+
height: DOT_SMALL_SIZE,
|
|
72
|
+
borderColor: Colors.black_01,
|
|
73
|
+
backgroundColor: Colors.red_03,
|
|
74
|
+
borderRadius: DOT_SMALL_SIZE / 2,
|
|
75
|
+
borderWidth: 0,
|
|
76
|
+
position: 'absolute',
|
|
33
77
|
},
|
|
34
78
|
ribbon: {
|
|
35
79
|
alignSelf: 'baseline',
|