@momo-kits/foundation 0.80.7 → 0.80.8-beta.10

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/Layout/Screen.tsx CHANGED
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  Animated,
3
+ Image,
3
4
  KeyboardAvoidingView,
4
5
  Platform,
5
6
  ScrollViewProps,
@@ -8,13 +9,24 @@ import {
8
9
  } from 'react-native';
9
10
  import {useHeaderHeight} from '@react-navigation/stack';
10
11
  import {useSafeAreaInsets} from 'react-native-safe-area-context';
11
- import React, {ReactNode, useContext, useEffect, useRef} from 'react';
12
+ import React, {
13
+ forwardRef,
14
+ ReactNode,
15
+ useContext,
16
+ useEffect,
17
+ useRef,
18
+ } from 'react';
12
19
  import {Spacing, Styles} from '../Consts';
13
20
  import {Card, Section, validateChildren} from './index';
14
- import {ApplicationContext, HeaderTitle} from '../Application';
21
+ import {
22
+ ApplicationContext,
23
+ HeaderBackground,
24
+ HeaderTitle,
25
+ } from '../Application';
15
26
  import Navigation from '../Application/Navigation';
16
27
 
17
28
  export interface ScreenProps extends ViewProps {
29
+ children: React.ReactNode;
18
30
  /**
19
31
  * Optional. Navigation object for handling navigation actions.
20
32
  */
@@ -25,31 +37,31 @@ export interface ScreenProps extends ViewProps {
25
37
  */
26
38
  enableKeyboardAvoidingView?: boolean;
27
39
 
28
- /**
29
- * Optional. Background color of the header.
30
- */
31
- headerBackground?: string;
32
-
33
40
  /**
34
41
  * Optional. If `true`, the screen content is scrollable.
35
42
  */
36
43
  scrollable?: boolean;
37
44
 
38
45
  /**
39
- * Optional. If `true`, use an animated header for the screen.
46
+ * Optional. If `true`, use an extended header for the screen.
40
47
  */
41
- useAnimatedHeader?: boolean;
48
+ useExtendedHeader?: boolean;
42
49
 
43
50
  /**
44
- * Optional. If `true`, use an extended header for the screen.
51
+ * Optional. Props for the animated component or bool used top navigation animated.
45
52
  */
46
- useExtendedHeader?: boolean;
53
+ animatedHeader?: boolean | ((props?: any) => React.ReactElement);
47
54
 
48
55
  /**
49
56
  * Optional. Props for the underlying ScrollView component when `scrollable` is `true`.
50
57
  */
51
58
  scrollViewProps?: ScrollViewProps;
52
59
 
60
+ /**
61
+ * Optional. ReactNode representing the header component of the screen.
62
+ */
63
+ headerComponent?: ReactNode;
64
+
53
65
  /**
54
66
  * Optional. ReactNode representing the footer component of the screen.
55
67
  */
@@ -66,191 +78,204 @@ export interface ScreenProps extends ViewProps {
66
78
  backgroundColor?: string;
67
79
  }
68
80
 
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;
81
+ const Screen = forwardRef(
82
+ (
83
+ {
84
+ children,
85
+ enableKeyboardAvoidingView = false,
86
+ scrollable = false,
87
+ navigation,
88
+ marginHeader,
89
+ useExtendedHeader,
90
+ animatedHeader,
91
+ scrollViewProps,
92
+ headerComponent: Header,
93
+ footerComponent: Footer,
94
+ backgroundColor,
95
+ }: ScreenProps,
96
+ ref,
97
+ ) => {
98
+ const animatedValue = useRef<Animated.Value | undefined>(undefined);
99
+ const {theme} = useContext(ApplicationContext);
100
+ const insets = useSafeAreaInsets();
86
101
 
87
- let handleScroll;
88
- let imageAnimatedStyle = {};
89
- let opacity;
90
- let Component: any = View;
91
- animatedValue.current = undefined;
102
+ let handleScroll;
103
+ let headerBackground: string | undefined = undefined;
104
+ let Component: any = View;
105
+ animatedValue.current = undefined;
106
+
107
+ if (useExtendedHeader || animatedHeader) {
108
+ marginHeader = marginHeader ?? useHeaderHeight();
109
+ }
92
110
 
93
- /**
94
- * automatic margin content if use header transparent
95
- */
96
- if (headerTransparent) {
97
- animatedValue.current = new Animated.Value(0);
98
- marginHeader = marginHeader ?? useHeaderHeight();
99
111
  if (useExtendedHeader) {
100
112
  headerBackground = theme.assets?.headerBackground;
101
113
  }
102
- }
103
114
 
104
- useEffect(() => {
105
- if (useAnimatedHeader != undefined || useExtendedHeader != undefined) {
106
- let title = {};
115
+ useEffect(() => {
107
116
  if (useExtendedHeader) {
108
- title = {headerTitle: HeaderTitle};
117
+ navigation?.setOptions({
118
+ headerTransparent: true,
119
+ headerBackground: () => null,
120
+ });
109
121
  }
110
- navigation?.setOptions({
111
- animatedValue: animatedValue.current,
112
- headerTransparent: headerTransparent,
113
- ...title,
114
- });
115
- }
116
- }, [useExtendedHeader, useAnimatedHeader]);
122
+ }, [useExtendedHeader]);
117
123
 
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,
124
+ useEffect(() => {
125
+ if (animatedHeader) {
126
+ navigation?.setOptions({
127
+ headerTransparent: true,
128
+ headerBackground: (props: any) => (
129
+ <HeaderBackground
130
+ {...props}
131
+ animatedValue={animatedValue.current}
132
+ />
133
+ ),
134
+ headerTitle: (props: any) => (
135
+ <HeaderTitle {...props} animatedValue={animatedValue.current} />
136
+ ),
128
137
  });
129
- });
130
- }
131
- }, [useAnimatedHeader, useExtendedHeader, navigation]);
138
+ const current: any = navigation;
139
+ const parent = current?.getParent?.();
140
+ if (parent) {
141
+ return current?.addListener?.('focus', () => {
142
+ const stackNavigation = new Navigation(parent, theme);
143
+ stackNavigation?.setOptions({
144
+ headerTransparent: true,
145
+ headerBackground: (props: any) => (
146
+ <HeaderBackground
147
+ {...props}
148
+ animatedValue={animatedValue.current}
149
+ />
150
+ ),
151
+ headerTitle: (props: any) => (
152
+ <HeaderTitle {...props} animatedValue={animatedValue.current} />
153
+ ),
154
+ });
155
+ });
156
+ }
157
+ }
158
+ }, [animatedHeader]);
132
159
 
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
- [
157
- {
158
- nativeEvent: {
159
- contentOffset: {y: animatedValue.current as Animated.Value},
160
+ /**
161
+ * animated when use scroll && animated value
162
+ */
163
+ if (scrollable) {
164
+ Component = Animated.ScrollView;
165
+ handleScroll = scrollViewProps?.onScroll;
166
+ if (animatedHeader) {
167
+ animatedValue.current = new Animated.Value(0);
168
+ handleScroll = Animated.event(
169
+ [
170
+ {
171
+ nativeEvent: {
172
+ contentOffset: {y: animatedValue.current as Animated.Value},
173
+ },
160
174
  },
161
- },
162
- ],
163
- {useNativeDriver: true},
164
- );
165
- }
166
- }
167
-
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;
176
- return (
177
- <View
178
- key={`ScreenItem${index}`}
179
- style={{
180
- marginTop: space,
181
- flex: item.props?.expanded ? 1 : undefined,
182
- }}>
183
- {item}
184
- </View>
175
+ ],
176
+ {useNativeDriver: true, listener: scrollViewProps?.onScroll},
185
177
  );
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
- );
178
+ }
200
179
  }
