From ca2eca4265363e100499d453caa44a291564bcc1 Mon Sep 17 00:00:00 2001 From: Braks <78412429+bcakmakoglu@users.noreply.github.com> Date: Sun, 12 Dec 2021 15:58:19 +0100 Subject: [PATCH] refactor(store): add getters for selected nodes/edges Signed-off-by: Braks <78412429+bcakmakoglu@users.noreply.github.com> --- examples/Basic/Basic.vue | 30 +++++- src/composables/useHooks.ts | 6 +- src/composables/useVueFlow.ts | 96 ++++++++++++++++++- src/container/SelectionPane/SelectionPane.vue | 21 ++-- src/container/ZoomPane/ZoomPane.vue | 2 +- src/store/getters.ts | 17 +++- src/types/store.ts | 40 ++++---- 7 files changed, 173 insertions(+), 39 deletions(-) diff --git a/examples/Basic/Basic.vue b/examples/Basic/Basic.vue index 3c20d8f7..838dc749 100644 --- a/examples/Basic/Basic.vue +++ b/examples/Basic/Basic.vue @@ -9,6 +9,7 @@ import { Elements, FlowInstance, addEdge, + isNode, removeElements, } from '~/index' @@ -25,29 +26,54 @@ const edges = ref([ { id: 'e1-3', source: '1', target: '3' }, ]) const vfInstance = ref() +const onConnect = (params: Edge | Connection) => addEdge(params, edges.value) const onLoad = (flowInstance: FlowInstance) => { flowInstance.fitView({ padding: 0.1 }) vfInstance.value = flowInstance } +const updatePos = () => { + nodes.value = nodes.value.map((el) => { + el.position = { + x: Math.random() * 400, + y: Math.random() * 400, + } + return el + }) +} + const logToObject = () => console.log(vfInstance.value?.toObject()) const resetTransform = () => vfInstance.value?.setTransform({ x: 0, y: 0, zoom: 1 }) + +const toggleclasss = () => { + nodes.value = nodes.value.map((el) => { + if (isNode(el)) el.class = el.class === 'light' ? 'dark' : 'light' + return el + }) +} diff --git a/src/composables/useHooks.ts b/src/composables/useHooks.ts index ace4b256..2410d5e5 100644 --- a/src/composables/useHooks.ts +++ b/src/composables/useHooks.ts @@ -2,8 +2,9 @@ import { EmitFunc, FlowHooks, FlowEvents, FlowStore } from '~/types' // flow event hooks export const createHooks = (): FlowHooks => ({ + edgesChange: createEventHook(), + nodesChange: createEventHook(), elementClick: createEventHook(), - elementsRemove: createEventHook(), nodeDoubleClick: createEventHook(), nodeClick: createEventHook(), nodeMouseEnter: createEventHook(), @@ -17,8 +18,7 @@ export const createHooks = (): FlowHooks => ({ connectStart: createEventHook(), connectStop: createEventHook(), connectEnd: createEventHook(), - elementsProcessed: createEventHook(), - load: createEventHook(), + paneReady: createEventHook(), move: createEventHook(), moveStart: createEventHook(), moveEnd: createEventHook(), diff --git a/src/composables/useVueFlow.ts b/src/composables/useVueFlow.ts index 3c738a5e..46d34108 100644 --- a/src/composables/useVueFlow.ts +++ b/src/composables/useVueFlow.ts @@ -1,6 +1,90 @@ -import useStore from './useStore' import useHooks from './useHooks' -import { EmitFunc, FlowStore } from '~/types' +import { + EdgeChange, + EmitFunc, + FlowOptions, + FlowStore, + NodeChange, + UseVueFlow, + FlowElements, + FlowElement, + GraphNode, + GraphEdge, +} from '~/types' +import { useStore } from '~/store' +import { isGraphNode } from '~/utils' + +const applyChanges = (changes: NodeChange[] | EdgeChange[], elements: FlowElements): FlowElements => { + const initElements: FlowElements = [] + + return elements.reduce((res: FlowElements, item: FlowElement) => { + const currentChange = changes.find((c) => c.id === item.id) + + if (currentChange) { + switch (currentChange.type) { + case 'select': { + res.push({ ...item, selected: currentChange.selected }) + return res + } + case 'dimensions': { + const updateItem = { ...item } + + if (isGraphNode(updateItem)) { + if (typeof currentChange.dimensions !== 'undefined') { + updateItem.dimensions.width = currentChange.dimensions.width + updateItem.dimensions.height = currentChange.dimensions.height + } + + if (typeof currentChange.position !== 'undefined') { + updateItem.position = currentChange.position + } + + if (typeof currentChange.dragging !== 'undefined') { + updateItem.dragging = currentChange.dragging + } + } + + res.push(updateItem) + return res + } + case 'remove': { + return res + } + } + } + + res.push(item) + return res + }, initElements) +} + +export const applyNodeChanges = (changes: NodeChange[], nodes: GraphNode[]): GraphNode[] => + applyChanges(changes, nodes) + +export const applyEdgeChanges = (changes: EdgeChange[], edges: GraphEdge[]): GraphEdge[] => + applyChanges(changes, edges) + +export const createSelectionChange = (id: string, selected: boolean) => ({ + id, + type: 'select', + selected, +}) + +export const getSelectionChanges = (items: any[], selectedIds: string[]) => { + return items.reduce((res, item) => { + const willBeSelected = selectedIds.includes(item.id) + + if (!item.selected && willBeSelected) { + item.selected = true + res.push(createSelectionChange(item.id, true)) + } else if (item.selected && !willBeSelected) { + item.selected = false + res.push(createSelectionChange(item.id, false)) + } + + return res + }, []) +} export const initFlow = (emit: EmitFunc, id?: string): FlowStore => { const store = useStore({ id }) @@ -8,4 +92,10 @@ export const initFlow = (emit: EmitFunc, id?: string): FlowStore => { return store } -export default useStore +export default (options?: FlowOptions): UseVueFlow => { + const store = useStore(options) + return { + store, + hooks: store.hooks, + } +} diff --git a/src/container/SelectionPane/SelectionPane.vue b/src/container/SelectionPane/SelectionPane.vue index a10c88f8..97a6f988 100644 --- a/src/container/SelectionPane/SelectionPane.vue +++ b/src/container/SelectionPane/SelectionPane.vue @@ -1,5 +1,5 @@ diff --git a/src/store/getters.ts b/src/store/getters.ts index 32b9ec24..5615de6e 100644 --- a/src/store/getters.ts +++ b/src/store/getters.ts @@ -1,5 +1,5 @@ import { defaultEdgeTypes, defaultNodeTypes } from './state' -import { FlowElements, FlowGetters, FlowState, GraphEdge, GraphNode } from '~/types' +import { FlowGetters, FlowState, GraphEdge, GraphNode } from '~/types' import { getNodesInside, isEdgeVisible } from '~/utils' export default (state: FlowState): FlowGetters => { @@ -69,10 +69,17 @@ export default (state: FlowState): FlowGetters => { return [] }) - const getSelectedElements = computed(() => [...(state.selectedNodes ?? []), ...(state.selectedEdges ?? [])]) + const getSelectedElements: FlowGetters['getSelectedElements'] = computed(() => [ + ...(state.selectedNodes ?? []), + ...(state.selectedEdges ?? []), + ]) + const getSelectedNodes: FlowGetters['getSelectedNodes'] = computed(() => state.nodes.filter((n) => n.selected)) + const getSelectedEdges: FlowGetters['getSelectedEdges'] = computed(() => state.edges.filter((e) => e.selected)) - const getNode: FlowGetters['getNode'] = computed(() => (id: string) => state.nodes.find((node) => node.id === id)) - const getEdge: FlowGetters['getEdge'] = computed(() => (id: string) => state.edges.find((edge) => edge.id === id)) + const nodeIds = computed(() => state.nodes.map((n) => n.id)) + const edgeIds = computed(() => state.edges.map((e) => e.id)) + const getNode: FlowGetters['getNode'] = computed(() => (id: string) => state.nodes[nodeIds.value.indexOf(id)]) + const getEdge: FlowGetters['getEdge'] = computed(() => (id: string) => state.edges[edgeIds.value.indexOf(id)]) return { getNode, @@ -82,5 +89,7 @@ export default (state: FlowState): FlowGetters => { getEdges, getNodes, getSelectedElements, + getSelectedNodes, + getSelectedEdges, } } diff --git a/src/types/store.ts b/src/types/store.ts index 424d2292..2dcdadba 100644 --- a/src/types/store.ts +++ b/src/types/store.ts @@ -13,8 +13,8 @@ export interface FlowState extends Omit, 'id'> instance?: FlowInstance elements: Elements - nodes: GraphNode[] - edges: GraphEdge[] + nodes: GraphNode[] + edges: GraphEdge[] d3Zoom?: D3Zoom d3Selection?: D3Selection @@ -71,32 +71,36 @@ export interface FlowState extends Omit, 'id'> vueFlowVersion: string } -export interface FlowActions { - setElements: (elements: Elements, extent: CoordinateExtent) => void - setNodes: (nodes: Node[], extent: CoordinateExtent) => void - setEdges: (edges: Edge[]) => void - addSelectedElements: (elements: FlowElements) => void - addSelectedEdges: (edges: GraphEdge[]) => void - addSelectedNodes: (nodes: GraphNode[]) => void +export interface FlowActions { + setElements: (elements: Elements, extent: CoordinateExtent) => void + setNodes: (nodes: Node[], extent: CoordinateExtent) => void + setEdges: (edges: Edge[]) => void + addSelectedElements: (elements: FlowElements) => void + addSelectedEdges: (edges: GraphEdge[]) => void + addSelectedNodes: (nodes: GraphNode[]) => void setMinZoom: (zoom: number) => void setMaxZoom: (zoom: number) => void setTranslateExtent: (translateExtent: CoordinateExtent) => void resetSelectedElements: () => void setConnectionNodeId: (payload: SetConnectionId) => void setInteractive: (isInteractive: boolean) => void - setState: (state: Partial) => void + setState: (state: Partial>) => void updateNodePosition: ({ id, diff, dragging }: { id?: string; diff?: XYPosition; dragging?: boolean }) => void } -export interface FlowGetters { +export interface FlowGetters { getEdgeTypes: ComputedRef> getNodeTypes: ComputedRef> - getNodes: ComputedRef - getEdges: ComputedRef - getNode: ComputedRef<(id: string) => GraphNode | undefined> - getEdge: ComputedRef<(id: string) => GraphEdge | undefined> - getSelectedElements: ComputedRef + getNodes: ComputedRef[]> + getEdges: ComputedRef[]> + getNode: ComputedRef<(id: string) => GraphNode | undefined> + getEdge: ComputedRef<(id: string) => GraphEdge | undefined> + getSelectedElements: ComputedRef> + getSelectedNodes: ComputedRef[]> + getSelectedEdges: ComputedRef[]> } -export type Store = { id: string; state: FlowState } & ToRefs & FlowActions & FlowGetters -export type FlowStore = UnwrapNestedRefs +export type Store = { id: string; state: FlowState } & ToRefs> & + FlowActions & + FlowGetters +export type FlowStore = UnwrapNestedRefs>