feat: complete TASK-WP5-02 sticker catalog
This commit is contained in:
@@ -49,6 +49,7 @@ import {
|
||||
type DynamicTemplateId,
|
||||
} from "./dynamic-provider.js";
|
||||
import { dynamicFontOptionsFor } from "./dynamic-render-models.js";
|
||||
import { P0A_STATIC_STICKER_CATALOG, P0A_STATIC_STICKER_COUNT, stickerWindow } from "./static-sticker-catalog.js";
|
||||
import {
|
||||
createColorCardElement,
|
||||
extractPaletteFromImage,
|
||||
@@ -112,11 +113,6 @@ function formatDate(value: string) {
|
||||
return new Intl.DateTimeFormat("zh-CN", { month: "numeric", day: "numeric", hour: "2-digit", minute: "2-digit" }).format(new Date(value));
|
||||
}
|
||||
|
||||
const stickerFixtures = [
|
||||
{ assetId: "STK001", label: "part1 原版草莓" },
|
||||
{ assetId: "STK002", label: "part2 原版小狗" },
|
||||
] as const;
|
||||
|
||||
function newElementIdentity(): CanvasElementIdentity {
|
||||
return { createdAt: new Date().toISOString(), elementId: crypto.randomUUID() };
|
||||
}
|
||||
@@ -139,6 +135,7 @@ export function EditorPage({ projectId }: { projectId: string }) {
|
||||
const [pendingBackground, setPendingBackground] = useState<string>();
|
||||
const [notice, setNotice] = useState("");
|
||||
const [activePanel, setActivePanel] = useState<EditorAssetPanel>("background");
|
||||
const [stickerScrollTop, setStickerScrollTop] = useState(0);
|
||||
const [selectedIds, setSelectedIds] = useState<string[]>([]);
|
||||
const [guides, setGuides] = useState<string[]>([]);
|
||||
const [multiMode, setMultiMode] = useState(false);
|
||||
@@ -816,7 +813,7 @@ export function EditorPage({ projectId }: { projectId: string }) {
|
||||
{ label: "普通贴纸", panel: "stickers" as const },
|
||||
{ label: "色卡", panel: "color" as const },
|
||||
{ label: "动态贴纸", panel: "dynamic" as const },
|
||||
]).map((item) => <button aria-current={item.panel === activePanel ? "page" : undefined} key={item.label} onClick={() => setActivePanel(item.panel)} type="button">{item.label}</button>)}
|
||||
]).map((item) => <button aria-current={item.panel === activePanel ? "page" : undefined} key={item.label} onClick={() => { setActivePanel(item.panel); if (item.panel === "stickers") setStickerScrollTop(0); }} type="button">{item.label}</button>)}
|
||||
</nav>
|
||||
{activePanel === "background" ? <section><h2>底图</h2><button className="editor-source active" type="button"><span className="editor-thumb" style={{ backgroundImage: `url(${imageUrl})` }} /><span>当前底图</span></button></section> : null}
|
||||
{activePanel === "history" ? <section><h2>历史</h2><div className="editor-history-list">
|
||||
@@ -834,9 +831,16 @@ export function EditorPage({ projectId }: { projectId: string }) {
|
||||
templates={P0A_TEXT_TEMPLATES}
|
||||
{...(templateCategory ? { category: templateCategory } : {})}
|
||||
/> : null}
|
||||
{activePanel === "stickers" ? <section><h2>普通贴纸</h2><div className="editor-sticker-grid">
|
||||
{stickerFixtures.map((sticker) => <button aria-label={`添加贴纸 ${sticker.assetId}`} disabled={!canEdit || canvasState.elements.length >= 50} key={sticker.assetId} onClick={() => addSticker(sticker.assetId)} type="button"><img alt="" className="editor-sticker-preview" src={`/api/v1/assets/public/fixture-v1/${sticker.assetId}`} /><strong>{sticker.assetId}</strong><span>{sticker.label}</span></button>)}
|
||||
</div>{canvasState.elements.length >= 50 ? <p className="editor-limit" role="status">画布最多 50 个元素,请先删除现有元素。</p> : null}</section> : null}
|
||||
{activePanel === "stickers" ? (() => {
|
||||
const visibleStickers = stickerWindow(P0A_STATIC_STICKER_CATALOG, stickerScrollTop, 280);
|
||||
return <section><h2>普通贴纸</h2><p aria-live="polite" className="editor-sticker-count">共 {P0A_STATIC_STICKER_COUNT.toLocaleString("zh-CN")} 张</p><div className="editor-sticker-virtual-list" data-testid="static-sticker-list" onScroll={(event) => setStickerScrollTop(event.currentTarget.scrollTop)} role="list">
|
||||
<div style={{ paddingTop: visibleStickers.top_spacer_px, paddingBottom: visibleStickers.bottom_spacer_px }}>
|
||||
<div className="editor-sticker-grid">
|
||||
{visibleStickers.items.map((sticker) => <button aria-label={`添加贴纸 ${sticker.stable_id}`} data-sticker-id={sticker.stable_id} disabled={!canEdit || canvasState.elements.length >= 50} key={sticker.stable_id} onClick={() => addSticker(sticker.stable_id)} type="button"><img alt="" className="editor-sticker-preview" decoding="async" loading="lazy" src={sticker.thumbnail_reference.url} /><strong>{sticker.stable_id}</strong><span>part{sticker.part} · {sticker.order}</span></button>)}
|
||||
</div>
|
||||
</div>
|
||||
</div>{canvasState.elements.length >= 50 ? <p className="editor-limit" role="status">画布最多 50 个元素,请先删除现有元素。</p> : null}</section>;
|
||||
})() : null}
|
||||
{activePanel === "color" ? <ColorCardPanel canAdd={canEdit && canvasState.elements.length < 50} hasBackground={Boolean(canvasState.background.asset_id)} onAdd={(definition) => { void addColorCard(definition); }} /> : null}
|
||||
{activePanel === "dynamic" ? <DynamicStickerPanel canAdd={canEdit && canvasState.elements.length < 50} fontStatuses={fontStatuses} onAdd={chooseDynamicSticker} /> : null}
|
||||
</aside>
|
||||
|
||||
Reference in New Issue
Block a user