diff --git a/src/hooks/useGlobalKeyHandler.ts b/src/hooks/useGlobalKeyHandler.ts
index 09e1b70c..dbcce67a 100644
--- a/src/hooks/useGlobalKeyHandler.ts
+++ b/src/hooks/useGlobalKeyHandler.ts
@@ -1,7 +1,6 @@
-import { computed, watch } from 'vue'
import useKeyPress from './useKeyPress'
import { isNode, getConnectedEdges } from '~/utils/graph'
-import { Elements, KeyCode, ElementId, FlowElement, RevueFlowStore, Edge } from '~/types'
+import { Elements, KeyCode, ElementId, FlowElement, RevueFlowStore } from '~/types'
interface HookParams {
deleteKeyCode: KeyCode
@@ -9,19 +8,14 @@ interface HookParams {
onElementsRemove?: (elements: Elements) => void
}
-export default ({ deleteKeyCode, multiSelectionKeyCode, onElementsRemove }: HookParams): void => {
- const selectedElements = ref([])
- const deleteKeyPressed = useKeyPress(deleteKeyCode)
- const multiSelectionKeyPressed = useKeyPress(multiSelectionKeyCode)
- /**
- * const selectedElements = computed(() => store.selectedElements || [])
- const edges = computed(() => store.edges)
+export default ({ deleteKeyCode, multiSelectionKeyCode, onElementsRemove = () => {} }: HookParams): void => {
+ const store = inject('store')!
- watch(selectedElements, () => {
- if (onElementsRemove && deleteKeyPressed.value && selectedElements.value.length > 0) {
- const selectedNodes = selectedElements.value.filter(isNode)
- const connectedEdges = getConnectedEdges(selectedNodes, edges.value as Edge[])
- const elementsToRemove = [...selectedElements.value, ...connectedEdges].reduce(
+ useKeyPress(deleteKeyCode, (keyPressed) => {
+ if (keyPressed && store.selectedElements) {
+ const selectedNodes = store.selectedElements.filter(isNode)
+ const connectedEdges = getConnectedEdges(selectedNodes, store.edges)
+ const elementsToRemove = [...store.selectedElements, ...connectedEdges].reduce(
(res, item) => res.set(item.id, item),
new Map(),
)
@@ -32,8 +26,5 @@ export default ({ deleteKeyCode, multiSelectionKeyCode, onElementsRemove }: Hook
}
})
- watch(multiSelectionKeyPressed, () => {
- store.multiSelectionActive = multiSelectionKeyPressed.value
- })
- */
+ useKeyPress(multiSelectionKeyCode, (keyPressed) => (store.multiSelectionActive = keyPressed))
}
diff --git a/src/hooks/useResizeHandler.ts b/src/hooks/useResizeHandler.ts
index 7b4f20f0..cd35613f 100644
--- a/src/hooks/useResizeHandler.ts
+++ b/src/hooks/useResizeHandler.ts
@@ -2,7 +2,7 @@ import { Ref } from 'vue'
import { getDimensions } from '~/utils'
import { Dimensions } from '~/types'
-export default (el: Ref) => {
+export default function (el: Ref) {
const dimensions = ref({ width: 0, height: 0 })
const updateDimensions = () => {
const unrefEl = unrefElement(el)
@@ -11,10 +11,13 @@ export default (el: Ref) => {
const size = getDimensions(unrefEl as HTMLDivElement)
if (size.height === 0 || size.width === 0)
console.log('The revue Flow parent container needs a width and a height to render the graph.')
+ else dimensions.value = size
}
useEventListener(window, 'resize', updateDimensions)
useResizeObserver(el, () => updateDimensions())
+ until(el).toBeTruthy().then(updateDimensions)
+
return dimensions
}
diff --git a/src/hooks/useUpdateNodeInternals.ts b/src/hooks/useUpdateNodeInternals.ts
index 7714bc98..2418bb3f 100644
--- a/src/hooks/useUpdateNodeInternals.ts
+++ b/src/hooks/useUpdateNodeInternals.ts
@@ -1,5 +1,4 @@
import { ElementId, RevueFlowStore, UpdateNodeInternals } from '../types'
-import { inject } from 'vue'
function useUpdateNodeInternals(): UpdateNodeInternals {
const store = inject('store')!
diff --git a/src/hooks/useZoomPanHelper.ts b/src/hooks/useZoomPanHelper.ts
index f245b38b..df3692ae 100644
--- a/src/hooks/useZoomPanHelper.ts
+++ b/src/hooks/useZoomPanHelper.ts
@@ -1,76 +1,90 @@
import { zoomIdentity } from 'd3-zoom'
-import { Selection, ZoomBehavior } from 'd3'
import { getRectOfNodes, pointToRendererPoint, getTransformForBounds } from '~/utils/graph'
-import { FitViewParams, FlowTransform, ZoomPanHelperFunctions, Rect, XYPosition, Node, Transform, SnapGrid } from '~/types'
+import {
+ FitViewParams,
+ FlowTransform,
+ ZoomPanHelperFunctions,
+ Rect,
+ XYPosition,
+ Node,
+ Transform,
+ SnapGrid,
+ Dimensions,
+ D3Zoom,
+ D3Selection,
+} from '~/types'
const DEFAULT_PADDING = 0.1
-const useZoomPanHelper = ({
+type UseZoomPanHelper = (
+ d3Zoom: D3Zoom,
+ d3Selection: D3Selection,
+ nodes: Node[],
+ transform: Transform,
+ dimensions: Dimensions,
+ minZoom: number,
+ maxZoom: number,
+ snapToGrid: boolean,
+ snapGrid: SnapGrid,
+) => ZoomPanHelperFunctions
+
+export default (function (
d3Zoom,
d3Selection,
nodes = [],
- width = 0,
- height = 0,
+ transform = [0, 0, 0],
+ dimensions = {
+ width: 0,
+ height: 0,
+ },
minZoom = 0.5,
maxZoom = 2,
- transform = [0, 0, 0],
snapToGrid = false,
snapGrid = [15, 15],
-}: {
- d3Zoom: ZoomBehavior
- d3Selection: Selection
- nodes: Node[]
- width: number
- height: number
- minZoom: number
- maxZoom: number
- transform: Transform
- snapToGrid: boolean
- snapGrid: SnapGrid
-}): ZoomPanHelperFunctions => ({
- zoomIn: () => d3Zoom?.scaleBy(d3Selection, 1.2),
- zoomOut: () => d3Zoom?.scaleBy(d3Selection, 1 / 1.2),
- zoomTo: (zoomLevel: number) => d3Zoom.scaleTo(d3Selection, zoomLevel),
- transform: (transform: FlowTransform) => {
- const nextTransform = zoomIdentity.translate(transform.x, transform.y).scale(transform.zoom)
+): ZoomPanHelperFunctions {
+ return {
+ zoomIn: () => d3Zoom?.scaleBy(d3Selection, 1.2),
+ zoomOut: () => d3Zoom?.scaleBy(d3Selection, 1 / 1.2),
+ zoomTo: (zoomLevel: number) => d3Zoom.scaleTo(d3Selection, zoomLevel),
+ transform: (transform: FlowTransform) => {
+ const nextTransform = zoomIdentity.translate(transform.x, transform.y).scale(transform.zoom)
- d3Zoom.transform(d3Selection, nextTransform)
- },
- fitView: (options: FitViewParams = { padding: DEFAULT_PADDING, includeHiddenNodes: false }) => {
- if (!nodes.length) {
- return
- }
+ d3Zoom.transform(d3Selection, nextTransform)
+ },
+ fitView: (options: FitViewParams = { padding: DEFAULT_PADDING, includeHiddenNodes: false }) => {
+ if (!nodes.length) {
+ return
+ }
- const bounds = getRectOfNodes(options.includeHiddenNodes ? nodes : nodes.filter((node) => !node.isHidden))
- const [x, y, zoom] = getTransformForBounds(
- bounds,
- width,
- height,
- options.minZoom || minZoom,
- options.maxZoom || maxZoom,
- options.padding || DEFAULT_PADDING,
- )
- const transform = zoomIdentity.translate(x, y).scale(zoom)
+ const bounds = getRectOfNodes(options.includeHiddenNodes ? nodes : nodes.filter((node) => !node.isHidden))
+ const [x, y, zoom] = getTransformForBounds(
+ bounds,
+ dimensions.width,
+ dimensions.height,
+ options.minZoom || minZoom,
+ options.maxZoom || maxZoom,
+ options.padding || DEFAULT_PADDING,
+ )
+ const transform = zoomIdentity.translate(x, y).scale(zoom)
- d3Zoom?.transform(d3Selection, transform)
- },
- setCenter: (x: number, y: number, zoom?: number) => {
- const nextZoom = typeof zoom !== 'undefined' ? zoom : maxZoom
- const centerX = width / 2 - x * nextZoom
- const centerY = height / 2 - y * nextZoom
- const transform = zoomIdentity.translate(centerX, centerY).scale(nextZoom)
+ d3Zoom?.transform(d3Selection, transform)
+ },
+ setCenter: (x: number, y: number, zoom?: number) => {
+ const nextZoom = typeof zoom !== 'undefined' ? zoom : maxZoom
+ const centerX = dimensions.width / 2 - x * nextZoom
+ const centerY = dimensions.height / 2 - y * nextZoom
+ const transform = zoomIdentity.translate(centerX, centerY).scale(nextZoom)
- d3Zoom.transform(d3Selection, transform)
- },
- fitBounds: (bounds: Rect, padding = DEFAULT_PADDING) => {
- const [x, y, zoom] = getTransformForBounds(bounds, width, height, minZoom, maxZoom, padding)
- const transform = zoomIdentity.translate(x, y).scale(zoom)
+ d3Zoom.transform(d3Selection, transform)
+ },
+ fitBounds: (bounds: Rect, padding = DEFAULT_PADDING) => {
+ const [x, y, zoom] = getTransformForBounds(bounds, dimensions.width, dimensions.height, minZoom, maxZoom, padding)
+ const transform = zoomIdentity.translate(x, y).scale(zoom)
- d3Zoom.transform(d3Selection, transform)
- },
- project: (position: XYPosition) => {
- return pointToRendererPoint(position, transform, snapToGrid, snapGrid)
- },
-})
-
-export default useZoomPanHelper
+ d3Zoom.transform(d3Selection, transform)
+ },
+ project: (position: XYPosition) => {
+ return pointToRendererPoint(position, transform, snapToGrid, snapGrid)
+ },
+ }
+} as UseZoomPanHelper)
diff --git a/src/index.ts b/src/index.ts
index f0148303..4a055031 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,9 +1,7 @@
-export { default as default } from './container/RevueFlow'
-export { default as Handle } from './components/Handle'
-export { default as EdgeText } from './components/Edges/EdgeText'
-export { getBezierPath } from './components/Edges/BezierEdge'
-export { getSmoothStepPath } from './components/Edges/SmoothStepEdge'
-export { getMarkerEnd, getCenter as getEdgeCenter } from './components/Edges/utils'
+export { default } from './container/Flow.vue'
+export { default as Handle } from './components/Handle/Handle.vue'
+export { default as EdgeText } from './components/Edges/EdgeText.vue'
+export { getBezierPath, getSmoothStepPath, getMarkerEnd, getCenter as getEdgeCenter } from './components/Edges/utils'
export {
isNode,
@@ -15,7 +13,7 @@ export {
getConnectedEdges,
updateEdge,
getTransformForBounds,
- getRectOfNodes
+ getRectOfNodes,
} from './utils/graph'
export { default as useZoomPanHelper } from './hooks/useZoomPanHelper'
export { default as useUpdateNodeInternals } from './hooks/useUpdateNodeInternals'
diff --git a/src/store/configure-store.ts b/src/store/configure-store.ts
index 6f6182be..2b24bc18 100644
--- a/src/store/configure-store.ts
+++ b/src/store/configure-store.ts
@@ -24,12 +24,11 @@ export default function configureStore(
getters: {},
actions: {
setElements(elements) {
- const propElements = elements
const nextElements: NextElements = {
nextNodes: [],
nextEdges: [],
}
- const { nextNodes, nextEdges } = propElements.reduce((res, propElement): NextElements => {
+ const { nextNodes, nextEdges } = elements.reduce((res, propElement): NextElements => {
if (isNode(propElement)) {
const storeNode = this.nodes.find((node) => node.id === propElement.id)
@@ -189,7 +188,7 @@ export default function configureStore(
this.userSelectionRect.draw = false
if (!selectedNodes || selectedNodes.length === 0) {
- this.selectedElements = null
+ this.selectedElements = undefined
this.nodesSelectionActive = false
} else {
this.selectedNodesBbox = getRectOfNodes(selectedNodes)
@@ -202,12 +201,11 @@ export default function configureStore(
this.selectedElements = selectedElementsUpdated ? selectedElementsArr : this.selectedElements
},
initD3Zoom(payload) {
- const { d3Zoom, d3Selection, d3ZoomHandler, transform } = payload
+ const { d3Zoom, d3Selection, d3ZoomHandler } = payload
this.d3Zoom = d3Zoom
this.d3Selection = d3Selection
this.d3ZoomHandler = d3ZoomHandler
- this.transform = transform
},
setMinZoom(minZoom) {
this.d3Zoom?.scaleExtent([minZoom, this.maxZoom])
@@ -237,14 +235,13 @@ export default function configureStore(
})
},
resetSelectedElements() {
- this.selectedElements = null
+ this.selectedElements = undefined
},
unsetNodesSelection() {
this.nodesSelectionActive = false
},
updateSize(size) {
- this.height = size.height
- this.width = size.width
+ this.dimensions = size
},
setConnectionNodeId(payload) {
this.connectionNodeId = payload.connectionNodeId
diff --git a/src/store/index.ts b/src/store/index.ts
index 9c119735..c936a3f6 100644
--- a/src/store/index.ts
+++ b/src/store/index.ts
@@ -1,16 +1,18 @@
import { RevueFlowState, ConnectionMode } from '../types'
export const initialState: RevueFlowState = {
- width: 0,
- height: 0,
+ dimensions: {
+ width: 0,
+ height: 0,
+ },
transform: [0, 0, 1],
nodes: [],
edges: [],
- selectedElements: null,
+ selectedElements: undefined,
selectedNodesBbox: { x: 0, y: 0, width: 0, height: 0 },
- d3Zoom: null,
- d3Selection: null,
+ d3Zoom: undefined,
+ d3Selection: undefined,
d3ZoomHandler: undefined,
minZoom: 0.5,
maxZoom: 2,
@@ -36,8 +38,8 @@ export const initialState: RevueFlowState = {
height: 0,
draw: false,
},
- connectionNodeId: null,
- connectionHandleId: null,
+ connectionNodeId: undefined,
+ connectionHandleId: undefined,
connectionHandleType: 'source',
connectionPosition: { x: 0, y: 0 },
connectionMode: ConnectionMode.Strict,
diff --git a/src/types/connection.ts b/src/types/connection.ts
index dacc7f02..19ea35c6 100644
--- a/src/types/connection.ts
+++ b/src/types/connection.ts
@@ -1,5 +1,6 @@
-import { ComponentPublicInstance } from 'vue'
-import { ElementId, HandleType, Position } from './types'
+import { DefineComponent } from 'vue'
+import { ElementId, Position } from './types'
+import { HandleType } from '~/types/handle'
export enum ConnectionLineType {
Bezier = 'default',
@@ -26,13 +27,13 @@ export type ConnectionLineComponentProps = {
connectionLineType: ConnectionLineType
}
-export type ConnectionLineComponent = ComponentPublicInstance
+export type CustomConnectionLine = DefineComponent
export type OnConnectFunc = (connection: Connection) => void
export type OnConnectStartParams = {
- nodeId: ElementId | null
- handleId: ElementId | null
- handleType: HandleType | null
+ nodeId: ElementId | undefined
+ handleId: ElementId | undefined
+ handleType: HandleType | undefined
}
export type OnConnectStartFunc = (event: MouseEvent, params: OnConnectStartParams) => void
export type OnConnectStopFunc = (event: MouseEvent) => void
@@ -44,7 +45,7 @@ export enum ConnectionMode {
}
export type SetConnectionId = {
- connectionNodeId: ElementId | null
- connectionHandleId: ElementId | null
- connectionHandleType: HandleType | null
+ connectionNodeId: ElementId | undefined
+ connectionHandleId: ElementId | undefined
+ connectionHandleType: HandleType | undefined
}
diff --git a/src/types/edge.ts b/src/types/edge.ts
index 5adae564..ac807b4d 100644
--- a/src/types/edge.ts
+++ b/src/types/edge.ts
@@ -1,4 +1,4 @@
-import { DefineComponent, HTMLAttributes, VNode } from 'vue'
+import { DefineComponent, VNode } from 'vue'
import { ArrowHeadType, ElementId, Position } from './types'
import { Connection } from './connection'
@@ -53,17 +53,6 @@ export interface EdgeSmoothStepProps extends EdgeProps {
borderRadius?: number
}
-export interface EdgeTextProps extends HTMLAttributes {
- x: number
- y: number
- label?: string | VNode
- labelStyle?: any
- labelShowBg?: boolean
- labelBgStyle?: any
- labelBgPadding?: [number, number]
- labelBgBorderRadius?: number
-}
-
export type EdgeType = DefineComponent
export type OnEdgeUpdateFunc = (oldEdge: Edge, newConnection: Connection) => void
diff --git a/src/types/handle.ts b/src/types/handle.ts
index 55c539ed..d44be907 100644
--- a/src/types/handle.ts
+++ b/src/types/handle.ts
@@ -1,5 +1,4 @@
import { Dimensions, ElementId, Position, XYPosition } from './types'
-import { Connection, OnConnectFunc } from './connection'
export type HandleType = 'source' | 'target'
@@ -7,12 +6,3 @@ export interface HandleElement extends XYPosition, Dimensions {
id?: ElementId | null
position: Position
}
-
-export interface HandleProps {
- type: HandleType
- position: Position
- isConnectable?: boolean
- onConnect?: OnConnectFunc
- isValidConnection?: (connection: Connection) => boolean
- id?: ElementId
-}
diff --git a/src/types/node.ts b/src/types/node.ts
index b4a861d8..f023a647 100644
--- a/src/types/node.ts
+++ b/src/types/node.ts
@@ -47,14 +47,14 @@ export type NodeDimensionUpdate = {
export interface NodeProps {
id: ElementId
type: string
- data: T
- selected: boolean
- isConnectable: boolean
+ data?: T
+ selected?: boolean
+ connectable?: boolean
xPos?: number
yPos?: number
targetPosition?: Position
sourcePosition?: Position
- isDragging?: boolean
+ dragging?: boolean
}
export type NodeType = DefineComponent
diff --git a/src/types/panel.ts b/src/types/panel.ts
index d1538e17..9e839a5d 100644
--- a/src/types/panel.ts
+++ b/src/types/panel.ts
@@ -1,5 +1,9 @@
-import { Selection as D3Selection, ZoomBehavior } from 'd3'
-import { FitViewFunc, FlowTransform, Rect, Transform, XYPosition } from './types'
+import { Selection, ZoomBehavior } from 'd3'
+import { FitViewFunc, FlowTransform, Rect, XYPosition } from './types'
+
+export type D3Zoom = ZoomBehavior
+export type D3Selection = Selection
+export type D3ZoomHandler = (this: HTMLDivElement, event: any, d: unknown) => void
export type KeyCode = number | string
@@ -21,8 +25,7 @@ export interface ZoomPanHelperFunctions {
}
export type InitD3ZoomPayload = {
- d3Zoom: ZoomBehavior
- d3Selection: D3Selection
- d3ZoomHandler: ((this: Element, event: any, d: unknown) => void) | undefined
- transform: Transform
+ d3Zoom: D3Zoom
+ d3Selection: D3Selection
+ d3ZoomHandler: D3ZoomHandler
}
diff --git a/src/types/store.ts b/src/types/store.ts
index 869e4895..81f241ff 100644
--- a/src/types/store.ts
+++ b/src/types/store.ts
@@ -1,24 +1,23 @@
-import { ZoomBehavior, Selection } from 'd3'
import { Store } from 'pinia'
-import { ElementId, Elements, Rect, SelectionRect, SnapGrid, Transform, XYPosition } from './types'
+import { Dimensions, ElementId, Elements, Rect, SelectionRect, SnapGrid, Transform, XYPosition } from './types'
import { HandleType } from './handle'
import { ConnectionMode, OnConnectEndFunc, OnConnectFunc, OnConnectStartFunc, OnConnectStopFunc } from './connection'
import { Edge } from './edge'
import { Node, NodeExtent, TranslateExtent } from './node'
import { RevueFlowActions } from './actions'
+import { D3Selection, D3Zoom, D3ZoomHandler } from '~/types/panel'
export interface RevueFlowState {
- width: number
- height: number
+ dimensions: Dimensions
transform: Transform
nodes: Node[]
edges: Edge[]
- selectedElements: Elements | null
+ selectedElements?: Elements
selectedNodesBbox: Rect
- d3Zoom: ZoomBehavior | null
- d3Selection: Selection | null
- d3ZoomHandler: ((this: Element, event: any, d: unknown) => void) | undefined
+ d3Zoom?: D3Zoom
+ d3Selection?: D3Selection
+ d3ZoomHandler?: D3ZoomHandler
minZoom: number
maxZoom: number
translateExtent: TranslateExtent
@@ -29,10 +28,10 @@ export interface RevueFlowState {
userSelectionRect: SelectionRect
- connectionNodeId: ElementId | null
- connectionHandleId: ElementId | null
- connectionHandleType: HandleType | null
- connectionPosition: XYPosition
+ connectionNodeId?: ElementId
+ connectionHandleId?: ElementId
+ connectionHandleType?: HandleType
+ connectionPosition?: XYPosition
connectionMode: ConnectionMode
snapToGrid: boolean
diff --git a/src/utils/index.ts b/src/utils/index.ts
index 357b97c3..81608401 100644
--- a/src/utils/index.ts
+++ b/src/utils/index.ts
@@ -17,5 +17,8 @@ export const clampPosition = (position: XYPosition, extent: NodeExtent): XYPosit
y: clamp(position.y, extent[0][1], extent[1][1]),
})
-export const getHostForElement = (element: HTMLElement): Document | ShadowRoot =>
- (element.getRootNode() as Document | ShadowRoot) || window.document
+export const getHostForElement = (element: HTMLElement): Document => {
+ const doc = element.getRootNode() as Document
+ if ('getElementFromPoint' in doc) return doc
+ else return window.document
+}