update(script-setup): Refactor additional-components

* Remove jsx files

fix: ConnectionLine.vue not recalculating properly
This commit is contained in:
Braks
2021-10-20 22:39:54 +02:00
parent 560bdc203b
commit 1ff3a8dc9c
26 changed files with 359 additions and 1269 deletions
@@ -1,6 +1,6 @@
<script lang="ts" setup>
import { CSSProperties } from 'vue'
import { ConnectionLineType, CustomConnectionLine, HandleElement, Position, RevueFlowStore, Node } from '~/types'
import { ConnectionLineType, CustomConnectionLine, HandleElement, Node, Position, RevueFlowStore } from '~/types'
import { RevueFlowHooks } from '~/hooks/RevueFlowHooks'
import { getBezierPath, getSmoothStepPath } from '~/components/Edges/utils'
@@ -16,76 +16,67 @@ const props = withDefaults(defineProps<ConnectionLineProps>(), {
connectionLineStyle: () => ({}),
})
const store = inject<RevueFlowStore>('store')!
const hooks = inject<RevueFlowHooks>('hooks')!
const store = inject<RevueFlowStore>('store')!
const sourceHandle = computed(() =>
const sourceHandle =
store.connectionHandleId && store.connectionHandleType
? props.sourceNode.__rf.handleBounds[store.connectionHandleType].find((d: HandleElement) => d.id === store.connectionHandleId)
: store.connectionHandleType && props.sourceNode.__rf.handleBounds[store.connectionHandleType][0],
)
const sourceHandleX = computed(() =>
sourceHandle.value ? sourceHandle.value.x + sourceHandle.value.width / 2 : (props.sourceNode.__rf.width as number) / 2,
)
const sourceHandleY = computed(() =>
sourceHandle.value ? sourceHandle.value.y + sourceHandle.value.height / 2 : props.sourceNode.__rf.height,
)
const sourceX = computed(() => props.sourceNode.__rf.position.x + sourceHandleX.value)
const sourceY = computed(() => props.sourceNode.__rf.position.y + sourceHandleY.value)
: store.connectionHandleType && props.sourceNode.__rf.handleBounds[store.connectionHandleType][0]
const sourceHandleX = sourceHandle ? sourceHandle.x + sourceHandle.width / 2 : (props.sourceNode.__rf.width as number) / 2
const sourceHandleY = sourceHandle ? sourceHandle.y + sourceHandle.height / 2 : props.sourceNode.__rf.height
const sourceX = props.sourceNode.__rf.position.x + sourceHandleX
const sourceY = props.sourceNode.__rf.position.y + sourceHandleY
const targetX = computed(() => (store.connectionPosition?.x - store.transform[0]) / store.transform[2])
const targetY = computed(() => (store.connectionPosition?.y - store.transform[1]) / store.transform[2])
const isRightOrLeft = sourceHandle.position === Position.Left || sourceHandle.position === Position.Right
const targetPosition = isRightOrLeft ? Position.Left : Position.Top
const isRightOrLeft = computed(
() => sourceHandle.value?.position === Position.Left || sourceHandle.value?.position === Position.Right,
)
const targetPosition = computed(() => (isRightOrLeft.value ? Position.Left : Position.Top))
const targetX = computed(() => (store.connectionPosition.x - store.transform[0]) / store.transform[2])
const targetY = computed(() => (store.connectionPosition.y - store.transform[1]) / store.transform[2])
let dAttr = computed(() => `M${sourceX.value},${sourceY.value} ${targetX.value},${targetY.value}`)
if (props.connectionLineType === ConnectionLineType.Bezier) {
dAttr = computed(() =>
getBezierPath({
sourceX: sourceX.value,
sourceY: sourceY.value,
sourcePosition: sourceHandle.value?.position,
targetX: targetX.value,
targetY: targetY.value,
targetPosition: targetPosition.value,
}),
)
} else if (props.connectionLineType === ConnectionLineType.Step) {
dAttr = computed(() =>
getSmoothStepPath({
sourceX: sourceX.value,
sourceY: sourceY.value,
sourcePosition: sourceHandle.value?.position,
targetX: targetX.value,
targetY: targetY.value,
targetPosition: targetPosition.value,
borderRadius: 0,
}),
)
} else if (props.connectionLineType === ConnectionLineType.SmoothStep) {
dAttr = computed(() =>
getSmoothStepPath({
sourceX: sourceX.value,
sourceY: sourceY.value,
sourcePosition: sourceHandle.value?.position,
targetX: targetX.value,
targetY: targetY.value,
targetPosition: targetPosition.value,
}),
)
}
watch(dAttr, () => console.log(dAttr.value))
const dAttr = computed(() => {
let path = `M${sourceX.value},${sourceY.value} ${targetX.value},${targetY.value}`
switch (props.connectionLineType) {
case ConnectionLineType.Bezier:
path = getBezierPath({
sourceX,
sourceY,
sourcePosition: sourceHandle.position,
targetX: targetX.value,
targetY: targetY.value,
targetPosition,
})
break
case ConnectionLineType.Step:
path = getSmoothStepPath({
sourceX,
sourceY,
sourcePosition: sourceHandle.position,
targetX: targetX.value,
targetY: targetY.value,
targetPosition,
borderRadius: 0,
})
break
case ConnectionLineType.SmoothStep:
path = getSmoothStepPath({
sourceX,
sourceY,
sourcePosition: sourceHandle.position,
targetX: targetX.value,
targetY: targetY.value,
targetPosition,
})
break
}
return path
})
</script>
<template>
<g>
<g class="revue-flow__connection">
<component
:is="props.customConnectionLine"
v-if="props.customConnectionLine"
class="revue-flow__connection"
v-bind="{
sourceX: sourceX,
sourceY: sourceY,
-125
View File
@@ -1,125 +0,0 @@
import { ref, defineComponent, CSSProperties, PropType, computed, inject, h } from 'vue'
import { getBezierPath } from '../Edges/BezierEdgeDepr'
import { getSmoothStepPath } from '../Edges/SmoothStepEdgeDepr'
import { Node, HandleElement, Position, ConnectionLineType, ConnectionLineComponent, RevueFlowStore } from '../../types'
interface ConnectionLineProps {
connectionLineType: ConnectionLineType
connectionLineStyle?: CSSProperties
customConnectionLine?: ConnectionLineComponent
}
export default defineComponent({
props: {
connectionLineStyle: {
type: Object as PropType<ConnectionLineProps['connectionLineStyle']>,
required: false,
default: () => {},
},
connectionLineType: {
type: String as PropType<ConnectionLineProps['connectionLineType']>,
required: false,
default: ConnectionLineType.Bezier,
},
customConnectionLine: {
type: Object as PropType<ConnectionLineProps['customConnectionLine']>,
required: false,
default: undefined,
},
},
setup(props) {
const store = inject<RevueFlowStore>('store')!
const sourceNode = ref<Node | null>(store.nodes.find((n) => n.id === store.connectionNodeId) || null)
const nodesConnectable = computed(() => store.nodesConnectable)
const sourceHandle = computed(() =>
store.connectionHandleId && store.connectionHandleType
? sourceNode.value?.__rf.handleBounds[store.connectionHandleType].find(
(d: HandleElement) => d.id === store.connectionHandleId,
)
: store.connectionHandleType && sourceNode.value?.__rf.handleBounds[store.connectionHandleType][0],
)
const sourceHandleX = computed(() =>
sourceHandle.value ? sourceHandle.value.x + sourceHandle.value.width / 2 : (sourceNode.value?.__rf.width as number) / 2,
)
const sourceHandleY = computed(() =>
sourceHandle.value ? sourceHandle.value.y + sourceHandle.value.height / 2 : sourceNode.value?.__rf.height,
)
const sourceX = computed(() => sourceNode.value?.__rf.position.x + sourceHandleX.value)
const sourceY = computed(() => sourceNode.value?.__rf.position.y + sourceHandleY.value)
const targetX = computed(() => (store.connectionPosition.x - store.transform[0]) / store.transform[2])
const targetY = computed(() => (store.connectionPosition.y - store.transform[1]) / store.transform[2])
const isRightOrLeft = computed(
() => sourceHandle.value?.position === Position.Left || sourceHandle.value?.position === Position.Right,
)
const targetPosition = computed(() => (isRightOrLeft.value ? Position.Left : Position.Top))
let dAttr = computed(() => `M${sourceX.value},${sourceY.value} ${targetX.value},${targetY.value}`)
if (props.connectionLineType === ConnectionLineType.Bezier) {
dAttr = computed(() =>
getBezierPath({
sourceX: sourceX.value,
sourceY: sourceY.value,
sourcePosition: sourceHandle.value?.position,
targetX: targetX.value,
targetY: targetY.value,
targetPosition: targetPosition.value,
}),
)
} else if (props.connectionLineType === ConnectionLineType.Step) {
dAttr = computed(() =>
getSmoothStepPath({
sourceX: sourceX.value,
sourceY: sourceY.value,
sourcePosition: sourceHandle.value?.position,
targetX: targetX.value,
targetY: targetY.value,
targetPosition: targetPosition.value,
borderRadius: 0,
}),
)
} else if (props.connectionLineType === ConnectionLineType.SmoothStep) {
dAttr = computed(() =>
getSmoothStepPath({
sourceX: sourceX.value,
sourceY: sourceY.value,
sourcePosition: sourceHandle.value?.position,
targetX: targetX.value,
targetY: targetY.value,
targetPosition: targetPosition.value,
}),
)
}
if (props.customConnectionLine) {
return () => (
<g className="revue-flow__connection">
{props.customConnectionLine &&
h(props.customConnectionLine, {
sourceX: sourceX.value,
sourceY: sourceY.value,
sourcePosition: sourceHandle.value?.position,
targetX: targetX.value,
targetY: targetY.value,
targetPosition: targetPosition.value,
connectionLineType: props.connectionLineType,
connectionLineStyle: props.connectionLineStyle,
})}
</g>
)
}
return () =>
nodesConnectable.value && sourceNode.value ? (
<g className="revue-flow__connection">
<path d={dAttr.value} className="revue-flow__connection-path" style={props.connectionLineStyle} />
</g>
) : (
''
)
},
})
+2 -3
View File
@@ -4,7 +4,7 @@ import { RevueFlowHooks } from '~/hooks/RevueFlowHooks'
import { getEdgePositions, getHandle, getSourceTargetNodes, isEdgeVisible } from '~/container/EdgeRenderer/utils'
import { isEdge } from '~/utils/graph'
import { ConnectionMode, Dimensions, Edge, EdgeType, Elements, Position, RevueFlowStore, Transform } from '~/types'
import { onMouseDown } from '~/components/Handle/handler'
import { onMouseDown } from '~/components/Handle/utils'
interface EdgeProps {
type: EdgeType
@@ -12,7 +12,7 @@ interface EdgeProps {
nodes: ReturnType<typeof getSourceTargetNodes>
dimensions: Dimensions
transform: Transform
selectedElements?: Elements | null
selectedElements?: Elements
elementsSelectable?: boolean
onlyRenderVisibleElements?: boolean
connectionMode?: ConnectionMode
@@ -23,7 +23,6 @@ interface EdgeProps {
const props = withDefaults(defineProps<EdgeProps>(), {
elementsSelectable: true,
onlyRenderVisibleElements: false,
selectedElements: () => null,
})
const store = inject<RevueFlowStore>('store')!
+14 -4
View File
@@ -1,6 +1,6 @@
<script lang="ts" setup>
import { ConnectionMode, ElementId, Position, RevueFlowStore } from '~/types'
import { onMouseDown, ValidConnectionFunc } from '~/components/Handle/handler'
import { ElementId, Position, RevueFlowStore } from '~/types'
import { onMouseDown, ValidConnectionFunc } from '~/components/Handle/utils'
import { RevueFlowHooks } from '~/hooks/RevueFlowHooks'
interface HandleProps {
@@ -15,7 +15,6 @@ const props = withDefaults(defineProps<HandleProps>(), {
id: '',
type: 'source',
position: Position.Top,
isValidConnection: () => true,
connectable: true,
})
@@ -24,7 +23,18 @@ const hooks = inject<RevueFlowHooks>('hooks')!
const nodeId = inject<ElementId>('NodeIdContext')!
const onMouseDownHandler = (event: MouseEvent) =>
onMouseDown(event, store, hooks, props.id, nodeId, props.type === 'target', props.isValidConnection)
onMouseDown(
event,
store,
hooks,
props.id,
nodeId,
props.type === 'target',
props.isValidConnection ??
function () {
return true
},
)
</script>
<template>
<div
-65
View File
@@ -1,65 +0,0 @@
import { ElementId, Position, RevueFlowStore } from '../../types'
import { onMouseDown, ValidConnectionFunc } from './handler'
import { defineComponent, inject, PropType } from 'vue'
import { RevueFlowHooks } from '../../hooks/RevueFlowHooks'
const alwaysValid = () => true
export default defineComponent({
props: {
type: {
type: String,
required: false,
default: 'source'
},
position: {
type: String as PropType<Position>,
required: false,
default: Position.Top
},
isValidConnection: {
type: Function as PropType<ValidConnectionFunc>,
required: false,
default: alwaysValid
},
isConnectable: {
type: Boolean,
required: false,
default: true
},
id: {
type: String,
required: false,
default: undefined
}
},
setup(props, { slots }) {
const store = inject<RevueFlowStore>('store')!
const hooks = inject<RevueFlowHooks>('hooks')!
const nodeId = inject<ElementId>('NodeIdContext')!
const onMouseDownHandler = (event: MouseEvent) =>
onMouseDown(event, store, hooks, props.id as string, nodeId, props.type === 'target', props.isValidConnection)
return () => (
<div
data-handleid={props.id}
data-nodeid={nodeId}
data-handlepos={props.position}
class={[
'revue-flow__handle',
`revue-flow__handle-${props.position}`,
'nodrag',
{
source: props.type !== 'target',
target: props.type === 'target',
connectable: props.isConnectable
}
]}
onMousedown={onMouseDownHandler}
>
{slots.default ? slots.default() : ''}
</div>
)
}
})
@@ -1,5 +1,5 @@
import { getHostForElement } from '~/utils'
import { ElementId, ConnectionMode, Connection, HandleType, XYPosition, RevueFlowStore } from '~/types'
import { ElementId, ConnectionMode, Connection, HandleType, RevueFlowStore } from '~/types'
import { RevueFlowHooks } from '~/hooks/RevueFlowHooks'
export type ValidConnectionFunc = (connection: Connection) => boolean
@@ -82,7 +82,9 @@ export function onMouseDown(
handleId: ElementId,
nodeId: ElementId,
isTarget: boolean,
isValidConnection: ValidConnectionFunc = () => true,
isValidConnection: ValidConnectionFunc = () => {
return true
},
elementEdgeUpdaterType?: HandleType,
): void {
const revueFlowNode = (event.target as Element).closest('.revue-flow')
@@ -105,12 +107,6 @@ export function onMouseDown(
const containerBounds = revueFlowNode.getBoundingClientRect()
let recentHoveredHandle: Element
const connectionPosition = ref<XYPosition>({
x: event.clientX - containerBounds.left,
y: event.clientY - containerBounds.top,
})
if (!store.connectionPosition) store.connectionPosition = { x: 0, y: 0 }
store.connectionPosition.x = event.clientX - containerBounds.left
store.connectionPosition.y = event.clientY - containerBounds.top
@@ -122,8 +118,8 @@ export function onMouseDown(
hooks.connectStart.trigger({ event, params: { nodeId, handleId, handleType } })
function onMouseMove(event: MouseEvent) {
connectionPosition.value.x = event.clientX - containerBounds.left
connectionPosition.value.y = event.clientY - containerBounds.top
store.connectionPosition.x = event.clientX - containerBounds.left
store.connectionPosition.y = event.clientY - containerBounds.top
const { connection, elementBelow, isValid, isHoveringHandle } = checkElementBelowIsValid(
event,
@@ -173,6 +169,7 @@ export function onMouseDown(
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)
-40
View File
@@ -1,40 +0,0 @@
import { defineComponent, PropType } from 'vue'
import Handle from '../../components/Handle'
import { NodeProps, NodeType, Position } from '../../types'
export default defineComponent({
name: 'DefaultNode',
components: { Handle },
inheritAttrs: false,
props: {
data: {
type: Object as PropType<NodeProps['data']>,
required: false,
default: undefined as any
},
isConnectable: {
type: Boolean as PropType<NodeProps['isConnectable']>,
required: false,
default: undefined
},
targetPosition: {
type: String as PropType<NodeProps['targetPosition']>,
required: false,
default: Position.Top
},
sourcePosition: {
type: String as PropType<NodeProps['sourcePosition']>,
required: false,
default: Position.Bottom
}
},
setup(props) {
return () => (
<>
<Handle type="target" position={props.targetPosition} isConnectable={props.isConnectable} />
{props.data?.label}
<Handle type="source" position={props.sourcePosition} isConnectable={props.isConnectable} />
</>
)
}
}) as NodeType
-34
View File
@@ -1,34 +0,0 @@
import Handle from '../../components/Handle'
import { NodeProps, NodeType, Position } from '../../types'
import { defineComponent, PropType } from 'vue'
export default defineComponent({
name: 'InputNode',
components: { Handle },
inheritAttrs: false,
props: {
data: {
type: Object as PropType<NodeProps['data']>,
required: false,
default: undefined as any
},
isConnectable: {
type: Boolean as PropType<NodeProps['isConnectable']>,
required: false,
default: false
},
sourcePosition: {
type: String as PropType<NodeProps['sourcePosition']>,
required: false,
default: Position.Bottom
}
},
setup(props) {
return () => (
<>
{props.data?.label}
<Handle type="source" position={props.sourcePosition} isConnectable={props.isConnectable} />
</>
)
}
}) as NodeType
-236
View File
@@ -1,236 +0,0 @@
import { computed, defineComponent, h, inject, onMounted, PropType, provide } from 'vue'
import { templateRef, useResizeObserver } from '@vueuse/core'
import { DraggableCore, DraggableEventListener } from '@braks/revue-draggable'
import { Node, NodeDimensionUpdate, NodeType, RevueFlowStore } from '../../types'
import { RevueFlowHooks } from '../../hooks/RevueFlowHooks'
interface NodeProps {
nodeTypes: Record<string, NodeType>
snapToGrid: boolean
snapGrid: [number, number]
selectNodesOnDrag: boolean
}
export default defineComponent({
components: { DraggableCore },
props: {
node: {
type: Object as PropType<Node>,
required: true
},
type: {
type: Object,
required: true
},
snapToGrid: {
type: Boolean as PropType<NodeProps['snapToGrid']>,
required: false,
default: undefined
},
snapGrid: {
type: Array as unknown as PropType<NodeProps['snapGrid']>,
required: false,
default: undefined
},
selectNodesOnDrag: {
type: Boolean as PropType<NodeProps['selectNodesOnDrag']>,
required: false,
default: true
}
},
setup(props) {
const store = inject<RevueFlowStore>('store')!
const hooks = inject<RevueFlowHooks>('hooks')!
provide('NodeIdContext', props.node.id)
const nodeElement = templateRef<HTMLDivElement>('nodeElement', null)
const selected = computed(() => store.selectedElements?.some(({ id }) => id === props.node.id) || false)
const isDraggable = computed(
() => props.node.draggable || (store.nodesDraggable && typeof props.node.draggable === 'undefined')
)
const isSelectable = computed(
() => props.node.selectable || (store.elementsSelectable && typeof props.node.selectable === 'undefined')
)
const isConnectable = computed(
() => props.node.connectable || (store.nodesConnectable && typeof props.node.connectable === 'undefined')
)
const node = () =>
({
id: props.node.id,
type: props.node.type,
position: { x: props.node.__rf.position.x, y: props.node.__rf.position.y },
data: props.node.data
} as Node)
const onMouseEnterHandler = () => {
if (props.node.__rf.isDragging) {
return
}
return (event: MouseEvent) => hooks.nodeMouseEnter.trigger({ event, node: node() })
}
const onMouseMoveHandler = () => {
if (props.node.__rf.isDragging) {
return
}
return (event: MouseEvent) => hooks.nodeMouseMove.trigger({ event, node: node() })
}
const onMouseLeaveHandler = () => {
if (props.node.__rf.isDragging) {
return
}
return (event: MouseEvent) => hooks.nodeMouseLeave.trigger({ event, node: node() })
}
const onContextMenuHandler = () => {
return (event: MouseEvent) => hooks.nodeContextMenu.trigger({ event, node: node() })
}
const onSelectNodeHandler = (event: MouseEvent) => {
if (!isDraggable.value) {
const n = node()
if (isSelectable.value) {
store.unsetNodesSelection()
if (!selected.value) {
store.addSelectedElements([n])
}
}
hooks.nodeClick.trigger({ event, node: n })
}
}
const onDragStart: DraggableEventListener = ({ event }) => {
const n = node()
hooks.nodeDragStart.trigger({ event, node: n })
if (props.selectNodesOnDrag && isSelectable.value) {
store.unsetNodesSelection()
if (!selected.value) {
store.addSelectedElements([n])
}
} else if (!props.selectNodesOnDrag && !selected.value && isSelectable.value) {
store.unsetNodesSelection()
store.addSelectedElements([])
}
}
const onDrag: DraggableEventListener = ({ event, data }) => {
const n = node()
n.position.x += data.deltaX
n.position.y += data.deltaY
hooks.nodeDrag.trigger({ event, node: n })
store?.updateNodePosDiff({
id: props.node.id as string,
diff: {
x: data.deltaX,
y: data.deltaY
},
isDragging: true
})
}
const onDragStop: DraggableEventListener = ({ event }) => {
const n = node()
// onDragStop also gets called when user just clicks on a node.
// Because of that we set dragging to true inside the onDrag handler and handle the click here
if (!props.node.__rf.isDragging) {
if (isSelectable.value && !props.selectNodesOnDrag && !selected.value) {
store.addSelectedElements([n])
}
hooks.nodeClick.trigger({ event, node: n })
return
}
store.updateNodePosDiff({
id: n.id || '',
isDragging: false
})
hooks.nodeDragStop.trigger({ event, node: n })
}
useResizeObserver(nodeElement, (entries) => {
const updates: NodeDimensionUpdate[] = entries.map((entry) => ({
id: entry.target.getAttribute('data-id') || '',
nodeElement: entry.target as HTMLDivElement
}))
store.updateNodeDimensions(updates)
})
onMounted(() => {
store.updateNodeDimensions([
{
id: props.node.id || '',
nodeElement: nodeElement.value,
forceUpdate: true
}
])
})
return () =>
props.node.isHidden ? (
''
) : (
<DraggableCore
cancel=".nodrag"
scale={store.transform[2]}
disabled={!isDraggable.value}
grid={props.snapGrid}
enableUserSelectHack={false}
onStart={onDragStart}
onMove={onDrag}
onStop={onDragStop}
>
<div
ref="nodeElement"
class={[
'revue-flow__node',
`revue-flow__node-${props.node.type}`,
{
selected: selected.value,
selectable: isSelectable.value
}
]}
style={{
zIndex: selected.value ? 10 : 3,
transform: `translate(${props.node.__rf.position.x}px,${props.node.__rf.position.y}px)`,
pointerEvents: isSelectable.value || isDraggable.value ? 'all' : 'none',
opacity: props.node.__rf.width !== null && props.node.__rf.height !== null ? 1 : 0,
...props.node.style
}}
data-id={props.node.id}
onMouseenter={onMouseEnterHandler}
onMousemove={onMouseMoveHandler}
onMouseleave={onMouseLeaveHandler}
onContextmenu={onContextMenuHandler}
onClick={onSelectNodeHandler}
>
{h(props.type, {
data: props.node.data,
type: props.node.type,
xPos: props.node.__rf.position.x,
yPos: props.node.__rf.position.y,
selected: selected.value,
isConnectable: isConnectable.value,
sourcePosition: props.node.sourcePosition,
targetPosition: props.node.targetPosition,
isDragging: props.node.__rf.isDragging
})}
</div>
</DraggableCore>
)
}
})
-34
View File
@@ -1,34 +0,0 @@
import Handle from '../../components/Handle'
import { NodeProps, NodeType, Position } from '../../types'
import { defineComponent, PropType } from 'vue'
export default defineComponent({
name: 'OutputNode',
components: { Handle },
inheritAttrs: false,
props: {
data: {
type: Object as PropType<NodeProps['data']>,
required: false,
default: undefined as any
},
isConnectable: {
type: Boolean as PropType<NodeProps['isConnectable']>,
required: false,
default: false
},
targetPosition: {
type: String as PropType<NodeProps['targetPosition']>,
required: false,
default: Position.Top
}
},
setup(props) {
return () => (
<>
{props.data?.label}
<Handle type="source" position={props.targetPosition} isConnectable={props.isConnectable} />
</>
)
}
}) as NodeType
-103
View File
@@ -1,103 +0,0 @@
// @ts-nocheck
/**
* The nodes selection rectangle gets displayed when a user
* made a selectio with on or several nodes
*/
import { computed, defineComponent, inject } from 'vue'
import { Draggable, DraggableEventListener } from '@braks/revue-draggable'
import { isNode } from '../../utils/graph'
import { Node, RevueFlowStore } from '../../types'
import { RevueFlowHooks } from '../../hooks/RevueFlowHooks'
export interface NodesSelectionProps {
onSelectionDragStart?: (event: MouseEvent, nodes: Node[]) => void
onSelectionDrag?: (event: MouseEvent, nodes: Node[]) => void
onSelectionDragStop?: (event: MouseEvent, nodes: Node[]) => void
onSelectionContextMenu?: (event: MouseEvent, nodes: Node[]) => void
}
const NodesSelection = defineComponent({
setup() {
const store = inject<RevueFlowStore>('store')!
const hooks = inject<RevueFlowHooks>('hooks')!
const grid = computed(() => (store.snapToGrid ? store.snapGrid : [1, 1])! as [number, number])
const transform = computed(() => store.transform)
const selectedNodes = computed(() =>
store.selectedElements
? store.selectedElements.filter(isNode).map((selectedNode) => {
const matchingNode = store.nodes.find((node) => node.id === selectedNode.id)
return {
...matchingNode,
position: matchingNode?.__rf.position
} as Node
})
: []
)
const style = computed(() => ({
transform: `translate(${store.transform[0]}px,${store.transform[1]}px) scale(${store.transform[2]})`
}))
const innerStyle = computed(() => ({
width: `${store.selectedNodesBbox.width}px`,
height: `${store.selectedNodesBbox.height}px`,
top: `${store.selectedNodesBbox.y}px`,
left: `${store.selectedNodesBbox.x}px`
}))
const onStart: DraggableEventListener = ({ event }) => {
hooks.selectionDragStart.trigger({ event, nodes: selectedNodes.value })
}
const onDrag: DraggableEventListener = ({ event, data }) => {
hooks.selectionDrag.trigger({ event, nodes: selectedNodes.value })
store.updateNodePosDiff({
diff: {
x: data.deltaX,
y: data.deltaY
},
isDragging: true
})
}
const onStop: DraggableEventListener = ({ event }) => {
store.updateNodePosDiff({
isDragging: false
})
hooks.selectionDragStop.trigger({ event, nodes: selectedNodes.value })
}
const onContextMenu = (event: MouseEvent) => {
const selectedNodes: Node[] = store.selectedElements
? store.selectedElements.filter(isNode).map((selectedNode) => store.nodes.find((node) => node.id === selectedNode.id))
: []
hooks.selectionContextMenu.trigger({ event, nodes: selectedNodes })
}
return () => {
return !store.selectedElements || store.selectionActive ? (
''
) : (
<div class="revue-flow__nodesselection" style={style.value}>
<Draggable
onStart={onStart}
onMove={onDrag}
onStop={onStop}
scale={transform.value[2]}
grid={grid.value}
enableUserSelectHack={false}
>
<div class="revue-flow__nodesselection-rect" onContextmenu={onContextMenu} style={innerStyle.value} />
</Draggable>
</div>
)
}
}
})
export default NodesSelection
-96
View File
@@ -1,96 +0,0 @@
/**
* The user selection rectangle gets displayed when a user drags the mouse while pressing shift
*/
import { RevueFlowStore, XYPosition } from '../../types'
import { computed, defineComponent, inject } from 'vue'
import { templateRef, useEventListener } from '@vueuse/core'
function getMousePosition(event: MouseEvent): XYPosition | void {
const revueFlowNode = (event.target as Element).closest('.revue-flow')
if (!revueFlowNode) {
return
}
const containerBounds = revueFlowNode.getBoundingClientRect()
return {
x: event.clientX - containerBounds.left,
y: event.clientY - containerBounds.top
}
}
const SelectionRect = (props: { width: number; height: number; x: number; y: number }) => {
return (
<div
class="revue-flow__selection"
style={{
width: `${props.width}px`,
height: `${props.height}px`,
transform: `translate(${props.x}px, ${props.y}px)`
}}
/>
)
}
export default defineComponent({
components: { SelectionRect },
setup() {
const store = inject<RevueFlowStore>('store')!
const el = templateRef('user-selection', null)
const shouldRender = computed(() => store.selectionActive || store.elementsSelectable)
const onMouseDown = (event: MouseEvent) => {
const mousePos = getMousePosition(event)
if (!mousePos) {
return
}
store.setUserSelection(mousePos)
}
const onMouseMove = (event: MouseEvent) => {
if (!store.selectionActive) {
return
}
const mousePos = getMousePosition(event)
if (!mousePos) {
return
}
store.updateUserSelection(mousePos)
}
const onMouseUp = () => {
store.unsetUserSelection()
}
const onMouseLeave = () => {
store.unsetUserSelection()
store.unsetNodesSelection()
}
useEventListener(el, 'mousedown', onMouseDown)
useEventListener(el, 'mousemove', onMouseMove)
useEventListener(el, 'click', onMouseUp)
useEventListener(el, 'mouseup', onMouseUp)
useEventListener(el, 'mouseleave', onMouseLeave)
return () =>
shouldRender.value ? (
<div class="revue-flow__selectionpane" ref="user-selection">
{store?.userSelectionRect.draw ? (
<SelectionRect
width={store.userSelectionRect.width}
height={store.userSelectionRect.height}
x={store.userSelectionRect.x}
y={store.userSelectionRect.y}
/>
) : (
''
)}
</div>
) : (
''
)
}
})