@momo-kits/foundation 0.80.8 → 0.81.1-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.
Files changed (87) hide show
  1. package/Application/BottomSheet.tsx +212 -0
  2. package/Application/BottomTab/BottomTabBar.tsx +278 -0
  3. package/Application/BottomTab/index.tsx +143 -0
  4. package/Application/Components.tsx +384 -213
  5. package/Application/ModalScreen.tsx +14 -178
  6. package/Application/Navigation.ts +15 -19
  7. package/Application/NavigationContainer.tsx +66 -63
  8. package/Application/Navigator.ts +106 -53
  9. package/Application/StackScreen.tsx +123 -9
  10. package/Application/index.ts +19 -6
  11. package/Application/types.ts +83 -26
  12. package/Application/utils.tsx +62 -74
  13. package/Assets/icon.json +83 -5
  14. package/Assets/icon_bank.json +506 -0
  15. package/Assets/language.json +3 -1
  16. package/Badge/Badge.tsx +56 -0
  17. package/Badge/BadgeDot.tsx +11 -0
  18. package/Badge/BadgeRibbon.tsx +125 -0
  19. package/Badge/Shape.tsx +29 -0
  20. package/Badge/index.tsx +7 -0
  21. package/Badge/styles.ts +58 -0
  22. package/Badge/types.ts +63 -0
  23. package/Button/index.tsx +64 -28
  24. package/Button/styles.ts +19 -0
  25. package/CheckBox/index.tsx +37 -27
  26. package/CheckBox/types.ts +5 -3
  27. package/Consts/colors+spacing+radius.ts +18 -0
  28. package/Icon/index.tsx +14 -9
  29. package/Icon/types.ts +4 -3
  30. package/IconButton/index.tsx +24 -15
  31. package/Image/index.tsx +62 -47
  32. package/Image/styles.ts +1 -9
  33. package/Image/types.ts +5 -1
  34. package/Input/Input.tsx +104 -25
  35. package/Input/InputDropDown.tsx +115 -110
  36. package/Input/InputMoney.tsx +79 -18
  37. package/Input/InputOTP.tsx +190 -149
  38. package/Input/InputSearch.tsx +96 -44
  39. package/Input/InputTextArea.tsx +33 -17
  40. package/Input/common.tsx +26 -17
  41. package/Input/index.tsx +108 -41
  42. package/Input/styles.ts +5 -7
  43. package/Input/utils.ts +1 -1
  44. package/Layout/AnimatedInputSearch.tsx +44 -0
  45. package/Layout/Card.tsx +37 -13
  46. package/Layout/FloatingButton.tsx +168 -0
  47. package/Layout/GridSystem.tsx +2 -3
  48. package/Layout/Item.tsx +1 -2
  49. package/Layout/ItemList.tsx +55 -0
  50. package/Layout/ItemSectionList.tsx +39 -0
  51. package/Layout/Screen.tsx +404 -181
  52. package/Layout/Section.tsx +22 -22
  53. package/Layout/TextTyping.tsx +114 -0
  54. package/Layout/index.ts +12 -2
  55. package/Layout/styles.ts +46 -13
  56. package/Layout/types.ts +13 -5
  57. package/Layout/utils.ts +7 -12
  58. package/Loader/index.tsx +2 -0
  59. package/Pagination/PaginationDot.tsx +6 -1
  60. package/Pagination/PaginationNumber.tsx +7 -4
  61. package/Pagination/PaginationScroll.tsx +0 -1
  62. package/Pagination/PaginationWhiteDot.tsx +6 -1
  63. package/Pagination/styles.ts +3 -0
  64. package/Pagination/types.ts +2 -0
  65. package/Popup/PopupNotify.tsx +108 -40
  66. package/Popup/PopupPromotion.tsx +45 -10
  67. package/Popup/types.ts +13 -10
  68. package/Radio/index.tsx +35 -21
  69. package/Radio/types.ts +7 -5
  70. package/Skeleton/index.tsx +1 -1
  71. package/Switch/index.tsx +32 -19
  72. package/Switch/types.ts +5 -3
  73. package/Tag/index.tsx +2 -4
  74. package/Text/index.tsx +26 -6
  75. package/Text/styles.ts +38 -0
  76. package/Text/types.ts +25 -13
  77. package/Title/index.tsx +177 -64
  78. package/Title/styles.ts +48 -15
  79. package/Title/types.ts +16 -7
  80. package/code-scanner.js +125 -16
  81. package/index.ts +9 -0
  82. package/migrate_typo.js +56 -0
  83. package/package.json +8 -7
  84. package/publish.sh +21 -16
  85. package/tsconfig.json +1 -2
  86. package/verify.js +24 -6
  87. package/Application/BottomTab.tsx +0 -65
