Compare commits

..
Author SHA1 Message Date
suyx edbefcc738 feat(POSTV1-10): 增加统一交互反馈
Dada P0-A isolated Windows CI / validate-and-package (push) Waiting to run
2026-08-05 19:19:33 +08:00
4 changed files with 172 additions and 2 deletions
+152
View File
@@ -0,0 +1,152 @@
:root {
--dada-interaction-duration: 120ms;
--dada-interaction-easing: cubic-bezier(0.2, 0.8, 0.2, 1);
}
:where(button:not(:disabled), a[href], label:has(input:not(:disabled))) {
cursor: pointer;
}
:where(button:not(:disabled), a[href]) {
transition:
transform var(--dada-interaction-duration) var(--dada-interaction-easing),
box-shadow var(--dada-interaction-duration) var(--dada-interaction-easing),
border-color var(--dada-interaction-duration) ease,
background-color var(--dada-interaction-duration) ease,
color var(--dada-interaction-duration) ease,
opacity var(--dada-interaction-duration) ease;
}
:where(input:not(:disabled), select:not(:disabled), textarea:not(:disabled)) {
transition:
border-color var(--dada-interaction-duration) ease,
box-shadow var(--dada-interaction-duration) ease,
background-color var(--dada-interaction-duration) ease;
}
:where(.product-page, .product-loading) :focus-visible {
outline: 2px solid #005fcc;
outline-offset: 3px;
}
:where(input:not(:disabled), select:not(:disabled), textarea:not(:disabled)):focus-visible {
border-color: #005fcc;
box-shadow: 0 0 0 3px rgb(0 95 204 / 16%);
}
.project-card,
.project-preview img,
.ratio-control span,
.reference-input,
.editor-sticker-preview,
.editor-template-mark,
.editor-color-card-preview,
.editor-dynamic-preview,
.editor-source-preview-canvas,
.editor-thumb {
transition:
transform var(--dada-interaction-duration) var(--dada-interaction-easing),
box-shadow var(--dada-interaction-duration) var(--dada-interaction-easing),
border-color var(--dada-interaction-duration) ease,
background-color var(--dada-interaction-duration) ease;
}
@media (hover: hover) and (pointer: fine) {
:where(button:not(:disabled)):hover {
border-color: #111111;
box-shadow: 0 2px 0 rgb(17 17 17 / 35%);
}
:where(a[href]):hover {
color: #005fcc;
opacity: 0.78;
text-decoration-thickness: 2px;
text-underline-offset: 3px;
}
.product-header nav a:hover {
color: #111111;
background: #e9e9e5;
box-shadow: inset 0 -3px #111111;
opacity: 1;
}
.product-header nav a[aria-current="page"]:hover {
background: #f2f500;
}
.ratio-control label:hover span,
.reference-input:hover {
border-color: #111111;
background: #ffffd6;
box-shadow: inset 0 -3px #111111;
}
.project-card:hover {
border-color: #111111;
box-shadow: 0 3px 0 rgb(17 17 17 / 22%);
}
.editor-asset-tabs button:not(:disabled):hover,
.editor-source:hover,
.editor-sticker-grid button:not(:disabled):hover,
.editor-template-categories button:not(:disabled):hover,
.editor-template-grid button:not(:disabled):hover,
.editor-provider-grid button:not(:disabled):hover,
.editor-candidates button:not(:disabled):hover {
border-color: #111111;
background: #ffffd6;
}
:where(input:not(:disabled), select:not(:disabled), textarea:not(:disabled)):hover {
border-color: #111111;
}
}
@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
:where(button:not(:disabled), a[href]):hover {
transform: translateY(-1px);
}
.project-card:hover .project-preview img,
.editor-sticker-grid button:not(:disabled):hover .editor-sticker-preview {
transform: scale(1.02);
}
.editor-template-grid button:not(:disabled):hover .editor-template-mark,
.editor-provider-grid button:not(:disabled):hover > :first-child {
transform: scale(1.03);
}
}
@media (prefers-reduced-motion: no-preference) {
:where(button:not(:disabled), a[href]):active {
transform: translateY(1px);
transition-duration: 45ms;
}
}
:where(button:not(:disabled)):active {
box-shadow: inset 0 2px 0 rgb(17 17 17 / 24%);
}
:where(a[href]):active {
opacity: 0.58;
}
@media (prefers-reduced-motion: reduce) {
:where(button, a[href], input, select, textarea),
.project-card,
.project-preview img,
.ratio-control span,
.reference-input,
.editor-sticker-preview,
.editor-template-mark,
.editor-color-card-preview,
.editor-dynamic-preview,
.editor-source-preview-canvas,
.editor-thumb {
animation-duration: 0s !important;
transition-duration: 0s !important;
}
}
+2
View File
@@ -16,6 +16,8 @@ import { ProjectDetailPage, ProjectsPage, WorkspacePage } from "./project-pages.
import { EditorPage } from "./editor-page.js"; import { EditorPage } from "./editor-page.js";
import { AdminOverviewPage, AdminPlaceholderPage, AdminProtectedRoute } from "./admin-shell.js"; import { AdminOverviewPage, AdminPlaceholderPage, AdminProtectedRoute } from "./admin-shell.js";
import "./interaction-feedback.css";
const root = document.getElementById("root"); const root = document.getElementById("root");
if (!root) { if (!root) {
+13 -1
View File
@@ -105,9 +105,21 @@ test("TDD-WP2-PROJ-005 limits failed-empty selection on the project list", async
await expect(page.getByLabel("选择失败草稿:失败草稿")).toBeVisible(); await expect(page.getByLabel("选择失败草稿:失败草稿")).toBeVisible();
await expect(page.getByLabel("选择失败草稿:城市工作室")).toHaveCount(0); await expect(page.getByLabel("选择失败草稿:城市工作室")).toHaveCount(0);
await page.getByLabel("选择失败草稿:失败草稿").check(); await page.getByLabel("选择失败草稿:失败草稿").check();
await page.getByRole("button", { name: "批量移入回收站" }).click(); const batchTrashButton = page.getByRole("button", { name: "批量移入回收站" });
await batchTrashButton.hover();
await expect(batchTrashButton).toHaveCSS("transform", "matrix(1, 0, 0, 1, 0, -1)");
const buttonBounds = await batchTrashButton.boundingBox();
if (!buttonBounds) throw new Error("Batch trash button geometry is unavailable.");
await page.mouse.move(buttonBounds.x + buttonBounds.width / 2, buttonBounds.y + buttonBounds.height / 2);
await page.mouse.down();
await expect(batchTrashButton).toHaveCSS("transform", "matrix(1, 0, 0, 1, 0, 1)");
await page.mouse.move(0, 0);
await page.mouse.up();
await batchTrashButton.click();
expect(batchPayload).toEqual({ project_ids: [failedId] }); expect(batchPayload).toEqual({ project_ids: [failedId] });
await expect(page.getByText("失败草稿已移入回收站")).toBeVisible(); await expect(page.getByText("失败草稿已移入回收站")).toBeVisible();
await page.emulateMedia({ reducedMotion: "reduce" });
await expect(page.getByRole("link", { name: "打开项目:城市工作室" })).toHaveCSS("transition-duration", "0s");
expect(await page.evaluate(() => document.documentElement.scrollWidth)).toBe(375); expect(await page.evaluate(() => document.documentElement.scrollWidth)).toBe(375);
await captureEvidence(page, "TDD-WP2-PROJ-005-failed-draft-retry", "projects-mobile.png"); await captureEvidence(page, "TDD-WP2-PROJ-005-failed-draft-retry", "projects-mobile.png");
}); });
+5 -1
View File
@@ -98,7 +98,11 @@ test("TDD-WP4-TXT-003 exposes the frozen catalog, display-name search and accoun
await expect(page.locator(".editor-template-grid button")).toHaveCount(0); await expect(page.locator(".editor-template-grid button")).toHaveCount(0);
await search.fill(""); await search.fill("");
expect(page.getByText("添加普通文字", { exact: true })).toHaveCount(0); expect(page.getByText("添加普通文字", { exact: true })).toHaveCount(0);
await page.getByRole("button", { name: /FLOWER001 春日计划/ }).click(); const flowerTemplate = page.getByRole("button", { name: /FLOWER001 春日计划/ });
await flowerTemplate.hover();
await expect(flowerTemplate).toHaveCSS("background-color", "rgb(255, 255, 214)");
await expect(flowerTemplate.locator(".editor-template-mark")).toHaveCSS("transform", "matrix(1.03, 0, 0, 1.03, 0, 0)");
await flowerTemplate.click();
await expect(page.getByText("对象 1 / 50")).toBeVisible(); await expect(page.getByText("对象 1 / 50")).toBeVisible();
await expect.poll(() => backend.recent).toEqual([{ asset_id: "FLOWER001", asset_kind: "text_template", resource_version: "p0a-complex-v1" }]); await expect.poll(() => backend.recent).toEqual([{ asset_id: "FLOWER001", asset_kind: "text_template", resource_version: "p0a-complex-v1" }]);
await page.reload(); await page.reload();