@@ -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-limi t" 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-lis t" 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 >