refactor(store)!: replace store injection with VueFlow injection

This commit is contained in:
Braks
2021-12-20 19:29:52 +01:00
parent c80ab8fe50
commit 17a1b08ee0
9 changed files with 152 additions and 152 deletions
+25 -113
View File
@@ -1,121 +1,33 @@
import {
EdgeChange,
FlowOptions,
NodeChange,
UseVueFlow,
FlowElements,
FlowElement,
GraphNode,
GraphEdge,
SelectionChange,
NodeDimensionChange,
CreatePositionChangeParams,
} from '~/types'
import { getCurrentInstance } from 'vue'
import { EdgeChange, FlowOptions, GraphEdge, GraphNode, NodeChange, UseVueFlow } from '~/types'
import { applyChanges } from '~/utils'
import { VueFlow } from '~/context'
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 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 => {
const store = useStore(options)
return {
store,
useNodesState: (nodes) => {
store.setNodes(nodes)
return store.nodes
},
useEdgesState: (edges) => {
store.setEdges(edges)
return store.edges
},
applyNodeChanges: (changes) => applyNodeChanges(changes, store.nodes),
applyEdgeChanges: (changes) => applyEdgeChanges(changes, store.edges),
...store.hooksOn,
const currentInstance = getCurrentInstance()
let vueFlow = currentInstance ? inject(VueFlow, undefined) : false
if (!vueFlow || (vueFlow && options?.id && options.id !== vueFlow.store.id)) {
const store = reactive(useStore(options))
vueFlow = {
store,
useNodesState: (nodes) => {
store.setNodes(nodes)
return store.nodes
},
useEdgesState: (edges) => {
store.setEdges(edges)
return store.edges
},
applyNodeChanges: (changes) => applyNodeChanges(changes, store.nodes),
applyEdgeChanges: (changes) => applyEdgeChanges(changes, store.edges),
...store.hooksOn,
}
}
if (currentInstance) provide(VueFlow, vueFlow)
return vueFlow
}
+5 -3
View File
@@ -1,7 +1,8 @@
<script lang="ts" setup>
import { createHooks, initFlow } from '../../store'
import { createHooks, useHooks } from '../../store'
import type { FlowProps } from '../../types/flow'
import ZoomPane from '../ZoomPane/ZoomPane.vue'
import { useVueFlow } from '../../composables'
const props = withDefaults(defineProps<FlowProps>(), {
snapToGrid: false,
@@ -19,8 +20,9 @@ const props = withDefaults(defineProps<FlowProps>(), {
paneMoveable: true,
})
const emit = defineEmits([...Object.keys(createHooks())])
const store = initFlow(emit, props.id)
nextTick(() => store.setState(props))
const { store } = useVueFlow(props)
useHooks(store, emit)
watch(
() => props,
(v) => nextTick(() => store.setState(v)),
+2 -2
View File
@@ -1,5 +1,5 @@
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')
+12 -2
View File
@@ -9,8 +9,18 @@ import {
NodeChange,
NodeDimensionChange,
} from '~/types'
import { isEdge, isGraphEdge, isGraphNode, isNode, isParentSelected, parseEdge, parseNode } from '~/utils'
import { createPositionChange, createSelectionChange, getSelectionChanges } from '~/composables/useVueFlow'
import {
createPositionChange,
createSelectionChange,
getSelectionChanges,
isEdge,
isGraphEdge,
isGraphNode,
isNode,
isParentSelected,
parseEdge,
parseNode,
} from '~/utils'
const getParent = (root: Node[], id: string): GraphNode | undefined => {
let node
+1 -1
View File
@@ -1,3 +1,3 @@
export { default as useHooks, createHooks } from './hooks'
export { default as useStore, initFlow } from './store'
export { default as useStore } from './store'
export * from './state'
+1 -6
View File
@@ -15,7 +15,7 @@ export const defaultEdgeTypes: DefaultEdgeTypes = {
smoothstep: SmoothStepEdge,
}
export const initialState = (): FlowState => ({
export default (): FlowState => ({
elements: [],
nodes: [],
edges: [],
@@ -84,13 +84,8 @@ export const initialState = (): FlowState => ({
deleteKeyCode: 'Backspace',
hooks: createHooks(),
loading: undefined,
storageKey: undefined,
vueFlowVersion: typeof __VUE_FLOW_VERSION__ !== 'undefined' ? __VUE_FLOW_VERSION__ : '-',
})
export default (preloadedState: FlowState) => ({
...preloadedState,
})
+6 -25
View File
@@ -1,14 +1,11 @@
import { getCurrentInstance } from 'vue'
import useState, { initialState } from './state'
import useState from './state'
import useActions from './actions'
import useGetters from './getters'
import { EmitFunc, FlowExportObject, FlowHooksOn, FlowOptions, FlowState, FlowStore, Store } from '~/types'
import { StoreSymbol } from '~/context'
import { FlowExportObject, FlowHooksOn, FlowOptions, FlowState, Store } from '~/types'
import { onLoadToObject } from '~/utils'
import { useHooks, useStore } from '~/store/index'
const useFlowStore = (id: string, preloadedState: FlowState): Store => {
const state = reactive(useState(preloadedState))
const state = reactive(useState())
const getters = useGetters(state)
const actions = useActions(state, getters)
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)}`
hooksOn[<keyof FlowHooksOn>name] = h.on as any
})
actions.setState(preloadedState)
return {
id,
state,
@@ -27,10 +25,10 @@ const useFlowStore = (id: string, preloadedState: FlowState): Store => {
}
let id = 0
export const createStore = (options?: FlowOptions) => {
export default (options?: FlowOptions) => {
const withStorage = options?.storageKey ?? false
let storedState = ref<FlowExportObject>()
const initial = initialState()
const initial = useState()
const storageKey = options?.id ?? `vue-flow-${id++}`
delete options?.id
const preloadedState = {
@@ -61,20 +59,3 @@ export const createStore = (options?: FlowOptions) => {
}
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)
}
+99
View File
@@ -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
View File
@@ -1,3 +1,4 @@
export * from './edge'
export * from './graph'
export * from './node'
export * from './changes'