refactor(store)!: replace store injection with VueFlow injection
This commit is contained in:
+25
-113
@@ -1,121 +1,33 @@
|
|||||||
import {
|
import { getCurrentInstance } from 'vue'
|
||||||
EdgeChange,
|
import { EdgeChange, FlowOptions, GraphEdge, GraphNode, NodeChange, UseVueFlow } from '~/types'
|
||||||
FlowOptions,
|
import { applyChanges } from '~/utils'
|
||||||
NodeChange,
|
import { VueFlow } from '~/context'
|
||||||
UseVueFlow,
|
|
||||||
FlowElements,
|
|
||||||
FlowElement,
|
|
||||||
GraphNode,
|
|
||||||
GraphEdge,
|
|
||||||
SelectionChange,
|
|
||||||
NodeDimensionChange,
|
|
||||||
CreatePositionChangeParams,
|
|
||||||
} from '~/types'
|
|
||||||
import { useStore } from '~/store'
|
import { useStore } from '~/store'
|
||||||
import { clampPosition, isGraphNode } from '~/utils'
|
|
||||||
|
|
||||||
const applyChanges = <T extends FlowElement = GraphNode, C extends NodeChange = T extends GraphNode ? NodeChange : EdgeChange>(
|
|
||||||
changes: C[],
|
|
||||||
elements: T[],
|
|
||||||
): T[] => {
|
|
||||||
let elementIds = elements.map((el) => el.id)
|
|
||||||
changes.forEach((change) => {
|
|
||||||
const i = elementIds.indexOf(change.id)
|
|
||||||
const el = elements[i]
|
|
||||||
if (el) {
|
|
||||||
switch (change.type) {
|
|
||||||
case 'select':
|
|
||||||
el.selected = change.selected
|
|
||||||
break
|
|
||||||
case 'dimensions':
|
|
||||||
if (isGraphNode(el)) {
|
|
||||||
if (typeof change.dimensions !== 'undefined') el.dimensions = change.dimensions
|
|
||||||
if (typeof change.position !== 'undefined') el.position = change.position
|
|
||||||
if (typeof change.dragging !== 'undefined') el.dragging = change.dragging
|
|
||||||
}
|
|
||||||
break
|
|
||||||
case 'remove':
|
|
||||||
elements.splice(i, 1)
|
|
||||||
elementIds = elements.map((el) => el.id)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return elements
|
|
||||||
}
|
|
||||||
|
|
||||||
const applyNodeChanges = (changes: NodeChange[], nodes: GraphNode[]) => applyChanges(changes, nodes)
|
const applyNodeChanges = (changes: NodeChange[], nodes: GraphNode[]) => applyChanges(changes, nodes)
|
||||||
const applyEdgeChanges = (changes: EdgeChange[], edges: GraphEdge[]) => applyChanges(changes, edges)
|
const applyEdgeChanges = (changes: EdgeChange[], edges: GraphEdge[]) => applyChanges(changes, edges)
|
||||||
|
|
||||||
export const createSelectionChange = (id: string, selected: boolean): SelectionChange => ({
|
|
||||||
id,
|
|
||||||
type: 'select',
|
|
||||||
selected,
|
|
||||||
})
|
|
||||||
|
|
||||||
export const createPositionChange = ({ node, diff, dragging, nodeExtent }: CreatePositionChangeParams): NodeDimensionChange => {
|
|
||||||
const change: NodeDimensionChange = {
|
|
||||||
id: node.id,
|
|
||||||
type: 'dimensions',
|
|
||||||
dragging: !!dragging,
|
|
||||||
handleBounds: node.handleBounds,
|
|
||||||
}
|
|
||||||
|
|
||||||
if (diff) {
|
|
||||||
const nextPosition = { x: node.position.x + diff.x, y: node.position.y + diff.y }
|
|
||||||
let currentExtent = nodeExtent || node.extent
|
|
||||||
|
|
||||||
if (node.extent === 'parent' && node.parentNode && node.dimensions.width && node.dimensions.height) {
|
|
||||||
currentExtent =
|
|
||||||
node.parentNode?.dimensions.width && node.parentNode?.dimensions.height
|
|
||||||
? [
|
|
||||||
[0, 0],
|
|
||||||
[
|
|
||||||
node.parentNode.dimensions.width - node.dimensions.width,
|
|
||||||
node.parentNode.dimensions.height - node.dimensions.height,
|
|
||||||
],
|
|
||||||
]
|
|
||||||
: currentExtent
|
|
||||||
}
|
|
||||||
|
|
||||||
change.position = currentExtent ? clampPosition(nextPosition, currentExtent) : nextPosition
|
|
||||||
}
|
|
||||||
|
|
||||||
return change
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getSelectionChanges = (items: FlowElements, selectedIds: string[]) => {
|
|
||||||
return items.reduce((res, item) => {
|
|
||||||
const willBeSelected =
|
|
||||||
selectedIds.includes(item.id) || (isGraphNode(item) && item.parentNode && selectedIds.includes(item.parentNode?.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
|
|
||||||
}, [] as SelectionChange[])
|
|
||||||
}
|
|
||||||
|
|
||||||
export default (options?: FlowOptions): UseVueFlow => {
|
export default (options?: FlowOptions): UseVueFlow => {
|
||||||
const store = useStore(options)
|
const currentInstance = getCurrentInstance()
|
||||||
return {
|
let vueFlow = currentInstance ? inject(VueFlow, undefined) : false
|
||||||
store,
|
if (!vueFlow || (vueFlow && options?.id && options.id !== vueFlow.store.id)) {
|
||||||
useNodesState: (nodes) => {
|
const store = reactive(useStore(options))
|
||||||
store.setNodes(nodes)
|
vueFlow = {
|
||||||
return store.nodes
|
store,
|
||||||
},
|
useNodesState: (nodes) => {
|
||||||
useEdgesState: (edges) => {
|
store.setNodes(nodes)
|
||||||
store.setEdges(edges)
|
return store.nodes
|
||||||
return store.edges
|
},
|
||||||
},
|
useEdgesState: (edges) => {
|
||||||
applyNodeChanges: (changes) => applyNodeChanges(changes, store.nodes),
|
store.setEdges(edges)
|
||||||
applyEdgeChanges: (changes) => applyEdgeChanges(changes, store.edges),
|
return store.edges
|
||||||
...store.hooksOn,
|
},
|
||||||
|
applyNodeChanges: (changes) => applyNodeChanges(changes, store.nodes),
|
||||||
|
applyEdgeChanges: (changes) => applyEdgeChanges(changes, store.edges),
|
||||||
|
...store.hooksOn,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if (currentInstance) provide(VueFlow, vueFlow)
|
||||||
|
|
||||||
|
return vueFlow
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { createHooks, initFlow } from '../../store'
|
import { createHooks, useHooks } from '../../store'
|
||||||
import type { FlowProps } from '../../types/flow'
|
import type { FlowProps } from '../../types/flow'
|
||||||
import ZoomPane from '../ZoomPane/ZoomPane.vue'
|
import ZoomPane from '../ZoomPane/ZoomPane.vue'
|
||||||
|
import { useVueFlow } from '../../composables'
|
||||||
|
|
||||||
const props = withDefaults(defineProps<FlowProps>(), {
|
const props = withDefaults(defineProps<FlowProps>(), {
|
||||||
snapToGrid: false,
|
snapToGrid: false,
|
||||||
@@ -19,8 +20,9 @@ const props = withDefaults(defineProps<FlowProps>(), {
|
|||||||
paneMoveable: true,
|
paneMoveable: true,
|
||||||
})
|
})
|
||||||
const emit = defineEmits([...Object.keys(createHooks())])
|
const emit = defineEmits([...Object.keys(createHooks())])
|
||||||
const store = initFlow(emit, props.id)
|
|
||||||
nextTick(() => store.setState(props))
|
const { store } = useVueFlow(props)
|
||||||
|
useHooks(store, emit)
|
||||||
watch(
|
watch(
|
||||||
() => props,
|
() => props,
|
||||||
(v) => nextTick(() => store.setState(v)),
|
(v) => nextTick(() => store.setState(v)),
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { InjectionKey } from 'vue'
|
import { InjectionKey } from 'vue'
|
||||||
import { Store as TStore } from '~/types'
|
import { UseVueFlow } from '~/types'
|
||||||
|
|
||||||
export const StoreSymbol: InjectionKey<TStore> = Symbol('store')
|
export const VueFlow: InjectionKey<UseVueFlow> = Symbol('vueFlow')
|
||||||
export const NodeId: InjectionKey<string> = Symbol('nodeId')
|
export const NodeId: InjectionKey<string> = Symbol('nodeId')
|
||||||
|
|||||||
+12
-2
@@ -9,8 +9,18 @@ import {
|
|||||||
NodeChange,
|
NodeChange,
|
||||||
NodeDimensionChange,
|
NodeDimensionChange,
|
||||||
} from '~/types'
|
} from '~/types'
|
||||||
import { isEdge, isGraphEdge, isGraphNode, isNode, isParentSelected, parseEdge, parseNode } from '~/utils'
|
import {
|
||||||
import { createPositionChange, createSelectionChange, getSelectionChanges } from '~/composables/useVueFlow'
|
createPositionChange,
|
||||||
|
createSelectionChange,
|
||||||
|
getSelectionChanges,
|
||||||
|
isEdge,
|
||||||
|
isGraphEdge,
|
||||||
|
isGraphNode,
|
||||||
|
isNode,
|
||||||
|
isParentSelected,
|
||||||
|
parseEdge,
|
||||||
|
parseNode,
|
||||||
|
} from '~/utils'
|
||||||
|
|
||||||
const getParent = (root: Node[], id: string): GraphNode | undefined => {
|
const getParent = (root: Node[], id: string): GraphNode | undefined => {
|
||||||
let node
|
let node
|
||||||
|
|||||||
+1
-1
@@ -1,3 +1,3 @@
|
|||||||
export { default as useHooks, createHooks } from './hooks'
|
export { default as useHooks, createHooks } from './hooks'
|
||||||
export { default as useStore, initFlow } from './store'
|
export { default as useStore } from './store'
|
||||||
export * from './state'
|
export * from './state'
|
||||||
|
|||||||
+1
-6
@@ -15,7 +15,7 @@ export const defaultEdgeTypes: DefaultEdgeTypes = {
|
|||||||
smoothstep: SmoothStepEdge,
|
smoothstep: SmoothStepEdge,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const initialState = (): FlowState => ({
|
export default (): FlowState => ({
|
||||||
elements: [],
|
elements: [],
|
||||||
nodes: [],
|
nodes: [],
|
||||||
edges: [],
|
edges: [],
|
||||||
@@ -84,13 +84,8 @@ export const initialState = (): FlowState => ({
|
|||||||
deleteKeyCode: 'Backspace',
|
deleteKeyCode: 'Backspace',
|
||||||
|
|
||||||
hooks: createHooks(),
|
hooks: createHooks(),
|
||||||
loading: undefined,
|
|
||||||
|
|
||||||
storageKey: undefined,
|
storageKey: undefined,
|
||||||
|
|
||||||
vueFlowVersion: typeof __VUE_FLOW_VERSION__ !== 'undefined' ? __VUE_FLOW_VERSION__ : '-',
|
vueFlowVersion: typeof __VUE_FLOW_VERSION__ !== 'undefined' ? __VUE_FLOW_VERSION__ : '-',
|
||||||
})
|
})
|
||||||
|
|
||||||
export default (preloadedState: FlowState) => ({
|
|
||||||
...preloadedState,
|
|
||||||
})
|
|
||||||
|
|||||||
+6
-25
@@ -1,14 +1,11 @@
|
|||||||
import { getCurrentInstance } from 'vue'
|
import useState from './state'
|
||||||
import useState, { initialState } from './state'
|
|
||||||
import useActions from './actions'
|
import useActions from './actions'
|
||||||
import useGetters from './getters'
|
import useGetters from './getters'
|
||||||
import { EmitFunc, FlowExportObject, FlowHooksOn, FlowOptions, FlowState, FlowStore, Store } from '~/types'
|
import { FlowExportObject, FlowHooksOn, FlowOptions, FlowState, Store } from '~/types'
|
||||||
import { StoreSymbol } from '~/context'
|
|
||||||
import { onLoadToObject } from '~/utils'
|
import { onLoadToObject } from '~/utils'
|
||||||
import { useHooks, useStore } from '~/store/index'
|
|
||||||
|
|
||||||
const useFlowStore = (id: string, preloadedState: FlowState): Store => {
|
const useFlowStore = (id: string, preloadedState: FlowState): Store => {
|
||||||
const state = reactive(useState(preloadedState))
|
const state = reactive(useState())
|
||||||
const getters = useGetters(state)
|
const getters = useGetters(state)
|
||||||
const actions = useActions(state, getters)
|
const actions = useActions(state, getters)
|
||||||
const hooksOn: FlowHooksOn = <any>{}
|
const hooksOn: FlowHooksOn = <any>{}
|
||||||
@@ -16,6 +13,7 @@ const useFlowStore = (id: string, preloadedState: FlowState): Store => {
|
|||||||
const name = `On${n.charAt(0).toUpperCase() + n.slice(1)}`
|
const name = `On${n.charAt(0).toUpperCase() + n.slice(1)}`
|
||||||
hooksOn[<keyof FlowHooksOn>name] = h.on as any
|
hooksOn[<keyof FlowHooksOn>name] = h.on as any
|
||||||
})
|
})
|
||||||
|
actions.setState(preloadedState)
|
||||||
return {
|
return {
|
||||||
id,
|
id,
|
||||||
state,
|
state,
|
||||||
@@ -27,10 +25,10 @@ const useFlowStore = (id: string, preloadedState: FlowState): Store => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let id = 0
|
let id = 0
|
||||||
export const createStore = (options?: FlowOptions) => {
|
export default (options?: FlowOptions) => {
|
||||||
const withStorage = options?.storageKey ?? false
|
const withStorage = options?.storageKey ?? false
|
||||||
let storedState = ref<FlowExportObject>()
|
let storedState = ref<FlowExportObject>()
|
||||||
const initial = initialState()
|
const initial = useState()
|
||||||
const storageKey = options?.id ?? `vue-flow-${id++}`
|
const storageKey = options?.id ?? `vue-flow-${id++}`
|
||||||
delete options?.id
|
delete options?.id
|
||||||
const preloadedState = {
|
const preloadedState = {
|
||||||
@@ -61,20 +59,3 @@ export const createStore = (options?: FlowOptions) => {
|
|||||||
}
|
}
|
||||||
return store
|
return store
|
||||||
}
|
}
|
||||||
|
|
||||||
export const initFlow = (emit: EmitFunc, id?: string): FlowStore => {
|
|
||||||
const store = useStore({ id })
|
|
||||||
useHooks(store, emit)
|
|
||||||
return store
|
|
||||||
}
|
|
||||||
|
|
||||||
export default (options?: FlowOptions): FlowStore => {
|
|
||||||
const currentInstance = getCurrentInstance()
|
|
||||||
let store = currentInstance ? inject(StoreSymbol, undefined) : false
|
|
||||||
if (!store || (store && options?.id && options.id !== store.id)) {
|
|
||||||
store = createStore(options)
|
|
||||||
}
|
|
||||||
if (currentInstance) provide(StoreSymbol, store)
|
|
||||||
|
|
||||||
return reactive(store)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,99 @@
|
|||||||
|
import {
|
||||||
|
CreatePositionChangeParams,
|
||||||
|
EdgeChange,
|
||||||
|
FlowElement,
|
||||||
|
FlowElements,
|
||||||
|
GraphNode,
|
||||||
|
NodeChange,
|
||||||
|
NodeDimensionChange,
|
||||||
|
SelectionChange,
|
||||||
|
} from '~/types'
|
||||||
|
import { clampPosition, isGraphNode } from '~/utils/graph'
|
||||||
|
|
||||||
|
export const applyChanges = <
|
||||||
|
T extends FlowElement = GraphNode,
|
||||||
|
C extends NodeChange = T extends GraphNode ? NodeChange : EdgeChange,
|
||||||
|
>(
|
||||||
|
changes: C[],
|
||||||
|
elements: T[],
|
||||||
|
): T[] => {
|
||||||
|
let elementIds = elements.map((el) => el.id)
|
||||||
|
changes.forEach((change) => {
|
||||||
|
const i = elementIds.indexOf(change.id)
|
||||||
|
const el = elements[i]
|
||||||
|
if (el) {
|
||||||
|
switch (change.type) {
|
||||||
|
case 'select':
|
||||||
|
el.selected = change.selected
|
||||||
|
break
|
||||||
|
case 'dimensions':
|
||||||
|
if (isGraphNode(el)) {
|
||||||
|
if (typeof change.dimensions !== 'undefined') el.dimensions = change.dimensions
|
||||||
|
if (typeof change.position !== 'undefined') el.position = change.position
|
||||||
|
if (typeof change.dragging !== 'undefined') el.dragging = change.dragging
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 'remove':
|
||||||
|
elements.splice(i, 1)
|
||||||
|
elementIds = elements.map((el) => el.id)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return elements
|
||||||
|
}
|
||||||
|
|
||||||
|
export const createSelectionChange = (id: string, selected: boolean): SelectionChange => ({
|
||||||
|
id,
|
||||||
|
type: 'select',
|
||||||
|
selected,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const createPositionChange = ({ node, diff, dragging, nodeExtent }: CreatePositionChangeParams): NodeDimensionChange => {
|
||||||
|
const change: NodeDimensionChange = {
|
||||||
|
id: node.id,
|
||||||
|
type: 'dimensions',
|
||||||
|
dragging: !!dragging,
|
||||||
|
handleBounds: node.handleBounds,
|
||||||
|
}
|
||||||
|
|
||||||
|
if (diff) {
|
||||||
|
const nextPosition = { x: node.position.x + diff.x, y: node.position.y + diff.y }
|
||||||
|
let currentExtent = nodeExtent || node.extent
|
||||||
|
|
||||||
|
if (node.extent === 'parent' && node.parentNode && node.dimensions.width && node.dimensions.height) {
|
||||||
|
currentExtent =
|
||||||
|
node.parentNode?.dimensions.width && node.parentNode?.dimensions.height
|
||||||
|
? [
|
||||||
|
[0, 0],
|
||||||
|
[
|
||||||
|
node.parentNode.dimensions.width - node.dimensions.width,
|
||||||
|
node.parentNode.dimensions.height - node.dimensions.height,
|
||||||
|
],
|
||||||
|
]
|
||||||
|
: currentExtent
|
||||||
|
}
|
||||||
|
|
||||||
|
change.position = currentExtent ? clampPosition(nextPosition, currentExtent) : nextPosition
|
||||||
|
}
|
||||||
|
|
||||||
|
return change
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getSelectionChanges = (items: FlowElements, selectedIds: string[]) => {
|
||||||
|
return items.reduce((res, item) => {
|
||||||
|
const willBeSelected =
|
||||||
|
selectedIds.includes(item.id) || (isGraphNode(item) && item.parentNode && selectedIds.includes(item.parentNode?.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
|
||||||
|
}, [] as SelectionChange[])
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
export * from './edge'
|
export * from './edge'
|
||||||
export * from './graph'
|
export * from './graph'
|
||||||
export * from './node'
|
export * from './node'
|
||||||
|
export * from './changes'
|
||||||
|
|||||||
Reference in New Issue
Block a user