fix(node-resizer): enforce min/max width/height on nodes
This commit is contained in:
@@ -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"
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user