31 lines
2.7 KiB
TypeScript
31 lines
2.7 KiB
TypeScript
/**
|
|
* 节点标题的局部编辑交互。展示态与输入态切换,提交时调用rename;本地value只是输入草稿。
|
|
*/
|
|
"use client";
|
|
import { useEffect, useRef, useState } from "react";
|
|
import {useScopedNodeEditing} from "./node-editing";
|
|
import styles from "./canvas.module.css";
|
|
/** 双击先选择节点再编辑,键盘F2/Enter也可进入;选中时标题才加入正常Tab顺序。
|
|
* renderedTarget把后续rename绑定到当前渲染的元素生命周期与位置。 */
|
|
export function NodeTitle({ id, title, selected,renderedTarget }: { id: string; title: string; selected: boolean;renderedTarget?:import("@/lib/canvas-bindings/compat-types").RenderedTarget }) {
|
|
const actions = useScopedNodeEditing(id,renderedTarget);
|
|
const [editing, setEditing] = useState(false), [value, setValue] = useState(title);
|
|
// cancelled用于Escape触发blur时抑制保存;input用于进入编辑后聚焦并选中全部文字。
|
|
const input = useRef<HTMLInputElement>(null), cancelled = useRef(false);
|
|
// 展示态跟随外部标题,输入态保留当前草稿;失去选择会退出编辑,进入编辑则集中处理焦点。
|
|
useEffect(() => { if (!editing) setValue(title); }, [title, editing]);
|
|
useEffect(() => { if (!selected) setEditing(false); }, [selected]);
|
|
useEffect(() => { if (editing) { input.current?.focus(); input.current?.select(); } }, [editing]);
|
|
/** blur是统一提交入口:忽略取消、空名称与无变化名称,随后复位取消标记并退出输入态。 */
|
|
function save() {
|
|
if (!cancelled.current) { const name = value.trim(); if (name && name !== title) actions.rename(id, name); }
|
|
cancelled.current = false; setEditing(false);
|
|
}
|
|
// Enter在非输入法组合态触发blur保存,Escape先置取消标记再blur;按键不继续冒泡到画布。
|
|
// 标题长度限制300字符,nodrag/nopan/nowheel避免文本操作被当成画布手势。
|
|
if (editing) return <input ref={input} className={`${styles.nodeTitleInput} nodrag nopan nowheel`} value={value} maxLength={300} aria-label="节点名称" onChange={(event) => setValue(event.target.value)} onBlur={save}
|
|
onKeyDown={(event) => { event.stopPropagation(); if (event.key === "Enter" && !event.nativeEvent.isComposing) event.currentTarget.blur(); if (event.key === "Escape") { cancelled.current = true; event.currentTarget.blur(); } }} />;
|
|
return <span className={styles.nodeTitle} title="双击编辑名称" role="button" tabIndex={selected ? 0 : -1} onDoubleClick={(event) => { event.preventDefault(); event.stopPropagation(); actions.select(id); setEditing(true); }}
|
|
onKeyDown={(event) => { if (event.key === "F2" || event.key === "Enter") { event.preventDefault(); event.stopPropagation(); setEditing(true); } }}>{title}</span>;
|
|
}
|