From fb09c693c467f9c9a7646af98111a14421bb6ded Mon Sep 17 00:00:00 2001 From: ypshim Date: Tue, 27 Oct 2026 17:01:32 +0900 Subject: [PATCH] =?UTF-8?q?refactor(copy):=20=EB=8C=80=EC=83=81=20?= =?UTF-8?q?=EA=B5=90=EA=B0=81=EC=9D=98=20=EA=B0=99=EC=9D=80=20=ED=98=95?= =?UTF-8?q?=EC=8B=9D=20=ED=95=84=ED=84=B0=EC=99=80=20=EA=B0=99=EC=9D=80=20?= =?UTF-8?q?=ED=98=95=EC=8B=9D=20=EB=AA=A8=EB=91=90=20=EC=84=A0=ED=83=9D=20?= =?UTF-8?q?=EB=B2=84=ED=8A=BC=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - "원본과 같은 형식만" 체크박스와 "같은 형식 모두 선택" 버튼 삭제, "선택 해제"는 유지 - 대상 칩에 원본과 형식이 다르면 "형식 다름" 표시 - copyCandidates와 selectedTargets에서 형식 필터 인자 제거, 테스트 갱신 - 테스트 타입 오류(optional 필드) 수정 Co-Authored-By: Claude Haiku 5.5 --- index.html | 2 -- src/core/copy-targets.ts | 11 +++++------ src/main.ts | 19 ++++--------------- tests/copy-targets.test.ts | 19 +++++++++---------- 4 files changed, 18 insertions(+), 33 deletions(-) diff --git a/index.html b/index.html index 7e54a19..ab2d123 100644 --- a/index.html +++ b/index.html @@ -159,8 +159,6 @@

1 · 대상 교각

