Files
tyx_AI_xhs/apps/web/src/text-template-panel.tsx
T

46 lines
3.3 KiB
TypeScript

import type { ArchivedFontStatus } from "./text-font-loader.js";
import { searchTextTemplates, type TextTemplateCategory, type TextTemplateDefinition } from "./text-assets.js";
const categories: Array<{ id?: TextTemplateCategory; label: string }> = [
{ label: "全部" }, { id: "flower", label: "花字" }, { id: "title", label: "标题" }, { id: "tag", label: "标签" }, { id: "simple", label: "简约" },
];
export function TextTemplatePanel(props: {
canAdd: boolean;
category?: TextTemplateCategory;
fontStatuses: Readonly<Record<string, ArchivedFontStatus>>;
onAdd: (template: TextTemplateDefinition) => void;
onCategory: (category?: TextTemplateCategory) => void;
onQuery: (query: string) => void;
onRetry: () => void;
query: string;
recentIds: readonly string[];
templates: readonly TextTemplateDefinition[];
}) {
const visible = searchTextTemplates(props.templates, { ...(props.category ? { category: props.category } : {}), query: props.query });
const recent = props.recentIds.map((id) => props.templates.find((template) => template.templateId === id)).filter((item): item is TextTemplateDefinition => Boolean(item));
const loadFailed = props.templates.some((template) => template.available && props.fontStatuses[template.defaultFontId] === "unavailable");
return <section className="editor-text-assets">
<h2>文字模板</h2>
<input aria-label="搜索文字模板显示名称" className="editor-template-search" onChange={(event) => props.onQuery(event.target.value)} placeholder="搜索文字模板显示名称" type="search" value={props.query} />
<div aria-label="文字模板分类" className="editor-template-categories" role="group">
{categories.map((item) => <button aria-pressed={item.id === props.category || (!item.id && !props.category)} className={item.id === props.category || (!item.id && !props.category) ? "active" : ""} key={item.label} onClick={() => props.onCategory(item.id)} type="button">{item.label}</button>)}
</div>
{loadFailed ? <button className="editor-template-retry" onClick={props.onRetry} type="button">重试文字素材</button> : null}
{recent.length > 0 ? <div aria-label="最近使用文字模板" className="editor-template-recent"><h3>最近使用</h3><div>{recent.map((template) => <span key={template.templateId}><strong>{template.templateId}</strong>{template.displayName}</span>)}</div></div> : null}
<div className="editor-template-grid">
{visible.map((template) => {
const status = props.fontStatuses[template.defaultFontId] ?? "idle";
const unavailable = !template.available || status === "unavailable";
return <button aria-label={`${template.templateId} ${template.displayName}${unavailable ? " 素材暂不可用" : ""}`} disabled={!props.canAdd || unavailable || status === "loading"} key={template.templateId} onClick={() => props.onAdd(template)} type="button">
<span className={`editor-template-mark ${template.category}`}>{template.displayName.slice(0, 2)}</span>
<strong>{template.templateId}</strong>
<span>{template.displayName}</span>
{unavailable ? <small>素材暂不可用</small> : status === "loading" ? <small>正在加载字体</small> : null}
</button>;
})}
</div>
{visible.length === 0 ? <p className="editor-muted">没有匹配的文字模板</p> : null}
</section>;
}