feat(resize-rotate): save frame data of node
This commit is contained in:
@@ -1,20 +1,23 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { MoveableProps } from 'vue3-moveable'
|
|
||||||
import Moveable from 'vue3-moveable'
|
import Moveable from 'vue3-moveable'
|
||||||
import type { Position } from '@braks/vue-flow'
|
import type { Dimensions } from '@braks/vue-flow'
|
||||||
import { Handle, useVueFlow } from '@braks/vue-flow'
|
import { Handle, useVueFlow } from '@braks/vue-flow'
|
||||||
import { useMoveable } from './utils'
|
import { useMoveable } from './utils'
|
||||||
|
import type { ResizeRotateNodeProps } from './types'
|
||||||
|
|
||||||
const props = defineProps<{
|
interface Emits {
|
||||||
id: string
|
(event: 'updateNodeInternals'): void
|
||||||
label: string
|
(event: 'resize', dimensions: Dimensions): void
|
||||||
targetPosition: Position
|
(event: 'rotate', rotation: number): void
|
||||||
sourcePosition: Position
|
}
|
||||||
data: any
|
|
||||||
moveableProps?: MoveableProps
|
|
||||||
}>()
|
|
||||||
|
|
||||||
const emits = defineEmits(['updateNodeInternals'])
|
const props = withDefaults(defineProps<ResizeRotateNodeProps>(), {
|
||||||
|
resizable: true,
|
||||||
|
rotatable: true,
|
||||||
|
moveableProps: () => ({}),
|
||||||
|
})
|
||||||
|
|
||||||
|
const emits = defineEmits<Emits>()
|
||||||
|
|
||||||
const { findNode, onPaneClick, onNodeClick, onNodeDragStart, snapGrid, snapToGrid } = useVueFlow()
|
const { findNode, onPaneClick, onNodeClick, onNodeDragStart, snapGrid, snapToGrid } = useVueFlow()
|
||||||
|
|
||||||
@@ -61,7 +64,19 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div ref="el" style="cursor: grab; pointer-events: all" :data-moveable-id="props.id" :style="data.style">
|
<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" />
|
<Handle type="target" :position="props.targetPosition" />
|
||||||
|
|
||||||
<slot>
|
<slot>
|
||||||
@@ -76,8 +91,8 @@ export default {
|
|||||||
class-name="nodrag"
|
class-name="nodrag"
|
||||||
style="pointer-events: all"
|
style="pointer-events: all"
|
||||||
:target="visible ? el : undefined"
|
:target="visible ? el : undefined"
|
||||||
:resizable="true"
|
:resizable="resizable"
|
||||||
:rotatable="true"
|
:rotatable="rotatable"
|
||||||
:snappable="snapToGrid"
|
:snappable="snapToGrid"
|
||||||
:snap-element="snapToGrid"
|
:snap-element="snapToGrid"
|
||||||
:snap-gap="snapToGrid"
|
:snap-gap="snapToGrid"
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import type { Dimensions, ElementData, Position } from '@braks/vue-flow'
|
||||||
|
import type { MoveableProps } from 'vue3-moveable'
|
||||||
|
import type { CSSProperties } from 'vue'
|
||||||
|
|
||||||
|
export interface ResizeRotateNodeEmits {
|
||||||
|
(event: 'updateNodeInternals'): void
|
||||||
|
(event: 'resize', dimensions: Dimensions): void
|
||||||
|
(event: 'rotate', rotation: number): void
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ResizeRotateNodeData extends ElementData {
|
||||||
|
rotate: number
|
||||||
|
translate: number[]
|
||||||
|
dimensions: Dimensions
|
||||||
|
style: CSSProperties
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ResizeRotateNodeProps {
|
||||||
|
id: string
|
||||||
|
label: string
|
||||||
|
targetPosition: Position
|
||||||
|
sourcePosition: Position
|
||||||
|
resizable?: boolean
|
||||||
|
rotatable?: boolean
|
||||||
|
data: ResizeRotateNodeData
|
||||||
|
moveableProps?: MoveableProps
|
||||||
|
}
|
||||||
@@ -1,35 +1,57 @@
|
|||||||
import type { Able, MoveableEvents, MoveableManagerInterface } from 'vue3-moveable'
|
import type { Able, MoveableEvents, MoveableManagerInterface } from 'vue3-moveable'
|
||||||
|
import type { GraphNode } from '@braks/vue-flow'
|
||||||
|
import { useVueFlow } from '@braks/vue-flow'
|
||||||
|
import type { ResizeRotateNodeData, ResizeRotateNodeEmits } from './types'
|
||||||
|
|
||||||
export function useMoveable(id: string, emits: (event: 'updateNodeInternals') => void) {
|
export function useMoveable(id: string, emits: ResizeRotateNodeEmits) {
|
||||||
const frame = reactive({
|
const { findNode } = useVueFlow()
|
||||||
translate: [0, 0],
|
const node = findNode(id)! as GraphNode<ResizeRotateNodeData>
|
||||||
rotate: 0,
|
|
||||||
})
|
if (!node.data) {
|
||||||
|
node.data = {
|
||||||
|
translate: [0, 0],
|
||||||
|
rotate: 0,
|
||||||
|
style: {},
|
||||||
|
dimensions: { width: NaN, height: NaN },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!node.data.style) node.data.style = {}
|
||||||
|
if (!node.data.rotate) node.data.rotate = 0
|
||||||
|
if (!node.data.translate) node.data.translate = [0, 0]
|
||||||
|
if (!node.data.dimensions) node.data.dimensions = { width: NaN, height: NaN }
|
||||||
|
|
||||||
const onResizeStart = (e: MoveableEvents['resizeStart']) => {
|
const onResizeStart = (e: MoveableEvents['resizeStart']) => {
|
||||||
e.setOrigin(['%', '%'])
|
e.setOrigin(['%', '%'])
|
||||||
|
|
||||||
if (e.dragStart) {
|
if (e.dragStart) {
|
||||||
e.dragStart.set(frame.translate)
|
e.dragStart.set(node.data!.translate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const onResize = (e: MoveableEvents['resize']) => {
|
const onResize = (e: MoveableEvents['resize']) => {
|
||||||
frame.translate = e.drag.beforeTranslate
|
node.data!.translate = e.drag.beforeTranslate
|
||||||
|
node.data!.dimensions = { width: e.width, height: e.height }
|
||||||
|
|
||||||
e.target.style.width = `${e.width}px`
|
e.target.style.width = `${e.width}px`
|
||||||
e.target.style.height = `${e.height}px`
|
e.target.style.height = `${e.height}px`
|
||||||
e.target.style.transform = `translate(${frame.translate[0]}px, ${frame.translate[1]}px)` + ` rotate(${frame.rotate}deg)`
|
e.target.style.transform =
|
||||||
|
`translate(${node.data!.translate[0]}px, ${node.data!.translate[1]}px)` + ` rotate(${node.data!.rotate}deg)`
|
||||||
|
|
||||||
|
emits('resize', { width: e.width, height: e.height })
|
||||||
}
|
}
|
||||||
|
|
||||||
const onRotateStart = (e: MoveableEvents['rotateStart']) => {
|
const onRotateStart = (e: MoveableEvents['rotateStart']) => {
|
||||||
e.set(frame.rotate)
|
e.set(node.data!.rotate)
|
||||||
}
|
}
|
||||||
|
|
||||||
const onRotate = (e: MoveableEvents['rotate']) => {
|
const onRotate = (e: MoveableEvents['rotate']) => {
|
||||||
frame.rotate = e.beforeRotate
|
node.data!.rotate = e.beforeRotate
|
||||||
e.target.style.transform = `translate(${frame.translate[0]}px, ${frame.translate[1]}px)` + ` rotate(${frame.rotate}deg)`
|
|
||||||
|
e.target.style.transform =
|
||||||
|
`translate(${node.data!.translate[0]}px, ${node.data!.translate[1]}px)` + ` rotate(${node.data!.rotate}deg)`
|
||||||
|
|
||||||
emits('updateNodeInternals')
|
emits('updateNodeInternals')
|
||||||
|
emits('rotate', node.data!.rotate)
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
Reference in New Issue
Block a user