feat(node-resizer): implement node resizer components
Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com>
This commit is contained in:
@@ -1,91 +1,57 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed, inject } from 'vue'
|
||||
import type { GraphNode, Rect, ViewpaneTransform } from '@vue-flow/core'
|
||||
import { NodeIdInjection, Position, getRectOfNodes, useVueFlow } from '@vue-flow/core'
|
||||
import ResizeControl from './ResizeControl.vue'
|
||||
import type { ControlLinePosition, ControlPosition, NodeResizerProps, ResizeDragEvent, ResizeEventParams } from './types'
|
||||
import { ResizeControlVariant } from './types'
|
||||
|
||||
import type { CSSProperties } from 'vue'
|
||||
import type { NodeToolbarProps } from './types'
|
||||
|
||||
const props = withDefaults(defineProps<NodeToolbarProps>(), {
|
||||
position: Position.Top,
|
||||
offset: 10,
|
||||
const props = withDefaults(defineProps<NodeResizerProps>(), {
|
||||
isVisible: true,
|
||||
})
|
||||
|
||||
const contextNodeId = inject(NodeIdInjection, null)
|
||||
const emits = defineEmits<{
|
||||
(event: 'resizeStart', data: { event: ResizeDragEvent; params: ResizeEventParams }): void
|
||||
(event: 'resize', data: { event: ResizeDragEvent; params: ResizeEventParams }): void
|
||||
(event: 'resizeEnd', data: { event: ResizeDragEvent; params: ResizeEventParams }): void
|
||||
}>()
|
||||
|
||||
const { viewportRef, viewport, getSelectedNodes, findNode } = useVueFlow()
|
||||
|
||||
function getTransform(nodeRect: Rect, transform: ViewpaneTransform, position: Position, offset: number): string {
|
||||
// position === Position.Top
|
||||
let xPos = (nodeRect.x + nodeRect.width / 2) * transform.zoom + transform.x
|
||||
let yPos = nodeRect.y * transform.zoom + transform.y - offset
|
||||
let xShift = -50
|
||||
let yShift = -100
|
||||
|
||||
switch (position) {
|
||||
case Position.Right:
|
||||
xPos = (nodeRect.x + nodeRect.width) * transform.zoom + transform.x + offset
|
||||
yPos = (nodeRect.y + nodeRect.height / 2) * transform.zoom + transform.y
|
||||
xShift = 0
|
||||
yShift = -50
|
||||
break
|
||||
case Position.Bottom:
|
||||
yPos = (nodeRect.y + nodeRect.height) * transform.zoom + transform.y + offset
|
||||
yShift = 0
|
||||
break
|
||||
case Position.Left:
|
||||
xPos = nodeRect.x * transform.zoom + transform.x - offset
|
||||
yPos = (nodeRect.y + nodeRect.height / 2) * transform.zoom + transform.y
|
||||
xShift = -100
|
||||
yShift = -50
|
||||
break
|
||||
}
|
||||
|
||||
return `translate(${xPos}px, ${yPos}px) translate(${xShift}%, ${yShift}%)`
|
||||
}
|
||||
|
||||
const nodes = computed(() => {
|
||||
const nodeIds = Array.isArray(props.nodeId) ? props.nodeId : [props.nodeId || contextNodeId || '']
|
||||
|
||||
return nodeIds.reduce<GraphNode[]>((acc, id) => {
|
||||
const node = findNode(id)
|
||||
|
||||
if (node) {
|
||||
acc.push(node)
|
||||
}
|
||||
|
||||
return acc
|
||||
}, [] as GraphNode[])
|
||||
})
|
||||
|
||||
const isActive = computed(() =>
|
||||
typeof props.isVisible === 'boolean'
|
||||
? props.isVisible
|
||||
: nodes.value.length === 1 && nodes.value[0].selected && getSelectedNodes.value.length === 1,
|
||||
)
|
||||
|
||||
const nodeRect = computed<Rect>(() => getRectOfNodes(nodes.value))
|
||||
|
||||
const zIndex = computed<number>(() => Math.max(...nodes.value.map((node) => (node.computedPosition.z || 1) + 1)))
|
||||
|
||||
const wrapperStyle = computed<CSSProperties>(() => ({
|
||||
position: 'absolute',
|
||||
transform: getTransform(nodeRect.value, viewport.value, props.position, props.offset),
|
||||
zIndex: zIndex.value,
|
||||
}))
|
||||
const handleControls: ControlPosition[] = ['top-left', 'top-right', 'bottom-left', 'bottom-right']
|
||||
const lineControls: ControlLinePosition[] = ['top', 'right', 'bottom', 'left']
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'NodeToolbar',
|
||||
name: 'NodeResizer',
|
||||
inheritAttrs: false,
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Teleport :to="viewportRef">
|
||||
<div v-if="isActive && nodes.length" v-bind="$attrs" :style="wrapperStyle" class="vue-flow__node-toolbar">
|
||||
<slot />
|
||||
</div>
|
||||
</Teleport>
|
||||
<ResizeControl
|
||||
v-for="c of lineControls"
|
||||
:key="c"
|
||||
:class="lineClassName"
|
||||
:style="lineStyle"
|
||||
:node-id="nodeId"
|
||||
:position="c"
|
||||
:variant="ResizeControlVariant.Line"
|
||||
:color="color"
|
||||
:min-width="minWidth"
|
||||
:min-height="minHeight"
|
||||
@resize-start="emits('resizeStart', $event)"
|
||||
@resize="emits('resize', $event)"
|
||||
@resize-end="emits('resizeEnd', $event)"
|
||||
/>
|
||||
<ResizeControl
|
||||
v-for="c of handleControls"
|
||||
:key="c"
|
||||
:class="lineClassName"
|
||||
:style="lineStyle"
|
||||
:node-id="nodeId"
|
||||
:position="c"
|
||||
:color="color"
|
||||
:min-width="minWidth"
|
||||
:min-height="minHeight"
|
||||
@resize-start="emits('resizeStart', $event)"
|
||||
@resize="emits('resize', $event)"
|
||||
@resize-end="emits('resizeEnd', $event)"
|
||||
/>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user