feat(node-resizer): add maxHeight and maxWidth props
Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com>
This commit is contained in:
@@ -7,7 +7,7 @@ defineProps(['label'])
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<NodeResizer min-width="100" min-height="30" />
|
<NodeResizer min-width="100" min-height="30" max-height="200" max-width="200" keep-aspect-ratio />
|
||||||
|
|
||||||
<Handle type="target" :position="Position.Left" />
|
<Handle type="target" :position="Position.Left" />
|
||||||
<div style="padding: 10px">{{ label }}</div>
|
<div style="padding: 10px">{{ label }}</div>
|
||||||
|
|||||||
@@ -40,6 +40,8 @@ export default {
|
|||||||
:color="color"
|
:color="color"
|
||||||
:min-width="minWidth"
|
:min-width="minWidth"
|
||||||
:min-height="minHeight"
|
:min-height="minHeight"
|
||||||
|
:max-width="maxWidth"
|
||||||
|
:max-height="maxHeight"
|
||||||
:should-resize="shouldResize"
|
:should-resize="shouldResize"
|
||||||
@resize-start="emits('resizeStart', $event)"
|
@resize-start="emits('resizeStart', $event)"
|
||||||
@resize="emits('resize', $event)"
|
@resize="emits('resize', $event)"
|
||||||
@@ -56,6 +58,8 @@ export default {
|
|||||||
:color="color"
|
:color="color"
|
||||||
:min-width="minWidth"
|
:min-width="minWidth"
|
||||||
:min-height="minHeight"
|
:min-height="minHeight"
|
||||||
|
:max-width="maxWidth"
|
||||||
|
:max-height="maxHeight"
|
||||||
:should-resize="shouldResize"
|
:should-resize="shouldResize"
|
||||||
:keep-aspect-ratio="keepAspectRatio"
|
:keep-aspect-ratio="keepAspectRatio"
|
||||||
@resize-start="emits('resizeStart', $event)"
|
@resize-start="emits('resizeStart', $event)"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<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, useGetPointerPosition, useVueFlow } from '@vue-flow/core'
|
import { NodeIdInjection, 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 type { OnResize, OnResizeStart, ResizeControlProps, ResizeDragEvent } from './types'
|
import type { OnResize, OnResizeStart, ResizeControlProps, ResizeDragEvent } from './types'
|
||||||
@@ -11,6 +11,9 @@ const props = withDefaults(defineProps<ResizeControlProps>(), {
|
|||||||
variant: 'handle' as ResizeControlVariant,
|
variant: 'handle' as ResizeControlVariant,
|
||||||
minWidth: 10,
|
minWidth: 10,
|
||||||
minHeight: 10,
|
minHeight: 10,
|
||||||
|
maxWidth: Number.MAX_VALUE,
|
||||||
|
maxHeight: Number.MAX_VALUE,
|
||||||
|
keepAspectRatio: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
const emits = defineEmits<{
|
const emits = defineEmits<{
|
||||||
@@ -25,33 +28,25 @@ const initStartValues = {
|
|||||||
...initPrevValues,
|
...initPrevValues,
|
||||||
pointerX: 0,
|
pointerX: 0,
|
||||||
pointerY: 0,
|
pointerY: 0,
|
||||||
|
aspectRatio: 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { findNode, emits: triggerEmits } = useVueFlow()
|
||||||
|
|
||||||
|
const getPointerPosition = useGetPointerPosition()
|
||||||
|
|
||||||
const contextNodeId = inject(NodeIdInjection, null)
|
const contextNodeId = inject(NodeIdInjection, null)
|
||||||
|
|
||||||
const resizeControlRef = ref<HTMLDivElement>()
|
const resizeControlRef = ref<HTMLDivElement>()
|
||||||
|
|
||||||
const id = computed(() => (typeof props.nodeId === 'string' ? props.nodeId : contextNodeId))
|
|
||||||
|
|
||||||
const { findNode, emits: triggerEmits } = useVueFlow()
|
|
||||||
|
|
||||||
const startValues = ref<typeof initStartValues>(initStartValues)
|
const startValues = ref<typeof initStartValues>(initStartValues)
|
||||||
|
|
||||||
const prevValues = ref<typeof initPrevValues>(initPrevValues)
|
const prevValues = ref<typeof initPrevValues>(initPrevValues)
|
||||||
|
|
||||||
const aspectRatio = computed(() => {
|
const id = computed(() => (typeof props.nodeId === 'string' ? props.nodeId : contextNodeId))
|
||||||
if (props.keepAspectRatio === true && startValues.value.width && startValues.value.height) {
|
|
||||||
return startValues.value.width / startValues.value.height
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof props.keepAspectRatio == 'number') {
|
|
||||||
return props.keepAspectRatio
|
|
||||||
}
|
|
||||||
|
|
||||||
return undefined
|
|
||||||
})
|
|
||||||
|
|
||||||
const getPointerPosition = useGetPointerPosition()
|
|
||||||
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) => {
|
||||||
@@ -61,6 +56,11 @@ watchEffect((onCleanup) => {
|
|||||||
|
|
||||||
const selection = select(resizeControlRef.value)
|
const selection = select(resizeControlRef.value)
|
||||||
|
|
||||||
|
const enableX = controlPosition.value.includes('right') || controlPosition.value.includes('left')
|
||||||
|
const enableY = controlPosition.value.includes('bottom') || controlPosition.value.includes('top')
|
||||||
|
const invertX = controlPosition.value.includes('left')
|
||||||
|
const invertY = controlPosition.value.includes('top')
|
||||||
|
|
||||||
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(id.value!)
|
||||||
@@ -77,6 +77,7 @@ watchEffect((onCleanup) => {
|
|||||||
...prevValues.value,
|
...prevValues.value,
|
||||||
pointerX: xSnapped,
|
pointerX: xSnapped,
|
||||||
pointerY: ySnapped,
|
pointerY: ySnapped,
|
||||||
|
aspectRatio: prevValues.value.width / prevValues.value.height,
|
||||||
}
|
}
|
||||||
|
|
||||||
emits('resizeStart', { event, params: prevValues.value })
|
emits('resizeStart', { event, params: prevValues.value })
|
||||||
@@ -84,10 +85,6 @@ 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(id.value!)
|
||||||
const enableX = controlPosition.value.includes('right') || controlPosition.value.includes('left')
|
|
||||||
const enableY = controlPosition.value.includes('bottom') || controlPosition.value.includes('top')
|
|
||||||
const invertX = controlPosition.value.includes('left')
|
|
||||||
const invertY = controlPosition.value.includes('top')
|
|
||||||
|
|
||||||
if (node) {
|
if (node) {
|
||||||
const changes: NodeChange[] = []
|
const changes: NodeChange[] = []
|
||||||
@@ -98,6 +95,7 @@ watchEffect((onCleanup) => {
|
|||||||
height: startHeight,
|
height: startHeight,
|
||||||
x: startNodeX,
|
x: startNodeX,
|
||||||
y: startNodeY,
|
y: startNodeY,
|
||||||
|
aspectRatio: startAspectRatio,
|
||||||
} = startValues.value
|
} = startValues.value
|
||||||
|
|
||||||
const { x: prevX, y: prevY, width: prevWidth, height: prevHeight } = prevValues.value
|
const { x: prevX, y: prevY, width: prevWidth, height: prevHeight } = prevValues.value
|
||||||
@@ -105,20 +103,38 @@ watchEffect((onCleanup) => {
|
|||||||
const distX = Math.floor(enableX ? xSnapped - startX : 0)
|
const distX = Math.floor(enableX ? xSnapped - startX : 0)
|
||||||
const distY = Math.floor(enableY ? ySnapped - startY : 0)
|
const distY = Math.floor(enableY ? ySnapped - startY : 0)
|
||||||
|
|
||||||
let width = Math.max(startWidth + (invertX ? -distX : distX), props.minWidth)
|
let width = clamp(startWidth + (invertX ? -distX : distX), props.minWidth, props.maxWidth)
|
||||||
let height = Math.max(startHeight + (invertY ? -distY : distY), props.minHeight)
|
let height = clamp(startHeight + (invertY ? -distY : distY), props.minHeight, props.maxHeight)
|
||||||
|
|
||||||
if (aspectRatio.value) {
|
if (props.keepAspectRatio) {
|
||||||
const currentAspectRatio = width / height
|
const nextAspectRatio = width / height
|
||||||
if (currentAspectRatio !== aspectRatio.value) {
|
let aspectRatio = startAspectRatio
|
||||||
const newWidth = height * aspectRatio.value
|
|
||||||
const newHeight = width / aspectRatio.value
|
|
||||||
|
|
||||||
if (distX > distY) {
|
if (typeof props.keepAspectRatio === 'number' && nextAspectRatio !== props.keepAspectRatio) {
|
||||||
height = Math.max(newHeight, props.minHeight)
|
aspectRatio = props.keepAspectRatio
|
||||||
} else {
|
}
|
||||||
width = Math.max(newWidth, props.minWidth)
|
|
||||||
}
|
const isDiagonal = enableX && enableY
|
||||||
|
const isHorizontal = enableX && !enableY
|
||||||
|
const isVertical = enableY && !enableX
|
||||||
|
|
||||||
|
width = (nextAspectRatio <= aspectRatio && isDiagonal) || isVertical ? height * aspectRatio : width
|
||||||
|
height = (nextAspectRatio > aspectRatio && isDiagonal) || isHorizontal ? width / aspectRatio : height
|
||||||
|
|
||||||
|
if (width >= props.maxWidth) {
|
||||||
|
width = props.maxWidth
|
||||||
|
height = props.maxWidth / aspectRatio
|
||||||
|
} else if (width <= props.minWidth) {
|
||||||
|
width = props.minWidth
|
||||||
|
height = props.minWidth / aspectRatio
|
||||||
|
}
|
||||||
|
|
||||||
|
if (height >= props.maxHeight) {
|
||||||
|
height = props.maxHeight
|
||||||
|
width = props.maxHeight * aspectRatio
|
||||||
|
} else if (height <= props.minHeight) {
|
||||||
|
height = props.minHeight
|
||||||
|
width = props.minHeight * aspectRatio
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,8 @@ export interface NodeResizerProps {
|
|||||||
isVisible?: boolean
|
isVisible?: boolean
|
||||||
minWidth?: number
|
minWidth?: number
|
||||||
minHeight?: number
|
minHeight?: number
|
||||||
|
maxWidth?: number
|
||||||
|
maxHeight?: number
|
||||||
shouldResize?: ShouldResize
|
shouldResize?: ShouldResize
|
||||||
keepAspectRatio?: boolean | number
|
keepAspectRatio?: boolean | number
|
||||||
}
|
}
|
||||||
@@ -65,17 +67,21 @@ export interface ResizeControlProps {
|
|||||||
color?: string
|
color?: string
|
||||||
minWidth?: number
|
minWidth?: number
|
||||||
minHeight?: number
|
minHeight?: number
|
||||||
|
maxWidth?: number
|
||||||
|
maxHeight?: number
|
||||||
position?: ControlPosition
|
position?: ControlPosition
|
||||||
variant?: ResizeControlVariant
|
variant?: ResizeControlVariant
|
||||||
shouldResize?: ShouldResize
|
shouldResize?: ShouldResize
|
||||||
keepAspectRatio?: boolean | number
|
keepAspectRatio?: boolean | number
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ResizeControlLineProps {
|
export interface ResizeControlLineProps extends ResizeControlProps {
|
||||||
nodeId?: string
|
nodeId?: string
|
||||||
color?: string
|
color?: string
|
||||||
minWidth?: number
|
minWidth?: number
|
||||||
minHeight?: number
|
minHeight?: number
|
||||||
|
maxWidth?: number
|
||||||
|
maxHeight?: number
|
||||||
variant?: ResizeControlVariant
|
variant?: ResizeControlVariant
|
||||||
position?: ControlLinePosition
|
position?: ControlLinePosition
|
||||||
keepAspectRatio?: boolean | number
|
keepAspectRatio?: boolean | number
|
||||||
|
|||||||
Reference in New Issue
Block a user