update: Add slots to CustomConnectionLine.vue, Edge.vue, Node.vue

This commit is contained in:
Braks
2021-10-20 22:39:54 +02:00
parent b3d276bfb8
commit 9d9a90e9a7
25 changed files with 363 additions and 391 deletions
+1 -5
View File
@@ -38,7 +38,6 @@ const updatePos = () => {
y: Math.random() * 400, y: Math.random() * 400,
} }
} }
return el return el
}) })
} }
@@ -48,10 +47,7 @@ const resetTransform = () => rfInstance.value?.setTransform({ x: 0, y: 0, zoom:
const toggleClassnames = () => { const toggleClassnames = () => {
elements.value = elements.value.map((el: FlowElement) => { elements.value = elements.value.map((el: FlowElement) => {
if (isNode(el)) { if (isNode(el)) el.className = el.className === 'light' ? 'dark' : 'light'
el.className = el.className === 'light' ? 'dark' : 'light'
}
return el return el
}) })
} }
@@ -15,7 +15,10 @@ const onElementsRemove = (elementsToRemove: Elements) =>
const onConnect = (params: Connection | Edge) => (elements.value = addEdge(params, elements.value as Elements)) const onConnect = (params: Connection | Edge) => (elements.value = addEdge(params, elements.value as Elements))
</script> </script>
<template> <template>
<Flow :elements="elements" :custom-connection-line="ConnectionLine" @elements-remove="onElementsRemove" @connect="onConnect"> <Flow :elements="elements" @elements-remove="onElementsRemove" @connect="onConnect">
<template #custom-connection-line="props">
<ConnectionLine v-bind="props" />
</template>
<Background :variant="BackgroundVariant.Lines" /> <Background :variant="BackgroundVariant.Lines" />
</Flow> </Flow>
</template> </template>
@@ -1,6 +1,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import { CSSProperties } from 'vue' import { CSSProperties } from 'vue'
import { ConnectionLineType, CustomConnectionLine, HandleElement, Node, Position } from '~/types' import { ConnectionLineType, HandleElement, Node, Position } from '~/types'
import { getBezierPath, getSmoothStepPath } from '~/components/Edges/utils' import { getBezierPath, getSmoothStepPath } from '~/components/Edges/utils'
import { Store } from '~/context' import { Store } from '~/context'
@@ -8,7 +8,6 @@ interface ConnectionLineProps {
sourceNode: Node sourceNode: Node
connectionLineType?: ConnectionLineType connectionLineType?: ConnectionLineType
connectionLineStyle?: CSSProperties connectionLineStyle?: CSSProperties
customConnectionLine?: CustomConnectionLine
} }
const props = withDefaults(defineProps<ConnectionLineProps>(), { const props = withDefaults(defineProps<ConnectionLineProps>(), {
@@ -73,20 +72,19 @@ const dAttr = computed(() => {
</script> </script>
<template> <template>
<g class="revue-flow__connection"> <g class="revue-flow__connection">
<component <slot
:is="props.customConnectionLine"
v-if="props.customConnectionLine"
v-bind="{ v-bind="{
sourceX: sourceX, sourceX,
sourceY: sourceY, sourceY,
sourcePosition: sourceHandle.position, sourcePosition: sourceHandle.position,
targetX: targetX, targetX,
targetY: targetY, targetY,
targetPosition: targetPosition, targetPosition,
connectionLineType: props.connectionLineType, connectionLineType: props.connectionLineType,
connectionLineStyle: props.connectionLineStyle, connectionLineStyle: props.connectionLineStyle,
}" }"
/> >
<path v-else :d="dAttr" class="revue-flow__connection-path" :style="props.connectionLineStyle" /> <path :d="dAttr" class="revue-flow__connection-path" :style="props.connectionLineStyle" />
</slot>
</g> </g>
</template> </template>
+38 -12
View File
@@ -2,16 +2,14 @@
import EdgeAnchor from './EdgeAnchor.vue' import EdgeAnchor from './EdgeAnchor.vue'
import { getEdgePositions, getHandle, getSourceTargetNodes, isEdgeVisible } from '~/container/EdgeRenderer/utils' import { getEdgePositions, getHandle, getSourceTargetNodes, isEdgeVisible } from '~/container/EdgeRenderer/utils'
import { isEdge } from '~/utils/graph' import { isEdge } from '~/utils/graph'
import { ConnectionMode, Dimensions, Edge, EdgeType, Position, Transform } from '~/types' import { ConnectionMode, Edge, EdgeType, Position } from '~/types'
import { onMouseDown } from '~/components/Handle/utils'
import { Hooks, Store } from '~/context' import { Hooks, Store } from '~/context'
import { useHandle } from '~/composables'
interface EdgeProps { interface EdgeProps {
type: EdgeType type: EdgeType
edge: Edge edge: Edge
nodes: ReturnType<typeof getSourceTargetNodes> nodes: ReturnType<typeof getSourceTargetNodes>
dimensions: Dimensions
transform: Transform
markerEndId?: string markerEndId?: string
edgeUpdaterRadius?: number edgeUpdaterRadius?: number
} }
@@ -68,6 +66,7 @@ const onEdgeMouseMove = (event: MouseEvent) => hooks.edgeMouseMove.trigger({ eve
const onEdgeMouseLeave = (event: MouseEvent) => hooks.edgeMouseLeave.trigger({ event, edge: props.edge }) const onEdgeMouseLeave = (event: MouseEvent) => hooks.edgeMouseLeave.trigger({ event, edge: props.edge })
const handler = useHandle()
const handleEdgeUpdater = (event: MouseEvent, isSourceHandle: boolean) => { const handleEdgeUpdater = (event: MouseEvent, isSourceHandle: boolean) => {
const nodeId = isSourceHandle ? props.edge.target : props.edge.source const nodeId = isSourceHandle ? props.edge.target : props.edge.source
const handleId = isSourceHandle ? props.edge.targetHandle : props.edge.sourceHandle const handleId = isSourceHandle ? props.edge.targetHandle : props.edge.sourceHandle
@@ -75,8 +74,7 @@ const handleEdgeUpdater = (event: MouseEvent, isSourceHandle: boolean) => {
const isTarget = isSourceHandle const isTarget = isSourceHandle
hooks.edgeUpdateStart.trigger({ event, edge: props.edge }) hooks.edgeUpdateStart.trigger({ event, edge: props.edge })
handleId && handleId && handler(event, handleId, nodeId, isTarget, isValidConnection, isSourceHandle ? 'target' : 'source')
onMouseDown(event, store, hooks, handleId, nodeId, isTarget, isValidConnection, isSourceHandle ? 'target' : 'source')
} }
const onEdgeUpdaterSourceMouseDown = (event: MouseEvent) => { const onEdgeUpdaterSourceMouseDown = (event: MouseEvent) => {
@@ -95,9 +93,9 @@ const isVisible = ({ sourceX, sourceY, targetX, targetY }: ReturnType<typeof get
? isEdgeVisible({ ? isEdgeVisible({
sourcePos: { x: sourceX, y: sourceY }, sourcePos: { x: sourceX, y: sourceY },
targetPos: { x: targetX, y: targetY }, targetPos: { x: targetX, y: targetY },
width: props.dimensions.width, width: store.dimensions.width,
height: props.dimensions.height, height: store.dimensions.height,
transform: props.transform, transform: store.transform,
}) })
: true : true
} }
@@ -125,8 +123,7 @@ const visible = computed(() => !props.edge.isHidden && isVisible(edgePos.value))
@mousemove="onEdgeMouseMove" @mousemove="onEdgeMouseMove"
@mouseleave="onEdgeMouseLeave" @mouseleave="onEdgeMouseLeave"
> >
<component <slot
:is="props.type"
v-bind="{ v-bind="{
id: props.edge.id, id: props.edge.id,
source: props.edge.source, source: props.edge.source,
@@ -152,7 +149,36 @@ const visible = computed(() => !props.edge.isHidden && isVisible(edgePos.value))
sourceHandleId: props.edge.sourceHandle, sourceHandleId: props.edge.sourceHandle,
targetHandleId: props.edge.targetHandle, targetHandleId: props.edge.targetHandle,
}" }"
/> >
<component
:is="props.type"
v-bind="{
id: props.edge.id,
source: props.edge.source,
target: props.edge.target,
selected: isSelected,
animated: props.edge.animated,
label: props.edge.label,
labelStyle: props.edge.labelStyle,
labelShowBg: props.edge.labelShowBg,
labelBgStyle: props.edge.labelBgStyle,
labelBgPadding: props.edge.labelBgPadding,
labelBgBorderRadius: props.edge.labelBgBorderRadius,
data: props.edge.data,
style: props.edge.style,
arrowHeadType: props.edge.arrowHeadType,
sourcePosition,
targetPosition,
sourceX: edgePos.sourceX,
sourceY: edgePos.sourceY,
targetX: edgePos.targetX,
targetY: edgePos.targetY,
markerEndId: props.markerEndId,
sourceHandleId: props.edge.sourceHandle,
targetHandleId: props.edge.targetHandle,
}"
/>
</slot>
<g @mousedown="onEdgeUpdaterSourceMouseDown" @mouseenter="onEdgeUpdaterMouseEnter" @mouseout="onEdgeUpdaterMouseOut"> <g @mousedown="onEdgeUpdaterSourceMouseDown" @mouseenter="onEdgeUpdaterMouseEnter" @mouseout="onEdgeUpdaterMouseOut">
<EdgeAnchor <EdgeAnchor
:position="sourcePosition" :position="sourcePosition"
+4 -14
View File
@@ -1,7 +1,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ElementId, Position } from '~/types' import { ElementId, Position, ValidConnectionFunc } from '~/types'
import { onMouseDown, ValidConnectionFunc } from '~/components/Handle/utils'
import { Hooks, Store } from '~/context' import { Hooks, Store } from '~/context'
import { useHandle } from '~/composables'
interface HandleProps { interface HandleProps {
id?: string id?: string
@@ -22,19 +22,9 @@ const store = inject(Store)!
const hooks = inject(Hooks)! const hooks = inject(Hooks)!
const nodeId = inject<ElementId>('NodeIdContext')! const nodeId = inject<ElementId>('NodeIdContext')!
const handler = useHandle()
const onMouseDownHandler = (event: MouseEvent) => const onMouseDownHandler = (event: MouseEvent) =>
onMouseDown( handler(event, props.id, nodeId, props.type === 'target', props.isValidConnection)
event,
store,
hooks,
props.id,
nodeId,
props.type === 'target',
props.isValidConnection ??
function () {
return true
},
)
</script> </script>
<template> <template>
<div <div
-179
View File
@@ -1,179 +0,0 @@
import { getHostForElement } from '~/utils'
import { ElementId, ConnectionMode, Connection, HandleType, FlowStore, FlowHooks } from '~/types'
export type ValidConnectionFunc = (connection: Connection) => boolean
type Result = {
elementBelow: Element | null
isValid: boolean
connection: Connection
isHoveringHandle: boolean
}
// checks if element below mouse is a handle and returns connection in form of an object { source: 123, target: 312 }
function checkElementBelowIsValid(
event: MouseEvent,
connectionMode: ConnectionMode,
isTarget: boolean,
nodeId: ElementId,
handleId: ElementId | null,
isValidConnection: ValidConnectionFunc,
doc: Document,
) {
const elementBelow = doc.elementFromPoint(event.clientX, event.clientY)
const elementBelowIsTarget = elementBelow?.classList.contains('target') || false
const elementBelowIsSource = elementBelow?.classList.contains('source') || false
const result: Result = {
elementBelow,
isValid: false,
connection: { source: null, target: null, sourceHandle: null, targetHandle: null },
isHoveringHandle: false,
}
if (elementBelow && (elementBelowIsTarget || elementBelowIsSource)) {
result.isHoveringHandle = true
// in strict mode we don't allow target to target or source to source connections
const isValid =
connectionMode === ConnectionMode.Strict ? (isTarget && elementBelowIsSource) || (!isTarget && elementBelowIsTarget) : true
if (isValid) {
const elementBelowNodeId = elementBelow.getAttribute('data-nodeid')
const elementBelowHandleId = elementBelow.getAttribute('data-handleid')
const connection: Connection = isTarget
? {
source: elementBelowNodeId,
sourceHandle: elementBelowHandleId,
target: nodeId,
targetHandle: handleId,
}
: {
source: nodeId,
sourceHandle: handleId,
target: elementBelowNodeId,
targetHandle: elementBelowHandleId,
}
result.connection = connection
result.isValid = isValidConnection(connection)
}
}
return result
}
function resetRecentHandle(hoveredHandle: Element): void {
hoveredHandle?.classList.remove('revue-flow__handle-valid')
hoveredHandle?.classList.remove('revue-flow__handle-connecting')
}
export function onMouseDown(
event: MouseEvent,
store: FlowStore,
hooks: {
connectStart: FlowHooks['connectStart']
connectStop: FlowHooks['connectStop']
connectEnd: FlowHooks['connectEnd']
connect: FlowHooks['connect']
edgeUpdateEnd: FlowHooks['edgeUpdateEnd']
},
handleId: ElementId,
nodeId: ElementId,
isTarget: boolean,
isValidConnection: ValidConnectionFunc = () => {
return true
},
elementEdgeUpdaterType?: HandleType,
): void {
const revueFlowNode = (event.target as Element).closest('.revue-flow')
// when revue-flow is used inside a shadow root we can't use document
const doc = getHostForElement(event.target as HTMLElement)
if (!doc) {
return
}
const elementBelow = doc.elementFromPoint(event.clientX, event.clientY)
const elementBelowIsTarget = elementBelow?.classList.contains('target')
const elementBelowIsSource = elementBelow?.classList.contains('source')
if (!revueFlowNode || (!elementBelowIsTarget && !elementBelowIsSource && !elementEdgeUpdaterType)) {
return
}
const handleType = elementEdgeUpdaterType || (elementBelowIsTarget ? 'target' : 'source')
const containerBounds = revueFlowNode.getBoundingClientRect()
let recentHoveredHandle: Element
store.connectionPosition.x = event.clientX - containerBounds.left
store.connectionPosition.y = event.clientY - containerBounds.top
store.setConnectionNodeId({
connectionNodeId: nodeId,
connectionHandleId: handleId,
connectionHandleType: handleType,
})
hooks.connectStart.trigger({ event, params: { nodeId, handleId, handleType } })
function onMouseMove(event: MouseEvent) {
store.connectionPosition.x = event.clientX - containerBounds.left
store.connectionPosition.y = event.clientY - containerBounds.top
const { connection, elementBelow, isValid, isHoveringHandle } = checkElementBelowIsValid(
event,
store.connectionMode,
isTarget,
nodeId,
handleId,
isValidConnection,
doc,
)
if (!isHoveringHandle) {
return resetRecentHandle(recentHoveredHandle)
}
const isOwnHandle = connection.source === connection.target
if (!isOwnHandle && elementBelow) {
recentHoveredHandle = elementBelow
elementBelow.classList.add('revue-flow__handle-connecting')
elementBelow.classList.toggle('revue-flow__handle-valid', isValid)
}
}
function onMouseUp(event: MouseEvent) {
const { connection, isValid } = checkElementBelowIsValid(
event,
store.connectionMode,
isTarget,
nodeId,
handleId,
isValidConnection,
doc,
)
hooks.connectStop.trigger(event)
if (isValid) {
hooks.connect.trigger(connection)
}
hooks.connectEnd.trigger(event)
if (elementEdgeUpdaterType) {
hooks.edgeUpdateEnd.trigger({ event } as any)
}
resetRecentHandle(recentHoveredHandle)
store.setConnectionNodeId({ connectionNodeId: undefined, connectionHandleId: undefined, connectionHandleType: undefined })
store.connectionPosition = { x: NaN, y: NaN }
doc.removeEventListener('mousemove', onMouseMove as EventListenerOrEventListenerObject)
doc.removeEventListener('mouseup', onMouseUp as EventListenerOrEventListenerObject)
}
doc.addEventListener('mousemove', onMouseMove as EventListenerOrEventListenerObject)
doc.addEventListener('mouseup', onMouseUp as EventListenerOrEventListenerObject)
}
+17 -4
View File
@@ -95,7 +95,6 @@ const onDragStop: DraggableEventListener = ({ event }) => {
if (props.selectable && !props.selectNodesOnDrag && !props.selected) { if (props.selectable && !props.selectNodesOnDrag && !props.selected) {
store.addSelectedElements([n]) store.addSelectedElements([n])
} }
hooks.nodeClick.trigger({ event, node: n }) hooks.nodeClick.trigger({ event, node: n })
return return
@@ -163,8 +162,7 @@ onMounted(() => {
@contextmenu="onContextMenuHandler" @contextmenu="onContextMenuHandler"
@click="onSelectNodeHandler" @click="onSelectNodeHandler"
> >
<component <slot
:is="props.type"
v-bind="{ v-bind="{
data: props.node.data, data: props.node.data,
type: props.node.type, type: props.node.type,
@@ -176,7 +174,22 @@ onMounted(() => {
targetPosition: props.node.targetPosition, targetPosition: props.node.targetPosition,
dragging: props.node.__rf.isDragging, dragging: props.node.__rf.isDragging,
}" }"
/> >
<component
:is="props.type"
v-bind="{
data: props.node.data,
type: props.node.type,
xPos: props.node.__rf.position.x,
yPos: props.node.__rf.position.y,
selected: props.selected,
connectable: props.connectable,
sourcePosition: props.node.sourcePosition,
targetPosition: props.node.targetPosition,
dragging: props.node.__rf.isDragging,
}"
/>
</slot>
</div> </div>
</DraggableCore> </DraggableCore>
</template> </template>
+1 -2
View File
@@ -1,7 +1,6 @@
export { default as useGlobalKeyHandler } from './useGlobalKeyHandler' export { default as useHandle } from './useHandle'
export { default as useHooks } from './useHooks' export { default as useHooks } from './useHooks'
export { default as useKeyPress } from './useKeyPress' export { default as useKeyPress } from './useKeyPress'
export { default as useResizeHandler } from './useResizeHandler'
export { default as useUpdateNodeInternals } from './useUpdateNodeInternals' export { default as useUpdateNodeInternals } from './useUpdateNodeInternals'
export { default as useZoom } from './useZoom' export { default as useZoom } from './useZoom'
export { default as useZoomPanHelper } from './useZoomPanHelper' export { default as useZoomPanHelper } from './useZoomPanHelper'
-31
View File
@@ -1,31 +0,0 @@
import useKeyPress from './useKeyPress'
import { isNode, getConnectedEdges } from '~/utils/graph'
import { Elements, KeyCode, ElementId, FlowElement } from '~/types'
import { Store } from '~/context'
interface HookParams {
deleteKeyCode: KeyCode
multiSelectionKeyCode: KeyCode
onElementsRemove?: (elements: Elements) => void
}
export default ({ deleteKeyCode, multiSelectionKeyCode, onElementsRemove = () => {} }: HookParams): void => {
const store = inject(Store)!
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<ElementId, FlowElement>(),
)
onElementsRemove(Array.from(elementsToRemove.values()))
store.unsetNodesSelection()
store.resetSelectedElements()
}
})
useKeyPress(multiSelectionKeyCode, (keyPressed) => (store.multiSelectionActive = keyPressed))
}
+171
View File
@@ -0,0 +1,171 @@
import { getHostForElement } from '~/utils'
import { Hooks, Store } from '~/context'
import { Connection, ConnectionMode, ElementId, HandleType, ValidConnectionFunc } from '~/types'
type Result = {
elementBelow: Element | null
isValid: boolean
connection: Connection
isHoveringHandle: boolean
}
// checks if element below mouse is a handle and returns connection in form of an object { source: 123, target: 312 }
export function checkElementBelowIsValid(
event: MouseEvent,
connectionMode: ConnectionMode,
isTarget: boolean,
nodeId: ElementId,
handleId: ElementId | null,
isValidConnection: ValidConnectionFunc,
doc: Document,
) {
const elementBelow = doc.elementFromPoint(event.clientX, event.clientY)
const elementBelowIsTarget = elementBelow?.classList.contains('target') || false
const elementBelowIsSource = elementBelow?.classList.contains('source') || false
const result: Result = {
elementBelow,
isValid: false,
connection: { source: null, target: null, sourceHandle: null, targetHandle: null },
isHoveringHandle: false,
}
if (elementBelow && (elementBelowIsTarget || elementBelowIsSource)) {
result.isHoveringHandle = true
// in strict mode we don't allow target to target or source to source connections
const isValid =
connectionMode === ConnectionMode.Strict ? (isTarget && elementBelowIsSource) || (!isTarget && elementBelowIsTarget) : true
if (isValid) {
const elementBelowNodeId = elementBelow.getAttribute('data-nodeid')
const elementBelowHandleId = elementBelow.getAttribute('data-handleid')
const connection: Connection = isTarget
? {
source: elementBelowNodeId,
sourceHandle: elementBelowHandleId,
target: nodeId,
targetHandle: handleId,
}
: {
source: nodeId,
sourceHandle: handleId,
target: elementBelowNodeId,
targetHandle: elementBelowHandleId,
}
result.connection = connection
result.isValid = isValidConnection(connection)
}
}
return result
}
function resetRecentHandle(hoveredHandle: Element): void {
hoveredHandle?.classList.remove('revue-flow__handle-valid')
hoveredHandle?.classList.remove('revue-flow__handle-connecting')
}
export default function () {
const store = inject(Store)!
const hooks = inject(Hooks)!
return (
event: MouseEvent,
handleId: ElementId,
nodeId: ElementId,
isTarget: boolean,
isValidConnection: ValidConnectionFunc = () => {
return true
},
elementEdgeUpdaterType?: HandleType,
) => {
const revueFlowNode = (event.target as Element).closest('.revue-flow')
// when revue-flow is used inside a shadow root we can't use document
const doc = getHostForElement(event.target as HTMLElement)
if (!doc) return
const elementBelow = doc.elementFromPoint(event.clientX, event.clientY)
const elementBelowIsTarget = elementBelow?.classList.contains('target')
const elementBelowIsSource = elementBelow?.classList.contains('source')
if (!revueFlowNode || (!elementBelowIsTarget && !elementBelowIsSource && !elementEdgeUpdaterType)) return
const handleType = elementEdgeUpdaterType || (elementBelowIsTarget ? 'target' : 'source')
const containerBounds = revueFlowNode.getBoundingClientRect()
let recentHoveredHandle: Element
store.connectionPosition.x = event.clientX - containerBounds.left
store.connectionPosition.y = event.clientY - containerBounds.top
store.setConnectionNodeId({
connectionNodeId: nodeId,
connectionHandleId: handleId,
connectionHandleType: handleType,
})
hooks.connectStart.trigger({ event, params: { nodeId, handleId, handleType } })
function onMouseMove(event: MouseEvent) {
store.connectionPosition.x = event.clientX - containerBounds.left
store.connectionPosition.y = event.clientY - containerBounds.top
const { connection, elementBelow, isValid, isHoveringHandle } = checkElementBelowIsValid(
event,
store.connectionMode,
isTarget,
nodeId,
handleId,
isValidConnection,
doc,
)
if (!isHoveringHandle) {
return resetRecentHandle(recentHoveredHandle)
}
const isOwnHandle = connection.source === connection.target
if (!isOwnHandle && elementBelow) {
recentHoveredHandle = elementBelow
elementBelow.classList.add('revue-flow__handle-connecting')
elementBelow.classList.toggle('revue-flow__handle-valid', isValid)
}
}
function onMouseUp(event: MouseEvent) {
const { connection, isValid } = checkElementBelowIsValid(
event,
store.connectionMode,
isTarget,
nodeId,
handleId,
isValidConnection,
doc,
)
hooks.connectStop.trigger(event)
if (isValid) {
hooks.connect.trigger(connection)
}
hooks.connectEnd.trigger(event)
if (elementEdgeUpdaterType) {
hooks.edgeUpdateEnd.trigger({ event } as any)
}
resetRecentHandle(recentHoveredHandle)
store.setConnectionNodeId({ connectionNodeId: undefined, connectionHandleId: undefined, connectionHandleType: undefined })
store.connectionPosition = { x: NaN, y: NaN }
doc.removeEventListener('mousemove', onMouseMove as EventListenerOrEventListenerObject)
doc.removeEventListener('mouseup', onMouseUp as EventListenerOrEventListenerObject)
}
doc.addEventListener('mousemove', onMouseMove as EventListenerOrEventListenerObject)
doc.addEventListener('mouseup', onMouseUp as EventListenerOrEventListenerObject)
}
}
-23
View File
@@ -1,23 +0,0 @@
import { Ref } from 'vue'
import { getDimensions } from '~/utils'
import { Dimensions } from '~/types'
export default function (el: Ref<HTMLDivElement>) {
const dimensions = ref<Dimensions>({ width: 0, height: 0 })
const updateDimensions = () => {
const unrefEl = unrefElement(el)
if (!unrefEl) return
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
}
+4 -5
View File
@@ -1,7 +1,8 @@
import { ElementId, RevueFlowStore, UpdateNodeInternals } from '../types' import { ElementId, UpdateNodeInternals } from '~/types'
import { Store } from '~/context'
function useUpdateNodeInternals(): UpdateNodeInternals { export default function (): UpdateNodeInternals {
const store = inject<RevueFlowStore>('store')! const store = inject(Store)!
return (id: ElementId) => { return (id: ElementId) => {
const nodeElement: HTMLDivElement | null = document.querySelector(`.revue-flow__node[data-id="${id}"]`) const nodeElement: HTMLDivElement | null = document.querySelector(`.revue-flow__node[data-id="${id}"]`)
@@ -11,5 +12,3 @@ function useUpdateNodeInternals(): UpdateNodeInternals {
} }
} }
} }
export default useUpdateNodeInternals
+1 -21
View File
@@ -2,7 +2,7 @@ import { D3ZoomEvent, zoom, zoomIdentity, ZoomTransform } from 'd3-zoom'
import { pointer, select } from 'd3-selection' import { pointer, select } from 'd3-selection'
import { Ref } from 'vue' import { Ref } from 'vue'
import { get } from '@vueuse/core' import { get } from '@vueuse/core'
import { D3Selection, D3Zoom, FlowTransform, KeyCode, PanOnScrollMode, Transform } from '~/types' import { FlowTransform, PanOnScrollMode, Transform, UseZoom, UseZoomOptions } from '~/types'
import { clamp } from '~/utils' import { clamp } from '~/utils'
import useKeyPress from '~/composables/useKeyPress' import useKeyPress from '~/composables/useKeyPress'
import { Hooks, Store } from '~/context' import { Hooks, Store } from '~/context'
@@ -16,26 +16,6 @@ const eventToFlowTransform = (eventTransform: ZoomTransform): FlowTransform => (
zoom: eventTransform.k, zoom: eventTransform.k,
}) })
interface UseZoomOptions {
selectionKeyCode?: KeyCode
zoomActivationKeyCode?: KeyCode
paneMoveable?: boolean
defaultZoom?: number
defaultPosition?: [number, number]
zoomOnScroll?: boolean
zoomOnPinch?: boolean
panOnScroll?: boolean
panOnScrollSpeed?: number
panOnScrollMode?: PanOnScrollMode
zoomOnDoubleClick?: boolean
}
interface UseZoom {
transform: Ref<Transform>
d3Zoom: Ref<D3Zoom>
d3Selection: Ref<D3Selection>
}
export default function (el: Ref<HTMLDivElement>, options: UseZoomOptions): UseZoom { export default function (el: Ref<HTMLDivElement>, options: UseZoomOptions): UseZoom {
const { const {
selectionKeyCode = 'Shift', selectionKeyCode = 'Shift',
+2 -7
View File
@@ -13,13 +13,10 @@ export default function (): UseZoomPanHelper {
zoomTo: (zoomLevel: number) => store.d3Selection && store.d3Zoom?.scaleTo(store.d3Selection, zoomLevel), zoomTo: (zoomLevel: number) => store.d3Selection && store.d3Zoom?.scaleTo(store.d3Selection, zoomLevel),
transform: (transform: FlowTransform) => { transform: (transform: FlowTransform) => {
const nextTransform = zoomIdentity.translate(transform.x, transform.y).scale(transform.zoom) const nextTransform = zoomIdentity.translate(transform.x, transform.y).scale(transform.zoom)
store.d3Selection && store.d3Zoom?.transform(store.d3Selection, nextTransform) store.d3Selection && store.d3Zoom?.transform(store.d3Selection, nextTransform)
}, },
fitView: (options: FitViewParams = { padding: DEFAULT_PADDING, includeHiddenNodes: false }) => { fitView: (options: FitViewParams = { padding: DEFAULT_PADDING, includeHiddenNodes: false }) => {
if (!store.nodes.length) { if (!store.nodes.length) return
return
}
const bounds = getRectOfNodes(options.includeHiddenNodes ? store.nodes : store.nodes.filter((node) => !node.isHidden)) const bounds = getRectOfNodes(options.includeHiddenNodes ? store.nodes : store.nodes.filter((node) => !node.isHidden))
const [x, y, zoom] = getTransformForBounds( const [x, y, zoom] = getTransformForBounds(
@@ -55,8 +52,6 @@ export default function (): UseZoomPanHelper {
store.d3Selection && store.d3Zoom?.transform(store.d3Selection, transform) store.d3Selection && store.d3Zoom?.transform(store.d3Selection, transform)
}, },
project: (position: XYPosition) => { project: (position: XYPosition) => pointToRendererPoint(position, store.transform, store.snapToGrid, store.snapGrid),
return pointToRendererPoint(position, store.transform, store.snapToGrid, store.snapGrid)
},
} }
} }
+13 -13
View File
@@ -4,25 +4,20 @@ import { getSourceTargetNodes } from './utils'
import MarkerDefinitions from './MarkerDefinitions.vue' import MarkerDefinitions from './MarkerDefinitions.vue'
import Edge from '~/components/Edges/Edge.vue' import Edge from '~/components/Edges/Edge.vue'
import ConnectionLine from '~/components/ConnectionLine/ConnectionLine.vue' import ConnectionLine from '~/components/ConnectionLine/ConnectionLine.vue'
import { ConnectionLineType, CustomConnectionLine, Dimensions, EdgeType, Transform } from '~/types' import { ConnectionLineType, EdgeType } from '~/types'
import { Store } from '~/context' import { Store } from '~/context'
interface EdgeRendererProps { interface EdgeRendererProps {
edgeTypes: Record<string, EdgeType> edgeTypes: Record<string, EdgeType>
dimensions: Dimensions
transform: Transform
connectionLineType?: ConnectionLineType connectionLineType?: ConnectionLineType
connectionLineStyle?: CSSProperties connectionLineStyle?: CSSProperties
customConnectionLine?: CustomConnectionLine
arrowHeadColor?: string arrowHeadColor?: string
markerEndId?: string markerEndId?: string
edgeUpdaterRadius?: number edgeUpdaterRadius?: number
} }
const props = withDefaults(defineProps<EdgeRendererProps>(), { const props = withDefaults(defineProps<EdgeRendererProps>(), {
transform: () => [0, 0, 1],
arrowHeadColor: '#b1b1b7', arrowHeadColor: '#b1b1b7',
dimensions: () => ({ width: 0, height: 0 }),
connectionLineType: ConnectionLineType.Bezier, connectionLineType: ConnectionLineType.Bezier,
edgeUpdaterRadius: 10, edgeUpdaterRadius: 10,
}) })
@@ -35,10 +30,10 @@ const connectionLineVisible = computed(
) )
</script> </script>
<template> <template>
<svg :width="props.dimensions.width" :height="props.dimensions.height" class="revue-flow__edges"> <svg :width="store.dimensions.width" :height="store.dimensions.height" class="revue-flow__edges">
<MarkerDefinitions :color="props.arrowHeadColor" /> <MarkerDefinitions :color="props.arrowHeadColor" />
<g <g
:transform="props.transform.length && `translate(${props.transform[0]},${props.transform[1]}) scale(${props.transform[2]})`" :transform="store.transform.length && `translate(${store.transform[0]},${store.transform[1]}) scale(${store.transform[2]})`"
> >
<template v-for="edge of store.edges" :key="edge.id"> <template v-for="edge of store.edges" :key="edge.id">
<Edge <Edge
@@ -46,19 +41,24 @@ const connectionLineVisible = computed(
:edge="edge" :edge="edge"
:nodes="getSourceTargetNodes(edge, store.nodes)" :nodes="getSourceTargetNodes(edge, store.nodes)"
:type="props.edgeTypes[edge.type || 'default']" :type="props.edgeTypes[edge.type || 'default']"
:dimensions="props.dimensions"
:transform="props.transform"
:marker-end-id="props.markerEndId" :marker-end-id="props.markerEndId"
:edge-updater-radius="props.edgeUpdaterRadius" :edge-updater-radius="props.edgeUpdaterRadius"
/> >
<template #default="edgeProps">
<slot name="edge" v-bind="edgeProps"></slot>
</template>
</Edge>
</template> </template>
<ConnectionLine <ConnectionLine
v-if="connectionLineVisible" v-if="connectionLineVisible"
:source-node="sourceNode" :source-node="sourceNode"
:connection-line-style="props.connectionLineStyle" :connection-line-style="props.connectionLineStyle"
:connection-line-type="props.connectionLineType" :connection-line-type="props.connectionLineType"
:custom-connection-line="props.customConnectionLine" >
/> <template #default="customConnectionLineProps">
<slot name="custom-connection-line" v-bind="customConnectionLineProps"></slot>
</template>
</ConnectionLine>
</g> </g>
</svg> </svg>
</template> </template>
+26 -22
View File
@@ -13,12 +13,11 @@ import {
PanOnScrollMode, PanOnScrollMode,
NodeType, NodeType,
EdgeType, EdgeType,
CustomConnectionLine,
KeyCode, KeyCode,
TranslateExtent, TranslateExtent,
NodeExtent, NodeExtent,
} from '~/types' } from '~/types'
import configureStore from '~/store/configure-store' import useFlowStore from '~/store/useFlowStore'
import { initialState } from '~/store' import { initialState } from '~/store'
import { DefaultNode, InputNode, OutputNode } from '~/components/Nodes' import { DefaultNode, InputNode, OutputNode } from '~/components/Nodes'
import { BezierEdge, SmoothStepEdge, StepEdge, StraightEdge } from '~/components/Edges' import { BezierEdge, SmoothStepEdge, StepEdge, StraightEdge } from '~/components/Edges'
@@ -34,7 +33,6 @@ export interface FlowProps {
connectionMode?: ConnectionMode connectionMode?: ConnectionMode
connectionLineType?: ConnectionLineType connectionLineType?: ConnectionLineType
connectionLineStyle?: CSSProperties connectionLineStyle?: CSSProperties
customConnectionLine?: CustomConnectionLine
deleteKeyCode?: KeyCode deleteKeyCode?: KeyCode
selectionKeyCode?: KeyCode selectionKeyCode?: KeyCode
multiSelectionKeyCode?: KeyCode multiSelectionKeyCode?: KeyCode
@@ -118,7 +116,7 @@ const defaultEdgeTypes: Record<string, EdgeType> = {
let store = inject(Store)! let store = inject(Store)!
if (!store) { if (!store) {
store = configureStore({ store = useFlowStore({
...initialState, ...initialState,
...props, ...props,
})() })()
@@ -160,25 +158,31 @@ const edgeTypes = createEdgeTypes({ ...defaultEdgeTypes, ...props.edgeTypes })
:pan-on-scroll-mode="props.panOnScrollMode" :pan-on-scroll-mode="props.panOnScrollMode"
:pane-moveable="props.paneMoveable" :pane-moveable="props.paneMoveable"
> >
<template #default="{ transform, dimensions }"> <SelectionPane
<SelectionPane :delete-key-code="props.deleteKeyCode"
:delete-key-code="props.deleteKeyCode" :multi-selection-key-code="props.multiSelectionKeyCode"
:multi-selection-key-code="props.multiSelectionKeyCode" :selection-key-code="props.selectionKeyCode"
:selection-key-code="props.selectionKeyCode" >
<NodeRenderer :node-types="nodeTypes">
<template #default="nodeProps">
<slot name="node" v-bind="nodeProps"></slot>
</template>
</NodeRenderer>
<EdgeRenderer
:connection-line-type="props.connectionLineType"
:connection-line-style="props.connectionLineStyle"
:arrow-head-color="props.arrowHeadColor"
:marker-end-id="props.markerEndId"
:edge-types="edgeTypes"
> >
<NodeRenderer :node-types="nodeTypes" :transform="transform" :dimensions="dimensions" /> <template #edge="edgeProps">
<EdgeRenderer <slot name="edge" v-bind="edgeProps"></slot>
:connection-line-type="props.connectionLineType" </template>
:connection-line-style="props.connectionLineStyle" <template #custom-connection-line="customConnectionLineProps">
:custom-connection-line="props.customConnectionLine" <slot name="custom-connection-line" v-bind="customConnectionLineProps"></slot>
:arrow-head-color="props.arrowHeadColor" </template>
:marker-end-id="props.markerEndId" </EdgeRenderer>
:edge-types="edgeTypes" </SelectionPane>
:transform="transform"
:dimensions="dimensions"
/>
</SelectionPane>
</template>
</ZoomPane> </ZoomPane>
<slot></slot> <slot></slot>
</div> </div>
+12 -13
View File
@@ -1,19 +1,14 @@
<script lang="ts" setup> <script lang="ts" setup>
import Node from '~/components/Nodes/Node.vue' import Node from '~/components/Nodes/Node.vue'
import { NodeType, Node as TNode, Transform, Dimensions } from '~/types' import { NodeType, Node as TNode } from '~/types'
import { getNodesInside } from '~/utils/graph' import { getNodesInside } from '~/utils/graph'
import { Store } from '~/context' import { Store } from '~/context'
interface NodeRendererProps { interface NodeRendererProps {
nodeTypes: Record<string, NodeType> nodeTypes: Record<string, NodeType>
dimensions: Dimensions
transform: Transform
} }
const props = withDefaults(defineProps<NodeRendererProps>(), { const props = defineProps<NodeRendererProps>()
transform: () => [0, 0, 1],
dimensions: () => ({ width: 0, height: 0 }),
})
const store = inject(Store)! const store = inject(Store)!
@@ -25,10 +20,10 @@ const getNodes = computed(() =>
{ {
x: 0, x: 0,
y: 0, y: 0,
width: props.dimensions.width, width: store.dimensions.width,
height: props.dimensions.height, height: store.dimensions.height,
}, },
props.transform, store.transform,
true, true,
) )
: store.nodes, : store.nodes,
@@ -48,20 +43,24 @@ const selected = (nodeId: string) => store.selectedElements?.some(({ id }) => id
<template> <template>
<div <div
class="revue-flow__nodes" class="revue-flow__nodes"
:style="{ transform: `translate(${props.transform[0]}px,${props.transform[1]}px) scale(${props.transform[2]})` }" :style="{ transform: `translate(${store.transform[0]}px,${store.transform[1]}px) scale(${store.transform[2]})` }"
> >
<template v-for="node of getNodes" :key="node.id"> <template v-for="node of getNodes" :key="node.id">
<Node <Node
v-if="!node.isHidden" v-if="!node.isHidden"
:node="node" :node="node"
:type="type(node)" :type="type(node)"
:scale="props.transform[2]" :scale="store.transform[2]"
:snap-grid="store.snapToGrid ? store.snapGrid : undefined" :snap-grid="store.snapToGrid ? store.snapGrid : undefined"
:selected="selected(node.id)" :selected="selected(node.id)"
:selectable="node.selectable || store.elementsSelectable" :selectable="node.selectable || store.elementsSelectable"
:connectable="node.connectable || store.nodesConnectable" :connectable="node.connectable || store.nodesConnectable"
:draggable="node.draggable || store.nodesDraggable" :draggable="node.draggable || store.nodesDraggable"
/> >
<template #default="nodeProps">
<slot v-bind="nodeProps"></slot>
</template>
</Node>
</template> </template>
</div> </div>
</template> </template>
+20 -11
View File
@@ -1,10 +1,10 @@
<script lang="ts" setup> <script lang="ts" setup>
import useKeyPress from '~/composables/useKeyPress' import useKeyPress from '~/composables/useKeyPress'
import { KeyCode } from '~/types' import { ElementId, FlowElement, KeyCode } from '~/types'
import useGlobalKeyHandler from '~/composables/useGlobalKeyHandler'
import NodesSelection from '~/components/NodesSelection/NodesSelection.vue' import NodesSelection from '~/components/NodesSelection/NodesSelection.vue'
import UserSelection from '~/components/UserSelection/UserSelection.vue' import UserSelection from '~/components/UserSelection/UserSelection.vue'
import { Hooks, Store } from '~/context' import { Hooks, Store } from '~/context'
import { getConnectedEdges, isNode } from '~/utils/graph'
interface SelectionPaneProps { interface SelectionPaneProps {
selectionKeyCode?: KeyCode selectionKeyCode?: KeyCode
@@ -18,7 +18,6 @@ const props = withDefaults(defineProps<SelectionPaneProps>(), {
}) })
const store = inject(Store)! const store = inject(Store)!
const hooks = inject(Hooks)! const hooks = inject(Hooks)!
const keyPressed = useKeyPress(props.selectionKeyCode)
const onClick = (event: MouseEvent) => { const onClick = (event: MouseEvent) => {
hooks.paneClick.trigger(event) hooks.paneClick.trigger(event)
@@ -30,18 +29,28 @@ const onContextMenu = (event: MouseEvent) => hooks.paneContextMenu.trigger(event
const onWheel = (event: WheelEvent) => hooks.paneScroll.trigger(event) const onWheel = (event: WheelEvent) => hooks.paneScroll.trigger(event)
useGlobalKeyHandler({ const selectionKeyPresed = useKeyPress(props.selectionKeyCode)
onElementsRemove: hooks.elementsRemove.trigger,
deleteKeyCode: props.deleteKeyCode, useKeyPress(props.deleteKeyCode, (keyPressed) => {
multiSelectionKeyCode: props.multiSelectionKeyCode, 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<ElementId, FlowElement>(),
)
hooks.elementsRemove.trigger(Array.from(elementsToRemove.values()))
store.unsetNodesSelection()
store.resetSelectedElements()
}
}) })
const userSelectionVisible = computed(() => keyPressed.value && (store.selectionActive || store.elementsSelectable)) useKeyPress(props.multiSelectionKeyCode, (keyPressed) => (store.multiSelectionActive = keyPressed))
const nodesSelectionVisible = computed(() => store.nodesSelectionActive)
</script> </script>
<template> <template>
<slot></slot> <slot></slot>
<UserSelection v-if="userSelectionVisible" id="user-selection" /> <UserSelection v-if="selectionKeyPresed && (store.selectionActive || store.elementsSelectable)" id="user-selection" />
<NodesSelection v-if="nodesSelectionVisible" id="nodes-selection" /> <NodesSelection v-if="store.nodesSelectionActive" id="nodes-selection" />
<div class="revue-flow__pane" @click="onClick" @contextmenu="onContextMenu" @wheel="onWheel" /> <div class="revue-flow__pane" @click="onClick" @contextmenu="onContextMenu" @wheel="onWheel" />
</template> </template>
+5 -4
View File
@@ -1,6 +1,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import { KeyCode, PanOnScrollMode } from '~/types' import { KeyCode, PanOnScrollMode } from '~/types'
import { useZoom, useResizeHandler, useZoomPanHelper } from '~/composables' import { useZoom, useZoomPanHelper } from '~/composables'
import { Hooks, Store } from '~/context' import { Hooks, Store } from '~/context'
import { onLoadGetElements, onLoadProject, onLoadToObject } from '~/utils/graph' import { onLoadGetElements, onLoadProject, onLoadToObject } from '~/utils/graph'
@@ -36,9 +36,10 @@ const hooks = inject(Hooks)!
const zoomPaneEl = templateRef<HTMLDivElement>('zoom-pane', null) const zoomPaneEl = templateRef<HTMLDivElement>('zoom-pane', null)
const { transform } = useZoom(zoomPaneEl, props) const { transform } = useZoom(zoomPaneEl, props)
const { width, height } = useElementBounding(zoomPaneEl)
watch(width, (val) => (store.dimensions.width = val), { flush: 'post' })
watch(height, (val) => (store.dimensions.height = val), { flush: 'post' })
watch(transform, (val) => (store.transform = val), { flush: 'post' }) watch(transform, (val) => (store.transform = val), { flush: 'post' })
const dimensions = useResizeHandler(zoomPaneEl)
watch(dimensions, (val) => (store.dimensions = val), { flush: 'post' })
const { zoomIn, zoomOut, zoomTo, transform: setTransform, fitView } = useZoomPanHelper() const { zoomIn, zoomOut, zoomTo, transform: setTransform, fitView } = useZoomPanHelper()
hooks.load.trigger({ hooks.load.trigger({
@@ -54,6 +55,6 @@ hooks.load.trigger({
</script> </script>
<template> <template>
<div ref="zoom-pane" class="revue-flow__renderer revue-flow__zoompane"> <div ref="zoom-pane" class="revue-flow__renderer revue-flow__zoompane">
<slot :transform="transform" :dimensions="dimensions"></slot> <slot v-bind="{ transform, dimensions: { width, height } }"></slot>
</div> </div>
</template> </template>
@@ -1,6 +1,6 @@
import { setActivePinia, createPinia, defineStore, StoreDefinition } from 'pinia' import { setActivePinia, createPinia, defineStore, StoreDefinition } from 'pinia'
import isEqual from 'fast-deep-equal' import isEqual from 'fast-deep-equal'
import { Edge, Node, NodeDiffUpdate, RevueFlowState, RevueFlowActions, XYPosition } from '~/types' import { Edge, FlowState, Node, NodeDiffUpdate, RevueFlowActions, XYPosition } from '~/types'
import { clampPosition, getDimensions } from '~/utils' import { clampPosition, getDimensions } from '~/utils'
import { getConnectedEdges, getNodesInside, getRectOfNodes, isEdge, isNode, parseEdge, parseNode } from '~/utils/graph' import { getConnectedEdges, getNodesInside, getRectOfNodes, isEdge, isNode, parseEdge, parseNode } from '~/utils/graph'
import { getHandleBounds } from '~/components/Nodes/utils' import { getHandleBounds } from '~/components/Nodes/utils'
@@ -11,9 +11,7 @@ type NextElements = {
} }
const pinia = createPinia() const pinia = createPinia()
export default function configureStore( export default function useFlowStore(preloadedState: FlowState): StoreDefinition<string, FlowState, any, RevueFlowActions> {
preloadedState: RevueFlowState,
): StoreDefinition<string, RevueFlowState, any, RevueFlowActions> {
setActivePinia(pinia) setActivePinia(pinia)
return defineStore({ return defineStore({
@@ -166,7 +164,7 @@ export default function configureStore(
const startX = this.userSelectionRect.startX || 0 const startX = this.userSelectionRect.startX || 0
const startY = this.userSelectionRect.startY || 0 const startY = this.userSelectionRect.startY || 0
const nextUserSelectRect: RevueFlowState['userSelectionRect'] = { const nextUserSelectRect: FlowState['userSelectionRect'] = {
...this.userSelectionRect, ...this.userSelectionRect,
x: mousePos.x < startX ? mousePos.x : this.userSelectionRect.x, x: mousePos.x < startX ? mousePos.x : this.userSelectionRect.x,
y: mousePos.y < startY ? mousePos.y : this.userSelectionRect.y, y: mousePos.y < startY ? mousePos.y : this.userSelectionRect.y,
+28
View File
@@ -0,0 +1,28 @@
import { Ref } from 'vue'
import { ElementId, Transform } from './types'
import { D3Zoom, D3Selection, PanOnScrollMode, KeyCode } from './panel'
import { Connection } from '@/src'
export type UpdateNodeInternals = (nodeId: ElementId) => void
export interface UseZoomOptions {
selectionKeyCode?: KeyCode
zoomActivationKeyCode?: KeyCode
paneMoveable?: boolean
defaultZoom?: number
defaultPosition?: [number, number]
zoomOnScroll?: boolean
zoomOnPinch?: boolean
panOnScroll?: boolean
panOnScrollSpeed?: number
panOnScrollMode?: PanOnScrollMode
zoomOnDoubleClick?: boolean
}
export interface UseZoom {
transform: Ref<Transform>
d3Zoom: Ref<D3Zoom>
d3Selection: Ref<D3Selection>
}
export type ValidConnectionFunc = (connection: Connection) => boolean
-3
View File
@@ -1,4 +1,3 @@
import { DefineComponent } from 'vue'
import { ElementId, Position } from './types' import { ElementId, Position } from './types'
import { HandleType } from '~/types/handle' import { HandleType } from '~/types/handle'
@@ -27,8 +26,6 @@ export type ConnectionLineComponentProps = {
connectionLineType: ConnectionLineType connectionLineType: ConnectionLineType
} }
export type CustomConnectionLine = DefineComponent<ConnectionLineComponentProps | any, any, any, any>
export type OnConnectFunc = (connection: Connection) => void export type OnConnectFunc = (connection: Connection) => void
export type OnConnectStartParams = { export type OnConnectStartParams = {
nodeId: ElementId | undefined nodeId: ElementId | undefined
+1
View File
@@ -7,3 +7,4 @@ export * from './node'
export * from './panel' export * from './panel'
export * from './store' export * from './store'
export * from './hooks' export * from './hooks'
export * from './composables'
+1 -2
View File
@@ -1,7 +1,7 @@
import { CSSProperties, HTMLAttributes } from 'vue' import { CSSProperties, HTMLAttributes } from 'vue'
import { Edge, EdgeType } from './edge' import { Edge, EdgeType } from './edge'
import { Node, NodeExtent, NodeType, TranslateExtent } from './node' import { Node, NodeExtent, NodeType, TranslateExtent } from './node'
import { CustomConnectionLine, ConnectionLineType, ConnectionMode } from '~/types/connection' import { ConnectionLineType, ConnectionMode } from '~/types/connection'
import { KeyCode, PanOnScrollMode } from '~/types/panel' import { KeyCode, PanOnScrollMode } from '~/types/panel'
export type ElementId = string export type ElementId = string
@@ -96,7 +96,6 @@ export interface FlowOptions extends Omit<HTMLAttributes, 'onLoad'> {
connectionMode?: ConnectionMode connectionMode?: ConnectionMode
connectionLineType?: ConnectionLineType connectionLineType?: ConnectionLineType
connectionLineStyle?: CSSProperties connectionLineStyle?: CSSProperties
connectionLineComponent?: CustomConnectionLine
deleteKeyCode?: KeyCode deleteKeyCode?: KeyCode
selectionKeyCode?: KeyCode selectionKeyCode?: KeyCode
multiSelectionKeyCode?: KeyCode multiSelectionKeyCode?: KeyCode
+1 -2
View File
@@ -32,7 +32,6 @@
"exclude": [ "exclude": [
"node_modules", "node_modules",
"build", "build",
"dist", "dist"
"examples"
] ]
} }