@momo-kits/foundation 0.80.9 → 0.81.1

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.
@@ -37,7 +37,7 @@ const InputSearch = forwardRef(
37
37
  const [focused, setFocused] = useState(false);
38
38
  const [haveValue, setHaveValue] = useState(!!value || !!defaultValue);
39
39
  const inputRef = (ref as React.RefObject<TextInput>) ?? useRef<TextInput>();
40
- let iconTintColor = iconColor;
40
+ const accessibilityLabel = `InputMoney/${placeholder}`;
41
41
 
42
42
  const _onFocus = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
43
43
  setFocused(true);
@@ -60,19 +60,16 @@ const InputSearch = forwardRef(
60
60
  };
61
61
 
62
62
  const renderInputView = () => {
63
- let textColor = theme.colors.text.default;
64
- let placeholderColor = theme.colors.text.hint;
65
-
66
63
  return (
67
64
  <TextInput
68
65
  {...props}
69
- accessibilityLabel={`TextInput`}
66
+ accessibilityLabel={accessibilityLabel}
70
67
  textAlignVertical="top"
71
68
  ref={inputRef}
72
69
  style={[
73
70
  styles.searchInput,
74
71
  {
75
- color: textColor,
72
+ color: theme.colors.text.default,
76
73
  },
77
74
  ]}
78
75
  value={value}
@@ -81,7 +78,7 @@ const InputSearch = forwardRef(
81
78
  onBlur={_onBlur}
82
79
  placeholder={placeholder}
83
80
  selectionColor={theme.colors.primary}
84
- placeholderTextColor={placeholderColor}
81
+ placeholderTextColor={theme.colors.text.hint}
85
82
  />
86
83
  );
87
84
  };
@@ -112,7 +109,7 @@ const InputSearch = forwardRef(
112
109
  ]}
113
110
  />
114
111
  <Icon
115
- color={iconTintColor}
112
+ color={iconColor}
116
113
  source={icon}
117
114
  style={styles.iconSearchInput}
118
115
  />
@@ -124,7 +121,7 @@ const InputSearch = forwardRef(
124
121
  return (
125
122
  <View
126
123
  style={[style, styles.searchInputContainer]}
127
- accessibilityLabel={`InputSearch/${placeholder}`}>
124
+ accessibilityLabel={accessibilityLabel}>
128
125
  <View
129
126
  style={[
130
127
  styles.searchInputWrapper,
@@ -37,6 +37,7 @@ const InputTextArea = forwardRef(
37
37
  maxLength,
38
38
  required,
39
39
  errorSpacing,
40
+ style,
40
41
  ...props
41
42
  }: InputTextAreaProps,
42
43
  ref,
@@ -45,6 +46,7 @@ const InputTextArea = forwardRef(
45
46
  const [focused, setFocused] = useState(false);
46
47
  const [inputValue, setInputValue] = useState(defaultValue ?? '');
47
48
  const inputRef = (ref as React.RefObject<TextInput>) ?? useRef<TextInput>();
49
+ const accessibilityLabel = `InputTextArea/${floatingValue ?? placeholder}`;
48
50
 
49
51
  const onClearText = () => {
50
52
  inputRef?.current?.clear();
@@ -106,7 +108,7 @@ const InputTextArea = forwardRef(
106
108
  <View style={styles.textAreaView}>
107
109
  <TextInput
108
110
  {...props}
109
- accessibilityLabel={`TextInput`}
111
+ accessibilityLabel={accessibilityLabel}
110
112
  ref={inputRef}
111
113
  editable={!disabled}
112
114
  textAlignVertical="top"
@@ -147,8 +149,8 @@ const InputTextArea = forwardRef(
147
149
 
148
150
  return (
149
151
  <View
150
- style={styles.wrapper}
151
- accessibilityLabel={`InputTextArea/${floatingValue}`}>
152
+ style={[style, styles.wrapper]}
153
+ accessibilityLabel={accessibilityLabel}>
152
154
  {renderInputView()}
153
155
  <ErrorView errorMessage={errorMessage} errorSpacing={errorSpacing} />
154
156
  </View>
package/Input/common.tsx CHANGED
@@ -1,6 +1,6 @@
1
1
  import {View, ViewStyle} from 'react-native';
2
2
  import styles from './styles';
3
- import {Text} from '../Text';
3
+ import {scaleSize, Text} from '../Text';
4
4
  import React, {FC, useContext} from 'react';
5
5
  import {ApplicationContext} from '../Application';
6
6
  import {Icon} from '../Icon';
@@ -16,7 +16,7 @@ type FloatingViewProps = {
16
16
  style?: ViewStyle;
17
17
  };
18
18
 
19
- export const DEFAULT_HEIGHT = 112;
19
+ export const DEFAULT_HEIGHT = scaleSize(104);
20
20
  export const MAX_LENGTH = 300;
21
21
 
22
22
  export const getBorderColor = (
package/Input/index.tsx CHANGED
@@ -4,7 +4,7 @@ import InputSearch from './InputSearch';
4
4
  import InputMoney from './InputMoney';
5
5
  import InputOTP from './InputOTP';
6
6
  import InputDropDown from './InputDropDown';
7
- import {TextInputProps} from 'react-native';
7
+ import {TextInputProps, ViewStyle} from 'react-native';
8
8
 
9
9
  export interface InputProps extends TextInputProps {
10
10
  /**
@@ -238,6 +238,8 @@ export interface InputDropDownProps {
238
238
  * Optional. Represents the color of the leading icon in the InputDropDown component.
239
239
  */
240
240
  leadingIconColor?: string;
241
+
242
+ style: ViewStyle | ViewStyle[];
241
243
  }
242
244
 
243
245
  export {Input, InputTextArea, InputSearch, InputMoney, InputOTP, InputDropDown};
package/Layout/Card.tsx CHANGED
@@ -7,16 +7,23 @@ import Item from './Item';
7
7
  import {Image} from '../Image';
8
8
  import {ApplicationContext} from '../Application';
9
9
  import styles from './styles';
10
- import {Colors, Spacing} from '../Consts';
10
+ import {Colors, Shadow, Spacing} from '../Consts';
11
11
 
12
12
  export interface CardProps extends ViewProps {
13
13
  /**
14
14
  * Optional. Background image for the card.
15
15
  */
16
16
  backgroundImage?: string;
17
+
18
+ useShadow?: boolean;
17
19
  }
18
20
 
19
- const Card: React.FC<CardProps> = ({children, style, backgroundImage}) => {
21
+ const Card: React.FC<CardProps> = ({
22
+ children,
23
+ style,
24
+ backgroundImage,
25
+ useShadow = false,
26
+ }) => {
20
27
  const {showGrid} = useContext(ApplicationContext);
21
28
  const {numberOfColumns} = useGridSystem();
22
29
 
@@ -127,6 +134,7 @@ const Card: React.FC<CardProps> = ({children, style, backgroundImage}) => {
127
134
  return element;
128
135
  };
129
136
 
137
+ const {theme} = useContext(ApplicationContext);
130
138
  return (
131
139
  <GridContext.Provider value={gridContext}>
132
140
  <View
@@ -137,12 +145,14 @@ const Card: React.FC<CardProps> = ({children, style, backgroundImage}) => {
137
145
  {
138
146
  width: widthSection,
139
147
  marginHorizontal: gridContext.margin,
148
+ backgroundColor: theme.colors.background.surface,
140
149
  },
150
+ useShadow && Shadow.Light,
141
151
  ]}>
142
152
  {!!backgroundImage && (
143
153
  <Image
144
154
  source={{uri: backgroundImage}}
145
- style={[style, styles.imageBackground]}
155
+ style={styles.imageBackground}
146
156
  />
147
157
  )}
148
158
  {renderView(validateChildren(children, [Item]))}
package/Layout/Screen.tsx CHANGED
@@ -1,6 +1,9 @@
1
1
  import {
2
2
  Animated,
3
+ Image,
3
4
  KeyboardAvoidingView,
5
+ NativeScrollEvent,
6
+ NativeSyntheticEvent,
4
7
  Platform,
5
8
  ScrollViewProps,
6
9
  View,
@@ -8,13 +11,26 @@ import {
8
11
  } from 'react-native';
9
12
  import {useHeaderHeight} from '@react-navigation/stack';
10
13
  import {useSafeAreaInsets} from 'react-native-safe-area-context';
11
- import React, {ReactNode, useContext, useEffect, useRef} from 'react';
12
- import {Spacing, Styles} from '../Consts';
14
+ import React, {
15
+ forwardRef,
16
+ ReactNode,
17
+ useContext,
18
+ useEffect,
19
+ useRef,
20
+ } from 'react';
21
+ import {Colors, Spacing, Styles} from '../Consts';
13
22
  import {Card, Section, validateChildren} from './index';
14
- import {ApplicationContext, HeaderTitle} from '../Application';
23
+ import {
24
+ ApplicationContext,
25
+ HeaderBackground,
26
+ HeaderTitle,
27
+ } from '../Application';
15
28
  import Navigation from '../Application/Navigation';
29
+ import styles from './styles';
30
+ import {AnimatedHeader, NavigationOptions} from '../Application/types';
16
31
 
17
32
  export interface ScreenProps extends ViewProps {
33
+ children: React.ReactNode;
18
34
  /**
19
35
  * Optional. Navigation object for handling navigation actions.
20
36
  */
@@ -25,25 +41,20 @@ export interface ScreenProps extends ViewProps {
25
41
  */
26
42
  enableKeyboardAvoidingView?: boolean;
27
43
 
28
- /**
29
- * Optional. Background color of the header.
30
- */
31
- headerBackground?: string;
32
-
33
44
  /**
34
45
  * Optional. If `true`, the screen content is scrollable.
35
46
  */
36
47
  scrollable?: boolean;
37
48
 
38
49
  /**
39
- * Optional. If `true`, use an animated header for the screen.
50
+ * Optional. If `true`, use an extended header for the screen.
40
51
  */
41
- useAnimatedHeader?: boolean;
52
+ useExtendedHeader?: boolean;
42
53
 
43
54
  /**
44
- * Optional. If `true`, use an extended header for the screen.
55
+ * Optional. Props for the animated component or bool used top navigation animated.
45
56
  */
46
- useExtendedHeader?: boolean;
57
+ animatedHeader?: AnimatedHeader;
47
58
 
48
59
  /**
49
60
  * Optional. Props for the underlying ScrollView component when `scrollable` is `true`.
@@ -51,206 +62,242 @@ export interface ScreenProps extends ViewProps {
51
62
  scrollViewProps?: ScrollViewProps;
52
63
 
53
64
  /**
54
- * Optional. ReactNode representing the footer component of the screen.
65
+ * Optional. ReactNode representing the header component of the screen.
55
66
  */
56
- footerComponent?: ReactNode;
67
+ headerComponent?: ReactNode;
57
68
 
58
69
  /**
59
- * Optional. Margin for the header component.
70
+ * Optional. ReactNode representing the footer component of the screen.
60
71
  */
61
- marginHeader?: number;
72
+ footerComponent?: ReactNode;
62
73
 
63
74
  /**
64
75
  * Optional. Background color of the screen.
65
76
  */
66
77
  backgroundColor?: string;
67
- }
68
-
69
- const Screen: React.FC<ScreenProps> = ({
70
- children,
71
- enableKeyboardAvoidingView = false,
72
- scrollable = false,
73
- navigation,
74
- marginHeader,
75
- useAnimatedHeader,
76
- useExtendedHeader,
77
- headerBackground,
78
- scrollViewProps,
79
- footerComponent: Footer,
80
- backgroundColor,
81
- }) => {
82
- const animatedValue = useRef<Animated.Value | undefined>(undefined);
83
- const {theme} = useContext(ApplicationContext);
84
- const insets = useSafeAreaInsets();
85
- const headerTransparent = useAnimatedHeader || useExtendedHeader;
86
-
87
- let handleScroll;
88
- let imageAnimatedStyle = {};
89
- let opacity;
90
- let Component: any = View;
91
- animatedValue.current = undefined;
92
78
 
93
79
  /**
94
- * automatic margin content if use header transparent
80
+ * Optional. layout card offset overlap header banner.
95
81
  */
96
- if (headerTransparent) {
97
- animatedValue.current = new Animated.Value(0);
98
- marginHeader = marginHeader ?? useHeaderHeight();
82
+ layoutOffset?: -8 | -24 | -56;
83
+ }
84
+
85
+ const Screen = forwardRef(
86
+ (
87
+ {
88
+ children,
89
+ enableKeyboardAvoidingView = false,
90
+ scrollable = false,
91
+ navigation,
92
+ useExtendedHeader,
93
+ animatedHeader,
94
+ scrollViewProps,
95
+ headerComponent: Header,
96
+ footerComponent: Footer,
97
+ layoutOffset = -56,
98
+ backgroundColor,
99
+ }: ScreenProps,
100
+ ref,
101
+ ) => {
102
+ const animatedValue = useRef<Animated.Value | undefined>(undefined);
103
+ const {theme} = useContext(ApplicationContext);
104
+ const insets = useSafeAreaInsets();
105
+ const heightHeader = useHeaderHeight();
106
+ const currentTint = useRef(Colors.black_01);
107
+ let styleAnimatedHeader: NavigationOptions;
108
+ let handleScroll;
109
+ let headerBackground: string | undefined = undefined;
110
+ let Component: any = View;
111
+ animatedValue.current = undefined;
112
+
99
113
  if (useExtendedHeader) {
100
114
  headerBackground = theme.assets?.headerBackground;
101
115
  }
102
- }
103
116
 
104
- useEffect(() => {
105
- if (useAnimatedHeader != undefined || useExtendedHeader != undefined) {
106
- let title = {};
117
+ useEffect(() => {
107
118
  if (useExtendedHeader) {
108
- title = {headerTitle: HeaderTitle};
119
+ navigation?.setOptions({
120
+ headerTransparent: true,
121
+ headerBackground: () => null,
122
+ });
109
123
  }
110
- navigation?.setOptions({
111
- animatedValue: animatedValue.current,
112
- headerTransparent: headerTransparent,
113
- ...title,
114
- });
115
- }
116
- }, [useExtendedHeader, useAnimatedHeader]);
124
+ }, [useExtendedHeader]);
117
125
 
118
- /**
119
- * animated for header stack when used bottom tab
120
- */
121
- useEffect(() => {
122
- const current: any = navigation;
123
- if (headerTransparent) {
124
- return current?.addListener?.('focus', () => {
125
- const stackNavigation = new Navigation(current?.getParent(), theme);
126
- stackNavigation?.setOptions({
127
- animatedValue: animatedValue.current,
128
- });
129
- });
130
- }
131
- }, [useAnimatedHeader, useExtendedHeader, navigation]);
126
+ useEffect(() => {
127
+ if (animatedHeader) {
128
+ styleAnimatedHeader = {
129
+ headerTransparent: true,
130
+ headerBackground: (props: any) => (
131
+ <HeaderBackground
132
+ {...props}
133
+ animatedValue={animatedValue.current}
134
+ />
135
+ ),
136
+ headerTitle: (props: any) => (
137
+ <HeaderTitle {...props} animatedValue={animatedValue.current} />
138
+ ),
139
+ };
140
+ if (animatedHeader.type == 'surface') {
141
+ styleAnimatedHeader = {
142
+ headerTransparent: true,
143
+ headerBackground: (props: any) => (
144
+ <HeaderBackground
145
+ {...props}
146
+ image={null}
147
+ backgroundColor={theme.colors.background.surface}
148
+ useBorder={true}
149
+ animatedValue={animatedValue.current}
150
+ />
151
+ ),
152
+ };
153
+ }
154
+ navigation?.setOptions(styleAnimatedHeader);
155
+ const current: any = navigation;
156
+ const parent = current?.getParent?.();
157
+ if (parent) {
158
+ return current?.addListener?.('focus', () => {
159
+ const stackNavigation = new Navigation(parent, theme);
160
+ stackNavigation?.setOptions(styleAnimatedHeader);
161
+ });
162
+ }
163
+ }
164
+ }, [animatedHeader]);
132
165
 
133
- /**
134
- * animated when use scroll && animated value
135
- */
136
- if (scrollable) {
137
- Component = Animated.ScrollView;
138
- if (useAnimatedHeader) {
139
- opacity = animatedValue.current?.interpolate({
140
- inputRange: [0, 100],
141
- outputRange: [1, 0],
142
- extrapolate: 'clamp',
143
- });
144
- imageAnimatedStyle = {
145
- transform: [
146
- {
147
- scale: animatedValue.current?.interpolate?.({
148
- inputRange: [-300, 0, 1],
149
- outputRange: [1.75, 1, 1],
150
- extrapolate: 'clamp',
151
- }),
152
- },
153
- ],
154
- };
155
- handleScroll = Animated.event(
156
- [
166
+ /**
167
+ * animated when use scroll && animated value
168
+ */
169
+ if (scrollable) {
170
+ Component = Animated.ScrollView;
171
+ handleScroll = scrollViewProps?.onScroll;
172
+ if (animatedHeader) {
173
+ animatedValue.current = new Animated.Value(0);
174
+ handleScroll = Animated.event(
175
+ [
176
+ {
177
+ nativeEvent: {
178
+ contentOffset: {y: animatedValue.current as Animated.Value},
179
+ },
180
+ },
181
+ ],
157
182
  {
158
- nativeEvent: {
159
- contentOffset: {y: animatedValue.current as Animated.Value},
183
+ useNativeDriver: true,
184
+ listener: (e: NativeSyntheticEvent<NativeScrollEvent>) => {
185
+ if (animatedHeader.type == 'surface') {
186
+ const offsetY = e.nativeEvent.contentOffset.y;
187
+ let color = Colors.black_01;
188
+ if (offsetY > 50) {
189
+ color = theme.colors.text.default;
190
+ }
191
+ if (color != currentTint.current) {
192
+ currentTint.current = color;
193
+ navigation?.setOptions({
194
+ headerTintColor: color,
195
+ });
196
+ }
197
+ }
198
+ scrollViewProps?.onScroll?.(e);
160
199
  },
161
200
  },
162
- ],
163
- {useNativeDriver: true},
164
- );
201
+ );
202
+ }
165
203
  }
166
- }
167
204
 
168
- /**
169
- * build content for screen
170
- */
171
- const renderContent = () => {
172
- const results = validateChildren(children, [Section, Card]);
173
- if (Array.isArray(results)) {
174
- return results.map((item, index) => {
175
- const space = item.props?.useMargin === false ? 0 : Spacing.M;
205
+ /**
206
+ * render top navigation banner
207
+ */
208
+ const renderHeader = () => {
209
+ if (typeof animatedHeader?.component === 'function') {
210
+ return animatedHeader?.component({
211
+ animatedValue: animatedValue.current,
212
+ });
213
+ }
214
+ };
215
+
216
+ /**
217
+ * build content for screen
218
+ */
219
+ const renderContent = () => {
220
+ const results = validateChildren(children, [Section, Card]);
221
+ if (Array.isArray(results)) {
222
+ return results.map((item, index) => {
223
+ const space = item.props?.useMargin === false ? 0 : Spacing.M;
224
+ return (
225
+ <View
226
+ key={`ScreenItem${index}`}
227
+ style={{
228
+ marginTop: space,
229
+ flex: item.props?.expanded ? 1 : undefined,
230
+ }}>
231
+ {item}
232
+ </View>
233
+ );
234
+ });
235
+ } else {
236
+ const item: any = children;
237
+ const space = item?.props?.useMargin === false ? 0 : Spacing.M;
176
238
  return (
177
239
  <View
178
- key={`ScreenItem${index}`}
240
+ key={`ScreenItem`}
179
241
  style={{
180
242
  marginTop: space,
181
- flex: item.props?.expanded ? 1 : undefined,
243
+ flex: item?.props?.expanded ? 1 : undefined,
182
244
  }}>
183
- {item}
245
+ {children}
184
246
  </View>
185
247
  );
186
- });
187
- } else {
188
- const item: any = children;
189
- const space = item?.props?.useMargin === false ? 0 : Spacing.M;
190
- return (
191
- <View
192
- key={`ScreenItem`}
193
- style={{
194
- marginTop: space,
195
- flex: item?.props?.expanded ? 1 : undefined,
196
- }}>
197
- {children}
198
- </View>
199
- );
200
- }
201
- };
248
+ }
249
+ };
202
250
 
203
- return (
204
- <View
205
- style={[Styles.flex, {backgroundColor}]}
206
- accessibilityLabel={`ScreenContainer`}>
251
+ return (
207
252
  <View
208
- style={{
209
- position: 'absolute',
210
- maxHeight: 215,
211
- width: '100%',
212
- overflow: 'hidden',
213
- }}>
253
+ style={[Styles.flex, {backgroundColor}]}
254
+ accessibilityLabel={`ScreenContainer`}>
214
255
  {!!headerBackground && (
215
- <Animated.Image
216
- source={{
217
- uri: headerBackground,
218
- }}
219
- style={{
220
- opacity: opacity ?? 1,
221
- aspectRatio: 1.75,
222
- ...imageAnimatedStyle,
223
- }}
224
- />
256
+ <>
257
+ <Image
258
+ source={{
259
+ uri: headerBackground,
260
+ }}
261
+ style={styles.extendedHeader}
262
+ />
263
+ <View style={{height: heightHeader}} />
264
+ </>
225
265
  )}
266
+ <KeyboardAvoidingView
267
+ style={Styles.flex}
268
+ enabled={enableKeyboardAvoidingView}
269
+ behavior={Platform.select({
270
+ ios: 'padding',
271
+ android: undefined,
272
+ })}>
273
+ {Header}
274
+ <Component
275
+ {...scrollViewProps}
276
+ ref={ref}
277
+ heightHeader={heightHeader}
278
+ showsVerticalScrollIndicator={false}
279
+ onScroll={handleScroll}
280
+ style={Styles.flex}>
281
+ <View
282
+ style={[styles.screenBanner, {maxHeight: 210 + layoutOffset}]}>
283
+ {renderHeader()}
284
+ </View>
285
+ {renderContent()}
286
+ </Component>
287
+ {Footer && (
288
+ <View
289
+ style={{
290
+ paddingTop: Spacing.S,
291
+ paddingBottom: Math.min(insets.bottom, 20) + Spacing.S,
292
+ backgroundColor: theme.colors.background.surface,
293
+ }}>
294
+ <Section>{Footer}</Section>
295
+ </View>
296
+ )}
297
+ </KeyboardAvoidingView>
226
298
  </View>
227
- <KeyboardAvoidingView
228
- style={Styles.flex}
229
- enabled={enableKeyboardAvoidingView}
230
- behavior={Platform.select({
231
- ios: 'padding',
232
- android: undefined,
233
- })}>
234
- <Component
235
- {...scrollViewProps}
236
- showsVerticalScrollIndicator={false}
237
- onScroll={handleScroll}
238
- style={[Styles.flex, {marginTop: marginHeader}]}>
239
- {renderContent()}
240
- </Component>
241
- {Footer && (
242
- <View
243
- style={{
244
- paddingTop: Spacing.S,
245
- paddingBottom: Math.min(insets.bottom, 20) + Spacing.S,
246
- backgroundColor: theme.colors.background.surface,
247
- }}>
248
- <Section>{Footer}</Section>
249
- </View>
250
- )}
251
- </KeyboardAvoidingView>
252
- </View>
253
- );
254
- };
299
+ );
300
+ },
301
+ );
255
302
 
256
303
  export default Screen;
package/Layout/styles.ts CHANGED
@@ -1,6 +1,16 @@
1
1
  import {StyleSheet} from 'react-native';
2
+ import {Radius, Spacing} from '../Consts';
2
3
 
3
4
  export default StyleSheet.create({
5
+ extendedHeader: {
6
+ aspectRatio: 1.75,
7
+ position: 'absolute',
8
+ width: '100%',
9
+ height: 210,
10
+ },
11
+ screenBanner: {
12
+ width: '100%',
13
+ },
4
14
  boxGrid: {
5
15
  position: 'absolute',
6
16
  top: 0,
@@ -34,6 +44,7 @@ export default StyleSheet.create({
34
44
  card: {
35
45
  flexDirection: 'row',
36
46
  flexWrap: 'wrap',
47
+ borderRadius: Radius.M,
37
48
  paddingTop: undefined,
38
49
  paddingBottom: undefined,
39
50
  paddingLeft: undefined,
@@ -64,5 +75,6 @@ export default StyleSheet.create({
64
75
  bottom: 0,
65
76
  left: 0,
66
77
  right: 0,
78
+ borderRadius: Spacing.M,
67
79
  },
68
80
  });