@bigfishtv/cockpit
Version:
396 lines (337 loc) • 13 kB
JavaScript
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 };