UNPKG

@bigfishtv/cockpit

Version:

396 lines (337 loc) 13 kB
var _class, _temp; function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } import PropTypes from 'prop-types'; import React, { Component } from 'react'; import ReactDOM from 'react-dom'; import throttle from 'lodash/throttle'; import { isShiftKeyPressed } from '../../utils/selectKeyUtils'; import Icon from '../Icon'; var ApplyButton = function ApplyButton(props) { return React.createElement( 'button', { className: 'button-secondary button-icon', onClick: props.onApply }, React.createElement(Icon, { name: 'tick' }) ); }; var defaultCrop = { top: 0, left: 0, width: 1, height: 1 }; var Cropper = (_temp = _class = function (_Component) { _inherits(Cropper, _Component); function Cropper(props) { _classCallCheck(this, Cropper); var _this = _possibleConstructorReturn(this, _Component.call(this)); _this.handleResize = function () { var offset = ReactDOM.findDOMNode(_this.refs.cropper).getBoundingClientRect(); _this.offsetTop = offset.top; _this.offsetLeft = offset.left; }; _this.moveStart = function (event) { var _this$state = _this.state, cropL = _this$state.cropL, cropT = _this$state.cropT; _this.dragX = _this.clientX - _this.offsetLeft - cropL; _this.dragY = _this.clientY - _this.offsetTop - cropT; _this.setState({ dragging: true }); }; _this.handleApply = function () { var _this$props = _this.props, width = _this$props.width, height = _this$props.height; var _this$state2 = _this.state, cropL = _this$state2.cropL, cropT = _this$state2.cropT, cropW = _this$state2.cropW, cropH = _this$state2.cropH; _this.props.onChange({ left: cropL / width, top: cropT / height, width: cropW / width, height: cropH / height }); _this.props.onApply(); }; var crop = props.defaultCrop || defaultCrop; _this.state = { dragging: false, resizing: false, cropL: crop.left * props.width, cropT: crop.top * props.height, cropW: crop.width * props.width, cropH: crop.height * props.height }; _this.cropType = null; _this.offsetTop = _this.offsetLeft = _this.clientX = _this.clientY = _this.dragX = _this.dragY = _this.origCropL = _this.origCropT = 0; _this.origCropW = props.width; _this.origCropH = props.height; _this.resizeRatio = 1; _this.handleMouseUp = _this.handleMouseUp.bind(_this); _this.handleMouseMove = _this.handleMouseMove.bind(_this); _this.handleMouseMove = throttle(_this.handleMouseMove, 1000 / 60); _this.handleResize = _this.handleResize.bind(_this); _this.handleResize = throttle(_this.handleResize, 200); return _this; } Cropper.prototype.componentDidMount = function componentDidMount() { this.refs.omg.setAttribute('fill-rule', 'evenodd'); // wtf react window.addEventListener('mouseup', this.handleMouseUp); window.addEventListener('mousemove', this.handleMouseMove); window.addEventListener('resize', this.handleResize); this.handleResize(); }; Cropper.prototype.componentWillUnmount = function componentWillUnmount() { window.removeEventListener('mouseup', this.handleMouseUp); window.removeEventListener('mousemove', this.handleMouseMove); window.removeEventListener('resize', this.handleResize); }; Cropper.prototype.componentWillReceiveProps = function componentWillReceiveProps(nextProps) { if (nextProps.fixedRatio !== this.props.fixedRatio && nextProps.fixedRatio !== null) { this.updateCrop(nextProps, true); } }; /** * called on window resize to position correctly against 'BoundingClientRect' */ /** * called by onMouseDown of any cropping edge/corner * @param {MouseEvent} event passed from onMouseDown of cropping edge/corner * @param {String} cropType type of crop, any of the following: TL, TM, TR, ML, MR, BL, BM, BR */ Cropper.prototype.cropStart = function cropStart(event, cropType) { event.stopPropagation(); var _state = this.state, cropW = _state.cropW, cropH = _state.cropH, cropL = _state.cropL, cropT = _state.cropT; this.cropType = cropType; this.resizeRatio = cropW / cropH; this.origCropW = cropW; this.origCropH = cropH; this.origCropL = cropL; this.origCropT = cropT; this.setState({ resizing: true }); }; /** * called by onMouseDown of the cropping area * @param {MouseEvent} event passed on from onMouseDown of cropping area */ /** * sets dragging and resizing state to false */ Cropper.prototype.handleMouseUp = function handleMouseUp() { this.setState({ dragging: false, resizing: false }); }; /** * called onMouseMove, updates mouse position vars (is throttled in constructor) * @param {MouseEvent} event */ Cropper.prototype.handleMouseMove = function handleMouseMove(event) { this.clientX = event.clientX; this.clientY = event.clientY; this.updateCrop(); }; /** * called onClick of Apply button, calls onChange prop with new crop object */ /** * called either from handleMouseMove or from componentWillReceiveProps (if fixed ratio has changed) * @param {Object} props * @param {Number} props.width image width * @param {Number} props.height image height * @param {Number} props.fixedRatio fixed ratio, either null or a ratio converted to decimal (e.g 3:2 = 1.5) * @param {Boolean} forceResize determines if a resize needs to be forced, typically on fixed ratio change */ Cropper.prototype.updateCrop = function updateCrop() { var props = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.props; var forceResize = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; var _state2 = this.state, dragging = _state2.dragging, resizing = _state2.resizing; var width = props.width, height = props.height, fixedRatio = props.fixedRatio; var mouseX = this.clientX - this.offsetLeft; var mouseY = this.clientY - this.offsetTop; mouseX = mouseX < 0 ? 0 : mouseX > width ? width : mouseX; mouseY = mouseY < 0 ? 0 : mouseY > height ? height : mouseY; if (dragging) { var _state3 = this.state, cropW = _state3.cropW, cropH = _state3.cropH; var cropL = mouseX - this.dragX; var cropT = mouseY - this.dragY; cropL = cropL < 0 ? 0 : cropL > width - cropW ? width - cropW : cropL; cropT = cropT < 0 ? 0 : cropT > height - cropH ? height - cropH : cropT; this.setState({ cropL: cropL, cropT: cropT }); this.props.onChange({ left: cropL / width, top: cropT / height, width: this.state.cropW / width, height: this.state.cropH / height }); } else if (resizing || forceResize) { var _state4 = this.state, _cropW = _state4.cropW, _cropH = _state4.cropH, _cropL = _state4.cropL, _cropT = _state4.cropT; var cmd = this.cropType || 'TL'; if (isShiftKeyPressed() || fixedRatio) { var ratio = fixedRatio || this.resizeRatio; if (cmd[0] !== 'M') { var diffX = 0; var diffY = 0; if (cmd[0] == 'T') { diffX = _cropL + _cropW - mouseX; diffY = _cropT + _cropH - mouseY; } else { diffX = mouseX - _cropL; diffY = mouseY - _cropT; } if (diffX > diffY) { _cropH = diffX / ratio; _cropW = diffX; } else { _cropW = diffY * ratio; _cropH = diffY; } if (cmd[0] == 'T') { _cropT = this.origCropT - (_cropH - this.origCropH); if (mouseY > _cropT + _cropH) this.cropType = 'B' + cmd[1]; } else { if (mouseY < _cropT) this.cropType = 'T' + cmd[1]; } if (cmd[1] == 'L') { _cropL = this.origCropL - (_cropW - this.origCropW); if (mouseX > _cropL + _cropW) this.cropType = cmd[0] + 'R'; } else { if (mouseX < _cropL) this.cropType = cmd[0] + 'L'; } if (_cropT < 0) _cropT = 0; if (_cropL < 0) _cropL = 0; if (_cropT + _cropH > height) { _cropH = height - _cropT; _cropW = _cropH * ratio; } if (_cropL + _cropW > width) { _cropW = width - _cropL; _cropH = _cropW / ratio; } } } else { if (cmd[1] == 'L') { _cropW = _cropW + (_cropL - mouseX); _cropL = mouseX; } else if (cmd[1] == 'R') { _cropW = mouseX - _cropL; } if (cmd[0] == 'T') { _cropH = _cropH + (_cropT - mouseY); _cropT = mouseY; } else if (cmd[0] == 'B') { _cropH = mouseY - _cropT; } } // check if cropping is going back on itself and adjust accordingly if (_cropW < 0) { _cropL += _cropW; _cropW = Math.abs(_cropW); this.cropType = cmd[0] + (cmd[1] == 'L' ? 'R' : 'L'); } if (_cropH < 0) { _cropT += _cropH; _cropH = Math.abs(_cropH); this.cropType = (cmd[0] == 'T' ? 'B' : 'T') + cmd[1]; } this.setState({ cropL: _cropL, cropT: _cropT, cropW: _cropW, cropH: _cropH }); this.props.onChange({ left: _cropL / width, top: _cropT / height, width: _cropW / width, height: _cropH / height }); } }; Cropper.prototype.render = function render() { var _this2 = this; var _props = this.props, width = _props.width, height = _props.height, ApplyButton = _props.ApplyButton; var _state5 = this.state, cropL = _state5.cropL, cropT = _state5.cropT, cropW = _state5.cropW, cropH = _state5.cropH; var path = 'M0 0 H ' + width + ' V ' + height + ' H 0 ZM' + cropL + ' ' + cropT + ' H ' + (cropL + cropW) + ' V ' + (cropT + cropH) + ' H ' + cropL + ' V ' + cropH + 'Z'; var cropStyle = { left: cropL, top: cropT, width: cropW, height: cropH }; return React.createElement( 'div', { className: 'cropper', style: { width: width, height: height }, ref: 'cropper' }, React.createElement( 'div', { className: 'crop-zone', style: cropStyle, onMouseDown: this.moveStart }, React.createElement('div', { className: 'crop-handle TL', onMouseDown: function onMouseDown(e) { return _this2.cropStart(e, 'TL'); } }), React.createElement('div', { className: 'crop-handle TM', onMouseDown: function onMouseDown(e) { return _this2.cropStart(e, 'TM'); } }), React.createElement('div', { className: 'crop-handle TR', onMouseDown: function onMouseDown(e) { return _this2.cropStart(e, 'TR'); } }), React.createElement('div', { className: 'crop-handle ML', onMouseDown: function onMouseDown(e) { return _this2.cropStart(e, 'ML'); } }), React.createElement('div', { className: 'crop-handle MR', onMouseDown: function onMouseDown(e) { return _this2.cropStart(e, 'MR'); } }), React.createElement('div', { className: 'crop-handle BL', onMouseDown: function onMouseDown(e) { return _this2.cropStart(e, 'BL'); } }), React.createElement('div', { className: 'crop-handle BM', onMouseDown: function onMouseDown(e) { return _this2.cropStart(e, 'BM'); } }), React.createElement('div', { className: 'crop-handle BR', onMouseDown: function onMouseDown(e) { return _this2.cropStart(e, 'BR'); } }), ApplyButton && React.createElement(ApplyButton, { onApply: this.handleApply }) ), React.createElement( 'svg', { width: width, height: height, viewBox: '0 0 ' + width + ' ' + height }, React.createElement('path', { d: path, fill: 'rgba(0,0,0,0.5)', ref: 'omg' }) ) ); }; return Cropper; }(Component), _class.propTypes = { /** Image width */ width: PropTypes.number, /** Image height */ height: PropTypes.number, /** Image ratio, either null for free-crop or a ratio converted to a decimal (e.g. 3:2 = 1.5) */ fixedRatio: PropTypes.number, /** Called on live resize */ onChange: PropTypes.func, /** Only called when apply button is pressed */ onApply: PropTypes.func, /** Object containing number perchanges for: top, left, width, height. Ranges from 0 to 1 */ defaultCrop: PropTypes.object, /** React component for the Apply button */ ApplyButton: PropTypes.func }, _class.defaultProps = { width: 640, height: 480, fixedRatio: null, onChange: function onChange() { return false; }, onApply: function onApply() { return false; }, defaultCrop: defaultCrop, ApplyButton: ApplyButton }, _temp); export { Cropper as default };