118 lines
2.9 KiB
Vue
118 lines
2.9 KiB
Vue
<script lang="ts" setup>
|
|
import Moveable from 'vue3-moveable'
|
|
import type { Dimensions } from '@vue-flow/core'
|
|
import { Handle, useVueFlow } from '@vue-flow/core'
|
|
import { useMoveable } from './utils'
|
|
import type { ResizeRotateNodeProps } from './types'
|
|
|
|
interface Emits {
|
|
(event: 'updateNodeInternals'): void
|
|
(event: 'resize', dimensions: Dimensions): void
|
|
(event: 'rotate', rotation: number): void
|
|
}
|
|
|
|
const props = withDefaults(defineProps<ResizeRotateNodeProps>(), {
|
|
resizable: true,
|
|
rotatable: true,
|
|
moveableProps: () => ({}),
|
|
})
|
|
|
|
const emits = defineEmits<Emits>()
|
|
|
|
const { findNode, onPaneClick, onNodeClick, onNodeDragStart, snapGrid, snapToGrid } = useVueFlow()
|
|
|
|
const el = ref()
|
|
|
|
const visible = ref(false)
|
|
|
|
const { onRotateStart, onRotate, onResizeStart, onResize, onResizeEnd } = useMoveable(props.id, emits)
|
|
|
|
const toggleVisibility = (val?: boolean) => {
|
|
visible.value = val ?? !visible.value
|
|
}
|
|
|
|
onNodeClick(({ node }) => {
|
|
toggleVisibility(node.id === props.id)
|
|
})
|
|
|
|
onNodeDragStart(({ node }) => {
|
|
toggleVisibility(node.id === props.id)
|
|
})
|
|
|
|
onPaneClick(() => toggleVisibility(false))
|
|
|
|
onBeforeMount(() => {
|
|
const node = findNode(props.id)!
|
|
|
|
node.dragHandle = `[data-moveable-id='${props.id}']`
|
|
|
|
const previousStyle = node.style
|
|
node.style = () => {
|
|
return {
|
|
...previousStyle,
|
|
pointerEvents: 'none',
|
|
cursor: 'default',
|
|
}
|
|
}
|
|
})
|
|
</script>
|
|
|
|
<script lang="ts">
|
|
export default {
|
|
inheritAttrs: false,
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div
|
|
ref="el"
|
|
style="cursor: grab; pointer-events: all"
|
|
:data-moveable-id="props.id"
|
|
:style="{
|
|
...data.style,
|
|
cursor: visible ? 'grab' : 'auto',
|
|
zIndex: visible ? 1000 : 0,
|
|
width: data.dimensions?.width ? `${data.dimensions.width}px` : undefined,
|
|
height: data.dimensions?.height ? `${data.dimensions.height}px` : undefined,
|
|
transform: `translate(${data.translate[0]}px, ${data.translate[1]}px)` + ` rotate(${data.rotate}deg)`,
|
|
}"
|
|
>
|
|
<Handle type="target" :position="props.targetPosition" />
|
|
|
|
<slot>
|
|
{{ props.label }}
|
|
</slot>
|
|
|
|
<Handle type="source" :position="props.sourcePosition" />
|
|
</div>
|
|
|
|
<Moveable
|
|
v-bind="props.moveableProps"
|
|
class-name="nodrag"
|
|
style="pointer-events: all"
|
|
:target="visible ? el : undefined"
|
|
:resizable="resizable"
|
|
:rotatable="rotatable"
|
|
:snappable="snapToGrid"
|
|
:snap-element="snapToGrid"
|
|
:snap-gap="snapToGrid"
|
|
:snap-directions="snapToGrid"
|
|
:snap-digit="snapGrid[0]"
|
|
:snap-treshold="snapGrid[0]"
|
|
:snap-grid-width="snapGrid[0]"
|
|
:snap-grid-height="snapGrid[1]"
|
|
:padding="{ left: 0, top: 0, right: 0, bottom: 0 }"
|
|
:origin="false"
|
|
:start-drag-rotate="0"
|
|
:throttle-drag-rotate="0"
|
|
:throttle-resize="1"
|
|
:render-directions="['nw', 'n', 'ne', 'w', 'e', 'sw', 's', 'se']"
|
|
:zoom="1"
|
|
@resize-start="onResizeStart"
|
|
@resize="onResize"
|
|
@resize-end="onResizeEnd"
|
|
@rotate-start="onRotateStart"
|
|
@rotate="onRotate"
|
|
/>
|
|
</template>
|