refactor(store): add getters for selected nodes/edges

Signed-off-by: Braks <78412429+bcakmakoglu@users.noreply.github.com>
This commit is contained in:
Braks
2021-12-20 19:29:52 +01:00
parent a80d475216
commit ca2eca4265
7 changed files with 173 additions and 39 deletions
+28 -2
View File
@@ -9,6 +9,7 @@ import {
Elements,
FlowInstance,
addEdge,
isNode,
removeElements,
} from '~/index'
@@ -25,29 +26,54 @@ const edges = ref<Elements>([
{ id: 'e1-3', source: '1', target: '3' },
])
const vfInstance = ref<FlowInstance>()
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
})
}
</script>
<template>
<VueFlow
class="vue-flow-basic-example"
:nodes="nodes"
:edges="edges"
class="vue-flow-basic-example"
:default-zoom="1.5"
:min-zoom="0.2"
:max-zoom="4"
:zoom-on-scroll="false"
@connect="onConnect"
@node-drag-stop="onNodeDragStop"
@node-click="onElementClick"
@load="onLoad"
@pane-ready="onLoad"
>
<Background />
<MiniMap />
<Controls />
<div style="position: absolute; right: 10px; top: 10px; z-index: 4">
<button style="margin-right: 5px" @click="resetTransform">reset transform</button>
<button style="margin-right: 5px" @click="updatePos">change pos</button>
<button style="margin-right: 5px" @click="toggleclasss">toggle classs</button>
<button @click="logToObject">toObject</button>
</div>
</VueFlow>
</template>
+3 -3
View File
@@ -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(),
+93 -3
View File
@@ -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[] =>
<GraphNode[]>applyChanges(changes, nodes)
export const applyEdgeChanges = (changes: EdgeChange[], edges: GraphEdge[]): 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,
}
}
+13 -8
View File
@@ -1,5 +1,5 @@
<script lang="ts" setup>
import { FlowElement } from '../../types'
import { EdgeChange, FlowElement, NodeChange } from '../../types'
import { useStore, useKeyPress } from '../../composables'
import { getConnectedEdges } from '../../utils'
import NodesSelection from '../../components/NodesSelection/NodesSelection.vue'
@@ -19,14 +19,19 @@ const onWheel = (event: WheelEvent) => store.hooks.paneScroll.trigger(event)
onMounted(() => {
useKeyPress(store.deleteKeyCode, (keyPressed) => {
if (keyPressed && (store.selectedNodes || store.selectedEdges)) {
const connectedEdges = (store.selectedNodes && getConnectedEdges(store.selectedNodes, store.getEdges)) ?? []
const elementsToRemove = [...(store.selectedNodes ?? []), ...(store.selectedEdges ?? []), ...connectedEdges].reduce(
(res, item) => res.set(item.id, item),
new Map<string, FlowElement>(),
)
const selectedNodes = store.getSelectedNodes
const selectedEdges = store.getSelectedEdges
if (keyPressed && (selectedNodes || selectedEdges)) {
const connectedEdges = (selectedNodes && getConnectedEdges(selectedNodes, store.edges)) ?? []
store.hooks.elementsRemove.trigger(Array.from(elementsToRemove.values()))
const nodeChanges: NodeChange[] = selectedNodes.map((n) => ({ id: n.id, type: 'remove' }))
const edgeChanges: EdgeChange[] = [...selectedEdges, ...connectedEdges].map((e) => ({
id: e.id,
type: 'remove',
}))
store.hooks.nodesChange.trigger(nodeChanges)
store.hooks.edgesChange.trigger(edgeChanges)
store.nodesSelectionActive = false
store.resetSelectedElements()
}
+1 -1
View File
@@ -209,7 +209,7 @@ nextTick(async () => {
getEdges: onLoadGetEdges(store),
toObject: onLoadToObject(store),
}
store.hooks.load.trigger(instance)
store.hooks.paneReady.trigger(instance)
store.instance = instance
})
</script>
+13 -4
View File
@@ -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<FlowElements>(() => [...(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,
}
}
+22 -18
View File
@@ -13,8 +13,8 @@ export interface FlowState<N = any, E = N> extends Omit<FlowOptions<N, E>, 'id'>
instance?: FlowInstance
elements: Elements
nodes: GraphNode[]
edges: GraphEdge[]
nodes: GraphNode<N>[]
edges: GraphEdge<E>[]
d3Zoom?: D3Zoom
d3Selection?: D3Selection
@@ -71,32 +71,36 @@ export interface FlowState<N = any, E = N> extends Omit<FlowOptions<N, E>, '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<N = any, E = N> {
setElements: (elements: Elements<N, E>, extent: CoordinateExtent) => void
setNodes: (nodes: Node<N>[], extent: CoordinateExtent) => void
setEdges: (edges: Edge<E>[]) => void
addSelectedElements: (elements: FlowElements<N, E>) => void
addSelectedEdges: (edges: GraphEdge<E>[]) => void
addSelectedNodes: (nodes: GraphNode<N>[]) => 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<FlowOptions>) => void
setState: (state: Partial<FlowOptions<N, E>>) => void
updateNodePosition: ({ id, diff, dragging }: { id?: string; diff?: XYPosition; dragging?: boolean }) => void
}
export interface FlowGetters {
export interface FlowGetters<N = any, E = N> {
getEdgeTypes: ComputedRef<Record<string, EdgeComponent>>
getNodeTypes: ComputedRef<Record<string, NodeComponent>>
getNodes: ComputedRef<GraphNode[]>
getEdges: ComputedRef<GraphEdge[]>
getNode: ComputedRef<(id: string) => GraphNode | undefined>
getEdge: ComputedRef<(id: string) => GraphEdge | undefined>
getSelectedElements: ComputedRef<FlowElements>
getNodes: ComputedRef<GraphNode<N>[]>
getEdges: ComputedRef<GraphEdge<E>[]>
getNode: ComputedRef<(id: string) => GraphNode<N> | undefined>
getEdge: ComputedRef<(id: string) => GraphEdge<E> | undefined>
getSelectedElements: ComputedRef<FlowElements<N, E>>
getSelectedNodes: ComputedRef<GraphNode<N>[]>
getSelectedEdges: ComputedRef<GraphEdge<E>[]>
}
export type Store = { id: string; state: FlowState } & ToRefs<FlowState> & FlowActions & FlowGetters
export type FlowStore = UnwrapNestedRefs<Store>
export type Store<N = any, E = N> = { id: string; state: FlowState<N, E> } & ToRefs<FlowState<N, E>> &
FlowActions<N, E> &
FlowGetters<N, E>
export type FlowStore<N = any, E = N> = UnwrapNestedRefs<Store<N, E>>