201
- };
202
180
 
203
- return (
204
- <View
205
- style={[Styles.flex, {backgroundColor}]}
206
- accessibilityLabel={`ScreenContainer`}>
207
- <View
208
- style={{
209
- position: 'absolute',
210
- maxHeight: 215,
211
- width: '100%',
212
- overflow: 'hidden',
213
- }}>
214
- {!!headerBackground && (
215
- <Animated.Image
181
+ /**
182
+ * render top navigation banner
183
+ */
184
+ const renderHeader = () => {
185
+ if (headerBackground != undefined) {
186
+ return (
187
+ <Image
216
188
  source={{
217
189
  uri: headerBackground,
218
190
  }}
219
191
  style={{
220
- opacity: opacity ?? 1,
221
192
  aspectRatio: 1.75,
222
- ...imageAnimatedStyle,
223
193
  }}
224
194
  />
225
- )}
226
- </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 && (
195
+ );
196
+ }
197
+ if (typeof animatedHeader === 'function') {
198
+ return animatedHeader({animatedValue: animatedValue.current});
199
+ }
200
+ };
201
+
202
+ /**
203
+ * build content for screen
204
+ */
205
+ const renderContent = () => {
206
+ const results = validateChildren(children, [Section, Card]);
207
+ if (Array.isArray(results)) {
208
+ return results.map((item, index) => {
209
+ const space = item.props?.useMargin === false ? 0 : Spacing.M;
210
+ return (
211
+ <View
212
+ key={`ScreenItem${index}`}
213
+ style={{
214
+ marginTop: space,
215
+ flex: item.props?.expanded ? 1 : undefined,
216
+ }}>
217
+ {item}
218
+ </View>
219
+ );
220
+ });
221
+ } else {
222
+ const item: any = children;
223
+ const space = item?.props?.useMargin === false ? 0 : Spacing.M;
224
+ return (
242
225
  <View
226
+ key={`ScreenItem`}
243
227
  style={{
244
- paddingTop: Spacing.S,
245
- paddingBottom: Math.min(insets.bottom, 20) + Spacing.S,
246
- backgroundColor: theme.colors.background.surface,
228
+ marginTop: space,
229
+ flex: item?.props?.expanded ? 1 : undefined,
247
230
  }}>
248
- <Section>{Footer}</Section>
231
+ {children}
249
232
  </View>
250
- )}
251
- </KeyboardAvoidingView>
252
- </View>
253
- );
254
- };
233
+ );
234
+ }
235
+ };
236
+
237
+ return (
238
+ <View
239
+ style={[Styles.flex, {backgroundColor}]}
240
+ accessibilityLabel={`ScreenContainer`}>
241
+ <View
242
+ style={{
243
+ position: 'absolute',
244
+ width: '100%',
245
+ maxHeight: 215,
246
+ }}>
247
+ {renderHeader()}
248
+ </View>
249
+ <KeyboardAvoidingView
250
+ style={[Styles.flex, {paddingTop: marginHeader}]}
251
+ enabled={enableKeyboardAvoidingView}
252
+ behavior={Platform.select({
253
+ ios: 'padding',
254
+ android: undefined,
255
+ })}>
256
+ {Header}
257
+ <Component
258
+ {...scrollViewProps}
259
+ ref={ref}
260
+ showsVerticalScrollIndicator={false}
261
+ onScroll={handleScroll}
262
+ style={Styles.flex}>
263
+ {renderContent()}
264
+ </Component>
265
+ {Footer && (
266
+ <View
267
+ style={{
268
+ paddingTop: Spacing.S,
269
+ paddingBottom: Math.min(insets.bottom, 20) + Spacing.S,
270
+ backgroundColor: theme.colors.background.surface,
271
+ }}>
272
+ <Section>{Footer}</Section>
273
+ </View>
274
+ )}
275
+ </KeyboardAvoidingView>
276
+ </View>
277
+ );
278
+ },
279
+ );
255
280
 
