From 638be23c8f6f33d829f632d37f20173537c393fb Mon Sep 17 00:00:00 2001 From: safydy Date: Mon, 14 Jun 2021 12:14:00 -0600 Subject: [PATCH 01/11] VMO-3944 rename selectedBlocks state --- src/components/interaction-designer/Block.vue | 4 ++-- .../toolbar/SelectionBanner.vue | 4 ++-- src/store/flow/block.ts | 4 ++-- src/store/flow/flow.ts | 14 +++++++------- src/store/flow/index.ts | 4 ++-- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/components/interaction-designer/Block.vue b/src/components/interaction-designer/Block.vue index 965bd623b..e400d653c 100644 --- a/src/components/interaction-designer/Block.vue +++ b/src/components/interaction-designer/Block.vue @@ -246,7 +246,7 @@ export default { computed: { ...mapState('flow', [ 'resources', - 'selectedBlocks', + 'selectedBlockUuids', ]), ...mapState('builder', ['activeBlockId', 'operations', 'activeConnectionsContext', 'draggableForExitsByUuid'] @@ -271,7 +271,7 @@ export default { }, isBlockSelected() { - return includes(this.selectedBlocks, this.block.uuid) + return includes(this.selectedBlockUuids, this.block.uuid) }, // todo: does this component know too much, what out of the above mapped state can be mapped? diff --git a/src/components/interaction-designer/toolbar/SelectionBanner.vue b/src/components/interaction-designer/toolbar/SelectionBanner.vue index 811a99493..b4ba2c7c9 100644 --- a/src/components/interaction-designer/toolbar/SelectionBanner.vue +++ b/src/components/interaction-designer/toolbar/SelectionBanner.vue @@ -61,7 +61,7 @@ export default class SelectionBanner extends mixins(Lang) { } get countSelectedBlocks() { - return size(this.selectedBlocks) + return size(this.selectedBlockUuids) } async confirmMultipleDeletion() { @@ -74,7 +74,7 @@ export default class SelectionBanner extends mixins(Lang) { await this.flow_clearMultiSelection() } - @flowVuexNamespace.State selectedBlocks!: IBlock['uuid'][] + @flowVuexNamespace.State selectedBlockUuids!: IBlock['uuid'][] @flowVuexNamespace.Action flow_clearMultiSelection!: () => void @flowVuexNamespace.Action flow_removeAllSelectedBlocks!: () => void @flowVuexNamespace.Action flow_duplicateAllSelectedBlocks!: () => void diff --git a/src/store/flow/block.ts b/src/store/flow/block.ts index 2472620f1..0729e599f 100644 --- a/src/store/flow/block.ts +++ b/src/store/flow/block.ts @@ -143,11 +143,11 @@ export const actions: ActionTree = { }, async block_select({ state, commit }, { blockId }: { blockId: IBlock['uuid']}) { - state.selectedBlocks.push(blockId) + state.selectedBlockUuids.push(blockId) }, async block_deselect({ state, commit }, { blockId }: { blockId: IBlock['uuid']}) { - state.selectedBlocks = state.selectedBlocks.filter((item) => item !== blockId) // remove it + state.selectedBlockUuids = state.selectedBlockUuids.filter((item) => item !== blockId) // remove it }, } diff --git a/src/store/flow/flow.ts b/src/store/flow/flow.ts index efa95a46e..380e640d9 100644 --- a/src/store/flow/flow.ts +++ b/src/store/flow/flow.ts @@ -345,26 +345,26 @@ export const actions: ActionTree = { }, async flow_clearMultiSelection({ state, dispatch }) { - forEach(state.selectedBlocks, (blockId: IBlock['uuid']) => { + forEach(state.selectedBlockUuids, (blockId: IBlock['uuid']) => { dispatch('block_deselect', { blockId }) }) }, async flow_removeAllSelectedBlocks({ state, dispatch }) { - forEach(state.selectedBlocks, (blockId: IBlock['uuid']) => { + forEach(state.selectedBlockUuids, (blockId: IBlock['uuid']) => { dispatch('flow_removeBlock', { blockId }) }) - state.selectedBlocks = [] + state.selectedBlockUuids = [] }, async flow_duplicateAllSelectedBlocks({ state, dispatch }) { - let newBlocksUuid: string[] = [] - forEach(state.selectedBlocks, async (blockId: IBlock['uuid']) => { + let newBlocksUuids: string[] = [] + forEach(state.selectedBlockUuids, async (blockId: IBlock['uuid']) => { const duplicatedBlock: IBlock = await dispatch('flow_duplicateBlock', { blockId }) - newBlocksUuid.push(duplicatedBlock.uuid) + newBlocksUuids.push(duplicatedBlock.uuid) }) - state.selectedBlocks = newBlocksUuid + state.selectedBlockUuids = newBlocksUuids }, } diff --git a/src/store/flow/index.ts b/src/store/flow/index.ts index 329d4a612..0b551233b 100644 --- a/src/store/flow/index.ts +++ b/src/store/flow/index.ts @@ -30,7 +30,7 @@ export interface IFlowsState { first_flow_id: string | null; // @note - for exciting future nested_flow_block_interaction_id_stack: string[]; - selectedBlocks: IBlock['uuid'][]; + selectedBlockUuids: IBlock['uuid'][]; } export const stateFactory = (): IFlowsState => ({ @@ -40,7 +40,7 @@ export const stateFactory = (): IFlowsState => ({ first_flow_id: null, nested_flow_block_interaction_id_stack: [], // todo: not quite right -- pulled from IContext - selectedBlocks: [], + selectedBlockUuids: [], }) export const getters: GetterTree = { From 8c3afe954d1da9a92e55e46bf88f0b4bb07535ba Mon Sep 17 00:00:00 2001 From: safydy Date: Mon, 14 Jun 2021 12:49:01 -0600 Subject: [PATCH 02/11] VMO-3944 move setBlockPositionTo as action --- src/components/interaction-designer/Block.vue | 44 ++++++++++++------- src/store/builder/index.ts | 34 ++++++++------ 2 files changed, 50 insertions(+), 28 deletions(-) diff --git a/src/components/interaction-designer/Block.vue b/src/components/interaction-designer/Block.vue index e400d653c..68c18c379 100644 --- a/src/components/interaction-designer/Block.vue +++ b/src/components/interaction-designer/Block.vue @@ -11,6 +11,7 @@ :startX="x" :startY="y" :is-editable="isEditable" + @initialized="handleDraggableInitializedForBlock(block, $event)" @dragged="onMoved" @dragStarted="selectBlock" @dragEnded="handleDraggableEndedForBlock" @@ -122,11 +123,11 @@ :key="`exit/${exit.uuid}/pseudo-block-handle`" :is-editable="isEditable" v-b-tooltip.hover.top="transIf(isEditable, 'flow-builder.tooltip-new-connection')" - @initialized="handleDraggableInitializedFor(exit, $event)" + @initialized="handleDraggableInitializedForExit(exit, $event)" @dragStarted="onCreateExitDragStarted($event, exit)" @dragged="onCreateExitDragged($event)" @dragEnded="onCreateExitDragEnded($event, exit)" - @destroyed="handleDraggableDestroyedFor(exit)"> + @destroyed="handleDraggableDestroyedForExit(exit)"> @@ -158,11 +159,11 @@ :key="`exit/${exit.uuid}/handle`" :is-editable="isEditable" v-b-tooltip.hover.top="transIf(isEditable, 'flow-builder.tooltip-relocate-connection')" - @initialized="handleDraggableInitializedFor(exit, $event)" + @initialized="handleDraggableInitializedForExit(exit, $event)" @dragStarted="onMoveExitDragStarted($event, exit)" @dragged="onMoveExitDragged($event)" @dragEnded="onMoveExitDragEnded($event, exit)" - @destroyed="handleDraggableDestroyedFor(exit)"> + @destroyed="handleDraggableDestroyedForExit(exit)"> @@ -248,9 +249,13 @@ export default { 'resources', 'selectedBlockUuids', ]), - ...mapState('builder', - ['activeBlockId', 'operations', 'activeConnectionsContext', 'draggableForExitsByUuid'] - ), + ...mapState('builder', [ + 'activeBlockId', + 'operations', + 'activeConnectionsContext', + 'draggableForExitsByUuid', + 'draggableForBlocksByUuid', + ]), ...mapState({ blockClasses: ({ trees: { ui } }) => ui.blockClasses, }), @@ -297,13 +302,14 @@ export default { generateConnectionLayoutKeyFor, }, - ...mapMutations('builder', ['setBlockPositionTo', 'initDraggableForExitsByUuid']), + ...mapMutations('builder', ['initDraggableForExitsByUuid']), ...mapActions('builder', { _removeConnectionFrom: 'removeConnectionFrom', }), ...mapActions('builder', [ + 'setBlockPositionTo', // ConnectionSourceRelocate 'initializeConnectionSourceRelocateWith', 'setConnectionSourceRelocateValue', @@ -427,16 +433,16 @@ export default { this.setConnectionCreateTargetBlockToNullFrom({ block }) }, - onMoved({ position: { left: x, top: y } }) { + onMoved({ draggable, position }) { // todo: try this the vuejs way where we push the change into state, then return false + modify draggable w/in store ? - + const { left: x, top: y } = position const { block } = this this.$nextTick(() => { this.setBlockPositionTo({ position: { x, y }, block }) - forEach(this.draggableForExitsByUuid, (draggable, key) => { + forEach(this.draggableForExitsByUuid, (exitDraggable, key) => { try { - draggable.position() + exitDraggable.position() } catch (e) { console.warn('Block', 'onMoved', 'positioning draggable on', key, 'can\'t access property "initElm", props is undefined') } @@ -451,16 +457,24 @@ export default { this.labelContainerMaxWidth = this.labelContainerMaxWidth + 0 // force render, useful if the exit label is very short }, - handleDraggableInitializedFor({ uuid }, { draggable }) { + handleDraggableInitializedForBlock(block, { draggable }) { + this.draggableForBlocksByUuid[block.uuid] = draggable + + const { left, top } = draggable + + console.debug('Block', 'handleDraggableInitializedForBlock', { blockId: block.uuid, coords: { left, top } }) + }, + + handleDraggableInitializedForExit({ uuid }, { draggable }) { this.draggableForExitsByUuid[uuid] = draggable const { left, top } = draggable const { uuid: blockId } = this.block - console.debug('Block', 'handleDraggableInitializedFor', { blockId, exitId: uuid, coords: { left, top } }) + console.debug('Block', 'handleDraggableInitializedForExit', { blockId, exitId: uuid, coords: { left, top } }) }, - handleDraggableDestroyedFor({ uuid }) { + handleDraggableDestroyedForExit({ uuid }) { delete this.draggableForExitsByUuid[uuid] }, diff --git a/src/store/builder/index.ts b/src/store/builder/index.ts index 1fe76ed16..c88c74f5c 100644 --- a/src/store/builder/index.ts +++ b/src/store/builder/index.ts @@ -1,5 +1,5 @@ import { - flatMap, isEqual, keyBy, map, mapValues, get, filter, union + flatMap, isEqual, keyBy, map, mapValues, get, filter, union, includes, } from 'lodash' import Vue from 'vue' import { @@ -57,7 +57,8 @@ export interface IBuilderState { [OperationKind.CONNECTION_CREATE]: IConnectionCreateOperation; [OperationKind.BLOCK_RELOCATE]: null; }; - draggableForExitsByUuid: object + draggableForExitsByUuid: object; + draggableForBlocksByUuid: object; } export const stateFactory = (): IBuilderState => ({ @@ -75,7 +76,8 @@ export const stateFactory = (): IBuilderState => ({ }, [OperationKind.BLOCK_RELOCATE]: null, }, - draggableForExitsByUuid: {} + draggableForExitsByUuid: {}, + draggableForBlocksByUuid: {}, }) export const getters: GetterTree = { @@ -113,27 +115,33 @@ export const mutations: MutationTree = { operations[operation.kind] = operation }, - setBlockPositionTo(state, { position: { x, y }, block }) { + setIsEditable(state, value) { + state.isEditable = value + }, + + initDraggableForExitsByUuid(state) { + state.draggableForExitsByUuid = {} + } +} + +export const actions: ActionTree = { + setBlockPositionTo({ rootState }, { position: { x, y }, block }) { // todo: ensure our vendor_metadata.io_viamo is always instantiated with builder // uiData props // if (!block.vendor_metadata.io_viamo.uiData) { // defaultsDeep(block, {vendor_metadata: {io_viamo: {uiData: {xPosition: 0, yPosition: 0}}}}) // Vue.observable(block.vendor_metadata.io_viamo.uiData) // } + // UI Position block.vendor_metadata.io_viamo.uiData.xPosition = x block.vendor_metadata.io_viamo.uiData.yPosition = y - }, - setIsEditable(state, value) { - state.isEditable = value + // If block is selected, then update other selected blocks + if (includes(rootState.flow.selectedBlockUuids, block.uuid)) { + // TODO: implement vmo-3944 here + } }, - initDraggableForExitsByUuid(state) { - state.draggableForExitsByUuid = {} - } -} - -export const actions: ActionTree = { removeConnectionFrom({ commit }, { block: { uuid: blockId }, exit: { uuid: exitId } }) { commit('flow/block_setBlockExitDestinationBlockId', { blockId, From 0e4b558f215362c57b6e48c177ad804e59e4935a Mon Sep 17 00:00:00 2001 From: safydy Date: Mon, 14 Jun 2021 15:54:31 -0600 Subject: [PATCH 03/11] VMO-3944 move all selected blocks --- src/store/builder/index.ts | 59 +++++++++++++++++++++++++++++++++----- src/store/flow/flow.ts | 8 +++++- 2 files changed, 59 insertions(+), 8 deletions(-) diff --git a/src/store/builder/index.ts b/src/store/builder/index.ts index c88c74f5c..34677c190 100644 --- a/src/store/builder/index.ts +++ b/src/store/builder/index.ts @@ -1,5 +1,5 @@ import { - flatMap, isEqual, keyBy, map, mapValues, get, filter, union, includes, + flatMap, isEqual, keyBy, map, mapValues, get, filter, union, includes, clone, forEach } from 'lodash' import Vue from 'vue' import { @@ -57,8 +57,8 @@ export interface IBuilderState { [OperationKind.CONNECTION_CREATE]: IConnectionCreateOperation; [OperationKind.BLOCK_RELOCATE]: null; }; - draggableForExitsByUuid: object; - draggableForBlocksByUuid: object; + draggableForExitsByUuid: {[key: string]: object}; + draggableForBlocksByUuid: {[key: string]: object}; } export const stateFactory = (): IBuilderState => ({ @@ -121,27 +121,72 @@ export const mutations: MutationTree = { initDraggableForExitsByUuid(state) { state.draggableForExitsByUuid = {} + }, + + updateBlockDraggablePosition(state, { uuid, position }: { uuid: IBlock['uuid'], position: IPosition }) { + Object.assign(state.draggableForBlocksByUuid[uuid], { left: position.x, top: position.y }) } } export const actions: ActionTree = { - setBlockPositionTo({ rootState }, { position: { x, y }, block }) { + setBlockPositionTo({ state, commit, dispatch, rootState, rootGetters }, { position: { x, y }, block }) { // todo: ensure our vendor_metadata.io_viamo is always instantiated with builder // uiData props // if (!block.vendor_metadata.io_viamo.uiData) { // defaultsDeep(block, {vendor_metadata: {io_viamo: {uiData: {xPosition: 0, yPosition: 0}}}}) // Vue.observable(block.vendor_metadata.io_viamo.uiData) // } - // UI Position + // Store UI Position + const initialPosition = { + x: clone(block.vendor_metadata.io_viamo.uiData.xPosition), + y: clone(block.vendor_metadata.io_viamo.uiData.yPosition), + } as IPosition block.vendor_metadata.io_viamo.uiData.xPosition = x block.vendor_metadata.io_viamo.uiData.yPosition = y - // If block is selected, then update other selected blocks + // If the block is selected, then translate other selected blocks if (includes(rootState.flow.selectedBlockUuids, block.uuid)) { - // TODO: implement vmo-3944 here + const translationDelta: IPosition = { + x: x - initialPosition.x, + y: y - initialPosition.y + } + + const otherSelectedBlocks = filter(rootGetters['flow/selectedBlocks'], currentBlock => currentBlock.uuid !== block.uuid) + + forEach(otherSelectedBlocks, async (currentBlock: IBlock) => { + let newPosition = {} as IPosition + await dispatch('setBlockPositionFromDelta', { delta: translationDelta, block: currentBlock }).then( + response => { + newPosition = response + } + ) + + commit('updateBlockDraggablePosition', { uuid: currentBlock.uuid, position: newPosition }) + }) } }, + setBlockPositionFromDelta({ dispatch }, { delta: { x, y }, block }) { + const { vendor_metadata: { + io_viamo: { + uiData: { + xPosition: initialXPosition, + yPosition: initialYPosition + } + } + }} = block + + const newPosition = { + x: initialXPosition + x, + y: initialYPosition + y + } + + block.vendor_metadata.io_viamo.uiData.xPosition = newPosition.x + block.vendor_metadata.io_viamo.uiData.yPosition = newPosition.y + + return newPosition + }, + removeConnectionFrom({ commit }, { block: { uuid: blockId }, exit: { uuid: exitId } }) { commit('flow/block_setBlockExitDestinationBlockId', { blockId, diff --git a/src/store/flow/flow.ts b/src/store/flow/flow.ts index 380e640d9..9b555f217 100644 --- a/src/store/flow/flow.ts +++ b/src/store/flow/flow.ts @@ -24,7 +24,8 @@ import { cloneDeep, get, has, - omit + omit, + filter } from 'lodash' import { discoverContentTypesFor } from '@/store/flow/resource' import { computeBlockUiData } from '@/store/builder' @@ -58,6 +59,11 @@ export const getters: GetterTree = { }, hasTextMode: (state, getters) => [SupportedMode.USSD, SupportedMode.SMS].some((mode) => includes(getters.activeFlow.supported_modes || [], mode)), hasVoiceMode: (state, getters) => includes(getters.activeFlow.supported_modes || [], SupportedMode.IVR), + selectedBlocks: (state, getters) => { + return filter(getters.activeFlow.blocks, (block) => { + return includes(state.selectedBlockUuids, block.uuid) + }) + } } export const mutations: MutationTree = { From 38bce20e3845b3195c552d1341f1a058d573fd15 Mon Sep 17 00:00:00 2001 From: safydy Date: Mon, 14 Jun 2021 19:23:12 -0600 Subject: [PATCH 04/11] VMO-3944 correct left/top limits --- src/store/builder/index.ts | 63 ++++++++++++++++++++++++------- src/store/flow/flow.ts | 11 +++++- src/views/InteractionDesigner.vue | 9 +++-- 3 files changed, 65 insertions(+), 18 deletions(-) diff --git a/src/store/builder/index.ts b/src/store/builder/index.ts index 34677c190..640283292 100644 --- a/src/store/builder/index.ts +++ b/src/store/builder/index.ts @@ -57,6 +57,7 @@ export interface IBuilderState { [OperationKind.CONNECTION_CREATE]: IConnectionCreateOperation; [OperationKind.BLOCK_RELOCATE]: null; }; + toolbarHeight: number; draggableForExitsByUuid: {[key: string]: object}; draggableForBlocksByUuid: {[key: string]: object}; } @@ -76,6 +77,7 @@ export const stateFactory = (): IBuilderState => ({ }, [OperationKind.BLOCK_RELOCATE]: null, }, + toolbarHeight: 60, draggableForExitsByUuid: {}, draggableForBlocksByUuid: {}, }) @@ -125,34 +127,69 @@ export const mutations: MutationTree = { updateBlockDraggablePosition(state, { uuid, position }: { uuid: IBlock['uuid'], position: IPosition }) { Object.assign(state.draggableForBlocksByUuid[uuid], { left: position.x, top: position.y }) + }, + + updateToolBarHeight(state, height) { + state.toolbarHeight = height } } export const actions: ActionTree = { - setBlockPositionTo({ state, commit, dispatch, rootState, rootGetters }, { position: { x, y }, block }) { + setBlockPositionTo({ state, commit, getters, dispatch, rootState, rootGetters }, { position: { x, y }, block }) { // todo: ensure our vendor_metadata.io_viamo is always instantiated with builder // uiData props // if (!block.vendor_metadata.io_viamo.uiData) { // defaultsDeep(block, {vendor_metadata: {io_viamo: {uiData: {xPosition: 0, yPosition: 0}}}}) // Vue.observable(block.vendor_metadata.io_viamo.uiData) // } - // Store UI Position - const initialPosition = { - x: clone(block.vendor_metadata.io_viamo.uiData.xPosition), - y: clone(block.vendor_metadata.io_viamo.uiData.yPosition), - } as IPosition - block.vendor_metadata.io_viamo.uiData.xPosition = x - block.vendor_metadata.io_viamo.uiData.yPosition = y - - // If the block is selected, then translate other selected blocks - if (includes(rootState.flow.selectedBlockUuids, block.uuid)) { + if (!includes(rootState.flow.selectedBlockUuids, block.uuid)) { + block.vendor_metadata.io_viamo.uiData.xPosition = x + block.vendor_metadata.io_viamo.uiData.yPosition = y + } else { // the block is selected + // Store UI Position + const initialPosition = { + x: clone(block.vendor_metadata.io_viamo.uiData.xPosition), + y: clone(block.vendor_metadata.io_viamo.uiData.yPosition), + } as IPosition + + // Prepare translation const translationDelta: IPosition = { x: x - initialPosition.x, y: y - initialPosition.y } - const otherSelectedBlocks = filter(rootGetters['flow/selectedBlocks'], currentBlock => currentBlock.uuid !== block.uuid) + let shouldReversePosition = false; + if (translationDelta.x > 0 /* moving to the right */ || + rootGetters['flow/selectedBlockAtTheFurthestLeftPosition'].vendor_metadata.io_viamo.uiData.xPosition > 0 /* left space still available */) { + block.vendor_metadata.io_viamo.uiData.xPosition = x + } else { + translationDelta.x = 0 + block.vendor_metadata.io_viamo.uiData.xPosition = initialPosition.x + shouldReversePosition = true + } + + if(translationDelta.y > 0 /* moving to the top */ || + rootGetters['flow/selectedBlockAtTheTopPosition'].vendor_metadata.io_viamo.uiData.yPosition - state.toolbarHeight > 0 /* top space still available */) { + block.vendor_metadata.io_viamo.uiData.yPosition = y + } else { + translationDelta.y = 0 + block.vendor_metadata.io_viamo.uiData.yPosition = initialPosition.y + shouldReversePosition = true + } + + // Reverse the draggable position + if (shouldReversePosition) { + commit('updateBlockDraggablePosition', { + uuid: block.uuid, + position: { + x: block.vendor_metadata.io_viamo.uiData.xPosition, + y: block.vendor_metadata.io_viamo.uiData.yPosition, + } + }) + } + // Translate other selected blocks + const otherSelectedBlocks = filter(rootGetters['flow/selectedBlocks'], currentBlock => currentBlock.uuid !== block.uuid); forEach(otherSelectedBlocks, async (currentBlock: IBlock) => { let newPosition = {} as IPosition await dispatch('setBlockPositionFromDelta', { delta: translationDelta, block: currentBlock }).then( @@ -162,7 +199,7 @@ export const actions: ActionTree = { ) commit('updateBlockDraggablePosition', { uuid: currentBlock.uuid, position: newPosition }) - }) + }); } }, diff --git a/src/store/flow/flow.ts b/src/store/flow/flow.ts index 9b555f217..282264f99 100644 --- a/src/store/flow/flow.ts +++ b/src/store/flow/flow.ts @@ -25,7 +25,8 @@ import { get, has, omit, - filter + filter, + minBy, } from 'lodash' import { discoverContentTypesFor } from '@/store/flow/resource' import { computeBlockUiData } from '@/store/builder' @@ -63,6 +64,14 @@ export const getters: GetterTree = { return filter(getters.activeFlow.blocks, (block) => { return includes(state.selectedBlockUuids, block.uuid) }) + }, + + selectedBlockAtTheTopPosition: (state, getters) => { + return minBy(getters.selectedBlocks, 'vendor_metadata.io_viamo.uiData.yPosition') + }, + + selectedBlockAtTheFurthestLeftPosition: (state, getters) => { + return minBy(getters.selectedBlocks, 'vendor_metadata.io_viamo.uiData.xPosition') } } diff --git a/src/views/InteractionDesigner.vue b/src/views/InteractionDesigner.vue index 06fe0b49b..59c55695b 100644 --- a/src/views/InteractionDesigner.vue +++ b/src/views/InteractionDesigner.vue @@ -48,7 +48,7 @@
@@ -116,7 +116,6 @@ export default { data() { return { - toolbarHeight: 60, pureVuejsBlocks: [ // todo: move this to BlockClassDetails spec // an inversion can be "legacy types" 'CallBackWithCallCenterBlock', 'CollaborativeFilteringQuestionBlock', @@ -165,6 +164,8 @@ export default { }), ...mapGetters('flow', ['activeFlow']), + + ...mapState('builder', ['toolbarHeight']), ...mapGetters('builder', ['activeBlock', 'isEditable']), jsKey() { @@ -247,7 +248,7 @@ export default { }, methods: { ...mapMutations(['deselectBlocks', 'configure']), - ...mapMutations('builder', ['activateBlock']), + ...mapMutations('builder', ['activateBlock', 'updateToolBarHeight']), ...mapActions('builder', ['setIsEditable']), ...mapMutations('flow', ['flow_setActiveFlowId']), @@ -324,7 +325,7 @@ export default { }, handleToolBarHeightUpdate(height) { - this.toolbarHeight = height + this.updateToolBarHeight(height) } }, From 1f9b7e275bd7389d6e25cbfe1665b92fbcc61d2a Mon Sep 17 00:00:00 2001 From: safydy Date: Mon, 21 Jun 2021 10:03:12 -0600 Subject: [PATCH 05/11] VMO-3944 move setBlockUIPositionTo back as mutation & fix some eslint errors --- src/components/interaction-designer/Block.vue | 6 +- src/store/builder/index.ts | 76 +++++++++++-------- 2 files changed, 47 insertions(+), 35 deletions(-) diff --git a/src/components/interaction-designer/Block.vue b/src/components/interaction-designer/Block.vue index 68c18c379..c5ac7e913 100644 --- a/src/components/interaction-designer/Block.vue +++ b/src/components/interaction-designer/Block.vue @@ -309,7 +309,7 @@ export default { }), ...mapActions('builder', [ - 'setBlockPositionTo', + 'changeBlockPositionTo', // ConnectionSourceRelocate 'initializeConnectionSourceRelocateWith', 'setConnectionSourceRelocateValue', @@ -433,12 +433,12 @@ export default { this.setConnectionCreateTargetBlockToNullFrom({ block }) }, - onMoved({ draggable, position }) { + onMoved({ position }) { // todo: try this the vuejs way where we push the change into state, then return false + modify draggable w/in store ? const { left: x, top: y } = position const { block } = this this.$nextTick(() => { - this.setBlockPositionTo({ position: { x, y }, block }) + this.changeBlockPositionTo({ position: { x, y }, block }) forEach(this.draggableForExitsByUuid, (exitDraggable, key) => { try { diff --git a/src/store/builder/index.ts b/src/store/builder/index.ts index 640283292..b4dfc4f19 100644 --- a/src/store/builder/index.ts +++ b/src/store/builder/index.ts @@ -117,6 +117,21 @@ export const mutations: MutationTree = { operations[operation.kind] = operation }, + setBlockPositionTo(state, { position: { x, y }, block }) { + // todo: ensure our vendor_metadata.io_viamo is always instantiated with builder // uiData props + // if (!block.vendor_metadata.io_viamo.uiData) { + // defaultsDeep(block, {vendor_metadata: {io_viamo: {uiData: {xPosition: 0, yPosition: 0}}}}) + // Vue.observable(block.vendor_metadata.io_viamo.uiData) + // } + + if (x !== undefined) { + block.vendor_metadata.io_viamo.uiData.xPosition = x + } + if (y !== undefined) { + block.vendor_metadata.io_viamo.uiData.yPosition = y + } + }, + setIsEditable(state, value) { state.isEditable = value }, @@ -135,16 +150,12 @@ export const mutations: MutationTree = { } export const actions: ActionTree = { - setBlockPositionTo({ state, commit, getters, dispatch, rootState, rootGetters }, { position: { x, y }, block }) { - // todo: ensure our vendor_metadata.io_viamo is always instantiated with builder // uiData props - // if (!block.vendor_metadata.io_viamo.uiData) { - // defaultsDeep(block, {vendor_metadata: {io_viamo: {uiData: {xPosition: 0, yPosition: 0}}}}) - // Vue.observable(block.vendor_metadata.io_viamo.uiData) - // } + changeBlockPositionTo( + { state, commit, dispatch, rootState, rootGetters }, + { position: { x, y }, block }) { if (!includes(rootState.flow.selectedBlockUuids, block.uuid)) { - block.vendor_metadata.io_viamo.uiData.xPosition = x - block.vendor_metadata.io_viamo.uiData.yPosition = y + commit('setBlockPositionTo', { position: { x, y }, block }) } else { // the block is selected // Store UI Position const initialPosition = { @@ -155,25 +166,25 @@ export const actions: ActionTree = { // Prepare translation const translationDelta: IPosition = { x: x - initialPosition.x, - y: y - initialPosition.y + y: y - initialPosition.y, } - let shouldReversePosition = false; - if (translationDelta.x > 0 /* moving to the right */ || - rootGetters['flow/selectedBlockAtTheFurthestLeftPosition'].vendor_metadata.io_viamo.uiData.xPosition > 0 /* left space still available */) { - block.vendor_metadata.io_viamo.uiData.xPosition = x + let shouldReversePosition = false + if (translationDelta.x > 0 /* moving to the right */ + || rootGetters['flow/selectedBlockAtTheFurthestLeftPosition'].vendor_metadata.io_viamo.uiData.xPosition > 0 /* left space still available */) { + commit('setBlockPositionTo', { position: { x }, block }) } else { translationDelta.x = 0 - block.vendor_metadata.io_viamo.uiData.xPosition = initialPosition.x + commit('setBlockPositionTo', { position: { x: initialPosition.x }, block }) shouldReversePosition = true } - if(translationDelta.y > 0 /* moving to the top */ || - rootGetters['flow/selectedBlockAtTheTopPosition'].vendor_metadata.io_viamo.uiData.yPosition - state.toolbarHeight > 0 /* top space still available */) { - block.vendor_metadata.io_viamo.uiData.yPosition = y + if (translationDelta.y > 0 /* moving to the top */ + || rootGetters['flow/selectedBlockAtTheTopPosition'].vendor_metadata.io_viamo.uiData.yPosition - state.toolbarHeight > 0 /* top space still available */) { + commit('setBlockPositionTo', { position: { y }, block }) } else { translationDelta.y = 0 - block.vendor_metadata.io_viamo.uiData.yPosition = initialPosition.y + commit('setBlockPositionTo', { position: { y: initialPosition.y }, block }) shouldReversePosition = true } @@ -184,16 +195,16 @@ export const actions: ActionTree = { position: { x: block.vendor_metadata.io_viamo.uiData.xPosition, y: block.vendor_metadata.io_viamo.uiData.yPosition, - } + }, }) } // Translate other selected blocks - const otherSelectedBlocks = filter(rootGetters['flow/selectedBlocks'], currentBlock => currentBlock.uuid !== block.uuid); + const otherSelectedBlocks = filter(rootGetters['flow/selectedBlocks'], (currentBlock) => currentBlock.uuid !== block.uuid) forEach(otherSelectedBlocks, async (currentBlock: IBlock) => { let newPosition = {} as IPosition await dispatch('setBlockPositionFromDelta', { delta: translationDelta, block: currentBlock }).then( - response => { + (response) => { newPosition = response } ) @@ -203,23 +214,24 @@ export const actions: ActionTree = { } }, - setBlockPositionFromDelta({ dispatch }, { delta: { x, y }, block }) { - const { vendor_metadata: { - io_viamo: { - uiData: { - xPosition: initialXPosition, - yPosition: initialYPosition - } + setBlockPositionFromDelta({ commit }, { delta: { x, y }, block }) { + const { + vendor_metadata: { + io_viamo: { + uiData: { + xPosition: initialXPosition, + yPosition: initialYPosition, + }, + }, } - }} = block + } = block const newPosition = { x: initialXPosition + x, - y: initialYPosition + y + y: initialYPosition + y, } - block.vendor_metadata.io_viamo.uiData.xPosition = newPosition.x - block.vendor_metadata.io_viamo.uiData.yPosition = newPosition.y + commit('setBlockPositionTo', { position: newPosition, block }) return newPosition }, From 222de04e693b2fb17f469f4498fa5c17a393c266 Mon Sep 17 00:00:00 2001 From: safydy Date: Mon, 21 Jun 2021 10:29:05 -0600 Subject: [PATCH 06/11] VMO-3944 use self-explaining variable instead of comments --- src/store/builder/index.ts | 128 ++++++++++++++++++++++--------------- src/store/flow/flow.ts | 8 --- 2 files changed, 75 insertions(+), 61 deletions(-) diff --git a/src/store/builder/index.ts b/src/store/builder/index.ts index b4dfc4f19..7202767a0 100644 --- a/src/store/builder/index.ts +++ b/src/store/builder/index.ts @@ -1,5 +1,5 @@ import { - flatMap, isEqual, keyBy, map, mapValues, get, filter, union, includes, clone, forEach + flatMap, isEqual, keyBy, map, mapValues, get, filter, union, includes, clone, forEach, minBy } from 'lodash' import Vue from 'vue' import { @@ -95,6 +95,26 @@ export const getters: GetterTree = { exitLabelsById: (state, getters, { flow: { flows } }, rootGetters) => mapValues(keyBy(flatMap(flows[0].blocks, 'exits'), 'uuid'), 'label'), isEditable: (state) => state.isEditable, + + selectedBlocks: (_state, _getters, _rootState, rootGetters) => { + return rootGetters['flow/selectedBlocks'] + }, + + selectedBlockAtTheTopPosition: (_state, getters) => { + return minBy(getters.selectedBlocks, 'vendor_metadata.io_viamo.uiData.yPosition') + }, + + selectedBlockAtTheFurthestLeftPosition: (_state, getters) => { + return minBy(getters.selectedBlocks, 'vendor_metadata.io_viamo.uiData.xPosition') + }, + + isAnyLeftSpaceAvailable: (_state, getters) => { + return getters.selectedBlockAtTheFurthestLeftPosition.vendor_metadata.io_viamo.uiData.xPosition > 0 + }, + + isAnyTopSpaceAvailable: (state, getters) => { + return getters.selectedBlockAtTheTopPosition.vendor_metadata.io_viamo.uiData.yPosition - state.toolbarHeight > 0 + } } export const mutations: MutationTree = { @@ -151,67 +171,69 @@ export const mutations: MutationTree = { export const actions: ActionTree = { changeBlockPositionTo( - { state, commit, dispatch, rootState, rootGetters }, + { state, commit, getters, dispatch, rootState }, { position: { x, y }, block }) { if (!includes(rootState.flow.selectedBlockUuids, block.uuid)) { commit('setBlockPositionTo', { position: { x, y }, block }) - } else { // the block is selected - // Store UI Position - const initialPosition = { - x: clone(block.vendor_metadata.io_viamo.uiData.xPosition), - y: clone(block.vendor_metadata.io_viamo.uiData.yPosition), - } as IPosition - - // Prepare translation - const translationDelta: IPosition = { - x: x - initialPosition.x, - y: y - initialPosition.y, - } + return + } - let shouldReversePosition = false - if (translationDelta.x > 0 /* moving to the right */ - || rootGetters['flow/selectedBlockAtTheFurthestLeftPosition'].vendor_metadata.io_viamo.uiData.xPosition > 0 /* left space still available */) { - commit('setBlockPositionTo', { position: { x }, block }) - } else { - translationDelta.x = 0 - commit('setBlockPositionTo', { position: { x: initialPosition.x }, block }) - shouldReversePosition = true - } + // The block is selected + // Store UI Position + const initialPosition = { + x: clone(block.vendor_metadata.io_viamo.uiData.xPosition), + y: clone(block.vendor_metadata.io_viamo.uiData.yPosition), + } as IPosition + + // Prepare translation + const translationDelta: IPosition = { + x: x - initialPosition.x, + y: y - initialPosition.y, + } - if (translationDelta.y > 0 /* moving to the top */ - || rootGetters['flow/selectedBlockAtTheTopPosition'].vendor_metadata.io_viamo.uiData.yPosition - state.toolbarHeight > 0 /* top space still available */) { - commit('setBlockPositionTo', { position: { y }, block }) - } else { - translationDelta.y = 0 - commit('setBlockPositionTo', { position: { y: initialPosition.y }, block }) - shouldReversePosition = true - } + let shouldReversePosition = false + const isMovingToTheRight = translationDelta.x > 0 + if (isMovingToTheRight || getters.isAnyLeftSpaceAvailable) { + commit('setBlockPositionTo', { position: { x }, block }) + } else { + translationDelta.x = 0 + commit('setBlockPositionTo', { position: { x: initialPosition.x }, block }) + shouldReversePosition = true + } - // Reverse the draggable position - if (shouldReversePosition) { - commit('updateBlockDraggablePosition', { - uuid: block.uuid, - position: { - x: block.vendor_metadata.io_viamo.uiData.xPosition, - y: block.vendor_metadata.io_viamo.uiData.yPosition, - }, - }) - } + const isMovingToTheTop = translationDelta.y > 0 + if (isMovingToTheTop || getters.isAnyTopSpaceAvailable) { + commit('setBlockPositionTo', { position: { y }, block }) + } else { + translationDelta.y = 0 + commit('setBlockPositionTo', { position: { y: initialPosition.y }, block }) + shouldReversePosition = true + } - // Translate other selected blocks - const otherSelectedBlocks = filter(rootGetters['flow/selectedBlocks'], (currentBlock) => currentBlock.uuid !== block.uuid) - forEach(otherSelectedBlocks, async (currentBlock: IBlock) => { - let newPosition = {} as IPosition - await dispatch('setBlockPositionFromDelta', { delta: translationDelta, block: currentBlock }).then( - (response) => { - newPosition = response - } - ) - - commit('updateBlockDraggablePosition', { uuid: currentBlock.uuid, position: newPosition }) - }); + // Reverse the draggable position + if (shouldReversePosition) { + commit('updateBlockDraggablePosition', { + uuid: block.uuid, + position: { + x: block.vendor_metadata.io_viamo.uiData.xPosition, + y: block.vendor_metadata.io_viamo.uiData.yPosition, + }, + }) } + + // Translate other selected blocks + const otherSelectedBlocks = filter(getters.selectedBlocks, (currentBlock) => currentBlock.uuid !== block.uuid) + forEach(otherSelectedBlocks, async (currentBlock: IBlock) => { + let newPosition = {} as IPosition + await dispatch('setBlockPositionFromDelta', { delta: translationDelta, block: currentBlock }).then( + (response) => { + newPosition = response + } + ) + + commit('updateBlockDraggablePosition', { uuid: currentBlock.uuid, position: newPosition }) + }) }, setBlockPositionFromDelta({ commit }, { delta: { x, y }, block }) { diff --git a/src/store/flow/flow.ts b/src/store/flow/flow.ts index 282264f99..10227533f 100644 --- a/src/store/flow/flow.ts +++ b/src/store/flow/flow.ts @@ -65,14 +65,6 @@ export const getters: GetterTree = { return includes(state.selectedBlockUuids, block.uuid) }) }, - - selectedBlockAtTheTopPosition: (state, getters) => { - return minBy(getters.selectedBlocks, 'vendor_metadata.io_viamo.uiData.yPosition') - }, - - selectedBlockAtTheFurthestLeftPosition: (state, getters) => { - return minBy(getters.selectedBlocks, 'vendor_metadata.io_viamo.uiData.xPosition') - } } export const mutations: MutationTree = { From 32f6617b536653398fd85f03a26018af41e3bea0 Mon Sep 17 00:00:00 2001 From: safydy Date: Mon, 21 Jun 2021 10:55:27 -0600 Subject: [PATCH 07/11] VMO-3944 enhance `Translate other selected blocks` --- src/store/builder/index.ts | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/src/store/builder/index.ts b/src/store/builder/index.ts index 7202767a0..5cece0f9e 100644 --- a/src/store/builder/index.ts +++ b/src/store/builder/index.ts @@ -223,20 +223,14 @@ export const actions: ActionTree = { } // Translate other selected blocks - const otherSelectedBlocks = filter(getters.selectedBlocks, (currentBlock) => currentBlock.uuid !== block.uuid) - forEach(otherSelectedBlocks, async (currentBlock: IBlock) => { - let newPosition = {} as IPosition - await dispatch('setBlockPositionFromDelta', { delta: translationDelta, block: currentBlock }).then( - (response) => { - newPosition = response - } - ) - - commit('updateBlockDraggablePosition', { uuid: currentBlock.uuid, position: newPosition }) + forEach(getters.selectedBlocks, (currentBlock: IBlock) => { + if (currentBlock.uuid !== block.uuid) { + dispatch('setBlockAndSyncDraggablePositionFromDelta', { delta: translationDelta, block: currentBlock }) + } }) }, - setBlockPositionFromDelta({ commit }, { delta: { x, y }, block }) { + setBlockAndSyncDraggablePositionFromDelta({ commit }, { delta: { x, y }, block }) { const { vendor_metadata: { io_viamo: { @@ -248,14 +242,13 @@ export const actions: ActionTree = { } } = block - const newPosition = { + const newPosition: IPosition = { x: initialXPosition + x, y: initialYPosition + y, } commit('setBlockPositionTo', { position: newPosition, block }) - - return newPosition + commit('updateBlockDraggablePosition', { uuid: block.uuid, position: newPosition }) }, removeConnectionFrom({ commit }, { block: { uuid: blockId }, exit: { uuid: exitId } }) { From eb9ab7ea6f77d7154d8b87782ae0a5d268a2d6b4 Mon Sep 17 00:00:00 2001 From: safydy Date: Mon, 21 Jun 2021 15:23:25 -0600 Subject: [PATCH 08/11] VMO-3944 use flex for notification banners --- .../interaction-designer/toolbar/ErrorNotifications.vue | 6 +----- .../interaction-designer/toolbar/TreeBuilderToolbar.vue | 4 ++-- src/views/InteractionDesigner.vue | 7 ++++--- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/components/interaction-designer/toolbar/ErrorNotifications.vue b/src/components/interaction-designer/toolbar/ErrorNotifications.vue index 76891374f..228b3bf1a 100644 --- a/src/components/interaction-designer/toolbar/ErrorNotifications.vue +++ b/src/components/interaction-designer/toolbar/ErrorNotifications.vue @@ -1,5 +1,5 @@