feat(canvas): refine connections, text styles and icon editing
This commit is contained in:
@@ -1,10 +1,11 @@
|
||||
"use client";
|
||||
|
||||
import { useContext } from "react";
|
||||
import { useContext, useRef } from "react";
|
||||
import { Handle, Position, type NodeProps } from "@xyflow/react";
|
||||
import { FileText, Image as ImageIcon, Plus, Type } from "lucide-react";
|
||||
import { elementCapabilities } from "@/lib/canvas-elements";
|
||||
import { nodeDirections, type CanvasNode, type NodeDirection } from "@/lib/canvas";
|
||||
import { nodeDirections, type CanvasNode } from "@/lib/canvas";
|
||||
import { textAppearance } from "./text-appearance";
|
||||
import styles from "./canvas.module.css";
|
||||
import { TransformableNode, NodeEditing } from "./transformable-node";
|
||||
export { NodeEditing } from "./transformable-node";
|
||||
@@ -15,26 +16,29 @@ const labels = { top: "上方", right: "右侧", bottom: "下方", left: "左侧
|
||||
export function MaterialNode(props: NodeProps<CanvasNode>) {
|
||||
const { id, data, selected } = props;
|
||||
const editing = useContext(NodeEditing);
|
||||
const pointerStart = useRef({ x: 0, y: 0 });
|
||||
const Icon = data.kind === "image" ? ImageIcon : data.kind === "script" ? FileText : Type;
|
||||
return (
|
||||
<TransformableNode {...props} className={`${styles.node} ${selected ? styles.selectedNode : ""}`}>
|
||||
<header className={styles.nodeHeading}><Icon size={13} /><span>{data.title}</span></header>
|
||||
<Handle type="target" position={Position.Left} className={styles.legacyHandle} isConnectable={false} />
|
||||
{data.kind === "image" ? <img className={styles.image} src={data.src} alt={data.title} draggable={false} referrerPolicy="no-referrer" /> : (
|
||||
<textarea className="nodrag nowheel" aria-label={data.title} placeholder={data.kind === "script" ? "写下你的脚本…" : "留住一个想法…"}
|
||||
<textarea style={textAppearance(data)} className={`${styles.cardText} nodrag nowheel`} aria-label={data.title} placeholder={data.kind === "script" ? "写下你的脚本…" : "留住一个想法…"}
|
||||
value={data.text} maxLength={100000} onFocus={editing.begin} onChange={(event) => editing.edit(id, event.target.value)} />
|
||||
)}
|
||||
<Handle type="source" position={Position.Right} className={styles.legacyHandle} isConnectable={false} />
|
||||
{nodeDirections.map((direction) => <Handle key={direction} id={direction} type="source" position={positions[direction]}
|
||||
style={direction === "left" || direction === "right" ? { top: "calc(50% + 20px)" } : { left: "calc(50% + 20px)" }} />)}
|
||||
{selected && elementCapabilities(data.kind).branchable && nodeDirections.map((direction) => (
|
||||
<button key={direction} type="button" className={`${styles.branchButton} ${styles["branch_" + direction]} nodrag nopan`}
|
||||
aria-label={`在${labels[direction]}添加节点`} title={`在${labels[direction]}添加节点并连线`}
|
||||
onPointerDown={(event) => event.stopPropagation()}
|
||||
onClick={(event) => { event.stopPropagation(); editing.branch(id, direction); }}>
|
||||
<Plus size={16} strokeWidth={1.6} aria-hidden="true" />
|
||||
</button>
|
||||
))}
|
||||
className={`${styles.centerHandle} ${selected ? styles.plusHandle : ""}`}
|
||||
aria-label={selected ? `在${labels[direction]}添加或连接节点` : `从${labels[direction]}连接节点`} role="button" tabIndex={selected ? 0 : -1}
|
||||
onPointerDown={(event) => { pointerStart.current = { x: event.clientX, y: event.clientY }; }}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
if (selected && elementCapabilities(data.kind).branchable && Math.hypot(event.clientX - pointerStart.current.x, event.clientY - pointerStart.current.y) < 5)
|
||||
editing.branch(id, direction, { x: event.clientX, y: event.clientY });
|
||||
}}
|
||||
onKeyDown={(event) => { if (selected && (event.key === "Enter" || event.key === " ")) { event.preventDefault(); event.stopPropagation(); const rect = event.currentTarget.getBoundingClientRect(); editing.branch(id, direction, { x: rect.x + rect.width / 2, y: rect.y + rect.height / 2 }); } }}>
|
||||
<Plus size={16} strokeWidth={1.6} aria-hidden="true" />
|
||||
</Handle>)}
|
||||
</TransformableNode>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user