react-native-boxes 1.0.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 (55) hide show
  1. package/README.md +1 -0
  2. package/dist/Bar.d.ts +26 -0
  3. package/dist/Bar.js +159 -0
  4. package/dist/Bar.js.map +1 -0
  5. package/dist/Box.d.ts +14 -0
  6. package/dist/Box.js +103 -0
  7. package/dist/Box.js.map +1 -0
  8. package/dist/Button.d.ts +20 -0
  9. package/dist/Button.js +220 -0
  10. package/dist/Button.js.map +1 -0
  11. package/dist/Image.d.ts +21 -0
  12. package/dist/Image.js +79 -0
  13. package/dist/Image.js.map +1 -0
  14. package/dist/Input.d.ts +27 -0
  15. package/dist/Input.js +190 -0
  16. package/dist/Input.js.map +1 -0
  17. package/dist/Message.d.ts +7 -0
  18. package/dist/Message.js +97 -0
  19. package/dist/Message.js.map +1 -0
  20. package/dist/Modal.d.ts +22 -0
  21. package/dist/Modal.js +247 -0
  22. package/dist/Modal.js.map +1 -0
  23. package/dist/Styles.d.ts +113 -0
  24. package/dist/Styles.js +105 -0
  25. package/dist/Styles.js.map +1 -0
  26. package/dist/Text.d.ts +5 -0
  27. package/dist/Text.js +49 -0
  28. package/dist/Text.js.map +1 -0
  29. package/dist/ThemeContext.d.ts +93 -0
  30. package/dist/ThemeContext.js +26 -0
  31. package/dist/ThemeContext.js.map +1 -0
  32. package/dist/demo.d.ts +5 -0
  33. package/dist/demo.js +301 -0
  34. package/dist/demo.js.map +1 -0
  35. package/dist/index.d.ts +8 -0
  36. package/dist/index.js +25 -0
  37. package/dist/index.js.map +1 -0
  38. package/dist/utils.d.ts +5 -0
  39. package/dist/utils.js +45 -0
  40. package/dist/utils.js.map +1 -0
  41. package/package.json +37 -0
  42. package/src/Bar.tsx +185 -0
  43. package/src/Box.tsx +106 -0
  44. package/src/Button.tsx +282 -0
  45. package/src/Image.tsx +107 -0
  46. package/src/Input.tsx +244 -0
  47. package/src/Message.tsx +90 -0
  48. package/src/Modal.tsx +306 -0
  49. package/src/Styles.tsx +117 -0
  50. package/src/Text.tsx +56 -0
  51. package/src/ThemeContext.ts +25 -0
  52. package/src/demo.tsx +383 -0
  53. package/src/index.tsx +8 -0
  54. package/src/utils.ts +49 -0
  55. package/tsconfig.json +25 -0