package/Layout/Screen.tsx CHANGED
@@ -1,20 +1,52 @@
1
+ import {useHeaderHeight} from '@react-navigation/stack';
2
+ import React, {
3
+ forwardRef,
4
+ Fragment,
5
+ ReactNode,
6
+ useContext,
7
+ useEffect,
8
+ useRef,
9
+ useState,
10
+ } from 'react';
1
11
  import {
2
12
  Animated,
3
13
  KeyboardAvoidingView,
14
+ LayoutChangeEvent,
15
+ NativeScrollEvent,
16
+ NativeSyntheticEvent,
4
17
  Platform,
5
18
  ScrollViewProps,
6
19
  View,
7
20
  ViewProps,
8
21
  } from 'react-native';
9
- import {useHeaderHeight} from '@react-navigation/stack';
10
22
  import {useSafeAreaInsets} from 'react-native-safe-area-context';
11
- import React, {ReactNode, useContext, useEffect, useRef} from 'react';
12
- import {Spacing, Styles} from '../Consts';
13
- 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 {
30
+ AnimatedHeader,
31
+ HeaderRightActions,
32
+ HeaderRightToolkit,
33
+ NavigationOptions,
34
+ } from '../Application/types';
35
+ import {Colors, Spacing, Styles} from '../Consts';
36
+ import {FloatingButton, FloatingButtonProps} from './FloatingButton';
37
+ import {Image} from '../Image';
38
+ import {Card, Section, validateChildren} from './index';
39
+ import styles from './styles';
40
+ import {HeaderType} from './types';
41
+ import AnimatedInputSearch from './AnimatedInputSearch';
42
+ import {InputSearchProps} from '../Input';
43
+ import {HeaderRight} from '../Application/Components';
16
44
 
