Merge remote-tracking branch 'origin/sub_laptop_6' into main_laptop_1
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
/* ☰ [설정] 창(PLAN 332-2) — 탭 줄 = 부품 보조 탭 · 휠 방향 = 부품 라디오 · CAD 별칭 표 = 부품 표 */
|
||||
/* ☰ [설정] 창(PLAN 332-2) — 탭 줄 = 부품 보조 탭 · 휠 방향 = 부품 라디오 · CAD 별칭 표 = 부품 표 · 규칙 줄 = 부품 값 목록 규칙 꼴 */
|
||||
.app-settings .ui-text[hidden] {
|
||||
display: none; /* 부품 보조 글은 display:flex 라 hidden 을 이김 */
|
||||
}
|
||||
@@ -28,29 +28,3 @@
|
||||
top: 0;
|
||||
background: var(--color-surface-raised);
|
||||
}
|
||||
|
||||
.app-settings__rules {
|
||||
margin: 0 0 var(--spacing-8);
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.app-settings__rules > li {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 15em) 1fr;
|
||||
align-items: center;
|
||||
gap: var(--spacing-12);
|
||||
padding: var(--spacing-4) 0;
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.app-settings__keys {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--spacing-4);
|
||||
}
|
||||
|
||||
.app-settings__keys:only-child {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
import { createBadge } from "@ui/ui_template_badge";
|
||||
import { createCheckbox } from "@ui/ui_template_checkbox";
|
||||
import { createKv } from "@ui/ui_template_kv";
|
||||
import { createButton, el, showToast } from "@ui/ui_template_elements";
|
||||
import { askLeave } from "@ui/ui_template_leave_dialog";
|
||||
import { t as L } from "@ui/ui_template_locale";
|
||||
@@ -58,24 +59,18 @@ const normalize = (p: InputPrefs): InputPrefs => ({
|
||||
});
|
||||
const snapshot = (p: InputPrefs): string => JSON.stringify(normalize(p));
|
||||
|
||||
/** 규칙 목록 — 한 줄 = 「입력 = 동작」 · 입력은 키 칩(` · ` 로 여럿) · 동작 없는 줄은 칩만 */
|
||||
/** 규칙 목록 = 값 목록 부품 규칙 꼴 — 한 줄 = 「입력 = 동작」 · 입력은 키 칩(` · ` 로 여럿) · 동작 없는 줄은 칩만 */
|
||||
function ruleList(keys: LocaleKey[]): HTMLElement {
|
||||
const items = keys
|
||||
const rows = keys
|
||||
.flatMap((key) => L(key).split("\n"))
|
||||
.map((line) => {
|
||||
const at = line.indexOf(" = ");
|
||||
const input = at < 0 ? line : line.slice(0, at);
|
||||
return el("li", {
|
||||
children: [
|
||||
el("span", {
|
||||
className: "app-settings__keys",
|
||||
children: input.split(" · ").map((text) => createBadge(text, "neutral")),
|
||||
}),
|
||||
...(at < 0 ? [] : [el("span", { text: line.slice(at + 3) })]),
|
||||
],
|
||||
});
|
||||
const chips = document.createDocumentFragment();
|
||||
chips.append(...input.split(" · ").map((text) => createBadge(text, "neutral")));
|
||||
return { label: chips, value: at < 0 ? undefined : line.slice(at + 3) };
|
||||
});
|
||||
return el("ul", { className: "app-settings__rules", children: items });
|
||||
return createKv(rows, { rule: true });
|
||||
}
|
||||
|
||||
function mouseTab(draft: InputPrefs): HTMLElement {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
[
|
||||
{"name":"D01_00_overview","route":"#/d01-plan-register","project":"none","before":[{"wait":".b02-proj__grid"},{"wait_ms":600}],"clip":null,"callouts":[{"n":1,"sel":".b02-proj__grid > .ui-field:nth-child(1)"},{"n":2,"sel":".b02-proj__grid > .ui-field:nth-child(3)"},{"n":3,"sel":".b02-proj__grid > .ui-field:nth-child(5)"},{"n":4,"sel":".b02-proj__grid > .ui-field:nth-child(7)"},{"n":5,"sel":".b02-proj__grid > .ui-field:nth-child(8)"},{"n":6,"sel":".b02-proj__actions","pos":"br"}]},
|
||||
{"name":"D01_00_overview","route":"#/d01-plan-register","project":"none","before":[{"wait":".ui-form-grid"},{"wait_ms":600}],"clip":null,"callouts":[{"n":1,"sel":".ui-form-grid > .ui-field:nth-child(1)"},{"n":2,"sel":".ui-form-grid > .ui-field:nth-child(3)"},{"n":3,"sel":".ui-form-grid > .ui-field:nth-child(5)"},{"n":4,"sel":".ui-form-grid > .ui-field:nth-child(7)"},{"n":5,"sel":".ui-form-grid > .ui-field:nth-child(8)"},{"n":6,"sel":".ui-general-block__actions--end","pos":"br"}]},
|
||||
{"name":"D01_01_dashboard_new","route":"#/b01-account","project":"none","before":[{"wait_ms":2500},{"eval":"[...document.querySelectorAll('button')].find(x=>x.textContent.trim()==='+ 새 프로젝트'&&x.offsetParent).click()"},{"wait":".b01-new-project__grid"},{"wait_ms":600}],"clip":".b01-new-project","callouts":[{"n":1,"sel":"[data-new-project=\"plan\"]"}]},
|
||||
{"name":"D01_02_year_region","route":"#/d01-plan-register","project":"none","before":[{"wait":".b02-proj__grid"},{"fill":".b02-proj__grid > .ui-field:nth-child(1) input","value":"2027"},{"fill":".b02-proj__grid > .ui-field:nth-child(2) input","value":"2029"},{"fill":".b02-proj__grid > .ui-field:nth-child(3) input","value":"울진군 금강송면"},{"wait_ms":400}],"clip":".b02-proj__grid","callouts":[{"n":1,"sel":".b02-proj__grid > .ui-field:nth-child(1)"},{"n":2,"sel":".b02-proj__grid > .ui-field:nth-child(2)"},{"n":3,"sel":".b02-proj__grid > .ui-field:nth-child(3)"},{"n":4,"sel":".b02-proj__grid > .ui-field:nth-child(4)"}]},
|
||||
{"name":"D01_03_type_speed","route":"#/d01-plan-register","project":"none","before":[{"wait":".b02-proj__grid"},{"select":".b02-proj__grid > .ui-field:nth-child(6) select","value":"40"},{"wait_ms":400}],"clip":".b02-proj__grid","callouts":[{"n":1,"sel":".b02-proj__grid > .ui-field:nth-child(5)"},{"n":2,"sel":".b02-proj__grid > .ui-field:nth-child(6)"}]},
|
||||
{"name":"D01_04_name","route":"#/d01-plan-register","project":"none","before":[{"wait":".b02-proj__grid"},{"fill":".b02-proj__grid > .ui-field:nth-child(3) input","value":"울진군 금강송면"},{"wait_ms":400}],"clip":".b02-proj__grid","callouts":[{"n":1,"sel":".b02-proj__grid > .ui-field:nth-child(7)"}]},
|
||||
{"name":"D01_05_chainage","route":"#/d01-plan-register","project":"none","before":[{"wait":".b02-proj__grid"},{"fill":".b02-proj__grid > .ui-field:nth-child(8) input","value":"0"},{"fill":".b02-proj__grid > .ui-field:nth-child(9) input","value":"1100"},{"wait_ms":400}],"clip":".b02-proj__grid","callouts":[{"n":1,"sel":".b02-proj__grid > .ui-field:nth-child(8)"},{"n":2,"sel":".b02-proj__grid > .ui-field:nth-child(9)"}]},
|
||||
{"name":"D01_06_errors","route":"#/d01-plan-register","project":"none","before":[{"wait":".b02-proj__grid"},{"fill":".b02-proj__grid > .ui-field:nth-child(1) input","value":"2029"},{"fill":".b02-proj__grid > .ui-field:nth-child(2) input","value":"2027"},{"fill":".b02-proj__grid > .ui-field:nth-child(8) input","value":"500"},{"fill":".b02-proj__grid > .ui-field:nth-child(9) input","value":"100"},{"click":".b02-proj__actions button:last-child"},{"wait":".ui-field__error.is-visible"},{"wait_ms":400}],"clip":".b02-proj__grid","callouts":[{"n":1,"sel":".b02-proj__grid > .ui-field:nth-child(2)"},{"n":2,"sel":".b02-proj__grid > .ui-field:nth-child(3)"},{"n":3,"sel":".b02-proj__grid > .ui-field:nth-child(9)"}]},
|
||||
{"name":"D01_07_buttons","route":"#/d01-plan-register","project":"none","before":[{"wait":".b02-proj__actions"}],"clip":".b02-proj__actions","callouts":[{"n":1,"sel":".b02-proj__actions button:nth-child(1)"},{"n":2,"sel":".b02-proj__actions button:nth-child(2)"},{"n":3,"sel":".b02-proj__actions button:nth-child(3)"}]}
|
||||
{"name":"D01_02_year_region","route":"#/d01-plan-register","project":"none","before":[{"wait":".ui-form-grid"},{"fill":".ui-form-grid > .ui-field:nth-child(1) input","value":"2027"},{"fill":".ui-form-grid > .ui-field:nth-child(2) input","value":"2029"},{"fill":".ui-form-grid > .ui-field:nth-child(3) input","value":"울진군 금강송면"},{"wait_ms":400}],"clip":".ui-form-grid","callouts":[{"n":1,"sel":".ui-form-grid > .ui-field:nth-child(1)"},{"n":2,"sel":".ui-form-grid > .ui-field:nth-child(2)"},{"n":3,"sel":".ui-form-grid > .ui-field:nth-child(3)"},{"n":4,"sel":".ui-form-grid > .ui-field:nth-child(4)"}]},
|
||||
{"name":"D01_03_type_speed","route":"#/d01-plan-register","project":"none","before":[{"wait":".ui-form-grid"},{"select":".ui-form-grid > .ui-field:nth-child(6) select","value":"40"},{"wait_ms":400}],"clip":".ui-form-grid","callouts":[{"n":1,"sel":".ui-form-grid > .ui-field:nth-child(5)"},{"n":2,"sel":".ui-form-grid > .ui-field:nth-child(6)"}]},
|
||||
{"name":"D01_04_name","route":"#/d01-plan-register","project":"none","before":[{"wait":".ui-form-grid"},{"fill":".ui-form-grid > .ui-field:nth-child(3) input","value":"울진군 금강송면"},{"wait_ms":400}],"clip":".ui-form-grid","callouts":[{"n":1,"sel":".ui-form-grid > .ui-field:nth-child(7)"}]},
|
||||
{"name":"D01_05_chainage","route":"#/d01-plan-register","project":"none","before":[{"wait":".ui-form-grid"},{"fill":".ui-form-grid > .ui-field:nth-child(8) input","value":"0"},{"fill":".ui-form-grid > .ui-field:nth-child(9) input","value":"1100"},{"wait_ms":400}],"clip":".ui-form-grid","callouts":[{"n":1,"sel":".ui-form-grid > .ui-field:nth-child(8)"},{"n":2,"sel":".ui-form-grid > .ui-field:nth-child(9)"}]},
|
||||
{"name":"D01_06_errors","route":"#/d01-plan-register","project":"none","before":[{"wait":".ui-form-grid"},{"fill":".ui-form-grid > .ui-field:nth-child(1) input","value":"2029"},{"fill":".ui-form-grid > .ui-field:nth-child(2) input","value":"2027"},{"fill":".ui-form-grid > .ui-field:nth-child(8) input","value":"500"},{"fill":".ui-form-grid > .ui-field:nth-child(9) input","value":"100"},{"click":".ui-general-block__actions--end button:last-child"},{"wait":".ui-field__error.is-visible"},{"wait_ms":400}],"clip":".ui-form-grid","callouts":[{"n":1,"sel":".ui-form-grid > .ui-field:nth-child(2)"},{"n":2,"sel":".ui-form-grid > .ui-field:nth-child(3)"},{"n":3,"sel":".ui-form-grid > .ui-field:nth-child(9)"}]},
|
||||
{"name":"D01_07_buttons","route":"#/d01-plan-register","project":"none","before":[{"wait":".ui-general-block__actions--end"}],"clip":".ui-general-block__actions--end","callouts":[{"n":1,"sel":".ui-general-block__actions--end button:nth-child(1)"},{"n":2,"sel":".ui-general-block__actions--end button:nth-child(2)"},{"n":3,"sel":".ui-general-block__actions--end button:nth-child(3)"}]}
|
||||
]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[
|
||||
{"name": "E01_00_overview", "route": "#/e01-stab-register", "project": "none", "before": [{"wait": ".b02-proj__grid select"}, {"wait_ms": 800}], "clip": null, "callouts": [{"n": 1, "sel": ".ui-general-layout__title"}, {"n": 2, "sel": ".b02-proj__grid > :nth-child(1)"}, {"n": 3, "sel": ".b02-proj__grid > :nth-child(2)"}, {"n": 4, "sel": ".b02-proj__actions"}]},
|
||||
{"name": "E01_01_source", "route": "#/e01-stab-register", "project": "none", "before": [{"wait": ".b02-proj__grid select"}, {"wait_ms": 800}], "clip": ".b02-proj__grid > :nth-child(1)", "callouts": []},
|
||||
{"name": "E01_02_name_typed", "route": "#/e01-stab-register", "project": "none", "before": [{"wait": ".b02-proj__grid select"}, {"wait_ms": 800}, {"fill": ".b02-proj__grid input", "value": "용화 안정성평가 1차"}], "clip": ".b02-proj__grid", "callouts": [{"n": 1, "sel": ".b02-proj__grid input"}]},
|
||||
{"name": "E01_03_buttons", "route": "#/e01-stab-register", "project": "none", "before": [{"wait": ".b02-proj__grid select"}, {"wait_ms": 800}], "clip": ".b02-proj__actions", "callouts": [{"n": 1, "sel": ".b02-proj__actions .ui-help-btn"}, {"n": 2, "sel": ".b02-proj__actions .ui-btn--ghost:not(.ui-help-btn)"}, {"n": 3, "sel": ".b02-proj__actions .ui-btn--filled"}]},
|
||||
{"name": "E01_04_help_modal", "route": "#/e01-stab-register", "project": "none", "before": [{"wait": ".b02-proj__grid select"}, {"click": ".b02-proj__actions .ui-help-btn"}, {"wait": ".ui-help__grid"}, {"wait_ms": 600}], "clip": null, "callouts": []}
|
||||
{"name": "E01_00_overview", "route": "#/e01-stab-register", "project": "none", "before": [{"wait": ".ui-form-grid select"}, {"wait_ms": 800}], "clip": null, "callouts": [{"n": 1, "sel": ".ui-general-layout__title"}, {"n": 2, "sel": ".ui-form-grid > :nth-child(1)"}, {"n": 3, "sel": ".ui-form-grid > :nth-child(2)"}, {"n": 4, "sel": ".ui-general-block__actions--end"}]},
|
||||
{"name": "E01_01_source", "route": "#/e01-stab-register", "project": "none", "before": [{"wait": ".ui-form-grid select"}, {"wait_ms": 800}], "clip": ".ui-form-grid > :nth-child(1)", "callouts": []},
|
||||
{"name": "E01_02_name_typed", "route": "#/e01-stab-register", "project": "none", "before": [{"wait": ".ui-form-grid select"}, {"wait_ms": 800}, {"fill": ".ui-form-grid input", "value": "용화 안정성평가 1차"}], "clip": ".ui-form-grid", "callouts": [{"n": 1, "sel": ".ui-form-grid input"}]},
|
||||
{"name": "E01_03_buttons", "route": "#/e01-stab-register", "project": "none", "before": [{"wait": ".ui-form-grid select"}, {"wait_ms": 800}], "clip": ".ui-general-block__actions--end", "callouts": [{"n": 1, "sel": ".ui-general-block__actions--end .ui-help-btn"}, {"n": 2, "sel": ".ui-general-block__actions--end .ui-btn--ghost:not(.ui-help-btn)"}, {"n": 3, "sel": ".ui-general-block__actions--end .ui-btn--filled"}]},
|
||||
{"name": "E01_04_help_modal", "route": "#/e01-stab-register", "project": "none", "before": [{"wait": ".ui-form-grid select"}, {"click": ".ui-general-block__actions--end .ui-help-btn"}, {"wait": ".ui-help__grid"}, {"wait_ms": 600}], "clip": null, "callouts": []}
|
||||
]
|
||||
|
||||
@@ -78,10 +78,10 @@ async function fetchPipeSummary(projectId: string): Promise<PipeSummary> {
|
||||
}
|
||||
|
||||
const PIPE_TOP = "관 공";
|
||||
/** 미연결 램프 칸 class(`B08_Quantity_UI_Page.css` · 198-1) · 램프 자리 폭(12px 글 기준) */
|
||||
/** 미연결 램프 칸 표지(198-1 · 풍선 · 가이드가 잡음) · 꼴 = 표 부품 칸 램프 · 램프 자리 폭(12px 글 기준) */
|
||||
export const LAMP_CLASS = "b08-summary__lamp";
|
||||
/** 모두 연결 = 같은 램프 초록(247-1) */
|
||||
const LINKED_CLASS = `${LAMP_CLASS} is-linked`;
|
||||
/** 모두 연결 = 같은 램프 초록 ✓(247-1) */
|
||||
const LINKED_CLASS = `ui-data-table__lamp ${LAMP_CLASS} is-linked`;
|
||||
const LAMP_W = 14;
|
||||
const stationKey = (station: number): number => Math.round(station * 100);
|
||||
/** 열 소수 자리 — 칸 · 계 값이 쓰는 만큼(최대 2). */
|
||||
@@ -236,7 +236,13 @@ export function summaryTable(view: SummaryView): {
|
||||
})),
|
||||
total,
|
||||
cellClass: (i, key) =>
|
||||
key !== "station" ? null : missing[i] ? LAMP_CLASS : linked[i] ? LINKED_CLASS : null,
|
||||
key !== "station"
|
||||
? null
|
||||
: missing[i]
|
||||
? `ui-data-table__lamp ${LAMP_CLASS}`
|
||||
: linked[i]
|
||||
? LINKED_CLASS
|
||||
: null,
|
||||
frozen: 1,
|
||||
empty: "",
|
||||
caption: L("B08_Quantity_Tab_StructureSummary"),
|
||||
|
||||
@@ -17,21 +17,6 @@
|
||||
border-top: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
.b08-sheets__title {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: var(--spacing-8);
|
||||
margin: 0;
|
||||
font-size: var(--text-body);
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.b08-sheets__from {
|
||||
font-size: var(--text-caption);
|
||||
font-weight: var(--font-weight-regular);
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
/* M02 에 없는 조합 안내 — 빨간 글 */
|
||||
.b08-sheets__combo > .ui-text {
|
||||
color: var(--color-danger);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* B08 4탭 홍수량 계산서(PLAN 62-2 · 69-4) — 공용 카드 · 입력 · 드롭다운 위에 줄 배치만.
|
||||
/* B08 4탭 홍수량 계산서(PLAN 62-2 · 69-4) — 공용 카드 · 계산 줄(557-2) · 입력 · 드롭다운 위에 카드 배치만.
|
||||
색은 테마 토큰만(밝음 · 어두움 같이) · 테두리는 입력 · 선택 칸(공용 ui-input · ui-select)만
|
||||
· 굵게 + 붉은 계열은 중요 결과(.is-key)만. */
|
||||
|
||||
@@ -61,83 +61,7 @@
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.b08-hydro__formula {
|
||||
margin: 0 0 0 var(--spacing-8);
|
||||
font-family: var(--font-body);
|
||||
font-size: var(--text-body-sm);
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.b08-hydro__row {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) 112px minmax(0, 0.8fr);
|
||||
align-items: center;
|
||||
gap: var(--spacing-8);
|
||||
min-height: var(--control-height-sm);
|
||||
}
|
||||
|
||||
.b08-hydro__row--f {
|
||||
grid-template-columns: max-content minmax(0, 1fr) 112px;
|
||||
}
|
||||
|
||||
.b08-hydro__label {
|
||||
margin: 0;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.b08-hydro__label--select {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-8);
|
||||
}
|
||||
|
||||
.b08-hydro__row .b08-hydro__formula {
|
||||
margin: 0;
|
||||
font-size: var(--text-caption);
|
||||
}
|
||||
|
||||
.b08-hydro__hint {
|
||||
color: var(--color-text-secondary);
|
||||
font-size: var(--text-caption);
|
||||
}
|
||||
|
||||
/* 계산 칸 — 테두리 없이 글자만 · 숫자 자리 맞춤 */
|
||||
.b08-hydro__out,
|
||||
.b08-hydro__value {
|
||||
padding: 0 var(--spacing-8);
|
||||
color: var(--color-text-body);
|
||||
font-variant-numeric: tabular-nums;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.b08-hydro__value {
|
||||
text-align: left;
|
||||
font-weight: var(--font-weight-medium);
|
||||
}
|
||||
|
||||
.b08-hydro__in {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
box-sizing: border-box;
|
||||
text-align: right;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
/* 줄에 바로 놓인 드롭다운은 값 칸 + 덧칸 — 선택지 글이 안 잘리게 */
|
||||
.b08-hydro__row > .b08-hydro__select {
|
||||
grid-column: 2 / 4;
|
||||
justify-self: start;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
/* 식 칸에 놓인 입력(하상경사 1 / ☐)은 값 칸 폭 */
|
||||
.b08-hydro__row--f > .b08-hydro__in:nth-child(2) {
|
||||
justify-self: start;
|
||||
width: 112px;
|
||||
}
|
||||
|
||||
/* 중요 결과 — Qd · 유출량 · 판정 · 유출율 · 글자색은 판정 따라(O K 초록 · N G 붉음 · 없으면 기본) */
|
||||
.is-key.b08-hydro__out,
|
||||
/* 중요 결과 — Qd · 유출량 · 판정 · 유출율 · 글자색은 판정 따라(O K 초록 · N G 붉음 · 없으면 기본) · 계산 칸 굵기 = 부품 계산 줄 */
|
||||
.is-key.b08-hydro__verdict {
|
||||
color: var(--color-text-body);
|
||||
font-weight: var(--font-weight-bold);
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
* ========================================================================== */
|
||||
|
||||
import { createCard, createInputField, createSelectField } from "@ui/ui_template_elements";
|
||||
import { createCalcFormula, createCalcFormulaRow, createCalcRow } from "@ui/ui_template_calc_row";
|
||||
|
||||
export interface HydroInputs {
|
||||
factor: number;
|
||||
@@ -117,7 +118,7 @@ function sheetLabel(left: HTMLElement, right: HTMLElement, key: string): HTMLEle
|
||||
const input = [left, right]
|
||||
.map((card) => card.querySelector(`input[data-b08-hydro="${key}"]`))
|
||||
.find(Boolean);
|
||||
return input?.closest(".b08-hydro__row")?.querySelector(".b08-hydro__label") ?? null;
|
||||
return input?.closest(".ui-calc-row")?.querySelector(".ui-calc-row__label") ?? null;
|
||||
}
|
||||
|
||||
function node<K extends keyof HTMLElementTagNameMap>(
|
||||
@@ -131,17 +132,8 @@ function node<K extends keyof HTMLElementTagNameMap>(
|
||||
return el;
|
||||
}
|
||||
|
||||
/** 식 글 — `^…^` 사이는 위첨자 · `_…_` 사이는 아래첨자. */
|
||||
function formula(text: string, className = "b08-hydro__formula"): HTMLElement {
|
||||
const box = node("div", className);
|
||||
for (const part of text.split(/(\^[^^]+\^|_[^_]+_)/)) {
|
||||
if (part.startsWith("^") && part.endsWith("^")) box.append(node("sup", "", part.slice(1, -1)));
|
||||
else if (part.startsWith("_") && part.endsWith("_"))
|
||||
box.append(node("sub", "", part.slice(1, -1)));
|
||||
else if (part) box.append(document.createTextNode(part));
|
||||
}
|
||||
return box;
|
||||
}
|
||||
/** 식 글 — 부품 계산 줄의 식(`^…^` 위첨자 · `_…_` 아래첨자) */
|
||||
const formula = createCalcFormula;
|
||||
|
||||
/** 관 단면 그림 — 단면 가정대로 물 찬 자리를 칠함(색은 테마 토큰 · CSS). */
|
||||
function pipeFigure(section: HydroInputs["section"]): SVGSVGElement {
|
||||
@@ -246,7 +238,7 @@ export function buildHydroSheet(
|
||||
};
|
||||
const out = new Map<string, HTMLElement>();
|
||||
const outCell = (id: string, key = false): HTMLElement => {
|
||||
const cell = node("span", `b08-hydro__out${key ? " is-key" : ""}`, DASH);
|
||||
const cell = node("span", `ui-calc-row__out${key ? " is-key" : ""}`, DASH);
|
||||
out.set(id, cell);
|
||||
return cell;
|
||||
};
|
||||
@@ -258,7 +250,7 @@ export function buildHydroSheet(
|
||||
};
|
||||
const numberInput = (key: Key, placeholder = ""): HTMLInputElement => {
|
||||
const { input } = createInputField({ type: "number", compact: true, placeholder });
|
||||
input.classList.add("b08-hydro__in");
|
||||
input.classList.add("ui-calc-row__in");
|
||||
input.step = "any";
|
||||
input.dataset.b08Hydro = key;
|
||||
const value = inputs[key];
|
||||
@@ -269,7 +261,7 @@ export function buildHydroSheet(
|
||||
const [source, title] =
|
||||
key === "slope_denominator" ? ["B06", "B06 횡단 경사"] : ["B05", "B05 분석"];
|
||||
input.title = `${title} ${linked}`;
|
||||
const mark = node("span", "b08-hydro__hint", source);
|
||||
const mark = node("span", "ui-calc-row__hint", source);
|
||||
mark.title = title;
|
||||
mark.dataset.b08HydroLinked = key;
|
||||
marks.set(key, mark);
|
||||
@@ -298,27 +290,16 @@ export function buildHydroSheet(
|
||||
options: choices.map(([value, text]) => ({ value, text })),
|
||||
value: String(inputs[key]),
|
||||
});
|
||||
el.classList.add("b08-hydro__select");
|
||||
el.classList.add("ui-calc-row__select");
|
||||
el.dataset.b08Hydro = key;
|
||||
el.addEventListener("change", () => commit(key, toValue(el.value)));
|
||||
return el;
|
||||
};
|
||||
/** 표 줄 — 이름 · 값 칸(입력 또는 계산) · 덧칸 */
|
||||
const row = (label: string | HTMLElement, value: HTMLElement, extra?: HTMLElement) => {
|
||||
const line = node("div", "b08-hydro__row");
|
||||
const name = typeof label === "string" ? node("span", "b08-hydro__label", label) : label;
|
||||
line.append(name, value);
|
||||
if (extra) line.append(extra);
|
||||
return line;
|
||||
};
|
||||
/** 식 줄 — 이름 · 식 · 값 칸 */
|
||||
const rowF = (label: string, middle: HTMLElement | null, value: HTMLElement) => {
|
||||
const line = node("div", "b08-hydro__row b08-hydro__row--f");
|
||||
line.append(node("span", "b08-hydro__label", label), middle ?? node("span"), value);
|
||||
return line;
|
||||
};
|
||||
/** 표 줄 = 부품 계산 줄(이름 · 값 칸 · 덧칸) · 식 줄(이름 · 식 · 값 칸) */
|
||||
const row = createCalcRow;
|
||||
const rowF = createCalcFormulaRow;
|
||||
const sub = (text: string): HTMLElement => node("h4", "b08-hydro__h2", text);
|
||||
const hint = (text: string): HTMLElement => node("span", "b08-hydro__hint", text);
|
||||
const hint = (text: string): HTMLElement => node("span", "ui-calc-row__hint", text);
|
||||
/** B05 분석 값 참고(비교만) — 값이 없으면 칸 없음 */
|
||||
const b05Hint = (
|
||||
key: "design_flow_m3s" | "tc_minutes",
|
||||
@@ -351,10 +332,10 @@ export function buildHydroSheet(
|
||||
if (hasRain) out.get("r24")!.textContent = fmt(r24Site(current.return_period), 1);
|
||||
});
|
||||
const region: HTMLElement = site.region.name
|
||||
? node("span", "b08-hydro__value", site.region.name)
|
||||
? node("span", "ui-calc-row__value", site.region.name)
|
||||
: (() => {
|
||||
const { input } = createInputField({ compact: true, placeholder: "지역 입력" });
|
||||
input.classList.add("b08-hydro__in");
|
||||
input.classList.add("ui-calc-row__in");
|
||||
input.dataset.b08Hydro = "region";
|
||||
input.value = inputs.region;
|
||||
input.addEventListener("change", () => commit("region", input.value.trim()));
|
||||
@@ -375,7 +356,7 @@ export function buildHydroSheet(
|
||||
formula("Q = 1/3.6 × f × rt × A"),
|
||||
row(
|
||||
(() => {
|
||||
const label = node("span", "b08-hydro__label b08-hydro__label--select");
|
||||
const label = node("span", "ui-calc-row__label ui-calc-row__label--select");
|
||||
label.append("Qd : 설계 홍수량 (㎥/s) = Q ", factor);
|
||||
return label;
|
||||
})(),
|
||||
@@ -406,7 +387,7 @@ export function buildHydroSheet(
|
||||
formula("rt = (R_24_ / 24) × (24 / T)^0.557^"),
|
||||
row("지역", region, hint(regionNote)),
|
||||
row("재현기간", periodSelect),
|
||||
row(formula("R_24_ : 확률강우량 (mm/day)", "b08-hydro__label"), r24Value, hint(rainNote)),
|
||||
row(formula("R_24_ : 확률강우량 (mm/day)", "ui-calc-row__label"), r24Value, hint(rainNote)),
|
||||
],
|
||||
});
|
||||
left.classList.add("b08-hydro__card");
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
import { ui_locales, currentLanguageIndex } from "@ui/ui_template_locale";
|
||||
import { createButton, showConfirmDialog, showToast } from "@ui/ui_template_elements";
|
||||
import { attachCollapsible } from "@ui/ui_template_collapsible";
|
||||
import { attachCollapsible, createCollapsibleHead } from "@ui/ui_template_collapsible";
|
||||
import { createBadge, type BadgeTone } from "@ui/ui_template_badge";
|
||||
import {
|
||||
projectItems,
|
||||
@@ -166,16 +166,12 @@ export function linkSection(
|
||||
// 공용 접기(163-1) — 모두 연결이면 접힘 · 미연결 있으면 펼침
|
||||
section.className = `b08-sheets__combo b08-link ui-collapsible${missing ? "" : " is-collapsed"}`;
|
||||
section.dataset.b08Link = "section";
|
||||
const title = document.createElement("h4");
|
||||
title.className = "b08-sheets__title ui-collapsible__title";
|
||||
const count = document.createElement("span");
|
||||
count.className = "b08-sheets__from";
|
||||
count.textContent = `${L("B08_Quantity_Link_Linked")} ${slots.length - missing} / ${slots.length}`;
|
||||
const label = document.createElement("span");
|
||||
label.textContent = L("B08_Quantity_Link_Title");
|
||||
label.append(" ", count);
|
||||
title.append(label);
|
||||
section.append(title);
|
||||
const head = createCollapsibleHead({
|
||||
title: L("B08_Quantity_Link_Title"),
|
||||
sub: `${L("B08_Quantity_Link_Linked")} ${slots.length - missing} / ${slots.length}`,
|
||||
level: 4,
|
||||
});
|
||||
section.append(head.root);
|
||||
attachCollapsible(section);
|
||||
for (const facility of facilities) {
|
||||
const line = document.createElement("div");
|
||||
|
||||
@@ -243,25 +243,3 @@
|
||||
.b08-milestone__route-addr {
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
/* 단가연결 램프(251-4) — 항목 칸 앞 · 노랑 = 없음 · 초록 = 연결 */
|
||||
[data-b08-milestone="table"] td.b08-milestone__lamp::before {
|
||||
content: "";
|
||||
display: inline-block;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
margin-right: 6px;
|
||||
border-radius: 50%;
|
||||
background: var(--color-warning);
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
[data-b08-milestone="table"] td.b08-milestone__lamp.is-linked::before {
|
||||
content: "✓"; /* 성공 램프는 ✓ 같이(색만으로 안 가름) */
|
||||
background: none;
|
||||
color: var(--color-success);
|
||||
font-size: 9px;
|
||||
font-weight: var(--font-weight-bold);
|
||||
line-height: 8px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ import {
|
||||
type MilestoneView,
|
||||
} from "./B08_Quantity_UI_Milestone_Route";
|
||||
|
||||
/** 단가연결 램프 칸 class(`B08_Quantity_UI_Milestone.css`) */
|
||||
/** 단가연결 램프 칸 표지(풍선이 잡음) · 꼴 = 표 부품 칸 램프 */
|
||||
const LAMP = "b08-milestone__lamp";
|
||||
|
||||
/** 램프 호버 글 — 연결된 것 */
|
||||
@@ -86,7 +86,12 @@ function tableView(projectId: string): MilestoneView {
|
||||
caption: "거리이정표",
|
||||
empty: "",
|
||||
editOnDouble: true,
|
||||
cellClass: (i, key) => (key !== "item" ? null : routeAt(i)?.ref ? `${LAMP} is-linked` : LAMP),
|
||||
cellClass: (i, key) =>
|
||||
key !== "item"
|
||||
? null
|
||||
: routeAt(i)?.ref
|
||||
? `ui-data-table__lamp ${LAMP} is-linked`
|
||||
: `ui-data-table__lamp ${LAMP}`,
|
||||
editable: (_i, key) => key === "item" || key === "company" || key === "note",
|
||||
onEdit: (i, key, text) => {
|
||||
const at = keys[i];
|
||||
|
||||
@@ -58,29 +58,6 @@ html.is-bm-route .b08-quantity > .b08-quantity__tabs:first-child {
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
/* 구조물 집계표 미연결 램프(198-1) — 측점 칸 왼쪽 경고 노랑 점 하나(177-1 미연결 노랑 토큰) · 연결 줄은 없음 */
|
||||
[data-b08-summary="table"] td.b08-summary__lamp::before {
|
||||
content: "";
|
||||
display: inline-block;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
margin-right: 6px;
|
||||
border-radius: 50%;
|
||||
background: var(--color-warning);
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
/* 247-1 — 모두 연결 = 같은 램프 초록 */
|
||||
[data-b08-summary="table"] td.b08-summary__lamp.is-linked::before {
|
||||
content: "✓"; /* 성공 램프는 ✓ 같이(색만으로 안 가름) */
|
||||
background: none;
|
||||
color: var(--color-success);
|
||||
font-size: 9px;
|
||||
font-weight: var(--font-weight-bold);
|
||||
line-height: 8px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* 좌측 패널 옵션(122) — 연암 · 경암 두 칸 나란히 · 설명 한 줄 */
|
||||
.b08-options__row {
|
||||
display: grid;
|
||||
|
||||
@@ -122,14 +122,11 @@ export function openHaulModal(o: HaulModalOpts): void {
|
||||
openModal({
|
||||
title: `운반 단가 — ${o.name}`,
|
||||
closeLabel: "닫기",
|
||||
// 크기(276) — 부품 넓은 중간 창(1100 × 680) · 가운데 · 몸 안 두 칸이 따로 스크롤
|
||||
wide: true,
|
||||
width: "1100px",
|
||||
dialogClass: "b09-haul-modal",
|
||||
mount: (body, close, head) => {
|
||||
// 크기(276) — 공용 큰 창(96vw × 94vh)보다 작게 · 가운데 · 몸 안 두 칸이 따로 스크롤
|
||||
body.parentElement?.setAttribute(
|
||||
"style",
|
||||
"--modal-max:min(680px,86vh);width:min(1100px,94vw);height:var(--modal-max)",
|
||||
);
|
||||
const title = el("strong");
|
||||
const linkBox = el("div", {
|
||||
attrs: { style: "display:flex;gap:8px;align-items:center;margin-bottom:8px" },
|
||||
|
||||
@@ -138,32 +138,6 @@ html.is-bm-route .b09-estimation > .b09-estimation__tabs:first-child {
|
||||
font-weight: var(--font-weight-bold);
|
||||
}
|
||||
|
||||
/* 257-1 설계내역서 단가연결 칸 = 램프(B08 구조물집계표 램프 꼴 · 글 숨김 · 풍선 = 글) — 노랑 = 없음 · 초록 = 연결 */
|
||||
.b09-estimation .ui-data-table__table tbody td.b09-bill__lamp {
|
||||
font-size: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.b09-estimation .ui-data-table__table tbody td.b09-bill__lamp::before {
|
||||
content: "";
|
||||
display: inline-block;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background: var(--color-warning);
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.b09-estimation .ui-data-table__table tbody td.b09-bill__lamp.is-linked::before {
|
||||
content: "✓"; /* 성공 램프는 ✓ 같이(색만으로 안 가름) */
|
||||
background: none;
|
||||
color: var(--color-success);
|
||||
font-size: 9px;
|
||||
font-weight: var(--font-weight-bold);
|
||||
line-height: 8px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* 184-3 설계내역서 고친 칸 — 자동 값과 다른 칸(풍선 = 자동 값) */
|
||||
.b09-estimation .ui-data-table__table tbody td.is-edited {
|
||||
background: color-mix(in srgb, var(--color-warning) 22%, var(--color-canvas));
|
||||
|
||||
@@ -94,7 +94,7 @@ export const buildBill: TabBuild = (ctx): BillTable => {
|
||||
const r = rows[i];
|
||||
const lamp =
|
||||
key === "connect" && r && linkable(raw[i])
|
||||
? `b09-bill__lamp${linkText(raw[i]) === "단가연결" ? "" : " is-linked"}`
|
||||
? `ui-data-table__lamp ui-data-table__lamp--only b09-bill__lamp${linkText(raw[i]) === "단가연결" ? "" : " is-linked"}`
|
||||
: "";
|
||||
return [lamp, edited(r, key) ? "is-edited" : ""].filter(Boolean).join(" ") || null;
|
||||
},
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
* [등록] = POST /api/d01/project → 현재 프로젝트 · D02 로.
|
||||
* ========================================================================== */
|
||||
|
||||
import "../B02_ProjRegister/B02_ProjRegister_UI_Style.css";
|
||||
import { ROUTES } from "@config/config_frontend";
|
||||
import {
|
||||
createButton,
|
||||
@@ -15,7 +14,7 @@ import {
|
||||
createInputField,
|
||||
createSelectField,
|
||||
} from "@ui/ui_template_elements";
|
||||
import { createGeneralLayout } from "@ui/ui_template_general_layout";
|
||||
import { createFormGrid, createGeneralLayout } from "@ui/ui_template_general_layout";
|
||||
import { createHelpButton } from "@ui/ui_template_help";
|
||||
import { createText } from "@ui/ui_template_text";
|
||||
import { t } from "@ui/ui_template_locale";
|
||||
@@ -144,13 +143,11 @@ export function renderD01PlanRegister(root: HTMLElement): void {
|
||||
);
|
||||
};
|
||||
|
||||
const grid = document.createElement("div");
|
||||
grid.className = "b02-proj__grid";
|
||||
grid.append(
|
||||
...[yearStart, yearEnd, region, kind, roadType, speed, name, startM, endM].map((f) => f.root),
|
||||
const grid = createFormGrid(
|
||||
[yearStart, yearEnd, region, kind, roadType, speed, name, startM, endM].map((f) => f.root),
|
||||
);
|
||||
const actions = document.createElement("div");
|
||||
actions.className = "ui-general-block__actions b02-proj__actions";
|
||||
actions.className = "ui-general-block__actions ui-general-block__actions--end";
|
||||
actions.append(
|
||||
createHelpButton(),
|
||||
createButton({
|
||||
@@ -161,7 +158,7 @@ export function renderD01PlanRegister(root: HTMLElement): void {
|
||||
createButton({ label: t("DE_Reg_Submit"), variant: "filled", onClick: submit }),
|
||||
);
|
||||
const layout = createGeneralLayout({
|
||||
pageClass: "b02-proj",
|
||||
narrow: true,
|
||||
title: t("DE_Reg_PlanTitle"),
|
||||
subtitle: t("DE_Reg_PlanSubtitle"),
|
||||
content: createCard({ body: [grid, actions], raised: true }),
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
* [등록] = POST /api/e01/project → 현재 프로젝트 · E02 로.
|
||||
* ========================================================================== */
|
||||
|
||||
import "../B02_ProjRegister/B02_ProjRegister_UI_Style.css";
|
||||
import { API_BASE_URL, ROUTES } from "@config/config_frontend";
|
||||
import {
|
||||
createButton,
|
||||
@@ -14,7 +13,7 @@ import {
|
||||
createSelectField,
|
||||
el,
|
||||
} from "@ui/ui_template_elements";
|
||||
import { createGeneralLayout } from "@ui/ui_template_general_layout";
|
||||
import { createFormGrid, createGeneralLayout } from "@ui/ui_template_general_layout";
|
||||
import { createHelpButton } from "@ui/ui_template_help";
|
||||
import { t } from "@ui/ui_template_locale";
|
||||
import { createGroupProject } from "../A00_Common/group_workflow";
|
||||
@@ -74,11 +73,9 @@ export function renderE01StabRegister(root: HTMLElement): void {
|
||||
if (!typed) name.input.value = autoName();
|
||||
})();
|
||||
|
||||
const grid = document.createElement("div");
|
||||
grid.className = "b02-proj__grid";
|
||||
grid.append(source.root, name.root);
|
||||
const grid = createFormGrid([source.root, name.root]);
|
||||
const actions = document.createElement("div");
|
||||
actions.className = "ui-general-block__actions b02-proj__actions";
|
||||
actions.className = "ui-general-block__actions ui-general-block__actions--end";
|
||||
actions.append(
|
||||
createHelpButton(),
|
||||
createButton({
|
||||
@@ -89,7 +86,7 @@ export function renderE01StabRegister(root: HTMLElement): void {
|
||||
submitButton,
|
||||
);
|
||||
const layout = createGeneralLayout({
|
||||
pageClass: "b02-proj",
|
||||
narrow: true,
|
||||
title: t("DE_Reg_StabTitle"),
|
||||
subtitle: t("DE_Reg_StabSubtitle"),
|
||||
content: createCard({ body: [grid, note, actions], raised: true }),
|
||||
|
||||
@@ -53,7 +53,7 @@ new Function("exports", "module", "require", js(fs.readFileSync({table}, "utf8")
|
||||
const LAMP_CLASS = {json.dumps(_lamp_class())};
|
||||
const missing = ["", "BOX", ""];
|
||||
const linked = [false, false, false];
|
||||
const LINKED_CLASS = LAMP_CLASS + " is-linked";
|
||||
const LINKED_CLASS = "ui-data-table__lamp " + LAMP_CLASS + " is-linked";
|
||||
const cellClass = {cell.group(1)};
|
||||
const html = box.exports.renderDataTableHtml({{
|
||||
columns: [{{ key: "station", label: "측점" }}, {{ key: "c1", label: "조합" }}],
|
||||
@@ -68,7 +68,10 @@ console.log(JSON.stringify({{
|
||||
"""
|
||||
)
|
||||
assert got["count"] == 1
|
||||
assert f'<td class="is-frozen is-edge {_lamp_class()}"' in got["row1"] # 측점 칸(첫 칸)
|
||||
# 꼴 = 표 부품 칸 램프(557-2) · 표지 = 화면 class
|
||||
assert (
|
||||
f'<td class="is-frozen is-edge ui-data-table__lamp {_lamp_class()}"' in got["row1"]
|
||||
) # 측점 칸(첫 칸)
|
||||
assert _lamp_class() not in got["row0"]
|
||||
|
||||
|
||||
@@ -106,11 +109,8 @@ def test_미지정_열_없음_줄_값_그대로():
|
||||
|
||||
|
||||
def test_램프_경고_노랑_토큰():
|
||||
css = (ROOT / "B08_Quantity" / "B08_Quantity_UI_Page.css").read_text(encoding="utf-8")
|
||||
m = re.search(
|
||||
r'\[data-b08-summary="table"\] td\.' + re.escape(_lamp_class()) + r"::before\s*\{([^}]*)\}",
|
||||
css,
|
||||
)
|
||||
css = (ROOT / "ui_template" / "ui_template_data_table.css").read_text(encoding="utf-8")
|
||||
m = re.search(r"td\.ui-data-table__lamp::before\s*\{([^}]*)\}", css) # 표 부품 칸 램프(557-2)
|
||||
assert m
|
||||
assert re.search(r"background:\s*var\(--color-warning\)", m.group(1))
|
||||
assert "border-radius: 50%" in m.group(1)
|
||||
|
||||
@@ -74,8 +74,12 @@ console.log(JSON.stringify({{ rows: rows.slice(0, 3), tail: html.split("</tbody>
|
||||
"""
|
||||
)
|
||||
lamp = _lamp_class()
|
||||
assert f'class="is-frozen is-edge {lamp} is-linked"' in got["rows"][0] # 0 = 모두 연결
|
||||
assert f'class="is-frozen is-edge {lamp}"' in got["rows"][1] # 20 = 빈 칸 하나 = 노랑
|
||||
assert (
|
||||
f'class="is-frozen is-edge ui-data-table__lamp {lamp} is-linked"' in got["rows"][0]
|
||||
) # 0 = 모두 연결
|
||||
assert (
|
||||
f'class="is-frozen is-edge ui-data-table__lamp {lamp}"' in got["rows"][1]
|
||||
) # 20 = 빈 칸 하나 = 노랑
|
||||
assert lamp not in got["rows"][2] # 40 = 시설 없음
|
||||
assert got["lamps"] == 2 # 합계 줄 없음
|
||||
|
||||
@@ -100,8 +104,9 @@ console.log(JSON.stringify([text(cell(0)), text(cell(1))]));
|
||||
|
||||
|
||||
def test_초록_성공_토큰():
|
||||
css = (ROOT / "B08_Quantity" / "B08_Quantity_UI_Page.css").read_text(encoding="utf-8")
|
||||
m = re.search(r"td\.b08-summary__lamp\.is-linked::before\s*\{([^}]*)\}", css)
|
||||
# 꼴 = 표 부품 칸 램프(557-2)
|
||||
css = (ROOT / "ui_template" / "ui_template_data_table.css").read_text(encoding="utf-8")
|
||||
m = re.search(r"td\.ui-data-table__lamp\.is-linked::before\s*\{([^}]*)\}", css)
|
||||
assert (
|
||||
m
|
||||
and re.search(r"color:\s*var\(--color-success\)", m.group(1))
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
"""PLAN 251-1 — 총괄자재표 → 운반 묶음(거리이정표 행 · B09 운반공 줄 한 원천)."""
|
||||
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
@@ -138,7 +139,11 @@ console.log(JSON.stringify([refText(null),
|
||||
"단가연결 — 일위대가 회사 조합",
|
||||
]
|
||||
# 램프 = 항목 칸 · 우클릭 = [단가연결] · 연결 있으면 [단가연결 풀기] · 창 = B09 와 같은 것
|
||||
assert 'key !== "item" ? null : routeAt(i)?.ref ? `${LAMP} is-linked` : LAMP' in src
|
||||
# 꼴 = 표 부품 칸 램프(557-2) · 표지 = LAMP
|
||||
assert (
|
||||
'key !== "item" ? null : routeAt(i)?.ref ? `ui-data-table__lamp ${LAMP} is-linked` : `ui-data-table__lamp ${LAMP}`'
|
||||
in re.sub(r"\s+", " ", src)
|
||||
)
|
||||
assert "openHaulModal({" in src and '"단가연결 풀기"' in src # 263-1 = 운반 단가 모달 하나
|
||||
diagram = (ROOT / "B08_Quantity" / "B08_Quantity_UI_Milestone_Diagram.ts").read_text("utf-8")
|
||||
assert "Math.abs(h - lastH) > 1" in diagram # 틀 높이 바뀜 = 다시 그림(아래 잘림 없음)
|
||||
|
||||
@@ -35,14 +35,17 @@ def test_램프_칸_class_풍선_머리():
|
||||
body = JS.replace("__ROWS__", json.dumps(ROWS, ensure_ascii=False))
|
||||
out = json.loads(_node(_ts([*SRC, "UI_Tabs_Bill"]) + body))
|
||||
assert out["col"] == {"label": "단가\n연결", "width": 44, "title": "단가연결"}
|
||||
assert out["cls"] == [
|
||||
"b09-bill__lamp is-linked", # 자동 줄 · 호표 연결
|
||||
"b09-bill__lamp", # 연결 없음 = 노랑
|
||||
"b09-bill__lamp is-linked", # 손 줄 · 비고 대가3
|
||||
None, # 자재 줄 = 연결 대상 아님
|
||||
None,
|
||||
None, # 묶음 줄
|
||||
]
|
||||
assert (
|
||||
out["cls"]
|
||||
== [
|
||||
"ui-data-table__lamp ui-data-table__lamp--only b09-bill__lamp is-linked", # 자동 줄 · 호표 연결
|
||||
"ui-data-table__lamp ui-data-table__lamp--only b09-bill__lamp", # 연결 없음 = 노랑
|
||||
"ui-data-table__lamp ui-data-table__lamp--only b09-bill__lamp is-linked", # 손 줄 · 비고 대가3
|
||||
None, # 자재 줄 = 연결 대상 아님
|
||||
None,
|
||||
None, # 묶음 줄
|
||||
]
|
||||
)
|
||||
assert out["tip"] == ["연결됨", "단가연결", "대가3", None, None, None]
|
||||
assert out["other"] is None
|
||||
labels = [g[0] for g in out["groups"]]
|
||||
@@ -52,7 +55,11 @@ def test_램프_칸_class_풍선_머리():
|
||||
|
||||
def test_CSS_램프_초록_노랑_머리_줄바꿈():
|
||||
css = (ROOT / "B09_Estimation" / "B09_Estimation_UI_Page.css").read_text("utf-8")
|
||||
assert "td.b09-bill__lamp::before" in css and "var(--color-warning)" in css
|
||||
assert "td.b09-bill__lamp.is-linked::before" in css and "var(--color-success)" in css
|
||||
part = (ROOT / "ui_template" / "ui_template_data_table.css").read_text(
|
||||
"utf-8"
|
||||
) # 꼴 = 표 부품(557-2)
|
||||
assert "td.ui-data-table__lamp::before" in part and "var(--color-warning)" in part
|
||||
assert "td.ui-data-table__lamp.is-linked::before" in part and "var(--color-success)" in part
|
||||
assert "td.ui-data-table__lamp--only" in part and "b09-bill__lamp" not in css
|
||||
head = css.split("[data-b09-table] thead th {", 1)[1].split("}", 1)[0]
|
||||
assert "white-space: pre-line" in head
|
||||
|
||||
@@ -28,7 +28,9 @@ def test_연결_있으면_운반_모달_없으면_옛_길():
|
||||
|
||||
def test_운반_모달_작게():
|
||||
modal = _src("B09_Estimation/B09_Estimation_UI_Haul_Modal.ts")
|
||||
assert '"--modal-max:min(680px,86vh);width:min(1100px,94vw);height:var(--modal-max)"' in modal
|
||||
assert 'width: "1100px",' in modal # 부품 넓은 중간 창(557-2)
|
||||
part = (ROOT / "ui_template" / "ui_template_modal.css").read_text(encoding="utf-8")
|
||||
assert '.ui-modal.ui-modal--wide[data-width="1100px"]' in part and "min(680px, 86vh)" in part
|
||||
assert "min-height:60vh" not in modal and "flex:1;min-height:0" in modal
|
||||
|
||||
|
||||
|
||||
@@ -38,6 +38,6 @@ def test_옛_보라_이름은_별칭():
|
||||
):
|
||||
assert f"--color-{old}: var(--color-{new});" in root, old
|
||||
assert "--color-accent: #1f7a4d;" in root and "--color-danger: #c0392b;" in root
|
||||
assert "--color-danger-fill: #e5484d;" in root
|
||||
assert "--color-danger-fill: #d6363c;" in root # 557-2 흰 글 대비 4.5 이상
|
||||
dark = THEME[THEME.index(':root[data-theme="dark"]') :]
|
||||
assert "deep-iris" not in dark and "royal-amethyst" not in dark # 어두움도 별칭으로 따라옴
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
"""557-2 부품 · 테마 작은 흠 지킴 시험 — 위험 채움 흰 글 대비 · 계산 줄 · 칸 램프 · 넓은 중간 창 · 값 목록 규칙 꼴 ·
|
||||
구역 머리 · 입력 칸 2열(D01 · E01 이 B02 클래스를 안 빌림)."""
|
||||
|
||||
import re
|
||||
|
||||
from test_552_3_card import ROOT, _text, check_part, kit_line
|
||||
|
||||
|
||||
def _lum(h: str) -> float:
|
||||
c = [int(h[i : i + 2], 16) / 255 for i in (1, 3, 5)]
|
||||
c = [v / 12.92 if v <= 0.03928 else ((v + 0.055) / 1.055) ** 2.4 for v in c]
|
||||
return 0.2126 * c[0] + 0.7152 * c[1] + 0.0722 * c[2]
|
||||
|
||||
|
||||
def _ratio(a: str, b: str) -> float:
|
||||
x, y = sorted((_lum(a), _lum(b)), reverse=True)
|
||||
return (x + 0.05) / (y + 0.05)
|
||||
|
||||
|
||||
def _tokens(block: str) -> dict[str, str]:
|
||||
return dict(re.findall(r"--(color-[a-z-]+):\s*(#[0-9a-fA-F]{6})\b", block))
|
||||
|
||||
|
||||
def test_위험_채움_흰_글_대비():
|
||||
theme = _text("ui_template_theme.css")
|
||||
light = _tokens(theme[theme.index(":root {") :].split("\n}\n", 1)[0])
|
||||
dark = _tokens(theme[theme.index(':root[data-theme="dark"]') :].split("\n}\n", 1)[0])
|
||||
# 채운 바탕 위 글 = canvas(밝음 흰 · 어두움 짙은 바탕)
|
||||
assert _ratio(light["color-danger-fill"], light["color-canvas"]) >= 4.5
|
||||
assert _ratio(dark["color-danger-fill"], dark["color-canvas"]) >= 4.5
|
||||
assert _ratio(light["color-danger"], light["color-canvas"]) >= 4.5 # 위험 단추 · 글
|
||||
assert _ratio(dark["color-danger"], dark["color-canvas"]) >= 4.5
|
||||
assert light["color-danger"] == "#c0392b" # 위험 글 색 그대로
|
||||
|
||||
|
||||
def test_계산_줄():
|
||||
check_part(
|
||||
"calc-row",
|
||||
"ui_template_calc_row.ts",
|
||||
["ui_template_calc_row.css"],
|
||||
["createCalcRow", "createCalcFormulaRow", "createCalcFormula"],
|
||||
[
|
||||
"ui-calc-row",
|
||||
"ui-calc-row--formula",
|
||||
"ui-calc-row__label",
|
||||
"ui-calc-row__out",
|
||||
"ui-calc-row__in",
|
||||
],
|
||||
)
|
||||
sheet = (ROOT / "B08_Quantity" / "B08_Quantity_UI_Hydro_Sheet.ts").read_text(encoding="utf-8")
|
||||
css = (ROOT / "B08_Quantity" / "B08_Quantity_UI_Hydro.css").read_text(encoding="utf-8")
|
||||
for old in (
|
||||
"b08-hydro__row",
|
||||
"b08-hydro__label",
|
||||
"b08-hydro__formula",
|
||||
"b08-hydro__out",
|
||||
"b08-hydro__in",
|
||||
):
|
||||
assert old not in sheet and old not in css, old
|
||||
|
||||
|
||||
def test_칸_램프():
|
||||
css = _text("ui_template_data_table.css")
|
||||
assert "td.ui-data-table__lamp::before" in css and "var(--color-warning)" in css
|
||||
assert re.search(r"td\.ui-data-table__lamp\.is-linked::before\s*\{[^}]*content: \"✓\"", css)
|
||||
assert "td.ui-data-table__lamp--only" in css
|
||||
assert "칸 램프" in kit_line("table") and "kitLampTable()" in kit_line("table")
|
||||
for page, old in (
|
||||
("B08_Quantity/B08_Quantity_UI_Page.css", "b08-summary__lamp"),
|
||||
("B08_Quantity/B08_Quantity_UI_Milestone.css", "b08-milestone__lamp"),
|
||||
("B09_Estimation/B09_Estimation_UI_Page.css", "b09-bill__lamp"),
|
||||
):
|
||||
assert old not in (ROOT / page).read_text(encoding="utf-8"), page
|
||||
|
||||
|
||||
def test_넓은_중간_창():
|
||||
css = _text("ui_template_modal.css")
|
||||
assert re.search(
|
||||
r'\.ui-modal\.ui-modal--wide\[data-width="1100px"\]\s*\{[^}]*min\(1100px, 94vw\)', css
|
||||
)
|
||||
assert '"1100px"' in _text("ui_template_modal.ts") and "1100" in kit_line("modal")
|
||||
haul = (ROOT / "B09_Estimation" / "B09_Estimation_UI_Haul_Modal.ts").read_text(encoding="utf-8")
|
||||
assert 'width: "1100px"' in haul and "--modal-max:min(680px" not in haul
|
||||
|
||||
|
||||
def test_값_목록_규칙_꼴():
|
||||
assert "ui-kv--rule" in _text("ui_template_kv.css") and "rule: true" in kit_line("kv")
|
||||
app = (ROOT / "A00_Common" / "app_settings.ts").read_text(encoding="utf-8")
|
||||
assert "createKv(rows, { rule: true })" in app
|
||||
assert "app-settings__rules" not in (ROOT / "A00_Common" / "app_settings.css").read_text(
|
||||
encoding="utf-8"
|
||||
)
|
||||
|
||||
|
||||
def test_구역_머리():
|
||||
ts = _text("ui_template_collapsible.ts")
|
||||
assert "export function createCollapsibleHead" in ts and "kitSection()" in kit_line(
|
||||
"collapsible"
|
||||
)
|
||||
link = (ROOT / "B08_Quantity" / "B08_Quantity_UI_Link.ts").read_text(encoding="utf-8")
|
||||
assert "createCollapsibleHead(" in link and "b08-sheets__title" not in link
|
||||
|
||||
|
||||
def test_입력_칸_2열_안_빌림():
|
||||
css = _text("ui_template_general_layout.css")
|
||||
assert (
|
||||
".ui-form-grid" in css
|
||||
and ".ui-general-layout--narrow" in css
|
||||
and ".ui-general-block__actions--end" in css
|
||||
)
|
||||
for page in (
|
||||
"D01_PlanRegister/D01_PlanRegister_UI_Page.ts",
|
||||
"E01_StabRegister/E01_StabRegister_UI_Page.ts",
|
||||
):
|
||||
src = (ROOT / page).read_text(encoding="utf-8")
|
||||
assert (
|
||||
"b02-proj" not in src
|
||||
and "B02_ProjRegister_UI_Style" not in src
|
||||
and "createFormGrid(" in src
|
||||
), page
|
||||
for scene in ("D01", "E01"):
|
||||
assert "b02-proj" not in (
|
||||
ROOT / "A05_EduDetail" / "guide" / scene / "_scenes.json"
|
||||
).read_text(encoding="utf-8")
|
||||
@@ -0,0 +1,83 @@
|
||||
/* 계산 줄(557-2) — 이름 | 값 112 | 덧칸 · 식 줄 = 이름 | 식 | 값 112 · 테두리는 입력 · 고르기 부품만 */
|
||||
.ui-calc-row {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) 112px minmax(0, 0.8fr);
|
||||
align-items: center;
|
||||
gap: var(--spacing-8);
|
||||
min-height: var(--control-height-sm);
|
||||
}
|
||||
|
||||
.ui-calc-row--formula {
|
||||
grid-template-columns: max-content minmax(0, 1fr) 112px;
|
||||
}
|
||||
|
||||
.ui-calc-row__label {
|
||||
margin: 0;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.ui-calc-row__label--select {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-8);
|
||||
}
|
||||
|
||||
.ui-calc-formula {
|
||||
margin: 0 0 0 var(--spacing-8);
|
||||
font-size: var(--text-body-sm);
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.ui-calc-row .ui-calc-formula {
|
||||
margin: 0;
|
||||
font-size: var(--text-caption);
|
||||
}
|
||||
|
||||
.ui-calc-row__hint {
|
||||
color: var(--color-text-secondary);
|
||||
font-size: var(--text-caption);
|
||||
}
|
||||
|
||||
/* 계산 칸 — 테두리 없이 글자만 · 숫자 자리 맞춤 */
|
||||
.ui-calc-row__out,
|
||||
.ui-calc-row__value {
|
||||
padding: 0 var(--spacing-8);
|
||||
color: var(--color-text-body);
|
||||
font-variant-numeric: tabular-nums;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.ui-calc-row__value {
|
||||
text-align: left;
|
||||
font-weight: var(--font-weight-medium);
|
||||
}
|
||||
|
||||
.ui-calc-row__out.is-key {
|
||||
font-weight: var(--font-weight-bold);
|
||||
}
|
||||
|
||||
.ui-calc-row__in {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
box-sizing: border-box;
|
||||
text-align: right;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
/* 줄에 바로 놓인 고르기는 값 칸 + 덧칸 — 선택지 글이 안 잘리게 */
|
||||
.ui-calc-row > .ui-calc-row__select {
|
||||
grid-column: 2 / 4;
|
||||
justify-self: start;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
/* 식 칸에 놓인 입력(하상경사 1 / ☐)은 값 칸 폭 */
|
||||
.ui-calc-row--formula > .ui-calc-row__in:nth-child(2) {
|
||||
justify-self: start;
|
||||
width: 112px;
|
||||
}
|
||||
|
||||
/* 숨김 — display 가 [hidden] 을 덮지 않게(공용 기본 꼴이 안 실린 곳 · 페이지 꾸밈 위에서도) */
|
||||
:is(.ui-calc-row, .ui-calc-row__label--select)[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/* =============================================================================
|
||||
* ui_template_calc_row.ts
|
||||
* 부품 「계산 줄」(557-2) — 계산서 한 줄: 이름 · 값 칸(입력 또는 계산 결과) · 덧칸(식 · 안내 · 둘째 값).
|
||||
* 꼴 = 값 줄(`createCalcRow` · 이름 | 값 112 | 덧칸) · 식 줄(`createCalcFormulaRow` · 이름 | 식 | 값 112)
|
||||
* · 식 글(`createCalcFormula` · `^…^` 위첨자 · `_…_` 아래첨자) · 이름 칸 안 고르기(`ui-calc-row__label--select`)
|
||||
* · 칸 조각 클래스 = 입력 `ui-calc-row__in` · 고르기 `__select`(값 칸 + 덧칸) · 결과 글 `__out`(숫자 오른쪽 · 중요 `is-key` 굵게) · 글 값 `__value` · 안내 `__hint`
|
||||
* 크기 = 줄 최소 30(작은 입력) · 값 칸 112 · 사이 8 · 이름 14 · 식 · 안내 12
|
||||
* 상태 = 기본만(입력 · 고르기 부품이 저마다 상태를 가짐 · 판정 색은 화면이 `is-key` 에 줌)
|
||||
* 쓰는 법 = `createCalcRow("f : 유출계수", input)` · `createCalcFormulaRow("유속 (m/s)", createCalcFormula("V = 1/n × R^2/3^"), out)`
|
||||
* 대신하는 옛 변종 = B08 수리집수면적표 `b08-hydro__row(--f)` · `__label` · `__formula` · `__out` · `__value` · `__in` · `__hint` · `__select`
|
||||
* ========================================================================== */
|
||||
|
||||
import "./ui_template_calc_row.css";
|
||||
|
||||
function span(className: string, text = ""): HTMLElement {
|
||||
const el = document.createElement("span");
|
||||
el.className = className;
|
||||
el.textContent = text;
|
||||
return el;
|
||||
}
|
||||
|
||||
/** 식 글 — `^…^` 사이는 위첨자 · `_…_` 사이는 아래첨자 · className 을 주면 이름 칸 등으로 씀 */
|
||||
export function createCalcFormula(text: string, className = "ui-calc-formula"): HTMLElement {
|
||||
const box = document.createElement("div");
|
||||
box.className = className;
|
||||
for (const part of text.split(/(\^[^^]+\^|_[^_]+_)/)) {
|
||||
const tag =
|
||||
part.startsWith("^") && part.endsWith("^")
|
||||
? "sup"
|
||||
: part.startsWith("_") && part.endsWith("_")
|
||||
? "sub"
|
||||
: "";
|
||||
if (tag) {
|
||||
const el = document.createElement(tag);
|
||||
el.textContent = part.slice(1, -1);
|
||||
box.append(el);
|
||||
} else if (part) box.append(part);
|
||||
}
|
||||
return box;
|
||||
}
|
||||
|
||||
/** 값 줄 — 이름 · 값 칸(입력 또는 계산) · 덧칸 */
|
||||
export function createCalcRow(
|
||||
label: string | HTMLElement,
|
||||
value: HTMLElement,
|
||||
extra?: HTMLElement,
|
||||
): HTMLElement {
|
||||
const row = document.createElement("div");
|
||||
row.className = "ui-calc-row";
|
||||
row.append(typeof label === "string" ? span("ui-calc-row__label", label) : label, value);
|
||||
if (extra) row.append(extra);
|
||||
return row;
|
||||
}
|
||||
|
||||
/** 식 줄 — 이름 · 식(없으면 빈 칸) · 값 칸 */
|
||||
export function createCalcFormulaRow(
|
||||
label: string,
|
||||
middle: HTMLElement | null,
|
||||
value: HTMLElement,
|
||||
): HTMLElement {
|
||||
const row = document.createElement("div");
|
||||
row.className = "ui-calc-row ui-calc-row--formula";
|
||||
row.append(span("ui-calc-row__label", label), middle ?? document.createElement("span"), value);
|
||||
return row;
|
||||
}
|
||||
@@ -2,10 +2,11 @@
|
||||
* ui_template_collapsible.ts
|
||||
* 부품 「접이 카드」(552-3 · 목록 16) — 제목 줄을 누르면 몸이 접히고 펴지는 카드.
|
||||
* 꼴 = 패널 카드(`createPanelCard` · 왼쪽 패널 · 공용 양식 붙음 · ⚙ 곁값) · 구역(아무 상자에 클래스만 + `attachCollapsible`)
|
||||
* · 구역 머리(`createCollapsibleHead` · 557-2) = 패널 머리(제목 14 · 부제 12 둘째 줄)를 접는 제목으로
|
||||
* 크기 = 제목 줄 32(글 14 · 캐럿 ▾ ▸ 글자 1.7 배)
|
||||
* 상태 = 기본 · 마우스(캐럿 진해짐) · 펼침 / 접힘(`.is-collapsed`)
|
||||
* 쓰는 법 = `const card = createPanelCard("구조물 배치", false); card.append(form); attachCollapsible(panel)`
|
||||
* 대신하는 옛 변종 = 날 `<details>` — B06 넷 · E01 `e-missing` · E03 `e03-prob-src` · M01 넷 · M02 일곱 · A07 약관
|
||||
* 대신하는 옛 변종 = 날 `<details>` — B06 넷 · E01 `e-missing` · E03 `e03-prob-src` · M01 넷 · M02 일곱 · A07 약관 · B08 구조물도 「조합 연결」 `b08-sheets__title`
|
||||
* 모양 = 제목 · 캐럿 `ui_template_theme.css` · 카드 `ui_template_overlay.css` · 마우스 `ui_template_collapsible.css`
|
||||
*
|
||||
* 페이지 공용 "접기 컨테이너" 동작. (B05 컨테이너 템플릿 기준으로 재사용)
|
||||
@@ -20,6 +21,7 @@
|
||||
* ========================================================================== */
|
||||
|
||||
import "./ui_template_collapsible.css";
|
||||
import { createPanelHead, type PanelHead, type PanelHeadOptions } from "./ui_template_panel_head";
|
||||
|
||||
/**
|
||||
* 좌측 패널 컨테이너 공용 카드(269) — 접는 제목 + 공용 양식(`ui-panel-form`) · 내용은 카드에 바로 붙임 · 기본 닫힘(389-3).
|
||||
@@ -51,6 +53,13 @@ export function createPanelCard(
|
||||
return section;
|
||||
}
|
||||
|
||||
/** 구역 머리(557-2) — 패널 머리(제목 · 부제)를 접는 제목으로 · 컨테이너에 `ui-collapsible` + `attachCollapsible` */
|
||||
export function createCollapsibleHead(opts: PanelHeadOptions): PanelHead {
|
||||
const head = createPanelHead(opts);
|
||||
head.root.classList.add("ui-collapsible__title");
|
||||
return head;
|
||||
}
|
||||
|
||||
/** root 아래 `.ui-collapsible__title` 클릭을 위임 처리해 해당 컨테이너를 접거나 편다. */
|
||||
export function attachCollapsible(root: HTMLElement): void {
|
||||
root.addEventListener("click", (event) => {
|
||||
|
||||
@@ -64,6 +64,37 @@
|
||||
white-space-collapse: preserve-spaces; /* 앞 빈칸(들여쓰기) · 겹 빈칸을 접지 않음 · 줄바꿈은 안 함 */
|
||||
}
|
||||
|
||||
/* 칸 램프(557-2) — 칸 글 앞 점 8 · 노랑 = 없음 · 연결 = 초록 ✓(색만으로 안 가름) · --only = 글 숨김 · 점만 가운데 */
|
||||
.ui-data-table__table td.ui-data-table__lamp::before {
|
||||
content: "";
|
||||
display: inline-block;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
margin-right: 6px;
|
||||
border-radius: 50%;
|
||||
background: var(--color-warning);
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.ui-data-table__table td.ui-data-table__lamp.is-linked::before {
|
||||
content: "✓";
|
||||
background: none;
|
||||
color: var(--color-success);
|
||||
font-size: 9px;
|
||||
font-weight: var(--font-weight-bold);
|
||||
line-height: 8px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.ui-data-table__table td.ui-data-table__lamp--only {
|
||||
font-size: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.ui-data-table__table td.ui-data-table__lamp--only::before {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
/* 숫자 — 오른쪽 맞춤 · 자리 폭 같게 */
|
||||
.ui-data-table__table [data-num] {
|
||||
text-align: right;
|
||||
@@ -291,6 +322,6 @@
|
||||
}
|
||||
|
||||
/* 숨김 — display 가 [hidden] 을 덮지 않게(공용 기본 꼴이 안 실린 곳 · 페이지 꾸밈 위에서도) */
|
||||
.ui-data-table__icon[hidden] {
|
||||
:is(.ui-data-table__icon, .ui-data-table__lamp)[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
* 칸 누른 뒤 Enter · F2 = 그 칸 입력 시작(PLAN 243-1 · keyEdit false = 표 밖이 맡음)
|
||||
* 같은 값 세로 병합(PLAN 201-1) = mergeSame [열] → 위아래로 같은 값이 이어지면 한 칸(rowSpan) · 본문만
|
||||
* 앞 병합 열이 갈리면 뒤 병합 열도 갈림 · 빈 값 · 고칠 칸은 안 합침 · 칸 → 열 = `cellColumn`(병합 표는 data-c)
|
||||
* 칸 램프(557-2) = cellClass 가 `ui-data-table__lamp` → 칸 글 앞 점 8 · 노랑 = 없음 · `is-linked` = 초록 ✓(색만으로 안 가름) ·
|
||||
* `ui-data-table__lamp--only` = 글 숨기고 점만 가운데 · 대신하는 옛 변종 = B08 `b08-summary__lamp` · `b08-milestone__lamp` · B09 `b09-bill__lamp`
|
||||
* ========================================================================== */
|
||||
|
||||
import "./ui_template_data_table.css";
|
||||
|
||||
@@ -33,6 +33,28 @@
|
||||
gap: var(--spacing-24);
|
||||
}
|
||||
|
||||
/* 좁은 폭 · 입력 칸 2열 · 끝 단추 줄(557-2) — 입력 칸 두 줄짜리 양식(D01 · E01) */
|
||||
.ui-general-layout--narrow .ui-general-layout__inner {
|
||||
max-width: 720px;
|
||||
}
|
||||
|
||||
.ui-form-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: var(--spacing-16);
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.ui-form-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-general-block__actions--end {
|
||||
justify-content: flex-end;
|
||||
margin-top: var(--spacing-16);
|
||||
}
|
||||
|
||||
/* 일반 화면 틀(552-4) — 대시보드 세부 페이지 꼴(제목 20 · 설명 13 = 대시보드 값) */
|
||||
.ui-page-frame {
|
||||
display: flex;
|
||||
@@ -166,6 +188,7 @@ html.is-bm-route .ui-general-block__section {
|
||||
/* 숨김 — display 가 [hidden] 을 덮지 않게(공용 기본 꼴이 안 실린 곳 · 페이지 꾸밈 위에서도) */
|
||||
:is(
|
||||
.ui-btn-group,
|
||||
.ui-form-grid,
|
||||
.ui-general-block__actions,
|
||||
.ui-general-block__section-header,
|
||||
.ui-general-layout__content,
|
||||
|
||||
@@ -2,17 +2,20 @@
|
||||
* ui_template_general_layout.ts
|
||||
* 일반 화면 틀 `page-frame`(552-4 틀) — 프로젝트 밖 화면(대시보드 세부 · 설정 · B02 · 결제) 몸통: 페이지 머리 + 몸.
|
||||
* 꼴 = createPageFrame — 머리(제목 20 · 설명 13 · 오른쪽 주 단추 자리) + 몸(세로 쌓음) · 대시보드 세부 페이지 꼴
|
||||
* createGeneralLayout — 바탕 · 가운데 폭(--page-max-width) · 큰 제목(옛 꼴 그대로)
|
||||
* createGeneralLayout — 바탕 · 가운데 폭(--page-max-width) · 큰 제목(옛 꼴 그대로) · 좁은 폭(`narrow` · 720 · 557-2)
|
||||
* createFormGrid — 입력 칸 2열 배치(좁은 화면 1열 · 사이 16 · 557-2) · 끝 단추 줄 = `ui-general-block__actions ui-general-block__actions--end`(오른쪽 · 위 16)
|
||||
* 크기 = 폭 1120(대시보드 세부) · 머리 · 몸 사이 16 · 몸 안 사이 12
|
||||
* 상태 = 없음
|
||||
* 쓰는 법 = const f = createPageFrame(host, "구성원", "회사 사람 · 역할"); f.setAction(addBtn); f.body.append(table)
|
||||
* 대신하는 옛 변종 = B01 `pageFrame`(`.b01-pg__head`) · `.b01-proj-head` · M01 `m01-head` · `.ui-wf`(안 씀)
|
||||
* 대신하는 옛 변종 = B01 `pageFrame`(`.b01-pg__head`) · `.b01-proj-head` · M01 `m01-head` · `.ui-wf`(안 씀) · D01 · E01 이 빌려 쓰던 B02 `b02-proj` · `b02-proj__grid` · `b02-proj__actions`
|
||||
* ========================================================================== */
|
||||
|
||||
import "./ui_template_general_layout.css";
|
||||
|
||||
export interface GeneralLayoutOptions {
|
||||
pageClass?: string;
|
||||
/** 좁은 폭 720 — 입력 칸 두 줄짜리 양식 */
|
||||
narrow?: boolean;
|
||||
title?: string;
|
||||
subtitle?: string;
|
||||
content: HTMLElement | readonly HTMLElement[];
|
||||
@@ -27,6 +30,7 @@ export function createGeneralLayout(options: GeneralLayoutOptions): GeneralLayou
|
||||
const root = document.createElement("div");
|
||||
root.className = "ui-general-layout";
|
||||
if (options.pageClass) root.classList.add(options.pageClass);
|
||||
if (options.narrow) root.classList.add("ui-general-layout--narrow");
|
||||
|
||||
const inner = document.createElement("div");
|
||||
inner.className = "ui-general-layout__inner";
|
||||
@@ -55,6 +59,14 @@ export function createGeneralLayout(options: GeneralLayoutOptions): GeneralLayou
|
||||
return { root, content };
|
||||
}
|
||||
|
||||
/** 입력 칸 2열 배치(557-2) — 좁은 화면(640 아래) 1열 */
|
||||
export function createFormGrid(fields: readonly HTMLElement[]): HTMLElement {
|
||||
const grid = document.createElement("div");
|
||||
grid.className = "ui-form-grid";
|
||||
grid.append(...fields);
|
||||
return grid;
|
||||
}
|
||||
|
||||
export interface PageFrame {
|
||||
root: HTMLElement;
|
||||
body: HTMLElement;
|
||||
|
||||
@@ -31,6 +31,34 @@
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
}
|
||||
/* 규칙 꼴(557-2) — 왼쪽 15em · 설명 왼쪽 맞춤 · 줄마다 아래 선 · 값 없는 줄은 이름이 통째 */
|
||||
.ui-kv--rule {
|
||||
margin-bottom: var(--spacing-8);
|
||||
}
|
||||
.ui-kv--rule .ui-kv__row {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 15em) 1fr;
|
||||
align-items: center;
|
||||
gap: var(--spacing-12);
|
||||
padding: var(--spacing-4) 0;
|
||||
border-top: 0;
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
white-space: nowrap;
|
||||
}
|
||||
.ui-kv--rule .ui-kv__label {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--spacing-4);
|
||||
color: inherit;
|
||||
font-size: inherit;
|
||||
}
|
||||
.ui-kv--rule .ui-kv__label:only-child {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
.ui-kv--rule .ui-kv__value {
|
||||
font-size: inherit;
|
||||
text-align: left;
|
||||
}
|
||||
.ui-kv__mark {
|
||||
margin-right: var(--spacing-4);
|
||||
font-weight: 700;
|
||||
@@ -43,6 +71,6 @@
|
||||
}
|
||||
|
||||
/* 숨김 — display 가 [hidden] 을 덮지 않게(공용 기본 꼴이 안 실린 곳 · 페이지 꾸밈 위에서도) */
|
||||
:is(.ui-kv, .ui-kv__row)[hidden] {
|
||||
:is(.ui-kv, .ui-kv__row, .ui-kv__label)[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
@@ -2,27 +2,34 @@
|
||||
* ui_template_kv.ts
|
||||
* 부품 「값 목록」(552-3 · 목록 29) — 「이름 : 값」 줄 여럿(dl).
|
||||
* 꼴 = 보통 줄 · 판정 줄(`ok: true` = ✓ · `false` = ✗ — 색만으로 안 가름) · 큰 값 줄(`big`)
|
||||
* · 규칙 꼴(`createKv(rows, { rule: true })` · 557-2) = 왼쪽 15em 칸(키 칩 등 요소) · 오른쪽 설명 글 왼쪽 맞춤 · 값 없는 줄(`value` 안 줌)은 이름이 줄 통째
|
||||
* 크기 = 줄 24 · 글 12(이름) / 14(값) · 큰 값 18
|
||||
* 상태 = 기본만
|
||||
* 쓰는 법 = `createKv([{ label: "연장", value: "1,080 m" }, { label: "종단 경사", value: "12 %", ok: true }])`
|
||||
* 대신하는 옛 변종 = B04 `b04-surface__line` · B07 `.b07-info__row` · CAD `.cad-properties dl` · E04 `e04-result__list` · E07 `e07-summary` · B08 `b08-hydro__row`
|
||||
* 대신하는 옛 변종 = B04 `b04-surface__line` · B07 `.b07-info__row` · CAD `.cad-properties dl` · E04 `e04-result__list` · E07 `e07-summary` · ☰ 설정 `app-settings__rules`
|
||||
* ========================================================================== */
|
||||
|
||||
import { el } from "./ui_template_elements_base";
|
||||
import "./ui_template_kv.css";
|
||||
|
||||
export interface KvRow {
|
||||
label: string;
|
||||
/** 없으면 「-」 */
|
||||
value: string | number | Node | null | undefined;
|
||||
label: string | Node;
|
||||
/** 없으면 「-」 · 규칙 꼴에서 안 주면 이름이 줄 통째 */
|
||||
value?: string | number | Node | null;
|
||||
/** 판정 — true ✓ · false ✗ */
|
||||
ok?: boolean;
|
||||
big?: boolean;
|
||||
}
|
||||
|
||||
export function createKv(rows: KvRow[]): HTMLDListElement {
|
||||
const list = el("dl", { className: "ui-kv" });
|
||||
export function createKv(rows: KvRow[], opts: { rule?: boolean } = {}): HTMLDListElement {
|
||||
const list = el("dl", { className: `ui-kv${opts.rule ? " ui-kv--rule" : ""}` });
|
||||
for (const row of rows) {
|
||||
const label = el("dt", { className: "ui-kv__label" });
|
||||
label.append(row.label);
|
||||
if (opts.rule && row.value === undefined) {
|
||||
list.append(el("div", { className: "ui-kv__row", children: [label] }));
|
||||
continue;
|
||||
}
|
||||
const value = el("dd", { className: "ui-kv__value" });
|
||||
const v = row.value;
|
||||
value.append(
|
||||
@@ -39,7 +46,7 @@ export function createKv(rows: KvRow[]): HTMLDListElement {
|
||||
list.append(
|
||||
el("div", {
|
||||
className: `ui-kv__row${row.big ? " ui-kv__row--big" : ""}`,
|
||||
children: [el("dt", { className: "ui-kv__label", text: row.label }), value],
|
||||
children: [label, value],
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -88,6 +88,12 @@
|
||||
.ui-modal[data-width="960px"] {
|
||||
width: min(960px, 92vw);
|
||||
}
|
||||
/* 넓은 중간 창(557-2) — wide 꼴 + 1100 × 680 · 몸 안 칸이 따로 스크롤 */
|
||||
.ui-modal.ui-modal--wide[data-width="1100px"] {
|
||||
--modal-max: min(680px, 86vh);
|
||||
width: min(1100px, 94vw);
|
||||
height: var(--modal-max);
|
||||
}
|
||||
.ui-confirm__title {
|
||||
margin: 0 0 var(--spacing-8);
|
||||
font-size: var(--text-body);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* ui_template_modal.ts
|
||||
* 부품 「모달 · 확인창」(552-3 · 목록 22) — 화면을 가리고 위에 뜨는 창.
|
||||
* 꼴 = 모달(`openModal` · 제목줄 · 닫기 · 몸은 `mount`) · 넓음(`wide` · 96vw × 94vh) · 폭 단(`width`: 24rem · 40rem · 960px · 기본 720)
|
||||
* · 넓은 중간 창(`wide` + `width: "1100px"` · 1100 × 680 · 몸 넘침만 스크롤)
|
||||
* · 확인창(`askConfirm`) · 위험 확인(`danger` · 확인 단추 빨강 = `showConfirmDialog`) · 글 묻기(`askText` · window.prompt 대신)
|
||||
* 크기 = 모달 여백 16 · 둥글기 12 · 확인창 폭 300 ~ 420 · 단추 = 단추 부품 기본
|
||||
* 상태 = 기본만 · Esc = 닫기(한글 조합 중 · 몸이 막은 Esc 는 안 닫음) · 바깥 누르기 = 누름 · 뗌 둘 다 배경일 때만
|
||||
@@ -26,8 +27,8 @@ export interface ModalOptions {
|
||||
actions?: HTMLElement[];
|
||||
/** 큰 창 — 96vw × 94vh · 몸 넘침만 스크롤 */
|
||||
wide?: boolean;
|
||||
/** 폭 단 — 없으면 720 */
|
||||
width?: "24rem" | "40rem" | "960px";
|
||||
/** 폭 단 — 없으면 720 · 1100px 은 `wide` 와 함께(높이 680) */
|
||||
width?: "24rem" | "40rem" | "960px" | "1100px";
|
||||
backdropClass?: string;
|
||||
/** 닫기 단추 · Esc · 바깥 누르기 전에 물음 — false 면 안 닫음(몸의 `close` 는 묻지 않고 닫음) */
|
||||
beforeClose?: () => boolean | Promise<boolean>;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* 좁은 꼴 = `settingRow(제목, 설명, 오른쪽, { compact: true })` — 좁은 왼쪽 패널용 · 줄 최소 28 · 글 12 · 아래 선 없음 · 줄 통째가 label(`data-size="compact"`)
|
||||
* 상태 = 기본만(조작 부품이 저마다 상태를 가짐)
|
||||
* 쓰는 법 = `settingCard([settingRow("회사 이름", "보고서 머리에 들어감", valueText("산림설계"))])`
|
||||
* 대신하는 옛 변종 = B01 `settingRow` · `settingCard`(`.b01-pg-row` · `.b01-pg-settings`) · `.b01-dashboard__setting-card` · E02 `e02-field` · E06 `e06-setting` · B08 `b08-hydro__row`
|
||||
* 대신하는 옛 변종 = B01 `settingRow` · `settingCard`(`.b01-pg-row` · `.b01-pg-settings`) · `.b01-dashboard__setting-card` · E02 `e02-field` · E06 `e06-setting`
|
||||
* ========================================================================== */
|
||||
|
||||
import { el } from "./ui_template_elements_base";
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
--color-success: #1f8a4c; /* 성공/승인 */
|
||||
--color-warning: #b45309; /* 경고 */
|
||||
--color-danger: #c0392b; /* 에러/삭제 글자 · 선 */
|
||||
--color-danger-fill: #e5484d; /* 위험 채운 바탕(알림 점 · 배지) */
|
||||
--color-danger-fill: #d6363c; /* 위험 채운 바탕(알림 점 · 배지) · 흰 글 대비 4.5 이상 */
|
||||
--color-info: #2e6fb5;
|
||||
/* 패널 접기/펼치기 손잡이 테두리 — 강조색 */
|
||||
--panel-handle-border: var(--color-accent);
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
import { createIconButton } from "./ui_template_button";
|
||||
import { createButton } from "./ui_template_elements";
|
||||
import { createFloatPanel } from "./ui_template_float_panel";
|
||||
import { createPageFrame } from "./ui_template_general_layout";
|
||||
import { createFormGrid, createPageFrame } from "./ui_template_general_layout";
|
||||
import { createInputField } from "./ui_template_input";
|
||||
import { createListRow } from "./ui_template_list_row";
|
||||
import { createWorkflowPanelHandle } from "./ui_template_overlay";
|
||||
import { createPanelHead } from "./ui_template_panel_head";
|
||||
@@ -93,6 +94,19 @@ function pageFrame(): HTMLElement {
|
||||
const f = createPageFrame(null, "구성원", "회사 사람 · 역할 · 초대");
|
||||
f.setAction(createButton({ label: "초대", onClick: noop }));
|
||||
f.body.append(div("ui-general-block__empty", "몸 — 표 · 카드가 들어감"));
|
||||
// 입력 칸 2열 · 끝 단추 줄(557-2 · D01 · E01 등록 양식)
|
||||
const actions = div("ui-general-block__actions ui-general-block__actions--end");
|
||||
actions.append(
|
||||
createButton({ label: "취소", variant: "ghost", onClick: noop }),
|
||||
createButton({ label: "등록", onClick: noop }),
|
||||
);
|
||||
f.body.append(
|
||||
createFormGrid([
|
||||
createInputField({ label: "프로젝트 이름" }).root,
|
||||
createInputField({ label: "지역" }).root,
|
||||
]),
|
||||
actions,
|
||||
);
|
||||
return f.root;
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,9 @@ import { buildModal, confirmPanel } from "./ui_template_modal";
|
||||
import { settingCard, settingRow, valueText } from "./ui_template_setting_row";
|
||||
import { createTable, type Cell, type Column } from "./ui_template_table";
|
||||
import { createCard } from "./ui_template_card";
|
||||
import { createPanelCard } from "./ui_template_collapsible";
|
||||
import { createCollapsibleHead, createPanelCard } from "./ui_template_collapsible";
|
||||
import { createCalcFormula, createCalcFormulaRow, createCalcRow } from "./ui_template_calc_row";
|
||||
import { createDataTable } from "./ui_template_data_table";
|
||||
import { createBarChart, createDonut, createLineChart } from "./ui_template_elements_chart";
|
||||
import { createEmpty } from "./ui_template_empty";
|
||||
import { createLegend } from "./ui_template_flow_legend";
|
||||
@@ -147,6 +149,56 @@ const col = (...children: HTMLElement[]): HTMLElement => {
|
||||
box.style.alignItems = "stretch";
|
||||
return box;
|
||||
};
|
||||
/** 557-2 견본 — 접이 구역 머리 · 칸 램프 표 · 규칙 꼴 키 칩 · 계산 줄 */
|
||||
const kitSection = (): HTMLElement => {
|
||||
const box = el("section", { className: "ui-collapsible" });
|
||||
box.append(
|
||||
createCollapsibleHead({ title: "조합 연결", sub: "연결됨 1 / 4", level: 4 }).root,
|
||||
createText("배관 · 본체 미연결"),
|
||||
);
|
||||
return box;
|
||||
};
|
||||
const kitLampTable = (): HTMLElement =>
|
||||
createDataTable({
|
||||
columns: [
|
||||
{ key: "st", label: "측점" },
|
||||
{ key: "c", label: "단가연결" },
|
||||
],
|
||||
rows: [
|
||||
{ st: "No.1", c: "단가연결" },
|
||||
{ st: "No.2", c: "연결됨" },
|
||||
],
|
||||
cellClass: (i, key) =>
|
||||
key === "st"
|
||||
? `ui-data-table__lamp${i ? " is-linked" : ""}`
|
||||
: `ui-data-table__lamp ui-data-table__lamp--only${i ? " is-linked" : ""}`,
|
||||
}).root;
|
||||
const kitKeys = (...keys: string[]): DocumentFragment => {
|
||||
const f = document.createDocumentFragment();
|
||||
f.append(...keys.map((k) => createBadge(k, "neutral")));
|
||||
return f;
|
||||
};
|
||||
const kitCalc = (): HTMLElement => {
|
||||
const input = (v: string): HTMLElement => {
|
||||
const f = createInputField({ type: "number", compact: true });
|
||||
f.input.value = v;
|
||||
f.input.classList.add("ui-calc-row__in");
|
||||
return f.input;
|
||||
};
|
||||
const out = (v: string, key = false): HTMLElement =>
|
||||
el("span", { className: `ui-calc-row__out${key ? " is-key" : ""}`, text: v });
|
||||
return col(
|
||||
createCalcFormula("Q = 1/3.6 × f × rt × A"),
|
||||
createCalcRow("f : 유출계수", input("0.8")),
|
||||
createCalcRow("A : 유역면적 (㎢)", input("0.46"), out("46.00 ha")),
|
||||
createCalcFormulaRow("경심", createCalcFormula("R = A / P"), out("0.250")),
|
||||
createCalcFormulaRow(
|
||||
"유속 (m/s)",
|
||||
createCalcFormula("V = 1/n × R^2/3^ × S^1/2^"),
|
||||
out("2.41", true),
|
||||
),
|
||||
);
|
||||
};
|
||||
/** 552-2 견본 조각 — 상태를 안쪽 요소에 굳힘 */
|
||||
const OPTS = [
|
||||
{ value: "a", text: "임도" },
|
||||
@@ -257,31 +309,34 @@ export const KIT_PARTS: KitPart[] = [
|
||||
{ key: "card", name: "카드", group: "compound", kinds: "선 카드 · 머리 카드 · 고르는 카드 · 여백 12 · 16", file: "ui_template_card.ts",
|
||||
demo: (s) => { const pick = (): HTMLElement => markState(createCard({ title: "새 노선", body: ["노선을 그려 시작"], pad: 12, onSelect: noop }), s);
|
||||
return s === "error" ? null : s === "default" ? col(createCard({ title: "개요", body: ["임도 1 노선 · 1,080 m"], pad: 16 }), createCard({ title: "머리 카드", body: ["몸"], pad: 12, actions: [createButton({ label: "열기", variant: "ghost", size: "sm", onClick: noop })] }), createCard({ title: "고른 카드", body: ["골라짐"], pad: 12, onSelect: noop, selected: true })) : pick(); } },
|
||||
{ key: "collapsible", name: "접이 카드", group: "compound", kinds: "패널 카드 · 구역 · 캐럿 ▾ ▸ · ⚙ 곁값", file: "ui_template_collapsible.ts",
|
||||
{ key: "collapsible", name: "접이 카드", group: "compound", kinds: "패널 카드 · 구역 · 구역 머리(제목 · 부제) · 캐럿 ▾ ▸ · ⚙ 곁값", file: "ui_template_collapsible.ts",
|
||||
demo: (s) => { if (s !== "default" && s !== "hover") return null; const open = createPanelCard("구조물 배치", false, noop); open.append(row(createInputField({ placeholder: "측점" }).root));
|
||||
if (s === "hover") { markState(open.querySelector<HTMLElement>(".ui-collapsible__title")!, s); return open; } return col(open, createPanelCard("접힌 카드")); } },
|
||||
{ key: "table", name: "표", group: "compound", kinds: "일반표(줄 40) · 데이터표(줄 28) · 격자표(세로선 · 합친 칸) · ⋯ 칸 · 한글 배지 · 빈 줄 · 줄 고름 · 바뀜 · 지움", file: "ui_template_table.ts",
|
||||
if (s === "hover") { markState(open.querySelector<HTMLElement>(".ui-collapsible__title")!, s); return open; } return col(open, createPanelCard("접힌 카드"), kitSection()); } },
|
||||
{ key: "table", name: "표", group: "compound", kinds: "일반표(줄 40) · 데이터표(줄 28) · 격자표(세로선 · 합친 칸) · ⋯ 칸 · 한글 배지 · 빈 줄 · 줄 고름 · 바뀜 · 지움 · 칸 램프(노랑 점 · ✓)", file: "ui_template_table.ts",
|
||||
demo: (s) => { if (s === "disabled" || s === "error") return null; const t = s === "default" ? createTable(KIT_COLS, kitRows()) : createTable(KIT_DATA, dataRows, { density: "data" });
|
||||
const first = t.querySelector<HTMLElement>("tbody tr")!; if (s === "hover") markState(first, s); if (s === "active") first.classList.add("is-active");
|
||||
if (s !== "default") return t; const data = createTable(KIT_DATA, dataRows, { density: "data" }); const rows = data.querySelectorAll("tbody tr"); rows[0].classList.add("is-changed"); rows[1].classList.add("is-removed");
|
||||
return col(t, data, createTable(KIT_DATA, [], { empty: "노선 없음" }), createTable([], gridRows(), { density: "data", grid: true })); } },
|
||||
return col(t, data, createTable(KIT_DATA, [], { empty: "노선 없음" }), createTable([], gridRows(), { density: "data", grid: true }), kitLampTable()); } },
|
||||
{ key: "row-menu", name: "⋯ · 오른쪽 메뉴", group: "compound", kinds: "줄 끝 ⋯(28) · 펼친 판 · 위험 항목(빨강 + 확인창) · 막힌 항목", file: "ui_template_context_menu.ts",
|
||||
demo: (s) => { if (s === "error") return null; if (s === "default") return createRowMenu(KIT_MENU); if (s === "hover") { const m = createRowMenu(KIT_MENU); markState(m.querySelector<HTMLElement>(".ui-row-menu__btn")!, s); return m; }
|
||||
return openMenu(s, s === "disabled" ? [{ label: "내보내기", onClick: noop, disabled: true }, ...KIT_MENU] : KIT_MENU); } },
|
||||
{ key: "empty", name: "빈 상태", group: "compound", kinds: "선 아이콘 · 한 줄 · 주 단추 · 사용법 · 작은 꼴", file: "ui_template_empty.ts",
|
||||
demo: (s) => (s === "default" ? col(createEmpty({ icon: "folder", text: "아직 프로젝트가 없음", hint: "새 프로젝트를 만들어 시작", action: createButton({ label: "새 프로젝트", onClick: noop }), onGuide: noop }), createEmpty({ text: "고른 측점 없음", small: true })) : null) },
|
||||
{ key: "modal", name: "모달 · 확인창", group: "compound", kinds: "보통(720) · 넓음 · 폭 단 24rem · 40rem · 960 · 긴 제목 = 한 줄 말줄임 · 확인 · 위험 확인 · 글 묻기(prompt 대신)", file: "ui_template_modal.ts",
|
||||
{ key: "modal", name: "모달 · 확인창", group: "compound", kinds: "보통(720) · 넓음 · 폭 단 24rem · 40rem · 960 · 넓은 중간 1100 × 680 · 긴 제목 = 한 줄 말줄임 · 확인 · 위험 확인 · 글 묻기(prompt 대신)", file: "ui_template_modal.ts",
|
||||
demo: (s) => { if (s !== "default") return null; const m = buildModal({ title: "구조물 고르기", closeLabel: "닫기", width: "40rem" }, noop); m.body.append("몸 — 표 · 양식이 들어감"); m.dialog.style.margin = "0";
|
||||
const long = buildModal({ title: "배수관 · 횡배수관 · 측구 · 집수정 · 옹벽 구조물을 한꺼번에 고르기", closeLabel: "닫기", width: "24rem" }, noop); long.body.append("[닫기] 는 한 줄 · 제목은 말줄임"); long.dialog.style.margin = "0";
|
||||
return col(m.dialog, long.dialog, confirmPanel({ message: "노선을 저장할까요?" }).panel, confirmPanel({ title: "삭제", message: "임도 2 노선을 지울까요? 되돌릴 수 없음", okLabel: "삭제", danger: true }).panel, confirmPanel({ message: "블록 이름", field: { placeholder: "예: 배수관" } }).panel); } },
|
||||
const mid = buildModal({ title: "운반 단가 — 넓은 중간 창", closeLabel: "닫기", wide: true, width: "1100px" }, noop); mid.body.append("몸 — 두 칸이 따로 스크롤"); mid.dialog.style.cssText = "margin:0;max-width:100%;height:160px";
|
||||
return col(m.dialog, long.dialog, mid.dialog, confirmPanel({ message: "노선을 저장할까요?" }).panel, confirmPanel({ title: "삭제", message: "임도 2 노선을 지울까요? 되돌릴 수 없음", okLabel: "삭제", danger: true }).panel, confirmPanel({ message: "블록 이름", field: { placeholder: "예: 배수관" } }).panel); } },
|
||||
{ key: "toast", name: "알림", group: "compound", kinds: "토스트 4꼴(성공 · 오류 · 정보 · 경고) · 로딩 가림막 · 진행 가림막", file: "ui_template_toast.ts",
|
||||
demo: (s) => (s === "default" ? col(toastNode("저장됨", "success"), toastNode("저장 못 함 — 다시 시도", "error"), toastNode("노선을 불러옴", "info"), toastNode("측점 간격이 넓음", "warning")) : null) },
|
||||
{ key: "banner", name: "띠 알림", group: "compound", kinds: "왼쪽 띠 · 정보 · 경고 · 위험 · 성공(✓) · 펼침 · 안 단추 · 닫기", file: "ui_template_banner.ts",
|
||||
demo: (s) => (s === "default" ? col(createBanner({ title: "안내", text: "초기값에서 시작함" }), createBanner({ tone: "warning", text: "저장 안 한 고침이 있음", actions: [createButton({ label: "저장", size: "sm", onClick: noop })] }), createBanner({ tone: "danger", text: "횡단 3 곳 계산 못 함", details: "No.12 · No.15 · No.20" }), createBanner({ tone: "success", text: "검토 끝", onClose: noop })) : null) },
|
||||
{ key: "stat", name: "숫자 카드", group: "compound", kinds: "값 · 단위 · 변화 · 게이지 · 판정", file: "ui_template_stat.ts",
|
||||
demo: (s) => (s === "default" ? statGrid([createStat({ label: "진행 중", value: 12, unit: "건", delta: "+3" }), createStat({ label: "디스크", value: "71 %", gauge: 0.71, tone: "warn" }), createStat({ label: "오류", value: 2, delta: "-1", tone: "bad" }), createStat({ label: "회사", value: 8, compact: true })]) : null) },
|
||||
{ key: "kv", name: "값 목록", group: "compound", kinds: "이름 : 값 줄 · 판정(✓ ✗) · 큰 값", file: "ui_template_kv.ts",
|
||||
demo: (s) => (s === "default" ? createKv([{ label: "연장", value: "1,080 m", big: true }, { label: "종단 경사", value: "12 %", ok: true }, { label: "최소 곡선반경", value: "12 m", ok: false }, { label: "비고", value: null }]) : null) },
|
||||
{ key: "kv", name: "값 목록", group: "compound", kinds: "이름 : 값 줄 · 판정(✓ ✗) · 큰 값 · 규칙 꼴(키 칩 = 설명)", file: "ui_template_kv.ts",
|
||||
demo: (s) => (s === "default" ? col(createKv([{ label: "연장", value: "1,080 m", big: true }, { label: "종단 경사", value: "12 %", ok: true }, { label: "최소 곡선반경", value: "12 m", ok: false }, { label: "비고", value: null }]), createKv([{ label: kitKeys("Ctrl", "Z"), value: "되돌리기" }, { label: kitKeys("휠 끌기"), value: "화면 옮기기" }, { label: kitKeys("Esc") }], { rule: true })) : null) },
|
||||
{ key: "calc-row", name: "계산 줄", group: "compound", kinds: "값 줄(이름 · 값 112 · 덧칸) · 식 줄(이름 · 식 · 값) · 위 · 아래첨자 · 중요 결과 굵게", file: "ui_template_calc_row.ts",
|
||||
demo: (s) => (s === "default" ? kitCalc() : null) },
|
||||
{ key: "progress", name: "진행 막대", group: "compound", kinds: "막대 6 · 8 · 쌓임 막대 · 원 · 오류", file: "ui_template_progress.ts",
|
||||
demo: (s) => { if (s === "error") return markState(createProgressBar({ value: 0.6, label: "실패" }).root, s); if (s !== "default") return null; const ring = createProgressCircle({ size: 48 }); ring.set(0.4);
|
||||
return col(createProgressBar({ value: 0.4, label: "40 %" }).root, createProgressBar({ value: 0.7, thick: 6 }).root, createProgressBar({ parts: [{ value: 2, tone: "soft" }, { value: 3 }, { value: 1, tone: "warn" }], label: "6 건" }).root, ring.root); } },
|
||||
@@ -298,7 +353,7 @@ export const KIT_PARTS: KitPart[] = [
|
||||
// ── 552-4 틀 ───────────────────────────────────────────────
|
||||
{ key: "work-frame", name: "작업 화면 틀", group: "frame", kinds: "왼쪽 패널(흰 판 · 1 px 선) · 본문 · 아래 단추 줄 · [?][<][>] · 머리 줄 없음 · ☰ 서랍", file: "ui_template_overlay.ts",
|
||||
demo: FRAME_DEMOS["work-frame"] },
|
||||
{ key: "page-frame", name: "일반 화면 틀", group: "frame", kinds: "페이지 머리(제목 · 설명 · 주 단추) · 몸통", file: "ui_template_general_layout.ts",
|
||||
{ key: "page-frame", name: "일반 화면 틀", group: "frame", kinds: "페이지 머리(제목 · 설명 · 주 단추) · 몸통 · 좁은 폭 720 · 입력 칸 2열 · 끝 단추 줄", file: "ui_template_general_layout.ts",
|
||||
demo: FRAME_DEMOS["page-frame"] },
|
||||
{ key: "public-frame", name: "공개 화면 틀", group: "frame", kinds: "머리 띠(제목 · 설명 · 주 단추) · 구역 · 본문", file: "ui_template_public_layout.ts",
|
||||
demo: FRAME_DEMOS["public-frame"] },
|
||||
|
||||
Reference in New Issue
Block a user