feat(node-resizer): implement node resizer components
Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com>
This commit is contained in:
@@ -32,8 +32,13 @@
|
|||||||
"@vue-flow/core": "^1.0.0",
|
"@vue-flow/core": "^1.0.0",
|
||||||
"vue": "^3.2.37"
|
"vue": "^3.2.37"
|
||||||
},
|
},
|
||||||
"dependencies": {},
|
"dependencies": {
|
||||||
|
"d3-drag": "^3.0.0",
|
||||||
|
"d3-selection": "^3.0.0"
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/d3-drag": "^3.0.1",
|
||||||
|
"@types/d3-selection": "^3.0.3",
|
||||||
"@vue-flow/core": "workspace:*",
|
"@vue-flow/core": "workspace:*",
|
||||||
"@vitejs/plugin-vue": "^3.2.0",
|
"@vitejs/plugin-vue": "^3.2.0",
|
||||||
"unplugin-auto-import": "^0.12.0",
|
"unplugin-auto-import": "^0.12.0",
|
||||||
|
|||||||
@@ -1,91 +1,57 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, inject } from 'vue'
|
import ResizeControl from './ResizeControl.vue'
|
||||||
import type { GraphNode, Rect, ViewpaneTransform } from '@vue-flow/core'
|
import type { ControlLinePosition, ControlPosition, NodeResizerProps, ResizeDragEvent, ResizeEventParams } from './types'
|
||||||
import { NodeIdInjection, Position, getRectOfNodes, useVueFlow } from '@vue-flow/core'
|
import { ResizeControlVariant } from './types'
|
||||||
|
|
||||||
import type { CSSProperties } from 'vue'
|
const props = withDefaults(defineProps<NodeResizerProps>(), {
|
||||||
import type { NodeToolbarProps } from './types'
|
isVisible: true,
|
||||||
|
|
||||||
const props = withDefaults(defineProps<NodeToolbarProps>(), {
|
|
||||||
position: Position.Top,
|
|
||||||
offset: 10,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const contextNodeId = inject(NodeIdInjection, null)
|
const emits = defineEmits<{
|
||||||
|
(event: 'resizeStart', data: { event: ResizeDragEvent; params: ResizeEventParams }): void
|
||||||
|
(event: 'resize', data: { event: ResizeDragEvent; params: ResizeEventParams }): void
|
||||||
|
(event: 'resizeEnd', data: { event: ResizeDragEvent; params: ResizeEventParams }): void
|
||||||
|
}>()
|
||||||
|
|
||||||
const { viewportRef, viewport, getSelectedNodes, findNode } = useVueFlow()
|
const handleControls: ControlPosition[] = ['top-left', 'top-right', 'bottom-left', 'bottom-right']
|
||||||
|
const lineControls: ControlLinePosition[] = ['top', 'right', 'bottom', 'left']
|
||||||
function getTransform(nodeRect: Rect, transform: ViewpaneTransform, position: Position, offset: number): string {
|
|
||||||
// position === Position.Top
|
|
||||||
let xPos = (nodeRect.x + nodeRect.width / 2) * transform.zoom + transform.x
|
|
||||||
let yPos = nodeRect.y * transform.zoom + transform.y - offset
|
|
||||||
let xShift = -50
|
|
||||||
let yShift = -100
|
|
||||||
|
|
||||||
switch (position) {
|
|
||||||
case Position.Right:
|
|
||||||
xPos = (nodeRect.x + nodeRect.width) * transform.zoom + transform.x + offset
|
|
||||||
yPos = (nodeRect.y + nodeRect.height / 2) * transform.zoom + transform.y
|
|
||||||
xShift = 0
|
|
||||||
yShift = -50
|
|
||||||
break
|
|
||||||
case Position.Bottom:
|
|
||||||
yPos = (nodeRect.y + nodeRect.height) * transform.zoom + transform.y + offset
|
|
||||||
yShift = 0
|
|
||||||
break
|
|
||||||
case Position.Left:
|
|
||||||
xPos = nodeRect.x * transform.zoom + transform.x - offset
|
|
||||||
yPos = (nodeRect.y + nodeRect.height / 2) * transform.zoom + transform.y
|
|
||||||
xShift = -100
|
|
||||||
yShift = -50
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
return `translate(${xPos}px, ${yPos}px) translate(${xShift}%, ${yShift}%)`
|
|
||||||
}
|
|
||||||
|
|
||||||
const nodes = computed(() => {
|
|
||||||
const nodeIds = Array.isArray(props.nodeId) ? props.nodeId : [props.nodeId || contextNodeId || '']
|
|
||||||
|
|
||||||
return nodeIds.reduce<GraphNode[]>((acc, id) => {
|
|
||||||
const node = findNode(id)
|
|
||||||
|
|
||||||
if (node) {
|
|
||||||
acc.push(node)
|
|
||||||
}
|
|
||||||
|
|
||||||
return acc
|
|
||||||
}, [] as GraphNode[])
|
|
||||||
})
|
|
||||||
|
|
||||||
const isActive = computed(() =>
|
|
||||||
typeof props.isVisible === 'boolean'
|
|
||||||
? props.isVisible
|
|
||||||
: nodes.value.length === 1 && nodes.value[0].selected && getSelectedNodes.value.length === 1,
|
|
||||||
)
|
|
||||||
|
|
||||||
const nodeRect = computed<Rect>(() => getRectOfNodes(nodes.value))
|
|
||||||
|
|
||||||
const zIndex = computed<number>(() => Math.max(...nodes.value.map((node) => (node.computedPosition.z || 1) + 1)))
|
|
||||||
|
|
||||||
const wrapperStyle = computed<CSSProperties>(() => ({
|
|
||||||
position: 'absolute',
|
|
||||||
transform: getTransform(nodeRect.value, viewport.value, props.position, props.offset),
|
|
||||||
zIndex: zIndex.value,
|
|
||||||
}))
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export default {
|
export default {
|
||||||
name: 'NodeToolbar',
|
name: 'NodeResizer',
|
||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Teleport :to="viewportRef">
|
<ResizeControl
|
||||||
<div v-if="isActive && nodes.length" v-bind="$attrs" :style="wrapperStyle" class="vue-flow__node-toolbar">
|
v-for="c of lineControls"
|
||||||
<slot />
|
:key="c"
|
||||||
</div>
|
:class="lineClassName"
|
||||||
</Teleport>
|
:style="lineStyle"
|
||||||
|
:node-id="nodeId"
|
||||||
|
:position="c"
|
||||||
|
:variant="ResizeControlVariant.Line"
|
||||||
|
:color="color"
|
||||||
|
:min-width="minWidth"
|
||||||
|
:min-height="minHeight"
|
||||||
|
@resize-start="emits('resizeStart', $event)"
|
||||||
|
@resize="emits('resize', $event)"
|
||||||
|
@resize-end="emits('resizeEnd', $event)"
|
||||||
|
/>
|
||||||
|
<ResizeControl
|
||||||
|
v-for="c of handleControls"
|
||||||
|
:key="c"
|
||||||
|
:class="lineClassName"
|
||||||
|
:style="lineStyle"
|
||||||
|
:node-id="nodeId"
|
||||||
|
:position="c"
|
||||||
|
:color="color"
|
||||||
|
:min-width="minWidth"
|
||||||
|
:min-height="minHeight"
|
||||||
|
@resize-start="emits('resizeStart', $event)"
|
||||||
|
@resize="emits('resize', $event)"
|
||||||
|
@resize-end="emits('resizeEnd', $event)"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -0,0 +1,183 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import type { NodeChange, NodeDimensionChange, NodePositionChange } from '@vue-flow/core'
|
||||||
|
import { NodeIdInjection, useGetPointerPosition, useVueFlow } from '@vue-flow/core'
|
||||||
|
import { select } from 'd3-selection'
|
||||||
|
import { drag } from 'd3-drag'
|
||||||
|
import type { ResizeControlProps, ResizeDragEvent, ResizeEventParams } from './types'
|
||||||
|
import { ResizeControlVariant } from './types'
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<ResizeControlProps>(), {
|
||||||
|
variant: 'handle' as ResizeControlVariant,
|
||||||
|
minWidth: 10,
|
||||||
|
minHeight: 10,
|
||||||
|
})
|
||||||
|
|
||||||
|
const emits = defineEmits<{
|
||||||
|
(event: 'resizeStart', data: { event: ResizeDragEvent; params: ResizeEventParams }): void
|
||||||
|
(event: 'resize', data: { event: ResizeDragEvent; params: ResizeEventParams }): void
|
||||||
|
(event: 'resizeEnd', data: { event: ResizeDragEvent; params: ResizeEventParams }): void
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const initPrevValues = { width: 0, height: 0, x: 0, y: 0 }
|
||||||
|
|
||||||
|
const initStartValues = {
|
||||||
|
...initPrevValues,
|
||||||
|
pointerX: 0,
|
||||||
|
pointerY: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
const contextNodeId = inject(NodeIdInjection, null)
|
||||||
|
|
||||||
|
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 prevValues = ref<typeof initPrevValues>(initPrevValues)
|
||||||
|
|
||||||
|
const getPointerPosition = useGetPointerPosition()
|
||||||
|
const defaultPosition = computed(() => (props.variant === ResizeControlVariant.Line ? 'right' : 'bottom-right'))
|
||||||
|
const controlPosition = computed(() => props.position ?? defaultPosition.value)
|
||||||
|
|
||||||
|
watchEffect((onCleanup) => {
|
||||||
|
if (!resizeControlRef.value || !id.value) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const selection = select(resizeControlRef.value)
|
||||||
|
|
||||||
|
const dragHandler = drag<HTMLDivElement, unknown>()
|
||||||
|
.on('start', (event: ResizeDragEvent) => {
|
||||||
|
const node = findNode(id.value!)
|
||||||
|
const { xSnapped, ySnapped } = getPointerPosition(event)
|
||||||
|
|
||||||
|
prevValues.value = {
|
||||||
|
width: node?.dimensions.width ?? 0,
|
||||||
|
height: node?.dimensions.height ?? 0,
|
||||||
|
x: node?.position.x ?? 0,
|
||||||
|
y: node?.position.y ?? 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
startValues.value = {
|
||||||
|
...prevValues.value,
|
||||||
|
pointerX: xSnapped,
|
||||||
|
pointerY: ySnapped,
|
||||||
|
}
|
||||||
|
|
||||||
|
emits('resizeStart', { event, params: prevValues.value })
|
||||||
|
})
|
||||||
|
.on('drag', (event: ResizeDragEvent) => {
|
||||||
|
const { xSnapped, ySnapped } = getPointerPosition(event)
|
||||||
|
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) {
|
||||||
|
const changes: NodeChange[] = []
|
||||||
|
const {
|
||||||
|
pointerX: startX,
|
||||||
|
pointerY: startY,
|
||||||
|
width: startWidth,
|
||||||
|
height: startHeight,
|
||||||
|
x: startNodeX,
|
||||||
|
y: startNodeY,
|
||||||
|
} = startValues.value
|
||||||
|
|
||||||
|
const { x: prevX, y: prevY, width: prevWidth, height: prevHeight } = prevValues.value
|
||||||
|
|
||||||
|
const distX = Math.floor(enableX ? xSnapped - startX : 0)
|
||||||
|
const distY = Math.floor(enableY ? ySnapped - startY : 0)
|
||||||
|
|
||||||
|
const width = Math.max(startWidth + (invertX ? -distX : distX), props.minWidth)
|
||||||
|
const height = Math.max(startHeight + (invertY ? -distY : distY), props.minHeight)
|
||||||
|
|
||||||
|
const isWidthChange = width !== prevWidth
|
||||||
|
const isHeightChange = height !== prevHeight
|
||||||
|
|
||||||
|
if (invertX || invertY) {
|
||||||
|
const x = invertX ? startNodeX - (width - startWidth) : startNodeX
|
||||||
|
const y = invertY ? startNodeY - (height - startHeight) : startNodeY
|
||||||
|
|
||||||
|
// only transform the node if the width or height changes
|
||||||
|
const isXPosChange = x !== prevX && isWidthChange
|
||||||
|
const isYPosChange = y !== prevY && isHeightChange
|
||||||
|
|
||||||
|
if (isXPosChange || isYPosChange) {
|
||||||
|
const positionChange: NodePositionChange = {
|
||||||
|
id: node.id,
|
||||||
|
type: 'position',
|
||||||
|
from: node.position,
|
||||||
|
position: {
|
||||||
|
x: isXPosChange ? x : prevX,
|
||||||
|
y: isYPosChange ? y : prevY,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
changes.push(positionChange)
|
||||||
|
|
||||||
|
prevValues.value.x = positionChange.position!.x
|
||||||
|
prevValues.value.y = positionChange.position!.y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isWidthChange || isHeightChange) {
|
||||||
|
const dimensionChange: NodeDimensionChange = {
|
||||||
|
id: id.value!,
|
||||||
|
type: 'dimensions',
|
||||||
|
updateStyle: true,
|
||||||
|
resizing: true,
|
||||||
|
dimensions: {
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
changes.push(dimensionChange)
|
||||||
|
|
||||||
|
prevValues.value.width = width
|
||||||
|
prevValues.value.height = height
|
||||||
|
}
|
||||||
|
|
||||||
|
emits('resize', { event, params: prevValues.value })
|
||||||
|
|
||||||
|
triggerEmits.nodesChange(changes)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.on('end', (event: ResizeDragEvent) => {
|
||||||
|
const dimensionChange: NodeDimensionChange = {
|
||||||
|
id: id.value!,
|
||||||
|
type: 'dimensions',
|
||||||
|
resizing: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
emits('resizeEnd', { event, params: prevValues.value })
|
||||||
|
|
||||||
|
triggerEmits.nodesChange([dimensionChange])
|
||||||
|
})
|
||||||
|
|
||||||
|
selection.call(dragHandler)
|
||||||
|
|
||||||
|
onCleanup(() => {
|
||||||
|
selection.on('.drag', null)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const positionClassNames = computed(() => controlPosition.value.split('-'))
|
||||||
|
const colorStyleProp = computed(() => (props.variant === ResizeControlVariant.Line ? 'borderColor' : 'backgroundColor'))
|
||||||
|
const controlStyle = computed(() => (props.color ? { [colorStyleProp.value]: props.color } : {}))
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
ref="resizeControlRef"
|
||||||
|
class="vue-flow__resize-control nodrag"
|
||||||
|
:class="[...positionClassNames, variant]"
|
||||||
|
:style="controlStyle"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import ResizeControl from './ResizeControl.vue'
|
||||||
|
import type { ResizeControlLineProps, ResizeDragEvent, ResizeEventParams } from './types'
|
||||||
|
import { ResizeControlVariant } from './types'
|
||||||
|
|
||||||
|
const props = defineProps<ResizeControlLineProps>()
|
||||||
|
|
||||||
|
const emits = defineEmits<{
|
||||||
|
(event: 'resizeStart', data: { event: ResizeDragEvent; params: ResizeEventParams }): void
|
||||||
|
(event: 'resize', data: { event: ResizeDragEvent; params: ResizeEventParams }): void
|
||||||
|
(event: 'resizeEnd', data: { event: ResizeDragEvent; params: ResizeEventParams }): void
|
||||||
|
}>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ResizeControl
|
||||||
|
v-bind="props"
|
||||||
|
:variant="ResizeControlVariant.Line"
|
||||||
|
@resize-start="emits('resizeStart', $event)"
|
||||||
|
@resize="emits('resize', $event)"
|
||||||
|
@resize-end="emits('resizeEnd', $event)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
@@ -1,2 +1,4 @@
|
|||||||
|
import './style.css'
|
||||||
export * from './types'
|
export * from './types'
|
||||||
export { default as NodeResizer } from './NodeResizer.vue'
|
export { default as NodeResizer } from './NodeResizer.vue'
|
||||||
|
export { default as NodeResizeControl } from './ResizeControl.vue'
|
||||||
|
|||||||
@@ -0,0 +1,103 @@
|
|||||||
|
.vue-flow__resize-control {
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-flow__resize-control.left,
|
||||||
|
.vue-flow__resize-control.right {
|
||||||
|
cursor: ew-resize;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-flow__resize-control.top,
|
||||||
|
.vue-flow__resize-control.bottom {
|
||||||
|
cursor: ns-resize;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-flow__resize-control.top.left,
|
||||||
|
.vue-flow__resize-control.bottom.right {
|
||||||
|
cursor: nwse-resize;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-flow__resize-control.bottom.left,
|
||||||
|
.vue-flow__resize-control.top.right {
|
||||||
|
cursor: nesw-resize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* handle styles */
|
||||||
|
.vue-flow__resize-control.handle {
|
||||||
|
width: 4px;
|
||||||
|
height: 4px;
|
||||||
|
border: 1px solid #fff;
|
||||||
|
border-radius: 1px;
|
||||||
|
background-color: #3367d9;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-flow__resize-control.handle.left {
|
||||||
|
left: 0;
|
||||||
|
top: 50%;
|
||||||
|
}
|
||||||
|
.vue-flow__resize-control.handle.right {
|
||||||
|
left: 100%;
|
||||||
|
top: 50%;
|
||||||
|
}
|
||||||
|
.vue-flow__resize-control.handle.top {
|
||||||
|
left: 50%;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
.vue-flow__resize-control.handle.bottom {
|
||||||
|
left: 50%;
|
||||||
|
top: 100%;
|
||||||
|
}
|
||||||
|
.vue-flow__resize-control.handle.top.left {
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
.vue-flow__resize-control.handle.bottom.left {
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
.vue-flow__resize-control.handle.top.right {
|
||||||
|
left: 100%;
|
||||||
|
}
|
||||||
|
.vue-flow__resize-control.handle.bottom.right {
|
||||||
|
left: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* line styles */
|
||||||
|
.vue-flow__resize-control.line {
|
||||||
|
border-color: #3367d9;
|
||||||
|
border-width: 0;
|
||||||
|
border-style: solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-flow__resize-control.line.left,
|
||||||
|
.vue-flow__resize-control.line.right {
|
||||||
|
width: 1px;
|
||||||
|
transform: translate(-50%, 0);
|
||||||
|
top: 0;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-flow__resize-control.line.left {
|
||||||
|
left: 0;
|
||||||
|
border-left-width: 1px;
|
||||||
|
}
|
||||||
|
.vue-flow__resize-control.line.right {
|
||||||
|
left: 100%;
|
||||||
|
border-right-width: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-flow__resize-control.line.top,
|
||||||
|
.vue-flow__resize-control.line.bottom {
|
||||||
|
height: 1px;
|
||||||
|
transform: translate(0, -50%);
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-flow__resize-control.line.top {
|
||||||
|
top: 0;
|
||||||
|
border-top-width: 1px;
|
||||||
|
}
|
||||||
|
.vue-flow__resize-control.line.bottom {
|
||||||
|
border-bottom-width: 1px;
|
||||||
|
top: 100%;
|
||||||
|
}
|
||||||
@@ -1,8 +1,56 @@
|
|||||||
import type { Position } from '@vue-flow/core'
|
import type { D3DragEvent, SubjectPosition } from 'd3-drag'
|
||||||
|
import type { CSSProperties } from 'vue'
|
||||||
|
|
||||||
export interface NodeToolbarProps {
|
export interface ResizeEventParams {
|
||||||
nodeId?: string | string[]
|
x: number
|
||||||
isVisible?: boolean
|
y: number
|
||||||
position?: Position
|
width: number
|
||||||
offset?: number
|
height: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface NodeResizerProps {
|
||||||
|
nodeId?: string
|
||||||
|
color?: string
|
||||||
|
handleClassName?: string
|
||||||
|
handleStyle?: CSSProperties
|
||||||
|
lineClassName?: string
|
||||||
|
lineStyle?: CSSProperties
|
||||||
|
isVisible?: boolean
|
||||||
|
minWidth?: number
|
||||||
|
minHeight?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NodeResizerEmits {
|
||||||
|
(event: 'resizeStart', data: { event: ResizeDragEvent; params: ResizeEventParams }): void
|
||||||
|
(event: 'resize', data: { event: ResizeDragEvent; params: ResizeEventParams }): void
|
||||||
|
(event: 'resizeEnd', data: { event: ResizeDragEvent; params: ResizeEventParams }): void
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ControlLinePosition = 'top' | 'bottom' | 'left' | 'right'
|
||||||
|
|
||||||
|
export type ControlPosition = ControlLinePosition | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'
|
||||||
|
|
||||||
|
export enum ResizeControlVariant {
|
||||||
|
Line = 'line',
|
||||||
|
Handle = 'handle',
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ResizeControlProps {
|
||||||
|
nodeId?: string
|
||||||
|
color?: string
|
||||||
|
minWidth?: number
|
||||||
|
minHeight?: number
|
||||||
|
position?: ControlPosition
|
||||||
|
variant?: ResizeControlVariant
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ResizeControlLineProps {
|
||||||
|
nodeId?: string
|
||||||
|
color?: string
|
||||||
|
minWidth?: number
|
||||||
|
minHeight?: number
|
||||||
|
variant?: ResizeControlVariant
|
||||||
|
position?: ControlLinePosition
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ResizeDragEvent = D3DragEvent<HTMLDivElement, null, SubjectPosition>
|
||||||
|
|||||||
Reference in New Issue
Block a user