react-native-country-select 0.3.4 ā 0.3.6
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/README.md +2 -0
- package/lib/components/BottomSheetModal/index.tsx +86 -61
- package/lib/components/FullscreenModal/index.tsx +47 -34
- package/lib/components/PopupModal/index.tsx +39 -26
- package/lib/components/styles.js +1 -3
- package/lib/constants/countries.json +4 -0
- package/package.json +11 -4
- package/scripts/install-react-native-safe-area-context.js +84 -0
package/README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* eslint-disable react-native/no-inline-styles */
|
|
2
|
-
import React, {useEffect, useMemo, useRef, useState} from 'react';
|
|
2
|
+
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
|
3
3
|
import {
|
|
4
4
|
Animated,
|
|
5
5
|
Modal,
|
|
@@ -10,9 +10,13 @@ import {
|
|
|
10
10
|
Keyboard,
|
|
11
11
|
NativeSyntheticEvent,
|
|
12
12
|
} from 'react-native';
|
|
13
|
+
import {
|
|
14
|
+
SafeAreaProvider,
|
|
15
|
+
SafeAreaView,
|
|
16
|
+
} from 'react-native-safe-area-context';
|
|
13
17
|
|
|
14
18
|
import parseHeight from '../../utils/parseHeight';
|
|
15
|
-
import {ICountrySelectStyle} from '../../interface';
|
|
19
|
+
import { ICountrySelectStyle } from '../../interface';
|
|
16
20
|
|
|
17
21
|
interface BottomSheetModalProps extends ModalProps {
|
|
18
22
|
visible: boolean;
|
|
@@ -67,19 +71,26 @@ export const BottomSheetModal: React.FC<BottomSheetModalProps> = ({
|
|
|
67
71
|
const dragStartYRef = useRef(0);
|
|
68
72
|
|
|
69
73
|
useEffect(() => {
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
+
const parsedMinHeight = parseHeight(
|
|
75
|
+
minBottomsheetHeight,
|
|
76
|
+
modalHeight
|
|
77
|
+
);
|
|
78
|
+
const parsedMaxHeight = parseHeight(
|
|
79
|
+
maxBottomsheetHeight,
|
|
80
|
+
modalHeight
|
|
81
|
+
);
|
|
74
82
|
const parsedInitialHeight = parseHeight(
|
|
75
83
|
initialBottomsheetHeight,
|
|
76
|
-
|
|
84
|
+
modalHeight
|
|
77
85
|
);
|
|
78
86
|
setBottomSheetSize({
|
|
79
|
-
minHeight:
|
|
80
|
-
|
|
87
|
+
minHeight:
|
|
88
|
+
parsedMinHeight || MIN_HEIGHT_PERCENTAGE * modalHeight,
|
|
89
|
+
maxHeight:
|
|
90
|
+
parsedMaxHeight || MAX_HEIGHT_PERCENTAGE * modalHeight,
|
|
81
91
|
initialHeight:
|
|
82
|
-
parsedInitialHeight ||
|
|
92
|
+
parsedInitialHeight ||
|
|
93
|
+
INITIAL_HEIGHT_PERCENTAGE * modalHeight,
|
|
83
94
|
});
|
|
84
95
|
}, [
|
|
85
96
|
modalHeight,
|
|
@@ -115,17 +126,17 @@ export const BottomSheetModal: React.FC<BottomSheetModalProps> = ({
|
|
|
115
126
|
onStartShouldSetPanResponder: () => true,
|
|
116
127
|
onMoveShouldSetPanResponder: (_evt, gestureState) =>
|
|
117
128
|
Math.abs(gestureState.dy) > 5,
|
|
118
|
-
onPanResponderGrant: e => {
|
|
129
|
+
onPanResponderGrant: (e) => {
|
|
119
130
|
dragStartYRef.current = e.nativeEvent.pageY;
|
|
120
131
|
sheetHeight.stopAnimation();
|
|
121
132
|
},
|
|
122
|
-
onPanResponderMove: e => {
|
|
133
|
+
onPanResponderMove: (e) => {
|
|
123
134
|
const currentY = e.nativeEvent.pageY;
|
|
124
135
|
const dy = currentY - dragStartYRef.current;
|
|
125
136
|
const proposedHeight = lastHeightRef.current - dy;
|
|
126
137
|
sheetHeight.setValue(proposedHeight);
|
|
127
138
|
},
|
|
128
|
-
onPanResponderRelease: e => {
|
|
139
|
+
onPanResponderRelease: (e) => {
|
|
129
140
|
const currentY = e.nativeEvent.pageY;
|
|
130
141
|
const dy = currentY - dragStartYRef.current;
|
|
131
142
|
const currentHeight = lastHeightRef.current - dy;
|
|
@@ -134,12 +145,14 @@ export const BottomSheetModal: React.FC<BottomSheetModalProps> = ({
|
|
|
134
145
|
toValue: 0,
|
|
135
146
|
duration: 200,
|
|
136
147
|
useNativeDriver: false,
|
|
137
|
-
}).start(() =>
|
|
148
|
+
}).start(() =>
|
|
149
|
+
onRequestClose({} as NativeSyntheticEvent<any>)
|
|
150
|
+
);
|
|
138
151
|
return;
|
|
139
152
|
}
|
|
140
153
|
const finalHeight = Math.min(
|
|
141
154
|
Math.max(currentHeight, bottomSheetSize.minHeight),
|
|
142
|
-
bottomSheetSize.maxHeight
|
|
155
|
+
bottomSheetSize.maxHeight
|
|
143
156
|
);
|
|
144
157
|
Animated.spring(sheetHeight, {
|
|
145
158
|
toValue: finalHeight,
|
|
@@ -159,7 +172,7 @@ export const BottomSheetModal: React.FC<BottomSheetModalProps> = ({
|
|
|
159
172
|
}).start();
|
|
160
173
|
},
|
|
161
174
|
}),
|
|
162
|
-
[bottomSheetSize, sheetHeight, onRequestClose]
|
|
175
|
+
[bottomSheetSize, sheetHeight, onRequestClose]
|
|
163
176
|
);
|
|
164
177
|
return (
|
|
165
178
|
<Modal
|
|
@@ -168,56 +181,68 @@ export const BottomSheetModal: React.FC<BottomSheetModalProps> = ({
|
|
|
168
181
|
animationType="slide"
|
|
169
182
|
onRequestClose={onRequestClose}
|
|
170
183
|
statusBarTranslucent={statusBarTranslucent}
|
|
171
|
-
{...props}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
style={
|
|
175
|
-
onLayout={e => setModalHeight(e.nativeEvent.layout.height)}>
|
|
176
|
-
<Pressable
|
|
177
|
-
testID="countrySelectBackdrop"
|
|
178
|
-
accessibilityRole="button"
|
|
179
|
-
accessibilityLabel={accessibilityLabelBackdrop}
|
|
180
|
-
accessibilityHint={accessibilityHintBackdrop}
|
|
181
|
-
disabled={disabledBackdropPress || removedBackdrop}
|
|
182
|
-
style={[
|
|
183
|
-
styles.backdrop,
|
|
184
|
-
countrySelectStyle?.backdrop,
|
|
185
|
-
removedBackdrop && {backgroundColor: 'transparent'},
|
|
186
|
-
]}
|
|
187
|
-
onPress={onBackdropPress || onRequestClose}
|
|
188
|
-
/>
|
|
189
|
-
<Animated.View
|
|
190
|
-
testID="countrySelectContent"
|
|
191
|
-
style={[
|
|
192
|
-
styles.content,
|
|
193
|
-
countrySelectStyle?.content,
|
|
194
|
-
{
|
|
195
|
-
height: sheetHeight,
|
|
196
|
-
},
|
|
197
|
-
]}>
|
|
184
|
+
{...props}
|
|
185
|
+
>
|
|
186
|
+
<SafeAreaProvider>
|
|
187
|
+
<SafeAreaView style={{ flex: 1 }}>
|
|
198
188
|
<View
|
|
199
|
-
|
|
200
|
-
style={[
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
189
|
+
testID="countrySelectContainer"
|
|
190
|
+
style={[styles.container, countrySelectStyle?.container]}
|
|
191
|
+
onLayout={(e) =>
|
|
192
|
+
setModalHeight(e.nativeEvent.layout.height)
|
|
193
|
+
}
|
|
194
|
+
>
|
|
195
|
+
<Pressable
|
|
196
|
+
testID="countrySelectBackdrop"
|
|
197
|
+
accessibilityRole="button"
|
|
198
|
+
accessibilityLabel={accessibilityLabelBackdrop}
|
|
199
|
+
accessibilityHint={accessibilityHintBackdrop}
|
|
200
|
+
disabled={disabledBackdropPress || removedBackdrop}
|
|
201
|
+
style={[
|
|
202
|
+
styles.backdrop,
|
|
203
|
+
countrySelectStyle?.backdrop,
|
|
204
|
+
removedBackdrop && { backgroundColor: 'transparent' },
|
|
205
|
+
]}
|
|
206
|
+
onPress={onBackdropPress || onRequestClose}
|
|
207
|
+
/>
|
|
208
|
+
<Animated.View
|
|
209
|
+
testID="countrySelectContent"
|
|
210
|
+
style={[
|
|
211
|
+
styles.content,
|
|
212
|
+
countrySelectStyle?.content,
|
|
213
|
+
{
|
|
214
|
+
height: sheetHeight,
|
|
215
|
+
},
|
|
216
|
+
]}
|
|
217
|
+
>
|
|
207
218
|
<View
|
|
219
|
+
{...panHandlers.panHandlers}
|
|
208
220
|
style={[
|
|
209
|
-
styles.
|
|
210
|
-
countrySelectStyle?.
|
|
221
|
+
styles.dragHandleContainer,
|
|
222
|
+
countrySelectStyle?.dragHandleContainer,
|
|
211
223
|
]}
|
|
212
|
-
|
|
213
|
-
|
|
224
|
+
>
|
|
225
|
+
{dragHandleIndicatorComponent ? (
|
|
226
|
+
dragHandleIndicatorComponent()
|
|
227
|
+
) : (
|
|
228
|
+
<View
|
|
229
|
+
style={[
|
|
230
|
+
styles.dragHandleIndicator,
|
|
231
|
+
countrySelectStyle?.dragHandleIndicator,
|
|
232
|
+
]}
|
|
233
|
+
/>
|
|
234
|
+
)}
|
|
235
|
+
</View>
|
|
236
|
+
{header}
|
|
237
|
+
<Animated.View
|
|
238
|
+
style={{ flex: 1, flexDirection: 'row' }}
|
|
239
|
+
>
|
|
240
|
+
{children}
|
|
241
|
+
</Animated.View>
|
|
242
|
+
</Animated.View>
|
|
214
243
|
</View>
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
{children}
|
|
218
|
-
</Animated.View>
|
|
219
|
-
</Animated.View>
|
|
220
|
-
</View>
|
|
244
|
+
</SafeAreaView>
|
|
245
|
+
</SafeAreaProvider>
|
|
221
246
|
</Modal>
|
|
222
247
|
);
|
|
223
248
|
};
|
|
@@ -7,8 +7,12 @@ import {
|
|
|
7
7
|
Pressable,
|
|
8
8
|
View,
|
|
9
9
|
} from 'react-native';
|
|
10
|
+
import {
|
|
11
|
+
SafeAreaProvider,
|
|
12
|
+
SafeAreaView,
|
|
13
|
+
} from 'react-native-safe-area-context';
|
|
10
14
|
|
|
11
|
-
import {ICountrySelectStyle} from '../../interface';
|
|
15
|
+
import { ICountrySelectStyle } from '../../interface';
|
|
12
16
|
|
|
13
17
|
interface FullscreenModalProps extends ModalProps {
|
|
14
18
|
visible: boolean;
|
|
@@ -47,39 +51,48 @@ export const FullscreenModal: React.FC<FullscreenModalProps> = ({
|
|
|
47
51
|
animationType="fade"
|
|
48
52
|
onRequestClose={onRequestClose}
|
|
49
53
|
statusBarTranslucent={statusBarTranslucent}
|
|
50
|
-
{...props}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
style={
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
54
|
+
{...props}
|
|
55
|
+
>
|
|
56
|
+
<SafeAreaProvider>
|
|
57
|
+
<SafeAreaView style={{ flex: 1 }}>
|
|
58
|
+
<View
|
|
59
|
+
testID="countrySelectContainer"
|
|
60
|
+
style={[
|
|
61
|
+
styles.container,
|
|
62
|
+
countrySelectStyle?.container,
|
|
63
|
+
{ flex: 1, width: '100%', height: '100%' },
|
|
64
|
+
]}
|
|
65
|
+
>
|
|
66
|
+
<Pressable
|
|
67
|
+
testID="countrySelectBackdrop"
|
|
68
|
+
accessibilityRole="button"
|
|
69
|
+
accessibilityLabel={accessibilityLabelBackdrop}
|
|
70
|
+
accessibilityHint={accessibilityHintBackdrop}
|
|
71
|
+
disabled={disabledBackdropPress || removedBackdrop}
|
|
72
|
+
style={[
|
|
73
|
+
styles.backdrop,
|
|
74
|
+
{ alignItems: 'center', justifyContent: 'center' },
|
|
75
|
+
countrySelectStyle?.backdrop,
|
|
76
|
+
removedBackdrop && { backgroundColor: 'transparent' },
|
|
77
|
+
]}
|
|
78
|
+
onPress={onBackdropPress || onRequestClose}
|
|
79
|
+
/>
|
|
80
|
+
<View
|
|
81
|
+
testID="countrySelectContent"
|
|
82
|
+
style={[
|
|
83
|
+
styles.content,
|
|
84
|
+
countrySelectStyle?.content,
|
|
85
|
+
{ borderRadius: 0, width: '100%', height: '100%' },
|
|
86
|
+
]}
|
|
87
|
+
>
|
|
88
|
+
{header}
|
|
89
|
+
<View style={{ flex: 1, flexDirection: 'row' }}>
|
|
90
|
+
{children}
|
|
91
|
+
</View>
|
|
92
|
+
</View>
|
|
93
|
+
</View>
|
|
94
|
+
</SafeAreaView>
|
|
95
|
+
</SafeAreaProvider>
|
|
83
96
|
</Modal>
|
|
84
97
|
);
|
|
85
98
|
};
|
|
@@ -7,8 +7,12 @@ import {
|
|
|
7
7
|
Pressable,
|
|
8
8
|
View,
|
|
9
9
|
} from 'react-native';
|
|
10
|
+
import {
|
|
11
|
+
SafeAreaProvider,
|
|
12
|
+
SafeAreaView,
|
|
13
|
+
} from 'react-native-safe-area-context';
|
|
10
14
|
|
|
11
|
-
import {ICountrySelectStyle} from '../../interface';
|
|
15
|
+
import { ICountrySelectStyle } from '../../interface';
|
|
12
16
|
|
|
13
17
|
interface PopupModalProps extends ModalProps {
|
|
14
18
|
visible: boolean;
|
|
@@ -47,31 +51,40 @@ export const PopupModal: React.FC<PopupModalProps> = ({
|
|
|
47
51
|
animationType="fade"
|
|
48
52
|
onRequestClose={onRequestClose}
|
|
49
53
|
statusBarTranslucent={statusBarTranslucent}
|
|
50
|
-
{...props}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
style={
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
54
|
+
{...props}
|
|
55
|
+
>
|
|
56
|
+
<SafeAreaProvider>
|
|
57
|
+
<SafeAreaView style={{ flex: 1 }}>
|
|
58
|
+
<View
|
|
59
|
+
testID="countrySelectContainer"
|
|
60
|
+
style={[styles.container, countrySelectStyle?.container]}
|
|
61
|
+
>
|
|
62
|
+
<Pressable
|
|
63
|
+
testID="countrySelectBackdrop"
|
|
64
|
+
accessibilityRole="button"
|
|
65
|
+
accessibilityLabel={accessibilityLabelBackdrop}
|
|
66
|
+
accessibilityHint={accessibilityHintBackdrop}
|
|
67
|
+
disabled={disabledBackdropPress || removedBackdrop}
|
|
68
|
+
style={[
|
|
69
|
+
styles.backdrop,
|
|
70
|
+
{ alignItems: 'center', justifyContent: 'center' },
|
|
71
|
+
countrySelectStyle?.backdrop,
|
|
72
|
+
removedBackdrop && { backgroundColor: 'transparent' },
|
|
73
|
+
]}
|
|
74
|
+
onPress={onBackdropPress || onRequestClose}
|
|
75
|
+
/>
|
|
76
|
+
<View
|
|
77
|
+
testID="countrySelectContent"
|
|
78
|
+
style={[styles.content, countrySelectStyle?.content]}
|
|
79
|
+
>
|
|
80
|
+
{header}
|
|
81
|
+
<View style={{ flex: 1, flexDirection: 'row' }}>
|
|
82
|
+
{children}
|
|
83
|
+
</View>
|
|
84
|
+
</View>
|
|
85
|
+
</View>
|
|
86
|
+
</SafeAreaView>
|
|
87
|
+
</SafeAreaProvider>
|
|
75
88
|
</Modal>
|
|
76
89
|
);
|
|
77
90
|
};
|
package/lib/components/styles.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {Platform,
|
|
1
|
+
import { Platform, StyleSheet } from 'react-native';
|
|
2
2
|
|
|
3
3
|
export const createStyles = (theme, modalType, isFullScreen) =>
|
|
4
4
|
StyleSheet.create({
|
|
@@ -23,7 +23,6 @@ export const createStyles = (theme, modalType, isFullScreen) =>
|
|
|
23
23
|
content:
|
|
24
24
|
modalType === 'popup' || isFullScreen
|
|
25
25
|
? {
|
|
26
|
-
marginTop: StatusBar.currentHeight,
|
|
27
26
|
width: '90%',
|
|
28
27
|
maxWidth: 600,
|
|
29
28
|
height: '60%',
|
|
@@ -32,7 +31,6 @@ export const createStyles = (theme, modalType, isFullScreen) =>
|
|
|
32
31
|
paddingVertical: 16,
|
|
33
32
|
}
|
|
34
33
|
: {
|
|
35
|
-
marginTop: StatusBar.currentHeight,
|
|
36
34
|
width: '100%',
|
|
37
35
|
borderTopLeftRadius: 20,
|
|
38
36
|
borderTopRightRadius: 20,
|
|
@@ -44941,6 +44941,10 @@
|
|
|
44941
44941
|
"official": "Amerika Birle\u015fik Devletleri K\u00fc\u00e7\u00fck D\u0131\u015f Adalar\u0131",
|
|
44942
44942
|
"common": "Amerika Birle\u015fik Devletleri K\u00fc\u00e7\u00fck D\u0131\u015f Adalar\u0131"
|
|
44943
44943
|
},
|
|
44944
|
+
"ukr": {
|
|
44945
|
+
"official": "\u041c\u0430\u043b\u0456\u0020\u0432\u0456\u0434\u0434\u0430\u043b\u0435\u043d\u0456\u0020\u043e\u0441\u0442\u0440\u043e\u0432\u0438\u0020\u0421\u0428\u0410",
|
|
44946
|
+
"common": "\u041c\u0430\u043b\u0456\u0020\u0432\u0456\u0434\u0434\u0430\u043b\u0435\u043d\u0456\u0020\u043e\u0441\u0442\u0440\u043e\u0432\u0438\u0020\u0421\u0428\u0410"
|
|
44947
|
+
},
|
|
44944
44948
|
"urd": {
|
|
44945
44949
|
"official": "\u0627\u0645\u0631\u06cc\u06a9\u06cc \u0686\u06be\u0648\u0679\u06d2 \u0628\u06cc\u0631\u0648\u0646\u06cc \u062c\u0632\u0627\u0626\u0631",
|
|
44946
44950
|
"common": "\u0627\u0645\u0631\u06cc\u06a9\u06cc \u0686\u06be\u0648\u0679\u06d2 \u0628\u06cc\u0631\u0648\u0646\u06cc \u062c\u0632\u0627\u0626\u0631"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-country-select",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.6",
|
|
4
4
|
"description": "š React Native country picker with flags, search, TypeScript, i18n, and offline support. Lightweight, customizable, and designed with a modern UI.",
|
|
5
5
|
"main": "lib/index.tsx",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
@@ -26,6 +26,9 @@
|
|
|
26
26
|
"react native country picker",
|
|
27
27
|
"react native country select"
|
|
28
28
|
],
|
|
29
|
+
"scripts": {
|
|
30
|
+
"postinstall": "node scripts/install-react-native-safe-area-context.js"
|
|
31
|
+
},
|
|
29
32
|
"repository": {
|
|
30
33
|
"type": "git",
|
|
31
34
|
"url": "git+https://github.com/AstrOOnauta/react-native-country-select.git"
|
|
@@ -39,10 +42,14 @@
|
|
|
39
42
|
"publishConfig": {
|
|
40
43
|
"registry": "https://registry.npmjs.org/"
|
|
41
44
|
},
|
|
42
|
-
"dependencies": {},
|
|
43
|
-
"devDependencies": {},
|
|
44
45
|
"peerDependencies": {
|
|
45
46
|
"react": "*",
|
|
46
|
-
"react-native": "*"
|
|
47
|
+
"react-native": "*",
|
|
48
|
+
"react-native-safe-area-context": ">=4.0.0"
|
|
49
|
+
},
|
|
50
|
+
"peerDependenciesMeta": {
|
|
51
|
+
"react-native-safe-area-context": {
|
|
52
|
+
"optional": false
|
|
53
|
+
}
|
|
47
54
|
}
|
|
48
55
|
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
const { execSync } = require('child_process');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
|
|
5
|
+
// Navigate to the project root directory (outside node_modules)
|
|
6
|
+
function findProjectRoot() {
|
|
7
|
+
let current = process.cwd();
|
|
8
|
+
|
|
9
|
+
// If we're inside node_modules, go up until we're out
|
|
10
|
+
while (current.includes('node_modules')) {
|
|
11
|
+
current = path.dirname(current);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Look for the project's package.json
|
|
15
|
+
while (!fs.existsSync(path.join(current, 'package.json'))) {
|
|
16
|
+
const next = path.dirname(current);
|
|
17
|
+
if (next === current) break;
|
|
18
|
+
current = next;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return current;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const root = findProjectRoot();
|
|
25
|
+
process.chdir(root);
|
|
26
|
+
|
|
27
|
+
// Check if react-native-safe-area-context is already installed
|
|
28
|
+
function isPackageInstalled() {
|
|
29
|
+
const packagePath = path.join(
|
|
30
|
+
root,
|
|
31
|
+
'node_modules',
|
|
32
|
+
'react-native-safe-area-context'
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
if (fs.existsSync(packagePath)) {
|
|
36
|
+
console.log(
|
|
37
|
+
'ā
react-native-safe-area-context is already installed!\n'
|
|
38
|
+
);
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// If already installed, do nothing
|
|
46
|
+
if (isPackageInstalled()) {
|
|
47
|
+
process.exit(0);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function detectPackageManager() {
|
|
51
|
+
if (fs.existsSync(path.join(root, 'bun.lockb'))) return 'bun';
|
|
52
|
+
if (fs.existsSync(path.join(root, 'pnpm-lock.yaml'))) return 'pnpm';
|
|
53
|
+
if (fs.existsSync(path.join(root, 'yarn.lock'))) return 'yarn';
|
|
54
|
+
return 'npm';
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const pm = detectPackageManager();
|
|
58
|
+
|
|
59
|
+
const commands = {
|
|
60
|
+
npm: 'npm install react-native-safe-area-context@latest --save',
|
|
61
|
+
yarn: 'yarn add react-native-safe-area-context@latest',
|
|
62
|
+
pnpm: 'pnpm add react-native-safe-area-context@latest',
|
|
63
|
+
bun: 'bun add react-native-safe-area-context@latest',
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const command = commands[pm] || commands.npm;
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
console.log(`\nš§ Detected package manager: ${pm}`);
|
|
70
|
+
console.log(`š¦ Installing react-native-safe-area-context using:`);
|
|
71
|
+
console.log(`ā”ļø ${command}\n`);
|
|
72
|
+
|
|
73
|
+
execSync(command, { stdio: 'inherit' });
|
|
74
|
+
|
|
75
|
+
console.log(
|
|
76
|
+
`\nā
react-native-safe-area-context installed successfully!\n`
|
|
77
|
+
);
|
|
78
|
+
} catch (err) {
|
|
79
|
+
console.error(
|
|
80
|
+
'\nā ļø Could not automatically install react-native-safe-area-context'
|
|
81
|
+
);
|
|
82
|
+
console.error('Please install it manually by running:\n');
|
|
83
|
+
console.error(` ${command}\n`);
|
|
84
|
+
}
|