Merge remote-tracking branch 'origin/sub_laptop_4' into main_laptop_1
This commit is contained in:
@@ -2,12 +2,12 @@
|
||||
* B05_Profile_UI_Structures_Balloon.ts
|
||||
* 종단 구조물 알약 정보 라벨 줄(54-14) — 화면 요소 없는 순수 규칙. B05 · B06 알약 레인이 같이 씀.
|
||||
*
|
||||
* · 줄 = 종류 이름(기타 = 손 이름) · 기준 측점(구간 = 시작 ~ 끝) · 컨테이너마다 첫 옵션 값 한 줄
|
||||
* (「관: 관경 1000」) · 컨테이너가 없으면 앞 두 줄만 · 메모 · 그 밖 옵션은 안 씀
|
||||
* · 2행 — 1행 = 종류 이름(기타 = 손 이름) + 기준 측점(구간 = 시작 ~ 끝) · 2행 = 컨테이너마다 첫 옵션
|
||||
* 값만 「 · 」 로 이음(제목 · 옵션 이름 없음 · 빈 값은 건너뜀) · 값이 하나도 없으면 1행만 · 메모 · 그 밖 옵션은 안 씀
|
||||
* · 컨테이너 = 좌측 패널 폼과 같은 상자 — 명세 종류(`STRUCTURE_SPECS`)는 명세 컨테이너(선택 컨테이너는
|
||||
* 인스턴스마다) · 그 밖 종류는 정의 옵션 그룹(`StructureType.groups`)
|
||||
* · 첫 옵션 = 지금 보이는 첫 입력 칸(자동 칸 · 갈래가 없어 가린 칸 · 끈 칸은 건너뜀)
|
||||
* · 값 `auto` = 「(자동)」 · 빈 값 = 「—」(54-39 · 기본값으로 안 바꿈 · 기본값은 새 구조물 칸에 실제 값)
|
||||
* · 값 `auto` = 「(자동)」 · 빈 값은 건너뜀(기본값으로 안 바꿈 · 기본값은 새 구조물 칸에 실제 값)
|
||||
* ========================================================================== */
|
||||
|
||||
import type { StructureInstance, StructureType } from "./B05_Profile_Api_Structures";
|
||||
@@ -18,8 +18,6 @@ import {
|
||||
branchOf,
|
||||
choiceLabel,
|
||||
choicesOf,
|
||||
containerTitle,
|
||||
itemLabel,
|
||||
readState,
|
||||
type Instance,
|
||||
type SpecContainer,
|
||||
@@ -28,12 +26,12 @@ import {
|
||||
} from "../A00_Common/design/design_structure_spec";
|
||||
|
||||
const AUTO_MARK = "(자동)";
|
||||
const NONE = "—";
|
||||
const SEP = " · ";
|
||||
|
||||
/** 선택 컨테이너 개수를 밖에서 정함(배관 흙막이 = 유출 단 수 · 폼 `hooks.count` 와 같은 값). */
|
||||
export type ContainerCount = (containerId: string) => number | null | undefined;
|
||||
|
||||
/** 라벨 줄 — [이름, 기준 측점, ...컨테이너 줄]. */
|
||||
/** 라벨 줄 — [이름 기준 측점, 컨테이너 값 이음?]. */
|
||||
export function balloonLines(
|
||||
structure: StructureInstance,
|
||||
type: StructureType | undefined,
|
||||
@@ -41,13 +39,14 @@ export function balloonLines(
|
||||
count?: ContainerCount,
|
||||
): string[] {
|
||||
const spec = STRUCTURE_SPECS[structure.type_id];
|
||||
const rest = spec
|
||||
? specLines(spec, structure, type, count)
|
||||
: groupLines(structure.options ?? {}, type);
|
||||
return [head.name, head.position, ...rest];
|
||||
const values = (
|
||||
spec ? specLines(spec, structure, type, count) : groupLines(structure.options ?? {}, type)
|
||||
).filter(Boolean);
|
||||
const first = `${head.name} ${head.position}`;
|
||||
return values.length ? [first, values.join(SEP)] : [first];
|
||||
}
|
||||
|
||||
/** 명세 컨테이너 줄 — 폼(`Drainage_Facility.setFacility` · `paintStructureSpec`)과 같은 값 · 이름. */
|
||||
/** 명세 컨테이너 값 — 폼(`Drainage_Facility.setFacility` · `paintStructureSpec`)과 같은 값 · 이름(빈 값 = ""). */
|
||||
function specLines(
|
||||
spec: StructureSpec,
|
||||
structure: StructureInstance,
|
||||
@@ -61,19 +60,14 @@ function specLines(
|
||||
names = pipeNames;
|
||||
}
|
||||
const state = formState(spec, structure.type_id, structure.options ?? {});
|
||||
const groups = Object.fromEntries((type?.groups ?? []).map((g) => [g.id, g.name]));
|
||||
return spec.containers.flatMap((c) => {
|
||||
const title = containerTitle(c, names, groups[c.link]);
|
||||
return instancesOf(c, state[c.id] ?? [], count).map((values, index) => {
|
||||
return spec.containers.flatMap((c) =>
|
||||
instancesOf(c, state[c.id] ?? [], count).map((values) => {
|
||||
const item = c.items.find(
|
||||
(entry) => entry.kind !== "auto" && (!entry.by || branchOf(entry, values)),
|
||||
);
|
||||
const head = c.required ? title : `${title} ${index + 1}`;
|
||||
return item
|
||||
? `${head}: ${itemLabel(c, item, names)} ${specValue(c, item, values, names)}`
|
||||
: `${head}: ${NONE}`;
|
||||
});
|
||||
});
|
||||
return item ? specValue(c, item, values, names) : "";
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
/** 저장 값 → 폼 칸 값 — 폼(`Drainage_Facility.setFacility`)과 같은 손질(BOX 옛 설치 · 본체 규격 ·
|
||||
@@ -116,10 +110,10 @@ function specValue(
|
||||
};
|
||||
const raw = (values[item.id] ?? "").trim();
|
||||
if (raw === "auto") return AUTO_MARK;
|
||||
return raw ? shown(raw) : NONE;
|
||||
return raw ? shown(raw) : "";
|
||||
}
|
||||
|
||||
/** 정의 옵션 그룹 줄 — 그룹마다 첫 옵션(정의에 있고 켜진 칸). */
|
||||
/** 정의 옵션 그룹 값 — 그룹마다 첫 옵션(정의에 있고 켜진 칸 · 빈 값 = ""). */
|
||||
function groupLines(
|
||||
options: Record<string, string | number>,
|
||||
type: StructureType | undefined,
|
||||
@@ -129,11 +123,9 @@ function groupLines(
|
||||
const option = group.options
|
||||
.map((key) => byKey.get(key))
|
||||
.find((entry) => entry && entry.visible !== false && entry.enabled !== false);
|
||||
if (!option) return `${group.name}: ${NONE}`;
|
||||
const unit = option.unit ?? "";
|
||||
if (!option) return "";
|
||||
const raw = options[option.key];
|
||||
const value =
|
||||
raw === "auto" ? AUTO_MARK : raw !== undefined && raw !== "" ? `${raw}${unit}` : NONE;
|
||||
return `${group.name}: ${option.label} ${value}`;
|
||||
if (raw === "auto") return AUTO_MARK;
|
||||
return raw !== undefined && raw !== "" ? `${raw}${option.unit ?? ""}` : "";
|
||||
});
|
||||
}
|
||||
|
||||
@@ -263,7 +263,7 @@ export function buildStructureLane(options: StructureMarksOptions): HTMLElement
|
||||
* 알약 **위**로 띄운다 — 아래로 펴면 도면 테이블에 가려 안 보인다(2026-08-18
|
||||
* 사용자 지시). 2행 구조에서 1행(윗줄)은 좌측 위, 2행(아랫줄)은 우측 위로
|
||||
* 전개해 서로 겹치지 않게 하고, 솔로 알약은 위 중앙이다.
|
||||
* 줄 = 이름 · 기준 측점 · 컨테이너마다 첫 옵션 값(`balloonLines` · 54-14) · 그래프 바닥 ▼ 단추를 가리지
|
||||
* 줄 = 2행 — 이름 + 기준 측점 · 컨테이너마다 첫 옵션 값 이음(`balloonLines` · 54-14 · 54-46) · 그래프 바닥 ▼ 단추를 가리지
|
||||
* 않게 단추 높이만큼 더 위 · 위가 잘리면 알약 아래로. */
|
||||
function buildBalloon(
|
||||
lines: string[],
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""54-14 종단 구조물 알약 정보 라벨 — 중요한 것만.
|
||||
|
||||
• 줄 = 이름 · 기준 측점 · 컨테이너마다 첫 옵션 값 한 줄(컨테이너가 없으면 앞 두 줄만)
|
||||
• 2행 — 1행 = 이름 + 기준 측점 · 2행 = 컨테이너마다 첫 옵션 값만 「 · 」 로 이음(54-46 · 제목 없음)
|
||||
• 컨테이너 = 좌측 패널 폼과 같은 상자(명세 컨테이너 · 선택 컨테이너는 인스턴스마다 · 정의 옵션 그룹)
|
||||
• 빈 값 = 등록부 기본값 「(기본)」 · 값 auto = 「(자동)」 · 그것도 없으면 「—」 · 메모 · 그 밖 옵션은 안 씀
|
||||
• 빈 값은 건너뜀 · 값 auto = 「(자동)」 · 값이 하나도 없으면 1행만 · 메모 · 그 밖 옵션은 안 씀
|
||||
• 라벨은 ▲▼ 단추층 위 · 단추 높이만큼 위로 비킴 · B05 · B06 은 같은 레인 — 실제 화면은 ORCA 로 따로 본다.
|
||||
화면 그릇 없이 Node 헬퍼가 TS 를 트랜스파일해 돌린다(`helper_54_14_balloon_lines.cjs`).
|
||||
"""
|
||||
@@ -33,56 +33,37 @@ def out():
|
||||
return json.loads(proc.stdout)
|
||||
|
||||
|
||||
def test_배관_컨테이너마다_첫_값_한_줄(out):
|
||||
assert out["pipeFull"] == [
|
||||
"이름",
|
||||
"5+0",
|
||||
"관: 관경 1200",
|
||||
"관 보호공(유입): 구조 집수정",
|
||||
"관 보호공(유출): 구조 흙막이",
|
||||
"흙막이 1: 형태 돌망태", # 구조 칸 없음(54-30)
|
||||
"도수로/돌붙임 1: 구조 도수로",
|
||||
]
|
||||
def test_배관_컨테이너마다_첫_값만_한_줄로_이음(out):
|
||||
# 흙막이 1 은 구조 칸 없음 — 형태(54-30)
|
||||
assert out["pipeFull"] == ["이름 5+0", "1200 · 집수정 · 흙막이 · 돌망태 · 도수로"]
|
||||
|
||||
|
||||
def test_빈_값은_등록부_기본값에_기본_표시(out):
|
||||
assert out["pipeBlank"] == [
|
||||
"이름",
|
||||
"5+0",
|
||||
"관: 관경 1000 (기본)",
|
||||
"관 보호공(유입): 구조 흙막이 (기본)",
|
||||
"관 보호공(유출): 구조 —",
|
||||
]
|
||||
def test_빈_값은_건너뜀(out):
|
||||
assert out["pipeBlank"] == ["이름 5+0"]
|
||||
|
||||
|
||||
def test_흙막이_상자_수는_밖에서_정한_단_수(out):
|
||||
assert out["pipeCount"][-2:] == ["흙막이 1: 형태 —", "흙막이 2: 형태 —"]
|
||||
assert len(out["pipeCount"]) == 2 + 3 + 2
|
||||
# 저장 1 벌(형태 빈 값) · 모자란 둘째도 빈 값 — 둘 다 건너뜀
|
||||
assert out["pipeCount"] == ["이름 5+0"]
|
||||
|
||||
|
||||
def test_박스_본체_규격은_폭_높이로_되찾음(out):
|
||||
assert out["boxPreset"] == [
|
||||
"이름",
|
||||
"5+0",
|
||||
"본체: 본체 규격 3.0 × 3.0",
|
||||
"유입 구조: 구조 날개벽",
|
||||
"유출 구조: 구조 —",
|
||||
]
|
||||
assert out["boxBlank"][2] == "본체: 본체 규격 2.0 × 2.0 (기본)"
|
||||
assert out["boxPreset"] == ["이름 5+0", "3.0 × 3.0 · 날개벽"]
|
||||
assert out["boxBlank"] == ["이름 5+0"]
|
||||
|
||||
|
||||
def test_정의_옵션_그룹은_그룹마다_첫_켜진_옵션(out):
|
||||
assert out["underdrain"] == ["이름", "5+0", "맹암거: 높이 0.6m"]
|
||||
assert out["underdrainBlank"] == ["이름", "5+0", "맹암거: 높이 —"]
|
||||
# 끈 칸(높이)은 건너뜀 · 빈 값은 기본값
|
||||
assert out["wall"] == ["이름", "5+0", "상세 내용: 길이 10m (기본)"]
|
||||
assert out["wallAuto"] == ["이름", "5+0", "상세 내용: 길이 (자동)"]
|
||||
assert out["underdrain"] == ["이름 5+0", "0.6m"]
|
||||
assert out["underdrainBlank"] == ["이름 5+0"]
|
||||
# 끈 칸(높이)은 건너뜀
|
||||
assert out["wall"] == ["이름 5+0"]
|
||||
assert out["wallAuto"] == ["이름 5+0", "(자동)"]
|
||||
|
||||
|
||||
def test_컨테이너가_없으면_이름과_기준_측점만(out):
|
||||
assert out["mirror"] == ["이름", "5+0"]
|
||||
assert out["etcEmpty"] == ["이름", "5+0"]
|
||||
assert out["etcNote"] == ["이름", "5+0", "상세 내용 1: 비고 시험 비고"]
|
||||
def test_컨테이너가_없으면_1행만(out):
|
||||
assert out["mirror"] == ["이름 5+0"]
|
||||
assert out["etcEmpty"] == ["이름 5+0"]
|
||||
assert out["etcNote"] == ["이름 5+0", "시험 비고"]
|
||||
|
||||
|
||||
def test_라벨은_단추층_위로_비켜_뜬다():
|
||||
|
||||
Reference in New Issue
Block a user