diff --git a/x-pack/plugins/canvas/public/state/actions/elements.js b/x-pack/plugins/canvas/public/state/actions/elements.js index be157d9d8085b..3c5142b0a9460 100644 --- a/x-pack/plugins/canvas/public/state/actions/elements.js +++ b/x-pack/plugins/canvas/public/state/actions/elements.js @@ -46,10 +46,8 @@ function getBareElement(el, includeId = false) { export const elementLayer = createAction('elementLayer'); -export const setPosition = createAction('setPosition', (elementId, pageId, position) => ({ - pageId, - elementId, - position, +export const setMultiplePositions = createAction('setMultiplePosition', repositionedElements => ({ + repositionedElements, })); export const flushContext = createAction('flushContext'); diff --git a/x-pack/plugins/canvas/public/state/middleware/aeroelastic.js b/x-pack/plugins/canvas/public/state/middleware/aeroelastic.js index f2ddac33feff5..21eab34ea35a6 100644 --- a/x-pack/plugins/canvas/public/state/middleware/aeroelastic.js +++ b/x-pack/plugins/canvas/public/state/middleware/aeroelastic.js @@ -7,12 +7,13 @@ import { shallowEqual } from 'recompose'; import { aeroelastic as aero } from '../../lib/aeroelastic_kibana'; import { matrixToAngle } from '../../lib/aeroelastic/matrix'; +import { identity } from '../../lib/aeroelastic/functional'; import { addElement, removeElements, duplicateElement, elementLayer, - setPosition, + setMultiplePositions, fetchAllRenderables, } from '../actions/elements'; import { restoreHistory } from '../actions/history'; @@ -65,40 +66,44 @@ const elementToShape = (element, i) => { }; }; -const updateGlobalPositions = (setPosition, { shapes, gestureEnd }, elems) => { - shapes.forEach((shape, i) => { - const elemPos = elems[i] && elems[i].position; - if (elemPos && gestureEnd) { - // get existing position information from element - const oldProps = { - left: elemPos.left, - top: elemPos.top, - width: elemPos.width, - height: elemPos.height, - angle: Math.round(elemPos.angle), - }; - - // cast shape into element-like object to compare - const newProps = { - left: shape.transformMatrix[12] - shape.a, - top: shape.transformMatrix[13] - shape.b, - width: shape.a * 2, - height: shape.b * 2, - angle: Math.round(matrixToAngle(shape.transformMatrix)), - }; - - if (1 / newProps.angle === -Infinity) newProps.angle = 0; // recompose.shallowEqual discerns between 0 and -0 - - if (!shallowEqual(oldProps, newProps)) setPosition(shape.id, newProps); - } - }); +const updateGlobalPositions = (setMultiplePositions, { shapes, gestureEnd }, elems) => { + const repositionings = shapes + .map((shape, i) => { + const elemPos = elems[i] && elems[i].position; + if (elemPos && gestureEnd) { + // get existing position information from element + const oldProps = { + left: elemPos.left, + top: elemPos.top, + width: elemPos.width, + height: elemPos.height, + angle: Math.round(elemPos.angle), + }; + + // cast shape into element-like object to compare + const newProps = { + left: shape.transformMatrix[12] - shape.a, + top: shape.transformMatrix[13] - shape.b, + width: shape.a * 2, + height: shape.b * 2, + angle: Math.round(matrixToAngle(shape.transformMatrix)), + }; + + if (1 / newProps.angle === -Infinity) newProps.angle = 0; // recompose.shallowEqual discerns between 0 and -0 + + return shallowEqual(oldProps, newProps) + ? null + : { position: newProps, elementId: shape.id }; + } + }) + .filter(identity); + if (repositionings.length) setMultiplePositions(repositionings); }; const id = element => element.id; export const aeroelastic = ({ dispatch, getState }) => { // When aeroelastic updates an element, we need to dispatch actions to notify redux of the changes - // dispatch(setPosition({ ... })); const onChangeCallback = ({ state }) => { const nextScene = state.currentScene; @@ -111,7 +116,7 @@ export const aeroelastic = ({ dispatch, getState }) => { const selectedElement = getSelectedElement(getState()); updateGlobalPositions( - (elementId, position) => dispatch(setPosition(elementId, page, position)), + positions => dispatch(setMultiplePositions(positions.map(p => ({ ...p, pageId: page })))), nextScene, elements ); @@ -223,7 +228,7 @@ export const aeroelastic = ({ dispatch, getState }) => { case addElement.toString(): case duplicateElement.toString(): case elementLayer.toString(): - case setPosition.toString(): + case setMultiplePositions.toString(): const page = getSelectedPage(getState()); const elements = getElements(getState(), page); @@ -232,7 +237,7 @@ export const aeroelastic = ({ dispatch, getState }) => { prevPage !== page || !shallowEqual(prevElements.map(id), elements.map(id)); if (shouldResetState) populateWithElements(page); - if (action.type !== setPosition.toString()) unselectShape(prevPage); + if (action.type !== setMultiplePositions.toString()) unselectShape(prevPage); break; } diff --git a/x-pack/plugins/canvas/public/state/reducers/elements.js b/x-pack/plugins/canvas/public/state/reducers/elements.js index de063965cfacb..e17c09639e2e9 100644 --- a/x-pack/plugins/canvas/public/state/reducers/elements.js +++ b/x-pack/plugins/canvas/public/state/reducers/elements.js @@ -66,10 +66,12 @@ export const elementsReducer = handleActions( const { filter, pageId, elementId } = payload; return assignElementProperties(workpadState, pageId, elementId, { filter }); }, - [actions.setPosition]: (workpadState, { payload }) => { - const { position, pageId, elementId } = payload; - return assignElementProperties(workpadState, pageId, elementId, { position }); - }, + [actions.setMultiplePositions]: (workpadState, { payload }) => + payload.repositionedElements.reduce( + (previousWorkpadState, { position, pageId, elementId }) => + assignElementProperties(previousWorkpadState, pageId, elementId, { position }), + workpadState + ), [actions.elementLayer]: (workpadState, { payload: { pageId, elementId, movement } }) => { return moveElementLayer(workpadState, pageId, elementId, movement); },