refactor(store)!: remove pinia
* replace pinia with a reactive state object and inject/provide Signed-off-by: Braks <78412429+bcakmakoglu@users.noreply.github.com> update(store)!: inject store if possible, otherwise create a new one by id Signed-off-by: Braks <78412429+bcakmakoglu@users.noreply.github.com>
This commit is contained in:
@@ -38,8 +38,7 @@
|
||||
"d3": "^7.1.1",
|
||||
"d3-selection": "^3.0.0",
|
||||
"d3-zoom": "^3.0.0",
|
||||
"microdiff": "^1.2.0",
|
||||
"pinia": "^2.0.0-rc.10"
|
||||
"microdiff": "^1.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@antfu/eslint-config": "^0.9.0",
|
||||
|
||||
@@ -54,7 +54,7 @@ export default {
|
||||
@stop="onStop"
|
||||
>
|
||||
<div
|
||||
:key="`vue-flow-nodesselection-rect-${store.$id}`"
|
||||
:key="`vue-flow-nodesselection-rect-${store.id}`"
|
||||
class="vue-flow__nodesselection-rect"
|
||||
:style="innerStyle"
|
||||
@contextmenu="onContextMenu"
|
||||
|
||||
@@ -3,6 +3,6 @@ export { default as useHooks } from './useHooks'
|
||||
export * from './useHooks'
|
||||
export { default as useKeyPress } from './useKeyPress'
|
||||
export { default as useZoomPanHelper } from './useZoomPanHelper'
|
||||
export { default as useStore } from './useStore'
|
||||
export { default as useStore, createStore } from './useStore'
|
||||
export { default as useWindow } from './useWindow'
|
||||
export { default as useVueFlow, initFlow } from './useVueFlow'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import useStore from './useStore'
|
||||
import { getHostForElement } from '~/utils'
|
||||
import { Connection, ConnectionMode, ElementId, HandleType, ValidConnectionFunc } from '~/types'
|
||||
import { Connection, ConnectionMode, ElementId, HandleType, ReactiveFlowStore, ValidConnectionFunc } from '~/types'
|
||||
|
||||
type Result = {
|
||||
elementBelow: Element | null
|
||||
@@ -67,7 +67,7 @@ const resetRecentHandle = (hoveredHandle: Element): void => {
|
||||
hoveredHandle?.classList.remove('vue-flow__handle-connecting')
|
||||
}
|
||||
|
||||
export default (store = useStore()) =>
|
||||
export default (store: ReactiveFlowStore = useStore()) =>
|
||||
(
|
||||
event: MouseEvent,
|
||||
handleId: ElementId,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { EmitFunc, FlowHooks, FlowEvents, FlowStore } from '~/types'
|
||||
import { EmitFunc, FlowHooks, FlowEvents, FlowStore, ReactiveFlowStore } from '~/types'
|
||||
|
||||
// flow event hooks
|
||||
export const createHooks = (): FlowHooks => ({
|
||||
@@ -49,4 +49,4 @@ const bind = (emit: EmitFunc, hooks: FlowHooks) => {
|
||||
}
|
||||
}
|
||||
|
||||
export default (store: FlowStore, emit: EmitFunc) => bind(emit, store.hooks)
|
||||
export default (store: ReactiveFlowStore, emit: EmitFunc) => bind(emit, store.hooks)
|
||||
|
||||
@@ -1,38 +1,46 @@
|
||||
import { FlowExportObject, FlowOptions, FlowState } from '~/types'
|
||||
import { useStateStore } from '~/store'
|
||||
import { FlowExportObject, FlowOptions, FlowState, ReactiveFlowStore } from '~/types'
|
||||
import { useNewStore } from '~/store'
|
||||
import { Store } from '~/context'
|
||||
import { onLoadToObject, initialState } from '~/utils'
|
||||
|
||||
let id = 0
|
||||
export default (options?: Partial<FlowOptions>, key?: string) => {
|
||||
let store = inject(Store, undefined)
|
||||
if (!store) {
|
||||
const withStorage = options?.storageKey ?? key ?? false
|
||||
if (import.meta.env.DEV) console.warn('store context not found; creating default store')
|
||||
let storedState = ref<FlowExportObject>()
|
||||
const initial = initialState()
|
||||
const storageKey = key ?? `vue-flow-${id++}`
|
||||
const preloadedState = {
|
||||
...initial,
|
||||
...(options as FlowState),
|
||||
}
|
||||
if (withStorage) {
|
||||
storedState = useStorage<FlowExportObject>(storageKey, { elements: [], position: [0, 0], zoom: 0 })
|
||||
if (storedState.value) {
|
||||
preloadedState.elements = storedState.value.elements ?? options?.elements ?? []
|
||||
if (storedState.value.position && storedState.value.zoom)
|
||||
preloadedState.transform = [storedState.value.position[0], storedState.value.position[1], storedState.value.zoom]
|
||||
}
|
||||
}
|
||||
store = useStateStore(storageKey, preloadedState)()
|
||||
if (withStorage && storageKey === store.$id) {
|
||||
const toObject = onLoadToObject(store)
|
||||
store.$subscribe(() => {
|
||||
storedState.value = toObject()
|
||||
})
|
||||
}
|
||||
provide(Store, store)
|
||||
export const createStore = (options?: FlowOptions) => {
|
||||
const withStorage = options?.storageKey ?? false
|
||||
let storedState = ref<FlowExportObject>()
|
||||
const initial = initialState()
|
||||
const storageKey = options?.id ?? `vue-flow-${id++}`
|
||||
const preloadedState = {
|
||||
...initial,
|
||||
...(options as FlowState),
|
||||
}
|
||||
if (withStorage) {
|
||||
storedState = useStorage<FlowExportObject>(storageKey, { elements: [], position: [0, 0], zoom: 0 })
|
||||
if (storedState.value) {
|
||||
preloadedState.elements = storedState.value.elements ?? options?.elements ?? []
|
||||
if (storedState.value.position && storedState.value.zoom)
|
||||
preloadedState.transform = [storedState.value.position[0], storedState.value.position[1], storedState.value.zoom]
|
||||
}
|
||||
}
|
||||
const store = useNewStore(storageKey, preloadedState)
|
||||
if (withStorage && storageKey === store.id?.value) {
|
||||
const toObject = onLoadToObject(store.state)
|
||||
watch(
|
||||
store.state,
|
||||
() => {
|
||||
storedState.value = toObject()
|
||||
},
|
||||
{ deep: true },
|
||||
)
|
||||
}
|
||||
|
||||
return store
|
||||
}
|
||||
|
||||
export default (options?: FlowOptions): ReactiveFlowStore => {
|
||||
let store = options?.id ? createStore(options) : inject(Store, undefined)
|
||||
if (!store) {
|
||||
store = createStore(options)
|
||||
}
|
||||
provide(Store, store)
|
||||
|
||||
return reactive(store)
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import useStore from './useStore'
|
||||
import useHooks from './useHooks'
|
||||
import { EmitFunc, FlowOptions, FlowStore } from '~/types'
|
||||
import { EmitFunc, ReactiveFlowStore } from '~/types'
|
||||
|
||||
export const initFlow = (emit: EmitFunc, key?: string, store = useStore(undefined, key)) => {
|
||||
let key = 0
|
||||
export const initFlow = (emit: EmitFunc, id = `vue-flow-${key++}`): ReactiveFlowStore => {
|
||||
const store = useStore({ id })
|
||||
useHooks(store, emit)
|
||||
return store
|
||||
}
|
||||
|
||||
export default (options?: Partial<FlowOptions>): FlowStore => useStore(options)
|
||||
export default useStore
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { zoomIdentity } from 'd3-zoom'
|
||||
import useStore from './useStore'
|
||||
import { getRectOfNodes, pointToRendererPoint, getTransformForBounds, isGraphNode } from '~/utils'
|
||||
import { FitViewParams, FlowTransform, GraphNode, Rect, UseZoomPanHelper, XYPosition } from '~/types'
|
||||
import { FitViewParams, FlowTransform, GraphNode, ReactiveFlowStore, Rect, UseZoomPanHelper, XYPosition } from '~/types'
|
||||
|
||||
const DEFAULT_PADDING = 0.1
|
||||
|
||||
export default (store = useStore()): UseZoomPanHelper => {
|
||||
export default (store: ReactiveFlowStore = useStore()): UseZoomPanHelper => {
|
||||
return {
|
||||
zoomIn: () => store.d3Selection && store.d3Zoom?.scaleBy(store.d3Selection, 1.2),
|
||||
zoomOut: () => store.d3Selection && store.d3Zoom?.scaleBy(store.d3Selection, 1 / 1.2),
|
||||
@@ -14,7 +14,13 @@ export default (store = useStore()): UseZoomPanHelper => {
|
||||
const nextTransform = zoomIdentity.translate(transform.x, transform.y).scale(transform.zoom)
|
||||
store.d3Selection && store.d3Zoom?.transform(store.d3Selection, nextTransform)
|
||||
},
|
||||
fitView: (options: FitViewParams = { padding: DEFAULT_PADDING, includeHiddenNodes: false, transitionDuration: 0 }) => {
|
||||
fitView: (
|
||||
options: FitViewParams = {
|
||||
padding: DEFAULT_PADDING,
|
||||
includeHiddenNodes: false,
|
||||
transitionDuration: 0,
|
||||
},
|
||||
) => {
|
||||
if (!store.getNodes.length) return
|
||||
|
||||
let nodes: GraphNode[] = []
|
||||
|
||||
@@ -70,12 +70,12 @@ export default {
|
||||
</script>
|
||||
<template>
|
||||
<slot></slot>
|
||||
<UserSelection v-if="userSelection" id="user-selection" :key="`user-selection-${store.$id}`" />
|
||||
<UserSelection v-if="userSelection" id="user-selection" :key="`user-selection-${store.id}`" />
|
||||
<NodesSelection
|
||||
v-if="props.nodesSelectionActive"
|
||||
id="nodes-selection"
|
||||
:key="`nodes-selction-${store.$id}`"
|
||||
:key="`nodes-selction-${store.id}`"
|
||||
:selected-elements="props.selectedElements"
|
||||
/>
|
||||
<div :key="`flow-pane-${store.$id}`" class="vue-flow__pane" @click="onClick" @contextmenu="onContextMenu" @wheel="onWheel" />
|
||||
<div :key="`flow-pane-${store.id}`" class="vue-flow__pane" @click="onClick" @contextmenu="onContextMenu" @wheel="onWheel" />
|
||||
</template>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
import { CSSProperties, onBeforeUnmount } from 'vue'
|
||||
import { CSSProperties } from 'vue'
|
||||
import {
|
||||
ConnectionLineType,
|
||||
ConnectionMode,
|
||||
@@ -64,6 +64,8 @@ interface FlowProps extends FlowOptions {
|
||||
|
||||
const props = withDefaults(defineProps<FlowProps>(), {
|
||||
modelValue: () => [],
|
||||
edgeTypes: () => [],
|
||||
nodeTypes: () => [],
|
||||
connectionMode: ConnectionMode.Loose,
|
||||
connectionLineType: ConnectionLineType.Bezier,
|
||||
selectionKeyCode: 'Shift',
|
||||
@@ -103,10 +105,9 @@ const props = withDefaults(defineProps<FlowProps>(), {
|
||||
loading: false,
|
||||
})
|
||||
const emit = defineEmits([...Object.keys(createHooks()), 'update:modelValue'])
|
||||
const store = initFlow(emit, typeof props.storageKey === 'string' ? props.storageKey : props.id)
|
||||
const store = initFlow(emit, props.id)
|
||||
const elements = useVModel(props, 'modelValue', emit)
|
||||
const options = Object.assign({}, store.$state, props)
|
||||
store.setState(options)
|
||||
store.setState(props)
|
||||
watch(
|
||||
() => props,
|
||||
() => store.setState(props),
|
||||
@@ -144,22 +145,16 @@ const transitionName = computed(() => {
|
||||
}
|
||||
return name
|
||||
})
|
||||
</script>
|
||||
|
||||
onBeforeUnmount(() => store?.$dispose())
|
||||
</script>
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'VueFlow'
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<div class="vue-flow">
|
||||
<Transition :key="`vue-flow-transition-${store.$id}`" :name="transitionName">
|
||||
<Transition :key="`vue-flow-transition-${store.id}`" :name="transitionName">
|
||||
<Suspense timeout="0">
|
||||
<template #default>
|
||||
<Renderer :key="`renderer-${store.$id}`" :elements="elements">
|
||||
<Renderer :key="`renderer-${store.id}`" :elements="elements">
|
||||
<ZoomPane
|
||||
:key="`zoom-pane-${store.$id}`"
|
||||
:key="`zoom-pane-${store.id}`"
|
||||
:prevent-scrolling="store.preventScrolling"
|
||||
:selection-key-code="store.selectionKeyCode"
|
||||
:zoom-activation-key-code="store.zoomActivationKeyCode"
|
||||
@@ -175,7 +170,7 @@ export default {
|
||||
>
|
||||
<template #default="zoomPaneProps">
|
||||
<SelectionPane
|
||||
:key="`selection-pane-${store.$id}`"
|
||||
:key="`selection-pane-${store.id}`"
|
||||
:edges="store.getEdges"
|
||||
:selected-elements="store.selectedElements"
|
||||
:selection-active="store.selectionActive"
|
||||
@@ -186,7 +181,7 @@ export default {
|
||||
:selection-key-code="store.selectionKeyCode"
|
||||
>
|
||||
<NodeRenderer
|
||||
:key="`node-renderer-${store.$id}`"
|
||||
:key="`node-renderer-${store.id}`"
|
||||
:nodes="store.getNodes"
|
||||
:node-types="store.getNodeTypes"
|
||||
:snap-to-grid="store.snapToGrid"
|
||||
@@ -200,13 +195,13 @@ export default {
|
||||
<template
|
||||
v-for="nodeName of Object.keys(store.getNodeTypes)"
|
||||
#[`node-${nodeName}`]="nodeProps"
|
||||
:key="`node-${nodeName}-${store.$id}`"
|
||||
:key="`node-${nodeName}-${store.id}`"
|
||||
>
|
||||
<slot :name="`node-${nodeName}`" v-bind="nodeProps" />
|
||||
</template>
|
||||
</NodeRenderer>
|
||||
<EdgeRenderer
|
||||
:key="`edge-renderer-${store.$id}`"
|
||||
:key="`edge-renderer-${store.id}`"
|
||||
:transform="store.transform"
|
||||
:dimensions="store.dimensions"
|
||||
:edges="store.getEdges"
|
||||
@@ -230,13 +225,13 @@ export default {
|
||||
<template
|
||||
v-for="edgeName of Object.keys(store.getEdgeTypes)"
|
||||
#[`edge-${edgeName}`]="edgeProps"
|
||||
:key="`edge-${edgeName}-${store.$id}`"
|
||||
:key="`edge-${edgeName}-${store.id}`"
|
||||
>
|
||||
<slot :name="`edge-${edgeName}`" v-bind="edgeProps" />
|
||||
</template>
|
||||
<template #custom-connection-line="customConnectionLineProps">
|
||||
<slot
|
||||
:key="`connection-line-${store.$id}`"
|
||||
:key="`connection-line-${store.id}`"
|
||||
name="custom-connection-line"
|
||||
v-bind="customConnectionLineProps"
|
||||
/>
|
||||
@@ -249,8 +244,8 @@ export default {
|
||||
</Renderer>
|
||||
</template>
|
||||
<template v-if="store.loading" #fallback>
|
||||
<slot :key="`loading-indicator-${store.$id}`" name="loading-indicator">
|
||||
<LoadingIndicator :key="`default-loading-indicator-${store.$id}`" v-bind="store.loading">
|
||||
<slot :key="`loading-indicator-${store.id}`" name="loading-indicator">
|
||||
<LoadingIndicator :key="`default-loading-indicator-${store.id}`" v-bind="store.loading">
|
||||
<slot name="loading-label" />
|
||||
</LoadingIndicator>
|
||||
</slot>
|
||||
|
||||
169
src/store/actions.ts
Normal file
169
src/store/actions.ts
Normal file
@@ -0,0 +1,169 @@
|
||||
import microDiff from 'microdiff'
|
||||
import { Elements, FlowActions, FlowGetters, FlowState } from '~/types'
|
||||
import { getConnectedEdges, getNodesInside, getRectOfNodes, parseElements, processElements } from '~/utils'
|
||||
|
||||
export default (state: FlowState, getters: FlowGetters): FlowActions => {
|
||||
const setElements: FlowActions['setElements'] = async (elements, force = true) => {
|
||||
const { nodes, edges } = parseElements(elements, state.elements, state.nodeExtent)
|
||||
if (force) state.elements = []
|
||||
await processElements([...nodes, ...edges], (processed) => {
|
||||
state.elements = [...state.elements, ...processed]
|
||||
})
|
||||
state.hooks.elementsProcessed.trigger(state.elements)
|
||||
}
|
||||
const setUserSelection: FlowActions['setUserSelection'] = (mousePos) => {
|
||||
state.selectionActive = true
|
||||
state.userSelectionRect = {
|
||||
width: 0,
|
||||
height: 0,
|
||||
startX: mousePos.x,
|
||||
startY: mousePos.y,
|
||||
x: mousePos.x,
|
||||
y: mousePos.y,
|
||||
draw: true,
|
||||
}
|
||||
}
|
||||
const updateUserSelection: FlowActions['updateUserSelection'] = (mousePos) => {
|
||||
const startX = state.userSelectionRect.startX
|
||||
const startY = state.userSelectionRect.startY
|
||||
|
||||
const nextUserSelectRect: FlowState['userSelectionRect'] = {
|
||||
...state.userSelectionRect,
|
||||
x: mousePos.x < startX ? mousePos.x : state.userSelectionRect.x,
|
||||
y: mousePos.y < startY ? mousePos.y : state.userSelectionRect.y,
|
||||
width: Math.abs(mousePos.x - startX),
|
||||
height: Math.abs(mousePos.y - startY),
|
||||
}
|
||||
const selectedNodes = getNodesInside(getters.getNodes.value, state.userSelectionRect, state.transform)
|
||||
const selectedEdges = getConnectedEdges(selectedNodes, getters.getEdges.value)
|
||||
|
||||
const nextSelectedElements = [...selectedNodes, ...selectedEdges]
|
||||
state.userSelectionRect = nextUserSelectRect
|
||||
state.selectedElements = nextSelectedElements
|
||||
}
|
||||
const unsetUserSelection: FlowActions['unsetUserSelection'] = () => {
|
||||
state.selectionActive = false
|
||||
state.userSelectionRect.draw = false
|
||||
|
||||
if (!getters.getSelectedNodes || getters.getSelectedNodes.value.length === 0) {
|
||||
state.selectedElements = undefined
|
||||
state.hooks.selectionChange.trigger(undefined)
|
||||
state.nodesSelectionActive = false
|
||||
} else {
|
||||
state.selectedNodesBbox = getRectOfNodes(getters.getSelectedNodes.value)
|
||||
state.nodesSelectionActive = true
|
||||
}
|
||||
}
|
||||
const addSelectedElements: FlowActions['addSelectedElements'] = (elements: any) => {
|
||||
const selectedElementsArr = Array.isArray(elements) ? elements : [elements]
|
||||
const selectedElementsUpdated = microDiff(selectedElementsArr, state.selectedElements ?? []).length
|
||||
state.selectedElements = selectedElementsUpdated ? selectedElementsArr : state.selectedElements
|
||||
if (selectedElementsUpdated) state.hooks.selectionChange.trigger(selectedElementsArr)
|
||||
}
|
||||
const initD3Zoom: FlowActions['initD3Zoom'] = ({ d3ZoomHandler, d3Zoom, d3Selection }) => {
|
||||
state.d3Zoom = d3Zoom
|
||||
state.d3Selection = d3Selection
|
||||
state.d3ZoomHandler = d3ZoomHandler
|
||||
}
|
||||
const setMinZoom: FlowActions['setMinZoom'] = (minZoom: any) => {
|
||||
state.d3Zoom?.scaleExtent([minZoom, state.maxZoom])
|
||||
state.minZoom = minZoom
|
||||
}
|
||||
const setMaxZoom: FlowActions['setMaxZoom'] = (maxZoom: any) => {
|
||||
state.d3Zoom?.scaleExtent([state.minZoom, maxZoom])
|
||||
state.maxZoom = maxZoom
|
||||
}
|
||||
const setTranslateExtent: FlowActions['setTranslateExtent'] = (translateExtent: any) => {
|
||||
state.d3Zoom?.translateExtent(translateExtent)
|
||||
state.translateExtent = translateExtent
|
||||
}
|
||||
const resetSelectedElements: FlowActions['resetSelectedElements'] = () => {
|
||||
state.selectedElements = undefined
|
||||
}
|
||||
const unsetNodesSelection: FlowActions['unsetNodesSelection'] = () => {
|
||||
state.nodesSelectionActive = false
|
||||
}
|
||||
const updateSize: FlowActions['updateSize'] = (size) => {
|
||||
state.dimensions = size
|
||||
}
|
||||
const setConnectionNodeId: FlowActions['setConnectionNodeId'] = ({
|
||||
connectionHandleId,
|
||||
connectionHandleType,
|
||||
connectionNodeId,
|
||||
}) => {
|
||||
state.connectionNodeId = connectionNodeId
|
||||
state.connectionHandleId = connectionHandleId
|
||||
state.connectionHandleType = connectionHandleType
|
||||
}
|
||||
const setInteractive: FlowActions['setInteractive'] = (isInteractive) => {
|
||||
state.nodesDraggable = isInteractive
|
||||
state.nodesConnectable = isInteractive
|
||||
state.elementsSelectable = isInteractive
|
||||
}
|
||||
const addElements: FlowActions['addElements'] = (elements: Elements) => {
|
||||
setElements(elements, false)
|
||||
}
|
||||
const setState: FlowActions['setState'] = (newState) => {
|
||||
if (typeof newState.loading !== 'undefined') state.loading = newState.loading
|
||||
if (typeof newState.panOnScroll !== 'undefined') state.panOnScroll = newState.panOnScroll
|
||||
if (typeof newState.panOnScrollMode !== 'undefined') state.panOnScrollMode = newState.panOnScrollMode
|
||||
if (typeof newState.panOnScrollSpeed !== 'undefined') state.panOnScrollSpeed = newState.panOnScrollSpeed
|
||||
if (typeof newState.paneMoveable !== 'undefined') state.paneMoveable = newState.paneMoveable
|
||||
if (typeof newState.zoomOnScroll !== 'undefined') state.zoomOnScroll = newState.zoomOnScroll
|
||||
if (typeof newState.preventScrolling !== 'undefined') state.preventScrolling = newState.preventScrolling
|
||||
if (typeof newState.zoomOnDoubleClick !== 'undefined') state.zoomOnDoubleClick = newState.zoomOnDoubleClick
|
||||
if (typeof newState.zoomOnPinch !== 'undefined') state.zoomOnPinch = newState.zoomOnPinch
|
||||
if (typeof newState.defaultZoom !== 'undefined') state.defaultZoom = newState.defaultZoom
|
||||
if (typeof newState.defaultPosition !== 'undefined') state.defaultPosition = newState.defaultPosition
|
||||
if (typeof newState.edgeTypes !== 'undefined') state.edgeTypes = newState.edgeTypes
|
||||
if (typeof newState.nodeTypes !== 'undefined') state.nodeTypes = newState.nodeTypes
|
||||
if (typeof newState.storageKey !== 'undefined') state.storageKey = newState.storageKey
|
||||
if (typeof newState.edgeUpdaterRadius !== 'undefined') state.edgeUpdaterRadius = newState.edgeUpdaterRadius
|
||||
if (typeof newState.elementsSelectable !== 'undefined') state.elementsSelectable = newState.elementsSelectable
|
||||
if (typeof newState.onlyRenderVisibleElements !== 'undefined')
|
||||
state.onlyRenderVisibleElements = newState.onlyRenderVisibleElements
|
||||
if (typeof newState.edgesUpdatable !== 'undefined') state.edgesUpdatable = newState.edgesUpdatable
|
||||
if (typeof newState.nodesConnectable !== 'undefined') state.nodesConnectable = newState.nodesConnectable
|
||||
if (typeof newState.nodesDraggable !== 'undefined') state.nodesDraggable = newState.nodesDraggable
|
||||
if (typeof newState.arrowHeadColor !== 'undefined') state.arrowHeadColor = newState.arrowHeadColor
|
||||
if (typeof newState.markerEndId !== 'undefined') state.markerEndId = newState.markerEndId
|
||||
if (typeof newState.deleteKeyCode !== 'undefined') state.deleteKeyCode = newState.deleteKeyCode
|
||||
if (typeof newState.selectionKeyCode !== 'undefined') state.selectionKeyCode = newState.selectionKeyCode
|
||||
if (typeof newState.zoomActivationKeyCode !== 'undefined') state.zoomActivationKeyCode = newState.zoomActivationKeyCode
|
||||
if (typeof newState.multiSelectionKeyCode !== 'undefined') state.multiSelectionKeyCode = newState.multiSelectionKeyCode
|
||||
if (typeof newState.snapToGrid !== 'undefined') state.snapToGrid = newState.snapToGrid
|
||||
if (typeof newState.snapGrid !== 'undefined') state.snapGrid = newState.snapGrid
|
||||
if (typeof newState.nodeExtent !== 'undefined') state.nodeExtent = newState.nodeExtent
|
||||
if (!state.isReady)
|
||||
until(() => state.d3Zoom)
|
||||
.not.toBeUndefined()
|
||||
.then(() => {
|
||||
if (typeof newState.maxZoom !== 'undefined') setMaxZoom(newState.maxZoom)
|
||||
if (typeof newState.minZoom !== 'undefined') setMinZoom(newState.minZoom)
|
||||
if (typeof newState.translateExtent !== 'undefined') setTranslateExtent(newState.translateExtent)
|
||||
})
|
||||
else {
|
||||
if (typeof newState.maxZoom !== 'undefined') setMaxZoom(newState.maxZoom)
|
||||
if (typeof newState.minZoom !== 'undefined') setMinZoom(newState.minZoom)
|
||||
if (typeof newState.translateExtent !== 'undefined') setTranslateExtent(newState.translateExtent)
|
||||
}
|
||||
}
|
||||
return {
|
||||
setElements,
|
||||
setUserSelection,
|
||||
updateUserSelection,
|
||||
unsetUserSelection,
|
||||
addSelectedElements,
|
||||
initD3Zoom,
|
||||
setMinZoom,
|
||||
setMaxZoom,
|
||||
setTranslateExtent,
|
||||
resetSelectedElements,
|
||||
unsetNodesSelection,
|
||||
updateSize,
|
||||
setConnectionNodeId,
|
||||
setInteractive,
|
||||
addElements,
|
||||
setState,
|
||||
}
|
||||
}
|
||||
73
src/store/getters.ts
Normal file
73
src/store/getters.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
import { FlowGetters, FlowState, GraphEdge, GraphNode } from '~/types'
|
||||
import { defaultEdgeTypes, defaultNodeTypes, getNodesInside, getSourceTargetNodes, isEdge, isGraphNode } from '~/utils'
|
||||
|
||||
export default (state: FlowState): FlowGetters => {
|
||||
const getEdgeTypes = computed(() => {
|
||||
const edgeTypes: Record<string, any> = {
|
||||
...defaultEdgeTypes,
|
||||
}
|
||||
state.edgeTypes?.forEach((n) => (edgeTypes[n] = n))
|
||||
return edgeTypes
|
||||
})
|
||||
|
||||
const getNodeTypes = computed(() => {
|
||||
const nodeTypes: Record<string, any> = {
|
||||
...defaultNodeTypes,
|
||||
}
|
||||
state.nodeTypes?.forEach((n) => (nodeTypes[n] = n))
|
||||
return nodeTypes
|
||||
})
|
||||
|
||||
const getNodes = computed<GraphNode[]>(() => {
|
||||
if (state.isReady) {
|
||||
const nodes = state.elements.filter((n) => isGraphNode(n) && !n.isHidden) as GraphNode[]
|
||||
return state.onlyRenderVisibleElements
|
||||
? nodes &&
|
||||
getNodesInside(
|
||||
nodes,
|
||||
{
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: state.dimensions.width,
|
||||
height: state.dimensions.height,
|
||||
},
|
||||
state.transform,
|
||||
true,
|
||||
)
|
||||
: nodes ?? []
|
||||
}
|
||||
return []
|
||||
})
|
||||
|
||||
const getEdges = computed<GraphEdge[]>(() => {
|
||||
const edges = state.elements.filter((e) => isEdge(e) && !e.isHidden) as GraphEdge[]
|
||||
if (state.isReady) {
|
||||
return (
|
||||
edges
|
||||
.map((edge) => {
|
||||
const { sourceNode, targetNode } = getSourceTargetNodes(edge, getNodes.value)
|
||||
if (!sourceNode) console.warn(`couldn't create edge for source id: ${edge.source}; edge id: ${edge.id}`)
|
||||
if (!targetNode) console.warn(`couldn't create edge for target id: ${edge.target}; edge id: ${edge.id}`)
|
||||
|
||||
return {
|
||||
...edge,
|
||||
sourceNode,
|
||||
targetNode,
|
||||
}
|
||||
})
|
||||
.filter(({ sourceNode, targetNode }) => !!(sourceNode && targetNode)) ?? []
|
||||
)
|
||||
}
|
||||
return []
|
||||
})
|
||||
|
||||
const getSelectedNodes = computed<GraphNode[]>(() => state.selectedElements?.filter(isGraphNode) ?? [])
|
||||
|
||||
return {
|
||||
getEdgeTypes,
|
||||
getNodeTypes,
|
||||
getEdges,
|
||||
getNodes,
|
||||
getSelectedNodes,
|
||||
}
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
export { default as useStateStore } from './stateStore'
|
||||
export { default as useNewStore } from './store'
|
||||
|
||||
5
src/store/state.ts
Normal file
5
src/store/state.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { FlowState } from '~/types'
|
||||
|
||||
export default (preloadedState: FlowState) => ({
|
||||
...preloadedState,
|
||||
})
|
||||
@@ -1,241 +0,0 @@
|
||||
import microDiff from 'microdiff'
|
||||
import { setActivePinia, createPinia, defineStore, StoreDefinition, acceptHMRUpdate, getActivePinia } from 'pinia'
|
||||
import { FlowState, FlowActions, Elements, FlowGetters, GraphNode, GraphEdge } from '~/types'
|
||||
import {
|
||||
getConnectedEdges,
|
||||
getNodesInside,
|
||||
getRectOfNodes,
|
||||
defaultNodeTypes,
|
||||
defaultEdgeTypes,
|
||||
isGraphNode,
|
||||
getSourceTargetNodes,
|
||||
isEdge,
|
||||
parseElements,
|
||||
processElements,
|
||||
} from '~/utils'
|
||||
|
||||
const pinia = createPinia()
|
||||
|
||||
export default (id: string, preloadedState: FlowState) => {
|
||||
setActivePinia(getActivePinia() ?? pinia)
|
||||
|
||||
const store: StoreDefinition<string, FlowState, FlowGetters, FlowActions> = defineStore({
|
||||
id: id ?? 'vue-flow',
|
||||
state: () => ({
|
||||
...preloadedState,
|
||||
}),
|
||||
getters: {
|
||||
getEdgeTypes() {
|
||||
const edgeTypes: Record<string, any> = {
|
||||
...defaultEdgeTypes,
|
||||
}
|
||||
this.edgeTypes?.forEach((n) => (edgeTypes[n] = n))
|
||||
return edgeTypes
|
||||
},
|
||||
getNodeTypes() {
|
||||
const nodeTypes: Record<string, any> = {
|
||||
...defaultNodeTypes,
|
||||
}
|
||||
this.nodeTypes?.forEach((n) => (nodeTypes[n] = n))
|
||||
return nodeTypes
|
||||
},
|
||||
getNodes(): GraphNode[] {
|
||||
if (this.isReady) {
|
||||
const nodes = this.elements.filter((n) => isGraphNode(n) && !n.isHidden) as GraphNode[]
|
||||
return this.onlyRenderVisibleElements
|
||||
? nodes &&
|
||||
getNodesInside(
|
||||
nodes,
|
||||
{
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: this.dimensions.width,
|
||||
height: this.dimensions.height,
|
||||
},
|
||||
this.transform,
|
||||
true,
|
||||
)
|
||||
: nodes ?? []
|
||||
}
|
||||
return []
|
||||
},
|
||||
getEdges(): GraphEdge[] {
|
||||
const edges = this.elements.filter((e) => isEdge(e) && !e.isHidden) as GraphEdge[]
|
||||
if (this.isReady) {
|
||||
return (
|
||||
edges
|
||||
.map((edge) => {
|
||||
const { sourceNode, targetNode } = getSourceTargetNodes(edge, this.getNodes)
|
||||
if (!sourceNode) console.warn(`couldn't create edge for source id: ${edge.source}; edge id: ${edge.id}`)
|
||||
if (!targetNode) console.warn(`couldn't create edge for target id: ${edge.target}; edge id: ${edge.id}`)
|
||||
|
||||
return {
|
||||
...edge,
|
||||
sourceNode,
|
||||
targetNode,
|
||||
}
|
||||
})
|
||||
.filter(({ sourceNode, targetNode }) => !!(sourceNode && targetNode)) ?? []
|
||||
)
|
||||
}
|
||||
return []
|
||||
},
|
||||
getSelectedNodes(): GraphNode[] {
|
||||
return this.selectedElements?.filter(isGraphNode) ?? []
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
async setElements(elements, force = true) {
|
||||
const { nodes, edges } = parseElements(elements, this.elements, this.nodeExtent)
|
||||
if (force) this.elements = []
|
||||
await processElements([...nodes, ...edges], (processed) => {
|
||||
this.elements = [...this.elements, ...processed]
|
||||
})
|
||||
this.hooks.elementsProcessed.trigger(this.elements)
|
||||
},
|
||||
setUserSelection(mousePos) {
|
||||
this.selectionActive = true
|
||||
this.userSelectionRect = {
|
||||
width: 0,
|
||||
height: 0,
|
||||
startX: mousePos.x,
|
||||
startY: mousePos.y,
|
||||
x: mousePos.x,
|
||||
y: mousePos.y,
|
||||
draw: true,
|
||||
}
|
||||
},
|
||||
updateUserSelection(mousePos) {
|
||||
const startX = this.userSelectionRect.startX
|
||||
const startY = this.userSelectionRect.startY
|
||||
|
||||
const nextUserSelectRect: FlowState['userSelectionRect'] = {
|
||||
...this.userSelectionRect,
|
||||
x: mousePos.x < startX ? mousePos.x : this.userSelectionRect.x,
|
||||
y: mousePos.y < startY ? mousePos.y : this.userSelectionRect.y,
|
||||
width: Math.abs(mousePos.x - startX),
|
||||
height: Math.abs(mousePos.y - startY),
|
||||
}
|
||||
const selectedNodes = getNodesInside(this.getNodes, this.userSelectionRect, this.transform)
|
||||
const selectedEdges = getConnectedEdges(selectedNodes, this.getEdges)
|
||||
|
||||
const nextSelectedElements = [...selectedNodes, ...selectedEdges]
|
||||
this.userSelectionRect = nextUserSelectRect
|
||||
this.selectedElements = nextSelectedElements
|
||||
},
|
||||
unsetUserSelection() {
|
||||
this.selectionActive = false
|
||||
this.userSelectionRect.draw = false
|
||||
|
||||
if (!this.getSelectedNodes || this.getSelectedNodes.length === 0) {
|
||||
this.selectedElements = undefined
|
||||
this.hooks.selectionChange.trigger(undefined)
|
||||
this.nodesSelectionActive = false
|
||||
} else {
|
||||
this.selectedNodesBbox = getRectOfNodes(this.getSelectedNodes)
|
||||
this.nodesSelectionActive = true
|
||||
}
|
||||
},
|
||||
addSelectedElements(elements) {
|
||||
const selectedElementsArr = Array.isArray(elements) ? elements : [elements]
|
||||
const selectedElementsUpdated = microDiff(selectedElementsArr, this.selectedElements ?? []).length
|
||||
this.selectedElements = selectedElementsUpdated ? selectedElementsArr : this.selectedElements
|
||||
if (selectedElementsUpdated) this.hooks.selectionChange.trigger(selectedElementsArr)
|
||||
},
|
||||
initD3Zoom({ d3ZoomHandler, d3Zoom, d3Selection }) {
|
||||
this.d3Zoom = d3Zoom
|
||||
this.d3Selection = d3Selection
|
||||
this.d3ZoomHandler = d3ZoomHandler
|
||||
},
|
||||
setMinZoom(minZoom) {
|
||||
this.d3Zoom?.scaleExtent([minZoom, this.maxZoom])
|
||||
this.minZoom = minZoom
|
||||
},
|
||||
setMaxZoom(maxZoom) {
|
||||
this.d3Zoom?.scaleExtent([this.minZoom, maxZoom])
|
||||
this.maxZoom = maxZoom
|
||||
},
|
||||
setTranslateExtent(translateExtent) {
|
||||
this.d3Zoom?.translateExtent(translateExtent)
|
||||
this.translateExtent = translateExtent
|
||||
},
|
||||
setNodeExtent(nodeExtent) {
|
||||
this.nodeExtent = nodeExtent
|
||||
},
|
||||
resetSelectedElements() {
|
||||
this.selectedElements = undefined
|
||||
},
|
||||
unsetNodesSelection() {
|
||||
this.nodesSelectionActive = false
|
||||
},
|
||||
updateSize(size) {
|
||||
this.dimensions = size
|
||||
},
|
||||
setConnectionNodeId({ connectionHandleId, connectionHandleType, connectionNodeId }) {
|
||||
this.connectionNodeId = connectionNodeId
|
||||
this.connectionHandleId = connectionHandleId
|
||||
this.connectionHandleType = connectionHandleType
|
||||
},
|
||||
setInteractive(isInteractive) {
|
||||
this.nodesDraggable = isInteractive
|
||||
this.nodesConnectable = isInteractive
|
||||
this.elementsSelectable = isInteractive
|
||||
},
|
||||
addElements(elements: Elements) {
|
||||
this.setElements(elements, false)
|
||||
},
|
||||
setState(state) {
|
||||
if (typeof state.loading !== 'undefined') this.loading = state.loading
|
||||
if (typeof state.panOnScroll !== 'undefined') this.panOnScroll = state.panOnScroll
|
||||
if (typeof state.panOnScrollMode !== 'undefined') this.panOnScrollMode = state.panOnScrollMode
|
||||
if (typeof state.panOnScrollSpeed !== 'undefined') this.panOnScrollSpeed = state.panOnScrollSpeed
|
||||
if (typeof state.paneMoveable !== 'undefined') this.paneMoveable = state.paneMoveable
|
||||
if (typeof state.zoomOnScroll !== 'undefined') this.zoomOnScroll = state.zoomOnScroll
|
||||
if (typeof state.preventScrolling !== 'undefined') this.preventScrolling = state.preventScrolling
|
||||
if (typeof state.zoomOnDoubleClick !== 'undefined') this.zoomOnDoubleClick = state.zoomOnDoubleClick
|
||||
if (typeof state.zoomOnPinch !== 'undefined') this.zoomOnPinch = state.zoomOnPinch
|
||||
if (typeof state.defaultZoom !== 'undefined') this.defaultZoom = state.defaultZoom
|
||||
if (typeof state.defaultPosition !== 'undefined') this.defaultPosition = state.defaultPosition
|
||||
if (typeof state.edgeTypes !== 'undefined') this.edgeTypes = state.edgeTypes
|
||||
if (typeof state.nodeTypes !== 'undefined') this.nodeTypes = state.nodeTypes
|
||||
if (typeof state.storageKey !== 'undefined') this.storageKey = state.storageKey
|
||||
if (typeof state.edgeUpdaterRadius !== 'undefined') this.edgeUpdaterRadius = state.edgeUpdaterRadius
|
||||
if (typeof state.elementsSelectable !== 'undefined') this.elementsSelectable = state.elementsSelectable
|
||||
if (typeof state.onlyRenderVisibleElements !== 'undefined')
|
||||
this.onlyRenderVisibleElements = state.onlyRenderVisibleElements
|
||||
if (typeof state.edgesUpdatable !== 'undefined') this.edgesUpdatable = state.edgesUpdatable
|
||||
if (typeof state.nodesConnectable !== 'undefined') this.nodesConnectable = state.nodesConnectable
|
||||
if (typeof state.nodesDraggable !== 'undefined') this.nodesDraggable = state.nodesDraggable
|
||||
if (typeof state.arrowHeadColor !== 'undefined') this.arrowHeadColor = state.arrowHeadColor
|
||||
if (typeof state.markerEndId !== 'undefined') this.markerEndId = state.markerEndId
|
||||
if (typeof state.deleteKeyCode !== 'undefined') this.deleteKeyCode = state.deleteKeyCode
|
||||
if (typeof state.selectionKeyCode !== 'undefined') this.selectionKeyCode = state.selectionKeyCode
|
||||
if (typeof state.zoomActivationKeyCode !== 'undefined') this.zoomActivationKeyCode = state.zoomActivationKeyCode
|
||||
if (typeof state.multiSelectionKeyCode !== 'undefined') this.multiSelectionKeyCode = state.multiSelectionKeyCode
|
||||
if (typeof state.snapToGrid !== 'undefined') this.snapToGrid = state.snapToGrid
|
||||
if (typeof state.snapGrid !== 'undefined') this.snapGrid = state.snapGrid
|
||||
if (!this.isReady)
|
||||
until(() => this.d3Zoom)
|
||||
.not.toBeUndefined()
|
||||
.then(() => {
|
||||
if (typeof state.maxZoom !== 'undefined') this.setMaxZoom(state.maxZoom)
|
||||
if (typeof state.minZoom !== 'undefined') this.setMinZoom(state.minZoom)
|
||||
if (typeof state.translateExtent !== 'undefined') this.setTranslateExtent(state.translateExtent)
|
||||
if (typeof state.nodeExtent !== 'undefined') this.setNodeExtent(state.nodeExtent)
|
||||
})
|
||||
else {
|
||||
if (typeof state.maxZoom !== 'undefined') this.setMaxZoom(state.maxZoom)
|
||||
if (typeof state.minZoom !== 'undefined') this.setMinZoom(state.minZoom)
|
||||
if (typeof state.translateExtent !== 'undefined') this.setTranslateExtent(state.translateExtent)
|
||||
if (typeof state.nodeExtent !== 'undefined') this.setNodeExtent(state.nodeExtent)
|
||||
}
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if (import.meta.hot) {
|
||||
import.meta.hot.accept(acceptHMRUpdate(store, import.meta.hot))
|
||||
}
|
||||
|
||||
return store
|
||||
}
|
||||
16
src/store/store.ts
Normal file
16
src/store/store.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import useState from './state'
|
||||
import useActions from './actions'
|
||||
import useGetters from './getters'
|
||||
import { FlowState, FlowStore } from '~/types'
|
||||
|
||||
export default (id: string, preloadedState: FlowState): FlowStore => {
|
||||
const state = reactive(useState(preloadedState))
|
||||
const getters = useGetters(state)
|
||||
const actions = useActions(state, getters)
|
||||
return {
|
||||
state,
|
||||
...toRefs(state),
|
||||
...getters,
|
||||
...actions,
|
||||
}
|
||||
}
|
||||
@@ -32,9 +32,6 @@ export type OnConnectStartParams = {
|
||||
handleId?: ElementId
|
||||
handleType?: HandleType
|
||||
}
|
||||
export type OnConnectStartFunc = (event: MouseEvent, params: OnConnectStartParams) => void
|
||||
export type OnConnectStopFunc = (event: MouseEvent) => void
|
||||
export type OnConnectEndFunc = (event: MouseEvent) => void
|
||||
|
||||
export enum ConnectionMode {
|
||||
Strict = 'strict',
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Store } from 'pinia'
|
||||
import { ComputedRef, ToRefs } from 'vue'
|
||||
import { UnwrapNestedRefs } from '@vue/reactivity'
|
||||
import {
|
||||
Dimensions,
|
||||
ElementId,
|
||||
@@ -13,14 +14,7 @@ import {
|
||||
XYPosition,
|
||||
} from './flow'
|
||||
import { HandleType, EdgeComponent, NodeComponent } from './components'
|
||||
import {
|
||||
ConnectionMode,
|
||||
OnConnectEndFunc,
|
||||
OnConnectFunc,
|
||||
OnConnectStartFunc,
|
||||
OnConnectStopFunc,
|
||||
SetConnectionId,
|
||||
} from './connection'
|
||||
import { ConnectionMode, SetConnectionId } from './connection'
|
||||
import { GraphEdge } from './edge'
|
||||
import { NodeExtent, GraphNode, TranslateExtent } from './node'
|
||||
import { D3Selection, D3Zoom, D3ZoomHandler, InitD3ZoomPayload } from './zoom'
|
||||
@@ -28,13 +22,13 @@ import { FlowHooks } from './hooks'
|
||||
|
||||
export interface FlowState extends Omit<FlowOptions, 'elements'> {
|
||||
hooks: FlowHooks
|
||||
instance?: FlowInstance
|
||||
instance: FlowInstance | undefined
|
||||
|
||||
elements: FlowElements
|
||||
|
||||
d3Zoom?: D3Zoom
|
||||
d3Selection?: D3Selection
|
||||
d3ZoomHandler?: D3ZoomHandler
|
||||
d3Zoom: D3Zoom | undefined
|
||||
d3Selection: D3Selection | undefined
|
||||
d3ZoomHandler: D3ZoomHandler | undefined
|
||||
minZoom: number
|
||||
maxZoom: number
|
||||
translateExtent: TranslateExtent
|
||||
@@ -42,16 +36,16 @@ export interface FlowState extends Omit<FlowOptions, 'elements'> {
|
||||
dimensions: Dimensions
|
||||
transform: Transform
|
||||
|
||||
selectedElements?: FlowElements
|
||||
selectedNodesBbox?: Rect
|
||||
selectedElements: FlowElements | undefined
|
||||
selectedNodesBbox: Rect | undefined
|
||||
nodesSelectionActive: boolean
|
||||
selectionActive: boolean
|
||||
userSelectionRect: SelectionRect
|
||||
multiSelectionActive: boolean
|
||||
|
||||
connectionNodeId?: ElementId
|
||||
connectionHandleId?: ElementId
|
||||
connectionHandleType?: HandleType
|
||||
connectionNodeId: ElementId | undefined
|
||||
connectionHandleId: ElementId | undefined
|
||||
connectionHandleType: HandleType | undefined
|
||||
connectionPosition: XYPosition
|
||||
connectionMode: ConnectionMode
|
||||
|
||||
@@ -63,11 +57,6 @@ export interface FlowState extends Omit<FlowOptions, 'elements'> {
|
||||
nodesConnectable: boolean
|
||||
elementsSelectable: boolean
|
||||
|
||||
onConnect?: OnConnectFunc
|
||||
onConnectStart?: OnConnectStartFunc
|
||||
onConnectStop?: OnConnectStopFunc
|
||||
onConnectEnd?: OnConnectEndFunc
|
||||
|
||||
isReady: boolean
|
||||
|
||||
vueFlowVersion: string
|
||||
@@ -83,22 +72,22 @@ export interface FlowActions {
|
||||
setMinZoom: (zoom: number) => void
|
||||
setMaxZoom: (zoom: number) => void
|
||||
setTranslateExtent: (translateExtent: TranslateExtent) => void
|
||||
setNodeExtent: (nodeExtent: NodeExtent) => void
|
||||
resetSelectedElements: () => void
|
||||
unsetNodesSelection: () => void
|
||||
updateSize: (size: Dimensions) => void
|
||||
setConnectionNodeId: (payload: SetConnectionId) => void
|
||||
setInteractive: (isInteractive: boolean) => void
|
||||
addElements: (elements: Elements) => void
|
||||
setState: (state: FlowOptions) => void
|
||||
setState: (state: Partial<FlowOptions>) => void
|
||||
}
|
||||
|
||||
export interface FlowGetters {
|
||||
getEdgeTypes: () => Record<string, EdgeComponent>
|
||||
getNodeTypes: () => Record<string, NodeComponent>
|
||||
getNodes: () => GraphNode[]
|
||||
getEdges: () => GraphEdge[]
|
||||
getSelectedNodes: () => GraphNode[]
|
||||
getEdgeTypes: ComputedRef<Record<string, EdgeComponent>>
|
||||
getNodeTypes: ComputedRef<Record<string, NodeComponent>>
|
||||
getNodes: ComputedRef<GraphNode[]>
|
||||
getEdges: ComputedRef<GraphEdge[]>
|
||||
getSelectedNodes: ComputedRef<GraphNode[]>
|
||||
}
|
||||
|
||||
export type FlowStore = Store<string, FlowState, FlowGetters, FlowActions>
|
||||
export type FlowStore = { state: FlowState } & ToRefs<FlowState> & FlowActions & FlowGetters
|
||||
export type ReactiveFlowStore = UnwrapNestedRefs<FlowStore>
|
||||
|
||||
@@ -15,8 +15,8 @@ import {
|
||||
export function getHandlePosition(position: Position, node: GraphNode, handle?: HandleElement): XYPosition {
|
||||
const x = (handle?.x ?? 0) + node.position.x
|
||||
const y = (handle?.y ?? 0) + node.position.y
|
||||
const width = handle?.width || node.__vf.width
|
||||
const height = handle?.height || node.__vf.height
|
||||
const width = handle?.width ?? node.__vf.width
|
||||
const height = handle?.height ?? node.__vf.height
|
||||
|
||||
switch (position) {
|
||||
case Position.Top:
|
||||
@@ -48,7 +48,7 @@ export function getHandle(bounds: HandleElement[], handleId?: ElementId): Handle
|
||||
// there is no handleId when there are no multiple handles/ handles with ids
|
||||
// so we just pick the first one
|
||||
let handle
|
||||
if (bounds.length === 1 || !handleId) {
|
||||
if (bounds.length === 1 ?? !handleId) {
|
||||
handle = bounds[0]
|
||||
} else if (handleId) {
|
||||
handle = bounds.find((d) => d.id === handleId)
|
||||
|
||||
@@ -16,6 +16,8 @@ import {
|
||||
FlowElements,
|
||||
GraphEdge,
|
||||
NextElements,
|
||||
ReactiveFlowStore,
|
||||
FlowState,
|
||||
} from '~/types'
|
||||
import { useWindow } from '~/composables'
|
||||
import { getSourceTargetNodes } from '~/utils/edge'
|
||||
@@ -161,7 +163,7 @@ export const pointToRendererPoint = (
|
||||
return position
|
||||
}
|
||||
|
||||
export const onLoadProject = (currentStore: FlowStore) => (position: XYPosition) =>
|
||||
export const onLoadProject = (currentStore: ReactiveFlowStore) => (position: XYPosition) =>
|
||||
pointToRendererPoint(position, currentStore.transform, currentStore.snapToGrid, currentStore.snapGrid)
|
||||
|
||||
export const parseNode = (node: Node, nodeExtent: NodeExtent): GraphNode => ({
|
||||
@@ -276,9 +278,9 @@ export const getConnectedEdges = (nodes: GraphNode[], edges: GraphEdge[]) => {
|
||||
return edges.filter((edge) => nodeIds.includes(edge.source) || nodeIds.includes(edge.target))
|
||||
}
|
||||
|
||||
export const onLoadGetElements = (currentStore: FlowStore) => (): FlowElements => currentStore.elements
|
||||
export const onLoadGetElements = (currentStore: ReactiveFlowStore) => (): FlowElements => currentStore.elements
|
||||
|
||||
export const onLoadToObject = (currentStore: FlowStore) => (): FlowExportObject => {
|
||||
export const onLoadToObject = (currentStore: FlowState) => (): FlowExportObject => {
|
||||
// we have to stringify/parse so objects containing refs (like nodes and edges) can potentially be saved in a storage
|
||||
return JSON.parse(
|
||||
JSON.stringify({
|
||||
|
||||
@@ -16,6 +16,8 @@ export const defaultEdgeTypes: DefaultEdgeTypes = {
|
||||
}
|
||||
|
||||
export const initialState = (): FlowState => ({
|
||||
isReady: false,
|
||||
instance: undefined,
|
||||
dimensions: {
|
||||
width: 0,
|
||||
height: 0,
|
||||
@@ -50,6 +52,9 @@ export const initialState = (): FlowState => ({
|
||||
panOnScrollMode: PanOnScrollMode.Free,
|
||||
paneMoveable: true,
|
||||
edgeUpdaterRadius: 10,
|
||||
onlyRenderVisibleElements: undefined,
|
||||
defaultZoom: undefined,
|
||||
defaultPosition: undefined,
|
||||
|
||||
nodesSelectionActive: false,
|
||||
selectionActive: false,
|
||||
@@ -65,6 +70,8 @@ export const initialState = (): FlowState => ({
|
||||
},
|
||||
|
||||
arrowHeadColor: '#b1b1b7',
|
||||
connectionLineStyle: {},
|
||||
connectionLineType: undefined,
|
||||
connectionNodeId: undefined,
|
||||
connectionHandleId: undefined,
|
||||
connectionHandleType: 'source',
|
||||
@@ -78,12 +85,18 @@ export const initialState = (): FlowState => ({
|
||||
nodesDraggable: true,
|
||||
nodesConnectable: true,
|
||||
elementsSelectable: true,
|
||||
|
||||
selectNodesOnDrag: undefined,
|
||||
multiSelectionActive: false,
|
||||
deleteKeyCode: undefined,
|
||||
selectionKeyCode: undefined,
|
||||
multiSelectionKeyCode: undefined,
|
||||
zoomActivationKeyCode: undefined,
|
||||
|
||||
isReady: false,
|
||||
hooks: createHooks(),
|
||||
loading: false,
|
||||
|
||||
markerEndId: undefined,
|
||||
storageKey: undefined,
|
||||
|
||||
vueFlowVersion: typeof __VUE_FLOW_VERSION__ !== 'undefined' ? __VUE_FLOW_VERSION__ : '-',
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user