继续收口编辑器空态与暗色动作按钮
视觉小说实体列表空态复用 PlatformEmptyState 角色素材工作室局部按钮改为委托 PlatformActionButton RPG大编辑器局部按钮改为委托 PlatformActionButton 更新 PlatformUiKit 收口文档与团队决策记录
This commit is contained in:
@@ -2077,8 +2077,8 @@
|
||||
- 决策:平台入口的创作前置泥点阻断提示只在 `platform-entry` 局部抽成 `src/components/platform-entry/PlatformDraftGenerationPointNoticeDialog.tsx`,并使用 `DraftGenerationPointNotice` union(`insufficient-points` / `balance-load-failed`)承接业务真相;不要在 `common/` 再抽一个泛化 `BlockingNoticeDialog`,否则会把 `PlatformAcknowledgeStatusDialog` 的样式透传再包装一层而不缩小调用面。
|
||||
- 决策:`PlatformAsyncStatePanel` 从 profile modal 扩展到作品架类白底 panel;`CustomWorldCreationHub.tsx` 的作品架主体现在也统一走 `loadingState / emptyState / children` 三段 slot,但 error + 重试继续留在业务层外侧,不把共享组件扩成“banner + retry + content”全能状态机。后续白底作品架或列表 panel 若只是互斥的 `loading / empty / content`,优先直接复用这套骨架。
|
||||
- 决策:`CopyFeedbackButton.tsx` 的 `actionSurface` 分支继续收口到 `PlatformActionButton`,`pill` 分支继续保留 `PlatformPillBadge` 风格;复制反馈按钮不再直接调用 `getPlatformActionButtonClassName` 手拼平台按钮基础 chrome。后续同类“复制状态机 + 平台动作按钮”组合优先直接复用 `CopyFeedbackButton`,不要在业务页重新混写图标、文案、aria 和动作按钮 class。
|
||||
- 决策:白底 / 暗色面板里的轻量空态和普通 CTA 继续向共享组件收口。`PuzzleResultView.tsx` 的缺草稿提示、`RpgCreationAssetDebugPanel.tsx` 的空诊断提示改为 `PlatformEmptyState`,`Match3DResultView.tsx` 的引用素材列表直接复用 `PlatformAssetPickerGrid` 自己的空态;`AdventureEntityModal.tsx` 的私聊按钮和 `InventoryPanel.tsx` 的锻造 / 合成按钮改为 `PlatformActionButton surface="editorDark"`,业务页只贴局部 sky / emerald 皮肤。后续白底子面板里的只读空态优先使用 `PlatformEmptyState surface="subpanel" size="inline"`,暗色编辑 / 运行面板里的普通动作优先使用 `PlatformActionButton surface="editorDark"`。
|
||||
- 验证方式:`npm run test -- src/components/common/PlatformAsyncStatePanel.test.tsx src/components/platform-entry/PlatformProfileReferralModal.test.tsx src/components/platform-entry/PlatformProfileWalletLedgerModal.test.tsx src/components/platform-entry/PlatformProfilePlayedWorksModal.test.tsx src/components/platform-entry/PlatformProfileTaskCenterModal.test.tsx src/components/platform-entry/PlatformProfileRechargeModal.test.tsx src/components/common/PlatformSegmentedTabs.test.tsx src/components/custom-world-home/CustomWorldCreationHub.test.tsx src/components/custom-world-home/CustomWorldCreationHub.interaction.test.tsx`、`npm run test -- src/components/common/PlatformModalCloseButton.test.tsx src/components/PixelCloseButton.test.tsx src/components/CharacterChatModal.test.tsx src/components/MapModal.test.tsx`、`npm run test -- src/components/common/useMudPointConfirmController.test.tsx src/components/match3d-result/Match3DResultView.test.tsx src/components/unified-creation/workspaces/PuzzleCreationWorkspace.interaction.test.tsx src/components/unified-creation/workspaces/Match3DCreationWorkspace.interaction.test.tsx src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx src/components/platform-entry/PlatformProfileRechargeModal.test.tsx src/components/CustomWorldEntityEditorModal.test.tsx src/components/rpg-creation-result/RpgCreationResultActionBar.test.tsx src/components/unified-creation/shared/PuzzleHistoryAssetPickerDialog.test.tsx src/components/puzzle-result/PuzzleResultView.test.tsx`、`npm run test -- src/components/common/CopyFeedbackButton.test.tsx src/components/common/PlatformActionButton.test.tsx src/components/AdventureEntityModal.test.tsx src/components/InventoryPanel.test.tsx src/components/rpg-creation-result/RpgCreationAssetDebugPanel.test.tsx`、`npm run typecheck`、`npm run check:encoding`、`git diff --check`。
|
||||
- 决策:白底 / 暗色面板里的轻量空态和普通 CTA 继续向共享组件收口。`PuzzleResultView.tsx` 的缺草稿提示、`RpgCreationAssetDebugPanel.tsx` 的空诊断提示、`VisualNovelEntityGrid` 的空实体列表都改为 `PlatformEmptyState`,`Match3DResultView.tsx` 的引用素材列表直接复用 `PlatformAssetPickerGrid` 自己的空态;`AdventureEntityModal.tsx` 的私聊按钮、`InventoryPanel.tsx` 的锻造 / 合成按钮,以及 `RpgCreationRoleAssetStudioModalImpl.tsx`、`RpgCreationEntityEditorShared.tsx` 里的局部 `ActionButton` 包装层都改为委托 `PlatformActionButton surface="editorDark"`。后续白底子面板里的只读空态优先使用 `PlatformEmptyState surface="subpanel" size="inline"`;暗色编辑 / 运行面板里的普通动作优先使用 `PlatformActionButton surface="editorDark"`,若业务仍需 `stopPropagation`、tone 映射或局部排版,可保留薄包装层,但不要再直接写原生 `<button>` 基础 chrome。
|
||||
- 验证方式:`npm run test -- src/components/common/PlatformAsyncStatePanel.test.tsx src/components/platform-entry/PlatformProfileReferralModal.test.tsx src/components/platform-entry/PlatformProfileWalletLedgerModal.test.tsx src/components/platform-entry/PlatformProfilePlayedWorksModal.test.tsx src/components/platform-entry/PlatformProfileTaskCenterModal.test.tsx src/components/platform-entry/PlatformProfileRechargeModal.test.tsx src/components/common/PlatformSegmentedTabs.test.tsx src/components/custom-world-home/CustomWorldCreationHub.test.tsx src/components/custom-world-home/CustomWorldCreationHub.interaction.test.tsx`、`npm run test -- src/components/common/PlatformModalCloseButton.test.tsx src/components/PixelCloseButton.test.tsx src/components/CharacterChatModal.test.tsx src/components/MapModal.test.tsx`、`npm run test -- src/components/common/useMudPointConfirmController.test.tsx src/components/match3d-result/Match3DResultView.test.tsx src/components/unified-creation/workspaces/PuzzleCreationWorkspace.interaction.test.tsx src/components/unified-creation/workspaces/Match3DCreationWorkspace.interaction.test.tsx src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx src/components/platform-entry/PlatformProfileRechargeModal.test.tsx src/components/CustomWorldEntityEditorModal.test.tsx src/components/rpg-creation-result/RpgCreationResultActionBar.test.tsx src/components/unified-creation/shared/PuzzleHistoryAssetPickerDialog.test.tsx src/components/puzzle-result/PuzzleResultView.test.tsx`、`npm run test -- src/components/common/CopyFeedbackButton.test.tsx src/components/common/PlatformActionButton.test.tsx src/components/AdventureEntityModal.test.tsx src/components/InventoryPanel.test.tsx src/components/rpg-creation-result/RpgCreationAssetDebugPanel.test.tsx src/components/visual-novel-result/VisualNovelResultView.test.tsx src/components/common/PlatformEmptyState.test.tsx src/components/rpg-creation-asset-studio/RpgCreationRoleAssetStudioModal.test.tsx`、`npm run typecheck`、`npm run check:encoding`、`git diff --check`。
|
||||
|
||||
## 2026-05-26 敲木鱼发布后作品架与推荐流刷新口径
|
||||
|
||||
|
||||
@@ -218,7 +218,7 @@
|
||||
18.7.13. creation-agent 工作台聊天区外壳迁移到 `PlatformSubpanel radius="xl" padding="none"`;消息列表、上传预览、错误提示和输入区继续由工作台组件持有。
|
||||
18.7.14. 绑定手机号页左侧的“当前登录身份”提示块迁移到 `PlatformSubpanel as="div" radius="sm" padding="md"`;认证页只保留品牌说明、当前用户显示名和绑定流程,不再手写 `platform-subpanel` 信息块外壳。
|
||||
18.8. 平台标签编辑器迁移到 `PlatformTagEditor`;拼图、敲木鱼和抓大鹅结果页标签编辑已先迁移。后续标签编辑只把 parse / normalize 和保存语义留在业务页,新增输入状态、删除 chip、空态、AI 生成按钮和错误提示统一由 Module 承接。
|
||||
19. 个人中心充值、任务、兑换、邀请、支付结果等弹窗里的普通主动作按钮迁移到 `PlatformActionButton surface="profile"`;RPG 首页作品卡删除小动作、RPG 作品详情、RPG / 拼图 / 抓大鹅 / 跳一跳 / 敲木鱼 / 拼消消 / 宝贝识物 / 方洞 / 汪汪声浪 / 视觉小说 / 大鱼吃小鱼结果页、自定义世界实体目录小动作、生成结果恢复面板、通用生成页重试 / 中断动作、法律信息弹窗 footer、公共确认弹窗 footer、统一创作工作台、统一创作页壳层、拼图创作工作台、拼消消创作工作台、宝贝识物创作工作台、视觉小说创作工作台、汪汪声浪创作工作台、creation-agent 推荐回复、creative-agent 工作台、creative-agent 模板确认弹窗、创作中心错误重试、创作中心作品卡积分激励领取按钮、反馈页 header 返回、通用创作输入面板、认证表单、敲木鱼 fallback 返回、跳一跳结算、拼消消 runtime header / 结算弹窗和视觉小说 runtime 普通白底面板里的普通主动作 / 次动作 / 危险动作迁移到 `PlatformActionButton surface="platform"`;RPG 暗色弹窗 / 运行面板中的角色自定义 footer、生成 footer、地图切换确认、营地编组普通动作和角色聊天刷新动作迁移到 `PlatformActionButton surface="editorDark"`;RPG 大编辑器暗色面板内的保存 / 角色槽动作继续走本地 `ActionButton`,不再混用白底平台 `platform-button` class。统一创作工作台、统一创作页壳层、玩法创作工作台、结果页返回按钮和反馈页 header 返回使用 `tone="ghost"`,提交 / 生成 / 发布 / 保存按钮使用默认主动作,素材槽小按钮、作品卡角落小动作、拼图图片生成模式选择器触发器和白底面板行内动作使用 `size="xs"` 与 `shape="pill"`,积分激励领取这类密集卡片小动作使用 `size="xxs"` 并由局部卡片 class 保留响应式布局,暗色微型刷新动作使用 `size="xxs" shape="pill"`,左对齐回复 / 列表动作使用 `align="start"`,认证表单提交、验证码、第三方登录和邀请码提交按钮使用 `size="lg"` 保持 48px 高度,文件上传 label 使用 `asChild="label"` 保持上传语义;复制邀请、错误复制、完成复制和分享复制继续使用 `CopyFeedbackButton` 管状态,并通过 `actionSurface` 复用动作按钮外观。大鱼吃小鱼结果页资产工坊 footer、关卡主图 / 动作入口和场地背景生成这类白底平台动作也使用 `shape="pill" size="xs"`,深色 hero 返回 / 测试 / 发布按钮保留玩法品牌布局。后续带复制三态的按钮不改用普通 ActionButton,避免复制状态分支回流业务页;暗色可选项卡继续使用 `PlatformDarkOptionCard`,像素风发送按钮和强品牌动作继续保留专用布局。
|
||||
19. 个人中心充值、任务、兑换、邀请、支付结果等弹窗里的普通主动作按钮迁移到 `PlatformActionButton surface="profile"`;RPG 首页作品卡删除小动作、RPG 作品详情、RPG / 拼图 / 抓大鹅 / 跳一跳 / 敲木鱼 / 拼消消 / 宝贝识物 / 方洞 / 汪汪声浪 / 视觉小说 / 大鱼吃小鱼结果页、自定义世界实体目录小动作、生成结果恢复面板、通用生成页重试 / 中断动作、法律信息弹窗 footer、公共确认弹窗 footer、统一创作工作台、统一创作页壳层、拼图创作工作台、拼消消创作工作台、宝贝识物创作工作台、视觉小说创作工作台、汪汪声浪创作工作台、creation-agent 推荐回复、creative-agent 工作台、creative-agent 模板确认弹窗、创作中心错误重试、创作中心作品卡积分激励领取按钮、反馈页 header 返回、通用创作输入面板、认证表单、敲木鱼 fallback 返回、跳一跳结算、拼消消 runtime header / 结算弹窗和视觉小说 runtime 普通白底面板里的普通主动作 / 次动作 / 危险动作迁移到 `PlatformActionButton surface="platform"`;RPG 暗色弹窗 / 运行面板中的角色自定义 footer、生成 footer、地图切换确认、营地编组普通动作、角色聊天刷新动作、角色素材工作室本地 `ActionButton`,以及 RPG 大编辑器暗色面板内的保存 / 角色槽动作都迁移到 `PlatformActionButton surface="editorDark"`。若业务侧仍需要 `stopPropagation`、局部 tone 映射或内容排版差异,可以保留局部 `ActionButton` 包装层,但包装层本体应委托共享按钮,而不是继续直接渲染原生 `<button>`。统一创作工作台、统一创作页壳层、玩法创作工作台、结果页返回按钮和反馈页 header 返回使用 `tone="ghost"`,提交 / 生成 / 发布 / 保存按钮使用默认主动作,素材槽小按钮、作品卡角落小动作、拼图图片生成模式选择器触发器和白底面板行内动作使用 `size="xs"` 与 `shape="pill"`,积分激励领取这类密集卡片小动作使用 `size="xxs"` 并由局部卡片 class 保留响应式布局,暗色微型刷新动作使用 `size="xxs" shape="pill"`,左对齐回复 / 列表动作使用 `align="start"`,认证表单提交、验证码、第三方登录和邀请码提交按钮使用 `size="lg"` 保持 48px 高度,文件上传 label 使用 `asChild="label"` 保持上传语义;复制邀请、错误复制、完成复制和分享复制继续使用 `CopyFeedbackButton` 管状态,并通过 `actionSurface` 复用动作按钮外观。大鱼吃小鱼结果页资产工坊 footer、关卡主图 / 动作入口和场地背景生成这类白底平台动作也使用 `shape="pill" size="xs"`,深色 hero 返回 / 测试 / 发布按钮保留玩法品牌布局。后续带复制三态的按钮不改用普通 ActionButton,避免复制状态分支回流业务页;暗色可选项卡继续使用 `PlatformDarkOptionCard`,像素风发送按钮和强品牌动作继续保留专用布局。
|
||||
19.1. `CopyFeedbackButton` 支持 `actionShape`,用于在复用共享复制状态机时直接对齐 `PlatformActionButton` 的圆角外观;拼图广场详情 hero 的“分享作品”已使用 `actionSurface="editorDark" actionShape="pill"`,不再手写复制按钮 rounded / border / bg class。
|
||||
19.2. 拼图广场详情 hero 的返回、上一张 / 下一张关卡图入口迁移到 `PlatformIconButton variant="darkMini"`,修改作品和进入第 1 关迁移到 `PlatformActionButton`,分享动作继续使用 `CopyFeedbackButton` 但复用共享动作按钮 chrome;详情页只保留轮播、复制和跳转语义,不再手写 hero 区按钮壳。
|
||||
19.3. 个人中心充值商品卡里的“购买 / 处理中”胶囊暂保留局部 `span`,不直接套用 `PlatformActionButton`,避免在 `PlatformSubpanel as="button"` 内再嵌套交互按钮;待出现第二个同形态的非交互 action chip 后,再决定是否沉淀独立的共享展示基元。
|
||||
@@ -257,6 +257,7 @@
|
||||
19.3.33. `PlatformAsyncStatePanel` 从 profile modal 扩展到作品架:`CustomWorldCreationHub.tsx` 的作品架主体现在也统一通过 `loadingState / emptyState / children` 三个 slot 切换,保留外层 error + 重试提示不并入共享状态骨架。后续白底作品架或列表 panel 若只是互斥的 `loading / empty / content`,优先直接复用 `PlatformAsyncStatePanel`,不要再在业务 JSX 中重复拼 skeleton 和“当前筛选下没有内容”的分支。验证命令:`npx vitest run src/components/custom-world-home/CustomWorldCreationHub.test.tsx src/components/custom-world-home/CustomWorldCreationHub.interaction.test.tsx`、`npm run check:encoding`。
|
||||
19.3.34. `CopyFeedbackButton.tsx` 的 `actionSurface` 分支继续向共享按钮收口:带平台动作外观的复制按钮现在直接组合 `PlatformActionButton`,仅保留 `pill` 分支继续复用 `PlatformPillBadge` 风格。复制反馈按钮不再手动调用 `getPlatformActionButtonClassName` 拼平台按钮基础 chrome;后续同类“复制状态机 + 平台动作按钮”组合也优先走 `CopyFeedbackButton + PlatformActionButton`,不要在业务页或按钮组件里重新混写图标、文案、aria 和 class。验证命令:`npm run test -- src/components/common/CopyFeedbackButton.test.tsx src/components/common/PlatformActionButton.test.tsx`。
|
||||
19.3.35. 白底 / 暗色面板里的轻量空态和普通 CTA 继续按共享组件收口:`PuzzleResultView.tsx` 的“还没有可编辑的拼图草稿”和 `RpgCreationAssetDebugPanel.tsx` 的“没有可诊断项”改为 `PlatformEmptyState`,`Match3DResultView.tsx` 的引用素材列表直接交给 `PlatformAssetPickerGrid` 自己处理空态;`AdventureEntityModal.tsx` 的私聊按钮与 `InventoryPanel.tsx` 的锻造 / 合成按钮改为 `PlatformActionButton surface="editorDark"`,业务页只贴回 sky / emerald 局部皮肤。后续白底子面板里的只读空态优先使用 `PlatformEmptyState surface="subpanel" size="inline"`,暗色编辑 / 运行面板里的普通动作优先使用 `PlatformActionButton surface="editorDark"`,不要再手写基础按钮壳或空态面板 chrome。验证命令:`npm run test -- src/components/puzzle-result/PuzzleResultView.test.tsx src/components/rpg-creation-result/RpgCreationAssetDebugPanel.test.tsx src/components/AdventureEntityModal.test.tsx src/components/InventoryPanel.test.tsx`、`npm run check:encoding`。
|
||||
19.3.36. `VisualNovelEntityGrid` 的空态也继续收口到 `PlatformEmptyState surface="subpanel" size="inline"`;角色 / 场景 / 剧情阶段共用这一网格组件后,白底实体列表里的“暂无角色 / 暂无场景 / 暂无剧情阶段”等同构空态不再回退成 `PlatformSubpanel`。同时,`RpgCreationRoleAssetStudioModalImpl.tsx` 与 `RpgCreationEntityEditorShared.tsx` 保留局部 `ActionButton` 语义壳,但按钮本体已统一委托给 `PlatformActionButton surface="editorDark"`,只在包装层补最小的 `stopPropagation`、tone 映射和局部 class 适配。后续类似“暗色编辑器局部包装按钮”优先沿用这种薄包装模式,不再直接手写原生 `<button>` 基础 chrome。验证命令:`npm run test -- src/components/visual-novel-result/VisualNovelResultView.test.tsx src/components/common/PlatformEmptyState.test.tsx src/components/rpg-creation-asset-studio/RpgCreationRoleAssetStudioModal.test.tsx src/components/CustomWorldEntityEditorModal.test.tsx src/components/common/PlatformActionButton.test.tsx`、`npm run check:encoding`。
|
||||
19.3. creative-agent 首页的侧边栏菜单、账号入口、开启新对话、我的创作、首页激励 CTA 和 prompt suggestion 按钮迁移到 `PlatformIconButton` / `PlatformActionButton`;首页继续保留 `creative-agent-home__*` 本地 class 承接透明顶栏、抽屉和品牌化胶囊视觉,不把视觉回收和语义收口绑成一次大改。`Beta` 徽标和历史记录纯文本行暂保留本地实现,等出现更多同构轻量列表行后再评估是否抽新的共享 row primitive。
|
||||
19.4. 大鱼吃小鱼结果页 hero 的返回入口迁移到 `PlatformIconButton variant="darkMini"`,测试 / 发布动作迁移到 `PlatformActionButton surface="editorDark"`;结果页只保留测试运行、发布提交和文案状态语义,不再手写 hero 顶栏按钮壳。
|
||||
19.4.1. 大鱼吃小鱼结果页的发布失败弹层迁移到 `src/components/common/PlatformStatusDialog.tsx`;`PlatformStatusDialog` 补充自定义图标、可访问标签和动作按钮样式透传后,`BigFishResultView` 不再保留 `BigFishResultErrorModal` 内联的 `UnifiedConfirmDialog + PlatformIconBadge` 组合。结果页只保留失败文案和关闭回调,发布失败的状态图标、遮罩、白底面板和“知道了”主动作统一由共享状态弹层承接。验证命令:`npm run test -- src/components/common/PlatformStatusDialog.test.tsx src/components/big-fish-result/BigFishResultView.test.tsx`、`npm run typecheck`。
|
||||
|
||||
@@ -1055,7 +1055,11 @@ test('基本设定面板只编辑六个角色维度名称', async () => {
|
||||
expect(screen.queryByLabelText('正向信号')).toBeNull();
|
||||
expect(screen.queryByLabelText('战斗体现')).toBeNull();
|
||||
|
||||
await user.click(screen.getByRole('button', { name: /保存修改/u }));
|
||||
const saveButton = screen.getByRole('button', { name: /保存修改/u });
|
||||
expect(saveButton.className).toContain('platform-action-button--editor-dark');
|
||||
expect(saveButton.className).toContain('rounded-full');
|
||||
|
||||
await user.click(saveButton);
|
||||
|
||||
expect(savedProfileRef.current?.attributeSchema.slots[0]?.name).toBe('潮骨');
|
||||
});
|
||||
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
import { buildProjectPixelStyleReferenceBoard } from '../asset-studio/projectPixelStyleReference';
|
||||
import { useAuthUi } from '../auth/AuthUiContext';
|
||||
import { CharacterAnimator } from '../CharacterAnimator';
|
||||
import { PlatformActionButton } from '../common/PlatformActionButton';
|
||||
import { PlatformMudPointConfirmDialog } from '../common/PlatformMudPointConfirmDialog';
|
||||
import {
|
||||
CORE_ACTIONS,
|
||||
@@ -206,16 +207,21 @@ function ActionButton({
|
||||
disabled?: boolean;
|
||||
tone?: 'default' | 'sky' | 'green';
|
||||
}) {
|
||||
const resolvedTone =
|
||||
tone === 'green' ? 'success' : tone === 'sky' ? 'primary' : 'ghost';
|
||||
const toneClassName =
|
||||
tone === 'green'
|
||||
? 'border-emerald-400/30 bg-emerald-500/10 text-emerald-100 hover:bg-emerald-500/20'
|
||||
? 'border-emerald-400/30 bg-emerald-500/10 text-emerald-100 hover:border-emerald-300/40 hover:bg-emerald-500/20 hover:text-white'
|
||||
: tone === 'sky'
|
||||
? 'border-sky-300/22 bg-sky-500/12 text-sky-50 hover:border-sky-200/40 hover:text-white'
|
||||
: 'border-white/12 bg-black/20 text-zinc-200 hover:border-white/22 hover:text-white';
|
||||
? 'border-sky-300/22 bg-sky-500/12 text-sky-50 hover:border-sky-200/40 hover:bg-sky-500/15 hover:text-white'
|
||||
: 'border-white/12 bg-black/20 text-zinc-200 hover:border-white/22 hover:bg-black/20 hover:text-white';
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
<PlatformActionButton
|
||||
surface="editorDark"
|
||||
tone={resolvedTone}
|
||||
shape="pill"
|
||||
align="start"
|
||||
onPointerDown={(event) => {
|
||||
event.stopPropagation();
|
||||
}}
|
||||
@@ -227,7 +233,12 @@ function ActionButton({
|
||||
onClick();
|
||||
}}
|
||||
disabled={disabled}
|
||||
className={`inline-flex items-center gap-2 rounded-full border px-4 py-2 text-sm font-semibold transition-colors ${toneClassName} ${disabled ? 'cursor-not-allowed opacity-45' : ''}`}
|
||||
className={[
|
||||
'py-2 font-semibold leading-tight disabled:opacity-45',
|
||||
toneClassName,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ')}
|
||||
>
|
||||
{icon ?? null}
|
||||
<span className="flex flex-col items-start leading-tight">
|
||||
@@ -236,7 +247,7 @@ function ActionButton({
|
||||
<span className="text-[11px] font-medium opacity-70">{subLabel}</span>
|
||||
) : null}
|
||||
</span>
|
||||
</button>
|
||||
</PlatformActionButton>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -95,6 +95,7 @@ import {
|
||||
import { useAuthUi } from '../auth/AuthUiContext';
|
||||
import { CharacterAnimator } from '../CharacterAnimator';
|
||||
import { PlatformAcknowledgeStatusDialog } from '../common/PlatformAcknowledgeStatusDialog';
|
||||
import { PlatformActionButton } from '../common/PlatformActionButton';
|
||||
import { PlatformAssetPickerGrid } from '../common/PlatformAssetPickerCard';
|
||||
import { PlatformEmptyState } from '../common/PlatformEmptyState';
|
||||
import { PlatformIconButton } from '../common/PlatformIconButton';
|
||||
@@ -1665,16 +1666,22 @@ function ActionButton({
|
||||
disabled?: boolean;
|
||||
className?: string;
|
||||
}) {
|
||||
const buttonTone =
|
||||
tone === 'sky' ? 'primary' : tone === 'rose' ? 'danger' : 'ghost';
|
||||
const toneClassName =
|
||||
tone === 'sky'
|
||||
? 'border-sky-300/22 bg-sky-500/12 text-sky-50 hover:border-sky-200/40 hover:text-white'
|
||||
? 'border-sky-300/22 bg-sky-500/12 text-sky-50 hover:border-sky-200/40 hover:bg-sky-500/18 hover:text-white'
|
||||
: tone === 'rose'
|
||||
? 'border-rose-300/22 bg-rose-500/12 text-rose-50 hover:border-rose-200/40 hover:text-white'
|
||||
? 'border-rose-300/22 bg-rose-500/12 text-rose-50 hover:border-rose-200/40 hover:bg-rose-500/16 hover:text-white'
|
||||
: 'border-white/12 bg-black/20 text-zinc-200 hover:border-white/22 hover:text-white';
|
||||
|
||||
return (
|
||||
<button
|
||||
<PlatformActionButton
|
||||
type="button"
|
||||
surface="editorDark"
|
||||
tone={buttonTone}
|
||||
size="sm"
|
||||
shape="pill"
|
||||
onPointerDown={(event) => {
|
||||
event.stopPropagation();
|
||||
}}
|
||||
@@ -1686,10 +1693,16 @@ function ActionButton({
|
||||
onClick();
|
||||
}}
|
||||
disabled={disabled}
|
||||
className={`rounded-full border px-4 py-2 text-sm font-semibold transition-colors ${toneClassName} ${disabled ? 'cursor-not-allowed opacity-45' : ''} ${className}`}
|
||||
className={[
|
||||
'py-2 font-semibold disabled:opacity-45',
|
||||
toneClassName,
|
||||
className,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ')}
|
||||
>
|
||||
{label}
|
||||
</button>
|
||||
</PlatformActionButton>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -113,7 +113,7 @@ test('visual novel entity list items use interactive PlatformSubpanel shells', a
|
||||
expect(characterCard.className).toContain('rounded-[1.25rem]');
|
||||
});
|
||||
|
||||
test('visual novel empty entity list uses PlatformSubpanel shell', async () => {
|
||||
test('visual novel empty entity list uses PlatformEmptyState shell', async () => {
|
||||
const user = userEvent.setup();
|
||||
const emptyCharacterDraft = {
|
||||
...mockVisualNovelDraft,
|
||||
@@ -126,11 +126,17 @@ test('visual novel empty entity list uses PlatformSubpanel shell', async () => {
|
||||
|
||||
await user.click(screen.getByRole('button', { name: '角色' }));
|
||||
|
||||
const emptyPanel = screen.getByText('暂无角色').closest('.platform-subpanel');
|
||||
const emptyPanel = screen
|
||||
.getByText('暂无角色')
|
||||
.closest('.platform-empty-state');
|
||||
|
||||
expect(emptyPanel?.className).toContain('platform-subpanel');
|
||||
expect(emptyPanel?.className).toContain('platform-empty-state');
|
||||
expect(emptyPanel?.className).toContain('bg-white/74');
|
||||
expect(emptyPanel?.className).toContain('rounded-[1.25rem]');
|
||||
expect(emptyPanel?.className).toContain('min-h-32');
|
||||
expect(emptyPanel?.className).toContain(
|
||||
'text-[var(--platform-text-soft)]',
|
||||
);
|
||||
});
|
||||
|
||||
test('visual novel result opens complex editors as a dialog', async () => {
|
||||
|
||||
@@ -1848,12 +1848,13 @@ function VisualNovelEntityGrid({
|
||||
</PlatformSubpanel>
|
||||
))}
|
||||
{items.length <= 0 ? (
|
||||
<PlatformSubpanel
|
||||
as="div"
|
||||
className="flex min-h-32 items-center justify-center rounded-[1.25rem] px-4 text-sm text-[var(--platform-text-soft)]"
|
||||
<PlatformEmptyState
|
||||
surface="subpanel"
|
||||
size="inline"
|
||||
className="flex min-h-32 items-center justify-center rounded-[1.25rem] p-4 font-normal"
|
||||
>
|
||||
{emptyText}
|
||||
</PlatformSubpanel>
|
||||
</PlatformEmptyState>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user