refactor(core): add explicit imports and remove auto-imports
Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
export * from './useDrag'
|
||||
export * from './useEdge'
|
||||
export * from './useEdgeHooks'
|
||||
export * from './useGetPointerPosition'
|
||||
export * from './useHandle'
|
||||
export * from './useKeyPress'
|
||||
export * from './useNode'
|
||||
export * from './useNodeHooks'
|
||||
export * from './useUpdateNodePositions'
|
||||
export * from './useViewport'
|
||||
export * from './useVueFlow'
|
||||
export * from './useWatchProps'
|
||||
export * from './useWindow'
|
||||
export * from './useZoomPanHelper'
|
||||
@@ -2,7 +2,19 @@ import type { D3DragEvent, DragBehavior, SubjectPosition } from 'd3-drag'
|
||||
import { drag } from 'd3-drag'
|
||||
import { select } from 'd3-selection'
|
||||
import type { Ref } from 'vue'
|
||||
import { ref, watch } from 'vue'
|
||||
import type { MaybeRefOrGetter } from '@vueuse/core'
|
||||
import { toValue } from '@vueuse/core'
|
||||
import { useGetPointerPosition, useVueFlow } from '.'
|
||||
import {
|
||||
calcAutoPan,
|
||||
calcNextPosition,
|
||||
getDragItems,
|
||||
getEventHandlerParams,
|
||||
getEventPosition,
|
||||
handleNodeClick,
|
||||
hasSelector,
|
||||
} from '~/utils'
|
||||
import type { NodeDragEvent, NodeDragItem, XYPosition } from '~/types'
|
||||
|
||||
export type UseDragEvent = D3DragEvent<HTMLDivElement, null, SubjectPosition>
|
||||
@@ -14,11 +26,11 @@ interface UseDragParams {
|
||||
el: Ref<Element | undefined>
|
||||
disabled?: MaybeRefOrGetter<boolean>
|
||||
selectable?: MaybeRefOrGetter<boolean>
|
||||
dragHandle?: MaybeRefOrGetter<string | undefined>
|
||||
id?: string
|
||||
dragHandle?: MaybeComputedRef<string | undefined>
|
||||
}
|
||||
|
||||
function useDrag(params: UseDragParams) {
|
||||
export function useDrag(params: UseDragParams) {
|
||||
const {
|
||||
vueFlowRef,
|
||||
snapToGrid,
|
||||
@@ -128,7 +140,7 @@ function useDrag(params: UseDragParams) {
|
||||
autoPanId = requestAnimationFrame(autoPan)
|
||||
}
|
||||
|
||||
watch([() => resolveUnref(disabled), el], ([isDisabled, nodeEl]) => {
|
||||
watch([() => toValue(disabled), el], ([isDisabled, nodeEl]) => {
|
||||
if (nodeEl) {
|
||||
const selection = select(nodeEl)
|
||||
|
||||
@@ -145,7 +157,7 @@ function useDrag(params: UseDragParams) {
|
||||
}
|
||||
}
|
||||
|
||||
if (node && resolveUnref(selectable) && selectNodesOnDrag.value) {
|
||||
if (node && toValue(selectable) && selectNodesOnDrag.value) {
|
||||
handleNodeClick(
|
||||
node,
|
||||
multiSelectionActive.value,
|
||||
@@ -209,7 +221,7 @@ function useDrag(params: UseDragParams) {
|
||||
})
|
||||
.filter((event: D3DragEvent<HTMLDivElement, null, SubjectPosition>['sourceEvent']) => {
|
||||
const target = event.target as HTMLDivElement
|
||||
const unrefDragHandle = resolveUnref(dragHandle)
|
||||
const unrefDragHandle = toValue(dragHandle)
|
||||
|
||||
return (
|
||||
!event.button &&
|
||||
@@ -226,5 +238,3 @@ function useDrag(params: UseDragParams) {
|
||||
|
||||
return dragging
|
||||
}
|
||||
|
||||
export default useDrag
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { inject } from 'vue'
|
||||
import { useVueFlow } from './useVueFlow'
|
||||
import type { CustomEvent, ElementData } from '~/types'
|
||||
import { VueFlowError } from '~/utils/errors'
|
||||
import { ErrorCode, VueFlowError } from '~/utils'
|
||||
import { EdgeId, EdgeRef } from '~/context'
|
||||
|
||||
/**
|
||||
* Access an edge
|
||||
@@ -8,7 +11,7 @@ import { VueFlowError } from '~/utils/errors'
|
||||
*
|
||||
* Meaning if you do not provide an id, this composable has to be called in a child of your custom edge component, or it will throw
|
||||
*/
|
||||
export default function useEdge<Data = ElementData, CustomEvents extends Record<string, CustomEvent> = any>(id?: string) {
|
||||
export function useEdge<Data = ElementData, CustomEvents extends Record<string, CustomEvent> = any>(id?: string) {
|
||||
const edgeId = id ?? inject(EdgeId, '')
|
||||
const edgeEl = inject(EdgeRef, null)
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { EdgeEventsEmit, EdgeEventsOn, GraphEdge, VueFlowStore } from '~/types'
|
||||
import { createExtendedEventHook } from '~/utils'
|
||||
|
||||
function createEdgeHooks() {
|
||||
return {
|
||||
@@ -14,7 +15,7 @@ function createEdgeHooks() {
|
||||
}
|
||||
}
|
||||
|
||||
export default function useEdgeHooks(edge: GraphEdge, emits: VueFlowStore['emits']): { emit: EdgeEventsEmit; on: EdgeEventsOn } {
|
||||
export function useEdgeHooks(edge: GraphEdge, emits: VueFlowStore['emits']): { emit: EdgeEventsEmit; on: EdgeEventsOn } {
|
||||
const edgeHooks = createEdgeHooks()
|
||||
|
||||
edgeHooks.doubleClick.on((event) => {
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import type { UseDragEvent } from './useDrag'
|
||||
import { useVueFlow } from './useVueFlow'
|
||||
|
||||
/**
|
||||
* This composable is for *internal* use only.
|
||||
*/
|
||||
export function useGetPointerPosition() {
|
||||
const { viewport, snapGrid, snapToGrid } = useVueFlow()
|
||||
|
||||
|
||||
@@ -1,5 +1,22 @@
|
||||
import type { MaybeRefOrGetter } from '@vueuse/core'
|
||||
import { computed } from 'vue'
|
||||
import { toValue } from '@vueuse/core'
|
||||
import { useVueFlow } from './useVueFlow'
|
||||
import type { Connection, ConnectionHandle, HandleType, MouseTouchEvent, ValidConnectionFunc } from '~/types'
|
||||
import {
|
||||
calcAutoPan,
|
||||
getClosestHandle,
|
||||
getConnectionStatus,
|
||||
getEventPosition,
|
||||
getHandleLookup,
|
||||
getHandleType,
|
||||
getHostForElement,
|
||||
isMouseEvent,
|
||||
isValidHandle,
|
||||
pointToRendererPoint,
|
||||
rendererPointToPoint,
|
||||
resetRecentHandle,
|
||||
} from '~/utils'
|
||||
|
||||
interface UseHandleProps {
|
||||
handleId: MaybeRefOrGetter<string | null>
|
||||
@@ -15,7 +32,7 @@ function alwaysValid() {
|
||||
return true
|
||||
}
|
||||
|
||||
export default function useHandle({
|
||||
export function useHandle({
|
||||
handleId,
|
||||
nodeId,
|
||||
type,
|
||||
@@ -49,7 +66,7 @@ export default function useHandle({
|
||||
let handleDomNode: Element | null = null
|
||||
|
||||
function handlePointerDown(event: MouseTouchEvent) {
|
||||
const isTarget = resolveUnref(type) === 'target'
|
||||
const isTarget = toValue(type) === 'target'
|
||||
|
||||
const isMouseTriggered = isMouseEvent(event)
|
||||
|
||||
@@ -57,9 +74,9 @@ export default function useHandle({
|
||||
const doc = getHostForElement(event.target as HTMLElement)
|
||||
|
||||
if ((isMouseTriggered && event.button === 0) || !isMouseTriggered) {
|
||||
const node = findNode(resolveUnref(nodeId))
|
||||
const node = findNode(toValue(nodeId))
|
||||
|
||||
let isValidConnectionHandler = resolveUnref(isValidConnection) || isValidConnectionProp.value || alwaysValid
|
||||
let isValidConnectionHandler = toValue(isValidConnection) || isValidConnectionProp.value || alwaysValid
|
||||
|
||||
if (!isValidConnectionHandler && node) {
|
||||
isValidConnectionHandler = (!isTarget ? node.isValidTargetPos : node.isValidSourcePos) || alwaysValid
|
||||
@@ -71,7 +88,7 @@ export default function useHandle({
|
||||
|
||||
const { x, y } = getEventPosition(event)
|
||||
const clickedHandle = doc?.elementFromPoint(x, y)
|
||||
const handleType = getHandleType(resolveUnref(edgeUpdaterType), clickedHandle)
|
||||
const handleType = getHandleType(toValue(edgeUpdaterType), clickedHandle)
|
||||
const containerBounds = vueFlowRef.value?.getBoundingClientRect()
|
||||
|
||||
if (!containerBounds || !handleType) {
|
||||
@@ -84,8 +101,8 @@ export default function useHandle({
|
||||
|
||||
const handleLookup = getHandleLookup({
|
||||
nodes: getNodes.value,
|
||||
nodeId: resolveUnref(nodeId),
|
||||
handleId: resolveUnref(handleId),
|
||||
nodeId: toValue(nodeId),
|
||||
handleId: toValue(handleId),
|
||||
handleType,
|
||||
})
|
||||
|
||||
@@ -103,8 +120,8 @@ export default function useHandle({
|
||||
|
||||
startConnection(
|
||||
{
|
||||
nodeId: resolveUnref(nodeId),
|
||||
handleId: resolveUnref(handleId),
|
||||
nodeId: toValue(nodeId),
|
||||
handleId: toValue(handleId),
|
||||
type: handleType,
|
||||
},
|
||||
{
|
||||
@@ -114,7 +131,7 @@ export default function useHandle({
|
||||
event,
|
||||
)
|
||||
|
||||
emits.connectStart({ event, nodeId: resolveUnref(nodeId), handleId: resolveUnref(handleId), handleType })
|
||||
emits.connectStart({ event, nodeId: toValue(nodeId), handleId: toValue(handleId), handleType })
|
||||
|
||||
function onPointerMove(event: MouseTouchEvent) {
|
||||
connectionPosition = getEventPosition(event, containerBounds)
|
||||
@@ -134,8 +151,8 @@ export default function useHandle({
|
||||
event,
|
||||
closestHandle,
|
||||
connectionMode.value,
|
||||
resolveUnref(nodeId),
|
||||
resolveUnref(handleId),
|
||||
toValue(nodeId),
|
||||
toValue(handleId),
|
||||
isTarget ? 'target' : 'source',
|
||||
isValidConnectionHandler,
|
||||
doc,
|
||||
@@ -223,21 +240,16 @@ export default function useHandle({
|
||||
return
|
||||
}
|
||||
|
||||
const isTarget = resolveUnref(type) === 'target'
|
||||
const isTarget = toValue(type) === 'target'
|
||||
|
||||
if (!connectionClickStartHandle.value) {
|
||||
emits.clickConnectStart({ event, nodeId: resolveUnref(nodeId), handleId: resolveUnref(handleId) })
|
||||
emits.clickConnectStart({ event, nodeId: toValue(nodeId), handleId: toValue(handleId) })
|
||||
|
||||
startConnection(
|
||||
{ nodeId: resolveUnref(nodeId), type: resolveUnref(type), handleId: resolveUnref(handleId) },
|
||||
undefined,
|
||||
event,
|
||||
true,
|
||||
)
|
||||
startConnection({ nodeId: toValue(nodeId), type: toValue(type), handleId: toValue(handleId) }, undefined, event, true)
|
||||
} else {
|
||||
let isValidConnectionHandler = resolveUnref(isValidConnection) || isValidConnectionProp.value || alwaysValid
|
||||
let isValidConnectionHandler = toValue(isValidConnection) || isValidConnectionProp.value || alwaysValid
|
||||
|
||||
const node = findNode(resolveUnref(nodeId))
|
||||
const node = findNode(toValue(nodeId))
|
||||
|
||||
if (!isValidConnectionHandler && node) {
|
||||
isValidConnectionHandler = (!isTarget ? node.isValidTargetPos : node.isValidSourcePos) || alwaysValid
|
||||
@@ -252,9 +264,9 @@ export default function useHandle({
|
||||
const { connection, isValid } = isValidHandle(
|
||||
event,
|
||||
{
|
||||
nodeId: resolveUnref(nodeId),
|
||||
id: resolveUnref(handleId),
|
||||
type: resolveUnref(type),
|
||||
nodeId: toValue(nodeId),
|
||||
id: toValue(handleId),
|
||||
type: toValue(type),
|
||||
},
|
||||
connectionMode.value,
|
||||
connectionClickStartHandle.value.nodeId,
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import type { Ref } from 'vue'
|
||||
import { ref, watch } from 'vue'
|
||||
import type { KeyFilter, KeyPredicate, MaybeRefOrGetter } from '@vueuse/core'
|
||||
import { onKeyStroke, toValue, useEventListener } from '@vueuse/core'
|
||||
import { useWindow } from './useWindow'
|
||||
import { isBoolean, isFunction } from '~/utils'
|
||||
|
||||
export function isInputDOMNode(event: KeyboardEvent): boolean {
|
||||
const target = (event.composedPath?.()?.[0] || event.target) as HTMLElement
|
||||
@@ -37,10 +40,10 @@ function createKeyPredicate(keyFilter: string[], pressedKeys: Set<string>): KeyP
|
||||
* @param keyFilter - Can be a boolean, a string or an array of strings. If it's a boolean, it will always return that value. If it's a string, it will return true if the key is pressed. If it's an array of strings, it will return true if any of the keys are pressed, or a combination is pressed (e.g. ['ctrl+a', 'ctrl+b'])
|
||||
* @param onChange - Callback function that will be called when the key state changes
|
||||
*/
|
||||
export default (keyFilter: MaybeRefOrGetter<KeyFilter | null>, onChange?: (keyPressed: boolean) => void): Ref<boolean> => {
|
||||
export function useKeyPress(keyFilter: MaybeRefOrGetter<KeyFilter | null>, onChange?: (keyPressed: boolean) => void) {
|
||||
const window = useWindow()
|
||||
|
||||
const isPressed = ref(resolveUnref(keyFilter) === true)
|
||||
const isPressed = ref(toValue(keyFilter) === true)
|
||||
|
||||
let modifierPressed = false
|
||||
|
||||
@@ -51,7 +54,7 @@ export default (keyFilter: MaybeRefOrGetter<KeyFilter | null>, onChange?: (keyPr
|
||||
})
|
||||
|
||||
watch(
|
||||
() => resolveUnref(keyFilter),
|
||||
() => toValue(keyFilter),
|
||||
(unrefKeyFilter) => {
|
||||
if (window && typeof window.addEventListener !== 'undefined') {
|
||||
useEventListener(window, 'blur', () => {
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import { computed, inject } from 'vue'
|
||||
import { useVueFlow } from './useVueFlow'
|
||||
import type { CustomEvent, ElementData } from '~/types'
|
||||
import { NodeId, NodeRef } from '~/context'
|
||||
import { ErrorCode, VueFlowError, getConnectedEdges } from '~/utils'
|
||||
|
||||
/**
|
||||
* Access a node, it's parent (if one exists) and connected edges
|
||||
@@ -7,7 +11,7 @@ import type { CustomEvent, ElementData } from '~/types'
|
||||
*
|
||||
* Meaning if you do not provide an id, this composable has to be called in a child of your custom node component, or it will throw
|
||||
*/
|
||||
export default function useNode<Data = ElementData, CustomEvents extends Record<string, CustomEvent> = any>(id?: string) {
|
||||
export function useNode<Data = ElementData, CustomEvents extends Record<string, CustomEvent> = any>(id?: string) {
|
||||
const nodeId = id ?? inject(NodeId, '')
|
||||
const nodeEl = inject(NodeRef, null)
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { GraphNode, NodeEventsEmit, NodeEventsOn, VueFlowStore } from '~/types'
|
||||
import { createExtendedEventHook } from '~/utils'
|
||||
|
||||
function createNodeHooks() {
|
||||
return {
|
||||
@@ -14,7 +15,7 @@ function createNodeHooks() {
|
||||
}
|
||||
}
|
||||
|
||||
export default function useNodeHooks(node: GraphNode, emits: VueFlowStore['emits']): { emit: NodeEventsEmit; on: NodeEventsOn } {
|
||||
export function useNodeHooks(node: GraphNode, emits: VueFlowStore['emits']): { emit: NodeEventsEmit; on: NodeEventsOn } {
|
||||
const nodeHooks = createNodeHooks()
|
||||
|
||||
nodeHooks.doubleClick.on((event) => {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { useVueFlow } from './useVueFlow'
|
||||
import type { NodeDragItem, XYPosition } from '~/types'
|
||||
import { calcNextPosition } from '~/utils'
|
||||
|
||||
function useUpdateNodePositions() {
|
||||
export function useUpdateNodePositions() {
|
||||
const { getSelectedNodes, nodeExtent, updateNodePositions, findNode, snapGrid, snapToGrid, nodesDraggable, emits } =
|
||||
useVueFlow()
|
||||
|
||||
@@ -39,5 +41,3 @@ function useUpdateNodePositions() {
|
||||
updateNodePositions(nodeUpdates, true, false)
|
||||
}
|
||||
}
|
||||
|
||||
export default useUpdateNodePositions
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { zoomIdentity } from 'd3-zoom'
|
||||
import { computed, ref } from 'vue'
|
||||
import type { ComputedGetters, D3Selection, GraphNode, State, ViewportFunctions } from '~/types'
|
||||
import { clampPosition, getRectOfNodes, getTransformForBounds, pointToRendererPoint } from '~/utils'
|
||||
|
||||
interface ExtendedViewport extends ViewportFunctions {
|
||||
initialized: boolean
|
||||
@@ -22,7 +24,7 @@ const initialViewportHelper: ExtendedViewport = {
|
||||
initialized: false,
|
||||
}
|
||||
|
||||
export default (state: State, getters: ComputedGetters) => {
|
||||
export function useViewport(state: State, getters: ComputedGetters) {
|
||||
const { nodes, d3Zoom, d3Selection, dimensions, translateExtent, minZoom, maxZoom, viewport, snapToGrid, snapGrid, hooks } =
|
||||
$(state)
|
||||
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import { toRefs, tryOnScopeDispose } from '@vueuse/core'
|
||||
import type { EffectScope } from 'vue'
|
||||
import { computed, getCurrentScope, inject, provide, reactive } from 'vue'
|
||||
import { useActions, useGetters, useState } from '~/store'
|
||||
import type { FlowOptions, FlowProps, State, VueFlowStore } from '~/types'
|
||||
import { VueFlow } from '~/context'
|
||||
import { warn } from '~/utils'
|
||||
|
||||
/**
|
||||
* Stores all currently created store instances
|
||||
@@ -81,7 +86,7 @@ type Injection = VueFlowStore | null | undefined
|
||||
type Scope = (EffectScope & { vueFlowId: string }) | undefined
|
||||
|
||||
// todo: maybe replace the storage with a context based solution; This would break calling useVueFlow outside a setup function though, which should be fine
|
||||
export default (options?: FlowProps): VueFlowStore => {
|
||||
export function useVueFlow(options?: FlowProps): VueFlowStore {
|
||||
const storage = Storage.getInstance()
|
||||
|
||||
const scope = getCurrentScope() as Scope
|
||||
|
||||
@@ -0,0 +1,328 @@
|
||||
import type { ToRefs } from 'vue'
|
||||
import { effectScope, nextTick, onScopeDispose, watch } from 'vue'
|
||||
import type { WatchPausableReturn } from '@vueuse/core'
|
||||
import { toRef, watchPausable } from '@vueuse/core'
|
||||
import type { Connection, FlowProps, VueFlowStore } from '~/types'
|
||||
import { isDef, isFunction } from '~/utils'
|
||||
|
||||
export function useWatchProps(
|
||||
models: ToRefs<Pick<FlowProps, 'nodes' | 'edges' | 'modelValue'>>,
|
||||
props: FlowProps,
|
||||
store: VueFlowStore,
|
||||
) {
|
||||
const scope = effectScope()
|
||||
|
||||
scope.run(() => {
|
||||
const watchModelValue = () => {
|
||||
scope.run(() => {
|
||||
let pauseModel: WatchPausableReturn
|
||||
let pauseStore: WatchPausableReturn
|
||||
|
||||
let immediateStore = !!(store.nodes.value.length || store.edges.value.length)
|
||||
|
||||
// eslint-disable-next-line prefer-const
|
||||
pauseModel = watchPausable([models.modelValue, () => models.modelValue?.value?.length], ([elements]) => {
|
||||
if (elements && Array.isArray(elements)) {
|
||||
pauseStore?.pause()
|
||||
|
||||
store.setElements(elements)
|
||||
|
||||
// only trigger store watcher immediately if we actually set any elements to the store
|
||||
if (!pauseStore && !immediateStore && elements.length) {
|
||||
immediateStore = true
|
||||
} else {
|
||||
pauseStore?.resume()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
pauseStore = watchPausable(
|
||||
[store.nodes, store.edges, () => store.edges.value.length, () => store.nodes.value.length],
|
||||
([nodes, edges]) => {
|
||||
if (models.modelValue?.value && Array.isArray(models.modelValue.value)) {
|
||||
pauseModel?.pause()
|
||||
|
||||
models.modelValue.value = [...nodes, ...edges]
|
||||
|
||||
nextTick(() => {
|
||||
pauseModel?.resume()
|
||||
})
|
||||
}
|
||||
},
|
||||
{ immediate: immediateStore },
|
||||
)
|
||||
|
||||
onScopeDispose(() => {
|
||||
pauseModel?.stop()
|
||||
pauseStore?.stop()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const watchNodesValue = () => {
|
||||
scope.run(() => {
|
||||
let pauseModel: WatchPausableReturn
|
||||
let pauseStore: WatchPausableReturn
|
||||
|
||||
let immediateStore = !!store.nodes.value.length
|
||||
|
||||
// eslint-disable-next-line prefer-const
|
||||
pauseModel = watchPausable([models.nodes, () => models.nodes?.value?.length], ([nodes]) => {
|
||||
if (nodes && Array.isArray(nodes)) {
|
||||
pauseStore?.pause()
|
||||
|
||||
store.setNodes(nodes)
|
||||
|
||||
// only trigger store watcher immediately if we actually set any elements to the store
|
||||
if (!pauseStore && !immediateStore && nodes.length) {
|
||||
immediateStore = true
|
||||
} else {
|
||||
pauseStore?.resume()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
pauseStore = watchPausable(
|
||||
[store.nodes, () => store.nodes.value.length],
|
||||
([nodes]) => {
|
||||
if (models.nodes?.value && Array.isArray(models.nodes.value)) {
|
||||
pauseModel?.pause()
|
||||
|
||||
models.nodes.value = [...nodes]
|
||||
|
||||
nextTick(() => {
|
||||
pauseModel?.resume()
|
||||
})
|
||||
}
|
||||
},
|
||||
{ immediate: immediateStore },
|
||||
)
|
||||
|
||||
onScopeDispose(() => {
|
||||
pauseModel?.stop()
|
||||
pauseStore?.stop()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const watchEdgesValue = () => {
|
||||
scope.run(() => {
|
||||
let pauseModel: WatchPausableReturn
|
||||
let pauseStore: WatchPausableReturn
|
||||
|
||||
let immediateStore = !!store.edges.value.length
|
||||
|
||||
// eslint-disable-next-line prefer-const
|
||||
pauseModel = watchPausable([models.edges, () => models.edges?.value?.length], ([edges]) => {
|
||||
if (edges && Array.isArray(edges)) {
|
||||
pauseStore?.pause()
|
||||
|
||||
store.setEdges(edges)
|
||||
|
||||
// only trigger store watcher immediately if we actually set any elements to the store
|
||||
if (!pauseStore && !immediateStore && edges.length) {
|
||||
immediateStore = true
|
||||
} else {
|
||||
pauseStore?.resume()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
pauseStore = watchPausable(
|
||||
[store.edges, () => store.edges.value.length],
|
||||
([edges]) => {
|
||||
if (models.edges?.value && Array.isArray(models.edges.value)) {
|
||||
pauseModel?.pause()
|
||||
|
||||
models.edges.value = [...edges]
|
||||
|
||||
nextTick(() => {
|
||||
pauseModel?.resume()
|
||||
})
|
||||
}
|
||||
},
|
||||
{ immediate: immediateStore },
|
||||
)
|
||||
|
||||
onScopeDispose(() => {
|
||||
pauseModel?.stop()
|
||||
pauseStore?.stop()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const watchMaxZoom = () => {
|
||||
scope.run(() => {
|
||||
watch(
|
||||
() => props.maxZoom,
|
||||
() => {
|
||||
if (props.maxZoom && isDef(props.maxZoom)) {
|
||||
store.setMaxZoom(props.maxZoom)
|
||||
}
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
const watchMinZoom = () => {
|
||||
scope.run(() => {
|
||||
watch(
|
||||
() => props.minZoom,
|
||||
() => {
|
||||
if (props.minZoom && isDef(props.minZoom)) {
|
||||
store.setMinZoom(props.minZoom)
|
||||
}
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
const watchTranslateExtent = () => {
|
||||
scope.run(() => {
|
||||
watch(
|
||||
() => props.translateExtent,
|
||||
() => {
|
||||
if (props.translateExtent && isDef(props.translateExtent)) {
|
||||
store.setTranslateExtent(props.translateExtent)
|
||||
}
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
const watchNodeExtent = () => {
|
||||
scope.run(() => {
|
||||
watch(
|
||||
() => props.nodeExtent,
|
||||
() => {
|
||||
if (props.nodeExtent && isDef(props.nodeExtent)) {
|
||||
store.setNodeExtent(props.nodeExtent)
|
||||
}
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
const watchApplyDefault = () => {
|
||||
scope.run(() => {
|
||||
watch(
|
||||
() => props.applyDefault,
|
||||
() => {
|
||||
if (isDef(props.applyDefault)) {
|
||||
store.applyDefault.value = props.applyDefault
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
watch(
|
||||
store.applyDefault,
|
||||
(_, __, onCleanup) => {
|
||||
if (store.applyDefault.value) {
|
||||
store.onNodesChange(store.applyNodeChanges)
|
||||
store.onEdgesChange(store.applyEdgeChanges)
|
||||
} else {
|
||||
store.hooks.value.nodesChange.off(store.applyNodeChanges)
|
||||
store.hooks.value.edgesChange.off(store.applyEdgeChanges)
|
||||
}
|
||||
|
||||
onCleanup(() => {
|
||||
store.hooks.value.nodesChange.off(store.applyNodeChanges)
|
||||
store.hooks.value.edgesChange.off(store.applyEdgeChanges)
|
||||
})
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
const watchAutoConnect = () => {
|
||||
scope.run(() => {
|
||||
const autoConnector = async (params: Connection) => {
|
||||
let connection: boolean | Connection = params
|
||||
|
||||
if (isFunction(props.autoConnect)) {
|
||||
connection = await props.autoConnect(params)
|
||||
}
|
||||
|
||||
if (connection !== false) {
|
||||
store.addEdges([connection])
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.autoConnect,
|
||||
() => {
|
||||
if (isDef(props.autoConnect)) {
|
||||
store.autoConnect.value = props.autoConnect
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
watch(
|
||||
store.autoConnect,
|
||||
(autoConnectEnabled, _, onCleanup) => {
|
||||
if (autoConnectEnabled) {
|
||||
store.onConnect(autoConnector)
|
||||
} else {
|
||||
store.hooks.value.connect.off(autoConnector)
|
||||
}
|
||||
|
||||
onCleanup(() => {
|
||||
store.hooks.value.connect.off(autoConnector)
|
||||
})
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
const watchRest = () => {
|
||||
const skip: (keyof typeof props)[] = [
|
||||
'id',
|
||||
'modelValue',
|
||||
'translateExtent',
|
||||
'nodeExtent',
|
||||
'edges',
|
||||
'nodes',
|
||||
'maxZoom',
|
||||
'minZoom',
|
||||
'applyDefault',
|
||||
'autoConnect',
|
||||
]
|
||||
|
||||
Object.keys(props).forEach((prop) => {
|
||||
if (!skip.includes(prop as keyof typeof props)) {
|
||||
const model = toRef(props, prop as keyof typeof props)
|
||||
const storedValue = store[prop as keyof typeof store] as typeof model
|
||||
|
||||
scope.run(() => {
|
||||
watch(
|
||||
model,
|
||||
(nextValue) => {
|
||||
if (isDef(nextValue)) {
|
||||
storedValue.value = nextValue
|
||||
}
|
||||
},
|
||||
{ flush: 'pre' },
|
||||
)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
;[
|
||||
watchModelValue,
|
||||
watchNodesValue,
|
||||
watchEdgesValue,
|
||||
watchMinZoom,
|
||||
watchMaxZoom,
|
||||
watchTranslateExtent,
|
||||
watchNodeExtent,
|
||||
watchApplyDefault,
|
||||
watchAutoConnect,
|
||||
watchRest,
|
||||
].forEach((watch) => watch())
|
||||
})
|
||||
|
||||
return () => scope.stop()
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
type UseWindow = Window & typeof globalThis & { chrome?: any }
|
||||
|
||||
export default (): UseWindow => {
|
||||
export function useWindow(): UseWindow {
|
||||
if (typeof window !== 'undefined') {
|
||||
return window as UseWindow
|
||||
} else {
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import { zoomIdentity } from 'd3-zoom'
|
||||
import { useVueFlow } from './useVueFlow'
|
||||
import type { D3Selection, GraphNode, ViewportFunctions } from '~/types'
|
||||
import { clampPosition, getRectOfNodes, getTransformForBounds, pointToRendererPoint } from '~/utils'
|
||||
|
||||
const DEFAULT_PADDING = 0.1
|
||||
|
||||
/**
|
||||
* @deprecated use {@link useVueFlow} instead (all viewport functions are also available in {@link useVueFlow})
|
||||
*/
|
||||
export default (vueFlowId?: string): ViewportFunctions => {
|
||||
export function useZoomPanHelper(vueFlowId?: string): ViewportFunctions {
|
||||
const { nodes, d3Zoom, d3Selection, dimensions, translateExtent, minZoom, maxZoom, viewport, snapToGrid, snapGrid, getNodes } =
|
||||
$(useVueFlow({ id: vueFlowId }))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user