@momo-kits/foundation 0.75.1-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/Assets/header-background.png +0 -0
- package/Assets/icon.json +3947 -0
- package/Button/index.tsx +278 -0
- package/Button/styles.ts +41 -0
- package/CheckBox/index.tsx +61 -0
- package/CheckBox/styles.ts +15 -0
- package/CheckBox/types.ts +38 -0
- package/Consts/colors+spacing+radius.ts +214 -0
- package/Consts/index.ts +4 -0
- package/Consts/styles.ts +78 -0
- package/Consts/theme.ts +121 -0
- package/Divider/index.tsx +19 -0
- package/Icon/index.tsx +37 -0
- package/Icon/types.ts +27 -0
- package/IconButton/index.tsx +140 -0
- package/IconButton/styles.ts +19 -0
- package/Image/index.tsx +80 -0
- package/Image/styles.ts +13 -0
- package/Image/types.ts +3 -0
- package/Input/Input.tsx +136 -0
- package/Input/InputMoney.tsx +132 -0
- package/Input/InputSearch.tsx +161 -0
- package/Input/InputTextArea.tsx +152 -0
- package/Input/common.tsx +118 -0
- package/Input/index.tsx +96 -0
- package/Input/styles.ts +122 -0
- package/Input/utils.ts +69 -0
- package/Layout/Card.tsx +141 -0
- package/Layout/GridSystem.tsx +124 -0
- package/Layout/Item.tsx +60 -0
- package/Layout/ScreenContainer.tsx +121 -0
- package/Layout/Section.tsx +141 -0
- package/Layout/index.ts +17 -0
- package/Layout/types.ts +59 -0
- package/Layout/utils.ts +60 -0
- package/Navigation/BottomTab.tsx +39 -0
- package/Navigation/Components.tsx +365 -0
- package/Navigation/ModalScreen.tsx +226 -0
- package/Navigation/Navigation.ts +44 -0
- package/Navigation/NavigationButton.tsx +54 -0
- package/Navigation/NavigationContainer.tsx +77 -0
- package/Navigation/Navigator.ts +82 -0
- package/Navigation/StackScreen.tsx +27 -0
- package/Navigation/index.ts +14 -0
- package/Navigation/types.ts +158 -0
- package/Navigation/utils.tsx +138 -0
- package/Pagination/Dot.tsx +16 -0
- package/Pagination/PaginationDot.tsx +33 -0
- package/Pagination/PaginationNumber.tsx +23 -0
- package/Pagination/PaginationScroll.tsx +88 -0
- package/Pagination/PaginationWhiteDot.tsx +32 -0
- package/Pagination/index.tsx +25 -0
- package/Pagination/styles.ts +47 -0
- package/Pagination/types.ts +38 -0
- package/Popup/PopupNotify.tsx +215 -0
- package/Popup/PopupPromotion.tsx +77 -0
- package/Popup/index.tsx +4 -0
- package/Popup/types.ts +70 -0
- package/Radio/index.tsx +65 -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 +3 -0
- package/Switch/index.tsx +40 -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 +123 -0
- package/Text/styles.ts +35 -0
- package/Text/types.ts +40 -0
- package/index.ts +38 -0
- package/package.json +41 -0
- package/publish.sh +26 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import {Dimensions, View} from 'react-native';
|
|
3
|
+
import {validateChildren} from './utils';
|
|
4
|
+
import {GridContextProps, SectionProps} from './types';
|
|
5
|
+
import {GridContext} from './index';
|
|
6
|
+
import {GRIDVIEW} from '../Consts';
|
|
7
|
+
import Item from './Item';
|
|
8
|
+
|
|
9
|
+
const Section: React.FC<SectionProps> = ({children, useMargin = true}) => {
|
|
10
|
+
const numberOfColumns = 12;
|
|
11
|
+
const gutterSize = 12;
|
|
12
|
+
let margin = 0;
|
|
13
|
+
if (useMargin) {
|
|
14
|
+
margin = 12;
|
|
15
|
+
}
|
|
16
|
+
let widthSection = Dimensions.get('window').width;
|
|
17
|
+
if (useMargin) widthSection = widthSection - margin * 2;
|
|
18
|
+
const totalGutterSize = gutterSize * (numberOfColumns - 1);
|
|
19
|
+
const sizePerSpan = (widthSection - totalGutterSize) / numberOfColumns;
|
|
20
|
+
|
|
21
|
+
const gridContext: GridContextProps = {
|
|
22
|
+
numberOfColumns,
|
|
23
|
+
gutterSize,
|
|
24
|
+
margin,
|
|
25
|
+
sizePerSpan,
|
|
26
|
+
getSizeSpan: span => {
|
|
27
|
+
return span * sizePerSpan + (span - 1) * gutterSize;
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* render overlay only dev mode
|
|
33
|
+
*/
|
|
34
|
+
const renderOverlay = () => {
|
|
35
|
+
return (
|
|
36
|
+
<View
|
|
37
|
+
pointerEvents={'none'}
|
|
38
|
+
style={{
|
|
39
|
+
position: 'absolute',
|
|
40
|
+
top: 0,
|
|
41
|
+
bottom: 0,
|
|
42
|
+
left: 0,
|
|
43
|
+
right: 0,
|
|
44
|
+
borderColor: 'red',
|
|
45
|
+
borderWidth: 1,
|
|
46
|
+
}}
|
|
47
|
+
/>
|
|
48
|
+
);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* build a row view in side session
|
|
53
|
+
* @param rows
|
|
54
|
+
* @param cursor
|
|
55
|
+
* @param endRow
|
|
56
|
+
*/
|
|
57
|
+
const addRowView = (
|
|
58
|
+
rows: any[],
|
|
59
|
+
cursor: number = 0,
|
|
60
|
+
endRow: boolean = false,
|
|
61
|
+
) => {
|
|
62
|
+
return (
|
|
63
|
+
<View
|
|
64
|
+
key={`Card${cursor}`}
|
|
65
|
+
style={{
|
|
66
|
+
flexDirection: 'row',
|
|
67
|
+
marginBottom: endRow ? 0 : gridContext.gutterSize,
|
|
68
|
+
}}>
|
|
69
|
+
{rows.map((i: React.ReactElement, index: number) => {
|
|
70
|
+
return (
|
|
71
|
+
<View key={`Card${cursor}${index}`} style={{flexDirection: 'row'}}>
|
|
72
|
+
{index != 0 && (
|
|
73
|
+
<View style={{width: gridContext.gutterSize}}></View>
|
|
74
|
+
)}
|
|
75
|
+
{i}
|
|
76
|
+
</View>
|
|
77
|
+
);
|
|
78
|
+
})}
|
|
79
|
+
</View>
|
|
80
|
+
);
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* render children content for session
|
|
85
|
+
* @param children
|
|
86
|
+
*/
|
|
87
|
+
const renderView = (
|
|
88
|
+
children: any,
|
|
89
|
+
): React.ReactElement | React.ReactElement[] => {
|
|
90
|
+
const element: React.ReactElement | React.ReactElement[] = [];
|
|
91
|
+
if (Array.isArray(children)) {
|
|
92
|
+
let cursor = 0;
|
|
93
|
+
let rows: any[] = [];
|
|
94
|
+
for (let i = 0; i < children.length; ++i) {
|
|
95
|
+
const item = children[i];
|
|
96
|
+
const totalSpan = rows
|
|
97
|
+
.concat([item])
|
|
98
|
+
.map(i => i.props?.widthSpan)
|
|
99
|
+
.reduce((previousValue, currentValue) => {
|
|
100
|
+
return previousValue + currentValue;
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
if (totalSpan <= gridContext.numberOfColumns) {
|
|
104
|
+
rows.push(item);
|
|
105
|
+
} else {
|
|
106
|
+
cursor += 1;
|
|
107
|
+
element.push(addRowView(rows, cursor));
|
|
108
|
+
rows = [item];
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (i == children.length - 1) {
|
|
112
|
+
cursor += 1;
|
|
113
|
+
element.push(addRowView(rows, cursor, true));
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
} else {
|
|
117
|
+
element.push(children);
|
|
118
|
+
}
|
|
119
|
+
return element;
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
return (
|
|
123
|
+
<GridContext.Provider value={gridContext}>
|
|
124
|
+
<View
|
|
125
|
+
accessibilityLabel={`Section`}
|
|
126
|
+
style={{
|
|
127
|
+
width: widthSection,
|
|
128
|
+
marginHorizontal: gridContext.margin,
|
|
129
|
+
flexDirection: 'row',
|
|
130
|
+
flexWrap: 'wrap',
|
|
131
|
+
}}>
|
|
132
|
+
{renderView(validateChildren(children, [Item]))}
|
|
133
|
+
{GRIDVIEW && renderOverlay()}
|
|
134
|
+
</View>
|
|
135
|
+
</GridContext.Provider>
|
|
136
|
+
);
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
Section.displayName = 'Section';
|
|
140
|
+
|
|
141
|
+
export default Section;
|
package/Layout/index.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
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
|
+
|
|
9
|
+
const GridContext = createContext<GridContextProps>({
|
|
10
|
+
numberOfColumns: 12,
|
|
11
|
+
gutterSize: 12,
|
|
12
|
+
margin: 12,
|
|
13
|
+
sizePerSpan: 0,
|
|
14
|
+
getSizeSpan: span => 0,
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
export {GridContext, Card, Section, GridSystem, validateChildren, Item};
|
package/Layout/types.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import {ViewProps} from 'react-native';
|
|
2
|
+
|
|
3
|
+
type SpanNumber = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
|
|
4
|
+
|
|
5
|
+
export type GridContextProps = {
|
|
6
|
+
/**
|
|
7
|
+
* Represents the number of columns in the grid.
|
|
8
|
+
*/
|
|
9
|
+
numberOfColumns: number;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Represents the size of the gutter between grid items.
|
|
13
|
+
*/
|
|
14
|
+
gutterSize: number;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Optional. Represents the margin around the grid.
|
|
18
|
+
*/
|
|
19
|
+
margin?: number;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Represents the size (width or height) per span number.
|
|
23
|
+
*/
|
|
24
|
+
sizePerSpan: number;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* A function that receives a span number and returns the calculated size for that span.
|
|
28
|
+
*/
|
|
29
|
+
getSizeSpan: (span: number) => number;
|
|
30
|
+
};
|
|
31
|
+
export interface SectionProps extends ViewProps {
|
|
32
|
+
/**
|
|
33
|
+
* Optional. If `true`, the Section component will be displayed in an expanded state.
|
|
34
|
+
* Defaults to `false` if not provided.
|
|
35
|
+
*/
|
|
36
|
+
expanded?: boolean;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Optional. If `true`, margin will be applied to the Section component.
|
|
40
|
+
* Defaults to `false` if not provided.
|
|
41
|
+
*/
|
|
42
|
+
useMargin?: boolean;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface ItemProps extends ViewProps {
|
|
46
|
+
/**
|
|
47
|
+
* Represents the width of the Card component in terms of span numbers (1-12).
|
|
48
|
+
*/
|
|
49
|
+
widthSpan: SpanNumber;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Optional. Represents the height of the Card component in terms of span numbers (1-12).
|
|
53
|
+
*/
|
|
54
|
+
heightSpan?: SpanNumber;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface CardProps extends ViewProps {
|
|
58
|
+
background?: string;
|
|
59
|
+
}
|
package/Layout/utils.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import {Alert} from 'react-native';
|
|
2
|
+
import React, {ReactElement} from 'react';
|
|
3
|
+
import {SectionProps} from './types';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* validate children type
|
|
7
|
+
* @param children
|
|
8
|
+
* @param childrenTypes
|
|
9
|
+
*/
|
|
10
|
+
const validateChildren = (
|
|
11
|
+
children:
|
|
12
|
+
| React.ReactElement<any, string | React.JSXElementConstructor<any>>
|
|
13
|
+
| string
|
|
14
|
+
| number
|
|
15
|
+
| {}
|
|
16
|
+
| Iterable<React.ReactNode>
|
|
17
|
+
| React.ReactPortal
|
|
18
|
+
| null
|
|
19
|
+
| undefined
|
|
20
|
+
| boolean,
|
|
21
|
+
childrenTypes: Array<React.FC<any>>,
|
|
22
|
+
) => {
|
|
23
|
+
if (!__DEV__) return children;
|
|
24
|
+
|
|
25
|
+
let results: React.ReactElement[] = [];
|
|
26
|
+
const validateType = (child: React.ReactElement) => {
|
|
27
|
+
return childrenTypes.some(childrenType => child?.type === childrenType);
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const alertWrongType = () => {
|
|
31
|
+
Alert.alert(
|
|
32
|
+
'Component Kits',
|
|
33
|
+
`Wrong children type for this component, expected are ${childrenTypes
|
|
34
|
+
.map(type => type.displayName)
|
|
35
|
+
.join(', ')}.`,
|
|
36
|
+
);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
if (Array.isArray(children)) {
|
|
40
|
+
const flattenChildren = children.flat();
|
|
41
|
+
results = flattenChildren.filter((child: React.ReactElement) => {
|
|
42
|
+
if (validateType(child)) {
|
|
43
|
+
return child;
|
|
44
|
+
} else {
|
|
45
|
+
alertWrongType();
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
} else {
|
|
49
|
+
const singleChild: any = children;
|
|
50
|
+
if (validateType(singleChild)) {
|
|
51
|
+
results = [singleChild];
|
|
52
|
+
} else {
|
|
53
|
+
alertWrongType();
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return results;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export {validateChildren};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import React, {FC} from 'react';
|
|
2
|
+
import {BottomTabProps} from './types';
|
|
3
|
+
import {createBottomTabNavigator} from '@react-navigation/bottom-tabs';
|
|
4
|
+
import {Icon} from '../Icon';
|
|
5
|
+
|
|
6
|
+
const Tab = createBottomTabNavigator();
|
|
7
|
+
|
|
8
|
+
const BottomTab: FC<BottomTabProps> = ({tabs}) => {
|
|
9
|
+
return (
|
|
10
|
+
<Tab.Navigator>
|
|
11
|
+
{tabs.map((item, index) => {
|
|
12
|
+
return (
|
|
13
|
+
<Tab.Screen
|
|
14
|
+
key={`${item.label}-${index}`}
|
|
15
|
+
name={item.label}
|
|
16
|
+
component={item.screen}
|
|
17
|
+
options={{
|
|
18
|
+
tabBarLabel: item.label,
|
|
19
|
+
tabBarBadge: item?.badgeLabel,
|
|
20
|
+
tabBarBadgeStyle: {
|
|
21
|
+
borderColor: 'white',
|
|
22
|
+
borderWidth: 2,
|
|
23
|
+
fontSize: 10,
|
|
24
|
+
lineHeight: 14,
|
|
25
|
+
fontWeight: 'bold',
|
|
26
|
+
alignSelf: 'center',
|
|
27
|
+
},
|
|
28
|
+
tabBarIcon: ({color, size}) => (
|
|
29
|
+
<Icon source={item.icon} color={color} size={size} />
|
|
30
|
+
),
|
|
31
|
+
}}
|
|
32
|
+
/>
|
|
33
|
+
);
|
|
34
|
+
})}
|
|
35
|
+
</Tab.Navigator>
|
|
36
|
+
);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export default BottomTab;
|
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
import React, {useContext, useEffect, useState} from 'react';
|
|
2
|
+
import {
|
|
3
|
+
Animated,
|
|
4
|
+
DeviceEventEmitter,
|
|
5
|
+
StatusBar,
|
|
6
|
+
StyleSheet,
|
|
7
|
+
TouchableOpacity,
|
|
8
|
+
View,
|
|
9
|
+
} from 'react-native';
|
|
10
|
+
import {ApplicationContext, NavigationButton} from './index';
|
|
11
|
+
import {Colors, Radius, Spacing, Styles} from '../Consts';
|
|
12
|
+
import {Image} from '../Image';
|
|
13
|
+
import {
|
|
14
|
+
HeaderBackgroundProps,
|
|
15
|
+
HeaderBackProps,
|
|
16
|
+
HeaderToolkitProps,
|
|
17
|
+
TitleCustomProps,
|
|
18
|
+
} from './types';
|
|
19
|
+
import {Text} from '../Text';
|
|
20
|
+
import {Icon} from '../Icon';
|
|
21
|
+
import {PopupNotify} from '../Popup';
|
|
22
|
+
|
|
23
|
+
const styles = StyleSheet.create({
|
|
24
|
+
headerBackground: {
|
|
25
|
+
width: '100%',
|
|
26
|
+
height: undefined,
|
|
27
|
+
position: 'absolute',
|
|
28
|
+
aspectRatio: 375 / 154,
|
|
29
|
+
},
|
|
30
|
+
headerTitleContainer: {
|
|
31
|
+
alignItems: 'center',
|
|
32
|
+
justifyContent: 'center',
|
|
33
|
+
width: '100%',
|
|
34
|
+
},
|
|
35
|
+
circle: {width: 36, height: 36, borderRadius: 18},
|
|
36
|
+
square: {width: 36, height: 36, borderRadius: Radius.XS},
|
|
37
|
+
dotAvatar: {
|
|
38
|
+
position: 'absolute',
|
|
39
|
+
width: 12,
|
|
40
|
+
height: 12,
|
|
41
|
+
borderRadius: 6,
|
|
42
|
+
bottom: 0,
|
|
43
|
+
right: 0,
|
|
44
|
+
borderWidth: 1,
|
|
45
|
+
borderColor: Colors.black_01,
|
|
46
|
+
},
|
|
47
|
+
headerRightButton: {
|
|
48
|
+
flexDirection: 'row',
|
|
49
|
+
overflow: 'hidden',
|
|
50
|
+
justifyContent: 'center',
|
|
51
|
+
alignItems: 'center',
|
|
52
|
+
paddingRight: Spacing.M,
|
|
53
|
+
},
|
|
54
|
+
toolkitContainer: {
|
|
55
|
+
marginLeft: Spacing.S,
|
|
56
|
+
padding: Spacing.XS,
|
|
57
|
+
height: 28,
|
|
58
|
+
borderRadius: 14,
|
|
59
|
+
borderColor: '#00000066',
|
|
60
|
+
justifyContent: 'center',
|
|
61
|
+
alignItems: 'center',
|
|
62
|
+
flexDirection: 'row',
|
|
63
|
+
},
|
|
64
|
+
divider: {
|
|
65
|
+
width: 1,
|
|
66
|
+
height: 12,
|
|
67
|
+
marginHorizontal: 4,
|
|
68
|
+
},
|
|
69
|
+
headerLeft: {
|
|
70
|
+
marginLeft: 12,
|
|
71
|
+
},
|
|
72
|
+
title: {fontSize: 15, lineHeight: 22, fontWeight: 'bold'},
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const HeaderTitle: React.FC<any> = props => {
|
|
76
|
+
const {theme} = useContext(ApplicationContext);
|
|
77
|
+
const opacity = props.animatedValue?.interpolate({
|
|
78
|
+
inputRange: [0, 200],
|
|
79
|
+
outputRange: [0, 1],
|
|
80
|
+
extrapolate: 'clamp',
|
|
81
|
+
});
|
|
82
|
+
return (
|
|
83
|
+
<Animated.Text
|
|
84
|
+
{...props}
|
|
85
|
+
accessibilityLabel={`HeaderTitle/${props.children}`}
|
|
86
|
+
style={[
|
|
87
|
+
styles.title,
|
|
88
|
+
{fontFamily: `${theme.font}-Bold`, opacity, color: props.tintColor},
|
|
89
|
+
]}
|
|
90
|
+
/>
|
|
91
|
+
);
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const HeaderLeft: React.FC<HeaderBackProps> = ({
|
|
95
|
+
tintColor,
|
|
96
|
+
preventBack,
|
|
97
|
+
useBorder,
|
|
98
|
+
backgroundColor,
|
|
99
|
+
}) => {
|
|
100
|
+
const {navigator} = useContext(ApplicationContext);
|
|
101
|
+
const goBack = () => {
|
|
102
|
+
const canGoBack = navigator?.ref.current?.canGoBack();
|
|
103
|
+
if (canGoBack) {
|
|
104
|
+
if (preventBack) {
|
|
105
|
+
navigator?.showModal({
|
|
106
|
+
screen: () => (
|
|
107
|
+
<PopupNotify
|
|
108
|
+
{...preventBack}
|
|
109
|
+
primary={{
|
|
110
|
+
title: preventBack.primary.title,
|
|
111
|
+
onPress: () => {
|
|
112
|
+
preventBack?.primary.onPress();
|
|
113
|
+
navigator?.ref.current?.goBack();
|
|
114
|
+
},
|
|
115
|
+
}}
|
|
116
|
+
/>
|
|
117
|
+
),
|
|
118
|
+
});
|
|
119
|
+
} else {
|
|
120
|
+
navigator?.ref.current?.goBack();
|
|
121
|
+
}
|
|
122
|
+
} else {
|
|
123
|
+
const currentTime = new Date().getTime();
|
|
124
|
+
const requestId = `navigationBackPress#${currentTime}`;
|
|
125
|
+
DeviceEventEmitter.emit('dismiss', {requestId});
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
return (
|
|
130
|
+
<View style={styles.headerLeft}>
|
|
131
|
+
<NavigationButton
|
|
132
|
+
icon="ic_back"
|
|
133
|
+
tintColor={tintColor}
|
|
134
|
+
onPress={goBack}
|
|
135
|
+
accessibilityLabel={'Back'}
|
|
136
|
+
useBorder={useBorder}
|
|
137
|
+
backgroundColor={backgroundColor}
|
|
138
|
+
/>
|
|
139
|
+
</View>
|
|
140
|
+
);
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
const HeaderBackground: React.FC<HeaderBackgroundProps> = ({
|
|
144
|
+
image,
|
|
145
|
+
backgroundColor,
|
|
146
|
+
animatedValue,
|
|
147
|
+
useBorder = false,
|
|
148
|
+
}) => {
|
|
149
|
+
const {theme} = useContext(ApplicationContext);
|
|
150
|
+
let headerImage = theme.assets?.headerBackground;
|
|
151
|
+
if (image === null) {
|
|
152
|
+
headerImage = undefined;
|
|
153
|
+
}
|
|
154
|
+
const opacity = animatedValue?.interpolate({
|
|
155
|
+
inputRange: [0, 200],
|
|
156
|
+
outputRange: [0, 1],
|
|
157
|
+
extrapolate: 'clamp',
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
return (
|
|
161
|
+
<Animated.View
|
|
162
|
+
style={[
|
|
163
|
+
Styles.flex,
|
|
164
|
+
{
|
|
165
|
+
backgroundColor: backgroundColor ?? theme.colors.background.default,
|
|
166
|
+
overflow: 'hidden',
|
|
167
|
+
borderBottomWidth: useBorder ? 1 : 0,
|
|
168
|
+
borderColor: theme.colors.border.default,
|
|
169
|
+
opacity,
|
|
170
|
+
},
|
|
171
|
+
]}>
|
|
172
|
+
<StatusBar
|
|
173
|
+
barStyle={headerImage || theme.dark ? 'light-content' : 'dark-content'}
|
|
174
|
+
/>
|
|
175
|
+
{!!headerImage && (
|
|
176
|
+
<Image style={styles.headerBackground} source={{uri: headerImage}} />
|
|
177
|
+
)}
|
|
178
|
+
</Animated.View>
|
|
179
|
+
);
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
const HeaderCustom: React.FC<TitleCustomProps> = ({
|
|
183
|
+
title,
|
|
184
|
+
subTitle,
|
|
185
|
+
image,
|
|
186
|
+
imageShape = 'circle',
|
|
187
|
+
content,
|
|
188
|
+
tintColor,
|
|
189
|
+
dotColor,
|
|
190
|
+
}) => {
|
|
191
|
+
const avatarStyle = imageShape == 'circle' ? styles.circle : styles.square;
|
|
192
|
+
const header = (
|
|
193
|
+
<View style={Styles.row}>
|
|
194
|
+
<View>
|
|
195
|
+
<Image source={{uri: image}} style={avatarStyle} />
|
|
196
|
+
{!!dotColor && (
|
|
197
|
+
<View style={[styles.dotAvatar, {backgroundColor: dotColor}]} />
|
|
198
|
+
)}
|
|
199
|
+
</View>
|
|
200
|
+
<View style={[Styles.flex, Styles.paddingHorizontal8]}>
|
|
201
|
+
<Text typography="title_xs" color={tintColor}>
|
|
202
|
+
{title}
|
|
203
|
+
</Text>
|
|
204
|
+
<Text typography="description_s" color={tintColor}>
|
|
205
|
+
{subTitle}
|
|
206
|
+
</Text>
|
|
207
|
+
</View>
|
|
208
|
+
</View>
|
|
209
|
+
);
|
|
210
|
+
return <View style={styles.headerTitleContainer}>{content ?? header}</View>;
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
const HeaderRightAction: React.FC<any> = ({children, ...restProps}) => {
|
|
214
|
+
const validType = (item: any) => {
|
|
215
|
+
if (item.type !== NavigationButton) {
|
|
216
|
+
console.warn(
|
|
217
|
+
'element type of NavigationButton, Please migrate to use NavigationButton of kits.',
|
|
218
|
+
);
|
|
219
|
+
return <View />;
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
const renderAction = () => {
|
|
223
|
+
if (Array.isArray(children)) {
|
|
224
|
+
return children.map((child, index) => {
|
|
225
|
+
if (__DEV__) {
|
|
226
|
+
validType(child);
|
|
227
|
+
}
|
|
228
|
+
return (
|
|
229
|
+
<React.Fragment key={index}>
|
|
230
|
+
{React.cloneElement(child, {...restProps})}
|
|
231
|
+
</React.Fragment>
|
|
232
|
+
);
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
validType(children);
|
|
237
|
+
return React.cloneElement(children, {...restProps});
|
|
238
|
+
};
|
|
239
|
+
return <View style={styles.headerRightButton}>{renderAction()}</View>;
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
const HeaderToolkitAction: React.FC<any> = ({tintColor}) => {
|
|
243
|
+
const {theme, navigator} = useContext(ApplicationContext);
|
|
244
|
+
const [toolConfig, setToolConfig] = useState<HeaderToolkitProps>();
|
|
245
|
+
const {appCode, hidden, type, onAction} = toolConfig ?? {};
|
|
246
|
+
|
|
247
|
+
let backgroundColor;
|
|
248
|
+
if (tintColor != Colors.black_01) {
|
|
249
|
+
backgroundColor = theme.colors.background.surface;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
useEffect(() => {
|
|
253
|
+
navigator?.getToolkit().then(toolConfig => {
|
|
254
|
+
setToolConfig(toolConfig);
|
|
255
|
+
});
|
|
256
|
+
}, []);
|
|
257
|
+
|
|
258
|
+
useEffect(() => {
|
|
259
|
+
let addFavorite: any;
|
|
260
|
+
let removeFavorite: any;
|
|
261
|
+
if (appCode) {
|
|
262
|
+
const addSchema = `observerToolkit_addFavorite_${appCode}`;
|
|
263
|
+
const removeSchema = `observerToolkit_removeFavorite_${appCode}`;
|
|
264
|
+
addFavorite = navigator?.requestApi(
|
|
265
|
+
'observer',
|
|
266
|
+
[addSchema],
|
|
267
|
+
(data: any) => {
|
|
268
|
+
if (data?.status) {
|
|
269
|
+
setToolConfig({
|
|
270
|
+
...(toolConfig as HeaderToolkitProps),
|
|
271
|
+
hidden: true,
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
},
|
|
275
|
+
);
|
|
276
|
+
addFavorite = navigator?.requestApi(
|
|
277
|
+
'observer',
|
|
278
|
+
[removeSchema],
|
|
279
|
+
(data: any) => {
|
|
280
|
+
if (data?.status) {
|
|
281
|
+
setToolConfig({
|
|
282
|
+
...(toolConfig as HeaderToolkitProps),
|
|
283
|
+
hidden: false,
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
},
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return () => {
|
|
291
|
+
addFavorite?.();
|
|
292
|
+
removeFavorite?.();
|
|
293
|
+
};
|
|
294
|
+
}, [appCode]);
|
|
295
|
+
|
|
296
|
+
const onUtilityTool = () => {
|
|
297
|
+
onAction?.('onUtilityTool');
|
|
298
|
+
navigator?.requestApi(type, undefined, undefined);
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
const onMore = () => {
|
|
302
|
+
onAction?.('onMore');
|
|
303
|
+
navigator?.requestApi('showToolkit', [toolConfig], undefined);
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
const onClose = () => {
|
|
307
|
+
onAction?.('onClose');
|
|
308
|
+
navigator?.requestApi('dismiss', undefined, undefined);
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
const renderUtilityTool = () => {
|
|
312
|
+
if (toolConfig?.hidden === false) {
|
|
313
|
+
return (
|
|
314
|
+
<NavigationButton
|
|
315
|
+
icon={type as string}
|
|
316
|
+
tintColor={tintColor}
|
|
317
|
+
onPress={onUtilityTool}
|
|
318
|
+
/>
|
|
319
|
+
);
|
|
320
|
+
}
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
if (toolConfig) {
|
|
324
|
+
return (
|
|
325
|
+
<View style={styles.headerRightButton}>
|
|
326
|
+
{renderUtilityTool()}
|
|
327
|
+
<View
|
|
328
|
+
style={[
|
|
329
|
+
styles.toolkitContainer,
|
|
330
|
+
{
|
|
331
|
+
backgroundColor: backgroundColor ?? '#00000066',
|
|
332
|
+
borderWidth: backgroundColor ? 0.5 : 0,
|
|
333
|
+
},
|
|
334
|
+
]}>
|
|
335
|
+
<TouchableOpacity
|
|
336
|
+
accessibilityLabel={'Toolkit/More'}
|
|
337
|
+
onPress={onMore}>
|
|
338
|
+
<Icon color={tintColor} source="navigation_more_horiz" size={20} />
|
|
339
|
+
</TouchableOpacity>
|
|
340
|
+
<View style={[styles.divider, {backgroundColor: tintColor}]} />
|
|
341
|
+
<TouchableOpacity
|
|
342
|
+
accessibilityLabel={'Toolkit/Close'}
|
|
343
|
+
onPress={onClose}>
|
|
344
|
+
<Icon
|
|
345
|
+
color={tintColor}
|
|
346
|
+
source="16_navigation_close_circle"
|
|
347
|
+
size={20}
|
|
348
|
+
/>
|
|
349
|
+
</TouchableOpacity>
|
|
350
|
+
</View>
|
|
351
|
+
</View>
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
return <View />;
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
export {
|
|
359
|
+
HeaderTitle,
|
|
360
|
+
HeaderLeft,
|
|
361
|
+
HeaderBackground,
|
|
362
|
+
HeaderRightAction,
|
|
363
|
+
HeaderToolkitAction,
|
|
364
|
+
HeaderCustom,
|
|
365
|
+
};
|