256
281
  export default Screen;
package/Layout/styles.ts CHANGED
@@ -47,6 +47,7 @@ export default StyleSheet.create({
47
47
  marginLeft: undefined,
48
48
  marginRight: undefined,
49
49
  marginVertical: undefined,
50
+ overflow: 'hidden',
50
51
  },
51
52
  item: {
52
53
  overflow: 'hidden',
@@ -9,6 +9,7 @@ import {ApplicationContext} from '../Application';
9
9
  const PaginationDot: FC<ChildPaginationProps> = ({
10
10
  dataLength = 2,
11
11
  activeIndex = 0,
12
+ style,
12
13
  }) => {
13
14
  const {theme} = useContext(ApplicationContext);
14
15
  const renderDots = () => {
@@ -27,7 +28,11 @@ const PaginationDot: FC<ChildPaginationProps> = ({
27
28
  }
28
29
  return dots;
29
30
  };
30
- return <View style={styles.paginationPinkContainer}>{renderDots()}</View>;
31
+ return (
32
+ <View style={[style, styles.row]}>
33
+ <View style={styles.paginationPinkContainer}>{renderDots()}</View>
34
+ </View>
35
+ );
31
36
  };
32
37
 
33
38
  export default PaginationDot;
@@ -8,12 +8,15 @@ import {Colors} from '../Consts';
8
8
  const PaginationNumber: FC<ChildPaginationProps> = ({
9
9
  activeIndex = 0,
10
10
  dataLength = 2,
11
+ style,
11
12
  }) => {
12
13
  return (
13
- <View style={[styles.paginationNumberContainer]}>
14
- <Text color={Colors.black_01} typography={'label_default'}>{`${
15
- activeIndex + 1
16
- }/${dataLength}`}</Text>
14
+ <View style={[style, styles.row]}>
15
+ <View style={styles.paginationNumberContainer}>
16
+ <Text color={Colors.black_01} typography={'label_default'}>{`${
17
+ activeIndex + 1
18
+ }/${dataLength}`}</Text>
19
+ </View>
17
20
  </View>
18
21
  );
19
22
  };
@@ -8,6 +8,7 @@ import {Colors, Spacing} from '../Consts';
8
8
  const PaginationWhiteDot: FC<ChildPaginationProps> = ({
9
9
  dataLength = 2,
10
10
  activeIndex = 0,
11
+ style,
11
12
  }) => {
12
13
  const renderDots = () => {
13
14
  const dots: React.ReactElement[] = [];
@@ -24,7 +25,11 @@ const PaginationWhiteDot: FC<ChildPaginationProps> = ({
24
25
  }
25
26
  return dots;
26
27
  };
27
- return <View style={[styles.paginationWhiteContainer]}>{renderDots()}</View>;
28
+ return (
29
+ <View style={[style, styles.row]}>
30
+ <View style={styles.paginationWhiteContainer}>{renderDots()}</View>
31
+ </View>
32
+ );
28
33
  };
29
34
 
30
35
  export default PaginationWhiteDot;
@@ -25,6 +25,8 @@ export default StyleSheet.create({
25
25
  paddingHorizontal: Spacing.S,
26
26
  borderRadius: Radius.L,
27
27
  backgroundColor: Colors.black_20 + '33',
28
+ alignItems: 'center',
29
+ justifyContent: 'center',
28
30
  },
29
31
  scrollContainer: {
30
32
  alignItems: 'center',
@@ -44,4 +46,5 @@ export default StyleSheet.create({
44
46
  height: 4,
45
47
  borderRadius: Radius.XS,
46
48
  },
49
+ row: {flexDirection: 'row'},
47
50
  });
@@ -20,6 +20,8 @@ export type PaginationProps = {
20
20
  * Represents the total number of items (pages) in the Pagination component.
21
21
  */
22
22
  dataLength: number;
23
+
24
+ style?: ViewStyle | ViewStyle[];
23
25
  };
24
26
 
25
27
  export type DotProps = {
package/Radio/types.ts CHANGED
@@ -5,7 +5,7 @@ export type RadioProps = {
5
5
  * `value`: Represents the value associated with the Radio component.
6
6
  * It is used to determine whether the Radio component is selected or not in comparison with the `groupValue`.
7
7
  */
8
- value: string;
8
+ value: any;
9
9
 
10
10
  /**
11
11
  * `disabled`: Optional. If `true`, the user won't be able to interact with the Radio component.
@@ -28,7 +28,7 @@ export type RadioProps = {
28
28
  * `groupValue`: Represents the value of the selected Radio component within a group of Radio components.
29
29
  * The Radio component for which `value` equals `groupValue` will be selected.
30
30
  */
31
- groupValue: string;
31
+ groupValue: any;
32
32
 
33
33
  /**
34
34
  * `style`: Optional. Used to apply custom styling to the Radio component.
package/Title/index.tsx CHANGED
@@ -5,7 +5,6 @@ import {Icon} from '../Icon';
5
5
  import styles from './styles';
6
6
  import {ApplicationContext} from '../Application';
7
7
  import {Colors, Spacing} from '../Consts';
8
- import {Button} from '../Button';
9
8
  import {scaleSize, Text} from '../Text';
10
9
 
11
10
  const Title: FC<TitleProps> = ({
package/Title/types.ts CHANGED
@@ -1,11 +1,51 @@
1
1
  export type TitleProps = {
2
+ /**
3
+ * Optional. Defines the type of Title component.
4
+ * 'screen' - Used for screen titles.
5
+ * 'section' - Used for section titles within a screen.
6
+ * 'card' - Used for titles within a card component.
7
+ */
2
8
  type?: 'screen' | 'section' | 'card';
9
+
10
+ /**
11
+ * Required. The text of the title.
12
+ */
3
13
  title: string;
14
+
15
+ /**
16
+ * Optional. A description or subtitle that appears below the title.
17
+ */
4
18
  description?: string;
19
+
20
+ /**
21
+ * Optional. Defines the size of the Title component.
22
+ * 'large' - A larger, more prominent title.
23
+ * 'small' - A smaller, less prominent title.
24
+ */
5
25
  size?: 'large' | 'small';
26
+
27
+ /**
28
+ * Optional. Represents the name or key of the icon to be displayed alongside the title.
29
+ */
6
30
  icon?: string;
31
+
32
+ /**
33
+ * Optional. If `true`, the title will be flexible in its container, adjusting its width dynamically.
34
+ */
7
35
  titleFlex?: boolean;
36
+
37
+ /**
38
+ * Optional. Text for an action button associated with the title.
39
+ */
8
40
  actionTitle?: string;
41
+
42
+ /**
43
+ * Optional. Function to be called when the action associated with the title is pressed.
44
+ */
9
45
  onPressAction?: () => void;
46
+
47
+ /**
48
+ * Optional. If `true`, an action button or icon is displayed alongside the title.
49
+ */
10
50
  showAction?: boolean;
11
51
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@momo-kits/foundation",
3
- "version": "0.80.7",
3
+ "version": "0.80.8-beta.10",
4
4
  "description": "React Native Component Kits",
5
5
  "main": "index.ts",
6
6
  "scripts": {
@@ -42,4 +42,4 @@
42
42
  },
43
43
  "author": "@momo-kits/foundation",
44
44
  "license": "ISC"
45
- }
45
+ }