17
45
  export interface ScreenProps extends ViewProps {
46
+ /**
47
+ * ReactNode representing the content of the screen.
48
+ */
49
+ children: React.ReactNode;
18
50
  /**
19
51
  * Optional. Navigation object for handling navigation actions.
20
52
  */
@@ -25,25 +57,20 @@ export interface ScreenProps extends ViewProps {
25
57
  */
26
58
  enableKeyboardAvoidingView?: boolean;
27
59
 
28
- /**
29
- * Optional. Background color of the header.
30
- */
31
- headerBackground?: string;
32
-
33
60
  /**
34
61
  * Optional. If `true`, the screen content is scrollable.
35
62
  */
36
63
  scrollable?: boolean;
37
64
 
38
65
  /**
39
- * Optional. If `true`, use an animated header for the screen.
66
+ * Optional. Set type of header.
40
67
  */
41
- useAnimatedHeader?: boolean;
68
+ headerType?: HeaderType;
42
69
 
43
70
  /**
44
- * Optional. If `true`, use an extended header for the screen.
71
+ * Optional. Props for the animated component or bool used top navigation animated.
45
72
  */
46
- useExtendedHeader?: boolean;
73
+ animatedHeader?: AnimatedHeader;
47
74
 
48
75
  /**
49
76
  * Optional. Props for the underlying ScrollView component when `scrollable` is `true`.
@@ -51,206 +78,402 @@ export interface ScreenProps extends ViewProps {
51
78
  scrollViewProps?: ScrollViewProps;
52
79
 
53
80
  /**
54
- * Optional. ReactNode representing the footer component of the screen.
81
+ * Optional. ReactNode representing the header component of the screen.
55
82
  */
56
- footerComponent?: ReactNode;
83
+ headerComponent?: ReactNode;
57
84
 
58
85
  /**
59
- * Optional. Margin for the header component.
86
+ * Optional. ReactNode representing the footer component of the screen.
60
87
  */
61
- marginHeader?: number;
88
+ footerComponent?: ReactNode;
62
89
 
63
90
  /**
64
91
  * Optional. Background color of the screen.
65
92
  */
66
93
  backgroundColor?: string;
67
- }
68
94
 
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;
95
+ /**
96
+ * Optional. layout card offset overlap header banner.
97
+ */
98
+ layoutOffset?: -8 | -24 | -56;
99
+
100
+ /**
101
+ * Optional. Props for the floating action button.
102
+ */
103
+ floatingButtonProps?: Omit<FloatingButtonProps, 'animatedValue' | 'bottom'>;
92
104
 
93
105
  /**
94
- * automatic margin content if use header transparent
106
+ * Optional. Keyboard vertical offset.
95
107
  */
96
- if (headerTransparent) {
97
- animatedValue.current = new Animated.Value(0);
98
- marginHeader = marginHeader ?? useHeaderHeight();
99
- if (useExtendedHeader) {
108
+ keyboardVerticalOffset?: number;
109
+
110
+ /**
111
+ * Optional. If `true`, the screen content is using grid layout by span.
112
+ */
113
+ useGridLayout?: boolean;
114
+
115
+ useSearchInput?: boolean;
116
+
117
+ inputSearchProps?: InputSearchProps;
118
+
119
+ headerRight?: HeaderRightActions | HeaderRightToolkit | CustomHeaderRight;
120
+ }
121
+
122
+ type CustomHeaderRight = (props: any) => Element;
123
+
124
+ const Screen = forwardRef(
125
+ (
126
+ {
127
+ children,
128
+ enableKeyboardAvoidingView = false,
129
+ scrollable = false,
130
+ navigation,
131
+ animatedHeader,
132
+ scrollViewProps,
133
+ headerComponent: Header,
134
+ footerComponent: Footer,
135
+ layoutOffset = -56,
136
+ backgroundColor,
137
+ headerType = 'default',
138
+ floatingButtonProps,
139
+ useGridLayout = true,
140
+ keyboardVerticalOffset,
141
+ useSearchInput = true,
142
+ inputSearchProps,
143
+ headerRight,
144
+ }: ScreenProps,
145
+ ref
146
+ ) => {
147
+ const animatedValue = useRef<Animated.Value>(new Animated.Value(0));
148
+ const {theme} = useContext(ApplicationContext);
149
+ const insets = useSafeAreaInsets();
150
+ const heightHeader = useHeaderHeight();
151
+ const scrollViewRef = useRef<any>(ref);
152
+ const currentTint = useRef(Colors.black_01);
153
+ const isTab = navigation?.instance?.getState?.()?.type === 'tab';
154
+ let [headerRightWidth, setHeaderRightWidth] = useState(0);
155
+
156
+ let styleAnimatedHeader: NavigationOptions;
157
+ let handleScroll;
158
+ let headerBackground: string | undefined = undefined;
159
+ let Component: any = View;
160
+ let keyboardOffset = heightHeader - 20;
161
+ if (headerType === 'extended' || animatedHeader) {
162
+ keyboardOffset = -20;
163
+ }
164
+
165
+ if (headerType === 'extended') {
100
166
  headerBackground = theme.assets?.headerBackground;
101
167
  }
102
- }
103
168
 
104
- useEffect(() => {
105
- if (useAnimatedHeader != undefined || useExtendedHeader != undefined) {
106
- let title = {};
107
- if (useExtendedHeader) {
108
- title = {headerTitle: HeaderTitle};
169
+ useEffect(() => {
170
+ if (animatedHeader) {
171
+ styleAnimatedHeader = {
172
+ headerTransparent: true,
173
+ headerBackground: (props: any) => (
174
+ <HeaderBackground
175
+ {...props}
176
+ animatedValue={animatedValue.current}
177
+ />
178
+ ),
179
+ headerTitle: (props: any) => (
180
+ <HeaderTitle {...props} animatedValue={animatedValue.current} />
181
+ ),
182
+ };
183
+ if (animatedHeader.type === 'surface') {
184
+ styleAnimatedHeader = {
185
+ ...styleAnimatedHeader,
186
+ headerBackground: (props: any) => (
187
+ <HeaderBackground
188
+ {...props}
189
+ image={null}
190
+ useSurface={true}
191
+ animatedValue={animatedValue.current}
192
+ />
193
+ ),
194
+ };
195
+ }
196
+ if (animatedHeader.headerTitle) {
197
+ styleAnimatedHeader = {
198
+ ...styleAnimatedHeader,
199
+ headerTitle: (props: any) =>
200
+ animatedHeader.headerTitle?.({
201
+ ...props,
202
+ animatedValue: animatedValue.current,
203
+ }),
204
+ };
205
+ }
206
+
207
+ navigation?.setOptions(styleAnimatedHeader);
109
208
  }
110
- navigation?.setOptions({
111
- animatedValue: animatedValue.current,
112
- headerTransparent: headerTransparent,
113
- ...title,
114
- });
115
- }
116
- }, [useExtendedHeader, useAnimatedHeader]);
209
+ }, [animatedHeader]);
117
210
 
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,
211
+ useEffect(() => {
212
+ if (!animatedHeader) {
213
+ let headerOptions: NavigationOptions;
214
+ switch (headerType) {
215
+ case 'none':
216
+ headerOptions = {
217
+ headerTransparent: true,
218
+ headerBackground: () => null,
219
+ headerShown: false,
220
+ };
221
+ break;
222
+
223
+ case 'surface':
224
+ headerOptions = {
225
+ headerTransparent: false,
226
+ headerShown: true,
227
+ headerTintColor: theme.colors.text.default,
228
+ headerBackground: (props: any) => (
229
+ <HeaderBackground {...props} image={null} useSurface={true} />
230
+ ),
231
+ headerTitle: (props: any) => <HeaderTitle {...props} />,
232
+ };
233
+ break;
234
+
235
+ case 'extended':
236
+ headerOptions = {
237
+ headerShown: true,
238
+ headerTransparent: true,
239
+ headerTintColor: Colors.black_01,
240
+ headerBackground: () => null,
241
+ headerTitle: (props: any) => (
242
+ <HeaderTitle
243
+ {...props}
244
+ useSearchInput={useSearchInput}
245
+ animatedValue={animatedValue.current}
246
+ />
247
+ ),
248
+ };
249
+ break;
250
+
251
+ default:
252
+ headerOptions = {
253
+ headerTransparent: false,
254
+ headerShown: true,
255
+ headerTintColor: Colors.black_01,
256
+ headerBackground: (props: any) => <HeaderBackground {...props} />,
257
+ };
258
+ }
259
+ navigation?.setOptions(headerOptions);
260
+ }
261
+ }, [headerType]);
262
+
263
+ useEffect(() => {
264
+ if (headerRight) {
265
+ navigation?.setOptions({
266
+ headerRight: (props: any) => (
267
+ <View
268
+ style={{backgroundColor: 'red'}}
269
+ onLayout={onHeaderRightLayout}>
270
+ {typeof headerRight === 'function' ? (
271
+ headerRight(props)
272
+ ) : (
273
+ <HeaderRight {...props} {...headerRight} />
274
+ )}
275
+ </View>
276
+ ),
128
277
  });
129
- });
130
- }
131
- }, [useAnimatedHeader, useExtendedHeader, navigation]);
278
+ }
279
+ }, [headerRight]);
132
280
 
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
- [
281
+ const onHeaderRightLayout = (e: LayoutChangeEvent) => {
282
+ setHeaderRightWidth(e.nativeEvent.layout.width);
283
+ };
284
+
285
+ /**
286
+ * animated when use scroll && animated value
287
+ */
288
+ if (scrollable) {
289
+ Component = Animated.ScrollView;
290
+ handleScroll = scrollViewProps?.onScroll;
291
+ if (animatedHeader || floatingButtonProps?.icon || useSearchInput) {
292
+ handleScroll = Animated.event(
293
+ [
294
+ {
295
+ nativeEvent: {
296
+ contentOffset: {y: animatedValue.current as Animated.Value},
297
+ },
298
+ },
299
+ ],
157
300
  {
158
- nativeEvent: {
159
- contentOffset: {y: animatedValue.current as Animated.Value},
301
+ useNativeDriver: false,
302
+ listener: (e: NativeSyntheticEvent<NativeScrollEvent>) => {
303
+ scrollViewProps?.onScroll?.(e);
304
+ if (animatedHeader?.type === 'surface') {
305
+ const offsetY = e.nativeEvent.contentOffset.y;
306
+ let color = Colors.black_01;
307
+ if (offsetY > 50) {
308
+ color = theme.colors.text.default;
309
+ }
310
+ if (color != currentTint.current) {
311
+ currentTint.current = color;
312
+ navigation?.setOptions({
313
+ headerTintColor: color,
314
+ });
315
+ }
316
+ }
160
317
  },
161
- },
162
- ],
163
- {useNativeDriver: true},
164
- );
318
+ }
319
+ );
320
+ }
165
321
  }
