feat(resize-rotate): add snappable
This commit is contained in:
@@ -1,23 +1,58 @@
|
||||
<script lang="ts" setup>
|
||||
import type { Connection, Edge, Elements } from '@braks/vue-flow'
|
||||
import type { Connection, Edge, Elements, VueFlowStore } from '@braks/vue-flow'
|
||||
import { Background, Controls, MiniMap, VueFlow, addEdge } from '@braks/vue-flow'
|
||||
import { ref } from 'vue'
|
||||
import { ResizeRotateNode } from '../src'
|
||||
import initialElements from './elements'
|
||||
|
||||
const elements = ref<Elements>(initialElements)
|
||||
|
||||
const onConnect = (params: Edge | Connection) => (elements.value = addEdge(params, elements.value))
|
||||
|
||||
const extent = ref([
|
||||
[0, 0],
|
||||
[Infinity, Infinity],
|
||||
])
|
||||
|
||||
const onPaneReady = (store: VueFlowStore) => {
|
||||
store.fitView({ minZoom: 1, maxZoom: 1 })
|
||||
|
||||
const bounds = store.vueFlowRef.value.getBoundingClientRect()
|
||||
|
||||
extent.value = [
|
||||
[0, 0],
|
||||
[bounds.width, bounds.height],
|
||||
]
|
||||
|
||||
store.setTranslateExtent(extent.value)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div style="height: 100%">
|
||||
<VueFlow v-model="elements" class="vue-flow-basic-example" :fit-view-on-init="true" @connect="onConnect">
|
||||
<div style="height: 100%; color: black">
|
||||
{{ extent }}
|
||||
<VueFlow
|
||||
v-model="elements"
|
||||
class="vue-flow-basic-example"
|
||||
snap-to-grid
|
||||
:snap-grid="[10, 10]"
|
||||
:prevent-scrolling="false"
|
||||
:zoom-on-scroll="false"
|
||||
:pan-on-drag="false"
|
||||
:translate-extent="extent"
|
||||
:node-extent="extent"
|
||||
@connect="onConnect"
|
||||
@pane-ready="onPaneReady"
|
||||
>
|
||||
<template #node-resize-rotate="props">
|
||||
<ResizeRotateNode v-bind="props" />
|
||||
</template>
|
||||
|
||||
<Controls />
|
||||
|
||||
<MiniMap />
|
||||
<Background color="#aaa" :gap="8" />
|
||||
|
||||
<Background variant="lines" color="#aaa" size="1" :gap="10" />
|
||||
</VueFlow>
|
||||
</div>
|
||||
</template>
|
||||
@@ -29,8 +64,9 @@ const onConnect = (params: Edge | Connection) => (elements.value = addEdge(param
|
||||
html,
|
||||
body,
|
||||
#root {
|
||||
height: 100%;
|
||||
height: 1024px;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
|
||||
@@ -38,13 +38,13 @@
|
||||
"@vitejs/plugin-vue": "^2.3.3",
|
||||
"ts-patch": "^2.0.1",
|
||||
"typescript-transform-paths": "^3.3.1",
|
||||
"unplugin-auto-import": "^0.7.2",
|
||||
"unplugin-auto-import": "^0.11.2",
|
||||
"vite": "^2.9.14",
|
||||
"vite-plugin-vue-type-imports": "^0.1.3",
|
||||
"vue-tsc": "^0.35.2"
|
||||
"vite-plugin-vue-type-imports": "^0.2.0",
|
||||
"vue-tsc": "^0.40.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@braks/vue-flow": "^0.4.29",
|
||||
"@braks/vue-flow": "^0.4.37",
|
||||
"vue": "^3.2.25"
|
||||
},
|
||||
"publishConfig": {
|
||||
|
||||
+11
-8
@@ -1,6 +1,7 @@
|
||||
// Generated by 'unplugin-auto-import'
|
||||
// We suggest you to commit this file into source control
|
||||
export {}
|
||||
declare global {
|
||||
const EffectScope: typeof import('vue')['EffectScope']
|
||||
const asyncComputed: typeof import('@vueuse/core')['asyncComputed']
|
||||
const autoResetRef: typeof import('@vueuse/core')['autoResetRef']
|
||||
const computed: typeof import('vue')['computed']
|
||||
@@ -24,7 +25,6 @@ declare global {
|
||||
const defineComponent: typeof import('vue')['defineComponent']
|
||||
const eagerComputed: typeof import('@vueuse/core')['eagerComputed']
|
||||
const effectScope: typeof import('vue')['effectScope']
|
||||
const EffectScope: typeof import('vue')['EffectScope']
|
||||
const extendRef: typeof import('@vueuse/core')['extendRef']
|
||||
const getCurrentInstance: typeof import('vue')['getCurrentInstance']
|
||||
const getCurrentScope: typeof import('vue')['getCurrentScope']
|
||||
@@ -32,6 +32,8 @@ declare global {
|
||||
const ignorableWatch: typeof import('@vueuse/core')['ignorableWatch']
|
||||
const inject: typeof import('vue')['inject']
|
||||
const isDefined: typeof import('@vueuse/core')['isDefined']
|
||||
const isProxy: typeof import('vue')['isProxy']
|
||||
const isReactive: typeof import('vue')['isReactive']
|
||||
const isReadonly: typeof import('vue')['isReadonly']
|
||||
const isRef: typeof import('vue')['isRef']
|
||||
const logicAnd: typeof import('@vueuse/core')['logicAnd']
|
||||
@@ -120,8 +122,8 @@ declare global {
|
||||
const useDark: typeof import('@vueuse/core')['useDark']
|
||||
const useDateFormat: typeof import('@vueuse/core')['useDateFormat']
|
||||
const useDebounce: typeof import('@vueuse/core')['useDebounce']
|
||||
const useDebouncedRefHistory: typeof import('@vueuse/core')['useDebouncedRefHistory']
|
||||
const useDebounceFn: typeof import('@vueuse/core')['useDebounceFn']
|
||||
const useDebouncedRefHistory: typeof import('@vueuse/core')['useDebouncedRefHistory']
|
||||
const useDeviceMotion: typeof import('@vueuse/core')['useDeviceMotion']
|
||||
const useDeviceOrientation: typeof import('@vueuse/core')['useDeviceOrientation']
|
||||
const useDevicePixelRatio: typeof import('@vueuse/core')['useDevicePixelRatio']
|
||||
@@ -202,11 +204,11 @@ declare global {
|
||||
const useStyleTag: typeof import('@vueuse/core')['useStyleTag']
|
||||
const useSwipe: typeof import('@vueuse/core')['useSwipe']
|
||||
const useTemplateRefsList: typeof import('@vueuse/core')['useTemplateRefsList']
|
||||
const useTextareaAutosize: typeof import('@vueuse/core')['useTextareaAutosize']
|
||||
const useTextSelection: typeof import('@vueuse/core')['useTextSelection']
|
||||
const useTextareaAutosize: typeof import('@vueuse/core')['useTextareaAutosize']
|
||||
const useThrottle: typeof import('@vueuse/core')['useThrottle']
|
||||
const useThrottledRefHistory: typeof import('@vueuse/core')['useThrottledRefHistory']
|
||||
const useThrottleFn: typeof import('@vueuse/core')['useThrottleFn']
|
||||
const useThrottledRefHistory: typeof import('@vueuse/core')['useThrottledRefHistory']
|
||||
const useTimeAgo: typeof import('@vueuse/core')['useTimeAgo']
|
||||
const useTimeout: typeof import('@vueuse/core')['useTimeout']
|
||||
const useTimeoutFn: typeof import('@vueuse/core')['useTimeoutFn']
|
||||
@@ -217,10 +219,10 @@ declare global {
|
||||
const useTransition: typeof import('@vueuse/core')['useTransition']
|
||||
const useUrlSearchParams: typeof import('@vueuse/core')['useUrlSearchParams']
|
||||
const useUserMedia: typeof import('@vueuse/core')['useUserMedia']
|
||||
const useVibrate: typeof import('@vueuse/core')['useVibrate']
|
||||
const useVirtualList: typeof import('@vueuse/core')['useVirtualList']
|
||||
const useVModel: typeof import('@vueuse/core')['useVModel']
|
||||
const useVModels: typeof import('@vueuse/core')['useVModels']
|
||||
const useVibrate: typeof import('@vueuse/core')['useVibrate']
|
||||
const useVirtualList: typeof import('@vueuse/core')['useVirtualList']
|
||||
const useWakeLock: typeof import('@vueuse/core')['useWakeLock']
|
||||
const useWebNotification: typeof import('@vueuse/core')['useWebNotification']
|
||||
const useWebSocket: typeof import('@vueuse/core')['useWebSocket']
|
||||
@@ -237,9 +239,10 @@ declare global {
|
||||
const watchIgnorable: typeof import('@vueuse/core')['watchIgnorable']
|
||||
const watchOnce: typeof import('@vueuse/core')['watchOnce']
|
||||
const watchPausable: typeof import('@vueuse/core')['watchPausable']
|
||||
const watchPostEffect: typeof import('vue')['watchPostEffect']
|
||||
const watchSyncEffect: typeof import('vue')['watchSyncEffect']
|
||||
const watchThrottled: typeof import('@vueuse/core')['watchThrottled']
|
||||
const watchTriggerable: typeof import('@vueuse/core')['watchTriggerable']
|
||||
const watchWithFilter: typeof import('@vueuse/core')['watchWithFilter']
|
||||
const whenever: typeof import('@vueuse/core')['whenever']
|
||||
}
|
||||
export {}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
import type { MoveableEvents } from 'vue3-moveable'
|
||||
import type { MoveableEvents, MoveableProps } from 'vue3-moveable'
|
||||
import Moveable from 'vue3-moveable'
|
||||
import type { Position } from '@braks/vue-flow'
|
||||
import { Handle, useVueFlow } from '@braks/vue-flow'
|
||||
@@ -9,14 +9,27 @@ const props = defineProps<{
|
||||
label: string
|
||||
targetPosition: Position
|
||||
sourcePosition: Position
|
||||
nodeElement: HTMLDivElement
|
||||
data: any
|
||||
moveableProps?: MoveableProps
|
||||
}>()
|
||||
|
||||
const { viewport, updateNodeDimensions, onPaneClick, onPaneScroll, onPaneContextMenu, getNode, onNodeClick, onNodeDragStart } =
|
||||
useVueFlow()
|
||||
const emits = defineEmits(['updateNodeInternals'])
|
||||
|
||||
const {
|
||||
viewport,
|
||||
updateNodeDimensions,
|
||||
onPaneClick,
|
||||
onPaneScroll,
|
||||
onPaneContextMenu,
|
||||
getNode,
|
||||
onNodeClick,
|
||||
onNodeDragStart,
|
||||
snapGrid,
|
||||
snapToGrid,
|
||||
} = useVueFlow()
|
||||
|
||||
const el = ref()
|
||||
|
||||
const visible = ref(false)
|
||||
|
||||
const frame = {
|
||||
@@ -37,28 +50,22 @@ const onRotateStart = (e: MoveableEvents['rotateStart']) => {
|
||||
const onRotate = (e: MoveableEvents['rotate']) => {
|
||||
frame.rotate = e.beforeRotate
|
||||
e.target.style.transform = `rotate(${e.beforeRotate}deg)`
|
||||
updateNodeDimensions([{ id: props.id, nodeElement: props.nodeElement, forceUpdate: true }])
|
||||
emits('updateNodeInternals')
|
||||
}
|
||||
|
||||
const onClick = () => {
|
||||
visible.value = true
|
||||
const toggleVisibility = (val?: boolean) => {
|
||||
visible.value = val ?? !visible.value
|
||||
}
|
||||
|
||||
onNodeClick(({ node }) => {
|
||||
if (node.id === props.id) onClick()
|
||||
toggleVisibility(node.id === props.id)
|
||||
})
|
||||
|
||||
onNodeDragStart(({ node }) => {
|
||||
if (node.id === props.id) onClick()
|
||||
toggleVisibility(node.id === props.id)
|
||||
})
|
||||
|
||||
const hideMoveable = () => {
|
||||
visible.value = false
|
||||
}
|
||||
|
||||
onPaneClick(hideMoveable)
|
||||
onPaneScroll(hideMoveable)
|
||||
onPaneContextMenu(hideMoveable)
|
||||
onPaneClick(() => toggleVisibility(false))
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
@@ -76,27 +83,41 @@ export default {
|
||||
...data.style,
|
||||
}"
|
||||
style="position: relative"
|
||||
@click="onClick"
|
||||
>
|
||||
<Handle type="target" :position="props.targetPosition" />
|
||||
|
||||
<slot>
|
||||
{{ props.label }}
|
||||
</slot>
|
||||
|
||||
<Handle type="source" :position="props.sourcePosition" />
|
||||
</div>
|
||||
|
||||
<Moveable
|
||||
v-if="visible"
|
||||
v-bind="props.moveableProps"
|
||||
class-name="nodrag"
|
||||
:target="[`[data-moveable-id='${props.id}']`]"
|
||||
:resizable="true"
|
||||
:rotatable="true"
|
||||
:draggable="true"
|
||||
: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[0]"
|
||||
rotation-position="top"
|
||||
:padding="{ left: 0, top: 0, right: 0, bottom: 0 }"
|
||||
:origin="false"
|
||||
:edge="true"
|
||||
:start-drag-rotate="0"
|
||||
:throttle-drag-rotate="0"
|
||||
:throttle-resize="1"
|
||||
:render-directions="['nw', 'n', 'ne', 'w', 'e', 'sw', 's', 'se']"
|
||||
:zoom="0.75"
|
||||
:zoom="1"
|
||||
@resize="onResize"
|
||||
@rotate="onRotate"
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user