@wordpress/components
Version:
UI components for WordPress.
393 lines (336 loc) • 10.5 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = exports.FocalPointPicker = void 0;
var _element = require("@wordpress/element");
var _classnames = _interopRequireDefault(require("classnames"));
var _i18n = require("@wordpress/i18n");
var _compose = require("@wordpress/compose");
var _keycodes = require("@wordpress/keycodes");
var _baseControl = _interopRequireDefault(require("../base-control"));
var _controls = _interopRequireDefault(require("./controls"));
var _focalPoint = _interopRequireDefault(require("./focal-point"));
var _grid = _interopRequireDefault(require("./grid"));
var _media = _interopRequireDefault(require("./media"));
var _focalPointPickerStyle = require("./styles/focal-point-picker-style");
var _math = require("../utils/math");
var _utils = require("./utils");
/**
* External dependencies
*/
/**
* WordPress dependencies
*/
/**
* Internal dependencies
*/
class FocalPointPicker extends _element.Component {
constructor(props) {
super(...arguments);
this.state = {
isDragging: false,
bounds: _utils.INITIAL_BOUNDS,
percentages: props.value
};
this.containerRef = (0, _element.createRef)();
this.mediaRef = (0, _element.createRef)();
this.onMouseDown = this.startDrag.bind(this);
this.onMouseUp = this.stopDrag.bind(this);
this.onKeyDown = this.onKeyDown.bind(this);
this.onMouseMove = this.doDrag.bind(this);
this.ifDraggingStop = () => {
if (this.state.isDragging) {
this.stopDrag();
}
};
this.onChangeAtControls = value => {
this.updateValue(value);
this.props.onChange(value);
};
this.updateBounds = this.updateBounds.bind(this);
this.updateValue = this.updateValue.bind(this);
}
componentDidMount() {
const {
defaultView
} = this.containerRef.current.ownerDocument;
defaultView.addEventListener('resize', this.updateBounds);
/*
* Set initial bound values.
*
* This is necessary for Safari:
* https://github.com/WordPress/gutenberg/issues/25814
*/
this.updateBounds();
}
componentDidUpdate(prevProps) {
if (prevProps.url !== this.props.url) {
this.ifDraggingStop();
}
/*
* Handles cases where the incoming value changes.
* An example is the values resetting based on an UNDO action.
*/
const {
isDragging,
percentages: {
x,
y
}
} = this.state;
const {
value
} = this.props;
if (!isDragging && (value.x !== x || value.y !== y)) {
this.setState({
percentages: this.props.value
});
}
}
componentWillUnmount() {
const {
defaultView
} = this.containerRef.current.ownerDocument;
defaultView.removeEventListener('resize', this.updateBounds);
this.ifDraggingStop();
}
calculateBounds() {
const bounds = _utils.INITIAL_BOUNDS;
if (!this.mediaRef.current) {
return bounds;
} // Prevent division by zero when updateBounds runs in componentDidMount
if (this.mediaRef.current.clientWidth === 0 || this.mediaRef.current.clientHeight === 0) {
return bounds;
}
const dimensions = {
width: this.mediaRef.current.clientWidth,
height: this.mediaRef.current.clientHeight
};
const pickerDimensions = this.pickerDimensions();
const widthRatio = pickerDimensions.width / dimensions.width;
const heightRatio = pickerDimensions.height / dimensions.height;
if (heightRatio >= widthRatio) {
bounds.width = bounds.right = pickerDimensions.width;
bounds.height = dimensions.height * widthRatio;
bounds.top = (pickerDimensions.height - bounds.height) / 2;
bounds.bottom = bounds.top + bounds.height;
} else {
bounds.height = bounds.bottom = pickerDimensions.height;
bounds.width = dimensions.width * heightRatio;
bounds.left = (pickerDimensions.width - bounds.width) / 2;
bounds.right = bounds.left + bounds.width;
}
return bounds;
}
updateValue(nextValue = {}) {
const {
x,
y
} = nextValue;
const nextPercentage = {
x: parseFloat(x).toFixed(2),
y: parseFloat(y).toFixed(2)
};
this.setState({
percentages: nextPercentage
});
}
updateBounds() {
this.setState({
bounds: this.calculateBounds()
});
}
startDrag(event) {
var _this$props$onDragSta, _this$props;
event.persist();
this.containerRef.current.focus();
this.setState({
isDragging: true
});
const {
ownerDocument
} = this.containerRef.current;
ownerDocument.addEventListener('mouseup', this.onMouseUp);
ownerDocument.addEventListener('mousemove', this.onMouseMove);
const value = this.getValueFromPoint({
x: event.pageX,
y: event.pageY
}, event.shiftKey);
this.updateValue(value);
(_this$props$onDragSta = (_this$props = this.props).onDragStart) === null || _this$props$onDragSta === void 0 ? void 0 : _this$props$onDragSta.call(_this$props, value, event);
}
stopDrag(event) {
var _this$props$onDragEnd, _this$props2;
const {
ownerDocument
} = this.containerRef.current;
ownerDocument.removeEventListener('mouseup', this.onMouseUp);
ownerDocument.removeEventListener('mousemove', this.onMouseMove);
this.setState({
isDragging: false
}, () => {
this.props.onChange(this.state.percentages);
});
(_this$props$onDragEnd = (_this$props2 = this.props).onDragEnd) === null || _this$props$onDragEnd === void 0 ? void 0 : _this$props$onDragEnd.call(_this$props2, event);
}
onKeyDown(event) {
const {
keyCode,
shiftKey
} = event;
if (![_keycodes.UP, _keycodes.DOWN, _keycodes.LEFT, _keycodes.RIGHT].includes(keyCode)) return;
event.preventDefault();
const next = { ...this.state.percentages
};
const step = shiftKey ? 0.1 : 0.01;
const delta = keyCode === _keycodes.UP || keyCode === _keycodes.LEFT ? -1 * step : step;
const axis = keyCode === _keycodes.UP || keyCode === _keycodes.DOWN ? 'y' : 'x';
const value = parseFloat(next[axis]) + delta;
next[axis] = (0, _math.roundClamp)(value, 0, 1, step);
this.updateValue(next);
this.props.onChange(next);
}
doDrag(event) {
var _this$props$onDrag, _this$props3;
// Prevents text-selection when dragging.
event.preventDefault();
const value = this.getValueFromPoint({
x: event.pageX,
y: event.pageY
}, event.shiftKey);
this.updateValue(value);
(_this$props$onDrag = (_this$props3 = this.props).onDrag) === null || _this$props$onDrag === void 0 ? void 0 : _this$props$onDrag.call(_this$props3, value, event);
}
getValueFromPoint(point, byTenths) {
const {
bounds
} = this.state;
const pickerDimensions = this.pickerDimensions();
const relativePoint = {
left: point.x - pickerDimensions.left,
top: point.y - pickerDimensions.top
};
const left = Math.max(bounds.left, Math.min(relativePoint.left, bounds.right));
const top = Math.max(bounds.top, Math.min(relativePoint.top, bounds.bottom));
let nextX = (left - bounds.left) / (pickerDimensions.width - bounds.left * 2);
let nextY = (top - bounds.top) / (pickerDimensions.height - bounds.top * 2); // Enables holding shift to jump values by 10%
const step = byTenths ? 0.1 : 0.01;
nextX = (0, _math.roundClamp)(nextX, 0, 1, step);
nextY = (0, _math.roundClamp)(nextY, 0, 1, step);
return {
x: nextX,
y: nextY
};
}
pickerDimensions() {
const containerNode = this.containerRef.current;
if (!containerNode) {
return {
width: 0,
height: 0,
left: 0,
top: 0
};
}
const {
clientHeight,
clientWidth
} = containerNode;
const {
top,
left
} = containerNode.getBoundingClientRect();
return {
width: clientWidth,
height: clientHeight,
top: top + document.body.scrollTop,
left
};
}
iconCoordinates() {
const {
bounds,
percentages: {
x,
y
}
} = this.state;
if (bounds.left === undefined || bounds.top === undefined) {
return {
left: '50%',
top: '50%'
};
}
const {
width,
height
} = this.pickerDimensions();
return {
left: x * (width - bounds.left * 2) + bounds.left,
top: y * (height - bounds.top * 2) + bounds.top
};
}
render() {
const {
autoPlay,
className,
help,
instanceId,
label,
url
} = this.props;
const {
bounds,
isDragging,
percentages
} = this.state;
const iconCoordinates = this.iconCoordinates();
const classes = (0, _classnames.default)('components-focal-point-picker-control', className);
const id = `inspector-focal-point-picker-control-${instanceId}`;
return (0, _element.createElement)(_baseControl.default, {
label: label,
id: id,
help: help,
className: classes
}, (0, _element.createElement)(_focalPointPickerStyle.MediaWrapper, {
className: "components-focal-point-picker-wrapper"
}, (0, _element.createElement)(_focalPointPickerStyle.MediaContainer, {
className: "components-focal-point-picker",
onKeyDown: this.onKeyDown,
onMouseDown: this.onMouseDown,
onBlur: this.ifDraggingStop,
ref: this.containerRef,
role: "button",
tabIndex: "-1"
}, (0, _element.createElement)(_grid.default, {
bounds: bounds,
value: percentages.x + percentages.y
}), (0, _element.createElement)(_media.default, {
alt: (0, _i18n.__)('Media preview'),
autoPlay: autoPlay,
mediaRef: this.mediaRef,
onLoad: this.updateBounds,
src: url
}), (0, _element.createElement)(_focalPoint.default, {
coordinates: iconCoordinates,
isDragging: isDragging
}))), (0, _element.createElement)(_controls.default, {
percentages: percentages,
onChange: this.onChangeAtControls
}));
}
}
exports.FocalPointPicker = FocalPointPicker;
FocalPointPicker.defaultProps = {
autoPlay: true,
value: {
x: 0.5,
y: 0.5
},
url: null
};
var _default = (0, _compose.withInstanceId)(FocalPointPicker);
exports.default = _default;
//# sourceMappingURL=index.js.map