继续收口平台空态与动作按钮
作品架异步状态切换复用 PlatformAsyncStatePanel 复制反馈动作外观改为组合 PlatformActionButton 结果页与调试面板空态继续收口到 PlatformEmptyState 暗色私聊与工坊按钮改为复用 PlatformActionButton 更新 PlatformUiKit 收口文档与团队决策记录
This commit is contained in:
@@ -847,6 +847,31 @@ test('creation hub works-only tab filters bark battle draft and published works'
|
||||
expect(onOpenBarkBattleDetail).toHaveBeenCalledWith(barkBattlePublishedItem);
|
||||
});
|
||||
|
||||
test('creation hub keeps filtered empty copy when selected tab has no works', async () => {
|
||||
const user = userEvent.setup();
|
||||
|
||||
render(
|
||||
<CustomWorldCreationHub
|
||||
mode="works-only"
|
||||
items={[]}
|
||||
barkBattleItems={[barkBattlePublishedItem]}
|
||||
loading={false}
|
||||
error={null}
|
||||
onRetry={() => {}}
|
||||
onCreateType={noopCreateType}
|
||||
onOpenDraft={() => {}}
|
||||
onEnterPublished={() => {}}
|
||||
entryConfig={testEntryConfig}
|
||||
creationTypes={testCreationTypes}
|
||||
/>,
|
||||
);
|
||||
|
||||
await user.click(screen.getByRole('tab', { name: '草稿 0' }));
|
||||
|
||||
expect(screen.getByText('当前筛选下没有内容')).toBeTruthy();
|
||||
expect(screen.queryByText('还没有作品')).toBeNull();
|
||||
});
|
||||
|
||||
test('creation hub published work delete action stays in revealed side actions', async () => {
|
||||
const user = userEvent.setup();
|
||||
const onDeletePuzzle = vi.fn();
|
||||
|
||||
@@ -3,6 +3,7 @@ import { useEffect, useMemo, useState } from 'react';
|
||||
import { resolveSelectionStageFromPath } from '../../routing/appPageRoutes';
|
||||
import type { CreationEntryConfig } from '../../services/creationEntryConfigService';
|
||||
import { PlatformActionButton } from '../common/PlatformActionButton';
|
||||
import { PlatformAsyncStatePanel } from '../common/PlatformAsyncStatePanel';
|
||||
import { PlatformEmptyState } from '../common/PlatformEmptyState';
|
||||
import { PlatformSubpanel } from '../common/PlatformSubpanel';
|
||||
import type { PublishShareModalPayload } from '../common/publishShareModalModel';
|
||||
@@ -206,6 +207,7 @@ export function CustomWorldCreationHub({
|
||||
const recentCreationTypeIds = [
|
||||
...new Set(recentWorkItems.map((item) => item.kind)),
|
||||
];
|
||||
const isWorkShelfEmpty = !loading && filteredItems.length === 0;
|
||||
|
||||
function handleOpenShelfItem(item: CreationWorkShelfItem) {
|
||||
onOpenShelfItem?.(item);
|
||||
@@ -238,6 +240,55 @@ export function CustomWorldCreationHub({
|
||||
|
||||
const showStartCard = mode !== 'works-only';
|
||||
const showWorkShelf = mode !== 'start-only';
|
||||
const workShelfLoadingState = (
|
||||
<div className={WORK_GRID_CLASS}>
|
||||
{Array.from({ length: 3 }).map((_, index) => (
|
||||
<PlatformSubpanel
|
||||
as="div"
|
||||
key={`skeleton-${index}`}
|
||||
padding="sm"
|
||||
radius="md"
|
||||
className="min-h-[10.5rem] sm:min-h-[12rem] sm:p-5"
|
||||
>
|
||||
<div className="h-4 w-20 rounded-full bg-[var(--platform-track-fill)]" />
|
||||
<div className="mt-5 h-6 w-24 rounded-full bg-[var(--platform-track-fill)] sm:mt-6 sm:h-8 sm:w-36" />
|
||||
<div className="mt-3 h-3 w-full rounded-full bg-[var(--platform-track-fill)] sm:mt-4 sm:h-4" />
|
||||
<div className="mt-2 h-4 w-4/5 rounded-full bg-[var(--platform-track-fill)]" />
|
||||
<div className="mt-6 flex flex-col gap-2 sm:mt-8 sm:flex-row">
|
||||
<div className="h-6 w-16 rounded-full bg-[var(--platform-track-fill)] sm:h-7 sm:w-20" />
|
||||
<div className="h-6 w-16 rounded-full bg-[var(--platform-track-fill)] sm:h-7 sm:w-20" />
|
||||
</div>
|
||||
</PlatformSubpanel>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
const workShelfEmptyState = (
|
||||
<EmptyState
|
||||
title={shelfItems.length === 0 ? '还没有作品' : '当前筛选下没有内容'}
|
||||
/>
|
||||
);
|
||||
const workShelfContent = (
|
||||
<div className={WORK_GRID_CLASS}>
|
||||
{filteredItems.map((item) => (
|
||||
<CustomWorldWorkCard
|
||||
key={`${item.kind}-${item.id}`}
|
||||
item={item}
|
||||
previousMetricValues={metricSnapshot[buildWorkMetricCacheItemKey(item)]}
|
||||
onOpen={() => {
|
||||
handleOpenShelfItem(item);
|
||||
}}
|
||||
onDelete={buildDeleteAction(item)}
|
||||
deleteBusy={deletingWorkId === item.id}
|
||||
onShare={buildShareAction(item)}
|
||||
onClaimPointIncentive={buildPointIncentiveAction(item)}
|
||||
pointIncentiveBusy={
|
||||
item.source.kind === 'puzzle' &&
|
||||
claimingPuzzleProfileId === item.source.item.profileId
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="platform-remap-surface w-full space-y-4 px-3 pb-4 pt-3 sm:px-4 sm:pt-4 xl:px-5 xl:pb-5 xl:pt-5">
|
||||
@@ -276,55 +327,14 @@ export function CustomWorldCreationHub({
|
||||
) : null}
|
||||
|
||||
{showWorkShelf ? (
|
||||
loading ? (
|
||||
<div className={WORK_GRID_CLASS}>
|
||||
{Array.from({ length: 3 }).map((_, index) => (
|
||||
<PlatformSubpanel
|
||||
as="div"
|
||||
key={`skeleton-${index}`}
|
||||
padding="sm"
|
||||
radius="md"
|
||||
className="min-h-[10.5rem] sm:min-h-[12rem] sm:p-5"
|
||||
>
|
||||
<div className="h-4 w-20 rounded-full bg-[var(--platform-track-fill)]" />
|
||||
<div className="mt-5 h-6 w-24 rounded-full bg-[var(--platform-track-fill)] sm:mt-6 sm:h-8 sm:w-36" />
|
||||
<div className="mt-3 h-3 w-full rounded-full bg-[var(--platform-track-fill)] sm:mt-4 sm:h-4" />
|
||||
<div className="mt-2 h-4 w-4/5 rounded-full bg-[var(--platform-track-fill)]" />
|
||||
<div className="mt-6 flex flex-col gap-2 sm:mt-8 sm:flex-row">
|
||||
<div className="h-6 w-16 rounded-full bg-[var(--platform-track-fill)] sm:h-7 sm:w-20" />
|
||||
<div className="h-6 w-16 rounded-full bg-[var(--platform-track-fill)] sm:h-7 sm:w-20" />
|
||||
</div>
|
||||
</PlatformSubpanel>
|
||||
))}
|
||||
</div>
|
||||
) : filteredItems.length > 0 ? (
|
||||
<div className={WORK_GRID_CLASS}>
|
||||
{filteredItems.map((item) => (
|
||||
<CustomWorldWorkCard
|
||||
key={`${item.kind}-${item.id}`}
|
||||
item={item}
|
||||
previousMetricValues={
|
||||
metricSnapshot[buildWorkMetricCacheItemKey(item)]
|
||||
}
|
||||
onOpen={() => {
|
||||
handleOpenShelfItem(item);
|
||||
}}
|
||||
onDelete={buildDeleteAction(item)}
|
||||
deleteBusy={deletingWorkId === item.id}
|
||||
onShare={buildShareAction(item)}
|
||||
onClaimPointIncentive={buildPointIncentiveAction(item)}
|
||||
pointIncentiveBusy={
|
||||
item.source.kind === 'puzzle' &&
|
||||
claimingPuzzleProfileId === item.source.item.profileId
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
) : shelfItems.length === 0 ? (
|
||||
<EmptyState title="还没有作品" />
|
||||
) : (
|
||||
<EmptyState title="当前筛选下没有内容" />
|
||||
)
|
||||
<PlatformAsyncStatePanel
|
||||
isLoading={loading}
|
||||
loadingState={workShelfLoadingState}
|
||||
isEmpty={isWorkShelfEmpty}
|
||||
emptyState={workShelfEmptyState}
|
||||
>
|
||||
{workShelfContent}
|
||||
</PlatformAsyncStatePanel>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user