Bind rendered editing callbacks to captured element lifetimes

This commit is contained in:
2026-11-09 19:26:00 +08:00
parent 28c792d640
commit 9efe6e7c9f
14 changed files with 103 additions and 45 deletions
+8 -21
View File
@@ -1,10 +1,10 @@
"use client";
import { createContext, useContext, useEffect, useRef, type PointerEvent, type ReactNode } from "react";
import { useEffect, useRef, type PointerEvent, type ReactNode } from "react";
import { useReactFlow, useUpdateNodeInternals, useViewport, type NodeProps, type XYPosition } from "@xyflow/react";
import { Grip, Maximize2, RotateCw } from "lucide-react";
import { elementCapabilities, type TextStyle } from "@/lib/canvas-elements";
import type { CanvasNode, NodeDirection } from "@/lib/canvas";
import { elementCapabilities } from "@/lib/canvas-elements";
import type { CanvasNode } from "@/lib/canvas";
import { resizeFromPointer, rotationFromPointer, type NodeTransform, type ResizeDirection } from "@/lib/canvas-geometry";
import type { EditingLease } from "./editing-lease";
import { NodeStyleToolbar } from "./node-style-toolbar";
@@ -13,26 +13,13 @@ import { ContentHandles } from "./content-handles";
import {ownsPointerCapture,acquirePointerCapture,releasePointerCapture} from "@/lib/canvas-bindings/pointer-capture";
import styles from "./canvas.module.css";
const defaultEditing = {
beginTransform: undefined as undefined | ((id: string) => boolean | void | EditingLease<NodeTransform>), finishTransform: undefined as undefined | (() => void), cancelTransform: undefined as undefined | (() => void),
endEditing: undefined as undefined | ((id: string) => void),
workspace: "", schema: 1 as 1 | 2,
begin: (_id?: string) => {}, select: (_id: string) => {}, edit: (_id: string, _text: string) => {},
branch: (_id: string, _direction: NodeDirection, _point: XYPosition) => {},
transform: (_id: string, _transform: NodeTransform) => {},
style: (_id: string, _style: TextStyle, _fontSize?: number) => {},
rename: (_id: string, _title: string) => {},
frame: (_id: string, _patch: { title?: string; labelMode?: import("@/lib/canvas-elements").FrameLabelMode; presentation?: import("@/lib/canvas-v2").FramePresentation }) => {},
rotating: null as string | null, rotate: (_id: string | null) => {},
resizing: null as string | null, resize: (_id: string | null) => {},
};
type GestureHooks = 'beginTransform' | 'finishTransform' | 'cancelTransform' | 'endEditing';
export const NodeEditing = createContext<Omit<typeof defaultEditing, GestureHooks> & Partial<Pick<typeof defaultEditing, GestureHooks>>>(defaultEditing);
export {NodeEditing} from "./node-editing";
import {useScopedNodeEditing} from "./node-editing";
type Gesture = { lease?: EditingLease<NodeTransform>; node: CanvasNode; start: XYPosition; direction: ResizeDirection | "rotate"; capture: HTMLElement; pointerId: number };
export function TransformableNode(props: NodeProps<CanvasNode> & { children: ReactNode; className?: string; hideStyleToolbar?: boolean }) {
const { id, data, selected, width, height, children, className = "" } = props;
const editing = useContext(NodeEditing);
const editing = useScopedNodeEditing(id,data.__headlessTarget);
const flow = useReactFlow<CanvasNode>();
const update = useUpdateNodeInternals();
const { zoom } = useViewport();
@@ -129,7 +116,7 @@ export function TransformableNode(props: NodeProps<CanvasNode> & { children: Rea
</>;
}
export function ResizeToggle({ id }: { id: string }) {
const editing = useContext(NodeEditing);
export function ResizeToggle({ id,renderedTarget }: { id: string;renderedTarget?:import("@/lib/canvas-bindings/compat-types").RenderedTarget }) {
const editing = useScopedNodeEditing(id,renderedTarget);
return <button type="button" data-resize-toggle={id} aria-label="八向拉伸模式" title="八向拉伸 · Shift 固定比例" aria-pressed={editing.resizing === id} onClick={() => editing.resize(editing.resizing === id ? null : id)}><Maximize2 size={16} /></button>;
}