mock: 選択枠を太くし、最前面の描画層に移す(T-30 単位1)
選択枠と長押し候補の枠を NodeSvg の再帰から外し、ノードの描画より後に描く PhySelectionLayer に移した。枠はワールド変換とローカル矩形から純粋関数 selectionFrames で求め、画面上3.5pxで描く。部品の選択表示は形状上に残す。 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TDkA8qMQ3Uf1eY3Ar19vjy
This commit is contained in:
+53
-5
@@ -15,6 +15,49 @@ async function makeCluster(page: import("@playwright/test").Page) {
|
||||
return page.locator('[data-node-type="cluster"]').first();
|
||||
}
|
||||
|
||||
test("選択レイヤーはキーより前面にあり、回転とフォーカスに追従する", async ({ page }) => {
|
||||
test.setTimeout(60_000);
|
||||
const keys = page.locator('[data-node-type="key"]');
|
||||
const leftId = await keys.first().getAttribute("data-node-id");
|
||||
await keys.first().click();
|
||||
const order = await page.locator("svg.canvas").evaluate((svg, id) => {
|
||||
const face = svg.querySelector(`[data-node-id="${id}"]`)!;
|
||||
const layer = svg.querySelector(`[data-sel-id="${id}"]`)!;
|
||||
return [
|
||||
face.compareDocumentPosition(layer) & Node.DOCUMENT_POSITION_FOLLOWING,
|
||||
layer.parentElement?.classList.contains("selection-layer"),
|
||||
];
|
||||
}, leftId);
|
||||
expect(order).toEqual([4, true]);
|
||||
|
||||
const cluster = await makeCluster(page);
|
||||
const clusterId = await cluster.getAttribute("data-node-id");
|
||||
await page.locator(`[data-tree-id="${clusterId}"]`).first().click();
|
||||
await page.getByLabel("Rotation").fill("35");
|
||||
await page.getByLabel("Rotation").dispatchEvent("change");
|
||||
const key = cluster.locator('[data-node-type="key"]').first();
|
||||
const id = await key.getAttribute("data-node-id");
|
||||
await key.click();
|
||||
const aligned = async () =>
|
||||
page.evaluate((keyId) => {
|
||||
const face = document.querySelector(`[data-node-id="${keyId}"] .key-face`)!.getBoundingClientRect();
|
||||
const outline = document.querySelector(`[data-sel-id="${keyId}"]`)!.getBoundingClientRect();
|
||||
return [
|
||||
Math.abs(face.x - outline.x),
|
||||
Math.abs(face.y - outline.y),
|
||||
Math.abs(face.width - outline.width),
|
||||
Math.abs(face.height - outline.height),
|
||||
];
|
||||
}, id);
|
||||
expect((await aligned()).every((delta) => delta < 1)).toBe(true);
|
||||
await page.locator(`[data-tree-id="${clusterId}"]`).first().click();
|
||||
await page.locator(".canvas-pane").focus();
|
||||
await page.keyboard.press("Enter");
|
||||
const focused = page.locator(`[data-node-id="${id}"]`).last();
|
||||
await focused.click();
|
||||
expect((await aligned()).every((delta) => delta < 1)).toBe(true);
|
||||
});
|
||||
|
||||
test("ドラッグの刻みをUndo/Redoでき、コンソールにエラーがない", async ({ page }) => {
|
||||
const errors: string[] = [];
|
||||
page.on("console", (message) => {
|
||||
@@ -151,7 +194,7 @@ test("入れ子の回転クラスターをフォーカスできる", async ({ pa
|
||||
await page.mouse.move(focusedBox!.x + focusedBox!.width / 2, focusedBox!.y + focusedBox!.height / 2);
|
||||
await page.mouse.down();
|
||||
await page.waitForTimeout(700);
|
||||
await expect(page.locator(`[data-node-id="${innerId}"] .group-sel.candidate`)).toBeVisible({
|
||||
await expect(page.locator(`[data-sel-id="${innerId}"].candidate`)).toBeVisible({
|
||||
timeout: 1800,
|
||||
});
|
||||
await page.mouse.up();
|
||||
@@ -164,11 +207,12 @@ test("入れ子の回転クラスターをフォーカスできる", async ({ pa
|
||||
});
|
||||
|
||||
test("選択枠は画面上で見え、バリアントバッジで表示を切り替えられる", async ({ page }) => {
|
||||
test.setTimeout(60_000);
|
||||
const keys = page.locator('[data-node-type="key"]');
|
||||
await keys.first().click();
|
||||
const outline = page.locator('[data-node-type="key"] .sel').first();
|
||||
const outline = page.locator(".selection-layer .sel").first();
|
||||
await expect(outline).toBeVisible();
|
||||
expect(await outline.evaluate((e) => getComputedStyle(e).strokeWidth)).toBe("2px");
|
||||
expect(await outline.evaluate((e) => getComputedStyle(e).strokeWidth)).toBe("3.5px");
|
||||
await keys.nth(1).click({ modifiers: ["Shift"] });
|
||||
await page.getByRole("button", { name: "選択からバリアントグループ" }).click();
|
||||
const group = page.locator('[data-node-type="variantGroup"]').first();
|
||||
@@ -456,12 +500,16 @@ test("長押し候補は離すまで選択を変えず、離すとクラスタ
|
||||
const clusterId = await cluster.getAttribute("data-node-id");
|
||||
const keys = page.locator('[data-node-type="key"]');
|
||||
await keys.nth(4).click();
|
||||
await expect(keys.nth(4).locator(".sel")).toBeVisible();
|
||||
await expect(
|
||||
page.locator(`[data-sel-id="${await keys.nth(4).getAttribute("data-node-id")}"]`),
|
||||
).toBeVisible();
|
||||
const b = await cluster.locator('[data-node-type="key"]').first().boundingBox();
|
||||
await page.mouse.move(b!.x + b!.width / 2, b!.y + b!.height / 2);
|
||||
await page.mouse.down();
|
||||
await expect(page.locator(".long-label")).toContainText("2/", { timeout: 1800 });
|
||||
await expect(keys.nth(4).locator(".sel")).toBeVisible();
|
||||
await expect(
|
||||
page.locator(`[data-sel-id="${await keys.nth(4).getAttribute("data-node-id")}"]`),
|
||||
).toBeVisible();
|
||||
await page.screenshot({ path: path.join(test.info().outputDir, "long-press-candidate.png") });
|
||||
await page.mouse.up();
|
||||
await expect(page.locator(`[data-tree-id="${clusterId}"]`).first()).toHaveClass(/chosen/);
|
||||
|
||||
Reference in New Issue
Block a user