react-mobile-cropper
Version:
The react mobile cropper inspired by Android mobile croppers
286 lines (271 loc) • 19 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var reactAdvancedCropper = require('react-advanced-cropper');
var tslib = require('tslib');
var React = require('react');
var cn = require('classnames');
var mobile = require('advanced-cropper/showcase/mobile');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
var cn__default = /*#__PURE__*/_interopDefaultLegacy(cn);
var FlipHorizontalIcon = function (_a) {
var className = _a.className;
return (React__default["default"].createElement("svg", { className: className, xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16" },
React__default["default"].createElement("path", { fill: "#FFF", d: "M8 15.9c-.6 0-1-.4-1-1V1c0-.6.4-1 1-1s1 .4 1 1v13.9c0 .6-.4 1-1 1zm4-2.4h-.5c-.4 0-.8-.3-.8-.8s.3-.8.8-.8h.5c.4 0 .8.3.8.8s-.4.8-.8.8zm2.7 0h-.5c-.4 0-.8-.3-.8-.8 0-.3.2-.6.5-.7.1-.3.4-.5.7-.5.4 0 .8.3.8.8v.5c.1.4-.3.7-.7.7zm0-2.8c-.4 0-.8-.3-.8-.8v-.8c0-.4.3-.8.8-.8s.8.3.8.8v.8c0 .4-.4.8-.8.8zm0-3.3c-.4 0-.8-.3-.8-.8v-.7c0-.4.3-.8.8-.8s.8.3.8.8v.8c0 .4-.4.7-.8.7zm0-3.3c-.3 0-.6-.2-.7-.5-.3-.1-.5-.4-.5-.7 0-.4.3-.8.8-.8h.5c.4 0 .8.3.8.8v.5c-.1.4-.5.7-.9.7zM12 3.6h-.5c-.4 0-.8-.3-.8-.8s.3-.8.8-.8h.5c.4 0 .8.3.8.8s-.4.8-.8.8zM4.5 13.5H1.3c-.4 0-.8-.3-.8-.8V2.9c0-.4.3-.8.8-.8h3.2v1.5H2V12h2.5v1.5z" })));
};
var RotateRightIcon = function (_a) {
var className = _a.className;
return (React__default["default"].createElement("svg", { className: className, xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16" },
React__default["default"].createElement("path", { fill: "#FFF", d: "M12.5 11.8c.2.2.2.6 0 .9-1.1 1.1-2.8 1.8-4.5 1.8-3.7 0-6.5-2.8-6.5-6.5s3-6.5 6.5-6.5c1.7 0 3.3.7 4.5 1.8l1-1c.4-.4 1-.1 1 .4v3.8c0 .3-.3.6-.6.6h-3.7c-.5 0-.8-.7-.4-1L11 4.9c-.9-.9-1.9-1.3-3-1.3-3 0-5.4 3.1-4 6.2.7 1.6 2.3 2.6 4 2.6 1.2 0 2.2-.4 3-1.3.2-.2.6-.2.8 0l.7.7z" })));
};
var RotateLeftIcon = function (_a) {
var className = _a.className;
return (React__default["default"].createElement("svg", { className: className, xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16" },
React__default["default"].createElement("path", { fill: "#FFF", d: "M3.5 11.8c-.2.2-.2.6 0 .9 1.1 1.1 2.8 1.8 4.5 1.8 3.7 0 6.5-2.8 6.5-6.5s-3-6.5-6.5-6.5c-1.7 0-3.3.7-4.5 1.8l-1-1c-.4-.4-1-.1-1 .4v3.8c0 .3.3.6.6.6h3.7c.5 0 .8-.7.4-1L5 4.9c.9-.9 1.9-1.3 3-1.3 3 0 5.4 3.1 4 6.2-.7 1.6-2.3 2.6-4 2.6-1.2 0-2.2-.4-3-1.3-.2-.2-.6-.2-.8 0l-.7.7z" })));
};
var FlipVerticalIcon = function (_a) {
var className = _a.className;
return (React__default["default"].createElement("svg", { className: className, xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16" },
React__default["default"].createElement("path", { fill: "#FFF", d: "M15 9H1c-.5 0-1-.5-1-1s.4-1 1-1h14c.6 0 1 .4 1 1s-.5 1-1 1zM3.1 12.7c-.4 0-.8-.3-.8-.8v-.5c0-.4.3-.8.8-.8s.8.3.8.8v.6c0 .4-.3.7-.8.7zM3.6 15.4h-.5c-.4 0-.8-.3-.8-.8v-.5c0-.4.3-.8.8-.8.3 0 .6.2.7.5.3.1.5.4.5.7.1.6-.2.9-.7.9zM10.1 15.4h-.8c-.4 0-.8-.3-.8-.8s.3-.8.8-.8h.8c.4 0 .8.3.8.8s-.4.8-.8.8zm-3.2 0H6c-.4 0-.8-.3-.8-.8s.3-.8.8-.8h.8c.4 0 .8.3.8.8s-.3.8-.7.8zM13 15.4h-.5c-.4 0-.8-.3-.8-.8 0-.3.2-.6.5-.7.1-.3.4-.5.7-.5.4 0 .8.3.8.8v.5c.1.4-.2.7-.7.7zM13 12.7c-.4 0-.8-.3-.8-.8v-.5c0-.4.3-.8.8-.8s.8.3.8.8v.6c0 .4-.3.7-.8.7z" }),
React__default["default"].createElement("g", null,
React__default["default"].createElement("path", { fill: "#FFF", d: "M13.8 4.5h-1.5V2H3.9v2.5H2.4V1.3c0-.4.3-.8.8-.8H13c.4 0 .8.3.8.8v3.2z" }))));
};
function range(from, to, step) {
if (step === void 0) { step = 1; }
var index = -1;
var length = Math.max(Math.ceil((to - from) / (step || 1)), 0);
var result = new Array(length);
while (length--) {
result[++index] = from;
from += step;
}
return result;
}
var RotateComponent = React.forwardRef(function (_a, ref) {
var from = _a.from, to = _a.to, value = _a.value, _b = _a.step, step = _b === void 0 ? 2.5 : _b, _c = _a.thickness, thickness = _c === void 0 ? 2 : _c, onBlur = _a.onBlur, onChange = _a.onChange, className = _a.className, valueBarClassName = _a.valueBarClassName, barsClassName = _a.barsClassName, barClassName = _a.barClassName, highlightedBarClassName = _a.highlightedBarClassName, zeroBarClassName = _a.zeroBarClassName, _d = _a.density, density = _d === void 0 ? 10 : _d;
var barsRef = React.useRef(null);
var _e = tslib.__read(React.useState(false), 2), dragging = _e[0], setDragging = _e[1];
var _f = tslib.__read(React.useState([]), 2), items = _f[0], setItems = _f[1];
var recalculate = function () {
if (barsRef.current) {
var width_1 = barsRef.current.clientWidth;
var count_1 = width_1 / density;
var neededLeftBarsCount = Math.max(0, Math.floor(count_1 / 2) - Math.round((value - from) / step));
var neededRightBarsCount = Math.max(0, Math.floor(count_1 / 2) - Math.round((to - value) / step));
var values = tslib.__spreadArray(tslib.__spreadArray(tslib.__spreadArray([], tslib.__read(range(from - neededLeftBarsCount * step, from, step)), false), tslib.__read(range(from, to + step, step)), false), tslib.__read(range(to + step, to + step + neededRightBarsCount * step, step)), false);
var radius_1 = Math.abs(Math.ceil(count_1 / 2) * step);
setItems(values.map(function (barValue) {
var sign = Math.sign(barValue - value);
// Opacity
var translate;
if (Math.abs(barValue - value) / step <= Math.ceil(count_1 / 2)) {
var multiplier = Math.sqrt(Math.pow(radius_1, 2) - Math.pow(value + sign * radius_1 - barValue, 2)) / radius_1;
translate = width_1 / 2 + sign * (width_1 / 2) * Math.pow(multiplier, 2.5);
}
else {
translate = width_1 / 2 + (sign * width_1) / 2;
}
// Translate
var opacity = 0;
if (count_1 > 0 && Math.abs(barValue - value) / step <= Math.ceil(count_1 / 2)) {
opacity = Math.pow(Math.sqrt(Math.pow(radius_1, 2) - Math.pow(value - barValue, 2)) / radius_1, 4);
}
return {
value: barValue,
highlighted: (value < 0 && barValue >= value && barValue <= 0) ||
(value > 0 && barValue <= value && barValue >= 0),
zero: barValue === 0,
opacity: opacity,
translate: translate - thickness / 2,
};
}));
}
};
React.useEffect(function () {
recalculate();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [density, thickness, from, to, value, step]);
React.useImperativeHandle(ref, function () {
return {
refresh: recalculate,
};
});
var onMove = function (directions) {
if (barsRef.current) {
var width = barsRef.current.clientWidth;
var count = width / density;
var shift = -(directions.left / barsRef.current.clientWidth) * count * step;
if (onChange) {
if (value + shift > to) {
onChange(to - value);
}
else if (value + shift < from) {
onChange(from - value);
}
else {
onChange(shift);
}
}
}
};
var onMoveEnd = function () {
document.body.classList.remove('dragging');
setDragging(false);
onBlur === null || onBlur === void 0 ? void 0 : onBlur();
};
var onMoveStart = function () {
document.body.classList.add('dragging');
setDragging(true);
};
return (React__default["default"].createElement("div", { className: cn__default["default"]('rmc-rotate-component', className) },
React__default["default"].createElement(reactAdvancedCropper.DraggableArea, { onMoveStart: onMoveStart, onMove: onMove, onMoveEnd: onMoveEnd, useAnchor: false },
React__default["default"].createElement("div", { className: cn__default["default"]('rmc-rotate-component__bars', dragging && 'rmc-rotate-component__bars--dragging', barsClassName), ref: barsRef },
items.map(function (bar) { return (React__default["default"].createElement("div", { className: cn__default["default"]('rmc-rotate-component__bar', bar.zero && 'rmc-rotate-component__bar--zero', bar.highlighted && 'rmc-rotate-component__bar--highlighted', barClassName, bar.highlighted && highlightedBarClassName, bar.zero && zeroBarClassName), key: bar.value, style: {
width: bar.opacity ? thickness : 0,
opacity: bar.opacity,
transform: "translate(" + bar.translate + "px, -50%)",
} })); }),
React__default["default"].createElement("div", { className: cn__default["default"]('rmc-rotate-component__value', valueBarClassName) },
React__default["default"].createElement("div", { className: "rmc-rotate-component__value-number" },
value.toFixed(1),
"\u00B0"))))));
});
RotateComponent.displayName = 'RotateComponent';
var Navigation = React.forwardRef(function (_a, ref) {
var className = _a.className, buttonClassName = _a.buttonClassName, rotateComponentClassName = _a.rotateComponentClassName, barClassName = _a.barClassName, highlightedBarClassName = _a.highlightedBarClassName, zeroBarClassName = _a.zeroBarClassName, valueBarClassName = _a.valueBarClassName, disabled = _a.disabled, value = _a.value, onRotate = _a.onRotate, onRotateEnd = _a.onRotateEnd, onFlip = _a.onFlip;
var _b = tslib.__read(React.useState(0), 2), quarter = _b[0], setQuarter = _b[1];
var _c = tslib.__read(React.useState(0), 2), adjustmentAngle = _c[0], setAdjustmentAngle = _c[1];
var rotateComponentRef = React.useRef(null);
React.useLayoutEffect(function () {
var absRotate = Math.abs(value);
var rotate;
if (absRotate % 90 > 45) {
rotate = (absRotate - (absRotate % 90) + 90) / 90;
}
else if (absRotate % 90 < 45) {
rotate = (absRotate - (absRotate % 90)) / 90;
}
else {
rotate = quarter;
}
rotate = Math.sign(rotate) * rotate;
if (rotate !== quarter) {
setQuarter(rotate);
}
setAdjustmentAngle(Math.sign(value) * (Math.abs(value) - Math.abs(rotate) * 90));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [value]);
React.useImperativeHandle(ref, function () {
return {
refresh: function () {
if (rotateComponentRef.current) {
rotateComponentRef.current.refresh();
}
},
};
});
var rotateTo = function (angle) {
if (onRotate && !disabled) {
onRotate(angle, {
transitions: false,
interaction: true,
immediately: true,
});
}
};
var rotateLeft = function () {
if (onRotate && !disabled) {
if (adjustmentAngle > 0) {
onRotate(-adjustmentAngle);
}
else if (adjustmentAngle < 0) {
onRotate(-90 - adjustmentAngle);
}
else {
onRotate(-90);
}
}
};
var rotateRight = function () {
if (onRotate && !disabled) {
if (adjustmentAngle > 0) {
onRotate(90 - adjustmentAngle);
}
else if (adjustmentAngle < 0) {
onRotate(-adjustmentAngle);
}
else {
onRotate(90);
}
}
};
var flipHorizontal = function () {
if (onFlip && !disabled) {
onFlip(true);
}
};
var flipVertical = function () {
if (onFlip && !disabled) {
onFlip(false, true);
}
};
return (React__default["default"].createElement("div", { className: cn__default["default"]('rmc-navigation', className) },
React__default["default"].createElement("button", { type: "button", className: cn__default["default"]('rmc-navigation__button', buttonClassName), onClick: flipHorizontal },
React__default["default"].createElement(FlipHorizontalIcon, null)),
React__default["default"].createElement("button", { type: "button", className: cn__default["default"]('rmc-navigation__button', buttonClassName), onClick: rotateRight },
React__default["default"].createElement(RotateRightIcon, null)),
React__default["default"].createElement(RotateComponent, { ref: rotateComponentRef, className: cn__default["default"]('rmc-navigation__rotator', rotateComponentClassName), barClassName: barClassName, zeroBarClassName: zeroBarClassName, valueBarClassName: valueBarClassName, highlightedBarClassName: highlightedBarClassName, onChange: rotateTo, onBlur: onRotateEnd, from: -45, to: 45, value: adjustmentAngle }),
React__default["default"].createElement("button", { type: "button", className: cn__default["default"]('rmc-navigation__button', buttonClassName), onClick: rotateLeft },
React__default["default"].createElement(RotateLeftIcon, null)),
React__default["default"].createElement("button", { type: "button", className: cn__default["default"]('rmc-navigation__button', buttonClassName), onClick: flipVertical },
React__default["default"].createElement(FlipVerticalIcon, null))));
});
Navigation.displayName = 'Navigation';
var Spinner = function (_a) {
var className = _a.className;
return (React__default["default"].createElement("svg", { className: className, width: "38", height: "38", viewBox: "0 0 38 38", xmlns: "http://www.w3.org/2000/svg" },
React__default["default"].createElement("g", { fill: "none", fillRule: "evenodd" },
React__default["default"].createElement("g", { transform: "translate(1 1)", strokeWidth: "2" },
React__default["default"].createElement("circle", { strokeOpacity: ".5", cx: "18", cy: "18", r: "18" }),
React__default["default"].createElement("path", { d: "M36 18c0-9.94-8.06-18-18-18" },
React__default["default"].createElement("animateTransform", { attributeName: "transform", type: "rotate", from: "0 18 18", to: "360 18 18", dur: "1s", repeatCount: "indefinite" }))))));
};
var CropperWrapper = function (_a) {
var cropper = _a.cropper, children = _a.children, loaded = _a.loaded, loading = _a.loading, className = _a.className, spinnerClassName = _a.spinnerClassName, navigation = _a.navigation, _b = _a.navigationProps, navigationProps = _b === void 0 ? {} : _b;
var navigationRef = React.useRef(null);
var state = cropper.getState();
var transitions = cropper.getTransitions();
var rotate = cropper.getTransforms().rotate;
React.useEffect(function () {
var _a;
(_a = navigationRef.current) === null || _a === void 0 ? void 0 : _a.refresh();
}, [state === null || state === void 0 ? void 0 : state.boundary.width, state === null || state === void 0 ? void 0 : state.boundary.height]);
return (React__default["default"].createElement("div", { className: cn__default["default"]('rmc-cropper-wrapper', navigation && 'rmc-cropper-wrapper--with-navigation', className) },
React__default["default"].createElement(reactAdvancedCropper.CropperFade, { className: 'rmc-cropper-wrapper__fade', visible: loaded },
children,
navigation && (React__default["default"].createElement(Navigation, { ref: navigationRef, value: rotate, onRotate: cropper.rotateImage, onRotateEnd: cropper.transformImageEnd, onFlip: cropper.flipImage, className: cn__default["default"]('rmc-cropper-wrapper__navigation', navigationProps.className), buttonClassName: navigationProps.buttonClassName, barClassName: navigationProps.barClassName, valueBarClassName: navigationProps.valueBarClassName, zeroBarClassName: navigationProps.zeroBarClassName, highlightedBarClassName: navigationProps.highlightedBarClassName, disabled: transitions.active }))),
React__default["default"].createElement(Spinner, { className: cn__default["default"]('rmc-cropper-wrapper__spinner', loading && 'rmc-cropper-wrapper__spinner--visible', spinnerClassName) })));
};
var Cropper = React.forwardRef(function (props, ref) {
var className = props.className, spinnerClassName = props.spinnerClassName, _a = props.navigation, navigation = _a === void 0 ? true : _a, _b = props.stencilProps, stencilProps = _b === void 0 ? {} : _b, _c = props.navigationProps, navigationProps = _c === void 0 ? {} : _c, wrapperComponent = props.wrapperComponent, _d = props.imageRestriction, imageRestriction = _d === void 0 ? reactAdvancedCropper.ImageRestriction.stencil : _d, cropperProps = tslib.__rest(props, ["className", "spinnerClassName", "navigation", "stencilProps", "navigationProps", "wrapperComponent", "imageRestriction"]);
var cropperRef = React.useRef(null);
var WrapperComponent = wrapperComponent || CropperWrapper;
return (React__default["default"].createElement(reactAdvancedCropper.Cropper, tslib.__assign({}, cropperProps, { ref: reactAdvancedCropper.mergeRefs([ref, cropperRef]), stencilConstraints: mobile.stencilConstraints, stencilProps: tslib.__assign(tslib.__assign({ grid: true }, stencilProps), { movable: false }), wrapperComponent: WrapperComponent, wrapperProps: {
navigationProps: navigationProps,
navigation: navigation,
spinnerClassName: spinnerClassName,
}, imageRestriction: reactAdvancedCropper.ImageRestriction.none, className: cn__default["default"]('rmc-cropper', className), defaultSize: mobile.defaultSize, transformImageAlgorithm: mobile.transformImage, transitions: true, postProcess: imageRestriction === reactAdvancedCropper.ImageRestriction.none ? mobile.zoomStencil : [mobile.fitStencilToImage, mobile.zoomStencil], resizeCoordinatesAlgorithm: imageRestriction === reactAdvancedCropper.ImageRestriction.none ? undefined : mobile.resizeCoordinates })));
});
Cropper.displayName = 'Cropper';
Object.defineProperty(exports, 'CircleStencil', {
enumerable: true,
get: function () { return reactAdvancedCropper.CircleStencil; }
});
Object.defineProperty(exports, 'RectangleStencil', {
enumerable: true,
get: function () { return reactAdvancedCropper.RectangleStencil; }
});
exports.Cropper = Cropper;
exports.Navigation = Navigation;
exports.RotateComponent = RotateComponent;
//# sourceMappingURL=index.cjs.js.map