UNPKG

react-mobile-cropper

Version:

The react mobile cropper inspired by Android mobile croppers

268 lines (257 loc) 17.3 kB
import { DraggableArea, CropperFade, ImageRestriction, Cropper as Cropper$1, mergeRefs } from 'react-advanced-cropper'; export { CircleStencil, RectangleStencil } from 'react-advanced-cropper'; import { __read, __spreadArray, __rest, __assign } from 'tslib'; import React, { forwardRef, useRef, useState, useEffect, useImperativeHandle, useLayoutEffect } from 'react'; import cn from 'classnames'; import { stencilConstraints, defaultSize, transformImage, zoomStencil, fitStencilToImage, resizeCoordinates } from 'advanced-cropper/showcase/mobile'; var FlipHorizontalIcon = function (_a) { var className = _a.className; return (React.createElement("svg", { className: className, xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16" }, React.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.createElement("svg", { className: className, xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16" }, React.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.createElement("svg", { className: className, xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16" }, React.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.createElement("svg", { className: className, xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16" }, React.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.createElement("g", null, React.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 = 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 = useRef(null); var _e = __read(useState(false), 2), dragging = _e[0], setDragging = _e[1]; var _f = __read(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 = __spreadArray(__spreadArray(__spreadArray([], __read(range(from - neededLeftBarsCount * step, from, step)), false), __read(range(from, to + step, step)), false), __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, }; })); } }; useEffect(function () { recalculate(); // eslint-disable-next-line react-hooks/exhaustive-deps }, [density, thickness, from, to, value, step]); 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.createElement("div", { className: cn('rmc-rotate-component', className) }, React.createElement(DraggableArea, { onMoveStart: onMoveStart, onMove: onMove, onMoveEnd: onMoveEnd, useAnchor: false }, React.createElement("div", { className: cn('rmc-rotate-component__bars', dragging && 'rmc-rotate-component__bars--dragging', barsClassName), ref: barsRef }, items.map(function (bar) { return (React.createElement("div", { className: cn('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.createElement("div", { className: cn('rmc-rotate-component__value', valueBarClassName) }, React.createElement("div", { className: "rmc-rotate-component__value-number" }, value.toFixed(1), "\u00B0")))))); }); RotateComponent.displayName = 'RotateComponent'; var Navigation = 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 = __read(useState(0), 2), quarter = _b[0], setQuarter = _b[1]; var _c = __read(useState(0), 2), adjustmentAngle = _c[0], setAdjustmentAngle = _c[1]; var rotateComponentRef = useRef(null); 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]); 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.createElement("div", { className: cn('rmc-navigation', className) }, React.createElement("button", { type: "button", className: cn('rmc-navigation__button', buttonClassName), onClick: flipHorizontal }, React.createElement(FlipHorizontalIcon, null)), React.createElement("button", { type: "button", className: cn('rmc-navigation__button', buttonClassName), onClick: rotateRight }, React.createElement(RotateRightIcon, null)), React.createElement(RotateComponent, { ref: rotateComponentRef, className: cn('rmc-navigation__rotator', rotateComponentClassName), barClassName: barClassName, zeroBarClassName: zeroBarClassName, valueBarClassName: valueBarClassName, highlightedBarClassName: highlightedBarClassName, onChange: rotateTo, onBlur: onRotateEnd, from: -45, to: 45, value: adjustmentAngle }), React.createElement("button", { type: "button", className: cn('rmc-navigation__button', buttonClassName), onClick: rotateLeft }, React.createElement(RotateLeftIcon, null)), React.createElement("button", { type: "button", className: cn('rmc-navigation__button', buttonClassName), onClick: flipVertical }, React.createElement(FlipVerticalIcon, null)))); }); Navigation.displayName = 'Navigation'; var Spinner = function (_a) { var className = _a.className; return (React.createElement("svg", { className: className, width: "38", height: "38", viewBox: "0 0 38 38", xmlns: "http://www.w3.org/2000/svg" }, React.createElement("g", { fill: "none", fillRule: "evenodd" }, React.createElement("g", { transform: "translate(1 1)", strokeWidth: "2" }, React.createElement("circle", { strokeOpacity: ".5", cx: "18", cy: "18", r: "18" }), React.createElement("path", { d: "M36 18c0-9.94-8.06-18-18-18" }, React.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 = useRef(null); var state = cropper.getState(); var transitions = cropper.getTransitions(); var rotate = cropper.getTransforms().rotate; 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.createElement("div", { className: cn('rmc-cropper-wrapper', navigation && 'rmc-cropper-wrapper--with-navigation', className) }, React.createElement(CropperFade, { className: 'rmc-cropper-wrapper__fade', visible: loaded }, children, navigation && (React.createElement(Navigation, { ref: navigationRef, value: rotate, onRotate: cropper.rotateImage, onRotateEnd: cropper.transformImageEnd, onFlip: cropper.flipImage, className: cn('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.createElement(Spinner, { className: cn('rmc-cropper-wrapper__spinner', loading && 'rmc-cropper-wrapper__spinner--visible', spinnerClassName) }))); }; var Cropper = 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 ? ImageRestriction.stencil : _d, cropperProps = __rest(props, ["className", "spinnerClassName", "navigation", "stencilProps", "navigationProps", "wrapperComponent", "imageRestriction"]); var cropperRef = useRef(null); var WrapperComponent = wrapperComponent || CropperWrapper; return (React.createElement(Cropper$1, __assign({}, cropperProps, { ref: mergeRefs([ref, cropperRef]), stencilConstraints: stencilConstraints, stencilProps: __assign(__assign({ grid: true }, stencilProps), { movable: false }), wrapperComponent: WrapperComponent, wrapperProps: { navigationProps: navigationProps, navigation: navigation, spinnerClassName: spinnerClassName, }, imageRestriction: ImageRestriction.none, className: cn('rmc-cropper', className), defaultSize: defaultSize, transformImageAlgorithm: transformImage, transitions: true, postProcess: imageRestriction === ImageRestriction.none ? zoomStencil : [fitStencilToImage, zoomStencil], resizeCoordinatesAlgorithm: imageRestriction === ImageRestriction.none ? undefined : resizeCoordinates }))); }); Cropper.displayName = 'Cropper'; export { Cropper, Navigation, RotateComponent }; //# sourceMappingURL=index.esm-bundler.js.map