feat(804-11): M01 목록 머리 단추 = 신규만 · M02 원문 손잡이 오른쪽 끝(요구 693 · 694)

- M01 단가산출 기초 목록 = [신규 로직]만 · [로직 복사/생성] · [로직 삭제] · [되돌림] · [저장] = 로직 열면 나타남(숨김)
- 요소 묶음 여덟 · 구조물 옵션 정의 · 별칭 장부 = 항목에 매인 머리 단추 없음(그대로)
- M02 원문 손잡이 = M01 687-17 과 같은 규칙(메인 칸 오른쪽 여백 24 만큼 밀어 붙임) · 단가산출 · 일위대가 손잡이 오른쪽 여백 24 → 0
- test_804_11 둘 · test_m02_layer_screen 머리 줄 기대에 804-8 [되돌림] 넣음(낡은 기대 고침)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-08 15:51:38 +09:00
co-authored by Claude Opus 5.5
parent 324ae1b047
commit 9130741751
4 changed files with 54 additions and 8 deletions
+10 -7
View File
@@ -6,7 +6,7 @@
*
* 데이터 흐름(CLAUDE.md 5장) — 고친 것은 캐시(sessionStorage)에 쌓고 [저장] 한 번에 `POST /save`.
* 409 = 그 사이 파일이 바뀜 · 422 = 검사 걸림(아무것도 안 씀) — 까닭은 서버 글 그대로 보임.
* 머리 단추(804-7 · 요구 687-2) = [신규 로직] · [로직 복사/생성] · [로직 삭제] · [되돌림] · [저장]
* 머리 단추(804-7 · 요구 687-2) = [신규 로직] · [로직 복사/생성] · [로직 삭제] · [되돌림] · [저장] · 목록에선 [신규 로직]만(요구 693)
* [저장] = 기본정보 · 별칭(로직 줄 `/save`) + 수식(줄 쌓기 `/logic/doc/save`) 한 번에 · [되돌림] = 둘 다 저장 뒤 상태로
* [로직 삭제] = 확인 → 서버 정본에서 바로(`/logic/remove` — 로직 줄 + 수식 문서 · 부르는 곳 있으면 막고 까닭) → 로직 목록
* ========================================================================== */
@@ -234,6 +234,8 @@ export async function mountM01LogicLab(
waiting.hidden = loaded;
panes.root.hidden = !opened;
back.hidden = views.root.hidden = listing;
// 로직을 골라야 뜻 있는 단추 = 목록에선 숨김(요구 693 · [신규 로직]만 보임)
copyButton.hidden = deleteButton.hidden = discardButton.hidden = saveButton.hidden = listing;
if (!opened) return;
if (!opened.row) {
editor.replaceChildren(createEmpty({ text: `● ${tx("List_Deleted")}`, small: true }));
@@ -528,6 +530,12 @@ export async function mountM01LogicLab(
})
: showToast(tl("Copy_NeedOpen"), "info"),
});
const deleteButton = createButton({
label: tx("Bar_Delete"),
variant: "danger",
iconStart: "trash",
onClick: () => void onDelete(),
});
// 제목 줄 = 요소 화면(인력 · 재료 …)과 같은 공용 조각
const bar = buildHeadBar({
lead: [back],
@@ -542,12 +550,7 @@ export async function mountM01LogicLab(
onClick: () => void onNew(),
}),
copyButton,
createButton({
label: tx("Bar_Delete"),
variant: "danger",
iconStart: "trash",
onClick: () => void onDelete(),
}),
deleteButton,
discardButton,
saveButton,
],
@@ -103,6 +103,11 @@ html.is-bm-route .m02-main__head {
min-height: 240px;
}
/* 원문 손잡이 = 브라우저 오른쪽 끝(요구 694 · M01 687-17 과 같은 규칙) — 메인 칸 오른쪽 여백(24)만큼 밀어 붙임 · 본문이 그 자리까지 */
html:not([data-size="compact"], [data-size="medium"]) .m02-main__host > .ui-side-panels-row {
margin-inline-end: calc(-1 * var(--spacing-24));
}
/* 면적산출 · 재료산출 전체 편집 — `.ss` 는 height:100% 로 부모 높이를 받음 · 도면 편집과 같이 메인 칸을 화면 높이로 채움 */
.m02-main:has(> .m02-main__host > .ss) {
box-sizing: border-box;
@@ -0,0 +1,38 @@
"""804-11(요구 693 · 694) — M01 목록 화면 머리 단추 · M02 원문 손잡이 오른쪽 끝.
① M01 단가산출 기초 목록 = [신규 로직]만 · 로직을 열면 [로직 복사/생성] · [로직 삭제] · [되돌림] · [저장] 나타남(숨김 · 흐림 아님)
요소 묶음(인력 · 재료 · 유가 · 기계 · 소요량 · 계수 · 환율 · 요율) · 구조물 옵션 정의 · 별칭 장부 = 항목 고름에 매인 머리 단추 없음(그대로)
② M02 원문 손잡이 = M01(804-7 · 687-17)과 같은 규칙 — 메인 칸 오른쪽 여백만큼 밀어 붙임(단가산출 · 일위대가 등 `m02-main__host` 바로 아래 칸)
"""
from __future__ import annotations
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
def _src(rel: str) -> str:
return (ROOT / rel).read_text(encoding="utf-8")
def test_M01_목록_머리_단추_숨김() -> None:
lab = _src("M01_MasterData/M01_MasterData_UI_LogicLab.ts")
line = "copyButton.hidden = deleteButton.hidden = discardButton.hidden = saveButton.hidden = listing;"
assert line in lab
at = lab.index("const drawEditor = (): void => {")
assert at < lab.index(line) < lab.index("if (!opened) return;", at) # 목록 갈래 return 앞
assert (
" copyButton,\n deleteButton,\n discardButton,\n saveButton,"
in lab.replace("\r\n", "\n")
)
def test_M02_원문_손잡이_오른쪽_끝() -> None:
css = _src("M02_MasterTemplete/M02_MasterTemplete_UI_Style.css")
m02 = ".m02-main__host > .ui-side-panels-row {\n margin-inline-end: calc(-1 * var(--spacing-24));"
assert m02 in css.replace("\r\n", "\n")
assert "padding: var(--spacing-16) var(--spacing-24);" in css # 민 만큼 = 메인 칸 오른쪽 여백
m01 = _src("M01_MasterData/M01_MasterData_UI_LogicLab_Side.css")
cond = 'html:not([data-size="compact"], [data-size="medium"])'
assert f"{cond} .m02-main__host" in css and f"{cond} .m01-master__panel" in m01 # 같은 조건
+1 -1
View File
@@ -315,7 +315,7 @@ def test_구조물_조합_제목_줄_새_항목():
main = _text(M02 / "M02_MasterTemplete_UI_Main.ts")
assert "export function newItemButton(" in main and 'classList.add("m02-main__new")' in main
assert re.search(
r"children: \[\s*back,\s*title,\s*nav\.root,\s*helpSlot,\s*slot,\s*reload,\s*actions\.reset,\s*actions\.del,\s*save,"
r"children: \[\s*back,\s*title,\s*nav\.root,\s*helpSlot,\s*slot,\s*reload,\s*actions\.reset,\s*actions\.del,\s*actions\.undo,\s*save,"
r"\s*\.\.\.\(extras\.newItem \? \[extras\.newItem\] : \[\]\),?\s*\]",
main,
)