import { Interaction } from "../core/Interaction.js"; import { ActionName } from "../core/scope.js"; import * as arr from "../utils/arr.js"; import * as dom from "../utils/domUtils.js"; import extend from "../utils/extend.js"; import * as is from "../utils/is.js"; ActionName.Resize = 'resize'; function install(scope) { const { actions, browser, /** @lends Interactable */ Interactable, // tslint:disable-line no-shadowed-variable defaults } = scope; // Less Precision with touch input resize.cursors = initCursors(browser); resize.defaultMargin = browser.supportsTouch || browser.supportsPointerEvent ? 20 : 10; /** * ```js * interact(element).resizable({ * onstart: function (event) {}, * onmove : function (event) {}, * onend : function (event) {}, * * edges: { * top : true, // Use pointer coords to check for resize. * left : false, // Disable resizing from left edge. * bottom: '.resize-s',// Resize if pointer target matches selector * right : handleEl // Resize if pointer target is the given Element * }, * * // Width and height can be adjusted independently. When `true`, width and * // height are adjusted at a 1:1 ratio. * square: false, * * // Width and height can be adjusted independently. When `true`, width and * // height maintain the aspect ratio they had when resizing started. * preserveAspectRatio: false, * * // a value of 'none' will limit the resize rect to a minimum of 0x0 * // 'negate' will allow the rect to have negative width/height * // 'reposition' will keep the width/height positive by swapping * // the top and bottom edges and/or swapping the left and right edges * invert: 'none' || 'negate' || 'reposition' * * // limit multiple resizes. * // See the explanation in the {@link Interactable.draggable} example * max: Infinity, * maxPerElement: 1, * }) * * var isResizeable = interact(element).resizable() * ``` * * Gets or sets whether resize actions can be performed on the target * * @param {boolean | object} [options] true/false or An object with event * listeners to be fired on resize events (object makes the Interactable * resizable) * @return {boolean | Interactable} A boolean indicating if this can be the * target of resize elements, or this Interactable */ Interactable.prototype.resizable = function (options) { return resizable(this, options, scope); }; actions[ActionName.Resize] = resize; actions.names.push(ActionName.Resize); arr.merge(actions.eventTypes, ['resizestart', 'resizemove', 'resizeinertiastart', 'resizeresume', 'resizeend']); actions.methodDict.resize = 'resizable'; defaults.actions.resize = resize.defaults; } function resizeChecker(arg) { const { interaction, interactable, element, rect, buttons } = arg; if (!rect) { return undefined; } const page = extend({}, interaction.coords.cur.page); const resizeOptions = interactable.options.resize; if (!(resizeOptions && resizeOptions.enabled) || // check mouseButton setting if the pointer is down interaction.pointerIsDown && /mouse|pointer/.test(interaction.pointerType) && (buttons & resizeOptions.mouseButtons) === 0) { return undefined; } // if using resize.edges if (is.object(resizeOptions.edges)) { const resizeEdges = { left: false, right: false, top: false, bottom: false }; for (const edge in resizeEdges) { resizeEdges[edge] = checkResizeEdge(edge, resizeOptions.edges[edge], page, interaction._latestPointer.eventTarget, element, rect, resizeOptions.margin || resize.defaultMargin); } resizeEdges.left = resizeEdges.left && !resizeEdges.right; resizeEdges.top = resizeEdges.top && !resizeEdges.bottom; if (resizeEdges.left || resizeEdges.right || resizeEdges.top || resizeEdges.bottom) { arg.action = { name: ActionName.Resize, edges: resizeEdges }; } } else { const right = resizeOptions.axis !== 'y' && page.x > rect.right - resize.defaultMargin; const bottom = resizeOptions.axis !== 'x' && page.y > rect.bottom - resize.defaultMargin; if (right || bottom) { arg.action = { name: 'resize', axes: (right ? 'x' : '') + (bottom ? 'y' : '') }; } } return arg.action ? false : undefined; } function resizable(interactable, options, scope) { if (is.object(options)) { interactable.options.resize.enabled = options.enabled !== false; interactable.setPerAction(ActionName.Resize, options); interactable.setOnEvents(ActionName.Resize, options); if (is.string(options.axis) && /^x$|^y$|^xy$/.test(options.axis)) { interactable.options.resize.axis = options.axis; } else if (options.axis === null) { interactable.options.resize.axis = scope.defaults.actions.resize.axis; } if (is.bool(options.preserveAspectRatio)) { interactable.options.resize.preserveAspectRatio = options.preserveAspectRatio; } else if (is.bool(options.square)) { interactable.options.resize.square = options.square; } return interactable; } if (is.bool(options)) { interactable.options.resize.enabled = options; return interactable; } return interactable.options.resize; } function checkResizeEdge(name, value, page, element, interactableElement, rect, margin) { // false, '', undefined, null if (!value) { return false; } // true value, use pointer coords and element rect if (value === true) { // if dimensions are negative, "switch" edges const width = is.number(rect.width) ? rect.width : rect.right - rect.left; const height = is.number(rect.height) ? rect.height : rect.bottom - rect.top; // don't use margin greater than half the relevent dimension margin = Math.min(margin, (name === 'left' || name === 'right' ? width : height) / 2); if (width < 0) { if (name === 'left') { name = 'right'; } else if (name === 'right') { name = 'left'; } } if (height < 0) { if (name === 'top') { name = 'bottom'; } else if (name === 'bottom') { name = 'top'; } } if (name === 'left') { return page.x < (width >= 0 ? rect.left : rect.right) + margin; } if (name === 'top') { return page.y < (height >= 0 ? rect.top : rect.bottom) + margin; } if (name === 'right') { return page.x > (width >= 0 ? rect.right : rect.left) - margin; } if (name === 'bottom') { return page.y > (height >= 0 ? rect.bottom : rect.top) - margin; } } // the remaining checks require an element if (!is.element(element)) { return false; } return is.element(value) // the value is an element to use as a resize handle ? value === element // otherwise check if element matches value as selector : dom.matchesUpTo(element, value, interactableElement); } function initCursors(browser) { return browser.isIe9 ? { x: 'e-resize', y: 's-resize', xy: 'se-resize', top: 'n-resize', left: 'w-resize', bottom: 's-resize', right: 'e-resize', topleft: 'se-resize', bottomright: 'se-resize', topright: 'ne-resize', bottomleft: 'ne-resize' } : { x: 'ew-resize', y: 'ns-resize', xy: 'nwse-resize', top: 'ns-resize', left: 'ew-resize', bottom: 'ns-resize', right: 'ew-resize', topleft: 'nwse-resize', bottomright: 'nwse-resize', topright: 'nesw-resize', bottomleft: 'nesw-resize' }; } function start({ iEvent, interaction }) { if (interaction.prepared.name !== 'resize' || !interaction.prepared.edges) { return; } const rect = interaction.rect; interaction._rects = { start: extend({}, rect), corrected: extend({}, rect), previous: extend({}, rect), delta: { left: 0, right: 0, width: 0, top: 0, bottom: 0, height: 0 } }; iEvent.edges = interaction.prepared.edges; iEvent.rect = interaction._rects.corrected; iEvent.deltaRect = interaction._rects.delta; } function move({ iEvent, interaction }) { if (interaction.prepared.name !== 'resize' || !interaction.prepared.edges) { return; } const resizeOptions = interaction.interactable.options.resize; const invert = resizeOptions.invert; const invertible = invert === 'reposition' || invert === 'negate'; // eslint-disable-next-line no-shadow const current = interaction.rect; const { start: startRect, corrected, delta: deltaRect, previous } = interaction._rects; extend(previous, corrected); if (invertible) { // if invertible, copy the current rect extend(corrected, current); if (invert === 'reposition') { // swap edge values if necessary to keep width/height positive if (corrected.top > corrected.bottom) { const swap = corrected.top; corrected.top = corrected.bottom; corrected.bottom = swap; } if (corrected.left > corrected.right) { const swap = corrected.left; corrected.left = corrected.right; corrected.right = swap; } } } else { // if not invertible, restrict to minimum of 0x0 rect corrected.top = Math.min(current.top, startRect.bottom); corrected.bottom = Math.max(current.bottom, startRect.top); corrected.left = Math.min(current.left, startRect.right); corrected.right = Math.max(current.right, startRect.left); } corrected.width = corrected.right - corrected.left; corrected.height = corrected.bottom - corrected.top; for (const edge in corrected) { deltaRect[edge] = corrected[edge] - previous[edge]; } iEvent.edges = interaction.prepared.edges; iEvent.rect = corrected; iEvent.deltaRect = deltaRect; } function end({ iEvent, interaction }) { if (interaction.prepared.name !== 'resize' || !interaction.prepared.edges) { return; } iEvent.edges = interaction.prepared.edges; iEvent.rect = interaction._rects.corrected; iEvent.deltaRect = interaction._rects.delta; } function updateEventAxes({ iEvent, interaction }) { if (interaction.prepared.name !== ActionName.Resize || !interaction.resizeAxes) { return; } const options = interaction.interactable.options; if (options.resize.square) { if (interaction.resizeAxes === 'y') { iEvent.delta.x = iEvent.delta.y; } else { iEvent.delta.y = iEvent.delta.x; } iEvent.axes = 'xy'; } else { iEvent.axes = interaction.resizeAxes; if (interaction.resizeAxes === 'x') { iEvent.delta.y = 0; } else if (interaction.resizeAxes === 'y') { iEvent.delta.x = 0; } } } const resize = { id: 'actions/resize', before: ['actions/drag'], install, listeners: { 'interactions:new': ({ interaction }) => { interaction.resizeAxes = 'xy'; }, 'interactions:action-start': arg => { start(arg); updateEventAxes(arg); }, 'interactions:action-move': arg => { move(arg); updateEventAxes(arg); }, 'interactions:action-end': end, 'auto-start:check': resizeChecker }, defaults: { square: false, preserveAspectRatio: false, axis: 'xy', // use default margin margin: NaN, // object with props left, right, top, bottom which are // true/false values to resize when the pointer is over that edge, // CSS selectors to match the handles for each direction // or the Elements for each handle edges: null, // a value of 'none' will limit the resize rect to a minimum of 0x0 // 'negate' will alow the rect to have negative width/height // 'reposition' will keep the width/height positive by swapping // the top and bottom edges and/or swapping the left and right edges invert: 'none' }, cursors: null, getCursor({ edges, axis, name }) { const cursors = resize.cursors; let result = null; if (axis) { result = cursors[name + axis]; } else if (edges) { let cursorKey = ''; for (const edge of ['top', 'bottom', 'left', 'right']) { if (edges[edge]) { cursorKey += edge; } } result = cursors[cursorKey]; } return result; }, defaultMargin: null }; export default resize; //# sourceMappingURL=resize.js.map