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
+4 -4
View File
@@ -1,19 +1,19 @@
"use client";
import { useContext } from "react";
import { AnimatePresence, motion, useReducedMotion } from "motion/react";
import { NodeToolbar, Position, type NodeProps } from "@xyflow/react";
import { AlignCenter, AlignLeft, AlignRight, Bold, Italic, RotateCw, Underline } from "lucide-react";
import type { CanvasNode } from "@/lib/canvas";
import type { TextStyle } from "@/lib/canvas-elements";
import { NodeEditing, ResizeToggle } from "./transformable-node";
import { ResizeToggle } from "./transformable-node";
import {useCanvasOverlay} from "./canvas-overlays";
import {useScopedNodeEditing} from "./node-editing";
import styles from "./canvas.module.css";
export function NodeStyleToolbar({ id, data, selected }: NodeProps<CanvasNode>) {
const overlay=useCanvasOverlay(id,"node-style");
const reduce = useReducedMotion();
const editing = useContext(NodeEditing);
const editing = useScopedNodeEditing(id,data.__headlessTarget);
const style = data.textStyle || {};
const text = data.kind === "text" || data.kind === "script" || data.kind === "plain-text";
const patch = (value: TextStyle, fontSize?: number) => editing.style(id, value, fontSize);
@@ -31,7 +31,7 @@ export function NodeStyleToolbar({ id, data, selected }: NodeProps<CanvasNode>)
</>}
{!(["image", "plain-image"].includes(data.kind)) && <label className={styles.colorPicker} title="背景色"><span style={{ background: style.background && style.background !== "transparent" ? style.background : "#ffffff" }} /><input aria-label="背景色" type="color" value={style.background && style.background !== "transparent" ? style.background : "#ffffff"} onChange={(event) => patch({ background: event.target.value })} /></label>}
{!(["image", "plain-image"].includes(data.kind)) && <button title="清除背景" aria-label="清除背景" onClick={() => patch({ background: "transparent" })}>∅</button>}
{data.kind !== "icon" && <ResizeToggle id={id} />}
{data.kind !== "icon" && <ResizeToggle id={id} renderedTarget={data.__headlessTarget} />}
<button data-rotate-toggle={id} title="旋转模式" aria-label="旋转模式" aria-pressed={editing.rotating === id} onClick={() => editing.rotate(editing.rotating === id ? null : id)}><RotateCw size={16} /></button>
</motion.div>}</AnimatePresence>
</NodeToolbar>;