Merge remote-tracking branch 'origin/sub_laptop_8' into main_laptop_1
This commit is contained in:
@@ -37,24 +37,25 @@
|
||||
color: var(--color-danger);
|
||||
}
|
||||
|
||||
/* 카드 셋(도면 · 일위대가 · 면적산출) = 한 줄 세 칸 · 일위대가 · 면적산출 넓게(왼쪽) · 도면 맨 오른쪽(163-2) ·
|
||||
/* 카드 셋 = 한 줄 세 칸 · 차례 늘 도면 › 일위대가 › 면적산출 · 폭 2 : 3 : 3(170-1) ·
|
||||
높이 = 큰 창에 맞춤(156-4) */
|
||||
.b08-sheets .m02-struct {
|
||||
--m02-struct-card-h: max(480px, calc(94vh - 14rem));
|
||||
grid-template-columns: minmax(0, 3fr) minmax(0, 3fr) minmax(0, 2fr);
|
||||
grid-template-columns: minmax(0, 2fr) minmax(0, 3fr) minmax(0, 3fr);
|
||||
gap: var(--spacing-16);
|
||||
}
|
||||
|
||||
.b08-sheets .m02-struct > :first-child {
|
||||
order: 3;
|
||||
}
|
||||
|
||||
@media (max-width: 960px) {
|
||||
.b08-sheets .m02-struct {
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
/* 머리 단추 칸(170-2) — 편집 화면 밖에선 숨김 */
|
||||
.b08-sheets .ui-modal__actions[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* 카드 [수정] → 사본 편집 화면(163-3) — 모달 몸통을 채움 · 넣을 값이면 안내 한 줄 위 */
|
||||
.b08-sheets .ui-item-picker__edit-host {
|
||||
flex-direction: column;
|
||||
@@ -88,6 +89,24 @@
|
||||
border-color: var(--color-danger);
|
||||
}
|
||||
|
||||
/* 칩 테두리 상태 색(170-3) — 연결됨 · 마스터가 바뀜 · 고침 있음 · 원본 없어짐(빨강 점선) */
|
||||
.b08-link__chip.is-linked {
|
||||
border-color: var(--color-success);
|
||||
}
|
||||
|
||||
.b08-link__chip.is-changed {
|
||||
border-color: var(--color-warning);
|
||||
}
|
||||
|
||||
.b08-link__chip.is-edited {
|
||||
border-color: var(--color-info);
|
||||
}
|
||||
|
||||
.b08-link__chip.is-gone {
|
||||
border-style: dashed;
|
||||
border-color: var(--color-danger);
|
||||
}
|
||||
|
||||
.b08-link__state {
|
||||
overflow: hidden;
|
||||
max-width: 18rem;
|
||||
|
||||
@@ -145,6 +145,14 @@ export function openStructureSheets(request: SheetsRequest): void {
|
||||
let edited = false; // 사본을 고쳐 저장함 — 닫을 때 집계표 다시 셈(163-3)
|
||||
let editView: EditView | null = null;
|
||||
let modalBody: HTMLElement | null = null;
|
||||
// 머리 단추 칸(170-2) — 공용 모달은 열 때 단추가 있어야 머리에 붙임 → 빈 칸을 미리 넘김 · 편집 화면에서만 채움
|
||||
const head = document.createElement("div");
|
||||
head.className = "ui-modal__actions";
|
||||
head.hidden = true;
|
||||
const clearHead = (): void => {
|
||||
head.replaceChildren();
|
||||
head.hidden = true;
|
||||
};
|
||||
/** 카드 [수정] — 몸통을 사본 편집 화면으로 · [뒤로] = 몸통 되돌리고 그 카드 다시 읽음 */
|
||||
const openEdit: OpenEdit = (copy, name, kind, reload) => {
|
||||
const body = modalBody;
|
||||
@@ -170,12 +178,18 @@ export function openStructureSheets(request: SheetsRequest): void {
|
||||
(saved) => {
|
||||
view.destroy();
|
||||
editView = null;
|
||||
clearHead();
|
||||
body.replaceChildren(...kept);
|
||||
edited ||= saved;
|
||||
if (saved) reload();
|
||||
},
|
||||
);
|
||||
editView = view;
|
||||
// [← 뒤로] · [저장] = 머리 [닫기] 왼쪽 · 크기 = [닫기](sm 뗌)
|
||||
view.back.classList.remove("ui-btn--sm");
|
||||
view.save.classList.remove("ui-btn--sm");
|
||||
head.replaceChildren(view.back, view.save);
|
||||
head.hidden = false;
|
||||
host.append(
|
||||
...(kind === "inputs" ? [note(L("B08_Quantity_Sheets_InputsOnly"))] : []),
|
||||
view.root,
|
||||
@@ -192,9 +206,11 @@ export function openStructureSheets(request: SheetsRequest): void {
|
||||
closeLabel: L("Common_Btn_Close"),
|
||||
wide: true, // 공용 큰 창(96vw × 94vh · 156-4)
|
||||
dialogClass: "b08-sheets",
|
||||
actions: [head],
|
||||
beforeClose: async () => {
|
||||
if (editView && !(await editView.leave())) return false; // 저장 안 한 고침 — 버릴지 물음
|
||||
editView?.destroy();
|
||||
clearHead();
|
||||
closed = true; // 아직 불러오는 중이면 뒤늦게 카드를 만들지 않음
|
||||
handles.forEach((cards) => cards.destroy());
|
||||
if (edited) request.onLinked(); // 사본을 고쳤으면 집계표 다시 셈(연결 저장과 같은 꼴)
|
||||
|
||||
@@ -102,10 +102,11 @@ async function update(projectId: string, info: ProjectItemInfo): Promise<boolean
|
||||
}
|
||||
}
|
||||
|
||||
/** 칩 배지 · [업데이트] — 사본 목록(원본판 대조)을 뒤에서 받아 칠함 · 못 받으면 단추 꺼진 채. */
|
||||
/** 칩 배지 · 테두리 색 · [업데이트] — 사본 목록(원본판 대조)을 뒤에서 받아 칠함 · 못 받으면 단추 꺼진 채. */
|
||||
async function paintCopyState(
|
||||
projectId: string,
|
||||
slot: UnlinkedSlot,
|
||||
chip: HTMLElement,
|
||||
badges: HTMLElement,
|
||||
button: HTMLButtonElement,
|
||||
): Promise<ProjectItemInfo | null> {
|
||||
@@ -124,6 +125,15 @@ async function paintCopyState(
|
||||
if (info.고침) badges.append(badge("B08_Quantity_Link_Edited", false));
|
||||
if (info.원본없음) badges.append(badge("B08_Quantity_Link_Gone", true));
|
||||
else if (info.마스터바뀜) badges.append(badge("B08_Quantity_Link_Changed", true));
|
||||
// 테두리 상태 색(170-3) — 앞섬 = 없어짐 › 바뀜 › 고침 › 연결
|
||||
const tone = info.원본없음
|
||||
? "is-gone"
|
||||
: info.마스터바뀜
|
||||
? "is-changed"
|
||||
: info.고침
|
||||
? "is-edited"
|
||||
: null;
|
||||
if (tone) chip.classList.replace("is-linked", tone);
|
||||
button.disabled = !info.마스터바뀜 || !!info.원본없음;
|
||||
button.title = button.disabled
|
||||
? L("B08_Quantity_Link_UpdateSame")
|
||||
@@ -163,7 +173,7 @@ export function linkSection(
|
||||
line.append(head);
|
||||
for (const slot of facility.slots) {
|
||||
const chip = document.createElement("span");
|
||||
chip.className = slot.linked ? "b08-link__chip" : "b08-link__chip is-missing";
|
||||
chip.className = slot.linked ? "b08-link__chip is-linked" : "b08-link__chip is-missing";
|
||||
chip.dataset.b08LinkSlot = slot.copy;
|
||||
const name = document.createElement("span");
|
||||
name.className = "b08-link__name";
|
||||
@@ -204,7 +214,9 @@ export function linkSection(
|
||||
);
|
||||
updateButton.disabled = true; // 원본판 대조를 받기 전엔 꺼 둠
|
||||
buttons.append(updateButton);
|
||||
void paintCopyState(projectId, slot, badges, updateButton).then((got) => (info = got));
|
||||
void paintCopyState(projectId, slot, chip, badges, updateButton).then(
|
||||
(got) => (info = got),
|
||||
);
|
||||
}
|
||||
chip.append(name, state, badges, buttons);
|
||||
line.append(chip);
|
||||
|
||||
@@ -19,6 +19,11 @@
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
/* 구조물 집계표 보조 탭 줄 자리(170-4) — 단추는 높이만 채움 */
|
||||
.b08-quantity__tabs--blank > .ui-btn {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
/* 자재 집계표 셋(121) — 안내 글 · [저장] 한 줄 */
|
||||
.b08-material__toolbar {
|
||||
display: flex;
|
||||
|
||||
@@ -87,7 +87,18 @@ function renderTab(host: HTMLElement, tab: TabId, projectId: string | null): voi
|
||||
const view = document.createElement("div");
|
||||
host.replaceChildren(view);
|
||||
if (tab === "material") return renderMaterials(view, projectId);
|
||||
if (tab === "summary" && projectId) return void renderStructureSummary(view, projectId);
|
||||
if (tab === "summary" && projectId) {
|
||||
// 보조 탭 줄 자리(170-4) — 보이지 않는 sm 단추 한 줄 · 자재 탭과 같은 높이라 탭 바꿀 때 안 튐
|
||||
const blank = document.createElement("div");
|
||||
blank.className = "b08-quantity__tabs b08-quantity__tabs--blank";
|
||||
blank.setAttribute("aria-hidden", "true");
|
||||
const ghost = createButton({ label: L(SHEETS[0].label), variant: "ghost", size: "sm" });
|
||||
ghost.tabIndex = -1;
|
||||
blank.append(ghost);
|
||||
const body = document.createElement("div");
|
||||
view.append(blank, body);
|
||||
return void renderStructureSummary(body, projectId);
|
||||
}
|
||||
if (tab === "hydrology" && projectId) return void renderHydro(view, projectId);
|
||||
if (tab === "earthwork" && projectId) return void renderEarthwork(view, projectId);
|
||||
view.append(pendingNote());
|
||||
|
||||
@@ -109,7 +109,7 @@ def test_156_4_구조물도_모달_큰_창_세_칸():
|
||||
assert "wide: true" in sheets
|
||||
css = (ROOT / "B08_Quantity/B08_Quantity_UI_Drawing.css").read_text(encoding="utf-8")
|
||||
assert "min(1320px" not in css # 자기 폭 없음 — 공용 wide 그대로
|
||||
# 163-2 — 일위대가 · 면적산출 넓게(왼쪽) · 도면 좁게 맨 오른쪽(order 3)
|
||||
assert "minmax(0, 3fr) minmax(0, 3fr) minmax(0, 2fr)" in css
|
||||
assert ".b08-sheets .m02-struct > :first-child {\n order: 3;" in css
|
||||
# 170-1 — 차례 늘 도면 › 일위대가 › 면적산출(뒤집기 없음) · 폭 2 : 3 : 3
|
||||
assert "minmax(0, 2fr) minmax(0, 3fr) minmax(0, 3fr)" in css
|
||||
assert " order: " not in css
|
||||
assert ".b08-sheets__combo + .b08-sheets__combo" in css
|
||||
|
||||
@@ -17,7 +17,7 @@ LOCALE = (ROOT / "ui_template/ui_template_locale_b4.ts").read_text(encoding="utf
|
||||
|
||||
def test_163_1_칩_한_줄_공용_접기():
|
||||
assert 'line.className = "b08-link__row"' in LINK # 시설마다 한 줄
|
||||
assert '"b08-link__chip"' in LINK and "chip.dataset.b08LinkSlot = slot.copy" in LINK
|
||||
assert '"b08-link__chip is-linked"' in LINK and "chip.dataset.b08LinkSlot = slot.copy" in LINK
|
||||
# 모두 연결이면 접힘 · 미연결 있으면 펼침
|
||||
assert 'ui-collapsible${missing ? "" : " is-collapsed"}' in LINK
|
||||
assert "ui-collapsible__title" in LINK and "attachCollapsible(section)" in LINK
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""170 B08 구조물도 모달 — 머리 단추(170-2) · 칩 테두리 상태 색(170-3) · 집계표 보조 탭 줄 자리(170-4).
|
||||
|
||||
화면 글 판정 — 공용 모달(ui_template_modal)은 안 고침.
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
PAGE = (ROOT / "B08_Quantity/B08_Quantity_UI_Page.ts").read_text(encoding="utf-8")
|
||||
PAGE_CSS = (ROOT / "B08_Quantity/B08_Quantity_UI_Page.css").read_text(encoding="utf-8")
|
||||
|
||||
|
||||
def test_170_4_집계표_보조_탭_줄_자리():
|
||||
branch = PAGE.split('if (tab === "summary" && projectId) {', 1)[1].split("\n }", 1)[0]
|
||||
# 자재 탭 보조 탭 줄과 같은 클래스 · sm 단추 하나(보이지 않음 · 초점 안 감)
|
||||
assert '"b08-quantity__tabs b08-quantity__tabs--blank"' in branch
|
||||
assert 'size: "sm"' in branch and "ghost.tabIndex = -1" in branch
|
||||
assert 'setAttribute("aria-hidden", "true")' in branch
|
||||
# 집계표는 줄 아래 몸통에 그림(replaceChildren 이 빈 줄을 지우지 않게)
|
||||
assert (
|
||||
"view.append(blank, body)" in branch and "renderStructureSummary(body, projectId)" in branch
|
||||
)
|
||||
assert ".b08-quantity__tabs--blank > .ui-btn {\n visibility: hidden;" in PAGE_CSS.replace(
|
||||
"\r\n", "\n"
|
||||
)
|
||||
|
||||
|
||||
LINK = (ROOT / "B08_Quantity/B08_Quantity_UI_Link.ts").read_text(encoding="utf-8")
|
||||
DRAWING_CSS = (ROOT / "B08_Quantity/B08_Quantity_UI_Drawing.css").read_text(encoding="utf-8")
|
||||
THEME = (ROOT / "ui_template/ui_template_theme.css").read_text(encoding="utf-8")
|
||||
|
||||
|
||||
def test_170_3_칩_테두리_상태_색():
|
||||
assert 'slot.linked ? "b08-link__chip is-linked" : "b08-link__chip is-missing"' in LINK
|
||||
assert "paintCopyState(projectId, slot, chip, badges, updateButton)" in LINK
|
||||
# 앞섬 = 없어짐 › 바뀜 › 고침 › 연결
|
||||
tone = LINK.split("const tone = info.원본없음", 1)[1].split(";", 1)[0]
|
||||
assert tone.index('"is-gone"') < tone.index('"is-changed"') < tone.index('"is-edited"')
|
||||
assert 'chip.classList.replace("is-linked", tone)' in LINK
|
||||
for cls, token in (
|
||||
("is-linked", "success"),
|
||||
("is-missing", "danger"),
|
||||
("is-changed", "warning"),
|
||||
("is-edited", "info"),
|
||||
("is-gone", "danger"),
|
||||
):
|
||||
rule = DRAWING_CSS.split(f".b08-link__chip.{cls} {{", 1)[1].split("}", 1)[0]
|
||||
assert f"border-color: var(--color-{token});" in rule
|
||||
assert f"--color-{token}:" in THEME # 밝음 · 어두움 토큰
|
||||
assert (
|
||||
"border-style: dashed;"
|
||||
in DRAWING_CSS.split(".b08-link__chip.is-gone {", 1)[1].split("}", 1)[0]
|
||||
)
|
||||
|
||||
|
||||
DRAWING = (ROOT / "B08_Quantity/B08_Quantity_UI_Drawing.ts").read_text(encoding="utf-8")
|
||||
EDIT = (ROOT / "ui_template/ui_template_item_picker_edit.ts").read_text(encoding="utf-8")
|
||||
PICKER = (ROOT / "ui_template/ui_template_item_picker.ts").read_text(encoding="utf-8")
|
||||
MODAL = (ROOT / "ui_template/ui_template_modal.ts").read_text(encoding="utf-8")
|
||||
|
||||
|
||||
def test_170_2_편집_화면_단추_모달_머리():
|
||||
# 편집 화면이 두 단추를 내보냄 · 고르기 모달(142-1)은 그대로 안 씀
|
||||
assert "back: HTMLButtonElement;" in EDIT and "save: HTMLButtonElement;" in EDIT
|
||||
assert "return { root, back, save," in EDIT
|
||||
assert "view.back" not in PICKER and "view.save" not in PICKER
|
||||
# 빈 머리 칸을 열 때 미리 넘김(공용 모달은 mount 때 자식 있을 때만 머리에 붙임)
|
||||
assert "if (actions.childElementCount) closeButton.before(actions)" in MODAL
|
||||
assert "actions: [head]," in DRAWING and "head.hidden = true;" in DRAWING
|
||||
# 들어갈 때 옮김 · 크기 = [닫기](md) · 차례 [← 뒤로] [저장] [닫기]
|
||||
assert 'view.back.classList.remove("ui-btn--sm")' in DRAWING
|
||||
assert 'view.save.classList.remove("ui-btn--sm")' in DRAWING
|
||||
assert "head.replaceChildren(view.back, view.save);\n head.hidden = false;" in DRAWING
|
||||
# 나올 때([뒤로] · 닫기) 거둠
|
||||
back = DRAWING.split("(saved) => {", 1)[1].split("},", 1)[0]
|
||||
assert "clearHead();" in back
|
||||
closing = DRAWING.split("beforeClose: async () => {", 1)[1].split("return true;", 1)[0]
|
||||
assert "clearHead();" in closing
|
||||
css = DRAWING_CSS
|
||||
assert ".b08-sheets .ui-modal__actions[hidden] {\n display: none;" in css
|
||||
@@ -20,6 +20,9 @@ import { laborValueData, PART_OF } from "./ui_template_item_picker_project";
|
||||
|
||||
export interface EditView {
|
||||
root: HTMLElement;
|
||||
/** [← 뒤로] · [저장] — 모달 머리로 옮겨 쓸 수 있음(B08 · 170-2) */
|
||||
back: HTMLButtonElement;
|
||||
save: HTMLButtonElement;
|
||||
/** 붙인 뒤 부름 — 편집 부품을 받아 세움 */
|
||||
start: () => void;
|
||||
isDirty: () => boolean;
|
||||
@@ -212,5 +215,5 @@ export function createEditView(req: EditRequest, onBack: (saved: boolean) => voi
|
||||
if (await leave()) onBack(saved);
|
||||
});
|
||||
|
||||
return { root, start: () => void start(), isDirty: () => dirty, leave, destroy };
|
||||
return { root, back, save, start: () => void start(), isDirty: () => dirty, leave, destroy };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user