130 lines
4.4 KiB
TypeScript
130 lines
4.4 KiB
TypeScript
"use client";
|
|
|
|
import { Trash2, X } from "lucide-react";
|
|
import type { AnnotatedElement } from "@/lib/flow-board/mind-annotations";
|
|
|
|
export function XmindAnnotationPanel({
|
|
element,
|
|
onChange,
|
|
onDelete,
|
|
onClose,
|
|
}: {
|
|
element: AnnotatedElement;
|
|
onChange: (patch: Record<string, unknown>) => void;
|
|
onDelete: () => void;
|
|
onClose: () => void;
|
|
}) {
|
|
const relationship = element.mindAnnotation.kind === "relationship";
|
|
const label = (element.texts?.[0]?.text?.children || [])
|
|
.map((leaf: { text?: string }) => leaf.text || "")
|
|
.join("");
|
|
return (
|
|
<aside
|
|
className="absolute right-0 top-[94px] bottom-9 z-30 w-full max-w-[304px] overflow-y-auto border-l border-[#dfe2e7] bg-white text-[#343b48]"
|
|
data-flow-board-shell-control="true"
|
|
>
|
|
<div className="flex h-12 items-center justify-between border-b border-[#eceef1] px-4">
|
|
<h2 className="text-sm font-semibold">
|
|
{relationship ? "联系格式" : "外框格式"}
|
|
</h2>
|
|
<button aria-label="关闭格式面板" onClick={onClose} type="button">
|
|
<X size={16} />
|
|
</button>
|
|
</div>
|
|
<div className="space-y-5 p-4 text-xs">
|
|
{relationship ? (
|
|
<label className="block space-y-2">
|
|
<span>联系文字</span>
|
|
<input
|
|
className="h-9 w-full rounded border border-[#dfe2e7] px-2 text-sm"
|
|
value={label}
|
|
onChange={(event) =>
|
|
onChange({
|
|
texts: event.target.value
|
|
? [
|
|
{
|
|
position: 0.5,
|
|
text: {
|
|
type: "paragraph",
|
|
children: [{ text: event.target.value }],
|
|
},
|
|
},
|
|
]
|
|
: [],
|
|
})
|
|
}
|
|
/>
|
|
</label>
|
|
) : (
|
|
<label className="flex items-center justify-between">
|
|
<span>外框留白</span>
|
|
<select
|
|
aria-label="外框留白"
|
|
className="h-8 rounded border border-[#dfe2e7] px-2"
|
|
value={
|
|
element.mindAnnotation.kind === "boundary"
|
|
? element.mindAnnotation.padding
|
|
: 18
|
|
}
|
|
onChange={(event) =>
|
|
onChange({
|
|
mindAnnotation: {
|
|
...element.mindAnnotation,
|
|
padding: Number(event.target.value),
|
|
},
|
|
})
|
|
}
|
|
>
|
|
{[12, 18, 24, 32].map((value) => (
|
|
<option key={value} value={value}>
|
|
{value} px
|
|
</option>
|
|
))}
|
|
</select>
|
|
</label>
|
|
)}
|
|
<label className="flex items-center justify-between">
|
|
<span>线条颜色</span>
|
|
<input
|
|
aria-label="联系或外框颜色"
|
|
className="h-7 w-12 cursor-pointer"
|
|
type="color"
|
|
value={element.strokeColor || "#64748b"}
|
|
onChange={(event) => onChange({ strokeColor: event.target.value })}
|
|
/>
|
|
</label>
|
|
<label className="flex items-center justify-between">
|
|
<span>线条粗细</span>
|
|
<select
|
|
aria-label="联系或外框粗细"
|
|
className="h-8 rounded border border-[#dfe2e7] px-2"
|
|
value={element.strokeWidth || 2}
|
|
onChange={(event) =>
|
|
onChange({ strokeWidth: Number(event.target.value) })
|
|
}
|
|
>
|
|
{[1, 2, 3, 4].map((value) => (
|
|
<option key={value} value={value}>
|
|
{value} pt
|
|
</option>
|
|
))}
|
|
</select>
|
|
</label>
|
|
<p className="leading-5 text-[#8b919c]">
|
|
{relationship
|
|
? "联系线会跟随主题移动。双击线上的文字也可以编辑。"
|
|
: "外框包含所选主题及其展开的子主题,随分支布局自动调整。"}
|
|
</p>
|
|
<button
|
|
className="flex h-9 w-full items-center justify-center gap-2 rounded border border-[#e5e7eb] text-[#b44343] hover:bg-red-50"
|
|
onClick={onDelete}
|
|
type="button"
|
|
>
|
|
<Trash2 size={14} />
|
|
删除{relationship ? "联系" : "外框"}
|
|
</button>
|
|
</div>
|
|
</aside>
|
|
);
|
|
}
|