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,
|
Elements,
|
||||||
FlowInstance,
|
FlowInstance,
|
||||||
addEdge,
|
addEdge,
|
||||||
|
isNode,
|
||||||
removeElements,
|
removeElements,
|
||||||
} from '~/index'
|
} from '~/index'
|
||||||
|
|
||||||
@@ -25,29 +26,54 @@ const edges = ref<Elements>([
|
|||||||
{ id: 'e1-3', source: '1', target: '3' },
|
{ id: 'e1-3', source: '1', target: '3' },
|
||||||
])
|
])
|
||||||
const vfInstance = ref<FlowInstance>()
|
const vfInstance = ref<FlowInstance>()
|
||||||
|
const onConnect = (params: Edge | Connection) => addEdge(params, edges.value)
|
||||||
const onLoad = (flowInstance: FlowInstance) => {
|
const onLoad = (flowInstance: FlowInstance) => {
|
||||||
flowInstance.fitView({ padding: 0.1 })
|
flowInstance.fitView({ padding: 0.1 })
|
||||||
vfInstance.value = flowInstance
|
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 logToObject = () => console.log(vfInstance.value?.toObject())
|
||||||
const resetTransform = () => vfInstance.value?.setTransform({ x: 0, y: 0, zoom: 1 })
|
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>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<VueFlow
|
<VueFlow
|
||||||
|
class="vue-flow-basic-example"
|
||||||
:nodes="nodes"
|
:nodes="nodes"
|
||||||
:edges="edges"
|
:edges="edges"
|
||||||
class="vue-flow-basic-example"
|
|
||||||
:default-zoom="1.5"
|
:default-zoom="1.5"
|
||||||
:min-zoom="0.2"
|
:min-zoom="0.2"
|
||||||
:max-zoom="4"
|
:max-zoom="4"
|
||||||
:zoom-on-scroll="false"
|
:zoom-on-scroll="false"
|
||||||
|
@connect="onConnect"
|
||||||
@node-drag-stop="onNodeDragStop"
|
@node-drag-stop="onNodeDragStop"
|
||||||
@node-click="onElementClick"
|
@node-click="onElementClick"
|
||||||
@load="onLoad"
|
@pane-ready="onLoad"
|
||||||
>
|
>
|
||||||
<Background />
|
<Background />
|
||||||
<MiniMap />
|
<MiniMap />
|
||||||
<Controls />
|
<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>
|
</VueFlow>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -2,8 +2,9 @@ import { EmitFunc, FlowHooks, FlowEvents, FlowStore } from '~/types'
|
|||||||
|
|
||||||
// flow event hooks
|
// flow event hooks
|
||||||
export const createHooks = (): FlowHooks => ({
|
export const createHooks = (): FlowHooks => ({
|
||||||
|
edgesChange: createEventHook(),
|
||||||
|
nodesChange: createEventHook(),
|
||||||
elementClick: createEventHook(),
|
elementClick: createEventHook(),
|
||||||
elementsRemove: createEventHook(),
|
|
||||||
nodeDoubleClick: createEventHook(),
|
nodeDoubleClick: createEventHook(),
|
||||||
nodeClick: createEventHook(),
|
nodeClick: createEventHook(),
|
||||||
nodeMouseEnter: createEventHook(),
|
nodeMouseEnter: createEventHook(),
|
||||||
@@ -17,8 +18,7 @@ export const createHooks = (): FlowHooks => ({
|
|||||||
connectStart: createEventHook(),
|
connectStart: createEventHook(),
|
||||||
connectStop: createEventHook(),
|
connectStop: createEventHook(),
|
||||||
connectEnd: createEventHook(),
|
connectEnd: createEventHook(),
|
||||||
elementsProcessed: createEventHook(),
|
paneReady: createEventHook(),
|
||||||
load: createEventHook(),
|
|
||||||
move: createEventHook(),
|
move: createEventHook(),
|
||||||
moveStart: createEventHook(),
|
moveStart: createEventHook(),
|
||||||
moveEnd: createEventHook(),
|
moveEnd: createEventHook(),
|
||||||
|
|||||||
@@ -1,6 +1,90 @@
|
|||||||
import useStore from './useStore'
|
|
||||||
import useHooks from './useHooks'
|
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 => {
|
export const initFlow = (emit: EmitFunc, id?: string): FlowStore => {
|
||||||
const store = useStore({ id })
|
const store = useStore({ id })
|
||||||
@@ -8,4 +92,10 @@ export const initFlow = (emit: EmitFunc, id?: string): FlowStore => {
|
|||||||
return store
|
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>
|
<script lang="ts" setup>
|
||||||
import { FlowElement } from '../../types'
|
import { EdgeChange, FlowElement, NodeChange } from '../../types'
|
||||||
import { useStore, useKeyPress } from '../../composables'
|
import { useStore, useKeyPress } from '../../composables'
|
||||||
import { getConnectedEdges } from '../../utils'
|
import { getConnectedEdges } from '../../utils'
|
||||||
import NodesSelection from '../../components/NodesSelection/NodesSelection.vue'
|
import NodesSelection from '../../components/NodesSelection/NodesSelection.vue'
|
||||||
@@ -19,14 +19,19 @@ const onWheel = (event: WheelEvent) => store.hooks.paneScroll.trigger(event)
|
|||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
useKeyPress(store.deleteKeyCode, (keyPressed) => {
|
useKeyPress(store.deleteKeyCode, (keyPressed) => {
|
||||||
if (keyPressed && (store.selectedNodes || store.selectedEdges)) {
|
const selectedNodes = store.getSelectedNodes
|
||||||
const connectedEdges = (store.selectedNodes && getConnectedEdges(store.selectedNodes, store.getEdges)) ?? []
|
const selectedEdges = store.getSelectedEdges
|
||||||
const elementsToRemove = [...(store.selectedNodes ?? []), ...(store.selectedEdges ?? []), ...connectedEdges].reduce(
|
if (keyPressed && (selectedNodes || selectedEdges)) {
|
||||||
(res, item) => res.set(item.id, item),
|
const connectedEdges = (selectedNodes && getConnectedEdges(selectedNodes, store.edges)) ?? []
|
||||||
new Map<string, FlowElement>(),
|
|
||||||
)
|
|
||||||
|
|
||||||
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.nodesSelectionActive = false
|
||||||
store.resetSelectedElements()
|
store.resetSelectedElements()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -209,7 +209,7 @@ nextTick(async () => {
|
|||||||
getEdges: onLoadGetEdges(store),
|
getEdges: onLoadGetEdges(store),
|
||||||
toObject: onLoadToObject(store),
|
toObject: onLoadToObject(store),
|
||||||
}
|
}
|
||||||
store.hooks.load.trigger(instance)
|
store.hooks.paneReady.trigger(instance)
|
||||||
store.instance = instance
|
store.instance = instance
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
+13
-4
@@ -1,5 +1,5 @@
|
|||||||
import { defaultEdgeTypes, defaultNodeTypes } from './state'
|
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'
|
import { getNodesInside, isEdgeVisible } from '~/utils'
|
||||||
|
|
||||||
export default (state: FlowState): FlowGetters => {
|
export default (state: FlowState): FlowGetters => {
|
||||||
@@ -69,10 +69,17 @@ export default (state: FlowState): FlowGetters => {
|
|||||||
return []
|
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 nodeIds = computed(() => state.nodes.map((n) => n.id))
|
||||||
const getEdge: FlowGetters['getEdge'] = computed(() => (id: string) => state.edges.find((edge) => edge.id === 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 {
|
return {
|
||||||
getNode,
|
getNode,
|
||||||
@@ -82,5 +89,7 @@ export default (state: FlowState): FlowGetters => {
|
|||||||
getEdges,
|
getEdges,
|
||||||
getNodes,
|
getNodes,
|
||||||
getSelectedElements,
|
getSelectedElements,
|
||||||
|
getSelectedNodes,
|
||||||
|
getSelectedEdges,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+22
-18
@@ -13,8 +13,8 @@ export interface FlowState<N = any, E = N> extends Omit<FlowOptions<N, E>, 'id'>
|
|||||||
instance?: FlowInstance
|
instance?: FlowInstance
|
||||||
|
|
||||||
elements: Elements
|
elements: Elements
|
||||||
nodes: GraphNode[]
|
nodes: GraphNode<N>[]
|
||||||
edges: GraphEdge[]
|
edges: GraphEdge<E>[]
|
||||||
|
|
||||||
d3Zoom?: D3Zoom
|
d3Zoom?: D3Zoom
|
||||||
d3Selection?: D3Selection
|
d3Selection?: D3Selection
|
||||||
@@ -71,32 +71,36 @@ export interface FlowState<N = any, E = N> extends Omit<FlowOptions<N, E>, 'id'>
|
|||||||
vueFlowVersion: string
|
vueFlowVersion: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FlowActions {
|
export interface FlowActions<N = any, E = N> {
|
||||||
setElements: (elements: Elements, extent: CoordinateExtent) => void
|
setElements: (elements: Elements<N, E>, extent: CoordinateExtent) => void
|
||||||
setNodes: (nodes: Node[], extent: CoordinateExtent) => void
|
setNodes: (nodes: Node<N>[], extent: CoordinateExtent) => void
|
||||||
setEdges: (edges: Edge[]) => void
|
setEdges: (edges: Edge<E>[]) => void
|
||||||
addSelectedElements: (elements: FlowElements) => void
|
addSelectedElements: (elements: FlowElements<N, E>) => void
|
||||||
addSelectedEdges: (edges: GraphEdge[]) => void
|
addSelectedEdges: (edges: GraphEdge<E>[]) => void
|
||||||
addSelectedNodes: (nodes: GraphNode[]) => void
|
addSelectedNodes: (nodes: GraphNode<N>[]) => void
|
||||||
setMinZoom: (zoom: number) => void
|
setMinZoom: (zoom: number) => void
|
||||||
setMaxZoom: (zoom: number) => void
|
setMaxZoom: (zoom: number) => void
|
||||||
setTranslateExtent: (translateExtent: CoordinateExtent) => void
|
setTranslateExtent: (translateExtent: CoordinateExtent) => void
|
||||||
resetSelectedElements: () => void
|
resetSelectedElements: () => void
|
||||||
setConnectionNodeId: (payload: SetConnectionId) => void
|
setConnectionNodeId: (payload: SetConnectionId) => void
|
||||||
setInteractive: (isInteractive: boolean) => 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
|
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>>
|
getEdgeTypes: ComputedRef<Record<string, EdgeComponent>>
|
||||||
getNodeTypes: ComputedRef<Record<string, NodeComponent>>
|
getNodeTypes: ComputedRef<Record<string, NodeComponent>>
|
||||||
getNodes: ComputedRef<GraphNode[]>
|
getNodes: ComputedRef<GraphNode<N>[]>
|
||||||
getEdges: ComputedRef<GraphEdge[]>
|
getEdges: ComputedRef<GraphEdge<E>[]>
|
||||||
getNode: ComputedRef<(id: string) => GraphNode | undefined>
|
getNode: ComputedRef<(id: string) => GraphNode<N> | undefined>
|
||||||
getEdge: ComputedRef<(id: string) => GraphEdge | undefined>
|
getEdge: ComputedRef<(id: string) => GraphEdge<E> | undefined>
|
||||||
getSelectedElements: ComputedRef<FlowElements>
|
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 Store<N = any, E = N> = { id: string; state: FlowState<N, E> } & ToRefs<FlowState<N, E>> &
|
||||||
export type FlowStore = UnwrapNestedRefs<Store>
|
FlowActions<N, E> &
|
||||||
|
FlowGetters<N, E>
|
||||||
|
export type FlowStore<N = any, E = N> = UnwrapNestedRefs<Store<N, E>>
|
||||||
|
|||||||
Reference in New Issue
Block a user