UNPKG

@wordpress/components

Version:
393 lines (336 loc) 10.5 kB
"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