antd-img-crop 4.9.0 → 4.10.1

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 CHANGED
@@ -37,29 +37,32 @@ const Demo = () => (
37
37
 
38
38
  ## Props
39
39
 
40
- | Prop | Type | Default | Description |
41
- | -------------- | -------------------- | -------------- | ----------------------------------------------------------------- |
42
- | quality | `number` | `0.4` | Cropped image quality, `0` to `1` |
43
- | fillColor | `string` | `'white'` | Fill color when cropped image smaller than canvas |
44
- | zoomSlider | `boolean` | `true` | Enable zoom adjustment for image |
45
- | rotationSlider | `boolean` | `false` | Enable rotation adjustment for image |
46
- | aspectSlider | `boolean` | `false` | Enable aspect adjustment for crop area |
47
- | aspect | `number` | `1 / 1` | Aspect of crop area , `width / height` |
48
- | minZoom | `number` | `1` | Minimum zoom factor |
49
- | maxZoom | `number` | `3` | Maximum zoom factor |
50
- | cropShape | `string` | `'rect'` | Shape of crop area, `'rect'` or `'round'` |
51
- | showGrid | `boolean` | `false` | Show grid of crop area (third-lines) |
52
- | cropperProps | `object` | - | [react-easy-crop] props (\* existing props cannot be overridden) |
53
- | modalClassName | `string` | `''` | Provide your own classname for the Modal container |
54
- | modalTitle | `string` | `'Edit image'` | Title of modal |
55
- | modalWidth | `number` \| `string` | | Width of modal in pixels number or percentages |
56
- | modalOk | `string` | | Text of modal confirm button |
57
- | modalCancel | `string` | | Text of modal cancel button |
58
- | onModalOk | `function` | - | Call when click modal confirm button |
59
- | onModalCancel | `function` | - | Call when click modal mask, top right "x", or cancel button |
60
- | modalProps | `object` | | [Ant Design Modal] props (\* existing props cannot be overridden) |
61
- | beforeCrop | `function` | - | Call before modal open, if return `false`, it'll not open |
62
- | onUploadFail | `function` | - | Call when upload failed |
40
+ | Prop | Type | Default | Description |
41
+ | -------------- | ---------- | -------------- | ----------------------------------------------------------------- |
42
+ | quality | `number` | `0.4` | Cropped image quality, `0` to `1` |
43
+ | fillColor | `string` | `'white'` | Fill color when cropped image smaller than canvas |
44
+ | zoomSlider | `boolean` | `true` | Enable zoom adjustment for image |
45
+ | rotationSlider | `boolean` | `false` | Enable rotation adjustment for image |
46
+ | aspectSlider | `boolean` | `false` | Enable aspect adjustment for crop area |
47
+ | showReset | `boolean` | `false` | show a reset button to reset zoom, rotation, aspect |
48
+ | aspect | `number` | `1 / 1` | Aspect of crop area , `width / height` |
49
+ | minZoom | `number` | `1` | Minimum zoom factor |
50
+ | maxZoom | `number` | `3` | Maximum zoom factor |
51
+ | cropShape | `string` | `'rect'` | Shape of crop area, `'rect'` or `'round'` |
52
+ | showGrid | `boolean` | `false` | Show grid of crop area (third-lines) |
53
+ | cropperProps | `object` | - | [react-easy-crop] props (\* existing props cannot be overridden) |
54
+ | modalClassName | `string` | `''` | Provide your own classname for the Modal container |
55
+ | modalTitle | `string` | `'Edit image'` | Title of modal |
56
+ | modalWidth | `number` | `string` | Width of modal in pixels number or percentages |
57
+ | modalOk | `string` | | Text of modal confirm button |
58
+ | modalCancel | `string` | | Text of modal cancel button |
59
+ | onModalOk | `function` | - | Call when click modal confirm button |
60
+ | onModalCancel | `function` | - | Call when click modal mask, top right "x", or cancel button |
61
+ | modalProps | `object` | | [Ant Design Modal] props (\* existing props cannot be overridden) |
62
+ | beforeCrop | `function` | - | Call before modal open, if return `false`, it'll not open |
63
+ | onUploadFail | `function` | - | Call when upload failed |
64
+
65
+ ## FAQ
63
66
 
64
67
  ### `ConfigProvider` not work?
65
68
 
@@ -73,7 +76,7 @@ module.exports = {
73
76
  };
74
77
  ```
75
78
 
76
- ## No style? (only `antd<=v4`)
79
+ ### No style? (only `antd<=v4`)
77
80
 
78
81
  If you use `antd<=v4` + `babel-plugin-import`, and no `Modal` or `Slider` were imported, please import these styles manually:
79
82
 
package/README.zh-CN.md CHANGED
@@ -44,6 +44,7 @@ const Demo = () => (
44
44
  | zoomSlider | `boolean` | `true` | 启用图片缩放调整 |
45
45
  | rotationSlider | `boolean` | `false` | 启用图片旋转调整 |
46
46
  | aspectSlider | `boolean` | `false` | 启用裁切比率调整 |
47
+ | showReset | `boolean` | `false` | 显示重置按钮,用以重置缩放、旋转、比率 |
47
48
  | aspect | `number` | `1 / 1` | 裁切区域宽高比,`width / height` |
48
49
  | minZoom | `number` | `1` | 最小缩放倍数 |
49
50
  | maxZoom | `number` | `3` | 最大缩放倍数 |
@@ -61,6 +62,8 @@ const Demo = () => (
61
62
  | beforeCrop | `function` | - | 弹窗打开前调用,若返回 `false`,弹框将不会打开 |
62
63
  | onUploadFail | `function` | - | 上传失败时的回调 |
63
64
 
65
+ ## FAQ
66
+
64
67
  ### `ConfigProvider` 无效?
65
68
 
66
69
  尝试设置 `libraryDirectory`(`'es'` 或 `'lib'`)到 `babel-plugin-import` 的配置项,看看哪个会生效。
@@ -73,7 +76,7 @@ module.exports = {
73
76
  };
74
77
  ```
75
78
 
76
- ## 没有样式?(仅 `antd<=v4`)
79
+ ### 没有样式?(仅 `antd<=v4`)
77
80
 
78
81
  若使用 `antd<=v4` + `babel-plugin-import`,且未引入 `Modal` 或 `Slider`,请手动引入这些样式:
79
82
 
@@ -82,13 +85,6 @@ import 'antd/es/modal/style';
82
85
  import 'antd/es/slider/style';
83
86
  ```
84
87
 
85
- ## 在 antd v5 之前
86
-
87
- ```js
88
- import 'antd/es/modal/style';
89
- import 'antd/es/slider/style';
90
- ```
91
-
92
88
  ## 协议
93
89
 
94
90
  [MIT License](https://github.com/nanxiaobei/antd-img-crop/blob/main/LICENSE) (c) [nanxiaobei](https://lee.so/)
@@ -1,19 +1,5 @@
1
1
  'use strict';
2
2
 
3
-
4
-
5
- function __$styleInject(css) {
6
- if (!css) return;
7
-
8
- if (typeof window == 'undefined') return;
9
- var style = document.createElement('style');
10
- style.setAttribute('media', 'screen');
11
-
12
- style.innerHTML = css;
13
- document.head.appendChild(style);
14
- return css;
15
- }
16
-
17
3
  var tslib = require('tslib');
18
4
  var jsxRuntime = require('react/jsx-runtime');
19
5
  var react = require('react');
@@ -22,6 +8,7 @@ var AntModal = require('antd/lib/modal');
22
8
  var AntUpload = require('antd/lib/upload');
23
9
  var compareVersions = require('compare-versions');
24
10
  var Cropper = require('react-easy-crop');
11
+ var AntButton = require('antd/lib/button');
25
12
  var AntSlider = require('antd/lib/slider');
26
13
 
27
14
  const PREFIX = 'img-crop';
@@ -36,11 +23,19 @@ const ASPECT_MAX = 2;
36
23
  const ASPECT_STEP = 0.01;
37
24
 
38
25
  const EasyCrop = react.forwardRef((props, ref) => {
39
- const { cropperRef, zoomSlider, rotationSlider, aspectSlider, image, aspect: propAspect, minZoom, maxZoom, cropShape, showGrid, cropperProps, } = props;
40
- const [crop, onCropChange] = react.useState({ x: 0, y: 0 });
26
+ const { cropperRef, zoomSlider, rotationSlider, aspectSlider, showReset, image, aspect: ASPECT_INITIAL, minZoom, maxZoom, cropShape, showGrid, cropperProps, isCN, } = props;
41
27
  const [zoom, setZoom] = react.useState(ZOOM_INITIAL);
42
28
  const [rotation, setRotation] = react.useState(ROTATION_INITIAL);
43
- const [aspect, setAspect] = react.useState(propAspect);
29
+ const [aspect, setAspect] = react.useState(ASPECT_INITIAL);
30
+ const isResetActive = zoom !== ZOOM_INITIAL ||
31
+ rotation !== ROTATION_INITIAL ||
32
+ aspect !== ASPECT_INITIAL;
33
+ const onReset = () => {
34
+ setZoom(ZOOM_INITIAL);
35
+ setRotation(ROTATION_INITIAL);
36
+ setAspect(ASPECT_INITIAL);
37
+ };
38
+ const [crop, onCropChange] = react.useState({ x: 0, y: 0 });
44
39
  const cropPixelsRef = react.useRef({ width: 0, height: 0, x: 0, y: 0 });
45
40
  const onCropComplete = react.useCallback((_, croppedAreaPixels) => {
46
41
  cropPixelsRef.current = croppedAreaPixels;
@@ -51,18 +46,49 @@ const EasyCrop = react.forwardRef((props, ref) => {
51
46
  setRotation,
52
47
  cropPixelsRef,
53
48
  }));
49
+ const wrapperClass = 'flex items-center w-3/5 mx-auto';
50
+ const buttonClass = 'flex items-center justify-center w-8 h-8 bg-transparent border-0 font-[inherit] text-[18px] cursor-pointer disabled:opacity-20 disabled:cursor-default';
51
+ const sliderClass = 'flex-1 mx-2';
54
52
  return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(Cropper, Object.assign({}, cropperProps, { ref: cropperRef, image: image, crop: crop,
55
53
  //
56
54
  zoom: zoom, rotation: rotation, aspect: aspect, minZoom: minZoom, maxZoom: maxZoom, zoomWithScroll: zoomSlider,
57
55
  //
58
56
  cropShape: cropShape, showGrid: showGrid, onCropChange: onCropChange, onZoomChange: setZoom, onRotationChange: setRotation, onCropComplete: onCropComplete, classes: {
59
- containerClassName: `${PREFIX}-container`,
57
+ containerClassName: `${PREFIX}-container !relative w-full h-[40vh] [&~section:first-of-type]:mt-4 [&~section:last-of-type]:mb-4`,
60
58
  mediaClassName: `${PREFIX}-media`,
61
- } })), zoomSlider && (jsxRuntime.jsxs("section", Object.assign({ className: `${PREFIX}-control ${PREFIX}-control-zoom` }, { children: [jsxRuntime.jsx("button", Object.assign({ onClick: () => setZoom(zoom - ZOOM_STEP), disabled: zoom - ZOOM_STEP < minZoom }, { children: "\uFF0D" })), jsxRuntime.jsx(AntSlider, { min: minZoom, max: maxZoom, step: ZOOM_STEP, value: zoom, onChange: setZoom }), jsxRuntime.jsx("button", Object.assign({ onClick: () => setZoom(zoom + ZOOM_STEP), disabled: zoom + ZOOM_STEP > maxZoom }, { children: "\uFF0B" }))] }))), rotationSlider && (jsxRuntime.jsxs("section", Object.assign({ className: `${PREFIX}-control ${PREFIX}-control-rotation` }, { children: [jsxRuntime.jsx("button", Object.assign({ onClick: () => setRotation(rotation - ROTATION_STEP), disabled: rotation === ROTATION_MIN }, { children: "\u21BA" })), jsxRuntime.jsx(AntSlider, { min: ROTATION_MIN, max: ROTATION_MAX, step: ROTATION_STEP, value: rotation, onChange: setRotation }), jsxRuntime.jsx("button", Object.assign({ onClick: () => setRotation(rotation + ROTATION_STEP), disabled: rotation === ROTATION_MAX }, { children: "\u21BB" }))] }))), aspectSlider && (jsxRuntime.jsxs("section", Object.assign({ className: `${PREFIX}-control ${PREFIX}-control-aspect` }, { children: [jsxRuntime.jsx("button", Object.assign({ onClick: () => setAspect(aspect - ASPECT_STEP), disabled: aspect - ASPECT_STEP < ASPECT_MIN }, { children: "\u2195\uFE0F" })), jsxRuntime.jsx(AntSlider, { min: ASPECT_MIN, max: ASPECT_MAX, step: ASPECT_STEP, value: aspect, onChange: setAspect }), jsxRuntime.jsx("button", Object.assign({ onClick: () => setAspect(aspect + ASPECT_STEP), disabled: aspect + ASPECT_STEP > ASPECT_MAX }, { children: "\u2194\uFE0F" }))] })))] }));
59
+ } })), zoomSlider && (jsxRuntime.jsxs("section", Object.assign({ className: `${PREFIX}-control ${PREFIX}-control-zoom ${wrapperClass}` }, { children: [jsxRuntime.jsx("button", Object.assign({ className: buttonClass, onClick: () => setZoom(zoom - ZOOM_STEP), disabled: true }, { children: "\uFF0D" })), jsxRuntime.jsx(AntSlider, { className: sliderClass, min: minZoom, max: maxZoom, step: ZOOM_STEP, value: zoom, onChange: setZoom }), jsxRuntime.jsx("button", Object.assign({ className: buttonClass, onClick: () => setZoom(zoom + ZOOM_STEP), disabled: zoom + ZOOM_STEP > maxZoom }, { children: "\uFF0B" }))] }))), rotationSlider && (jsxRuntime.jsxs("section", Object.assign({ className: `${PREFIX}-control ${PREFIX}-control-rotation ${wrapperClass}` }, { children: [jsxRuntime.jsx("button", Object.assign({ className: `${buttonClass} !text-[16px]`, onClick: () => setRotation(rotation - ROTATION_STEP), disabled: rotation === ROTATION_MIN }, { children: "\u21BA" })), jsxRuntime.jsx(AntSlider, { className: sliderClass, min: ROTATION_MIN, max: ROTATION_MAX, step: ROTATION_STEP, value: rotation, onChange: setRotation }), jsxRuntime.jsx("button", Object.assign({ className: `${buttonClass} !text-[16px]`, onClick: () => setRotation(rotation + ROTATION_STEP), disabled: rotation === ROTATION_MAX }, { children: "\u21BB" }))] }))), aspectSlider && (jsxRuntime.jsxs("section", Object.assign({ className: `${PREFIX}-control ${PREFIX}-control-aspect ${wrapperClass}` }, { children: [jsxRuntime.jsx("button", Object.assign({ className: buttonClass, onClick: () => setAspect(aspect - ASPECT_STEP), disabled: aspect - ASPECT_STEP < ASPECT_MIN }, { children: "\u2195\uFE0F" })), jsxRuntime.jsx(AntSlider, { className: sliderClass, min: ASPECT_MIN, max: ASPECT_MAX, step: ASPECT_STEP, value: aspect, onChange: setAspect }), jsxRuntime.jsx("button", Object.assign({ className: buttonClass, onClick: () => setAspect(aspect + ASPECT_STEP), disabled: aspect + ASPECT_STEP > ASPECT_MAX }, { children: "\u2194\uFE0F" }))] }))), showReset && (zoomSlider || rotationSlider || aspectSlider) && (jsxRuntime.jsx(AntButton, Object.assign({ className: "absolute bottom-[20px]", style: isResetActive ? {} : { opacity: 0.3, pointerEvents: 'none' }, onClick: onReset }, { children: isCN ? '重置' : 'Reset' })))] }));
62
60
  });
63
61
  var EasyCrop$1 = react.memo(EasyCrop);
64
62
 
65
- __$styleInject(".img-crop-modal .img-crop-container {\n position: relative;\n width: 100%;\n height: 40vh;\n}\n.img-crop-modal .img-crop-control {\n display: flex;\n align-items: center;\n width: 60%;\n margin-left: auto;\n margin-right: auto;\n}\n.img-crop-modal .img-crop-control:first-of-type {\n margin-top: 16px;\n}\n.img-crop-modal .img-crop-control:last-of-type {\n margin-bottom: -8px;\n}\n.img-crop-modal .img-crop-control button {\n display: flex;\n justify-content: center;\n align-items: center;\n width: 34px;\n height: 34px;\n padding: 0;\n font-style: normal;\n background: transparent;\n border: 0;\n outline: 0;\n cursor: pointer;\n}\n.img-crop-modal .img-crop-control button[disabled] {\n cursor: default;\n}\n.img-crop-modal .img-crop-control button + div:only-of-type {\n flex: 1;\n margin: 0 8px;\n}\n.img-crop-modal .img-crop-control-zoom button {\n font-size: 18px;\n}\n.img-crop-modal .img-crop-control-rotation button {\n font-size: 16px;\n}\n.img-crop-modal .img-crop-control-rotation button:first-of-type {\n transform: rotate(-20deg);\n}\n.img-crop-modal .img-crop-control-rotation button:last-of-type {\n transform: rotate(20deg);\n}\n");
63
+ function styleInject(css, ref) {
64
+ if ( ref === void 0 ) ref = {};
65
+ var insertAt = ref.insertAt;
66
+
67
+ if (!css || typeof document === 'undefined') { return; }
68
+
69
+ var head = document.head || document.getElementsByTagName('head')[0];
70
+ var style = document.createElement('style');
71
+ style.type = 'text/css';
72
+
73
+ if (insertAt === 'top') {
74
+ if (head.firstChild) {
75
+ head.insertBefore(style, head.firstChild);
76
+ } else {
77
+ head.appendChild(style);
78
+ }
79
+ } else {
80
+ head.appendChild(style);
81
+ }
82
+
83
+ if (style.styleSheet) {
84
+ style.styleSheet.cssText = css;
85
+ } else {
86
+ style.appendChild(document.createTextNode(css));
87
+ }
88
+ }
89
+
90
+ var css_248z = ".container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.visible{visibility:visible}.absolute{position:absolute}.\\!relative{position:relative!important}.bottom-\\[20px\\]{bottom:20px}.mx-2{margin-left:.5rem;margin-right:.5rem}.mx-auto{margin-left:auto;margin-right:auto}.flex{display:flex}.grid{display:grid}.h-8{height:2rem}.h-\\[40vh\\]{height:40vh}.w-3\\/5{width:60%}.w-8{width:2rem}.w-full{width:100%}.flex-1{flex:1 1 0%}.cursor-pointer{cursor:pointer}.items-center{align-items:center}.justify-center{justify-content:center}.border-0{border-width:0}.bg-transparent{background-color:transparent}.font-\\[inherit\\]{font-family:inherit}.\\!text-\\[16px\\]{font-size:16px!important}.text-\\[18px\\]{font-size:18px}.disabled\\:cursor-default:disabled{cursor:default}.disabled\\:opacity-20:disabled{opacity:.2}.\\[\\&\\~section\\:first-of-type\\]\\:mt-4~section:first-of-type{margin-top:1rem}.\\[\\&\\~section\\:last-of-type\\]\\:mb-4~section:last-of-type{margin-bottom:1rem}";
91
+ styleInject(css_248z);
66
92
 
67
93
  const openKey = compareVersions.compareVersions(antd.version, '4.23.0') === -1 ? 'visible' : 'open';
68
94
  const deprecate = (obj, old, now) => {
@@ -81,7 +107,7 @@ const ImgCrop = react.forwardRef((props, cropperRef) => {
81
107
  // @ts-ignore
82
108
  cropShape: CROP_SHAPE = 'rect',
83
109
  // @ts-ignore
84
- showGrid: SHOW_GRID = false, cropperProps, modalClassName, modalTitle, modalWidth, modalOk, modalCancel, onModalOk, onModalCancel, modalProps, beforeCrop, onUploadFail, children, } = props;
110
+ showGrid: SHOW_GRID = false, cropperProps, modalClassName, modalTitle, modalWidth, modalOk, modalCancel, showReset = false, onModalOk, onModalCancel, modalProps, beforeCrop, onUploadFail, children, } = props;
85
111
  const cropShape = deprecate(props, 'shape', 'cropShape');
86
112
  const showGrid = deprecate(props, 'grid', 'showGrid');
87
113
  const zoomSlider = deprecate(props, 'zoom', 'zoomSlider');
@@ -234,14 +260,10 @@ const ImgCrop = react.forwardRef((props, cropperRef) => {
234
260
  }), type, quality);
235
261
  }), [fillColor, quality, rotationSlider]);
236
262
  const wrapClassName = `${PREFIX}-modal${modalClassName ? ` ${modalClassName}` : ''}`;
237
- const title = react.useMemo(() => {
238
- if (modalTitle) {
239
- return modalTitle;
240
- }
241
- const lang = typeof window !== 'undefined' ? window.navigator.language : '';
242
- return lang === 'zh-CN' ? '编辑图片' : 'Edit image';
243
- }, [modalTitle]);
244
- return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [uploadComponent, image && (jsxRuntime.jsx(AntModal, Object.assign({}, modalProps, modalBaseProps, { [openKey]: true }, { title: title, onOk: onOk, onCancel: onCancel, wrapClassName: wrapClassName, maskClosable: false, destroyOnClose: true }, { children: jsxRuntime.jsx(EasyCrop$1, { ref: easyCropRef, cropperRef: cropperRef, zoomSlider: zoomSlider, rotationSlider: rotationSlider, aspectSlider: aspectSlider, image: image, aspect: aspect, minZoom: minZoom, maxZoom: maxZoom, cropShape: cropShape, showGrid: showGrid, cropperProps: cropperProps }) })))] }));
263
+ const lang = typeof window === 'undefined' ? '' : window.navigator.language;
264
+ const isCN = lang === 'zh-CN';
265
+ const title = modalTitle || (isCN ? '编辑图片' : 'Edit image');
266
+ return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [uploadComponent, image && (jsxRuntime.jsx(AntModal, Object.assign({}, modalProps, modalBaseProps, { [openKey]: true }, { title: title, onOk: onOk, onCancel: onCancel, wrapClassName: wrapClassName, maskClosable: false, destroyOnClose: true }, { children: jsxRuntime.jsx(EasyCrop$1, { ref: easyCropRef, cropperRef: cropperRef, zoomSlider: zoomSlider, rotationSlider: rotationSlider, aspectSlider: aspectSlider, showReset: showReset, image: image, aspect: aspect, minZoom: minZoom, maxZoom: maxZoom, cropShape: cropShape, showGrid: showGrid, cropperProps: cropperProps, isCN: isCN }) })))] }));
245
267
  });
246
268
 
247
269
  module.exports = ImgCrop;
@@ -9,6 +9,7 @@ type ImgCropProps = {
9
9
  zoomSlider?: boolean;
10
10
  rotationSlider?: boolean;
11
11
  aspectSlider?: boolean;
12
+ showReset?: boolean;
12
13
  aspect?: number;
13
14
  minZoom?: number;
14
15
  maxZoom?: number;
@@ -1,15 +1,3 @@
1
- function __$styleInject(css) {
2
- if (!css) return;
3
-
4
- if (typeof window == 'undefined') return;
5
- var style = document.createElement('style');
6
- style.setAttribute('media', 'screen');
7
-
8
- style.innerHTML = css;
9
- document.head.appendChild(style);
10
- return css;
11
- }
12
-
13
1
  import { __rest, __awaiter } from 'tslib';
14
2
  import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
15
3
  import { forwardRef, useState, useRef, useCallback, useImperativeHandle, memo, useMemo } from 'react';
@@ -18,6 +6,7 @@ import AntModal from 'antd/es/modal';
18
6
  import AntUpload from 'antd/es/upload';
19
7
  import { compareVersions } from 'compare-versions';
20
8
  import Cropper from 'react-easy-crop';
9
+ import AntButton from 'antd/es/button';
21
10
  import AntSlider from 'antd/es/slider';
22
11
 
23
12
  const PREFIX = 'img-crop';
@@ -32,11 +21,19 @@ const ASPECT_MAX = 2;
32
21
  const ASPECT_STEP = 0.01;
33
22
 
34
23
  const EasyCrop = forwardRef((props, ref) => {
35
- const { cropperRef, zoomSlider, rotationSlider, aspectSlider, image, aspect: propAspect, minZoom, maxZoom, cropShape, showGrid, cropperProps, } = props;
36
- const [crop, onCropChange] = useState({ x: 0, y: 0 });
24
+ const { cropperRef, zoomSlider, rotationSlider, aspectSlider, showReset, image, aspect: ASPECT_INITIAL, minZoom, maxZoom, cropShape, showGrid, cropperProps, isCN, } = props;
37
25
  const [zoom, setZoom] = useState(ZOOM_INITIAL);
38
26
  const [rotation, setRotation] = useState(ROTATION_INITIAL);
39
- const [aspect, setAspect] = useState(propAspect);
27
+ const [aspect, setAspect] = useState(ASPECT_INITIAL);
28
+ const isResetActive = zoom !== ZOOM_INITIAL ||
29
+ rotation !== ROTATION_INITIAL ||
30
+ aspect !== ASPECT_INITIAL;
31
+ const onReset = () => {
32
+ setZoom(ZOOM_INITIAL);
33
+ setRotation(ROTATION_INITIAL);
34
+ setAspect(ASPECT_INITIAL);
35
+ };
36
+ const [crop, onCropChange] = useState({ x: 0, y: 0 });
40
37
  const cropPixelsRef = useRef({ width: 0, height: 0, x: 0, y: 0 });
41
38
  const onCropComplete = useCallback((_, croppedAreaPixels) => {
42
39
  cropPixelsRef.current = croppedAreaPixels;
@@ -47,18 +44,49 @@ const EasyCrop = forwardRef((props, ref) => {
47
44
  setRotation,
48
45
  cropPixelsRef,
49
46
  }));
47
+ const wrapperClass = 'flex items-center w-3/5 mx-auto';
48
+ const buttonClass = 'flex items-center justify-center w-8 h-8 bg-transparent border-0 font-[inherit] text-[18px] cursor-pointer disabled:opacity-20 disabled:cursor-default';
49
+ const sliderClass = 'flex-1 mx-2';
50
50
  return (jsxs(Fragment, { children: [jsx(Cropper, Object.assign({}, cropperProps, { ref: cropperRef, image: image, crop: crop,
51
51
  //
52
52
  zoom: zoom, rotation: rotation, aspect: aspect, minZoom: minZoom, maxZoom: maxZoom, zoomWithScroll: zoomSlider,
53
53
  //
54
54
  cropShape: cropShape, showGrid: showGrid, onCropChange: onCropChange, onZoomChange: setZoom, onRotationChange: setRotation, onCropComplete: onCropComplete, classes: {
55
- containerClassName: `${PREFIX}-container`,
55
+ containerClassName: `${PREFIX}-container !relative w-full h-[40vh] [&~section:first-of-type]:mt-4 [&~section:last-of-type]:mb-4`,
56
56
  mediaClassName: `${PREFIX}-media`,
57
- } })), zoomSlider && (jsxs("section", Object.assign({ className: `${PREFIX}-control ${PREFIX}-control-zoom` }, { children: [jsx("button", Object.assign({ onClick: () => setZoom(zoom - ZOOM_STEP), disabled: zoom - ZOOM_STEP < minZoom }, { children: "\uFF0D" })), jsx(AntSlider, { min: minZoom, max: maxZoom, step: ZOOM_STEP, value: zoom, onChange: setZoom }), jsx("button", Object.assign({ onClick: () => setZoom(zoom + ZOOM_STEP), disabled: zoom + ZOOM_STEP > maxZoom }, { children: "\uFF0B" }))] }))), rotationSlider && (jsxs("section", Object.assign({ className: `${PREFIX}-control ${PREFIX}-control-rotation` }, { children: [jsx("button", Object.assign({ onClick: () => setRotation(rotation - ROTATION_STEP), disabled: rotation === ROTATION_MIN }, { children: "\u21BA" })), jsx(AntSlider, { min: ROTATION_MIN, max: ROTATION_MAX, step: ROTATION_STEP, value: rotation, onChange: setRotation }), jsx("button", Object.assign({ onClick: () => setRotation(rotation + ROTATION_STEP), disabled: rotation === ROTATION_MAX }, { children: "\u21BB" }))] }))), aspectSlider && (jsxs("section", Object.assign({ className: `${PREFIX}-control ${PREFIX}-control-aspect` }, { children: [jsx("button", Object.assign({ onClick: () => setAspect(aspect - ASPECT_STEP), disabled: aspect - ASPECT_STEP < ASPECT_MIN }, { children: "\u2195\uFE0F" })), jsx(AntSlider, { min: ASPECT_MIN, max: ASPECT_MAX, step: ASPECT_STEP, value: aspect, onChange: setAspect }), jsx("button", Object.assign({ onClick: () => setAspect(aspect + ASPECT_STEP), disabled: aspect + ASPECT_STEP > ASPECT_MAX }, { children: "\u2194\uFE0F" }))] })))] }));
57
+ } })), zoomSlider && (jsxs("section", Object.assign({ className: `${PREFIX}-control ${PREFIX}-control-zoom ${wrapperClass}` }, { children: [jsx("button", Object.assign({ className: buttonClass, onClick: () => setZoom(zoom - ZOOM_STEP), disabled: true }, { children: "\uFF0D" })), jsx(AntSlider, { className: sliderClass, min: minZoom, max: maxZoom, step: ZOOM_STEP, value: zoom, onChange: setZoom }), jsx("button", Object.assign({ className: buttonClass, onClick: () => setZoom(zoom + ZOOM_STEP), disabled: zoom + ZOOM_STEP > maxZoom }, { children: "\uFF0B" }))] }))), rotationSlider && (jsxs("section", Object.assign({ className: `${PREFIX}-control ${PREFIX}-control-rotation ${wrapperClass}` }, { children: [jsx("button", Object.assign({ className: `${buttonClass} !text-[16px]`, onClick: () => setRotation(rotation - ROTATION_STEP), disabled: rotation === ROTATION_MIN }, { children: "\u21BA" })), jsx(AntSlider, { className: sliderClass, min: ROTATION_MIN, max: ROTATION_MAX, step: ROTATION_STEP, value: rotation, onChange: setRotation }), jsx("button", Object.assign({ className: `${buttonClass} !text-[16px]`, onClick: () => setRotation(rotation + ROTATION_STEP), disabled: rotation === ROTATION_MAX }, { children: "\u21BB" }))] }))), aspectSlider && (jsxs("section", Object.assign({ className: `${PREFIX}-control ${PREFIX}-control-aspect ${wrapperClass}` }, { children: [jsx("button", Object.assign({ className: buttonClass, onClick: () => setAspect(aspect - ASPECT_STEP), disabled: aspect - ASPECT_STEP < ASPECT_MIN }, { children: "\u2195\uFE0F" })), jsx(AntSlider, { className: sliderClass, min: ASPECT_MIN, max: ASPECT_MAX, step: ASPECT_STEP, value: aspect, onChange: setAspect }), jsx("button", Object.assign({ className: buttonClass, onClick: () => setAspect(aspect + ASPECT_STEP), disabled: aspect + ASPECT_STEP > ASPECT_MAX }, { children: "\u2194\uFE0F" }))] }))), showReset && (zoomSlider || rotationSlider || aspectSlider) && (jsx(AntButton, Object.assign({ className: "absolute bottom-[20px]", style: isResetActive ? {} : { opacity: 0.3, pointerEvents: 'none' }, onClick: onReset }, { children: isCN ? '重置' : 'Reset' })))] }));
58
58
  });
59
59
  var EasyCrop$1 = memo(EasyCrop);
60
60
 
61
- __$styleInject(".img-crop-modal .img-crop-container {\n position: relative;\n width: 100%;\n height: 40vh;\n}\n.img-crop-modal .img-crop-control {\n display: flex;\n align-items: center;\n width: 60%;\n margin-left: auto;\n margin-right: auto;\n}\n.img-crop-modal .img-crop-control:first-of-type {\n margin-top: 16px;\n}\n.img-crop-modal .img-crop-control:last-of-type {\n margin-bottom: -8px;\n}\n.img-crop-modal .img-crop-control button {\n display: flex;\n justify-content: center;\n align-items: center;\n width: 34px;\n height: 34px;\n padding: 0;\n font-style: normal;\n background: transparent;\n border: 0;\n outline: 0;\n cursor: pointer;\n}\n.img-crop-modal .img-crop-control button[disabled] {\n cursor: default;\n}\n.img-crop-modal .img-crop-control button + div:only-of-type {\n flex: 1;\n margin: 0 8px;\n}\n.img-crop-modal .img-crop-control-zoom button {\n font-size: 18px;\n}\n.img-crop-modal .img-crop-control-rotation button {\n font-size: 16px;\n}\n.img-crop-modal .img-crop-control-rotation button:first-of-type {\n transform: rotate(-20deg);\n}\n.img-crop-modal .img-crop-control-rotation button:last-of-type {\n transform: rotate(20deg);\n}\n");
61
+ function styleInject(css, ref) {
62
+ if ( ref === void 0 ) ref = {};
63
+ var insertAt = ref.insertAt;
64
+
65
+ if (!css || typeof document === 'undefined') { return; }
66
+
67
+ var head = document.head || document.getElementsByTagName('head')[0];
68
+ var style = document.createElement('style');
69
+ style.type = 'text/css';
70
+
71
+ if (insertAt === 'top') {
72
+ if (head.firstChild) {
73
+ head.insertBefore(style, head.firstChild);
74
+ } else {
75
+ head.appendChild(style);
76
+ }
77
+ } else {
78
+ head.appendChild(style);
79
+ }
80
+
81
+ if (style.styleSheet) {
82
+ style.styleSheet.cssText = css;
83
+ } else {
84
+ style.appendChild(document.createTextNode(css));
85
+ }
86
+ }
87
+
88
+ var css_248z = ".container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.visible{visibility:visible}.absolute{position:absolute}.\\!relative{position:relative!important}.bottom-\\[20px\\]{bottom:20px}.mx-2{margin-left:.5rem;margin-right:.5rem}.mx-auto{margin-left:auto;margin-right:auto}.flex{display:flex}.grid{display:grid}.h-8{height:2rem}.h-\\[40vh\\]{height:40vh}.w-3\\/5{width:60%}.w-8{width:2rem}.w-full{width:100%}.flex-1{flex:1 1 0%}.cursor-pointer{cursor:pointer}.items-center{align-items:center}.justify-center{justify-content:center}.border-0{border-width:0}.bg-transparent{background-color:transparent}.font-\\[inherit\\]{font-family:inherit}.\\!text-\\[16px\\]{font-size:16px!important}.text-\\[18px\\]{font-size:18px}.disabled\\:cursor-default:disabled{cursor:default}.disabled\\:opacity-20:disabled{opacity:.2}.\\[\\&\\~section\\:first-of-type\\]\\:mt-4~section:first-of-type{margin-top:1rem}.\\[\\&\\~section\\:last-of-type\\]\\:mb-4~section:last-of-type{margin-bottom:1rem}";
89
+ styleInject(css_248z);
62
90
 
63
91
  const openKey = compareVersions(version, '4.23.0') === -1 ? 'visible' : 'open';
64
92
  const deprecate = (obj, old, now) => {
@@ -77,7 +105,7 @@ const ImgCrop = forwardRef((props, cropperRef) => {
77
105
  // @ts-ignore
78
106
  cropShape: CROP_SHAPE = 'rect',
79
107
  // @ts-ignore
80
- showGrid: SHOW_GRID = false, cropperProps, modalClassName, modalTitle, modalWidth, modalOk, modalCancel, onModalOk, onModalCancel, modalProps, beforeCrop, onUploadFail, children, } = props;
108
+ showGrid: SHOW_GRID = false, cropperProps, modalClassName, modalTitle, modalWidth, modalOk, modalCancel, showReset = false, onModalOk, onModalCancel, modalProps, beforeCrop, onUploadFail, children, } = props;
81
109
  const cropShape = deprecate(props, 'shape', 'cropShape');
82
110
  const showGrid = deprecate(props, 'grid', 'showGrid');
83
111
  const zoomSlider = deprecate(props, 'zoom', 'zoomSlider');
@@ -230,14 +258,10 @@ const ImgCrop = forwardRef((props, cropperRef) => {
230
258
  }), type, quality);
231
259
  }), [fillColor, quality, rotationSlider]);
232
260
  const wrapClassName = `${PREFIX}-modal${modalClassName ? ` ${modalClassName}` : ''}`;
233
- const title = useMemo(() => {
234
- if (modalTitle) {
235
- return modalTitle;
236
- }
237
- const lang = typeof window !== 'undefined' ? window.navigator.language : '';
238
- return lang === 'zh-CN' ? '编辑图片' : 'Edit image';
239
- }, [modalTitle]);
240
- return (jsxs(Fragment, { children: [uploadComponent, image && (jsx(AntModal, Object.assign({}, modalProps, modalBaseProps, { [openKey]: true }, { title: title, onOk: onOk, onCancel: onCancel, wrapClassName: wrapClassName, maskClosable: false, destroyOnClose: true }, { children: jsx(EasyCrop$1, { ref: easyCropRef, cropperRef: cropperRef, zoomSlider: zoomSlider, rotationSlider: rotationSlider, aspectSlider: aspectSlider, image: image, aspect: aspect, minZoom: minZoom, maxZoom: maxZoom, cropShape: cropShape, showGrid: showGrid, cropperProps: cropperProps }) })))] }));
261
+ const lang = typeof window === 'undefined' ? '' : window.navigator.language;
262
+ const isCN = lang === 'zh-CN';
263
+ const title = modalTitle || (isCN ? '编辑图片' : 'Edit image');
264
+ return (jsxs(Fragment, { children: [uploadComponent, image && (jsx(AntModal, Object.assign({}, modalProps, modalBaseProps, { [openKey]: true }, { title: title, onOk: onOk, onCancel: onCancel, wrapClassName: wrapClassName, maskClosable: false, destroyOnClose: true }, { children: jsx(EasyCrop$1, { ref: easyCropRef, cropperRef: cropperRef, zoomSlider: zoomSlider, rotationSlider: rotationSlider, aspectSlider: aspectSlider, showReset: showReset, image: image, aspect: aspect, minZoom: minZoom, maxZoom: maxZoom, cropShape: cropShape, showGrid: showGrid, cropperProps: cropperProps, isCN: isCN }) })))] }));
241
265
  });
242
266
 
243
267
  export { ImgCrop as default };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "antd-img-crop",
3
- "version": "4.9.0",
3
+ "version": "4.10.1",
4
4
  "description": "An image cropper for Ant Design Upload",
5
5
  "keywords": [
6
6
  "react",
@@ -36,23 +36,26 @@
36
36
  "@rollup/plugin-replace": "^5.0.2",
37
37
  "@rollup/plugin-typescript": "^11.0.0",
38
38
  "@trivago/prettier-plugin-sort-imports": "^4.1.1",
39
- "@types/node": "^18.15.1",
39
+ "@types/node": "^18.15.3",
40
40
  "@types/react": "^18.0.28",
41
41
  "@types/react-dom": "^18.0.11",
42
- "@types/rollup-plugin-less": "^1.1.2",
43
42
  "@vitejs/plugin-react": "^3.1.0",
44
43
  "antd": "^5.3.1",
44
+ "autoprefixer": "^10.4.14",
45
45
  "eslint": "^8.36.0",
46
46
  "eslint-config-prettier": "^8.7.0",
47
47
  "eslint-config-react-app": "^7.0.1",
48
+ "postcss": "^8.4.21",
48
49
  "prettier": "^2.8.4",
50
+ "prettier-plugin-tailwindcss": "^0.2.5",
49
51
  "react": "^18.2.0",
50
52
  "react-dom": "^18.2.0",
51
53
  "rollup": "3.19.1",
52
- "rollup-plugin-dts": "^5.2.0",
53
- "rollup-plugin-less": "^1.1.3",
54
- "typescript": "^4.9.5",
55
- "vite": "^4.1.4"
54
+ "rollup-plugin-dts": "^5.3.0",
55
+ "rollup-plugin-postcss": "^4.0.2",
56
+ "tailwindcss": "^3.2.7",
57
+ "typescript": "^5.0.2",
58
+ "vite": "^4.2.0"
56
59
  },
57
60
  "scripts": {
58
61
  "build": "rm -rf dist && rollup -c --configPlugin @rollup/plugin-typescript"