From 38954e9b365f5639bd94bf0cf205a2bf47300495 Mon Sep 17 00:00:00 2001 From: EwdErna Date: Thu, 22 Oct 2026 09:19:06 +0900 Subject: [PATCH] =?UTF-8?q?mock:=20=E3=83=90=E3=83=AA=E3=82=A2=E3=83=B3?= =?UTF-8?q?=E3=83=88=E3=81=AE=E6=97=A2=E5=AE=9A=E3=81=AE=E5=8D=B0=E3=83=BB?= =?UTF-8?q?=E3=83=90=E3=83=83=E3=82=B8=E3=83=BB=E4=BD=9C=E6=88=90=E5=85=88?= =?UTF-8?q?=E3=82=92=E7=9B=B4=E3=81=99(T-30=20=E5=8D=98=E4=BD=8D3)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ポップオーバーの既定の印を「既定」の札にする。バッジには名前を書かず、 表示中のバリアント名はツールチップで示す(はみ出しをなくす。T-30の着手前の決定を反映)。 バリアントグループは選択の入れ物(クラスター・バリアント)の中に作る。 そのため domain に createVariantGroupInParent を足し、variant のIDを渡すと 例外になっていた parentNodes を直した。 Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01NznQtmVPoQAnietqwr1auL --- mock/e2e/phy.spec.ts | 20 ++++++++++++++++++++ mock/src/domain/phyCommands.ts | 22 ++++++++++++++++++---- mock/src/ui/phy/NodeSvg.vue | 21 ++++++++++++++------- mock/src/ui/phy/PhyEditor.vue | 10 +++++++++- mock/src/ui/phy/usePhyCommands.ts | 14 +++++++++----- mock/tests/phyDomain.test.ts | 19 +++++++++++++++++++ provisional/decisions.md | 3 +-- 7 files changed, 90 insertions(+), 19 deletions(-) diff --git a/mock/e2e/phy.spec.ts b/mock/e2e/phy.spec.ts index 401d05b..be6aed3 100644 --- a/mock/e2e/phy.spec.ts +++ b/mock/e2e/phy.spec.ts @@ -257,6 +257,26 @@ test("選択枠は画面上で見え、バリアントバッジで表示を切 await expect(keys).toHaveCount(16); }); +test("クラスター内のキーから作ったバリアントグループはクラスターの中にでき、バッジは名前を書かない", async ({ + page, +}) => { + const cluster = await makeCluster(page); + const key = cluster.locator('[data-node-type="key"]').first(); + await key.click(); + await page.getByRole("button", { name: "選択からバリアントグループ" }).click(); + const group = cluster.locator('[data-node-type="variantGroup"]'); + await expect(group).toHaveCount(1); + const badge = group.locator(".badge"); + await expect(badge.locator("text")).toHaveText("⇄"); + await expect(badge.locator("title")).toHaveText("Variant Group: Option A(既定)"); + await badge.locator("rect").click(); + const popover = page.locator(".popover"); + await expect(popover.getByRole("button", { name: /Option A/ }).locator(".default-tag")).toHaveText("既定"); + await expect(popover.getByRole("button", { name: /Option B/ }).locator(".default-tag")).toHaveCount(0); + await popover.getByRole("button", { name: /Option B/ }).click(); + await expect(badge.locator("title")).toHaveText("Variant Group: Option B"); +}); + test("削除確認は参照を列挙し、実行後はIDXからも消える", async ({ page }) => { const key = page.locator('[data-node-type="key"]').first(); const id = await key.getAttribute("data-node-id"); diff --git a/mock/src/domain/phyCommands.ts b/mock/src/domain/phyCommands.ts index 7e4aab3..f0073a4 100644 --- a/mock/src/domain/phyCommands.ts +++ b/mock/src/domain/phyCommands.ts @@ -114,9 +114,12 @@ export function insertNodes(f: PhyFile, parentId: string | null, nodes: Node[], function parentNodes(f: PhyFile, parentId: string | null): Node[] { if (!parentId) return f.root; - const n = find(f, parentId); - if (n.type === "cluster") return n.children; - if (n.type === "variantGroup") throw new Error("Choose a variant as the parent"); + // variantのIDは containers に子の入れ物として載るので、find ではノードとして引けない + const n = containers(f) + .get(parentId) + ?.nodes.find((x) => x.id === parentId); + if (n?.type === "cluster") return n.children; + if (n?.type === "variantGroup") throw new Error("Choose a variant as the parent"); return findVariant(f, parentId).children; } /** ローカル座標の差分でノードを移動する。 */ @@ -397,6 +400,17 @@ export function createVariantGroup( at: Transform, variants: [{ name: string; children?: Node[] }, { name: string; children?: Node[] }], gen?: () => string, +): PhyFile { + return createVariantGroupInParent(f, null, name, at, variants, gen); +} +/** 指定コンテナへVariantGroupを作成する。T-11。 */ +export function createVariantGroupInParent( + f: PhyFile, + parentId: string | null, + name: string, + at: Transform, + variants: [{ name: string; children?: Node[] }, { name: string; children?: Node[] }], + gen?: () => string, ): PhyFile { const o = mutable(f), children = variants.map((x) => ({ @@ -405,7 +419,7 @@ export function createVariantGroup( name: x.name, children: structuredClone(x.children ?? []), })); - o.root.push({ + parentNodes(o, parentId).push({ type: "variantGroup", id: nodeId("variantGroup", gen), name, diff --git a/mock/src/ui/phy/NodeSvg.vue b/mock/src/ui/phy/NodeSvg.vue index dbda78f..5be01e7 100644 --- a/mock/src/ui/phy/NodeSvg.vue +++ b/mock/src/ui/phy/NodeSvg.vue @@ -28,7 +28,7 @@ function bounds(n: Node) { } function badgeBox(n: Node) { const b = bounds(n); - const size = 26 / props.zoom; + const size = 20 / props.zoom; return { x: b.x + b.width + 4 / props.zoom, y: b.y - size - 4 / props.zoom, @@ -36,6 +36,12 @@ function badgeBox(n: Node) { angle: -(props.angle + n.transform.rotation), }; } +/** バッジは名前を書かず、表示中のバリアント名はツールチップで示す。T-30。 */ +function badgeTitle(n: Extract) { + const shown = props.variants[n.id] || n.default; + const name = n.children.find((v) => v.id === shown)?.name ?? ""; + return `${n.name}: ${name}${shown === n.default ? "(既定)" : ""}`; +}