166
- }
167
322
 
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;
323
+ const handleScrollEnd = (e: NativeSyntheticEvent<NativeScrollEvent>) => {
324
+ const offsetY = e.nativeEvent.contentOffset.y;
325
+
326
+ if (useSearchInput && offsetY < 100 && offsetY > 0) {
327
+ Animated.timing(animatedValue.current, {
328
+ toValue: 0,
329
+ useNativeDriver: false,
330
+ duration: 300,
331
+ }).start();
332
+ scrollViewRef.current?.scrollTo({y: 0, animated: true});
333
+ }
334
+
335
+ scrollViewProps?.onScrollEndDrag?.(e);
336
+ };
337
+
338
+ /**
339
+ * render top navigation banner
340
+ */
341
+ const renderHeader = () => {
342
+ if (typeof animatedHeader?.component === 'function') {
343
+ return animatedHeader?.component({
344
+ animatedValue: animatedValue.current,
345
+ });
346
+ }
347
+ };
348
+
349
+ /**
350
+ * build content for screen
351
+ */
352
+ const renderContent = (children: any): any => {
353
+ const results = validateChildren(children, [Section, Card, undefined]);
354
+ if (Array.isArray(results)) {
355
+ return results.map((item, index) => {
356
+ const space = item?.props?.useMargin === false ? 0 : Spacing.M;
357
+ if (item?.type === Fragment) {
358
+ return renderContent(item?.props?.children);
359
+ }
360
+ if (item) {
361
+ return (
362
+ <View
363
+ key={`ScreenItem${index}`}
364
+ style={{
365
+ marginTop: space,
366
+ flex: item.props?.expanded ? 1 : undefined,
367
+ }}
368
+ onLayout={item.props?.onLayout}>
369
+ {item}
370
+ </View>
371
+ );
372
+ }
373
+ });
374
+ } else {
375
+ const item: any = children;
376
+ const space = item?.props?.useMargin === false ? 0 : Spacing.M;
377
+ if (item?.type === Fragment) {
378
+ return renderContent(item?.props?.children);
379
+ }
176
380
  return (
177
381
  <View
178
- key={`ScreenItem${index}`}
382
+ key={`ScreenItem`}
179
383
  style={{
180
384
  marginTop: space,
181
- flex: item.props?.expanded ? 1 : undefined,
182
- }}>
183
- {item}
385
+ flex: item?.props?.expanded ? 1 : undefined,
386
+ }}
387
+ onLayout={item?.props?.onLayout}>
388
+ {children}
184
389
  </View>
185
390
  );
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
- };
202
-
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
- }}>
391
+ }
392
+ };
393
+
394
+ return (
395
+ <View style={[Styles.flex, {backgroundColor}]}>
214
396
  {!!headerBackground && (
215
- <Animated.Image
216
- source={{
217
- uri: headerBackground,
218
- }}
219
- style={{
220
- opacity: opacity ?? 1,
221
- aspectRatio: 1.75,
222
- ...imageAnimatedStyle,
223
- }}
397
+ <>
398
+ <Image
399
+ source={{
400
+ uri: headerBackground,
401
+ }}
402
+ style={styles.extendedHeader}
403
+ />
404
+ <Animated.View
405
+ style={{
406
+ height: animatedValue.current.interpolate({
407
+ inputRange: [0, 100],
408
+ outputRange: [heightHeader, heightHeader - 52],
409
+ extrapolate: 'clamp',
410
+ }),
411
+ }}
412
+ />
413
+ </>
414
+ )}
415
+ {useSearchInput && (
416
+ <AnimatedInputSearch
417
+ animatedValue={animatedValue.current}
418
+ headerRightWidth={headerRightWidth}
419
+ headerType={headerType}
420
+ heightHeader={heightHeader}
421
+ inputSearchProps={inputSearchProps}
224
422
  />
225
423
  )}
424
+ <KeyboardAvoidingView
425
+ style={Styles.flex}
426
+ enabled={enableKeyboardAvoidingView}
427
+ keyboardVerticalOffset={keyboardVerticalOffset ?? keyboardOffset}
428
+ behavior={Platform.select({
429
+ ios: 'padding',
430
+ android: undefined,
431
+ })}>
432
+ {Header}
433
+
434
+ <Component
435
+ {...scrollViewProps}
436
+ ref={scrollViewRef}
437
+ showsVerticalScrollIndicator={false}
438
+ onScroll={handleScroll}
439
+ onScrollEndDrag={handleScrollEnd}
440
+ scrollEventThrottle={16}
441
+ style={Styles.flex}>
442
+ <View
443
+ style={[styles.screenBanner, {maxHeight: 210 + layoutOffset}]}>
444
+ {renderHeader()}
445
+ </View>
446
+ {useGridLayout ? renderContent(children) : children}
447
+ </Component>
448
+
449
+ {floatingButtonProps && (
450
+ <View>
451
+ <FloatingButton
452
+ {...floatingButtonProps}
453
+ animatedValue={animatedValue.current}
454
+ bottom={
455
+ Footer || isTab ? 12 : Math.min(insets.bottom, 21) + Spacing.S
456
+ }
457
+ />
458
+ </View>
459
+ )}
460
+
461
+ {Footer && (
462
+ <View
463
+ style={[
464
+ styles.shadow,
465
+ {
466
+ paddingBottom: Math.min(insets.bottom, 21) + Spacing.S,
467
+ backgroundColor: theme.colors.background.surface,
468
+ },
469
+ ]}>
470
+ <Section>{Footer}</Section>
471
+ </View>
472
+ )}
473
+ </KeyboardAvoidingView>
226
474
  </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
- };
475
+ );
476
+ }
477
+ );
255
478
 
256
479
  export default Screen;