@fto-consult/expo-ui 8.46.3 → 8.47.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.
- package/bin/create-app/dependencies.js +2 -1
- package/package.json +1 -1
- package/src/components/Image/Cropper/ExpoImageManipulator.js +311 -0
- package/src/components/Image/Cropper/ImageCropOverlay.js +219 -0
- package/src/components/Image/Cropper/index.js +83 -0
- package/src/components/Image/Editor/index.js +0 -1
- package/src/components/Image/index.js +27 -18
- package/src/screens/Help/openLibraries.js +107 -99
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@fto-consult/expo-ui",
|
3
|
-
"version": "8.
|
3
|
+
"version": "8.47.0",
|
4
4
|
"description": "Bibliothèque de composants UI Expo,react-native",
|
5
5
|
"react-native-paper-doc": "https://github.com/callstack/react-native-paper/tree/main/docs/docs/guides",
|
6
6
|
"scripts": {
|
@@ -0,0 +1,311 @@
|
|
1
|
+
import React, { Component } from 'react'
|
2
|
+
import {
|
3
|
+
Dimensions,
|
4
|
+
Image,
|
5
|
+
ScrollView,
|
6
|
+
Modal,
|
7
|
+
View,
|
8
|
+
Text,
|
9
|
+
SafeAreaView,
|
10
|
+
TouchableOpacity,
|
11
|
+
} from 'react-native'
|
12
|
+
import * as ImageManipulator from 'expo-image-manipulator'
|
13
|
+
import PropTypes from 'prop-types'
|
14
|
+
import AutoHeightImage from '../../Avatar/AutoHeightImage'
|
15
|
+
import Icon from "$ecomponents/Icon";
|
16
|
+
import {isIphoneX } from 'react-native-iphone-x-helper'
|
17
|
+
import ImageCropOverlay from './ImageCropOverlay'
|
18
|
+
|
19
|
+
const { width, height } = Dimensions.get('window')
|
20
|
+
|
21
|
+
|
22
|
+
class ExpoImageManipulator extends Component {
|
23
|
+
constructor(props) {
|
24
|
+
super(props)
|
25
|
+
const { squareAspect } = this.props
|
26
|
+
this.state = {
|
27
|
+
cropMode: true,
|
28
|
+
processing: false,
|
29
|
+
zoomScale: 1,
|
30
|
+
squareAspect,
|
31
|
+
}
|
32
|
+
|
33
|
+
this.scrollOffset = 0
|
34
|
+
|
35
|
+
this.currentPos = {
|
36
|
+
left: 0,
|
37
|
+
top: 0,
|
38
|
+
}
|
39
|
+
|
40
|
+
this.currentSize = {
|
41
|
+
width: 0,
|
42
|
+
height: 0,
|
43
|
+
}
|
44
|
+
|
45
|
+
this.maxSizes = {
|
46
|
+
width: 0,
|
47
|
+
height: 0,
|
48
|
+
}
|
49
|
+
|
50
|
+
this.actualSize = {
|
51
|
+
width: 0,
|
52
|
+
height: 0
|
53
|
+
}
|
54
|
+
}
|
55
|
+
|
56
|
+
async componentDidMount() {
|
57
|
+
await this.onConvertImageToEditableSize()
|
58
|
+
}
|
59
|
+
|
60
|
+
async onConvertImageToEditableSize() {
|
61
|
+
const { photo: { uri: rawUri } } = this.props
|
62
|
+
const { uri, width, height } = await ImageManipulator.manipulateAsync(rawUri,
|
63
|
+
[
|
64
|
+
{
|
65
|
+
resize: {
|
66
|
+
width: 1080,
|
67
|
+
},
|
68
|
+
},
|
69
|
+
])
|
70
|
+
this.setState({
|
71
|
+
uri,
|
72
|
+
})
|
73
|
+
this.actualSize.width = width
|
74
|
+
this.actualSize.height = height
|
75
|
+
}
|
76
|
+
onCropImage = () => {
|
77
|
+
this.setState({ processing: true })
|
78
|
+
const { uri } = this.state
|
79
|
+
Image.getSize(uri, async (actualWidth, actualHeight) => {
|
80
|
+
let cropObj = this.getCropBounds(actualWidth, actualHeight);
|
81
|
+
if (cropObj.height > 0 && cropObj.width > 0) {
|
82
|
+
let uriToCrop = uri
|
83
|
+
const { uri: uriCroped, base64, width: croppedWidth, height: croppedHeight } = await this.crop(cropObj, uriToCrop)
|
84
|
+
|
85
|
+
this.actualSize.width = croppedWidth
|
86
|
+
this.actualSize.height = croppedHeight
|
87
|
+
|
88
|
+
this.setState({
|
89
|
+
uri: uriCroped, base64, cropMode: false, processing: false,
|
90
|
+
})
|
91
|
+
} else {
|
92
|
+
this.setState({cropMode: false, processing: false})
|
93
|
+
}
|
94
|
+
})
|
95
|
+
}
|
96
|
+
|
97
|
+
onRotateImage = async () => {
|
98
|
+
const { uri } = this.state
|
99
|
+
let uriToCrop = uri
|
100
|
+
Image.getSize(uri, async (width2, height2) => {
|
101
|
+
const { uri: rotUri, base64 } = await this.rotate(uriToCrop, width2, height2)
|
102
|
+
this.setState({ uri: rotUri, base64 })
|
103
|
+
})
|
104
|
+
}
|
105
|
+
|
106
|
+
onFlipImage = async (orientation) => {
|
107
|
+
const { uri } = this.state
|
108
|
+
let uriToCrop = uri
|
109
|
+
Image.getSize(uri, async (width2, height2) => {
|
110
|
+
const { uri: rotUri, base64 } = await this.filp(uriToCrop, orientation)
|
111
|
+
this.setState({ uri: rotUri, base64 })
|
112
|
+
})
|
113
|
+
}
|
114
|
+
|
115
|
+
onHandleScroll = (event) => {
|
116
|
+
this.scrollOffset = event.nativeEvent.contentOffset.y
|
117
|
+
}
|
118
|
+
|
119
|
+
getCropBounds = (actualWidth, actualHeight) => {
|
120
|
+
let imageRatio = actualHeight / actualWidth
|
121
|
+
var originalHeight = Dimensions.get('window').height - 64
|
122
|
+
if (isIphoneX()) {
|
123
|
+
originalHeight = Dimensions.get('window').height - 122
|
124
|
+
}
|
125
|
+
let renderedImageWidth = imageRatio < (originalHeight / width) ? width : originalHeight / imageRatio
|
126
|
+
let renderedImageHeight = imageRatio < (originalHeight / width) ? width * imageRatio : originalHeight
|
127
|
+
|
128
|
+
let renderedImageY = (originalHeight - renderedImageHeight) / 2.0
|
129
|
+
let renderedImageX = (width - renderedImageWidth) / 2.0
|
130
|
+
|
131
|
+
const renderImageObj = {
|
132
|
+
left: renderedImageX,
|
133
|
+
top: renderedImageY,
|
134
|
+
width: renderedImageWidth,
|
135
|
+
height: renderedImageHeight,
|
136
|
+
}
|
137
|
+
const cropOverlayObj = {
|
138
|
+
left: this.currentPos.left,
|
139
|
+
top: this.currentPos.top,
|
140
|
+
width: this.currentSize.width,
|
141
|
+
height: this.currentSize.height,
|
142
|
+
}
|
143
|
+
|
144
|
+
var intersectAreaObj = {}
|
145
|
+
|
146
|
+
let x = Math.max(renderImageObj.left, cropOverlayObj.left);
|
147
|
+
let num1 = Math.min(renderImageObj.left + renderImageObj.width, cropOverlayObj.left + cropOverlayObj.width);
|
148
|
+
let y = Math.max(renderImageObj.top, cropOverlayObj.top);
|
149
|
+
let num2 = Math.min(renderImageObj.top + renderImageObj.height, cropOverlayObj.top + cropOverlayObj.height);
|
150
|
+
if (num1 >= x && num2 >= y)
|
151
|
+
intersectAreaObj = {
|
152
|
+
originX: (x - renderedImageX) * (actualWidth / renderedImageWidth) ,
|
153
|
+
originY: (y - renderedImageY) * (actualWidth / renderedImageWidth),
|
154
|
+
width: (num1 - x) * (actualWidth / renderedImageWidth),
|
155
|
+
height: (num2 - y) * (actualWidth / renderedImageWidth)
|
156
|
+
}
|
157
|
+
else {
|
158
|
+
intersectAreaObj = {
|
159
|
+
originX: x - renderedImageX,
|
160
|
+
originY: y - renderedImageY,
|
161
|
+
width: 0,
|
162
|
+
height: 0
|
163
|
+
}
|
164
|
+
}
|
165
|
+
return intersectAreaObj
|
166
|
+
}
|
167
|
+
|
168
|
+
filp = async (uri, orientation) => {
|
169
|
+
const { saveOptions } = this.props
|
170
|
+
const manipResult = await ImageManipulator.manipulateAsync(uri, [{
|
171
|
+
flip: orientation == 'vertical' ? ImageManipulator.FlipType.Vertical : ImageManipulator.FlipType.Horizontal
|
172
|
+
}],
|
173
|
+
saveOptions
|
174
|
+
);
|
175
|
+
return manipResult;
|
176
|
+
};
|
177
|
+
|
178
|
+
rotate = async (uri, width2, height2) => {
|
179
|
+
const { saveOptions } = this.props
|
180
|
+
const manipResult = await ImageManipulator.manipulateAsync(uri, [{
|
181
|
+
rotate: -90,
|
182
|
+
}, {
|
183
|
+
resize: {
|
184
|
+
width: this.trueWidth || width2,
|
185
|
+
// height: this.trueHeight || height2,
|
186
|
+
},
|
187
|
+
}], saveOptions)
|
188
|
+
return manipResult
|
189
|
+
}
|
190
|
+
|
191
|
+
crop = async (cropObj, uri) => {
|
192
|
+
const { saveOptions } = this.props
|
193
|
+
if (cropObj.height > 0 && cropObj.width > 0) {
|
194
|
+
const manipResult = await ImageManipulator.manipulateAsync(
|
195
|
+
uri,
|
196
|
+
[{
|
197
|
+
crop: cropObj,
|
198
|
+
}],
|
199
|
+
saveOptions,
|
200
|
+
)
|
201
|
+
return manipResult
|
202
|
+
}
|
203
|
+
return {
|
204
|
+
uri: null,
|
205
|
+
base64: null,
|
206
|
+
}
|
207
|
+
};
|
208
|
+
|
209
|
+
calculateMaxSizes = (event) => {
|
210
|
+
let w1 = event.nativeEvent.layout.width || 100
|
211
|
+
let h1 = event.nativeEvent.layout.height || 100
|
212
|
+
if (this.state.squareAspect) {
|
213
|
+
if (w1 < h1) h1 = w1
|
214
|
+
else w1 = h1
|
215
|
+
}
|
216
|
+
this.maxSizes.width = w1
|
217
|
+
this.maxSizes.height = h1
|
218
|
+
};
|
219
|
+
|
220
|
+
// eslint-disable-next-line camelcase
|
221
|
+
async UNSAFE_componentWillReceiveProps() {
|
222
|
+
await this.onConvertImageToEditableSize()
|
223
|
+
}
|
224
|
+
|
225
|
+
zoomImage() {
|
226
|
+
// this.refs.imageScrollView.zoomScale = 5
|
227
|
+
// this.setState({width: width})
|
228
|
+
// this.setState({zoomScale: 5})
|
229
|
+
|
230
|
+
// this.setState(curHeight)
|
231
|
+
}
|
232
|
+
|
233
|
+
render() {
|
234
|
+
const {
|
235
|
+
uri,
|
236
|
+
base64,
|
237
|
+
cropMode,
|
238
|
+
processing,
|
239
|
+
zoomScale
|
240
|
+
} = this.state
|
241
|
+
|
242
|
+
let imageRatio = this.actualSize.height / this.actualSize.width
|
243
|
+
var originalHeight = Dimensions.get('window').height - 64
|
244
|
+
if (isIphoneX()) {
|
245
|
+
originalHeight = Dimensions.get('window').height - 122
|
246
|
+
}
|
247
|
+
|
248
|
+
let cropRatio = originalHeight / width
|
249
|
+
|
250
|
+
let cropWidth = imageRatio < cropRatio ? width : originalHeight / imageRatio
|
251
|
+
let cropHeight = imageRatio < cropRatio ? width * imageRatio : originalHeight
|
252
|
+
|
253
|
+
let cropInitialTop = (originalHeight - cropHeight) / 2.0
|
254
|
+
let cropInitialLeft = (width - cropWidth) / 2.0
|
255
|
+
|
256
|
+
|
257
|
+
if (this.currentSize.width == 0 && cropMode) {
|
258
|
+
this.currentSize.width = cropWidth;
|
259
|
+
this.currentSize.height = cropHeight;
|
260
|
+
|
261
|
+
this.currentPos.top = cropInitialTop;
|
262
|
+
this.currentPos.left = cropInitialLeft;
|
263
|
+
}
|
264
|
+
return <>
|
265
|
+
<AutoHeightImage
|
266
|
+
style={{ backgroundColor: 'black' }}
|
267
|
+
source={{ uri }}
|
268
|
+
resizeMode={imageRatio >= 1 ? "contain" : 'contain'}
|
269
|
+
width={width}
|
270
|
+
height={originalHeight}
|
271
|
+
onLayout={this.calculateMaxSizes}
|
272
|
+
/>
|
273
|
+
{!!cropMode && (
|
274
|
+
<ImageCropOverlay onLayoutChanged={(top, left, width, height) => {
|
275
|
+
this.currentSize.width = width;
|
276
|
+
this.currentSize.height = height;
|
277
|
+
this.currentPos.top = top
|
278
|
+
this.currentPos.left = left
|
279
|
+
}} initialWidth={cropWidth} initialHeight={cropHeight} initialTop={cropInitialTop} initialLeft={cropInitialLeft} minHeight={100} minWidth={100} />
|
280
|
+
)}
|
281
|
+
</>
|
282
|
+
}
|
283
|
+
}
|
284
|
+
|
285
|
+
export default ExpoImageManipulator
|
286
|
+
|
287
|
+
ExpoImageManipulator.defaultProps = {
|
288
|
+
onPictureChoosed: ({ uri, base64 }) => console.log('URI:', uri, base64),
|
289
|
+
btnTexts: {
|
290
|
+
crop: 'Crop',
|
291
|
+
rotate: 'Rotate',
|
292
|
+
done: 'Done',
|
293
|
+
processing: 'Processing',
|
294
|
+
},
|
295
|
+
dragVelocity: 100,
|
296
|
+
resizeVelocity: 50,
|
297
|
+
saveOptions: {
|
298
|
+
compress: 1,
|
299
|
+
format: ImageManipulator.SaveFormat.PNG,
|
300
|
+
base64: false,
|
301
|
+
},
|
302
|
+
}
|
303
|
+
|
304
|
+
ExpoImageManipulator.propTypes = {
|
305
|
+
onPictureChoosed: PropTypes.func,
|
306
|
+
btnTexts: PropTypes.object,
|
307
|
+
saveOptions: PropTypes.object,
|
308
|
+
photo: PropTypes.object.isRequired,
|
309
|
+
dragVelocity: PropTypes.number,
|
310
|
+
resizeVelocity: PropTypes.number,
|
311
|
+
}
|
@@ -0,0 +1,219 @@
|
|
1
|
+
import React, { Component } from 'react'
|
2
|
+
import { View, PanResponder, Dimensions } from 'react-native';
|
3
|
+
|
4
|
+
class ImageCropOverlay extends React.Component {
|
5
|
+
|
6
|
+
state = {
|
7
|
+
draggingTL: false,
|
8
|
+
draggingTM: false,
|
9
|
+
draggingTR: false,
|
10
|
+
draggingML: false,
|
11
|
+
draggingMM: false,
|
12
|
+
draggingMR: false,
|
13
|
+
draggingBL: false,
|
14
|
+
draggingBM: false,
|
15
|
+
draggingBR: false,
|
16
|
+
initialTop: this.props.initialTop,
|
17
|
+
initialLeft: this.props.initialLeft,
|
18
|
+
initialWidth: this.props.initialWidth,
|
19
|
+
initialHeight: this.props.initialHeight,
|
20
|
+
|
21
|
+
offsetTop: 0,
|
22
|
+
offsetLeft: 0,
|
23
|
+
}
|
24
|
+
|
25
|
+
panResponder = {}
|
26
|
+
|
27
|
+
UNSAFE_componentWillMount() {
|
28
|
+
this.panResponder = PanResponder.create({
|
29
|
+
onStartShouldSetPanResponder: this.handleStartShouldSetPanResponder,
|
30
|
+
onPanResponderGrant: this.handlePanResponderGrant,
|
31
|
+
onPanResponderMove: this.handlePanResponderMove,
|
32
|
+
onPanResponderRelease: this.handlePanResponderEnd,
|
33
|
+
onPanResponderTerminate: this.handlePanResponderEnd,
|
34
|
+
})
|
35
|
+
}
|
36
|
+
|
37
|
+
render() {
|
38
|
+
const { draggingTL, draggingTM, draggingTR, draggingML, draggingMM, draggingMR, draggingBL, draggingBM, draggingBR, initialTop, initialLeft, initialHeight, initialWidth, offsetTop, offsetLeft} = this.state
|
39
|
+
const style = {}
|
40
|
+
|
41
|
+
style.top = initialTop + ((draggingTL || draggingTM || draggingTR || draggingMM) ? offsetTop : 0)
|
42
|
+
style.left = initialLeft + ((draggingTL || draggingML || draggingBL || draggingMM) ? offsetLeft : 0)
|
43
|
+
style.width = initialWidth + ((draggingTL || draggingML || draggingBL) ? - offsetLeft : (draggingTM || draggingMM || draggingBM) ? 0 : offsetLeft)
|
44
|
+
style.height = initialHeight + ((draggingTL || draggingTM || draggingTR) ? - offsetTop : (draggingML || draggingMM || draggingMR) ? 0 : offsetTop)
|
45
|
+
|
46
|
+
if (style.width > this.props.initialWidth) {
|
47
|
+
style.width = this.props.initialWidth
|
48
|
+
}
|
49
|
+
if (style.width < this.props.minWidth) {
|
50
|
+
style.width = this.props.minWidth
|
51
|
+
}
|
52
|
+
if (style.height > this.props.initialHeight) {
|
53
|
+
style.height = this.props.initialHeight
|
54
|
+
}
|
55
|
+
if (style.height < this.props.minHeight) {
|
56
|
+
style.height = this.props.minHeight
|
57
|
+
}
|
58
|
+
return (
|
59
|
+
<View {...this.panResponder.panHandlers} style={[{flex: 1, justifyContent: 'center', alignItems: 'center', position: 'absolute', borderStyle: 'solid', borderWidth: 2, borderColor: '#a4a4a4', backgroundColor: 'rgb(0,0,0,0.5)'}, style]}>
|
60
|
+
<View style={{flexDirection: 'row', width: '100%', flex: 1/3, backgroundColor: 'transparent'}}>
|
61
|
+
<View style={{borderWidth: '#a4a4a4', borderWidth: 0, backgroundColor: draggingTL ? 'transparent' : 'transparent', flex: 1/3, height: '100%'}}></View>
|
62
|
+
<View style={{borderWidth: '#a4a4a4', borderWidth: 0, backgroundColor: draggingTM ? 'transparent' : 'transparent', flex: 1/3, height: '100%'}}></View>
|
63
|
+
<View style={{borderWidth: '#a4a4a4', borderWidth: 0, backgroundColor: draggingTR ? 'transparent' : 'transparent', flex: 1/3, height: '100%'}}></View>
|
64
|
+
</View>
|
65
|
+
<View style={{flexDirection: 'row', width: '100%', flex: 1/3, backgroundColor: 'transparent'}}>
|
66
|
+
<View style={{borderWidth: '#a4a4a4', borderWidth: 0, backgroundColor: draggingML ? 'transparent' : 'transparent', flex: 1/3, height: '100%'}}></View>
|
67
|
+
<View style={{borderWidth: '#a4a4a4', borderWidth: 0, backgroundColor: draggingMM ? 'transparent' : 'transparent', flex: 1/3, height: '100%'}}></View>
|
68
|
+
<View style={{borderWidth: '#a4a4a4', borderWidth: 0, backgroundColor: draggingMR ? 'transparent' : 'transparent', flex: 1/3, height: '100%'}}></View>
|
69
|
+
</View>
|
70
|
+
<View style={{flexDirection: 'row', width: '100%', flex: 1/3, backgroundColor: 'transparent'}}>
|
71
|
+
<View style={{borderWidth: '#a4a4a4', borderWidth: 0, backgroundColor: draggingBL ? 'transparent' : 'transparent', flex: 1/3, height: '100%'}}></View>
|
72
|
+
<View style={{borderWidth: '#a4a4a4', borderWidth: 0, backgroundColor: draggingBM ? 'transparent' : 'transparent', flex: 1/3, height: '100%'}}></View>
|
73
|
+
<View style={{borderWidth: '#a4a4a4', borderWidth: 0, backgroundColor: draggingBR ? 'transparent' : 'transparent', flex: 1/3, height: '100%'}}></View>
|
74
|
+
</View>
|
75
|
+
<View style={{top: 0, left: 0, width: '100%', height: '100%', position: 'absolute', backgroundColor: 'rgba(0, 0, 0, 0.5)'}}>
|
76
|
+
<View style={{flex: 1/3, flexDirection: 'row'}}>
|
77
|
+
<View style={{ flex: 3, borderRightWidth: 1, borderBottomWidth: 1, borderColor: '#c9c9c9', borderStyle: 'solid' }}>
|
78
|
+
<View style={{ position: 'absolute', left: 5, top: 5, borderLeftWidth: 2, borderTopWidth: 2, height: 48, width: 48, borderColor: '#f4f4f4', borderStyle: 'solid' }}/>
|
79
|
+
</View>
|
80
|
+
<View style={{ flex: 3, borderRightWidth: 1, borderBottomWidth: 1, borderColor: '#c9c9c9', borderStyle: 'solid' }}>
|
81
|
+
</View>
|
82
|
+
<View style={{ flex: 3, borderBottomWidth: 1, borderColor: '#c9c9c9', borderStyle: 'solid' }}>
|
83
|
+
<View style={{ position: 'absolute', right: 5, top: 5, borderRightWidth: 2, borderTopWidth: 2, height: 48, width: 48, borderColor: '#f4f4f4', borderStyle: 'solid' }}/>
|
84
|
+
</View>
|
85
|
+
</View>
|
86
|
+
<View style={{flex: 1/3, flexDirection: 'row'}}>
|
87
|
+
<View style={{ flex: 3, borderRightWidth: 1, borderBottomWidth: 1, borderColor: '#c9c9c9', borderStyle: 'solid' }}>
|
88
|
+
</View>
|
89
|
+
<View style={{ flex: 3, borderRightWidth: 1, borderBottomWidth: 1, borderColor: '#c9c9c9', borderStyle: 'solid' }}>
|
90
|
+
</View>
|
91
|
+
<View style={{ flex: 3, borderBottomWidth: 1, borderColor: '#c9c9c9', borderStyle: 'solid' }}>
|
92
|
+
</View>
|
93
|
+
</View>
|
94
|
+
<View style={{flex: 1/3, flexDirection: 'row'}}>
|
95
|
+
<View style={{ flex: 3, borderRightWidth: 1, borderColor: '#c9c9c9', borderStyle: 'solid', position: 'relative', }}>
|
96
|
+
<View style={{ position: 'absolute', left: 5, bottom: 5, borderLeftWidth: 2, borderBottomWidth: 2, height: 48, width: 48, borderColor: '#f4f4f4', borderStyle: 'solid' }}/>
|
97
|
+
</View>
|
98
|
+
<View style={{ flex: 3, borderRightWidth: 1, borderColor: '#c9c9c9', borderStyle: 'solid' }}>
|
99
|
+
</View>
|
100
|
+
<View style={{ flex: 3, position: 'relative' }}>
|
101
|
+
<View style={{ position: 'absolute', right: 5, bottom: 5, borderRightWidth: 2, borderBottomWidth: 2, height: 48, width: 48, borderColor: '#f4f4f4', borderStyle: 'solid' }}/>
|
102
|
+
</View>
|
103
|
+
</View>
|
104
|
+
</View>
|
105
|
+
</View>
|
106
|
+
)
|
107
|
+
}
|
108
|
+
|
109
|
+
getTappedItem(x, y) {
|
110
|
+
const { initialLeft, initialTop, initialWidth, initialHeight } = this.state
|
111
|
+
let xPos = parseInt((x - initialLeft) / (initialWidth / 3))
|
112
|
+
let yPos = parseInt((y - initialTop - 64) / (initialHeight / 3))
|
113
|
+
|
114
|
+
let index = yPos * 3 + xPos
|
115
|
+
if (index == 0) {
|
116
|
+
return 'tl';
|
117
|
+
} else if (index == 1) {
|
118
|
+
return 'tm';
|
119
|
+
} else if (index == 2) {
|
120
|
+
return 'tr';
|
121
|
+
} else if (index == 3) {
|
122
|
+
return 'ml';
|
123
|
+
} else if (index == 4) {
|
124
|
+
return 'mm';
|
125
|
+
} else if (index == 5) {
|
126
|
+
return 'mr';
|
127
|
+
} else if (index == 6) {
|
128
|
+
return 'bl';
|
129
|
+
} else if (index == 7) {
|
130
|
+
return 'bm';
|
131
|
+
} else if (index == 8) {
|
132
|
+
return 'br';
|
133
|
+
} else {
|
134
|
+
return '';
|
135
|
+
}
|
136
|
+
}
|
137
|
+
|
138
|
+
// Should we become active when the user presses down on the square?
|
139
|
+
handleStartShouldSetPanResponder = (event) => {
|
140
|
+
return true
|
141
|
+
}
|
142
|
+
|
143
|
+
// We were granted responder status! Let's update the UI
|
144
|
+
handlePanResponderGrant = (event) => {
|
145
|
+
// console.log(event.nativeEvent.locationX + ', ' + event.nativeEvent.locationY)
|
146
|
+
|
147
|
+
let selectedItem = this.getTappedItem(event.nativeEvent.pageX, event.nativeEvent.pageY)
|
148
|
+
if (selectedItem == 'tl') {
|
149
|
+
this.setState({draggingTL: true})
|
150
|
+
} else if (selectedItem == 'tm') {
|
151
|
+
this.setState({draggingTM: true})
|
152
|
+
} else if (selectedItem == 'tr') {
|
153
|
+
this.setState({draggingTR: true})
|
154
|
+
} else if (selectedItem == 'ml') {
|
155
|
+
this.setState({draggingML: true})
|
156
|
+
} else if (selectedItem == 'mm') {
|
157
|
+
this.setState({draggingMM: true})
|
158
|
+
} else if (selectedItem == 'mr') {
|
159
|
+
this.setState({draggingMR: true})
|
160
|
+
} else if (selectedItem == 'bl') {
|
161
|
+
this.setState({draggingBL: true})
|
162
|
+
} else if (selectedItem == 'bm') {
|
163
|
+
this.setState({draggingBM: true})
|
164
|
+
} else if (selectedItem == 'br') {
|
165
|
+
this.setState({draggingBR: true})
|
166
|
+
}
|
167
|
+
}
|
168
|
+
|
169
|
+
// Every time the touch/mouse moves
|
170
|
+
handlePanResponderMove = (e, gestureState) => {
|
171
|
+
// Keep track of how far we've moved in total (dx and dy)
|
172
|
+
this.setState({
|
173
|
+
offsetTop: gestureState.dy,
|
174
|
+
offsetLeft: gestureState.dx,
|
175
|
+
})
|
176
|
+
}
|
177
|
+
|
178
|
+
// When the touch/mouse is lifted
|
179
|
+
handlePanResponderEnd = (e, gestureState) => {
|
180
|
+
const {initialTop, initialLeft, initialWidth, initialHeight, draggingTL, draggingTM, draggingTR, draggingML, draggingMM, draggingMR, draggingBL, draggingBM, draggingBR } = this.state
|
181
|
+
|
182
|
+
const state = {
|
183
|
+
draggingTL: false,
|
184
|
+
draggingTM: false,
|
185
|
+
draggingTR: false,
|
186
|
+
draggingML: false,
|
187
|
+
draggingMM: false,
|
188
|
+
draggingMR: false,
|
189
|
+
draggingBL: false,
|
190
|
+
draggingBM: false,
|
191
|
+
draggingBR: false,
|
192
|
+
offsetTop: 0,
|
193
|
+
offsetLeft: 0,
|
194
|
+
}
|
195
|
+
|
196
|
+
state.initialTop = initialTop + ((draggingTL || draggingTM || draggingTR || draggingMM) ? gestureState.dy : 0)
|
197
|
+
state.initialLeft = initialLeft + ((draggingTL || draggingML || draggingBL || draggingMM) ? gestureState.dx : 0)
|
198
|
+
state.initialWidth = initialWidth + ((draggingTL || draggingML || draggingBL) ? - gestureState.dx : (draggingTM || draggingMM || draggingBM) ? 0 : gestureState.dx)
|
199
|
+
state.initialHeight = initialHeight + ((draggingTL || draggingTM || draggingTR) ? - gestureState.dy : (draggingML || draggingMM || draggingMR) ? 0 : gestureState.dy)
|
200
|
+
|
201
|
+
if (state.initialWidth > this.props.initialWidth) {
|
202
|
+
state.initialWidth = this.props.initialWidth
|
203
|
+
}
|
204
|
+
if (state.initialWidth < this.props.minWidth) {
|
205
|
+
state.initialWidth = this.props.minWidth
|
206
|
+
}
|
207
|
+
if (state.initialHeight > this.props.initialHeight) {
|
208
|
+
state.initialHeight = this.props.initialHeight
|
209
|
+
}
|
210
|
+
if (state.initialHeight < this.props.minHeight) {
|
211
|
+
state.initialHeight = this.props.minHeight
|
212
|
+
}
|
213
|
+
|
214
|
+
this.setState(state)
|
215
|
+
this.props.onLayoutChanged(state.initialTop, state.initialLeft, state.initialWidth, state.initialHeight)
|
216
|
+
}
|
217
|
+
}
|
218
|
+
|
219
|
+
export default ImageCropOverlay;
|
@@ -0,0 +1,83 @@
|
|
1
|
+
import React, { useState, useEffect,useMemo,useRef } from '$react';
|
2
|
+
import { View, StyleSheet,ImageBackground} from 'react-native';
|
3
|
+
import theme from "$theme";
|
4
|
+
import ActivityIndicator from "$ecomponents/ActivityIndicator";
|
5
|
+
import Label from "$ecomponents/Label";
|
6
|
+
import PropTypes from "prop-types";
|
7
|
+
import { isNonNullString,defaultStr,defaultObj } from '$cutils';
|
8
|
+
import Button from "$ecomponents/Button";
|
9
|
+
import Dialog from "$ecomponents/Dialog";
|
10
|
+
import ExpoImageManipulator from './ExpoImageManipulator';
|
11
|
+
import ImageCropOverlay from './ImageCropOverlay';
|
12
|
+
|
13
|
+
|
14
|
+
/***@see : https://docs.expo.dev/versions/latest/sdk/bar-code-scanner/ */
|
15
|
+
export default function ImageCropperComponent({src,testID,onCancel,dialogProps}) {
|
16
|
+
testID = defaultStr(testID,"RN_ImageCropperComponent");
|
17
|
+
const [visible,setVisible] = useState(true);
|
18
|
+
dialogProps = Object.assign({},dialogProps);
|
19
|
+
const prevVisible = React.usePrevious(visible);
|
20
|
+
const cancelRef = React.useRef(false);
|
21
|
+
const cancel = ()=>{
|
22
|
+
cancelRef.current = true;
|
23
|
+
setVisible(false);
|
24
|
+
}
|
25
|
+
useEffect(()=>{
|
26
|
+
if(prevVisible === visible) return;
|
27
|
+
if(prevVisible && !visible && cancelRef.current && typeof onCancel =="function"){
|
28
|
+
onCancel();
|
29
|
+
}
|
30
|
+
cancelRef.current = false;
|
31
|
+
},[visible]);
|
32
|
+
return <Dialog
|
33
|
+
fullPage
|
34
|
+
actions={[]}
|
35
|
+
title = {`Rogner l'image`}
|
36
|
+
{...dialogProps}
|
37
|
+
onBackActionPress={cancel}
|
38
|
+
visible = {visible}
|
39
|
+
>
|
40
|
+
<ImageBackground
|
41
|
+
resizeMode="contain"
|
42
|
+
style={styles.imageBackground}
|
43
|
+
source={{ uri : src }}
|
44
|
+
>
|
45
|
+
<ImageCropOverlay onLayoutChanged={(top, left, width, height) => {
|
46
|
+
console.log(top,lef,width,height," is to lefff")
|
47
|
+
}} initialWidth={50} initialHeight={50}
|
48
|
+
initialTop={0} initialLeft={0}
|
49
|
+
minHeight={100} minWidth={100}
|
50
|
+
/>
|
51
|
+
</ImageBackground>
|
52
|
+
</Dialog>;
|
53
|
+
}
|
54
|
+
const styles = StyleSheet.create({
|
55
|
+
center : {
|
56
|
+
justifyContent : "center",
|
57
|
+
alignItems : "center",
|
58
|
+
flexDirection : "column",
|
59
|
+
flex : 1,
|
60
|
+
},
|
61
|
+
row : {
|
62
|
+
flexDirection : "row",
|
63
|
+
justifyContent : "center",
|
64
|
+
alignItems : "center",
|
65
|
+
flexWrap :"wrap",
|
66
|
+
},
|
67
|
+
imageBackground : {
|
68
|
+
flex : 1,
|
69
|
+
width : "100%",
|
70
|
+
height : "100%",
|
71
|
+
justifyContent: 'center',
|
72
|
+
padding: 20, alignItems: 'center',
|
73
|
+
}
|
74
|
+
});
|
75
|
+
|
76
|
+
/***
|
77
|
+
@see : https://docs.expo.dev/versions/latest/sdk/camera-next
|
78
|
+
*/
|
79
|
+
ImageCropperComponent.propTypes = {
|
80
|
+
onScan : PropTypes.func,
|
81
|
+
onGrantAccess : PropTypes.func, //lorsque la permission est allouée
|
82
|
+
onDenyAccess : PropTypes.func, //lorsque la permission est refusée
|
83
|
+
}
|
@@ -6,7 +6,6 @@ import {defaultObj} from "$cutils";
|
|
6
6
|
|
7
7
|
const ImageEditorComponent = React.forwardRef((props,ref)=>{
|
8
8
|
let {source,uri,onSuccess,imageUri,lockAspectRatio,dialogProps,onDismiss,visible,imageProps,...rest} = props;
|
9
|
-
const isMounted = React.useIsMounted();
|
10
9
|
const [context] = React.useState({});
|
11
10
|
imageProps = defaultObj(imageProps);
|
12
11
|
dialogProps = defaultObj(dialogProps);
|
@@ -3,15 +3,15 @@ import Menu from "$ecomponents/Menu";
|
|
3
3
|
import Avatar from "$ecomponents/Avatar";
|
4
4
|
import {isDecimal,setQueryParams,isValidURL,defaultDecimal,defaultStr as defaultString,isDataURL,isPromise,defaultBool,isObj,isNonNullString} from "$cutils";
|
5
5
|
import notify from "$enotify";
|
6
|
-
let maxWidthDiff =
|
6
|
+
let maxWidthDiff = 100, maxHeightDiff = 100;
|
7
7
|
import {StyleSheet} from "react-native";
|
8
8
|
import React from "$react";
|
9
9
|
import PropTypes from "prop-types";
|
10
10
|
import {isMobileNative} from "$cplatform";
|
11
|
+
import {uniqid} from "$cutils";
|
11
12
|
//import Signature from "$ecomponents/Signature";
|
12
13
|
import Label from "$ecomponents/Label";
|
13
|
-
//import
|
14
|
-
import {Component as CameraComponent} from "$emedia/camera";
|
14
|
+
//import Cropper from "./Cropper";
|
15
15
|
|
16
16
|
import {pickImage,nonZeroMin,canTakePhoto,takePhoto} from "$emedia";
|
17
17
|
import addPhoto from "$eassets/add_photo.png";
|
@@ -48,11 +48,8 @@ export const getUri = (src,onlySting)=>{
|
|
48
48
|
|
49
49
|
export default function ImageComponent(props){
|
50
50
|
const [src,setSrc] = React.useState(defaultVal(props.src));
|
51
|
+
const [cropWindowProp,setCropWindowProp] = React.useState(null);
|
51
52
|
const prevSrc = React.usePrevious(src);
|
52
|
-
/*const [editorProps,setEditorProps] = React.useState({
|
53
|
-
visible : false,
|
54
|
-
options : {}
|
55
|
-
})*/
|
56
53
|
const [isDrawing,setIsDrawing] = React.useState(false);
|
57
54
|
let {disabled,onMount,defaultSource,editable,onUnmount,label,text,labelProps,readOnly,beforeRemove,
|
58
55
|
onChange,draw,round,drawText,drawLabel,rounded,defaultSrc,
|
@@ -114,8 +111,8 @@ export default function ImageComponent(props){
|
|
114
111
|
if(!imageWidth && !imageHeight){
|
115
112
|
imageWidth = imageHeight = rest.size;
|
116
113
|
}
|
117
|
-
let cropWidth = nonZeroMin(cropProps.width,width)
|
118
|
-
let cropHeight = nonZeroMin(cropProps.height,height);
|
114
|
+
let cropWidth = nonZeroMin(cropProps.width,imageWidth,width,size)
|
115
|
+
let cropHeight = nonZeroMin(cropProps.height,imageHeight,height,size);
|
119
116
|
if(!cropWidth) cropWidth = undefined;
|
120
117
|
if(!cropHeight) cropHeight = undefined;
|
121
118
|
|
@@ -125,24 +122,26 @@ export default function ImageComponent(props){
|
|
125
122
|
if(cropWidth || cropHeight){
|
126
123
|
canCrop = true;
|
127
124
|
if(cropWidth) opts.width = cropWidth;
|
128
|
-
|
125
|
+
if(cropHeight) opts.height = cropHeight;
|
129
126
|
}
|
130
127
|
opts.allowsEditing = canCrop;
|
131
|
-
return opts;
|
128
|
+
return {...cropProps,...opts};
|
132
129
|
}
|
133
130
|
const handlePickedImage = (image,opts)=>{
|
134
131
|
opts = defaultObj(opts);
|
135
132
|
if(!isDataURL(image.dataURL)){
|
136
133
|
return notify.error(`Le fichier sélectionné est une image non valide`);
|
137
134
|
}
|
138
|
-
let diffWidth = image.width - cropWidth - maxWidthDiff,
|
139
|
-
diffHeight = image.height - cropHeight - maxHeightDiff;
|
140
|
-
let canCrop = isMobileNative()? false : ((width && diffWidth > 0) || (height && diffHeight > 0)? true : false);
|
141
135
|
const imageSrc = pickUri ? image.uri : image.dataURL;
|
142
|
-
if(
|
143
|
-
|
144
|
-
|
145
|
-
|
136
|
+
if(imageSrc){
|
137
|
+
const diffWidth = image.width - cropWidth - maxWidthDiff,diffHeight = image.height - cropHeight - maxHeightDiff;
|
138
|
+
const canCrop = isMobileNative()? false : ((diffWidth > 0) || (diffHeight > 0)? true : false);
|
139
|
+
if(canCrop){
|
140
|
+
const cProps = getCropProps(opts);
|
141
|
+
return context.cropImage({...cProps,source:image,uri:image.dataURL,src:imageSrc}).then((props)=>{
|
142
|
+
setSrc(imageSrc)
|
143
|
+
});
|
144
|
+
}
|
146
145
|
}
|
147
146
|
pickedImageRef.current = image;
|
148
147
|
setSrc(imageSrc);
|
@@ -170,6 +169,11 @@ export default function ImageComponent(props){
|
|
170
169
|
},[src]),
|
171
170
|
cropImage : (props)=>{
|
172
171
|
return Promise.resolve(props);
|
172
|
+
if(!isMobileNative()){
|
173
|
+
return new Promise((resolve,reject)=>{
|
174
|
+
setCropWindowProp(props);
|
175
|
+
});
|
176
|
+
}
|
173
177
|
return new Promise((resolve,reject)=>{
|
174
178
|
console.log({...editorProps,visible:true,...props},"is editor props");
|
175
179
|
setEditorProps({...editorProps,visible:true,...props})
|
@@ -263,6 +267,11 @@ export default function ImageComponent(props){
|
|
263
267
|
const _label = withLabel !== false ? defaultString(label) : "";
|
264
268
|
const isDisabled = menuItems.length > 0 ? true : false;
|
265
269
|
return <View testID={testID+"_FagmentContainer"}>
|
270
|
+
{false && src && !isMobileNative() && isObj(cropWindowProp) && Object.size(cropWindowProp,true) ? <Cropper
|
271
|
+
src={src}
|
272
|
+
{...cropWindowProp}
|
273
|
+
key = {uniqid("crop-image")}
|
274
|
+
/> : null}
|
266
275
|
{!createSignatureOnly ? (<Menu
|
267
276
|
{...menuProps}
|
268
277
|
disabled = {isDisabled}
|
@@ -1,21 +1,22 @@
|
|
1
1
|
module.exports = {
|
2
2
|
"@fto-consult/expo-ui": {
|
3
|
-
"
|
4
|
-
"
|
5
|
-
"
|
6
|
-
"type": "git",
|
7
|
-
"url": "git+https://github.com/borispipo/expo-ui.git"
|
8
|
-
},
|
9
|
-
"homepage": "https://github.com/borispipo/expo-ui#readme"
|
3
|
+
"version": "8.46.3",
|
4
|
+
"url": "https://github.com/borispipo/expo-ui#readme",
|
5
|
+
"license": "ISC"
|
10
6
|
},
|
11
7
|
"@babel/plugin-proposal-export-namespace-from": {
|
12
8
|
"version": "7.18.9",
|
13
9
|
"url": "https://babel.dev/docs/en/next/babel-plugin-proposal-export-namespace-from",
|
14
10
|
"license": "MIT"
|
15
11
|
},
|
16
|
-
"@emotion/
|
17
|
-
"version": "11.11.
|
18
|
-
"url": "https://
|
12
|
+
"@emotion/native": {
|
13
|
+
"version": "11.11.0",
|
14
|
+
"url": "https://emotion.sh",
|
15
|
+
"license": "MIT"
|
16
|
+
},
|
17
|
+
"@expo/html-elements": {
|
18
|
+
"version": "0.9.1",
|
19
|
+
"url": "https://github.com/expo/expo/tree/main/packages/html-elements",
|
19
20
|
"license": "MIT"
|
20
21
|
},
|
21
22
|
"@expo/metro-config": {
|
@@ -23,28 +24,49 @@ module.exports = {
|
|
23
24
|
"url": "https://github.com/expo/expo.git",
|
24
25
|
"license": "MIT"
|
25
26
|
},
|
27
|
+
"@expo/vector-icons": {
|
28
|
+
"version": "14.0.0",
|
29
|
+
"url": "https://expo.github.io/vector-icons",
|
30
|
+
"license": "MIT"
|
31
|
+
},
|
26
32
|
"@expo/webpack-config": {
|
27
33
|
"version": "19.0.1",
|
28
34
|
"url": "https://github.com/expo/expo-webpack-integrations/tree/main/packages/webpack-config#readme",
|
29
35
|
"license": "MIT"
|
30
36
|
},
|
31
|
-
"@
|
32
|
-
"version": "
|
33
|
-
"url": "https://github.com/
|
37
|
+
"@pchmn/expo-material3-theme": {
|
38
|
+
"version": "1.3.2",
|
39
|
+
"url": "https://github.com/pchmn/expo-material3-theme#readme",
|
34
40
|
"license": "MIT"
|
35
41
|
},
|
36
|
-
"@
|
37
|
-
"version": "
|
38
|
-
"url": "https://github.com/
|
39
|
-
"license": "
|
42
|
+
"@react-native-community/netinfo": {
|
43
|
+
"version": "11.1.0",
|
44
|
+
"url": "https://github.com/react-native-netinfo/react-native-netinfo#readme",
|
45
|
+
"license": "MIT"
|
46
|
+
},
|
47
|
+
"@react-native/assets-registry": {
|
48
|
+
"version": "0.74.0",
|
49
|
+
"url": "https://github.com/facebook/react-native/tree/HEAD/packages/assets#readme",
|
50
|
+
"license": "MIT"
|
40
51
|
},
|
41
|
-
"@
|
42
|
-
"version": "1.
|
52
|
+
"@react-navigation/native": {
|
53
|
+
"version": "6.1.10",
|
54
|
+
"url": "https://reactnavigation.org",
|
43
55
|
"license": "MIT"
|
44
56
|
},
|
45
|
-
"
|
46
|
-
"version": "
|
47
|
-
"url": "https://
|
57
|
+
"@react-navigation/native-stack": {
|
58
|
+
"version": "6.9.18",
|
59
|
+
"url": "https://github.com/software-mansion/react-native-screens#readme",
|
60
|
+
"license": "MIT"
|
61
|
+
},
|
62
|
+
"@react-navigation/stack": {
|
63
|
+
"version": "6.3.21",
|
64
|
+
"url": "https://reactnavigation.org/docs/stack-navigator/",
|
65
|
+
"license": "MIT"
|
66
|
+
},
|
67
|
+
"@shopify/flash-list": {
|
68
|
+
"version": "1.6.3",
|
69
|
+
"url": "https://shopify.github.io/flash-list/",
|
48
70
|
"license": "MIT"
|
49
71
|
},
|
50
72
|
"babel-plugin-inline-dotenv": {
|
@@ -57,122 +79,108 @@ module.exports = {
|
|
57
79
|
"url": "https://github.com/tleunen/babel-plugin-module-resolver.git",
|
58
80
|
"license": "MIT"
|
59
81
|
},
|
60
|
-
"
|
61
|
-
"version": "
|
62
|
-
"url": "https://github.com/
|
82
|
+
"expo": {
|
83
|
+
"version": "50.0.11",
|
84
|
+
"url": "https://github.com/expo/expo/tree/main/packages/expo",
|
63
85
|
"license": "MIT"
|
64
86
|
},
|
65
|
-
"
|
66
|
-
"version": "
|
67
|
-
"url": "https://
|
87
|
+
"expo-camera": {
|
88
|
+
"version": "14.0.6",
|
89
|
+
"url": "https://docs.expo.dev/versions/latest/sdk/camera/",
|
68
90
|
"license": "MIT"
|
69
91
|
},
|
70
|
-
"
|
71
|
-
"version": "
|
72
|
-
"url": "https://
|
73
|
-
"license": "(MIT AND Apache-2.0)"
|
74
|
-
},
|
75
|
-
"html2canvas": {
|
76
|
-
"version": "1.4.1",
|
77
|
-
"url": "https://html2canvas.hertzen.com",
|
92
|
+
"expo-clipboard": {
|
93
|
+
"version": "5.0.1",
|
94
|
+
"url": "https://docs.expo.dev/versions/latest/sdk/clipboard",
|
78
95
|
"license": "MIT"
|
79
96
|
},
|
80
|
-
"
|
81
|
-
"version": "
|
82
|
-
"url": "
|
97
|
+
"expo-font": {
|
98
|
+
"version": "11.10.3",
|
99
|
+
"url": "https://docs.expo.dev/versions/latest/sdk/font/",
|
83
100
|
"license": "MIT"
|
84
101
|
},
|
85
|
-
"
|
86
|
-
"version": "
|
102
|
+
"expo-image-picker": {
|
103
|
+
"version": "14.7.1",
|
104
|
+
"url": "https://docs.expo.dev/versions/latest/sdk/imagepicker/",
|
87
105
|
"license": "MIT"
|
88
106
|
},
|
89
|
-
"
|
90
|
-
"version": "
|
91
|
-
"url": "https://
|
107
|
+
"expo-linking": {
|
108
|
+
"version": "6.2.2",
|
109
|
+
"url": "https://docs.expo.dev/versions/latest/sdk/linking",
|
92
110
|
"license": "MIT"
|
93
111
|
},
|
94
|
-
"
|
95
|
-
"version": "
|
96
|
-
"url": "https://
|
112
|
+
"expo-sharing": {
|
113
|
+
"version": "11.10.0",
|
114
|
+
"url": "https://docs.expo.dev/versions/latest/sdk/sharing/",
|
97
115
|
"license": "MIT"
|
98
116
|
},
|
99
|
-
"
|
100
|
-
"version": "
|
101
|
-
"url": "https://
|
117
|
+
"expo-sqlite": {
|
118
|
+
"version": "13.3.0",
|
119
|
+
"url": "https://docs.expo.dev/versions/latest/sdk/sqlite/",
|
102
120
|
"license": "MIT"
|
103
121
|
},
|
104
|
-
"
|
105
|
-
"version": "
|
106
|
-
"url": "https://
|
122
|
+
"expo-status-bar": {
|
123
|
+
"version": "1.11.1",
|
124
|
+
"url": "https://docs.expo.dev/versions/latest/sdk/status-bar/",
|
107
125
|
"license": "MIT"
|
108
126
|
},
|
109
|
-
"
|
110
|
-
"version": "
|
111
|
-
"url": "https://
|
112
|
-
"license": "GPL-3.0-or-later"
|
113
|
-
},
|
114
|
-
"react-native-iphone-x-helper": {
|
115
|
-
"version": "1.3.1",
|
116
|
-
"url": "https://github.com/ptelad/react-native-iphone-x-helper#readme",
|
127
|
+
"expo-system-ui": {
|
128
|
+
"version": "2.9.3",
|
129
|
+
"url": "https://docs.expo.dev/versions/latest/sdk/system-ui",
|
117
130
|
"license": "MIT"
|
118
131
|
},
|
119
|
-
"
|
120
|
-
"version": "
|
132
|
+
"expo-web-browser": {
|
133
|
+
"version": "12.8.2",
|
134
|
+
"url": "https://docs.expo.dev/versions/latest/sdk/webbrowser/",
|
121
135
|
"license": "MIT"
|
122
136
|
},
|
123
|
-
"react
|
124
|
-
"version": "
|
125
|
-
"url": "https://
|
137
|
+
"react": {
|
138
|
+
"version": "18.2.0",
|
139
|
+
"url": "https://reactjs.org/",
|
126
140
|
"license": "MIT"
|
127
141
|
},
|
128
|
-
"react-native
|
129
|
-
"version": "0.
|
130
|
-
"url": "https://
|
142
|
+
"react-native": {
|
143
|
+
"version": "0.73.4",
|
144
|
+
"url": "https://reactnative.dev/",
|
131
145
|
"license": "MIT"
|
132
146
|
},
|
133
|
-
"react-native-
|
134
|
-
"version": "
|
135
|
-
"url": "
|
147
|
+
"react-native-gesture-handler": {
|
148
|
+
"version": "2.14.1",
|
149
|
+
"url": "https://github.com/software-mansion/react-native-gesture-handler#readme",
|
136
150
|
"license": "MIT"
|
137
151
|
},
|
138
|
-
"react-
|
139
|
-
"version": "
|
140
|
-
"url": "https://virtuoso.dev/",
|
152
|
+
"react-native-get-random-values": {
|
153
|
+
"version": "1.8.0",
|
141
154
|
"license": "MIT"
|
142
155
|
},
|
143
|
-
"
|
144
|
-
"version": "
|
145
|
-
"url": "https://github.com/
|
156
|
+
"react-native-reanimated": {
|
157
|
+
"version": "3.6.2",
|
158
|
+
"url": "https://github.com/software-mansion/react-native-reanimated#readme",
|
146
159
|
"license": "MIT"
|
147
160
|
},
|
148
|
-
"
|
149
|
-
"version": "
|
150
|
-
"url": "
|
151
|
-
"license": "WTFPL OR ISC"
|
152
|
-
},
|
153
|
-
"sharp-cli": {
|
154
|
-
"version": "2.1.1",
|
155
|
-
"url": "https://github.com/vseventer/sharp-cli",
|
161
|
+
"react-native-safe-area-context": {
|
162
|
+
"version": "4.8.2",
|
163
|
+
"url": "https://github.com/th3rdwave/react-native-safe-area-context#readme",
|
156
164
|
"license": "MIT"
|
157
165
|
},
|
158
|
-
"
|
159
|
-
"version": "3.
|
160
|
-
"url": "https://github.com/
|
166
|
+
"react-native-screens": {
|
167
|
+
"version": "3.29.0",
|
168
|
+
"url": "https://github.com/software-mansion/react-native-screens#readme",
|
161
169
|
"license": "MIT"
|
162
170
|
},
|
163
|
-
"
|
164
|
-
"version": "
|
165
|
-
"url": "https://
|
171
|
+
"react-native-svg": {
|
172
|
+
"version": "14.1.0",
|
173
|
+
"url": "https://github.com/react-native-community/react-native-svg",
|
166
174
|
"license": "MIT"
|
167
175
|
},
|
168
|
-
"
|
169
|
-
"version": "
|
170
|
-
"url": "https://github.com/
|
176
|
+
"react-native-view-shot": {
|
177
|
+
"version": "3.8.0",
|
178
|
+
"url": "https://github.com/gre/react-native-view-shot",
|
171
179
|
"license": "MIT"
|
172
180
|
},
|
173
|
-
"
|
174
|
-
"version": "
|
175
|
-
"url": "https://
|
176
|
-
"license": "
|
181
|
+
"react-native-webview": {
|
182
|
+
"version": "13.6.4",
|
183
|
+
"url": "https://github.com/react-native-webview/react-native-webview#readme",
|
184
|
+
"license": "MIT"
|
177
185
|
}
|
178
186
|
};
|