52 lines
1.9 KiB
TypeScript
52 lines
1.9 KiB
TypeScript
"use client"
|
||
import { type PointerEvent } from "react"
|
||
import { useReactFlow } from "@xyflow/react"
|
||
|
||
/** 节点右边缘 resize 把手:手写 pointer 事件改 node.width,自适应 viewport zoom,不走 xyflow NodeResizeControl(在 glass-node overflow:hidden 下不工作)。 */
|
||
export function ResizeRight({ minWidth = 240, maxWidth = 1200 }: { minWidth?: number; maxWidth?: number }) {
|
||
const { setNodes, getZoom } = useReactFlow()
|
||
|
||
const onPointerDown = (e: PointerEvent<HTMLDivElement>) => {
|
||
e.preventDefault()
|
||
e.stopPropagation()
|
||
const target = e.currentTarget
|
||
const nodeEl = target.closest(".react-flow__node") as HTMLElement | null
|
||
const nodeId = nodeEl?.getAttribute("data-id")
|
||
if (!nodeId || !nodeEl) return
|
||
target.setPointerCapture(e.pointerId)
|
||
|
||
const startX = e.clientX
|
||
const zoom = getZoom() || 1
|
||
const startWidth = parseFloat(getComputedStyle(nodeEl).width) / zoom
|
||
|
||
const onMove = (ev: globalThis.PointerEvent) => {
|
||
const dx = (ev.clientX - startX) / zoom
|
||
const next = Math.max(minWidth, Math.min(maxWidth, startWidth + dx))
|
||
setNodes((nodes) => nodes.map((n) => (n.id === nodeId ? { ...n, width: next, style: { ...n.style, width: next } } : n)))
|
||
}
|
||
const onUp = () => {
|
||
window.removeEventListener("pointermove", onMove)
|
||
window.removeEventListener("pointerup", onUp)
|
||
try { target.releasePointerCapture(e.pointerId) } catch {}
|
||
}
|
||
window.addEventListener("pointermove", onMove)
|
||
window.addEventListener("pointerup", onUp)
|
||
}
|
||
|
||
return (
|
||
<div
|
||
onPointerDown={onPointerDown}
|
||
title="拖动调整宽度"
|
||
className="nodrag absolute z-20 hover:bg-violet-400/60 active:bg-violet-400/80 transition rounded-r"
|
||
style={{
|
||
right: 0,
|
||
top: 12,
|
||
bottom: 12,
|
||
width: 6,
|
||
cursor: "ew-resize",
|
||
touchAction: "none",
|
||
}}
|
||
/>
|
||
)
|
||
}
|