Merge remote-tracking branch 'origin/sub_laptop_5' into main_laptop_1

# Conflicts:
#	M01_MasterData/M01_MasterData_UI_Logic_List.ts
This commit is contained in:
2026-10-02 09:44:33 +09:00
6 changed files with 75 additions and 17 deletions
+18
View File
@@ -0,0 +1,18 @@
.m01-head {
display: flex;
align-items: center;
gap: var(--spacing-8);
flex-wrap: wrap;
}
.m01-head__title {
margin: 0 auto 0 0;
color: var(--color-text);
font-size: 18px;
}
.m01-head__filters {
display: flex;
flex-direction: column;
gap: var(--spacing-4);
}
+36
View File
@@ -0,0 +1,36 @@
/* =============================================================================
* M01_MasterData_UI_Head.ts
* 마스터 화면 머리 공용 조각 — 제목 줄(제목 왼쪽 · 단추 오른쪽) + 찾기 줄.
* 인력 · 재료 · 유가·전력 · 기계 · 소요량 · 계수 · 환율 · 요율 상세와 「단가산출 로직」 화면이 같은 조각을 써
* 제목 크기 · 자리 · 단추 차례 · 공용 단추 크기 · 찾기 칸 꼴이 한 벌로 같음.
* ========================================================================== */
import { el } from "@ui/ui_template_elements";
import "./M01_MasterData_UI_Head.css";
export interface HeadOptions {
/** 제목 앞에 놓을 것(예: ← 목록 단추) */
lead?: HTMLElement[];
/** 제목 — 글이면 h2 를 만듦 */
title: HTMLElement | string;
/** 제목 바로 뒤 · 단추 앞 알림(고친 개수 등) */
status?: HTMLElement[];
/** 오른쪽 단추 — 차례 그대로(공용 단추 · 크기를 덮지 않음) */
buttons: HTMLElement[];
}
/** 제목 줄 — 제목은 18px · 왼쪽 · 남는 자리를 차지해 단추를 오른쪽으로 밂 */
export function buildHeadBar(opts: HeadOptions): { root: HTMLElement; title: HTMLElement } {
const title = typeof opts.title === "string" ? el("h2", { text: opts.title }) : opts.title;
title.classList.add("m01-head__title");
const root = el("div", {
className: "m01-head",
children: [...(opts.lead ?? []), title, ...(opts.status ?? []), ...opts.buttons],
});
return { root, title };
}
/** 찾기 줄 — 찾기 칸 · 거름 · 쪽 넘기기를 위에서 아래로 쌓음 */
export function buildHeadFilters(children: HTMLElement[]): HTMLElement {
return el("div", { className: "m01-head__filters", children });
}
+8 -7
View File
@@ -52,6 +52,7 @@ import {
type LogicFlow,
} from "./M01_MasterData_UI_LogicLab_Flow";
import { askBasicInfo } from "./M01_MasterData_UI_LogicLab_Basic";
import { buildHeadBar } from "./M01_MasterData_UI_Head";
import { fetchSessionUser } from "../A06_Login/A06_Login_Api_Fetch";
import type { CfLogic } from "../M02_MasterTemplete/DataFlow/M02_DF_Model";
import "./M01_MasterData_UI_Logic_Style.css";
@@ -539,19 +540,19 @@ export async function mountM01LogicLab(
})
: showToast(tl("Copy_NeedOpen"), "info"),
});
const bar = el("div", {
className: "m01-logic__bar",
children: [
back,
el("h2", { text: tx("Title") }),
pending,
// 제목 줄 = 요소 화면(인력 · 재료 …)과 같은 공용 조각
const bar = buildHeadBar({
lead: [back],
title: tx("Title"),
status: [pending],
buttons: [
createButton({ label: tf("New"), variant: "filled", onClick: () => void onNew() }),
copyButton,
createButton({ label: tx("Bar_Delete"), variant: "danger", onClick: () => void onDelete() }),
discardButton,
saveButton,
],
});
}).root;
host.replaceChildren(
el("div", {
className: "m01-logic",
@@ -8,6 +8,7 @@ import { createInputField, el } from "@ui/ui_template_elements";
import { buildPager } from "./M01_MasterData_UI_Cells";
import type { LogicSummary } from "./M01_MasterData_UI_Logic_Api";
import { tx } from "./M01_MasterData_UI_Logic_Text";
import "./M01_MasterData_UI_Head.css";
export type ListMark = "edited" | "new" | "deleted";
@@ -111,7 +112,7 @@ export function buildList(onOpen: (item: ListItem) => void): ListHandle {
className: "m01-logic__listview",
children: [
el("div", {
className: "m01-logic__filters",
className: "m01-head__filters",
children: [searchField.root, pager],
}),
grid,
+6 -3
View File
@@ -23,6 +23,7 @@ import { navigateTo } from "../A00_Common/router";
import { fetchSessionUser } from "../A06_Login/A06_Login_Api_Fetch";
import { saveFiles, type FileInfo } from "./M01_MasterData_Api_Fetch";
import { discard, isStale, onDraftChange, payload, totalCount } from "./M01_MasterData_Draft";
import { buildHeadBar, buildHeadFilters } from "./M01_MasterData_UI_Head";
import { renderRows } from "./M01_MasterData_UI_Rows";
import {
buildSide,
@@ -62,7 +63,7 @@ function buildPage(): HTMLElement {
let logicMounted = false;
/* --- 우측: 머리(제목·찾기·저장) + 알림 + 본문 --- */
const title = el("h2", { className: "m01-master__title", text: L("M01_PickFile") });
const title = el("h2", { text: L("M01_PickFile") });
const searchField = createInputField({ type: "search", placeholder: L("M01_Search") });
const search = searchField.input;
const summary = el("span", { className: "m01-master__summary" });
@@ -70,9 +71,11 @@ function buildPage(): HTMLElement {
const drop = createButton({ label: L("M01_Discard"), variant: "ghost" });
const notice = el("div", { className: "m01-master__notice", attrs: { hidden: "" } });
const body = el("div", { className: "m01-master__body" });
// 제목 줄 · 찾기 줄 = 「단가산출 로직」 화면과 같은 공용 조각
const headBar = buildHeadBar({ title, status: [summary], buttons: [drop, save] });
const head = el("div", {
className: "m01-master__head",
children: [title, searchField.root, summary, drop, save],
className: "m01-master__headbox",
children: [headBar.root, buildHeadFilters([searchField.root])],
});
const logicHost = el("div", { className: "m01-master__logic", attrs: { hidden: "" } });
const elementView = el("div", {
+5 -6
View File
@@ -32,7 +32,6 @@
padding: var(--spacing-16) var(--spacing-24);
}
.m01-master__head,
.m01-master__bar,
.m01-master__filter {
display: flex;
@@ -41,11 +40,11 @@
gap: var(--spacing-12);
}
.m01-master__title {
flex: 1 1 auto;
margin: 0;
color: var(--color-text);
font-size: var(--text-body);
/* 머리 상자 — 제목 줄(공용 Head) 아래 찾기 줄 */
.m01-master__headbox {
display: flex;
flex-direction: column;
gap: var(--spacing-8);
}
.m01-master__summary {