feat: implement workspaces
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
<script lang="ts" setup>
|
||||
import ZoomPane from '../ZoomPane/ZoomPane.vue'
|
||||
import { createHooks, useHooks } from '../../store'
|
||||
import { useVueFlow } from '../../composables'
|
||||
import type { FlowProps } from '../../types/flow'
|
||||
import useWatch from './watch'
|
||||
|
||||
const props = withDefaults(defineProps<FlowProps>(), {
|
||||
snapToGrid: undefined,
|
||||
onlyRenderVisibleElements: undefined,
|
||||
edgesUpdatable: undefined,
|
||||
nodesConnectable: undefined,
|
||||
nodesDraggable: undefined,
|
||||
elementsSelectable: undefined,
|
||||
selectNodesOnDrag: undefined,
|
||||
preventScrolling: undefined,
|
||||
zoomOnScroll: undefined,
|
||||
zoomOnPinch: undefined,
|
||||
zoomOnDoubleClick: undefined,
|
||||
panOnScroll: undefined,
|
||||
panOnDrag: undefined,
|
||||
applyDefault: undefined,
|
||||
fitViewOnInit: undefined,
|
||||
connectOnClick: undefined,
|
||||
connectionLineStyle: () => null,
|
||||
})
|
||||
const emit = defineEmits([...Object.keys(createHooks()), 'update:modelValue', 'update:edges', 'update:nodes'])
|
||||
|
||||
const {
|
||||
id,
|
||||
store,
|
||||
hooks,
|
||||
applyDefault,
|
||||
setElements,
|
||||
setEdges,
|
||||
setNodes,
|
||||
getNodeTypes,
|
||||
getEdgeTypes,
|
||||
onNodesChange,
|
||||
onEdgesChange,
|
||||
applyNodeChanges,
|
||||
applyEdgeChanges,
|
||||
nodes: storedNodes,
|
||||
edges: storedEdges,
|
||||
} = useVueFlow(props)
|
||||
useHooks(hooks.value, emit)
|
||||
const { modelValue, nodes, edges } = useVModels(props, emit)
|
||||
onMounted(() => useWatch({ modelValue, nodes, edges }, props, store))
|
||||
|
||||
if (applyDefault.value) {
|
||||
onNodesChange(applyNodeChanges)
|
||||
onEdgesChange(applyEdgeChanges)
|
||||
}
|
||||
if (props.modelValue && !storedNodes.value.length) setElements(props.modelValue)
|
||||
if (props.nodes && !storedNodes.value.length) setNodes(props.nodes)
|
||||
if (props.edges && !storedEdges.value.length) setEdges(props.edges)
|
||||
|
||||
if (modelValue && modelValue.value) {
|
||||
watch([() => storedEdges.value.length, () => storedNodes.value.length], () => {
|
||||
modelValue.value = [...storedNodes.value, ...storedEdges.value]
|
||||
})
|
||||
}
|
||||
|
||||
if (nodes && nodes.value) {
|
||||
watch(
|
||||
() => storedNodes.value.length,
|
||||
() => (nodes.value = [...storedNodes.value]),
|
||||
)
|
||||
}
|
||||
|
||||
if (edges && edges.value) {
|
||||
watch(
|
||||
() => storedEdges.value.length,
|
||||
() => (edges.value = [...storedEdges.value]),
|
||||
)
|
||||
}
|
||||
</script>
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'VueFlow',
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<div class="vue-flow">
|
||||
<ZoomPane :key="`renderer-${id}`">
|
||||
<template v-for="nodeName of Object.keys(getNodeTypes)" #[`node-${nodeName}`]="nodeProps" :key="`node-${nodeName}-${id}`">
|
||||
<slot :name="`node-${nodeName}`" v-bind="nodeProps" />
|
||||
</template>
|
||||
<template v-for="edgeName of Object.keys(getEdgeTypes)" #[`edge-${edgeName}`]="edgeProps" :key="`edge-${edgeName}-${id}`">
|
||||
<slot :name="`edge-${edgeName}`" v-bind="edgeProps" />
|
||||
</template>
|
||||
<template #connection-line="customConnectionLineProps">
|
||||
<slot name="connection-line" v-bind="customConnectionLineProps" />
|
||||
</template>
|
||||
<slot name="zoom-pane" />
|
||||
</ZoomPane>
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
<style>
|
||||
@import '../../style.css';
|
||||
</style>
|
||||
@@ -0,0 +1,77 @@
|
||||
import { Ref } from 'vue'
|
||||
import { FlowProps, FlowStore, Node, Edge, Elements } from '~/types'
|
||||
|
||||
const isDef = <T>(val: T): val is NonNullable<T> => typeof val !== 'undefined'
|
||||
export default (
|
||||
{
|
||||
modelValue,
|
||||
nodes,
|
||||
edges,
|
||||
}: {
|
||||
modelValue?: Ref<Elements | undefined>
|
||||
nodes?: Ref<Node[] | undefined>
|
||||
edges?: Ref<Edge[] | undefined>
|
||||
},
|
||||
props: FlowProps,
|
||||
store: FlowStore,
|
||||
) => {
|
||||
const scope = getCurrentScope()
|
||||
scope?.run(() => {
|
||||
if (isDefined(props.modelValue)) {
|
||||
const { pause, resume } = pausableWatch([() => props.modelValue, () => props.modelValue?.length], async ([v]) => {
|
||||
if (v && Array.isArray(v)) {
|
||||
pause()
|
||||
store.setElements(v)
|
||||
if (modelValue) modelValue.value = [...store.nodes, ...store.edges]
|
||||
await nextTick()
|
||||
resume()
|
||||
}
|
||||
})
|
||||
}
|
||||
if (isDefined(props.nodes)) {
|
||||
const { pause, resume } = pausableWatch([() => props.nodes, () => props.nodes?.length], async ([v]) => {
|
||||
if (v && Array.isArray(v)) {
|
||||
pause()
|
||||
store.setNodes(v)
|
||||
if (nodes) nodes.value = store.nodes
|
||||
await nextTick()
|
||||
resume()
|
||||
}
|
||||
})
|
||||
}
|
||||
if (isDefined(props.edges)) {
|
||||
const { pause, resume } = pausableWatch([() => props.edges, () => props.edges?.length], async ([v]) => {
|
||||
if (v && Array.isArray(v)) {
|
||||
pause()
|
||||
store.setEdges(v)
|
||||
if (edges) edges.value = store.edges
|
||||
await nextTick()
|
||||
resume()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.maxZoom,
|
||||
(v) => isDef(v) && store.setMaxZoom(v),
|
||||
{ immediate: isDef(props.maxZoom) },
|
||||
)
|
||||
watch(
|
||||
() => props.minZoom,
|
||||
(v) => isDef(v) && store.setMinZoom(v),
|
||||
{ immediate: isDef(props.minZoom) },
|
||||
)
|
||||
|
||||
const skip = ['modelValue', 'edges', 'nodes', 'maxZoom', 'minZoom']
|
||||
Object.keys(props).forEach((p) => {
|
||||
if (!skip.includes(p)) {
|
||||
const prop = props[p as keyof typeof props]
|
||||
watch(
|
||||
() => prop,
|
||||
(v) => isDef(v) && ((store as any)[p] = v),
|
||||
{ immediate: isDef(prop) },
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user