package/src/Input.tsx ADDED
@@ -0,0 +1,244 @@
1
+ import { ReactNode, useContext, useEffect, useState } from "react";
2
+ import { GestureResponderEvent, TextInput, TextInputProps, TouchableOpacity } from "react-native";
3
+ import { ThemeContext } from "./ThemeContext";
4
+ import { assignFields, isWeb } from "./utils";
5
+ import { HBox, VBox } from "./Box";
6
+ import { TextView } from "./Text";
7
+ import { Icon } from "./Image";
8
+
9
+
10
+ /**
11
+ *
12
+ * @param initialText if you use initialText then you dont need to maintain the 2 way binding between value and setValue, just enjoy props.onChangeText events
13
+ * @returns
14
+ */
15
+ export function TextInputView(props: TextInputProps & {
16
+ initialText?: string,
17
+ pattern?: string,
18
+ }) {
19
+ const theme = useContext(ThemeContext)
20
+ const [text, setText] = useState(props.initialText)
21
+ const [focused, setFocused] = useState(false)
22
+ function onTextChange(newText: string) {
23
+ setText(newText)
24
+ props.onChangeText && props.onChangeText(newText)
25
+ }
26
+
27
+ return (
28
+ <TextInput
29
+ {...props}
30
+ onFocus={(e) => {
31
+ setFocused(true)
32
+ props.onFocus && props.onFocus(e)
33
+ }}
34
+ onBlur={(e) => {
35
+ setFocused(false)
36
+ props.onBlur && props.onBlur(e)
37
+ }}
38
+ value={props.value || text}
39
+ onChangeText={(newText) => {
40
+ if (props.pattern) {
41
+ const re = new RegExp(props.pattern);
42
+ if (newText === "" || re.test(newText)) {
43
+ onTextChange(newText)
44
+ }
45
+ } else {
46
+ onTextChange(newText)
47
+ }
48
+ }}
49
+ style={[{
50
+ color: theme.colors.text,
51
+ textAlignVertical: "top",
52
+ width: 'auto',
53
+ fontSize: theme.dimens.font.md,
54
+ fontFamily: theme.fonts.Regular,
55
+ padding: theme.dimens.space.md,
56
+ paddingStart: theme.dimens.space.md,
57
+ paddingBottom: isWeb() ? theme.dimens.space.md : theme.dimens.space.sm / 2,
58
+ margin: theme.dimens.space.sm,
59
+ borderWidth: focused ? 1.5 : 1.5,
60
+ borderRadius: theme.dimens.space.sm,
61
+ borderColor: !focused ? theme.colors.caption : theme.colors.accent
62
+ }, isWeb() ? {
63
+ //@ts-ignore
64
+ outline: 'none',
65
+ } : {},
66
+ props.style]}
67
+ />
68
+ )
69
+ }
70
+
71
+
72
+ /**
73
+ * Note: if input is inside a ScrollView in heirarchy anywhere then add keyboardShouldPersistTaps={'handled'}
74
+ * to the scrollview else the icon click wont work
75
+ * In case, you textinput is getting hidden due to keyboard see https://stackoverflow.com/a/77563800/6865753
76
+
77
+ * @param props
78
+ * @returns
79
+ */
80
+ export function CompositeTextInputView(props: TextInputProps & {
81
+ hint?: string,
82
+ alertText?: string,
83
+ alertTextColor?: string,
84
+ pattern?: string,
85
+ initialText?: string,
86
+ icon?: 'close' | 'eye' | string | React.Component,
87
+ onIconPress?: ((event: GestureResponderEvent) => void) | undefined
88
+ }) {
89
+ const theme = useContext(ThemeContext)
90
+ const [text, setText] = useState(props.initialText)
91
+ const [alerttext, setAlertText] = useState(props.alertText)
92
+ const [focused, setFocused] = useState(false)
93
+ function onTextChange(newText: string) {
94
+ setText(newText)
95
+ props.onChangeText && props.onChangeText(newText)
96
+ }
97
+ const fontStyles: any = assignFields({}, props.style,
98
+ [
99
+ "returnKeyType",
100
+ "keyboardType",
101
+ "textContentType",
102
+ "multiline",
103
+ "fontFamily",
104
+ "fontSize",
105
+ "fontWeight",
106
+ "fontVariant",
107
+ "color",
108
+ "inputMode"
109
+ ])
110
+ var hintVisible = false
111
+ if (props.placeholder && props.placeholder.length > 0 && focused) {
112
+ hintVisible = true
113
+ }
114
+ if (!focused && text && text.length > 0) {
115
+ hintVisible = true
116
+ }
117
+ if (!props.placeholder) {
118
+ hintVisible = false
119
+ }
120
+ // (focused || ((text ||
121
+ // props.value == undefined ? props.placeholder : props.value || ''
122
+ // )?.length || 0) > 0)
123
+ // if (!hintVisible && props.placeholder != undefined && props.placeholder?.length > 0 && text != undefined && text?.length > 0) {
124
+ // hintVisible = false
125
+ // }
126
+ const alertVisible = alerttext && alerttext.length
127
+ useEffect(() => {
128
+ setText(props.value)
129
+ }, [props.value])
130
+ useEffect(() => {
131
+ setAlertText(props.alertText)
132
+ }, [props.alertText])
133
+
134
+ //@ts-ignore
135
+ const IconComponent: ReactNode = typeof props.icon == 'string' ? (
136
+ <Icon
137
+ color={!focused ? theme.colors.caption : theme.colors.accent}
138
+ style={{
139
+ minWidth: isWeb() ? theme.dimens.icon.lg : theme.dimens.icon.sm,
140
+ padding: theme.dimens.space.sm / 2,
141
+ }} name={props.icon} />
142
+ ) : props.icon
143
+
144
+ return (
145
+ <HBox style={[{
146
+ paddingEnd: theme.dimens.space.md,
147
+ justifyContent: 'space-between',
148
+ alignItems: 'center',
149
+ padding: 0,
150
+ paddingStart: theme.dimens.space.sm * 1.5,
151
+ margin: theme.dimens.space.sm,
152
+ borderWidth: 1.5,
153
+ borderRadius: theme.dimens.space.sm,
154
+ borderColor: !focused ? theme.colors.caption : theme.colors.accent,
155
+ }, (!hintVisible && !alertVisible) ? {
156
+ paddingTop: theme.dimens.space.sm,
157
+ paddingBottom: theme.dimens.space.sm
158
+ } : {
159
+
160
+ }, props.style]}>
161
+
162
+
163
+ <VBox style={[{
164
+ flex: 1,
165
+ paddingBottom: alertVisible ? 0 : theme.dimens.space.sm
166
+ }, hintVisible ? {
167
+ } : {
168
+ paddingTop: theme.dimens.space.sm
169
+ }]}>
170
+ {hintVisible && <TextView
171
+ style={{
172
+ padding: 0,
173
+ margin: 0,
174
+ fontSize: theme.dimens.font.sm,
175
+ color: !focused ? theme.colors.caption : theme.colors.accent,
176
+
177
+ }}
178
+ >{props.placeholder}</TextView>}
179
+ <TextInput
180
+ selectionColor={props.selectionColor || theme.colors.accent}
181
+ secureTextEntry={props.secureTextEntry}
182
+ placeholderTextColor={theme.colors.caption}
183
+ placeholder={hintVisible ? '' : props.placeholder}
184
+ keyboardType={props.keyboardType}
185
+ returnKeyType={props.returnKeyType || 'default'}
186
+ onFocus={(e) => {
187
+ setFocused(true)
188
+ props.onFocus && props.onFocus(e)
189
+ }}
190
+ onBlur={(e) => {
191
+ setFocused(false)
192
+ props.onBlur && props.onBlur(e)
193
+ }}
194
+ value={props.value || text}
195
+ onChangeText={(newText) => {
196
+ if (props.pattern) {
197
+ const re = new RegExp(props.pattern);
198
+ if (newText === "" || re.test(newText)) {
199
+ onTextChange(newText)
200
+ }
201
+ } else {
202
+ onTextChange(newText)
203
+ }
204
+ }}
205
+ style={[{
206
+ color: theme.colors.text,
207
+ fontSize: theme.dimens.font.md,
208
+ fontFamily: theme.fonts.Regular,
209
+ }, isWeb() ? {
210
+ //@ts-ignore
211
+ outline: 'none',
212
+ } : {
213
+
214
+ }, fontStyles]}
215
+ />
216
+ {
217
+ alertVisible && <TextView
218
+ style={{
219
+ color: props.alertTextColor || theme.colors.critical,
220
+ padding: 0,
221
+ margin: 0,
222
+ paddingBottom: theme.dimens.space.sm,
223
+ fontSize: theme.dimens.font.sm,
224
+ }}
225
+ >{alerttext}</TextView>
226
+ }
227
+ </VBox>
228
+ {
229
+ props.icon != undefined &&
230
+ <TouchableOpacity onPress={(e) => {
231
+ if (!props.onIconPress && props.icon == 'close') {
232
+ onTextChange('')
233
+ }
234
+ props.onIconPress && props.onIconPress(e)
235
+ }}>
236
+ {
237
+ (IconComponent)
238
+ }
239
+ </TouchableOpacity>
240
+ }
241
+
242
+ </HBox >
243
+ )
244
+ }
@@ -0,0 +1,90 @@
1
+ import { HBox } from "./Box";
2
+ import * as React from "react";
3
+ import { useContext } from 'react'
4
+ import { ThemeContext } from "./ThemeContext";
5
+ import FontAwesome from '@expo/vector-icons/FontAwesome';
6
+ import { Platform, Pressable, Text, ViewProps } from "react-native";
7
+ import { TextView } from "./Text";
8
+
9
+
10
+
11
+ export function AlertMessage(props:
12
+ ViewProps & {
13
+ text: string,
14
+ type?: 'info' | 'success' | 'warning' | 'critical',
15
+ onDismiss?: Function
16
+
17
+ }) {
18
+ const theme = useContext(ThemeContext)
19
+ const type = props.type || 'info'
20
+ //@ts-ignore
21
+ let icon: 'info-circle' | 'warning' = {
22
+ 'info': 'info-circle',
23
+ 'success': 'check-circle',
24
+ 'warning': 'info-circle',
25
+ 'critical': 'warning'
26
+ }[type]
27
+
28
+ let color = {
29
+ 'info': '#2196F3',
30
+ 'success': '#4CAF50',
31
+ 'warning': '#FFC107',
32
+ 'critical': '#F44336'
33
+ }[type];
34
+
35
+ if (!props.text || props.text.length < 1) {
36
+ return undefined
37
+ }
38
+
39
+ return (
40
+ <HBox style={[{
41
+ flex: 1,
42
+ margin: theme.dimens.space.sm,
43
+ backgroundColor: theme.colors[type] || color,
44
+ alignItems: 'center',
45
+ justifyContent: 'space-between',
46
+ flexDirection: 'row',
47
+ paddingStart: theme.dimens.space.md,
48
+ paddingEnd: theme.dimens.space.md,
49
+ paddingBottom: props.text.length > 40 && Platform.OS == 'web' ?
50
+ theme.dimens.space.lg : theme.dimens.space.md,
51
+ paddingTop: props.text.length > 40 && Platform.OS == 'web' ?
52
+ theme.dimens.space.lg : theme.dimens.space.md,
53
+ borderRadius: theme.dimens.space.sm,
54
+ }, props.style]}>
55
+
56
+ <HBox style={{
57
+ flex: 0.93,
58
+ alignItems: 'center',
59
+ }}>
60
+ <FontAwesome
61
+ style={{
62
+ marginRight: theme.dimens.space.md
63
+ }}
64
+ name={icon}
65
+ size={theme.dimens.icon.md}
66
+ color={theme.colors.invert.text} />
67
+ <TextView style={{
68
+ flexShrink: 1,
69
+ padding: 0,
70
+ color: theme.colors.invert.text,
71
+ }}>{props.text}
72
+ </TextView>
73
+ </HBox>
74
+ {
75
+ props.onDismiss && <Pressable
76
+ style={{
77
+ flexDirection: 'row-reverse',
78
+ flex: 0.06,
79
+ }}
80
+ onPress={() => {
81
+ props.onDismiss && props.onDismiss()
82
+ }}>
83
+ <FontAwesome name={'close'}
84
+ size={theme.dimens.icon.md}
85
+ color={theme.colors.invert.text} />
86
+ </Pressable>
87
+ }
88
+ </HBox>
89
+ )
90
+ }
package/src/Modal.tsx ADDED
@@ -0,0 +1,306 @@
1
+ import * as React from 'react';
2
+ import { useContext, useEffect, useRef, useState } from 'react';
3
+ import { Animated, Easing, LayoutChangeEvent, Modal, Platform, Pressable, ScrollView, StyleProp, StyleSheet, TextStyle, TouchableHighlight, TouchableOpacity, View, ViewProps } from 'react-native';
4
+ import { Icon, IconProps } from './Image';
5
+ import { isDesktop } from './utils';
6
+ import { ThemeContext } from './ThemeContext';
7
+ import { Center, HBox, VBox } from './Box';
8
+ import { Subtitle, Title } from './Text';
9
+ import { useSafeAreaInsets } from 'react-native-safe-area-context';
10
+
11
+ export type BottomSheetProps = {
12
+ visible: boolean,
13
+ title?: string | React.Component,
14
+ cancellable?: boolean
15
+ children: any
16
+ onDismiss?: Function
17
+ }
18
+ export const BottomSheet = (props: BottomSheetProps) => {
19
+ const [modalVisible, setModalVisible] = useState(false);
20
+ const theme = useContext(ThemeContext)
21
+ let cancellable = props.cancellable != undefined ?
22
+ props.cancellable : true
23
+
24
+ useEffect(() => {
25
+ setModalVisible(props.visible)
26
+ }, [props.visible])
27
+
28
+ function cancel() {
29
+ setModalVisible(false)
30
+ if (props.onDismiss) {
31
+ props.onDismiss()
32
+ }
33
+ }
34
+
35
+ return (
36
+ <View style={styles.container}>
37
+ <Modal
38
+ onDismiss={() => {
39
+
40
+ }}
41
+ animationType="fade"
42
+ transparent={true}
43
+ visible={modalVisible}
44
+ onRequestClose={() => {
45
+ cancel()
46
+ }}
47
+ >
48
+ <TouchableHighlight
49
+ onPress={() => {
50
+ cancel()
51
+ }}
52
+ style={{
53
+ flex: 1,
54
+ backgroundColor: 'rgba(0, 0, 0, 0.5)'
55
+ }}>
56
+ <View />
57
+ </TouchableHighlight>
58
+
59
+ </Modal>
60
+
61
+ <Modal
62
+ onDismiss={() => {
63
+ cancel()
64
+ }}
65
+ style={{
66
+ flex: 1
67
+ }}
68
+ animationType="slide"
69
+ transparent={true}
70
+ visible={modalVisible}
71
+ onRequestClose={() => cancel()}
72
+ >
73
+ <View style={[styles.modalContainer, {
74
+ backgroundColor: theme.colors.forground
75
+ }]}>
76
+ <View style={[{
77
+ paddingTop: theme.dimens.space.md,
78
+ paddingStart: theme.dimens.space.lg,
79
+ paddingEnd: theme.dimens.space.lg,
80
+ }]}>
81
+ <HBox style={{
82
+ justifyContent: 'space-between',
83
+ width: '100%'
84
+ }}>
85
+ <View style={{ width: theme.dimens.icon.md }} />
86
+ {
87
+ typeof props.title == 'string' ? (
88
+ <Subtitle style={{
89
+ fontFamily: theme.fonts.Bold
90
+ }}>{props.title.toString()}</Subtitle>
91
+ ) : <>{props.title}</>
92
+ }
93
+ {
94
+ cancellable ? (<TouchableOpacity
95
+ style={{
96
+ padding: theme.dimens.space.sm
97
+ }}
98
+ onPress={() => {
99
+ cancel()
100
+ }}>
101
+ <Icon
102
+ color={theme.colors.caption} name="close" />
103
+ </TouchableOpacity>) : (
104
+ <View style={{ width: theme.dimens.icon.md }} />
105
+ )
106
+ }
107
+ </HBox>
108
+ <VBox style={{
109
+ width: '100%'
110
+ }}>
111
+ <ScrollView
112
+ nestedScrollEnabled={true}
113
+ showsVerticalScrollIndicator={false}
114
+ style={{
115
+ flex: 1,
116
+ maxHeight: 500,
117
+ }}>
118
+ {props.children}
119
+ </ScrollView>
120
+ </VBox>
121
+ </View>
122
+ </View>
123
+ </Modal>
124
+ </View>
125
+ );
126
+ };
127
+
128
+ const styles = StyleSheet.create({
129
+ container: {
130
+ flex: 1,
131
+ justifyContent: 'center',
132
+ alignItems: 'center',
133
+ },
134
+ modalContainer: isDesktop() ? {
135
+ marginLeft: '20%',
136
+ marginRight: '20%',
137
+ marginBottom: '1%',
138
+ width: '60%',
139
+ overflow: 'hidden',
140
+ borderBottomRightRadius: 20,
141
+ borderBottomLeftRadius: 20,
142
+ borderTopLeftRadius: 20,
143
+ borderTopRightRadius: 20,
144
+ position: 'absolute',
145
+ left: 0,
146
+ right: 0,
147
+ bottom: 0,
148
+ } : {
149
+ position: 'absolute',
150
+ left: 0,
151
+ right: 0,
152
+ bottom: 0,
153
+ borderTopLeftRadius: 20,
154
+ borderTopRightRadius: 20,
155
+ }
156
+ });
157
+
158
+
159
+
160
+
161
+ export function Expand(props: ViewProps & {
162
+ title?: string,
163
+ iconName?: string,
164
+ iconPosition?: 'left' | 'right',
165
+ titleStyle?: StyleProp<TextStyle>,
166
+ titleBackgroundColor?: string,
167
+ iconStyle?: IconProps,
168
+ initialExpand?: boolean,
169
+ duration?: number,
170
+ onChange?: (isExpanded: boolean) => void
171
+ }) {
172
+ const theme = useContext(ThemeContext);
173
+ const [expanded, setExpanded] = useState(props.initialExpand != undefined ? props.initialExpand : false);
174
+ const [contentHeight, setContentHeight] = useState<number | null>(null);
175
+ const spinValue = useRef(new Animated.Value(expanded ? 1 : 0)).current;
176
+ const [initDone, setInitDone] = useState(false)
177
+
178
+ const [fadeAnim] = useState(new Animated.Value(0));
179
+
180
+ useEffect(() => {
181
+ Animated.timing(spinValue, {
182
+ toValue: expanded ? 1 : 0,
183
+ duration: props.duration || 200,
184
+ easing: Easing.ease,
185
+ useNativeDriver: false
186
+ }).start();
187
+
188
+ Animated.timing(
189
+ fadeAnim,
190
+ {
191
+ toValue: expanded ? 0 : -20,
192
+ duration: props.duration || 200, // Animation duration in milliseconds
193
+ useNativeDriver: true // Add this line for better performance
194
+ }
195
+ ).start();
196
+ }, [expanded]);
197
+
198
+ useEffect(() => {
199
+ setInitDone(true)
200
+ }, [])
201
+
202
+ const toggleExpand = () => {
203
+ if (props.onChange) {
204
+ props.onChange(!expanded);
205
+ }
206
+ setExpanded(!expanded);
207
+ };
208
+ var onLayoutContent = (event: LayoutChangeEvent) => {
209
+ if (!contentHeight) {
210
+ setContentHeight(event.nativeEvent.layout.height);
211
+ }
212
+ };
213
+
214
+ if (Platform.OS == "web") {
215
+ const [initalLayoutWait, setinitalLayoutWait] = useState(false)
216
+ useEffect(() => {
217
+ setTimeout(() => {
218
+ setinitalLayoutWait(true)
219
+ }, 2000)
220
+ }, [])
221
+ onLayoutContent = (event: LayoutChangeEvent) => {
222
+ if (!contentHeight && initalLayoutWait) {
223
+ setContentHeight(event.nativeEvent.layout.height);
224
+ }
225
+ };
226
+ }
227
+
228
+ const spinInterpolated = spinValue.interpolate({
229
+ easing: Easing.ease,
230
+ inputRange: [0, 1],
231
+ outputRange: ['0deg', '90deg']
232
+ });
233
+
234
+ const ExpandIcon = () => {
235
+ const insets = useSafeAreaInsets()
236
+ return (
237
+ <Pressable
238
+ style={[{
239
+ borderRadius: theme.dimens.space.md,
240
+ padding: theme.dimens.space.md,
241
+ }, {
242
+ backgroundColor: props.titleBackgroundColor
243
+ }]}
244
+ onPress={toggleExpand}>
245
+ <Center style={props.iconPosition == 'right' ? {
246
+ paddingLeft: theme.dimens.space.sm,
247
+ justifyContent: 'space-between',
248
+ flexDirection: 'row-reverse',
249
+ } : {
250
+ justifyContent: 'flex-start',
251
+ flexDirection: 'row',
252
+ }}>
253
+ <Animated.View
254
+ style={{
255
+ transform: [{ rotate: spinInterpolated }],
256
+ }}>
257
+ <Icon
258
+ color={theme.colors.text}
259
+ {...props.iconStyle}
260
+ name={props.iconName || 'chevron-right'}
261
+ />
262
+ </Animated.View>
263
+ <HBox style={{
264
+ width: theme.dimens.space.sm
265
+ }} />
266
+ <Title style={[{
267
+ padding: 0,
268
+ paddingLeft: props.iconPosition == 'right' ? 0 :
269
+ theme.dimens.space.sm,
270
+ margin: 0,
271
+ fontSize: theme.dimens.font.md
272
+ }, props.titleStyle]}>{props.title}</Title>
273
+ </Center>
274
+ </Pressable >
275
+ )
276
+ }
277
+
278
+ return (
279
+ <VBox
280
+ style={[{
281
+ padding: 0,
282
+ borderRadius: theme.dimens.space.md,
283
+ margin: theme.dimens.space.sm
284
+ }, {
285
+ justifyContent: (props.iconPosition) == 'right' ? 'space-between' : 'flex-start'
286
+ }, props.style]}>
287
+ <ExpandIcon />
288
+ <Animated.View
289
+ style={{
290
+ zIndex: -1,
291
+ transform: [{ translateY: fadeAnim }],
292
+ height: expanded ? 'auto' : 0,
293
+ overflow: 'hidden'
294
+ }}
295
+ onLayout={onLayoutContent}>
296
+ {
297
+ (expanded || initDone) && (
298
+ <VBox onLayout={onLayoutContent} >
299
+ {props.children}
300
+ </VBox>
301
+ )
302
+ }
303
+ </Animated.View>
304
+ </VBox>
305
+ );
306
+ }