react-native-expo-cropper 1.2.44 → 1.2.46

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/.babelrc CHANGED
@@ -1,7 +1,7 @@
1
- {
2
- "presets": [
3
- "@babel/preset-env",
4
- "@babel/preset-react"
5
- ]
6
- }
1
+ {
2
+ "presets": [
3
+ "@babel/preset-env",
4
+ "@babel/preset-react"
5
+ ]
6
+ }
7
7
 
package/App.js CHANGED
@@ -1,27 +1,27 @@
1
- import React from 'react';
2
- import { SafeAreaView, StatusBar, StyleSheet } from 'react-native';
3
- import ImageCropper from './src/ImageCropper';
4
-
5
- const App = () => {
6
- const handleCrop = (uri) => {
7
- console.log('Cropped Image URI:', uri);
8
- };
9
-
10
- return (
11
- <>
12
- <StatusBar backgroundColor="black" barStyle="light-content" />
13
- <SafeAreaView style={styles.container}>
14
- <ImageCropper onCrop={handleCrop} />
15
- </SafeAreaView>
16
- </>
17
- );
18
- };
19
-
20
- const styles = StyleSheet.create({
21
- container: {
22
- flex: 1,
23
- backgroundColor: 'black',
24
- },
25
- });
26
-
27
- export default App;
1
+ import React from 'react';
2
+ import { SafeAreaView, StatusBar, StyleSheet } from 'react-native';
3
+ import ImageCropper from './src/ImageCropper';
4
+
5
+ const App = () => {
6
+ const handleCrop = (uri) => {
7
+ console.log('Cropped Image URI:', uri);
8
+ };
9
+
10
+ return (
11
+ <>
12
+ <StatusBar backgroundColor="black" barStyle="light-content" />
13
+ <SafeAreaView style={styles.container}>
14
+ <ImageCropper onCrop={handleCrop} />
15
+ </SafeAreaView>
16
+ </>
17
+ );
18
+ };
19
+
20
+ const styles = StyleSheet.create({
21
+ container: {
22
+ flex: 1,
23
+ backgroundColor: 'black',
24
+ },
25
+ });
26
+
27
+ export default App;
package/app.json CHANGED
@@ -1,3 +1,3 @@
1
- {
2
- "expo": {}
1
+ {
2
+ "expo": {}
3
3
  }
package/babel.config.js CHANGED
@@ -1,6 +1,6 @@
1
- module.exports = {
2
- presets: [
3
- '@babel/preset-env',
4
- '@babel/preset-react'
5
- ]
1
+ module.exports = {
2
+ presets: [
3
+ '@babel/preset-env',
4
+ '@babel/preset-react'
5
+ ]
6
6
  };
@@ -25,11 +25,14 @@ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r)
25
25
  function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
26
26
  function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
27
27
  function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
28
- var _Dimensions$get = _reactNative.Dimensions.get('window'),
29
- width = _Dimensions$get.width;
28
+ // Max width for camera preview on large screens (tablets) so it doesn't stretch full width
29
+ var CAMERA_PREVIEW_MAX_WIDTH = 500;
30
30
  function CustomCamera(_ref) {
31
31
  var onPhotoCaptured = _ref.onPhotoCaptured,
32
32
  onFrameCalculated = _ref.onFrameCalculated;
33
+ var _useWindowDimensions = (0, _reactNative.useWindowDimensions)(),
34
+ windowWidth = _useWindowDimensions.width;
35
+ var cameraPreviewWidth = Math.min(windowWidth, CAMERA_PREVIEW_MAX_WIDTH);
33
36
  var _useState = (0, _react.useState)(false),
34
37
  _useState2 = _slicedToArray(_useState, 2),
35
38
  isReady = _useState2[0],
@@ -249,7 +252,9 @@ function CustomCamera(_ref) {
249
252
  return /*#__PURE__*/_react["default"].createElement(_reactNative.SafeAreaView, {
250
253
  style: styles.outerContainer
251
254
  }, /*#__PURE__*/_react["default"].createElement(_reactNative.View, {
252
- style: styles.cameraWrapper,
255
+ style: [styles.cameraWrapper, {
256
+ width: cameraPreviewWidth
257
+ }],
253
258
  ref: cameraWrapperRef,
254
259
  onLayout: function onLayout(e) {
255
260
  var layout = e.nativeEvent.layout;
@@ -310,7 +315,6 @@ var styles = _reactNative.StyleSheet.create({
310
315
  alignItems: 'center'
311
316
  },
312
317
  cameraWrapper: {
313
- width: width,
314
318
  aspectRatio: 9 / 16,
315
319
  borderRadius: 30,
316
320
  overflow: 'hidden',
@@ -36,6 +36,9 @@ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length)
36
36
  function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
37
37
  function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
38
38
  var PRIMARY_GREEN = '#198754';
39
+
40
+ // Max width for crop preview on large screens (tablets) - must match CustomCamera.js
41
+ var CAMERA_PREVIEW_MAX_WIDTH = 500;
39
42
  var ImageCropper = function ImageCropper(_ref) {
40
43
  var _cameraFrameData$curr3;
41
44
  var onConfirm = _ref.onConfirm,
@@ -43,6 +46,9 @@ var ImageCropper = function ImageCropper(_ref) {
43
46
  initialImage = _ref.initialImage,
44
47
  addheight = _ref.addheight,
45
48
  rotationLabel = _ref.rotationLabel;
49
+ var _useWindowDimensions = (0, _reactNative.useWindowDimensions)(),
50
+ windowWidth = _useWindowDimensions.width;
51
+ var cameraPreviewWidth = Math.min(windowWidth, CAMERA_PREVIEW_MAX_WIDTH);
46
52
  var _useState = (0, _react.useState)(null),
47
53
  _useState2 = _slicedToArray(_useState, 2),
48
54
  image = _useState2[0],
@@ -1299,6 +1305,31 @@ var ImageCropper = function ImageCropper(_ref) {
1299
1305
  style: _ImageCropperStyles["default"].container
1300
1306
  }, showCustomCamera ? /*#__PURE__*/_react["default"].createElement(_CustomCamera["default"], {
1301
1307
  onPhotoCaptured: function onPhotoCaptured(uri, frameData) {
1308
+ // ✅ Reset refs for new image so second (and later) photos don't use first image's layout (fixes white screen on some devices)
1309
+ originalImageDimensions.current = {
1310
+ width: 0,
1311
+ height: 0
1312
+ };
1313
+ imageDisplayRect.current = {
1314
+ x: 0,
1315
+ y: 0,
1316
+ width: 0,
1317
+ height: 0
1318
+ };
1319
+ displayedImageLayout.current = {
1320
+ x: 0,
1321
+ y: 0,
1322
+ width: 0,
1323
+ height: 0
1324
+ };
1325
+ imageMeasure.current = {
1326
+ x: 0,
1327
+ y: 0,
1328
+ width: 0,
1329
+ height: 0
1330
+ };
1331
+ sourceImageUri.current = uri;
1332
+
1302
1333
  // ✅ CRITICAL FIX: Store green frame coordinates for coordinate conversion
1303
1334
  if (frameData && frameData.greenFrame) {
1304
1335
  cameraFrameData.current = {
@@ -1322,17 +1353,9 @@ var ImageCropper = function ImageCropper(_ref) {
1322
1353
  return setShowCustomCamera(false);
1323
1354
  }
1324
1355
  }) : /*#__PURE__*/_react["default"].createElement(_react["default"].Fragment, null, image && /*#__PURE__*/_react["default"].createElement(_reactNative.View, {
1325
- style: {
1326
- width: _reactNative.Dimensions.get('window').width,
1327
- aspectRatio: 9 / 16,
1328
- borderRadius: 30,
1329
- overflow: 'hidden',
1330
- alignItems: 'center',
1331
- justifyContent: 'center',
1332
- position: 'relative',
1333
- backgroundColor: 'black',
1334
- marginBottom: 0 // ✅ Les boutons sont maintenant en position absolue en bas
1335
- },
1356
+ style: [_ImageCropperStyles["default"].commonWrapper, {
1357
+ width: cameraPreviewWidth
1358
+ }],
1336
1359
  ref: commonWrapperRef,
1337
1360
  onLayout: onCommonWrapperLayout
1338
1361
  }, /*#__PURE__*/_react["default"].createElement(_reactNative.View, {
@@ -1402,6 +1425,7 @@ var ImageCropper = function ImageCropper(_ref) {
1402
1425
  return hasMovement;
1403
1426
  }
1404
1427
  }, /*#__PURE__*/_react["default"].createElement(_reactNative.Image, {
1428
+ key: image,
1405
1429
  source: {
1406
1430
  uri: image
1407
1431
  },
@@ -1413,8 +1437,8 @@ var ImageCropper = function ImageCropper(_ref) {
1413
1437
  pointerEvents: "none"
1414
1438
  }, function () {
1415
1439
  // ✅ Use wrapper dimensions for SVG path (wrapper coordinates)
1416
- var wrapperWidth = commonWrapperLayout.current.width || _reactNative.Dimensions.get('window').width;
1417
- var wrapperHeight = commonWrapperLayout.current.height || _reactNative.Dimensions.get('window').width * 16 / 9;
1440
+ var wrapperWidth = commonWrapperLayout.current.width || cameraPreviewWidth;
1441
+ var wrapperHeight = commonWrapperLayout.current.height || cameraPreviewWidth * 16 / 9;
1418
1442
  return /*#__PURE__*/_react["default"].createElement(_react["default"].Fragment, null, /*#__PURE__*/_react["default"].createElement(_reactNativeSvg.Path, {
1419
1443
  d: "M 0 0 H ".concat(wrapperWidth, " V ").concat(wrapperHeight, " H 0 Z ").concat(createPath()),
1420
1444
  fill: showResult ? 'white' : 'rgba(0, 0, 0, 0.8)',
@@ -21,6 +21,17 @@ var styles = _reactNative.StyleSheet.create({
21
21
  // ✅ Start from top, allow content to flow down
22
22
  backgroundColor: DEEP_BLACK
23
23
  },
24
+ // Wrapper for crop preview (9/16, same as CustomCamera); width set dynamically for tablet
25
+ commonWrapper: {
26
+ aspectRatio: 9 / 16,
27
+ borderRadius: 30,
28
+ overflow: 'hidden',
29
+ alignItems: 'center',
30
+ justifyContent: 'center',
31
+ position: 'relative',
32
+ backgroundColor: 'black',
33
+ marginBottom: 0
34
+ },
24
35
  buttonContainer: {
25
36
  position: 'absolute',
26
37
  bottom: 50,
@@ -14,18 +14,18 @@ function _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present,
14
14
  function _regeneratorDefine2(e, r, n, t) { var i = Object.defineProperty; try { i({}, "", {}); } catch (e) { i = 0; } _regeneratorDefine2 = function _regeneratorDefine(e, r, n, t) { function o(r, n) { _regeneratorDefine2(e, r, function (e) { return this._invoke(r, n, e); }); } r ? i ? i(e, r, { value: n, enumerable: !t, configurable: !t, writable: !t }) : e[r] = n : (o("next", 0), o("throw", 1), o("return", 2)); }, _regeneratorDefine2(e, r, n, t); }
15
15
  function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); }
16
16
  function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; }
17
- /**
18
- * ImageMaskProcessor - Module pour appliquer un masque alpha à une image
19
- *
20
- * NOTE: Ce module est actuellement désactivé par défaut (enableMask = false)
21
- * car captureRef peut se bloquer sur certaines configurations.
22
- *
23
- * Pour activer le masque, mettre enableMask = true dans ImageCropper.js
24
- * ATTENTION: Peut causer des blocages sur certaines configurations
17
+ /**
18
+ * ImageMaskProcessor - Module pour appliquer un masque alpha à une image
19
+ *
20
+ * NOTE: Ce module est actuellement désactivé par défaut (enableMask = false)
21
+ * car captureRef peut se bloquer sur certaines configurations.
22
+ *
23
+ * Pour activer le masque, mettre enableMask = true dans ImageCropper.js
24
+ * ATTENTION: Peut causer des blocages sur certaines configurations
25
25
  */
26
26
 
27
- /**
28
- * Crée un chemin SVG à partir de points
27
+ /**
28
+ * Crée un chemin SVG à partir de points
29
29
  */
30
30
  var createPathFromPoints = function createPathFromPoints(points) {
31
31
  if (points.length < 3) return '';
@@ -36,9 +36,9 @@ var createPathFromPoints = function createPathFromPoints(points) {
36
36
  return path + 'Z';
37
37
  };
38
38
 
39
- /**
40
- * Composant MaskView - Vue avec image et masque SVG
41
- * Utilisé pour capturer l'image avec le masque appliqué
39
+ /**
40
+ * Composant MaskView - Vue avec image et masque SVG
41
+ * Utilisé pour capturer l'image avec le masque appliqué
42
42
  */
43
43
  var MaskView = exports.MaskView = /*#__PURE__*/(0, _react.forwardRef)(function (_ref, ref) {
44
44
  var imageUri = _ref.imageUri,
@@ -78,18 +78,18 @@ var MaskView = exports.MaskView = /*#__PURE__*/(0, _react.forwardRef)(function (
78
78
  });
79
79
  MaskView.displayName = 'MaskView';
80
80
 
81
- /**
82
- * Applique un masque alpha à une image en utilisant captureRef
83
- *
84
- * @param {string} imageUri - URI de l'image à masquer
85
- * @param {Array} points - Points définissant le masque (coordonnées normalisées)
86
- * @param {number} width - Largeur de l'image
87
- * @param {number} height - Hauteur de l'image
88
- * @param {React.Ref} maskViewRef - Référence à la vue MaskView
89
- * @returns {Promise<string>} URI de l'image masquée
90
- *
91
- * NOTE: Cette fonction utilise captureRef qui peut se bloquer.
92
- * Utiliser uniquement si enableMask = true dans ImageCropper.js
81
+ /**
82
+ * Applique un masque alpha à une image en utilisant captureRef
83
+ *
84
+ * @param {string} imageUri - URI de l'image à masquer
85
+ * @param {Array} points - Points définissant le masque (coordonnées normalisées)
86
+ * @param {number} width - Largeur de l'image
87
+ * @param {number} height - Hauteur de l'image
88
+ * @param {React.Ref} maskViewRef - Référence à la vue MaskView
89
+ * @returns {Promise<string>} URI de l'image masquée
90
+ *
91
+ * NOTE: Cette fonction utilise captureRef qui peut se bloquer.
92
+ * Utiliser uniquement si enableMask = true dans ImageCropper.js
93
93
  */
94
94
  // width/height = dimensions de sortie souhaitées EN PIXELS (pas dp)
95
95
  var applyMaskToImage = exports.applyMaskToImage = /*#__PURE__*/function () {
@@ -11,13 +11,13 @@ function _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present,
11
11
  function _regeneratorDefine2(e, r, n, t) { var i = Object.defineProperty; try { i({}, "", {}); } catch (e) { i = 0; } _regeneratorDefine2 = function _regeneratorDefine(e, r, n, t) { function o(r, n) { _regeneratorDefine2(e, r, function (e) { return this._invoke(r, n, e); }); } r ? i ? i(e, r, { value: n, enumerable: !t, configurable: !t, writable: !t }) : e[r] = n : (o("next", 0), o("throw", 1), o("return", 2)); }, _regeneratorDefine2(e, r, n, t); }
12
12
  function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); }
13
13
  function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; }
14
- /**
15
- * Retourne l'image originale sans redimensionnement pour préserver la qualité maximale.
16
- * Le redimensionnement destructif a été supprimé pour éviter la perte de résolution.
17
- *
18
- * @param {string} uri - URI de l'image originale
19
- * @param {number} addheight - Paramètre conservé pour compatibilité (non utilisé)
20
- * @returns {Promise<string>} URI de l'image originale (sans modification)
14
+ /**
15
+ * Retourne l'image originale sans redimensionnement pour préserver la qualité maximale.
16
+ * Le redimensionnement destructif a été supprimé pour éviter la perte de résolution.
17
+ *
18
+ * @param {string} uri - URI de l'image originale
19
+ * @param {number} addheight - Paramètre conservé pour compatibilité (non utilisé)
20
+ * @returns {Promise<string>} URI de l'image originale (sans modification)
21
21
  */
22
22
  var enhanceImage = exports.enhanceImage = /*#__PURE__*/function () {
23
23
  var _ref = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee(uri, addheight) {
package/index.js CHANGED
@@ -1,6 +1,6 @@
1
- import { registerRootComponent } from 'expo';
2
- import App from './App';
3
-
4
- registerRootComponent(App);
5
-
6
-
1
+ import { registerRootComponent } from 'expo';
2
+ import App from './App';
3
+
4
+ registerRootComponent(App);
5
+
6
+
package/package.json CHANGED
@@ -1,59 +1,59 @@
1
- {
2
- "name": "react-native-expo-cropper",
3
- "version": "1.2.44",
4
- "description": "Recadrage polygonal d'images.",
5
- "main": "index.js",
6
- "author": "PCS AGRI",
7
- "license": "MIT",
8
- "keywords": [
9
- "react-native",
10
- "expo",
11
- "image-cropper",
12
- "camera",
13
- "polygonal",
14
- "image",
15
- "react-native-component"
16
- ],
17
- "peerDependencies": {
18
- "expo": "54.0.0",
19
- "react": "19.1.0",
20
- "react-native": "0.81.4"
21
- },
22
- "dependencies": {
23
- "@expo/vector-icons": "^15.0.2",
24
- "expo": "54.0.0",
25
- "expo-camera": "~17.0.8",
26
- "expo-image-manipulator": "~14.0.7",
27
- "expo-image-picker": "~17.0.8",
28
- "expo-media-library": "~18.2.0",
29
- "expo-screen-orientation": "~9.0.7",
30
- "react-native-svg": "15.12.1",
31
- "react-native-view-shot": "4.0.3",
32
- "@react-native-masked-view/masked-view": "^0.3.1"
33
- },
34
- "overrides": {
35
- "glob": "^10.5.0"
36
- },
37
- "engines": {
38
- "node": ">=14"
39
- },
40
- "repository": {
41
- "type": "git",
42
- "url": "git+https://github.com/pcsagri/react-native-expo-cropper.git"
43
- },
44
- "bugs": {
45
- "url": "https://github.com/pcsagri/react-native-expo-cropper/issues"
46
- },
47
- "homepage": "https://github.com/pcsagri/react-native-expo-cropper#readme",
48
- "scripts": {
49
- "test": "echo \"Error: no test specified\" && exit 1",
50
- "build": "npx --package=@babel/cli babel src --out-dir dist"
51
- },
52
- "devDependencies": {
53
- "@babel/cli": "^7.28.3",
54
- "@babel/core": "^7.28.5",
55
- "@babel/preset-env": "^7.28.5",
56
- "@babel/preset-react": "^7.28.5",
57
- "baseline-browser-mapping": "^2.9.6"
58
- }
59
- }
1
+ {
2
+ "name": "react-native-expo-cropper",
3
+ "version": "1.2.46",
4
+ "description": "Recadrage polygonal d'images.",
5
+ "main": "index.js",
6
+ "author": "PCS AGRI",
7
+ "license": "MIT",
8
+ "keywords": [
9
+ "react-native",
10
+ "expo",
11
+ "image-cropper",
12
+ "camera",
13
+ "polygonal",
14
+ "image",
15
+ "react-native-component"
16
+ ],
17
+ "peerDependencies": {
18
+ "expo": "54.0.0",
19
+ "react": "19.1.0",
20
+ "react-native": "0.81.4"
21
+ },
22
+ "dependencies": {
23
+ "@expo/vector-icons": "^15.0.2",
24
+ "expo": "54.0.0",
25
+ "expo-camera": "~17.0.8",
26
+ "expo-image-manipulator": "~14.0.7",
27
+ "expo-image-picker": "~17.0.8",
28
+ "expo-media-library": "~18.2.0",
29
+ "expo-screen-orientation": "~9.0.7",
30
+ "react-native-svg": "15.12.1",
31
+ "react-native-view-shot": "4.0.3",
32
+ "@react-native-masked-view/masked-view": "^0.3.1"
33
+ },
34
+ "overrides": {
35
+ "glob": "^10.5.0"
36
+ },
37
+ "engines": {
38
+ "node": ">=14"
39
+ },
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "git+https://github.com/pcsagri/react-native-expo-cropper.git"
43
+ },
44
+ "bugs": {
45
+ "url": "https://github.com/pcsagri/react-native-expo-cropper/issues"
46
+ },
47
+ "homepage": "https://github.com/pcsagri/react-native-expo-cropper#readme",
48
+ "scripts": {
49
+ "test": "echo \"Error: no test specified\" && exit 1",
50
+ "build": "npx --package=@babel/cli babel src --out-dir dist"
51
+ },
52
+ "devDependencies": {
53
+ "@babel/cli": "^7.28.3",
54
+ "@babel/core": "^7.28.5",
55
+ "@babel/preset-env": "^7.28.5",
56
+ "@babel/preset-react": "^7.28.5",
57
+ "baseline-browser-mapping": "^2.9.6"
58
+ }
59
+ }