@momo-kits/foundation 0.74.2-react-native.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.
- package/Application/BottomTab.tsx +120 -0
- package/Application/Components.tsx +534 -0
- package/Application/ModalScreen.tsx +268 -0
- package/Application/Navigation.ts +50 -0
- package/Application/NavigationContainer.tsx +120 -0
- package/Application/Navigator.ts +131 -0
- package/Application/StackScreen.tsx +71 -0
- package/Application/index.ts +41 -0
- package/Application/types.ts +216 -0
- package/Application/utils.tsx +151 -0
- package/Assets/DotAnimation.json +256 -0
- package/Assets/SpinnerAnimation.json +1027 -0
- package/Assets/icon.json +4031 -0
- package/Assets/icon_bank.json +506 -0
- package/Assets/language.json +202 -0
- package/Badge/Badge.tsx +56 -0
- package/Badge/BadgeDot.tsx +11 -0
- package/Badge/BadgeRibbon.tsx +125 -0
- package/Badge/Shape.tsx +29 -0
- package/Badge/index.tsx +7 -0
- package/Badge/styles.ts +58 -0
- package/Badge/types.ts +63 -0
- package/Button/index.tsx +310 -0
- package/Button/styles.ts +60 -0
- package/CheckBox/index.tsx +65 -0
- package/CheckBox/styles.ts +15 -0
- package/CheckBox/types.ts +38 -0
- package/Consts/colors+spacing+radius.ts +218 -0
- package/Consts/index.ts +4 -0
- package/Consts/styles.ts +52 -0
- package/Consts/theme.ts +125 -0
- package/Divider/index.tsx +21 -0
- package/Icon/index.tsx +41 -0
- package/Icon/types.ts +27 -0
- package/IconButton/index.tsx +139 -0
- package/IconButton/styles.ts +19 -0
- package/Image/index.tsx +83 -0
- package/Image/styles.ts +5 -0
- package/Image/types.ts +5 -0
- package/Input/Input.tsx +192 -0
- package/Input/InputDropDown.tsx +114 -0
- package/Input/InputMoney.tsx +163 -0
- package/Input/InputOTP.tsx +235 -0
- package/Input/InputSearch.tsx +151 -0
- package/Input/InputTextArea.tsx +159 -0
- package/Input/common.tsx +129 -0
- package/Input/index.tsx +251 -0
- package/Input/styles.ts +160 -0
- package/Input/utils.ts +69 -0
- package/Layout/Card.tsx +180 -0
- package/Layout/GridSystem.tsx +136 -0
- package/Layout/Item.tsx +56 -0
- package/Layout/ItemList.tsx +55 -0
- package/Layout/ItemSectionList.tsx +39 -0
- package/Layout/Screen.tsx +355 -0
- package/Layout/Section.tsx +179 -0
- package/Layout/index.ts +29 -0
- package/Layout/styles.ts +97 -0
- package/Layout/types.ts +23 -0
- package/Layout/utils.ts +55 -0
- package/Loader/DotLoader.tsx +37 -0
- package/Loader/Loader.tsx +13 -0
- package/Loader/ProgressBar.tsx +44 -0
- package/Loader/Spinner.tsx +28 -0
- package/Loader/index.tsx +4 -0
- package/Loader/styles.ts +10 -0
- package/Loader/types.ts +35 -0
- package/Loader/utils.ts +25 -0
- package/Pagination/Dot.tsx +16 -0
- package/Pagination/PaginationDot.tsx +38 -0
- package/Pagination/PaginationNumber.tsx +24 -0
- package/Pagination/PaginationScroll.tsx +88 -0
- package/Pagination/PaginationWhiteDot.tsx +35 -0
- package/Pagination/index.tsx +25 -0
- package/Pagination/styles.ts +50 -0
- package/Pagination/types.ts +40 -0
- package/Popup/PopupNotify.tsx +266 -0
- package/Popup/PopupPromotion.tsx +120 -0
- package/Popup/index.tsx +4 -0
- package/Popup/types.ts +87 -0
- package/Radio/index.tsx +64 -0
- package/Radio/styles.ts +11 -0
- package/Radio/types.ts +38 -0
- package/Skeleton/index.tsx +72 -0
- package/Skeleton/styles.ts +5 -0
- package/Skeleton/types.ts +6 -0
- package/Switch/index.tsx +37 -0
- package/Switch/styles.ts +23 -0
- package/Switch/types.ts +26 -0
- package/Tag/index.tsx +79 -0
- package/Tag/styles.ts +24 -0
- package/Tag/types.ts +36 -0
- package/Text/index.tsx +110 -0
- package/Text/styles.ts +73 -0
- package/Text/types.ts +34 -0
- package/Text/utils.ts +14 -0
- package/Title/index.tsx +132 -0
- package/Title/styles.ts +52 -0
- package/Title/types.ts +52 -0
- package/code-scanner.js +51 -0
- package/index.ts +51 -0
- package/package.json +36 -0
- package/publish.sh +23 -0
- package/tsconfig.json +24 -0
- package/verify.js +44 -0
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Animated,
|
|
3
|
+
KeyboardAvoidingView,
|
|
4
|
+
NativeScrollEvent,
|
|
5
|
+
NativeSyntheticEvent,
|
|
6
|
+
Platform,
|
|
7
|
+
ScrollViewProps,
|
|
8
|
+
View,
|
|
9
|
+
ViewProps,
|
|
10
|
+
} from 'react-native';
|
|
11
|
+
import {useHeaderHeight} from '@react-navigation/elements';
|
|
12
|
+
import {useSafeAreaInsets} from 'react-native-safe-area-context';
|
|
13
|
+
import React, {
|
|
14
|
+
forwardRef,
|
|
15
|
+
Fragment,
|
|
16
|
+
ReactNode,
|
|
17
|
+
useContext,
|
|
18
|
+
useEffect,
|
|
19
|
+
useRef,
|
|
20
|
+
} from 'react';
|
|
21
|
+
import {Colors, Spacing, Styles} from '../Consts';
|
|
22
|
+
import {Card, Section, validateChildren} from './index';
|
|
23
|
+
import {
|
|
24
|
+
ApplicationContext,
|
|
25
|
+
HeaderBackground,
|
|
26
|
+
HeaderTitle,
|
|
27
|
+
} from '../Application';
|
|
28
|
+
import Navigation from '../Application/Navigation';
|
|
29
|
+
import styles from './styles';
|
|
30
|
+
import {AnimatedHeader, NavigationOptions} from '../Application/types';
|
|
31
|
+
import {Image} from '../Image';
|
|
32
|
+
import {HeaderType} from './types';
|
|
33
|
+
|
|
34
|
+
export interface ScreenProps extends ViewProps {
|
|
35
|
+
children: React.ReactNode;
|
|
36
|
+
/**
|
|
37
|
+
* Optional. Navigation object for handling navigation actions.
|
|
38
|
+
*/
|
|
39
|
+
navigation?: Navigation;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Optional. If `true`, enable keyboard avoiding view for the screen.
|
|
43
|
+
*/
|
|
44
|
+
enableKeyboardAvoidingView?: boolean;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Optional. If `true`, the screen content is scrollable.
|
|
48
|
+
*/
|
|
49
|
+
scrollable?: boolean;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Optional. Set type of header.
|
|
53
|
+
*/
|
|
54
|
+
headerType?: HeaderType;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Optional. Props for the animated component or bool used top navigation animated.
|
|
58
|
+
*/
|
|
59
|
+
animatedHeader?: AnimatedHeader;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Optional. Props for the underlying ScrollView component when `scrollable` is `true`.
|
|
63
|
+
*/
|
|
64
|
+
scrollViewProps?: ScrollViewProps;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Optional. ReactNode representing the header component of the screen.
|
|
68
|
+
*/
|
|
69
|
+
headerComponent?: ReactNode;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Optional. ReactNode representing the footer component of the screen.
|
|
73
|
+
*/
|
|
74
|
+
footerComponent?: ReactNode;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Optional. Background color of the screen.
|
|
78
|
+
*/
|
|
79
|
+
backgroundColor?: string;
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Optional. layout card offset overlap header banner.
|
|
83
|
+
*/
|
|
84
|
+
layoutOffset?: -8 | -24 | -56;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const Screen = forwardRef(
|
|
88
|
+
(
|
|
89
|
+
{
|
|
90
|
+
children,
|
|
91
|
+
enableKeyboardAvoidingView = false,
|
|
92
|
+
scrollable = false,
|
|
93
|
+
navigation,
|
|
94
|
+
animatedHeader,
|
|
95
|
+
scrollViewProps,
|
|
96
|
+
headerComponent: Header,
|
|
97
|
+
footerComponent: Footer,
|
|
98
|
+
layoutOffset = -56,
|
|
99
|
+
backgroundColor,
|
|
100
|
+
headerType = 'default',
|
|
101
|
+
}: ScreenProps,
|
|
102
|
+
ref,
|
|
103
|
+
) => {
|
|
104
|
+
const animatedValue = useRef<Animated.Value | undefined>(undefined);
|
|
105
|
+
const {theme} = useContext(ApplicationContext);
|
|
106
|
+
const insets = useSafeAreaInsets();
|
|
107
|
+
const heightHeader = useHeaderHeight();
|
|
108
|
+
const currentTint = useRef(Colors.black_01);
|
|
109
|
+
let styleAnimatedHeader: NavigationOptions;
|
|
110
|
+
let handleScroll;
|
|
111
|
+
let headerBackground: string | undefined = undefined;
|
|
112
|
+
let Component: any = View;
|
|
113
|
+
let keyboardVerticalOffset = heightHeader - 20;
|
|
114
|
+
animatedValue.current = undefined;
|
|
115
|
+
|
|
116
|
+
if (headerType == 'extended' || animatedHeader) {
|
|
117
|
+
keyboardVerticalOffset = -20;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (headerType == 'extended') {
|
|
121
|
+
headerBackground = theme.assets?.headerBackground;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
useEffect(() => {
|
|
125
|
+
if (animatedHeader) {
|
|
126
|
+
styleAnimatedHeader = {
|
|
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
|
+
),
|
|
137
|
+
};
|
|
138
|
+
if (animatedHeader.type == 'surface') {
|
|
139
|
+
styleAnimatedHeader = {
|
|
140
|
+
headerTransparent: true,
|
|
141
|
+
headerBackground: (props: any) => (
|
|
142
|
+
<HeaderBackground
|
|
143
|
+
{...props}
|
|
144
|
+
image={null}
|
|
145
|
+
backgroundColor={theme.colors.background.surface}
|
|
146
|
+
useBorder={true}
|
|
147
|
+
animatedValue={animatedValue.current}
|
|
148
|
+
/>
|
|
149
|
+
),
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
navigation?.setOptions(styleAnimatedHeader);
|
|
153
|
+
}
|
|
154
|
+
}, [animatedHeader]);
|
|
155
|
+
|
|
156
|
+
useEffect(() => {
|
|
157
|
+
if (!animatedHeader) {
|
|
158
|
+
let headerOptions: NavigationOptions = {};
|
|
159
|
+
switch (headerType) {
|
|
160
|
+
case 'none':
|
|
161
|
+
headerOptions = {
|
|
162
|
+
headerTransparent: true,
|
|
163
|
+
headerBackground: () => null,
|
|
164
|
+
headerShown: false,
|
|
165
|
+
};
|
|
166
|
+
break;
|
|
167
|
+
|
|
168
|
+
case 'surface':
|
|
169
|
+
headerOptions = {
|
|
170
|
+
headerTransparent: false,
|
|
171
|
+
headerShown: true,
|
|
172
|
+
headerTintColor: theme.colors.text.default,
|
|
173
|
+
headerBackground: (props: any) => (
|
|
174
|
+
<HeaderBackground
|
|
175
|
+
{...props}
|
|
176
|
+
image={null}
|
|
177
|
+
backgroundColor={theme.colors.background.surface}
|
|
178
|
+
useBorder={true}
|
|
179
|
+
animatedValue={animatedValue.current}
|
|
180
|
+
/>
|
|
181
|
+
),
|
|
182
|
+
headerTitle: (props: any) => (
|
|
183
|
+
<HeaderTitle {...props} animatedValue={animatedValue.current} />
|
|
184
|
+
),
|
|
185
|
+
};
|
|
186
|
+
break;
|
|
187
|
+
|
|
188
|
+
case 'extended':
|
|
189
|
+
headerOptions = {
|
|
190
|
+
headerShown: true,
|
|
191
|
+
headerTransparent: true,
|
|
192
|
+
headerTintColor: Colors.black_01,
|
|
193
|
+
headerBackground: () => null,
|
|
194
|
+
headerTitle: (props: any) => <HeaderTitle {...props} />,
|
|
195
|
+
};
|
|
196
|
+
break;
|
|
197
|
+
|
|
198
|
+
default:
|
|
199
|
+
headerOptions = {
|
|
200
|
+
headerTransparent: false,
|
|
201
|
+
headerShown: true,
|
|
202
|
+
headerTintColor: Colors.black_01,
|
|
203
|
+
headerBackground: (props: any) => <HeaderBackground {...props} />,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
navigation?.setOptions(headerOptions);
|
|
207
|
+
}
|
|
208
|
+
}, [headerType]);
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* animated when use scroll && animated value
|
|
212
|
+
*/
|
|
213
|
+
if (scrollable) {
|
|
214
|
+
Component = Animated.ScrollView;
|
|
215
|
+
handleScroll = scrollViewProps?.onScroll;
|
|
216
|
+
if (animatedHeader) {
|
|
217
|
+
animatedValue.current = new Animated.Value(0);
|
|
218
|
+
handleScroll = Animated.event(
|
|
219
|
+
[
|
|
220
|
+
{
|
|
221
|
+
nativeEvent: {
|
|
222
|
+
contentOffset: {y: animatedValue.current as Animated.Value},
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
],
|
|
226
|
+
{
|
|
227
|
+
useNativeDriver: true,
|
|
228
|
+
listener: (e: NativeSyntheticEvent<NativeScrollEvent>) => {
|
|
229
|
+
if (animatedHeader.type == 'surface') {
|
|
230
|
+
const offsetY = e.nativeEvent.contentOffset.y;
|
|
231
|
+
let color = Colors.black_01;
|
|
232
|
+
if (offsetY > 50) {
|
|
233
|
+
color = theme.colors.text.default;
|
|
234
|
+
}
|
|
235
|
+
if (color != currentTint.current) {
|
|
236
|
+
currentTint.current = color;
|
|
237
|
+
navigation?.setOptions({
|
|
238
|
+
headerTintColor: color,
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
scrollViewProps?.onScroll?.(e);
|
|
243
|
+
},
|
|
244
|
+
},
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* render top navigation banner
|
|
251
|
+
*/
|
|
252
|
+
const renderHeader = () => {
|
|
253
|
+
if (typeof animatedHeader?.component === 'function') {
|
|
254
|
+
return animatedHeader?.component({
|
|
255
|
+
animatedValue: animatedValue.current,
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* build content for screen
|
|
262
|
+
*/
|
|
263
|
+
const renderContent = (children: any): any => {
|
|
264
|
+
const results = validateChildren(children, [Section, Card, undefined]);
|
|
265
|
+
if (Array.isArray(results)) {
|
|
266
|
+
return results.map((item, index) => {
|
|
267
|
+
const space = item?.props?.useMargin === false ? 0 : Spacing.M;
|
|
268
|
+
if (item?.type == Fragment) {
|
|
269
|
+
return renderContent(item?.props?.children);
|
|
270
|
+
}
|
|
271
|
+
if (item) {
|
|
272
|
+
return (
|
|
273
|
+
<View
|
|
274
|
+
key={`ScreenItem${index}`}
|
|
275
|
+
style={{
|
|
276
|
+
marginTop: space,
|
|
277
|
+
flex: item.props?.expanded ? 1 : undefined,
|
|
278
|
+
}}>
|
|
279
|
+
{item}
|
|
280
|
+
</View>
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
} else {
|
|
285
|
+
const item: any = children;
|
|
286
|
+
const space = item?.props?.useMargin === false ? 0 : Spacing.M;
|
|
287
|
+
if (item?.type == Fragment) {
|
|
288
|
+
return renderContent(item?.props?.children);
|
|
289
|
+
}
|
|
290
|
+
return (
|
|
291
|
+
<View
|
|
292
|
+
key={`ScreenItem`}
|
|
293
|
+
style={{
|
|
294
|
+
marginTop: space,
|
|
295
|
+
flex: item?.props?.expanded ? 1 : undefined,
|
|
296
|
+
}}>
|
|
297
|
+
{children}
|
|
298
|
+
</View>
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
return (
|
|
304
|
+
<View style={[Styles.flex, {backgroundColor}]}>
|
|
305
|
+
{!!headerBackground && (
|
|
306
|
+
<>
|
|
307
|
+
<Image
|
|
308
|
+
source={{
|
|
309
|
+
uri: headerBackground,
|
|
310
|
+
}}
|
|
311
|
+
style={styles.extendedHeader}
|
|
312
|
+
/>
|
|
313
|
+
<View style={{height: heightHeader}} />
|
|
314
|
+
</>
|
|
315
|
+
)}
|
|
316
|
+
<KeyboardAvoidingView
|
|
317
|
+
style={Styles.flex}
|
|
318
|
+
enabled={enableKeyboardAvoidingView}
|
|
319
|
+
keyboardVerticalOffset={keyboardVerticalOffset}
|
|
320
|
+
behavior={Platform.select({
|
|
321
|
+
ios: 'padding',
|
|
322
|
+
android: undefined,
|
|
323
|
+
})}>
|
|
324
|
+
{Header}
|
|
325
|
+
<Component
|
|
326
|
+
{...scrollViewProps}
|
|
327
|
+
ref={ref}
|
|
328
|
+
showsVerticalScrollIndicator={false}
|
|
329
|
+
onScroll={handleScroll}
|
|
330
|
+
style={Styles.flex}>
|
|
331
|
+
<View
|
|
332
|
+
style={[styles.screenBanner, {maxHeight: 210 + layoutOffset}]}>
|
|
333
|
+
{renderHeader()}
|
|
334
|
+
</View>
|
|
335
|
+
{renderContent(children)}
|
|
336
|
+
</Component>
|
|
337
|
+
{Footer && (
|
|
338
|
+
<View
|
|
339
|
+
style={[
|
|
340
|
+
styles.footerContainer,
|
|
341
|
+
{
|
|
342
|
+
paddingBottom: Math.min(insets.bottom, 21) + Spacing.S,
|
|
343
|
+
backgroundColor: theme.colors.background.surface,
|
|
344
|
+
},
|
|
345
|
+
]}>
|
|
346
|
+
<Section>{Footer}</Section>
|
|
347
|
+
</View>
|
|
348
|
+
)}
|
|
349
|
+
</KeyboardAvoidingView>
|
|
350
|
+
</View>
|
|
351
|
+
);
|
|
352
|
+
},
|
|
353
|
+
);
|
|
354
|
+
|
|
355
|
+
export default Screen;
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import React, {Fragment, useContext} from 'react';
|
|
2
|
+
import {Dimensions, View, ViewProps} from 'react-native';
|
|
3
|
+
import {useGridSystem, validateChildren} from './utils';
|
|
4
|
+
import {GridContextProps} from './types';
|
|
5
|
+
import {GridContext} from './index';
|
|
6
|
+
import Item from './Item';
|
|
7
|
+
import {ApplicationContext} from '../Application';
|
|
8
|
+
import styles from './styles';
|
|
9
|
+
import {Image} from '../Image';
|
|
10
|
+
import ItemList from './ItemList';
|
|
11
|
+
|
|
12
|
+
export interface SectionProps extends ViewProps {
|
|
13
|
+
/**
|
|
14
|
+
* Optional. If `true`, the Section component will be displayed in an expanded state.
|
|
15
|
+
* Defaults to `false` if not provided.
|
|
16
|
+
*/
|
|
17
|
+
expanded?: boolean;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Optional. If `true`, margin will be applied to the Section component.
|
|
21
|
+
* Defaults to `false` if not provided.
|
|
22
|
+
*/
|
|
23
|
+
useMargin?: boolean;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Optional. Background image for the Section component.
|
|
27
|
+
*/
|
|
28
|
+
backgroundImage?: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const Section: React.FC<SectionProps> = ({
|
|
32
|
+
children,
|
|
33
|
+
useMargin = true,
|
|
34
|
+
backgroundImage,
|
|
35
|
+
style,
|
|
36
|
+
...props
|
|
37
|
+
}) => {
|
|
38
|
+
const {showGrid} = useContext(ApplicationContext);
|
|
39
|
+
const {numberOfColumns} = useGridSystem();
|
|
40
|
+
|
|
41
|
+
const gutterSize = 12;
|
|
42
|
+
let margin = 0;
|
|
43
|
+
if (useMargin) {
|
|
44
|
+
margin = 12;
|
|
45
|
+
}
|
|
46
|
+
let widthSection = Dimensions.get('window').width;
|
|
47
|
+
if (useMargin) widthSection = widthSection - margin * 2;
|
|
48
|
+
const totalGutterSize = gutterSize * (numberOfColumns - 1);
|
|
49
|
+
const sizePerSpan = (widthSection - totalGutterSize) / numberOfColumns;
|
|
50
|
+
|
|
51
|
+
const gridContext: GridContextProps = {
|
|
52
|
+
numberOfColumns,
|
|
53
|
+
gutterSize,
|
|
54
|
+
sizePerSpan,
|
|
55
|
+
getSizeSpan: span => {
|
|
56
|
+
return span * sizePerSpan + (span - 1) * gutterSize;
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* render overlay only dev mode
|
|
62
|
+
*/
|
|
63
|
+
const renderOverlay = () => {
|
|
64
|
+
return <View pointerEvents={'none'} style={styles.boxGridSection} />;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* build a row view in side session
|
|
69
|
+
* @param rows
|
|
70
|
+
* @param cursor
|
|
71
|
+
* @param endRow
|
|
72
|
+
*/
|
|
73
|
+
const addRowView = (
|
|
74
|
+
rows: any[],
|
|
75
|
+
cursor: number = 0,
|
|
76
|
+
endRow: boolean = false,
|
|
77
|
+
) => {
|
|
78
|
+
return (
|
|
79
|
+
<View
|
|
80
|
+
key={`Card${cursor}`}
|
|
81
|
+
style={{
|
|
82
|
+
flexDirection: 'row',
|
|
83
|
+
marginBottom: endRow ? 0 : gridContext.gutterSize,
|
|
84
|
+
}}>
|
|
85
|
+
{rows.map((i: React.ReactElement, index: number) => {
|
|
86
|
+
return (
|
|
87
|
+
<View key={`Card${cursor}${index}`} style={{flexDirection: 'row'}}>
|
|
88
|
+
{index != 0 && (
|
|
89
|
+
<View style={{width: gridContext.gutterSize}}></View>
|
|
90
|
+
)}
|
|
91
|
+
{i}
|
|
92
|
+
</View>
|
|
93
|
+
);
|
|
94
|
+
})}
|
|
95
|
+
</View>
|
|
96
|
+
);
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* render children content for session
|
|
101
|
+
* @param children
|
|
102
|
+
*/
|
|
103
|
+
const renderView = (
|
|
104
|
+
children: any,
|
|
105
|
+
): React.ReactElement | React.ReactElement[] => {
|
|
106
|
+
const element: React.ReactElement | React.ReactElement[] = [];
|
|
107
|
+
if (Array.isArray(children)) {
|
|
108
|
+
let cursor = 0;
|
|
109
|
+
let rows: any[] = [];
|
|
110
|
+
const arrayChild = children.filter(item => !!item);
|
|
111
|
+
for (let i = 0; i < arrayChild.length; ++i) {
|
|
112
|
+
const item = arrayChild[i];
|
|
113
|
+
const totalSpan = rows
|
|
114
|
+
.concat([item])
|
|
115
|
+
.map(i => i.props?.widthSpan)
|
|
116
|
+
.reduce((previousValue, currentValue) => {
|
|
117
|
+
return previousValue + currentValue;
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
if (totalSpan <= gridContext.numberOfColumns) {
|
|
121
|
+
rows.push(item);
|
|
122
|
+
} else {
|
|
123
|
+
cursor += 1;
|
|
124
|
+
element.push(addRowView(rows, cursor));
|
|
125
|
+
rows = [item];
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (i == arrayChild.length - 1) {
|
|
129
|
+
cursor += 1;
|
|
130
|
+
element.push(addRowView(rows, cursor, true));
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
} else {
|
|
134
|
+
element.push(children);
|
|
135
|
+
if (children?.type == Fragment) {
|
|
136
|
+
return renderView(children?.props.children);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return element;
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
return (
|
|
143
|
+
<GridContext.Provider
|
|
144
|
+
value={{
|
|
145
|
+
numberOfColumns,
|
|
146
|
+
gutterSize,
|
|
147
|
+
sizePerSpan,
|
|
148
|
+
getSizeSpan: span => {
|
|
149
|
+
return span * sizePerSpan + (span - 1) * gutterSize;
|
|
150
|
+
},
|
|
151
|
+
}}>
|
|
152
|
+
<View
|
|
153
|
+
{...props}
|
|
154
|
+
style={[
|
|
155
|
+
style,
|
|
156
|
+
styles.protectedStyle,
|
|
157
|
+
{
|
|
158
|
+
width: widthSection,
|
|
159
|
+
marginHorizontal: margin,
|
|
160
|
+
flexDirection: 'row',
|
|
161
|
+
flexWrap: 'wrap',
|
|
162
|
+
},
|
|
163
|
+
]}>
|
|
164
|
+
{!!backgroundImage && (
|
|
165
|
+
<Image
|
|
166
|
+
source={{uri: backgroundImage}}
|
|
167
|
+
style={styles.imageBackground}
|
|
168
|
+
/>
|
|
169
|
+
)}
|
|
170
|
+
{renderView(validateChildren(children, [Item, ItemList]))}
|
|
171
|
+
{showGrid && renderOverlay()}
|
|
172
|
+
</View>
|
|
173
|
+
</GridContext.Provider>
|
|
174
|
+
);
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
Section.displayName = 'Section';
|
|
178
|
+
|
|
179
|
+
export default Section;
|
package/Layout/index.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import Card from './Card';
|
|
2
|
+
import Section from './Section';
|
|
3
|
+
import GridSystem from './GridSystem';
|
|
4
|
+
import {validateChildren} from './utils';
|
|
5
|
+
import {createContext} from 'react';
|
|
6
|
+
import {GridContextProps} from './types';
|
|
7
|
+
import Item from './Item';
|
|
8
|
+
import ItemList from './ItemList';
|
|
9
|
+
import ItemSectionList from './ItemSectionList';
|
|
10
|
+
import Screen from './Screen';
|
|
11
|
+
|
|
12
|
+
const GridContext = createContext<GridContextProps>({
|
|
13
|
+
numberOfColumns: 12,
|
|
14
|
+
gutterSize: 12,
|
|
15
|
+
sizePerSpan: 0,
|
|
16
|
+
getSizeSpan: () => 0,
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
export {
|
|
20
|
+
GridContext,
|
|
21
|
+
Card,
|
|
22
|
+
Section,
|
|
23
|
+
GridSystem,
|
|
24
|
+
validateChildren,
|
|
25
|
+
Item,
|
|
26
|
+
ItemList,
|
|
27
|
+
ItemSectionList,
|
|
28
|
+
Screen,
|
|
29
|
+
};
|
package/Layout/styles.ts
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import {Platform, StyleSheet} from 'react-native';
|
|
2
|
+
import {Colors, Radius, Spacing} from '../Consts';
|
|
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
|
+
},
|
|
14
|
+
boxGrid: {
|
|
15
|
+
position: 'absolute',
|
|
16
|
+
top: 0,
|
|
17
|
+
bottom: 0,
|
|
18
|
+
left: 0,
|
|
19
|
+
right: 0,
|
|
20
|
+
borderColor: 'red',
|
|
21
|
+
borderWidth: 1,
|
|
22
|
+
zIndex: 99,
|
|
23
|
+
},
|
|
24
|
+
boxGridSection: {
|
|
25
|
+
position: 'absolute',
|
|
26
|
+
top: 0,
|
|
27
|
+
bottom: 0,
|
|
28
|
+
left: 0,
|
|
29
|
+
right: 0,
|
|
30
|
+
borderColor: 'blue',
|
|
31
|
+
borderWidth: 2,
|
|
32
|
+
zIndex: 100,
|
|
33
|
+
},
|
|
34
|
+
boxGridCard: {
|
|
35
|
+
position: 'absolute',
|
|
36
|
+
top: 0,
|
|
37
|
+
bottom: 0,
|
|
38
|
+
left: 0,
|
|
39
|
+
right: 0,
|
|
40
|
+
borderColor: 'green',
|
|
41
|
+
borderWidth: 2,
|
|
42
|
+
zIndex: 100,
|
|
43
|
+
},
|
|
44
|
+
card: {
|
|
45
|
+
flexDirection: 'row',
|
|
46
|
+
flexWrap: 'wrap',
|
|
47
|
+
borderRadius: Radius.M,
|
|
48
|
+
padding: 12,
|
|
49
|
+
},
|
|
50
|
+
item: {
|
|
51
|
+
overflow: 'hidden',
|
|
52
|
+
margin: undefined,
|
|
53
|
+
marginTop: undefined,
|
|
54
|
+
marginBottom: undefined,
|
|
55
|
+
marginLeft: undefined,
|
|
56
|
+
marginRight: undefined,
|
|
57
|
+
marginVertical: undefined,
|
|
58
|
+
marginHorizontal: undefined,
|
|
59
|
+
},
|
|
60
|
+
imageBackground: {
|
|
61
|
+
position: 'absolute',
|
|
62
|
+
top: 0,
|
|
63
|
+
bottom: 0,
|
|
64
|
+
left: 0,
|
|
65
|
+
right: 0,
|
|
66
|
+
},
|
|
67
|
+
protectedStyle: {
|
|
68
|
+
marginTop: undefined,
|
|
69
|
+
marginBottom: undefined,
|
|
70
|
+
marginLeft: undefined,
|
|
71
|
+
marginRight: undefined,
|
|
72
|
+
paddingTop: undefined,
|
|
73
|
+
paddingBottom: undefined,
|
|
74
|
+
paddingLeft: undefined,
|
|
75
|
+
paddingRight: undefined,
|
|
76
|
+
padding: undefined,
|
|
77
|
+
margin: undefined,
|
|
78
|
+
marginHorizontal: undefined,
|
|
79
|
+
marginVertical: undefined,
|
|
80
|
+
paddingHorizontal: undefined,
|
|
81
|
+
paddingVertical: undefined,
|
|
82
|
+
},
|
|
83
|
+
footerContainer: {
|
|
84
|
+
paddingTop: Spacing.S,
|
|
85
|
+
...Platform.select({
|
|
86
|
+
android: {
|
|
87
|
+
elevation: 4,
|
|
88
|
+
},
|
|
89
|
+
ios: {
|
|
90
|
+
shadowColor: Colors.black_20,
|
|
91
|
+
shadowOffset: {width: 0, height: -2},
|
|
92
|
+
shadowOpacity: 0.1,
|
|
93
|
+
shadowRadius: 3,
|
|
94
|
+
},
|
|
95
|
+
}),
|
|
96
|
+
},
|
|
97
|
+
});
|
package/Layout/types.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export type HeaderType = 'default' | 'surface' | 'extended' | 'none';
|
|
2
|
+
|
|
3
|
+
export type GridContextProps = {
|
|
4
|
+
/**
|
|
5
|
+
* Represents the number of columns in the grid.
|
|
6
|
+
*/
|
|
7
|
+
numberOfColumns: number;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Represents the size of the gutter between grid items.
|
|
11
|
+
*/
|
|
12
|
+
gutterSize: number;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Represents the size (width or height) per span number.
|
|
16
|
+
*/
|
|
17
|
+
sizePerSpan: number;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* A function that receives a span number and returns the calculated size for that span.
|
|
21
|
+
*/
|
|
22
|
+
getSizeSpan: (span: number) => number;
|
|
23
|
+
};
|