- -
diff --git a/src/core/copy-targets.ts b/src/core/copy-targets.ts index b73103b..25c8c3b 100644 --- a/src/core/copy-targets.ts +++ b/src/core/copy-targets.ts @@ -16,17 +16,16 @@ export interface TargetCandidate { selectable: boolean; } -/** The piers offered as targets: every other pier, or only those of the source type. */ -export function copyCandidates(piers: CopyCandidate[], sourceName: string, sameTypeOnly: boolean): TargetCandidate[] { - const source = piers.find((p) => p.name === sourceName); +/** The piers offered as targets: every other pier. The type is shown so a different type is visible. */ +export function copyCandidates(piers: CopyCandidate[], sourceName: string): TargetCandidate[] { return piers - .filter((p) => p.name !== sourceName && (!sameTypeOnly || p.type === source?.type)) + .filter((p) => p.name !== sourceName) .map((p) => ({ name: p.name, type: p.type, selectable: p.hasModel })); } /** The targets that get the change: ticked by the user, selectable, and visible under the current filter. */ -export function selectedTargets(piers: CopyCandidate[], sourceName: string, sameTypeOnly: boolean, ticked: Set): string[] { - return copyCandidates(piers, sourceName, sameTypeOnly) +export function selectedTargets(piers: CopyCandidate[], sourceName: string, ticked: Set): string[] { + return copyCandidates(piers, sourceName) .filter((c) => c.selectable && ticked.has(c.name)) .map((c) => c.name); } diff --git a/src/main.ts b/src/main.ts index ec4dd41..3726670 100644 --- a/src/main.ts +++ b/src/main.ts @@ -640,7 +640,7 @@ function copyCandidateList(): CopyCandidate[] { function selectedCopyTargets(): Pier[] { const src = activePier(); if (!src) return []; - const names = new Set(selectedTargets(copyCandidateList(), src.input.name, $('cp-same-type').checked, copySelection)); + const names = new Set(selectedTargets(copyCandidateList(), src.input.name, copySelection)); return state.piers.filter((p) => names.has(p.input.name)); } @@ -685,8 +685,7 @@ function renderCopyTargets(): void { const box = $('cp-targets'); box.replaceChildren(); const src = activePier(); - const sameType = $('cp-same-type').checked; - for (const c of copyCandidates(copyCandidateList(), src?.input.name ?? '', sameType)) { + for (const c of copyCandidates(copyCandidateList(), src?.input.name ?? '')) { const label = el('label', undefined, 'chk'); const check = el('input'); check.type = 'checkbox'; @@ -698,7 +697,8 @@ function renderCopyTargets(): void { else copySelection.delete(c.name); renderCopyPreview(); }); - const text = c.selectable ? `${c.name} · ${c.type}` : `${c.name} · ${c.type} (모델 없음, 제외)`; + const otherType = src !== undefined && c.type !== src.input.type ? ' · 형식 다름' : ''; + const text = c.selectable ? `${c.name} · ${c.type}${otherType}` : `${c.name} · ${c.type} (모델 없음, 제외)`; label.append(check, document.createTextNode(text)); box.appendChild(label); } @@ -3076,17 +3076,6 @@ $('member-regen').addEventListener('click', () => { const pier = activePier(); if (pier) regenerateOne(pier.input.name); }); -$('cp-same-type').addEventListener('change', () => { - renderCopyTargets(); - renderCopyPreview(); -}); -$('cp-select-same').addEventListener('click', () => { - const src = activePier(); - const sameType = $('cp-same-type').checked; - copySelection = new Set(copyCandidates(copyCandidateList(), src?.input.name ?? '', sameType).filter((c) => c.selectable).map((c) => c.name)); - renderCopyTargets(); - renderCopyPreview(); -}); $('cp-items-reset').addEventListener('click', () => { const src = activePier(); copyItemChoice = new Set(src ? copyItemsWithOverrides(src) : []); diff --git a/tests/copy-targets.test.ts b/tests/copy-targets.test.ts index 75d1279..6aeb8f9 100644 --- a/tests/copy-targets.test.ts +++ b/tests/copy-targets.test.ts @@ -12,32 +12,31 @@ const piers: CopyCandidate[] = [ describe('copyCandidates (대상 후보)', () => { it('excludes the source pier', () => { - expect(copyCandidates(piers, 'P3', false).map((c) => c.name)).not.toContain('P3'); + expect(copyCandidates(piers, 'P3').map((c) => c.name)).not.toContain('P3'); }); - it('keeps only the source type when 같은 형식만 is on', () => { - expect(copyCandidates(piers, 'P3', true).map((c) => c.name)).toEqual(['P1', 'P2', 'P5']); + it('offers piers of every type, so a different type is visible', () => { + expect(copyCandidates(piers, 'P3').map((c) => c.type)).toEqual(['T형', 'T형', 'Y형', 'T형']); }); it('marks a pier without a model as not selectable', () => { - const p5 = copyCandidates(piers, 'P3', false).find((c) => c.name === 'P5'); + const p5 = copyCandidates(piers, 'P3').find((c) => c.name === 'P5'); expect(p5?.selectable).toBe(false); }); }); describe('selectedTargets (선택한 대상만)', () => { it('is empty when nothing is ticked', () => { - expect(selectedTargets(piers, 'P3', false, new Set())).toEqual([]); + expect(selectedTargets(piers, 'P3', new Set())).toEqual([]); }); it('returns only ticked, selectable piers', () => { const picked = new Set(['P1', 'P4', 'P5']); - expect(selectedTargets(piers, 'P3', false, picked)).toEqual(['P1', 'P4']); + expect(selectedTargets(piers, 'P3', picked)).toEqual(['P1', 'P4']); }); - it('ignores a tick on a pier hidden by the same-type filter', () => { - const picked = new Set(['P4']); - expect(selectedTargets(piers, 'P3', true, picked)).toEqual([]); + it('keeps a tick on a pier of another type', () => { + expect(selectedTargets(piers, 'P3', new Set(['P4']))).toEqual(['P4']); }); }); @@ -82,7 +81,7 @@ describe('copyItemsWithOverrides (기준 교각에서 되돌리기가 표시된 }); it('selects the coping h1, h2 and Level 폭 items when those values changed', () => { - expect(copyItemsWithOverrides(source({ copingH1Mm: base.copingH1Mm + 1, levelWidthMm: base.levelWidthMm + 1 }))).toEqual(['coping-h1', 'level-width']); + expect(copyItemsWithOverrides(source({ copingH1Mm: (base.copingH1Mm ?? 0) + 1, levelWidthMm: (base.levelWidthMm ?? 0) + 1 }))).toEqual(['coping-h1', 'level-width']); }); it('ignores a changed value that no copy item carries (groundEl)', () => {