@hero-design/rn 7.1.1 → 7.1.2
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/.eslintrc.json +4 -3
- package/es/index.js +283 -113
- package/lib/index.js +282 -111
- package/package.json +4 -3
- package/playground/components/BottomNavigation.tsx +69 -0
- package/playground/components/Card.tsx +149 -70
- package/playground/index.tsx +3 -0
- package/rollup.config.js +1 -1
- package/src/components/BottomNavigation/StyledBottomNavigation.tsx +58 -0
- package/src/components/BottomNavigation/__tests__/BottomNavigation.spec.tsx +95 -0
- package/src/components/BottomNavigation/__tests__/__snapshots__/BottomNavigation.spec.tsx.snap +315 -0
- package/src/components/BottomNavigation/index.tsx +169 -0
- package/src/components/Icon/utils.ts +6 -0
- package/src/components/Typography/Text/StyledText.tsx +5 -1
- package/src/components/Typography/Text/index.tsx +1 -1
- package/src/index.ts +2 -0
- package/src/theme/__tests__/__snapshots__/index.spec.ts.snap +16 -0
- package/src/theme/components/bottomNavigation.ts +23 -0
- package/src/theme/components/typography.ts +1 -0
- package/src/theme/index.ts +3 -0
- package/src/utils/helpers.ts +4 -0
- package/types/playground/components/BottomNavigation.d.ts +2 -0
- package/types/src/components/BottomNavigation/StyledBottomNavigation.d.ts +17 -0
- package/types/src/components/BottomNavigation/__tests__/BottomNavigation.spec.d.ts +1 -0
- package/types/src/components/BottomNavigation/index.d.ts +40 -0
- package/types/src/components/Icon/utils.d.ts +2 -0
- package/types/src/components/Typography/Text/StyledText.d.ts +1 -1
- package/types/src/components/Typography/Text/index.d.ts +1 -1
- package/types/src/index.d.ts +2 -1
- package/types/src/theme/components/bottomNavigation.d.ts +17 -0
- package/types/src/theme/components/typography.d.ts +1 -0
- package/types/src/theme/index.d.ts +2 -0
- package/types/src/utils/helpers.d.ts +2 -0
- package/.expo/README.md +0 -15
- package/.expo/packager-info.json +0 -10
- package/.expo/settings.json +0 -10
package/src/components/BottomNavigation/__tests__/__snapshots__/BottomNavigation.spec.tsx.snap
ADDED
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`BottomNavigation renders correctly 1`] = `
|
|
4
|
+
<RNCSafeAreaProvider
|
|
5
|
+
onInsetsChange={[Function]}
|
|
6
|
+
style={
|
|
7
|
+
Array [
|
|
8
|
+
Object {
|
|
9
|
+
"flex": 1,
|
|
10
|
+
},
|
|
11
|
+
undefined,
|
|
12
|
+
]
|
|
13
|
+
}
|
|
14
|
+
>
|
|
15
|
+
<View
|
|
16
|
+
style={
|
|
17
|
+
Object {
|
|
18
|
+
"flexBasis": 0,
|
|
19
|
+
"flexGrow": 1,
|
|
20
|
+
"flexShrink": 1,
|
|
21
|
+
"overflow": "hidden",
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
>
|
|
25
|
+
<View
|
|
26
|
+
style={
|
|
27
|
+
Object {
|
|
28
|
+
"flexBasis": 0,
|
|
29
|
+
"flexGrow": 1,
|
|
30
|
+
"flexShrink": 1,
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
>
|
|
34
|
+
<View
|
|
35
|
+
accessibilityElementsHidden={false}
|
|
36
|
+
collapsable={false}
|
|
37
|
+
importantForAccessibility="auto"
|
|
38
|
+
pointerEvents="auto"
|
|
39
|
+
removeClippedSubviews={false}
|
|
40
|
+
style={
|
|
41
|
+
Object {
|
|
42
|
+
"display": "flex",
|
|
43
|
+
"flexBasis": 0,
|
|
44
|
+
"flexGrow": 1,
|
|
45
|
+
"flexShrink": 1,
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
testID="route-screen-home"
|
|
49
|
+
themeVisibility={true}
|
|
50
|
+
>
|
|
51
|
+
<Text>
|
|
52
|
+
Home Screen
|
|
53
|
+
</Text>
|
|
54
|
+
</View>
|
|
55
|
+
</View>
|
|
56
|
+
<View
|
|
57
|
+
style={
|
|
58
|
+
Object {
|
|
59
|
+
"backgroundColor": "#ffffff",
|
|
60
|
+
"elevation": 10,
|
|
61
|
+
"height": 72,
|
|
62
|
+
"paddingBottom": 0,
|
|
63
|
+
"paddingLeft": 0,
|
|
64
|
+
"paddingRight": 0,
|
|
65
|
+
"shadowColor": "#292a2b",
|
|
66
|
+
"shadowOffset": Object {
|
|
67
|
+
"height": 3,
|
|
68
|
+
"width": 0,
|
|
69
|
+
},
|
|
70
|
+
"shadowOpacity": 0.27,
|
|
71
|
+
"shadowRadius": 4.65,
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
themeInsets={
|
|
75
|
+
Object {
|
|
76
|
+
"bottom": 0,
|
|
77
|
+
"left": 0,
|
|
78
|
+
"right": 0,
|
|
79
|
+
"top": 0,
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
>
|
|
83
|
+
<View
|
|
84
|
+
style={
|
|
85
|
+
Object {
|
|
86
|
+
"alignItems": "center",
|
|
87
|
+
"flexBasis": 0,
|
|
88
|
+
"flexDirection": "row",
|
|
89
|
+
"flexGrow": 1,
|
|
90
|
+
"flexShrink": 1,
|
|
91
|
+
"overflow": "hidden",
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
>
|
|
95
|
+
<View
|
|
96
|
+
accessible={true}
|
|
97
|
+
focusable={true}
|
|
98
|
+
onClick={[Function]}
|
|
99
|
+
onResponderGrant={[Function]}
|
|
100
|
+
onResponderMove={[Function]}
|
|
101
|
+
onResponderRelease={[Function]}
|
|
102
|
+
onResponderTerminate={[Function]}
|
|
103
|
+
onResponderTerminationRequest={[Function]}
|
|
104
|
+
onStartShouldSetResponder={[Function]}
|
|
105
|
+
style={
|
|
106
|
+
Object {
|
|
107
|
+
"alignItems": "center",
|
|
108
|
+
"flexBasis": 0,
|
|
109
|
+
"flexGrow": 1,
|
|
110
|
+
"flexShrink": 1,
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
>
|
|
114
|
+
<HeroIcon
|
|
115
|
+
name="home"
|
|
116
|
+
style={
|
|
117
|
+
Object {
|
|
118
|
+
"color": "#7622d7",
|
|
119
|
+
"fontSize": 24,
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
testID="hero-icon-home"
|
|
123
|
+
themeIntent="primary"
|
|
124
|
+
themeSize="medium"
|
|
125
|
+
/>
|
|
126
|
+
<Text
|
|
127
|
+
numberOfLines={1}
|
|
128
|
+
style={
|
|
129
|
+
Array [
|
|
130
|
+
Object {
|
|
131
|
+
"color": "#7622d7",
|
|
132
|
+
"fontSize": 12,
|
|
133
|
+
"fontWeight": "600",
|
|
134
|
+
"letterSpacing": 0.36,
|
|
135
|
+
"lineHeight": 20,
|
|
136
|
+
},
|
|
137
|
+
Object {
|
|
138
|
+
"marginTop": 4,
|
|
139
|
+
},
|
|
140
|
+
]
|
|
141
|
+
}
|
|
142
|
+
themeFontSize="small"
|
|
143
|
+
themeFontWeight="semi-bold"
|
|
144
|
+
themeIntent="primary"
|
|
145
|
+
>
|
|
146
|
+
Home
|
|
147
|
+
</Text>
|
|
148
|
+
</View>
|
|
149
|
+
<View
|
|
150
|
+
accessible={true}
|
|
151
|
+
focusable={true}
|
|
152
|
+
onClick={[Function]}
|
|
153
|
+
onResponderGrant={[Function]}
|
|
154
|
+
onResponderMove={[Function]}
|
|
155
|
+
onResponderRelease={[Function]}
|
|
156
|
+
onResponderTerminate={[Function]}
|
|
157
|
+
onResponderTerminationRequest={[Function]}
|
|
158
|
+
onStartShouldSetResponder={[Function]}
|
|
159
|
+
style={
|
|
160
|
+
Object {
|
|
161
|
+
"alignItems": "center",
|
|
162
|
+
"flexBasis": 0,
|
|
163
|
+
"flexGrow": 1,
|
|
164
|
+
"flexShrink": 1,
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
>
|
|
168
|
+
<HeroIcon
|
|
169
|
+
name="speaker-outlined"
|
|
170
|
+
style={
|
|
171
|
+
Object {
|
|
172
|
+
"color": "#292a2b",
|
|
173
|
+
"fontSize": 24,
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
testID="hero-icon-speaker-outlined"
|
|
177
|
+
themeIntent="text"
|
|
178
|
+
themeSize="medium"
|
|
179
|
+
/>
|
|
180
|
+
<Text
|
|
181
|
+
numberOfLines={1}
|
|
182
|
+
style={
|
|
183
|
+
Array [
|
|
184
|
+
Object {
|
|
185
|
+
"color": "#292a2b",
|
|
186
|
+
"fontSize": 12,
|
|
187
|
+
"fontWeight": "600",
|
|
188
|
+
"letterSpacing": 0.36,
|
|
189
|
+
"lineHeight": 20,
|
|
190
|
+
},
|
|
191
|
+
Object {
|
|
192
|
+
"marginTop": 4,
|
|
193
|
+
},
|
|
194
|
+
]
|
|
195
|
+
}
|
|
196
|
+
themeFontSize="small"
|
|
197
|
+
themeFontWeight="semi-bold"
|
|
198
|
+
themeIntent="body"
|
|
199
|
+
>
|
|
200
|
+
Feed
|
|
201
|
+
</Text>
|
|
202
|
+
</View>
|
|
203
|
+
<View
|
|
204
|
+
accessible={true}
|
|
205
|
+
focusable={true}
|
|
206
|
+
onClick={[Function]}
|
|
207
|
+
onResponderGrant={[Function]}
|
|
208
|
+
onResponderMove={[Function]}
|
|
209
|
+
onResponderRelease={[Function]}
|
|
210
|
+
onResponderTerminate={[Function]}
|
|
211
|
+
onResponderTerminationRequest={[Function]}
|
|
212
|
+
onStartShouldSetResponder={[Function]}
|
|
213
|
+
style={
|
|
214
|
+
Object {
|
|
215
|
+
"alignItems": "center",
|
|
216
|
+
"flexBasis": 0,
|
|
217
|
+
"flexGrow": 1,
|
|
218
|
+
"flexShrink": 1,
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
>
|
|
222
|
+
<HeroIcon
|
|
223
|
+
name="bell-outlined"
|
|
224
|
+
style={
|
|
225
|
+
Object {
|
|
226
|
+
"color": "#292a2b",
|
|
227
|
+
"fontSize": 24,
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
testID="hero-icon-bell-outlined"
|
|
231
|
+
themeIntent="text"
|
|
232
|
+
themeSize="medium"
|
|
233
|
+
/>
|
|
234
|
+
<Text
|
|
235
|
+
numberOfLines={1}
|
|
236
|
+
style={
|
|
237
|
+
Array [
|
|
238
|
+
Object {
|
|
239
|
+
"color": "#292a2b",
|
|
240
|
+
"fontSize": 12,
|
|
241
|
+
"fontWeight": "600",
|
|
242
|
+
"letterSpacing": 0.36,
|
|
243
|
+
"lineHeight": 20,
|
|
244
|
+
},
|
|
245
|
+
Object {
|
|
246
|
+
"marginTop": 4,
|
|
247
|
+
},
|
|
248
|
+
]
|
|
249
|
+
}
|
|
250
|
+
themeFontSize="small"
|
|
251
|
+
themeFontWeight="semi-bold"
|
|
252
|
+
themeIntent="body"
|
|
253
|
+
>
|
|
254
|
+
Alerts
|
|
255
|
+
</Text>
|
|
256
|
+
</View>
|
|
257
|
+
<View
|
|
258
|
+
accessible={true}
|
|
259
|
+
focusable={true}
|
|
260
|
+
onClick={[Function]}
|
|
261
|
+
onResponderGrant={[Function]}
|
|
262
|
+
onResponderMove={[Function]}
|
|
263
|
+
onResponderRelease={[Function]}
|
|
264
|
+
onResponderTerminate={[Function]}
|
|
265
|
+
onResponderTerminationRequest={[Function]}
|
|
266
|
+
onStartShouldSetResponder={[Function]}
|
|
267
|
+
style={
|
|
268
|
+
Object {
|
|
269
|
+
"alignItems": "center",
|
|
270
|
+
"flexBasis": 0,
|
|
271
|
+
"flexGrow": 1,
|
|
272
|
+
"flexShrink": 1,
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
>
|
|
276
|
+
<HeroIcon
|
|
277
|
+
name="user-outlined"
|
|
278
|
+
style={
|
|
279
|
+
Object {
|
|
280
|
+
"color": "#292a2b",
|
|
281
|
+
"fontSize": 24,
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
testID="hero-icon-user-outlined"
|
|
285
|
+
themeIntent="text"
|
|
286
|
+
themeSize="medium"
|
|
287
|
+
/>
|
|
288
|
+
<Text
|
|
289
|
+
numberOfLines={1}
|
|
290
|
+
style={
|
|
291
|
+
Array [
|
|
292
|
+
Object {
|
|
293
|
+
"color": "#292a2b",
|
|
294
|
+
"fontSize": 12,
|
|
295
|
+
"fontWeight": "600",
|
|
296
|
+
"letterSpacing": 0.36,
|
|
297
|
+
"lineHeight": 20,
|
|
298
|
+
},
|
|
299
|
+
Object {
|
|
300
|
+
"marginTop": 4,
|
|
301
|
+
},
|
|
302
|
+
]
|
|
303
|
+
}
|
|
304
|
+
themeFontSize="small"
|
|
305
|
+
themeFontWeight="semi-bold"
|
|
306
|
+
themeIntent="body"
|
|
307
|
+
>
|
|
308
|
+
Profile
|
|
309
|
+
</Text>
|
|
310
|
+
</View>
|
|
311
|
+
</View>
|
|
312
|
+
</View>
|
|
313
|
+
</View>
|
|
314
|
+
</RNCSafeAreaProvider>
|
|
315
|
+
`;
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import React, { ComponentProps, ReactNode } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
TouchableWithoutFeedback,
|
|
4
|
+
StyleProp,
|
|
5
|
+
ViewStyle,
|
|
6
|
+
ViewProps,
|
|
7
|
+
} from 'react-native';
|
|
8
|
+
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
9
|
+
import { useTheme } from 'styled-components-native';
|
|
10
|
+
import Icon from '../Icon';
|
|
11
|
+
import { isHeroIcon } from '../Icon/utils';
|
|
12
|
+
import Typography from '../Typography';
|
|
13
|
+
import {
|
|
14
|
+
BottomBar,
|
|
15
|
+
BottomBarItem,
|
|
16
|
+
BottomBarWrapper,
|
|
17
|
+
BottomNavigationContainer,
|
|
18
|
+
BottomNavigationTab,
|
|
19
|
+
ContentWrapper,
|
|
20
|
+
} from './StyledBottomNavigation';
|
|
21
|
+
import { isIOS } from '../../utils/helpers';
|
|
22
|
+
|
|
23
|
+
type IconType = ComponentProps<typeof Icon>['icon'];
|
|
24
|
+
|
|
25
|
+
export type Tab<V> = {
|
|
26
|
+
key: V;
|
|
27
|
+
title?: string;
|
|
28
|
+
icon: IconType;
|
|
29
|
+
component: ReactNode;
|
|
30
|
+
testID?: string;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
interface BottomNavigationType<V extends string | number, T extends Tab<V>>
|
|
34
|
+
extends ViewProps {
|
|
35
|
+
/**
|
|
36
|
+
* Callback which is called on tab change, receiving id of upcoming active Tab.
|
|
37
|
+
*/
|
|
38
|
+
onChange: (key: V) => void;
|
|
39
|
+
/**
|
|
40
|
+
* Whether inactive tabs should be removed and unmounted in React.
|
|
41
|
+
* Defaults to `false`.
|
|
42
|
+
*/
|
|
43
|
+
renderActiveTabOnly?: boolean;
|
|
44
|
+
/**
|
|
45
|
+
* Current selected tab key.
|
|
46
|
+
*/
|
|
47
|
+
selectedTabKey: V;
|
|
48
|
+
/**
|
|
49
|
+
* List of Tab to be rendered. Each Tab must have an unique id.
|
|
50
|
+
*/
|
|
51
|
+
tabs: T[];
|
|
52
|
+
/**
|
|
53
|
+
* Addditional style.
|
|
54
|
+
*/
|
|
55
|
+
style?: StyleProp<ViewStyle>;
|
|
56
|
+
/**
|
|
57
|
+
* Testing id of the component.
|
|
58
|
+
*/
|
|
59
|
+
testID?: string;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const getInactiveIcon = (icon: IconType): IconType => {
|
|
63
|
+
const inactiveIcon = `${icon}-outlined`;
|
|
64
|
+
return isHeroIcon(inactiveIcon) ? inactiveIcon : icon;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const BottomNavigation = <V extends string | number, T extends Tab<V>>({
|
|
68
|
+
onChange,
|
|
69
|
+
renderActiveTabOnly = false,
|
|
70
|
+
selectedTabKey,
|
|
71
|
+
tabs,
|
|
72
|
+
...nativeProps
|
|
73
|
+
}: BottomNavigationType<V, T>): JSX.Element => {
|
|
74
|
+
const theme = useTheme();
|
|
75
|
+
const insets = useSafeAreaInsets();
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* List of loaded tabs, tabs will be loaded when navigated to.
|
|
79
|
+
*/
|
|
80
|
+
const [loaded, setLoaded] = React.useState([selectedTabKey]);
|
|
81
|
+
|
|
82
|
+
if (!loaded.includes(selectedTabKey)) {
|
|
83
|
+
// Set the current tab to be loaded if it was not loaded before
|
|
84
|
+
setLoaded(loadedState => [...loadedState, selectedTabKey]);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
<BottomNavigationContainer {...nativeProps}>
|
|
89
|
+
<ContentWrapper>
|
|
90
|
+
{tabs.map(tab => {
|
|
91
|
+
const { key, component } = tab;
|
|
92
|
+
const active = selectedTabKey === key;
|
|
93
|
+
|
|
94
|
+
if (renderActiveTabOnly && !active) {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (!loaded.includes(key)) {
|
|
99
|
+
// Don't render a screen if we've never navigated to it
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return (
|
|
104
|
+
<BottomNavigationTab
|
|
105
|
+
key={key}
|
|
106
|
+
testID={`route-screen-${selectedTabKey}`}
|
|
107
|
+
pointerEvents={active ? 'auto' : 'none'}
|
|
108
|
+
accessibilityElementsHidden={!active}
|
|
109
|
+
importantForAccessibility={
|
|
110
|
+
active ? 'auto' : 'no-hide-descendants'
|
|
111
|
+
}
|
|
112
|
+
collapsable={false}
|
|
113
|
+
removeClippedSubviews={
|
|
114
|
+
// On iOS, set removeClippedSubviews to true only when not focused
|
|
115
|
+
// This is an workaround for a bug where the clipped view never re-appears.
|
|
116
|
+
isIOS ? selectedTabKey !== key : true
|
|
117
|
+
}
|
|
118
|
+
themeVisibility={active}
|
|
119
|
+
>
|
|
120
|
+
{component}
|
|
121
|
+
</BottomNavigationTab>
|
|
122
|
+
);
|
|
123
|
+
})}
|
|
124
|
+
</ContentWrapper>
|
|
125
|
+
<BottomBarWrapper themeInsets={insets}>
|
|
126
|
+
<BottomBar>
|
|
127
|
+
{tabs.map(tab => {
|
|
128
|
+
const { key, icon, title, testID } = tab;
|
|
129
|
+
const active = selectedTabKey === key;
|
|
130
|
+
const inactiveIcon = getInactiveIcon(icon);
|
|
131
|
+
|
|
132
|
+
return (
|
|
133
|
+
<TouchableWithoutFeedback
|
|
134
|
+
key={key}
|
|
135
|
+
onPress={() => {
|
|
136
|
+
if (key !== selectedTabKey) {
|
|
137
|
+
onChange(key);
|
|
138
|
+
}
|
|
139
|
+
}}
|
|
140
|
+
testID={testID}
|
|
141
|
+
>
|
|
142
|
+
<BottomBarItem>
|
|
143
|
+
<Icon
|
|
144
|
+
icon={active ? icon : inactiveIcon}
|
|
145
|
+
intent={active ? 'primary' : 'text'}
|
|
146
|
+
testID={`hero-icon-${icon}`}
|
|
147
|
+
/>
|
|
148
|
+
{title && (
|
|
149
|
+
<Typography.Text
|
|
150
|
+
fontSize="small"
|
|
151
|
+
fontWeight="semi-bold"
|
|
152
|
+
intent={active ? 'primary' : 'body'}
|
|
153
|
+
style={{ marginTop: theme.space.xsmall }}
|
|
154
|
+
numberOfLines={1}
|
|
155
|
+
>
|
|
156
|
+
{title}
|
|
157
|
+
</Typography.Text>
|
|
158
|
+
)}
|
|
159
|
+
</BottomBarItem>
|
|
160
|
+
</TouchableWithoutFeedback>
|
|
161
|
+
);
|
|
162
|
+
})}
|
|
163
|
+
</BottomBar>
|
|
164
|
+
</BottomBarWrapper>
|
|
165
|
+
</BottomNavigationContainer>
|
|
166
|
+
);
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
export default BottomNavigation;
|
|
@@ -4,7 +4,7 @@ import styled, { css } from '../../../styled-components';
|
|
|
4
4
|
const StyledText = styled(Text)<{
|
|
5
5
|
themeFontSize: 'small' | 'medium' | 'large' | 'xlarge';
|
|
6
6
|
themeFontWeight: 'light' | 'regular' | 'semi-bold';
|
|
7
|
-
themeIntent: 'body' | 'subdued';
|
|
7
|
+
themeIntent: 'body' | 'subdued' | 'primary';
|
|
8
8
|
}>`
|
|
9
9
|
${({ themeFontSize, theme }) => {
|
|
10
10
|
switch (themeFontSize) {
|
|
@@ -62,6 +62,10 @@ const StyledText = styled(Text)<{
|
|
|
62
62
|
return css`
|
|
63
63
|
color: ${theme.__hd__.typography.colors.subdued};
|
|
64
64
|
`;
|
|
65
|
+
case 'primary':
|
|
66
|
+
return css`
|
|
67
|
+
color: ${theme.__hd__.typography.colors.primary};
|
|
68
|
+
`;
|
|
65
69
|
}
|
|
66
70
|
}}
|
|
67
71
|
`;
|
package/src/index.ts
CHANGED
|
@@ -3,6 +3,7 @@ import theme, { getTheme } from './theme';
|
|
|
3
3
|
import { scale } from './utils/scale';
|
|
4
4
|
|
|
5
5
|
import Badge from './components/Badge';
|
|
6
|
+
import BottomNavigation from './components/BottomNavigation';
|
|
6
7
|
import Card from './components/Card';
|
|
7
8
|
import Divider from './components/Divider';
|
|
8
9
|
import Icon from './components/Icon';
|
|
@@ -16,6 +17,7 @@ export {
|
|
|
16
17
|
scale,
|
|
17
18
|
ThemeProvider,
|
|
18
19
|
Badge,
|
|
20
|
+
BottomNavigation,
|
|
19
21
|
Card,
|
|
20
22
|
Divider,
|
|
21
23
|
Icon,
|
|
@@ -30,6 +30,21 @@ Object {
|
|
|
30
30
|
"default": "4px",
|
|
31
31
|
},
|
|
32
32
|
},
|
|
33
|
+
"bottomNavigation": Object {
|
|
34
|
+
"colors": Object {
|
|
35
|
+
"background": "#ffffff",
|
|
36
|
+
"shadow": "#292a2b",
|
|
37
|
+
},
|
|
38
|
+
"shadows": Object {
|
|
39
|
+
"elevation": 10,
|
|
40
|
+
"offset": "0px 3px",
|
|
41
|
+
"opacity": 0.27,
|
|
42
|
+
"radius": "4.65px",
|
|
43
|
+
},
|
|
44
|
+
"sizes": Object {
|
|
45
|
+
"height": 72,
|
|
46
|
+
},
|
|
47
|
+
},
|
|
33
48
|
"card": Object {
|
|
34
49
|
"padding": Object {
|
|
35
50
|
"default": "8px",
|
|
@@ -86,6 +101,7 @@ Object {
|
|
|
86
101
|
"typography": Object {
|
|
87
102
|
"colors": Object {
|
|
88
103
|
"body": "#292a2b",
|
|
104
|
+
"primary": "#7622d7",
|
|
89
105
|
"subdued": "#8b8d92",
|
|
90
106
|
},
|
|
91
107
|
"fontSizes": Object {
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { GlobalTheme } from '../global';
|
|
2
|
+
|
|
3
|
+
const getBottomNavigationTheme = (theme: GlobalTheme) => {
|
|
4
|
+
const colors = {
|
|
5
|
+
shadow: theme.colors.backgroundDark,
|
|
6
|
+
background: theme.colors.platformBackground,
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
const sizes = {
|
|
10
|
+
height: 72,
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const shadows = {
|
|
14
|
+
offset: `0px 3px`,
|
|
15
|
+
opacity: 0.27,
|
|
16
|
+
radius: `4.65px`,
|
|
17
|
+
elevation: 10,
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
return { colors, shadows, sizes };
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export default getBottomNavigationTheme;
|
package/src/theme/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import globalTheme, { GlobalTheme } from './global';
|
|
2
2
|
|
|
3
3
|
import getBadgeTheme from './components/badge';
|
|
4
|
+
import getBottomNavigationTheme from './components/bottomNavigation';
|
|
4
5
|
import getCardTheme from './components/card';
|
|
5
6
|
import getDividerTheme from './components/divider';
|
|
6
7
|
import getIconTheme from './components/icon';
|
|
@@ -10,6 +11,7 @@ import getFABTheme from './components/fab';
|
|
|
10
11
|
type Theme = GlobalTheme & {
|
|
11
12
|
__hd__: {
|
|
12
13
|
badge: ReturnType<typeof getBadgeTheme>;
|
|
14
|
+
bottomNavigation: ReturnType<typeof getBottomNavigationTheme>;
|
|
13
15
|
card: ReturnType<typeof getCardTheme>;
|
|
14
16
|
divider: ReturnType<typeof getDividerTheme>;
|
|
15
17
|
icon: ReturnType<typeof getIconTheme>;
|
|
@@ -22,6 +24,7 @@ const getTheme = (theme: GlobalTheme = globalTheme): Theme => ({
|
|
|
22
24
|
...theme,
|
|
23
25
|
__hd__: {
|
|
24
26
|
badge: getBadgeTheme(theme),
|
|
27
|
+
bottomNavigation: getBottomNavigationTheme(theme),
|
|
25
28
|
card: getCardTheme(theme),
|
|
26
29
|
divider: getDividerTheme(theme),
|
|
27
30
|
icon: getIconTheme(theme),
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { View } from 'react-native';
|
|
2
|
+
declare const BottomNavigationTab: import("styled-components").StyledComponent<typeof View, import("../../theme").Theme, {
|
|
3
|
+
themeVisibility?: boolean | undefined;
|
|
4
|
+
}, never>;
|
|
5
|
+
declare const BottomNavigationContainer: import("styled-components").StyledComponent<typeof View, import("../../theme").Theme, {}, never>;
|
|
6
|
+
declare const ContentWrapper: import("styled-components").StyledComponent<typeof View, import("../../theme").Theme, {}, never>;
|
|
7
|
+
declare const BottomBarWrapper: import("styled-components").StyledComponent<typeof View, import("../../theme").Theme, {
|
|
8
|
+
themeInsets: {
|
|
9
|
+
top: number;
|
|
10
|
+
right: number;
|
|
11
|
+
bottom: number;
|
|
12
|
+
left: number;
|
|
13
|
+
};
|
|
14
|
+
}, never>;
|
|
15
|
+
declare const BottomBar: import("styled-components").StyledComponent<typeof View, import("../../theme").Theme, {}, never>;
|
|
16
|
+
declare const BottomBarItem: import("styled-components").StyledComponent<typeof View, import("../../theme").Theme, {}, never>;
|
|
17
|
+
export { BottomBar, BottomBarItem, BottomNavigationTab, BottomNavigationContainer, BottomBarWrapper, ContentWrapper, };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { ComponentProps, ReactNode } from 'react';
|
|
2
|
+
import { StyleProp, ViewStyle, ViewProps } from 'react-native';
|
|
3
|
+
import Icon from '../Icon';
|
|
4
|
+
declare type IconType = ComponentProps<typeof Icon>['icon'];
|
|
5
|
+
export declare type Tab<V> = {
|
|
6
|
+
key: V;
|
|
7
|
+
title?: string;
|
|
8
|
+
icon: IconType;
|
|
9
|
+
component: ReactNode;
|
|
10
|
+
testID?: string;
|
|
11
|
+
};
|
|
12
|
+
interface BottomNavigationType<V extends string | number, T extends Tab<V>> extends ViewProps {
|
|
13
|
+
/**
|
|
14
|
+
* Callback which is called on tab change, receiving id of upcoming active Tab.
|
|
15
|
+
*/
|
|
16
|
+
onChange: (key: V) => void;
|
|
17
|
+
/**
|
|
18
|
+
* Whether inactive tabs should be removed and unmounted in React.
|
|
19
|
+
* Defaults to `false`.
|
|
20
|
+
*/
|
|
21
|
+
renderActiveTabOnly?: boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Current selected tab key.
|
|
24
|
+
*/
|
|
25
|
+
selectedTabKey: V;
|
|
26
|
+
/**
|
|
27
|
+
* List of Tab to be rendered. Each Tab must have an unique id.
|
|
28
|
+
*/
|
|
29
|
+
tabs: T[];
|
|
30
|
+
/**
|
|
31
|
+
* Addditional style.
|
|
32
|
+
*/
|
|
33
|
+
style?: StyleProp<ViewStyle>;
|
|
34
|
+
/**
|
|
35
|
+
* Testing id of the component.
|
|
36
|
+
*/
|
|
37
|
+
testID?: string;
|
|
38
|
+
}
|
|
39
|
+
declare const BottomNavigation: <V extends string | number, T extends Tab<V>>({ onChange, renderActiveTabOnly, selectedTabKey, tabs, ...nativeProps }: BottomNavigationType<V, T>) => JSX.Element;
|
|
40
|
+
export default BottomNavigation;
|