fix: align map control hover spacing

Let control surfaces size to their 40px buttons, padding and borders. Reserve the resulting 50px mobile dock height to keep the map controls clear.

The initial alignment check covered only horizontal spacing and missed the mobile height constraint. Check all four outer edges at desktop and mobile widths, plus the dock gap, to prevent recurrence.
This commit is contained in:
2026-11-02 15:35:11 +08:00
parent 5f7041ffae
commit 74bf7c64c4
6 changed files with 62 additions and 6 deletions
+1 -1
View File
@@ -47,7 +47,7 @@ export function MapToolbar({
"pointer-events-auto p-1",
MAP_CONTROL_RADIUS_CLASS_NAME,
MAP_CONTROL_SURFACE_CLASS_NAME,
isVertical ? "flex w-12 flex-col" : "inline-flex",
isVertical ? "flex w-fit flex-col" : "inline-flex",
className
)}
>
+1 -1
View File
@@ -86,7 +86,7 @@ export function MapZoom({
<div
role="group"
aria-label="地图缩放"
className={cn("pointer-events-auto flex w-12 flex-col gap-2", className)}
className={cn("pointer-events-auto flex w-fit flex-col gap-2", className)}
>
<div
className={cn(
@@ -27,12 +27,12 @@ describe("workbench floating panels", () => {
expect(resolveMobileWorkbenchOverlayLayout(true)).toEqual({
timelineBottom: 12,
dockBottom: 68,
mapControlBottom: 120
mapControlBottom: 126
});
expect(resolveMobileWorkbenchOverlayLayout(false)).toEqual({
timelineBottom: 12,
dockBottom: 12,
mapControlBottom: 64
mapControlBottom: 70
});
});
@@ -32,7 +32,7 @@ export const WORKBENCH_LAYOUT = {
mobileOverlay: {
edgeInset: 12,
gap: 8,
dockHeight: 44,
dockHeight: 50, // 40px buttons + 4px padding and 1px border on each side.
timelineSummaryHeight: 48
}
} as const;
@@ -1624,7 +1624,7 @@ function MapWorkbenchContent({
<MapToolbar
items={toolbarItems}
orientation="horizontal"
className="h-11 min-w-0 flex-1 justify-center [&>div]:min-w-0 [&>div]:flex-1 [&>div>div]:hidden [&_button]:w-full"
className="min-w-0 flex-1 justify-center [&>div]:min-w-0 [&>div]:flex-1 [&>div>div]:hidden [&_button]:w-full"
/>
) : null}
<button
@@ -0,0 +1,56 @@
import { expect, test } from "@playwright/test";
import { mockAgentApi } from "./support/mock-agent-api";
import { mockWorkbenchContext } from "./support/mock-workbench-context";
for (const width of [1280, 375, 320]) {
test(`map control hover borders have symmetric spacing at ${width}px`, async ({ page }, testInfo) => {
await mockWorkbenchContext(page);
await mockAgentApi(page);
await page.route("**/city-model/manifest.json", (route) => route.fulfill({ status: 404 }));
await page.setViewportSize({ width, height: 900 });
await page.goto("/", { waitUntil: "domcontentloaded" });
const zoomControls = page.getByRole("group", { name: "地图缩放", exact: true });
const toolbar = page.getByRole("navigation", { name: "地图工具", exact: true });
await expect(zoomControls).toBeVisible();
await expect(toolbar).toBeVisible();
await expect(zoomControls.getByRole("button").first()).toBeEnabled();
const surfaces = zoomControls.locator(".acrylic-control").or(toolbar);
await expect(surfaces).toHaveCount(3);
for (const surface of await surfaces.all()) {
const buttons = surface.getByRole("button");
for (const button of await buttons.all()) {
await button.hover();
const size = await button.boundingBox();
expect(size!.height).toBe(40);
if (width >= 1024 || (await zoomControls.getByRole("button").and(button).count()) > 0) {
expect(size!.width).toBe(40);
}
}
// Check all four outer edges, including the final row's bottom spacing.
const insets = await surface.evaluate((element) => {
const bounds = element.getBoundingClientRect();
const buttons = [...element.querySelectorAll("button")].map((button) => button.getBoundingClientRect());
return {
top: Math.min(...buttons.map((button) => button.top)) - bounds.top,
bottom: bounds.bottom - Math.max(...buttons.map((button) => button.bottom)),
left: Math.min(...buttons.map((button) => button.left)) - bounds.left,
right: bounds.right - Math.max(...buttons.map((button) => button.right))
};
});
expect.soft(insets).toEqual({ top: 5, bottom: 5, left: 5, right: 5 });
}
if (width < 1024) {
const zoomBounds = await zoomControls.boundingBox();
const toolbarBounds = await toolbar.boundingBox();
expect(toolbarBounds!.height).toBe(50);
expect(toolbarBounds!.y - (zoomBounds!.y + zoomBounds!.height)).toBe(8);
}
await zoomControls.getByRole("button", { name: "放大", exact: true }).hover();
await page.screenshot({ path: testInfo.outputPath("map-control-hover.png") });
});
}