ai-chatbot/components/flow-board/xmind-marker-panel.tsx

47 lines
3.5 KiB
TypeScript

"use client";
import { useState } from "react";
import { ChevronDown, Search, X } from "lucide-react";
import { XMIND_MARKER_GROUPS, type XmindMarker } from "@/lib/flow-board/xmind-markers";
export function XmindMarkerPanel({ selectedCount, activeIds, mixedIds, onApply, onClear, onClose }: {
selectedCount: number;
activeIds: string[];
mixedIds: string[];
onApply: (marker: XmindMarker) => void;
onClear: () => void;
onClose: () => void;
}) {
const [query, setQuery] = useState("");
const [showAll, setShowAll] = useState(false);
const [collapsed, setCollapsed] = useState<string[]>([]);
const needle = query.trim().toLowerCase();
const groups = XMIND_MARKER_GROUPS.filter(group => showAll || needle || !group.hidden).map(group => ({
...group, markers: group.markers.filter(marker => (showAll || needle || !marker.hidden) && (!needle || `${group.label} ${marker.name} ${marker.id}`.toLowerCase().includes(needle))),
})).filter(group => group.markers.length);
return <aside className="xmind-format-panel xmind-marker-panel" aria-label="标记面板" data-flow-board-shell-control="true"
onPointerDownCapture={event => event.stopPropagation()} onMouseDownCapture={event => event.stopPropagation()}>
<div className="xmind-panel-heading"><span>标记</span><button type="button" aria-label="关闭标记面板" onClick={onClose}><X size={16} /></button></div>
<div className="xmind-marker-search"><Search size={14} /><input aria-label="搜索标记" placeholder="搜索标记" value={query} onChange={event => setQuery(event.target.value)} /></div>
<p className="xmind-marker-selection" role="status">{selectedCount ? `已选中 ${selectedCount} 个主题` : "先选中主题,再添加标记"}</p>
<div className="xmind-panel-content">
{groups.map(group => <section className="xmind-marker-group" key={group.id}>
<button type="button" className="xmind-marker-group-heading" aria-expanded={!collapsed.includes(group.id) || Boolean(needle)} onClick={() => setCollapsed(ids => ids.includes(group.id) ? ids.filter(id => id !== group.id) : [...ids, group.id])}>
<ChevronDown size={13} /><span>{group.label}</span><span>{group.markers.length}</span>
</button>
{!collapsed.includes(group.id) || needle ? <div className="xmind-marker-grid">
{group.markers.map(marker => <button type="button" key={marker.id} data-marker-id={marker.id}
aria-label={marker.id.startsWith("priority-") ? marker.name : `${group.label} ${marker.name}`}
title={`${group.label} · ${marker.name}`} aria-pressed={activeIds.includes(marker.id) ? true : mixedIds.includes(marker.id) ? "mixed" : false}
disabled={!selectedCount} onPointerDown={event => event.preventDefault()} onMouseDown={event => event.preventDefault()} onClick={() => onApply(marker)}>
<img src={marker.asset} alt="" draggable={false} loading="lazy" />
</button>)}
</div> : null}
</section>)}
{!groups.length ? <p className="xmind-marker-empty">没有匹配的标记</p> : null}
{!needle ? <button type="button" className="xmind-marker-more" aria-expanded={showAll} onClick={() => setShowAll(value => !value)}>{showAll ? "收起更多标记" : "更多标记"}</button> : null}
</div>
<div className="xmind-marker-footer"><button type="button" disabled={!selectedCount} onPointerDown={event => event.preventDefault()} onMouseDown={event => event.preventDefault()} onClick={onClear}>清空所选节点标记</button></div>
</aside>;
}