docs(canvas): explain node editing scopes and display interactions

This commit is contained in:
2026-11-12 01:55:38 +08:00
parent b51d78b47d
commit 01c4a9d8c5
15 changed files with 147 additions and 0 deletions
+14
View File
@@ -1,3 +1,7 @@
/**
* 文本、脚本、图片卡片共用的节点组件。组合标题、内容、通用变换容器与四向连线入口。
* 编辑通过作用域动作接口发出,不直接写文档;旧连线的无名连接点仍保留用于兼容定位。
*/
"use client";
import { CanvasImage } from "./canvas-image";
import { managedImageSource } from "@/lib/canvas-v2";
@@ -14,16 +18,26 @@ import styles from "./canvas.module.css";
import { TransformableNode } from "./transformable-node";
export { NodeEditing } from "./transformable-node";
/** 方向字符串到ReactFlow几何方位的映射,与nodeDirections的稳定Handle ID配套。 */
const positions = { top: Position.Top, right: Position.Right, bottom: Position.Bottom, left: Position.Left };
/** 四向连接入口的中文可访问名称,不参与端点数据编码。 */
const labels = { top: "上方", right: "右侧", bottom: "下方", left: "左侧" };
/** 按kind选择图片或文本编辑器,并显示对应标题图标;selected影响样式及连接操作入口。
* 指针点击连接点可打开分支菜单,拖动则留给ReactFlow创建连接。 */
export function MaterialNode(props: NodeProps<CanvasNode>) {
const { id, data, selected } = props;
// Headless目标携带元素生命周期与归属;旧渲染回调经过作用域检查,避免迟到事件编辑已替换的对象。
const editing = useScopedNodeEditing(id,data.__headlessTarget);
const { zoom } = useViewport();
// 以卡片宽度(默认260)×视口缩放×设备像素比估算显示成本;1024以内优先预览源。
const preview = (props.width || 260) * zoom * (typeof window === "undefined" ? 1 : window.devicePixelRatio || 1) <= 1024;
// 仅记录连接点按下的屏幕坐标,用小于5px的位移区分点击与拖动;不把它写进文档或history。
const pointerStart = useRef({ x: 0, y: 0 });
const Icon = data.kind === "image" ? ImageIcon : data.kind === "script" ? FileText : Type;
// 旧target/source Handle禁用新连接但保留定位;四向Handle使用方向作ID,选中时允许开始连线。
// 指针点击还检查branchable能力和位移;键盘Enter/空格使用Handle中心打开菜单,由动作层再处理合法性。
// 点击/按键停止冒泡,避免分支操作同时触发节点或画布交互;文本内容的编辑生命周期由子组件负责。
return (
<TransformableNode {...props} className={`${styles.node} ${selected ? styles.selectedNode : ""}`}>
<header className={styles.nodeHeading}><Icon size={13} /><NodeTitle id={id} renderedTarget={data.__headlessTarget} title={data.title} selected={selected} /></header>