refactor(store): add getters for selected nodes/edges
Signed-off-by: Braks <78412429+bcakmakoglu@users.noreply.github.com>
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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>>
|
||||
|
||||
Reference in New Issue
Block a user