fix(node-resizer): enforce min/max width/height on nodes

This commit is contained in:
braks
2023-08-21 17:44:20 +02:00
committed by Braks
parent 7cf967a9c3
commit 06851e6894
3 changed files with 72 additions and 22 deletions
+54 -2
View File
@@ -1,10 +1,13 @@
<script lang="ts" setup> <script lang="ts" setup>
// todo: add plugin to emit resize events via vue flow store; requires plugin API to be added to core // todo: add plugin to emit resize events via vue flow store; requires plugin API to be added to core
import { computed, inject, watch } from 'vue'
import type { NodeDimensionChange } from '@vue-flow/core'
import { NodeIdInjection, useVueFlow } from '@vue-flow/core'
import ResizeControl from './ResizeControl.vue' import ResizeControl from './ResizeControl.vue'
import type { ControlLinePosition, ControlPosition, NodeResizerProps, OnResize, OnResizeStart } from './types' import type { ControlLinePosition, ControlPosition, NodeResizerProps, OnResize, OnResizeStart } from './types'
import { ResizeControlVariant } from './types' import { ResizeControlVariant } from './types'
withDefaults(defineProps<NodeResizerProps>(), { const props = withDefaults(defineProps<NodeResizerProps>(), {
isVisible: true, isVisible: true,
}) })
@@ -14,9 +17,58 @@ const emits = defineEmits<{
(event: 'resizeEnd', resizeEvent: OnResizeStart): void (event: 'resizeEnd', resizeEvent: OnResizeStart): void
}>() }>()
const { findNode, emits: triggerEmits } = useVueFlow()
const handleControls: ControlPosition[] = ['top-left', 'top-right', 'bottom-left', 'bottom-right'] const handleControls: ControlPosition[] = ['top-left', 'top-right', 'bottom-left', 'bottom-right']
const lineControls: ControlLinePosition[] = ['top', 'right', 'bottom', 'left'] const lineControls: ControlLinePosition[] = ['top', 'right', 'bottom', 'left']
const contextNodeId = inject(NodeIdInjection, null)
const id = computed(() => (typeof props.nodeId === 'string' ? props.nodeId : contextNodeId))
watch(
[() => props.minWidth, () => props.minHeight, () => props.maxWidth, () => props.maxHeight],
([minWidth, minHeight, maxWidth, maxHeight]) => {
const node = findNode(id.value)
if (node) {
const dimensionChange: NodeDimensionChange = {
id: node.id,
type: 'dimensions',
updateStyle: true,
dimensions: {
width: node.dimensions.width,
height: node.dimensions.height,
},
}
if (minWidth && node.dimensions.width < minWidth) {
dimensionChange.dimensions!.width = minWidth
}
if (minHeight && node.dimensions.height < minHeight) {
dimensionChange.dimensions!.height = minHeight
}
if (maxWidth && node.dimensions.width > maxWidth) {
dimensionChange.dimensions!.width = maxWidth
}
if (maxHeight && node.dimensions.height > maxHeight) {
dimensionChange.dimensions!.height = maxHeight
}
if (
dimensionChange.dimensions!.width !== node.dimensions.width ||
dimensionChange.dimensions!.height !== node.dimensions.height
) {
triggerEmits.nodesChange([dimensionChange])
}
}
},
{ flush: 'post', immediate: true },
)
</script> </script>
<script lang="ts"> <script lang="ts">
@@ -54,7 +106,7 @@ export default {
:key="c" :key="c"
:class="handleClassName" :class="handleClassName"
:style="handleStyle" :style="handleStyle"
:node-id="nodeId" :node-id="id"
:position="c" :position="c"
:color="color" :color="color"
:min-width="minWidth" :min-width="minWidth"
+17 -19
View File
@@ -1,9 +1,9 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { NodeChange, NodeDimensionChange, NodePositionChange } from '@vue-flow/core' import type { NodeChange, NodeDimensionChange, NodePositionChange } from '@vue-flow/core'
import { NodeIdInjection, clamp, useGetPointerPosition, useVueFlow } from '@vue-flow/core' import { clamp, useGetPointerPosition, useVueFlow } from '@vue-flow/core'
import { select } from 'd3-selection' import { select } from 'd3-selection'
import { drag } from 'd3-drag' import { drag } from 'd3-drag'
import { computed, inject, ref, watchEffect } from 'vue' import { computed, ref, watchEffect } from 'vue'
import type { OnResize, OnResizeStart, ResizeControlProps, ResizeDragEvent } from './types' import type { OnResize, OnResizeStart, ResizeControlProps, ResizeDragEvent } from './types'
import { ResizeControlVariant } from './types' import { ResizeControlVariant } from './types'
import { getDirection } from './utils' import { getDirection } from './utils'
@@ -36,22 +36,18 @@ const { findNode, emits: triggerEmits } = useVueFlow()
const getPointerPosition = useGetPointerPosition() const getPointerPosition = useGetPointerPosition()
const contextNodeId = inject(NodeIdInjection, null)
const resizeControlRef = ref<HTMLDivElement>() const resizeControlRef = ref<HTMLDivElement>()
let startValues: typeof initStartValues = initStartValues let startValues: typeof initStartValues = initStartValues
let prevValues: typeof initPrevValues = initPrevValues let prevValues: typeof initPrevValues = initPrevValues
const id = computed(() => (typeof props.nodeId === 'string' ? props.nodeId : contextNodeId))
const defaultPosition = computed(() => (props.variant === ResizeControlVariant.Line ? 'right' : 'bottom-right')) const defaultPosition = computed(() => (props.variant === ResizeControlVariant.Line ? 'right' : 'bottom-right'))
const controlPosition = computed(() => props.position ?? defaultPosition.value) const controlPosition = computed(() => props.position ?? defaultPosition.value)
watchEffect((onCleanup) => { watchEffect((onCleanup) => {
if (!resizeControlRef.value || !id.value) { if (!resizeControlRef.value || !props.nodeId) {
return return
} }
@@ -64,7 +60,7 @@ watchEffect((onCleanup) => {
const dragHandler = drag<HTMLDivElement, unknown>() const dragHandler = drag<HTMLDivElement, unknown>()
.on('start', (event: ResizeDragEvent) => { .on('start', (event: ResizeDragEvent) => {
const node = findNode(id.value!) const node = findNode(props.nodeId)
const { xSnapped, ySnapped } = getPointerPosition(event) const { xSnapped, ySnapped } = getPointerPosition(event)
prevValues = { prevValues = {
@@ -85,7 +81,7 @@ watchEffect((onCleanup) => {
}) })
.on('drag', (event: ResizeDragEvent) => { .on('drag', (event: ResizeDragEvent) => {
const { xSnapped, ySnapped } = getPointerPosition(event) const { xSnapped, ySnapped } = getPointerPosition(event)
const node = findNode(id.value!) const node = findNode(props.nodeId)
if (node) { if (node) {
const changes: NodeChange[] = [] const changes: NodeChange[] = []
@@ -168,9 +164,9 @@ watchEffect((onCleanup) => {
} }
} }
if (isWidthChange || isHeightChange) { if (props.nodeId && (isWidthChange || isHeightChange)) {
const dimensionChange: NodeDimensionChange = { const dimensionChange: NodeDimensionChange = {
id: id.value!, id: props.nodeId,
type: 'dimensions', type: 'dimensions',
updateStyle: true, updateStyle: true,
resizing: true, resizing: true,
@@ -213,15 +209,17 @@ watchEffect((onCleanup) => {
} }
}) })
.on('end', (event: ResizeDragEvent) => { .on('end', (event: ResizeDragEvent) => {
const dimensionChange: NodeDimensionChange = { if (props.nodeId) {
id: id.value!, const dimensionChange: NodeDimensionChange = {
type: 'dimensions', id: props.nodeId,
resizing: false, type: 'dimensions',
resizing: false,
}
emits('resizeEnd', { event, params: prevValues })
triggerEmits.nodesChange([dimensionChange])
} }
emits('resizeEnd', { event, params: prevValues })
triggerEmits.nodesChange([dimensionChange])
}) })
selection.call(dragHandler) selection.call(dragHandler)
+1 -1
View File
@@ -63,7 +63,7 @@ export enum ResizeControlVariant {
} }
export interface ResizeControlProps { export interface ResizeControlProps {
nodeId?: string nodeId?: string | null
color?: string color?: string
minWidth?: number minWidth?: number
minHeight?: number minHeight?: number