- DataFlow: 표 넷 = 데이터표 · 함수 판 알림 · 선/알약 경고 · 끊긴 곳 = 띠 알림 · 알약 · 칸 종류 = 배지 · 고르는 줄 = 목록 줄 · 입력 넷 = 입력 · 도구줄 여섯 · 날 색 → 공통 값 - 구조물 옵션 · 구조물 · 도면 · 면적 · 품목 고르기: 판 띠 = 띠 알림 · 한눈/종류별/부품 = 탭 · 표 둘 = 데이터표 · 체크 둘 = 체크 · 카드 · 번호 = 카드 · 배지 · 도면 도구줄 - 일위대가 · 호표 · 실무자료 · 본 화면 · 서고: 알림 = 띠 알림 · 빈 상태 · 보조 글 · 도구줄 여섯 · 옛 보라 별칭 0 - M01 로직 입력칸 = 입력 부품(작은 칸) · 일위대가 상세 줄 수 고정 시험 693 · 지킴 시험 M02 몫 더함 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
143 lines
4.9 KiB
TypeScript
143 lines
4.9 KiB
TypeScript
/* =============================================================================
|
|
* M02_DF_InputPanel_Rule.ts
|
|
* 입력 판 — 인력 · 기계 · 유가전력의 「조건 찾기」: 이름 또는 키가 이 로직 변수 알약 값과 같은 줄을 잡음
|
|
* (마스터 로직의 LB:{직종} · EQ:{기계} 꼴) · 지금 잡히는 줄 미리 보기(0 · 여럿이면 까닭) · 글 [한, 영] 짝은 아래
|
|
* 선택 자리(choice)는 재료만 — 이 판에 없음
|
|
* ========================================================================== */
|
|
|
|
import { currentLanguageIndex } from "@ui/ui_template_locale";
|
|
import {
|
|
createButton,
|
|
createInputField,
|
|
createSelectField,
|
|
el,
|
|
showToast,
|
|
} from "@ui/ui_template_elements";
|
|
import type { CfElementRef, CfGroup, CfLogic } from "./M02_DF_Model";
|
|
import {
|
|
catchNote,
|
|
checkGroupRule,
|
|
groupRuleElement,
|
|
variablePills,
|
|
type GroupRuleField,
|
|
} from "./M02_DF_InputPanel_Build";
|
|
import "@ui/ui_template_text.css";
|
|
import "@ui/ui_template_badge.css";
|
|
import "@ui/ui_template_list_row.css";
|
|
import "@ui/ui_template_toolbar.css";
|
|
|
|
const TEXT = {
|
|
Help: [
|
|
"변수 알약 값과 이름(또는 키)이 같은 줄을 잡음 — 예: 직종 알약 값이 「석공」이면 석공 줄",
|
|
"Catches the row whose name (or key) equals a variable pill's value",
|
|
],
|
|
SlotName: ["자리 이름(알약 이름)", "Slot name (pill name)"],
|
|
Field: ["같아야 하는 칸", "Column"],
|
|
FieldName: ["이름", "Name"],
|
|
FieldKey: ["키(원문번호)", "Key (source no.)"],
|
|
Pill: ["변수 알약", "Variable pill"],
|
|
NoPill: ["변수 알약이 없음 — 변수 입력 상자를 먼저 만듦", "No variable pill yet"],
|
|
Preview: ["지금 잡히는 줄", "Rows caught now"],
|
|
Put: ["넣기", "Add"],
|
|
Failed: ["불러오지 못함", "Could not load"],
|
|
} as const;
|
|
|
|
type TKey = keyof typeof TEXT;
|
|
const t = (key: TKey): string => TEXT[key][currentLanguageIndex] as string;
|
|
|
|
/** 미리 보기 길 — 그룹 · 칸 · 알약 값으로 마스터 줄을 읽어 잡힌 수 · 이름 · 단위(부르는 쪽이 줌) */
|
|
export type RulePreview = (
|
|
group: CfGroup,
|
|
field: GroupRuleField,
|
|
value: unknown,
|
|
) => Promise<{ found: number; names: string[]; unit: string }>;
|
|
|
|
/** 조건 찾기 요소 추가 판 */
|
|
export function renderGroupRule(
|
|
host: HTMLElement,
|
|
lg: CfLogic,
|
|
boxId: string,
|
|
group: CfGroup,
|
|
addEl: (lg: CfLogic, el: CfElementRef) => CfLogic,
|
|
preview: RulePreview,
|
|
on: { change(lg: CfLogic): void },
|
|
): void {
|
|
const els = lg.boxes.find((b) => b.id === boxId)?.input?.elements ?? [];
|
|
const pills = variablePills(lg);
|
|
const name = createInputField({
|
|
type: "text",
|
|
label: t("SlotName"),
|
|
compact: true,
|
|
placeholder: group === "인력" ? "예: 직종" : "예: 기종",
|
|
});
|
|
const field = createSelectField({
|
|
label: t("Field"),
|
|
options: [
|
|
{ value: "이름", text: t("FieldName") },
|
|
{ value: "키", text: t("FieldKey") },
|
|
],
|
|
value: "이름",
|
|
compact: true,
|
|
onChange: () => void refresh(),
|
|
});
|
|
const pill = createSelectField({
|
|
label: t("Pill"),
|
|
options: pills.map((p, i) => ({
|
|
value: String(i),
|
|
text: `${p.port.name}${p.example !== null ? ` = ${p.example}` : ""}`,
|
|
})),
|
|
value: "0",
|
|
compact: true,
|
|
onChange: () => void refresh(),
|
|
});
|
|
const note = el("p", { className: "m02-dfip__note" });
|
|
const build = (): CfElementRef =>
|
|
groupRuleElement(
|
|
name.input.value,
|
|
field.select.value as GroupRuleField,
|
|
pills[Number(pill.select.value)]?.port ?? { box: "", name: "" },
|
|
);
|
|
let seq = 0;
|
|
const refresh = async (): Promise<void> => {
|
|
const mine = ++seq;
|
|
const value = pills[Number(pill.select.value)]?.example;
|
|
note.textContent = `${t("Preview")}: …`;
|
|
try {
|
|
const p = await preview(group, field.select.value as GroupRuleField, value);
|
|
if (mine !== seq) return;
|
|
const n = catchNote(p.found, p.names[0], "줄");
|
|
note.textContent = `${t("Preview")}: ${n.text}`;
|
|
note.className = `m02-dfip__note${n.kind === "one" ? "" : " is-bad"}`;
|
|
} catch {
|
|
if (mine === seq) note.textContent = t("Failed");
|
|
}
|
|
};
|
|
const add = createButton({
|
|
label: t("Put"),
|
|
size: "sm",
|
|
onClick: () => {
|
|
const cand = build();
|
|
const why = checkGroupRule(cand, els);
|
|
name.setError(why ?? undefined);
|
|
if (why) return void showToast(why, "error");
|
|
// 잡히는 줄이 하나면 그 단위를 알약 단위로
|
|
void preview(
|
|
group,
|
|
field.select.value as GroupRuleField,
|
|
pills[Number(pill.select.value)]?.example,
|
|
).then(
|
|
(p) => on.change(addEl(lg, p.unit ? { ...cand, unit: p.unit } : cand)),
|
|
() => on.change(addEl(lg, cand)),
|
|
);
|
|
},
|
|
});
|
|
host.replaceChildren(
|
|
el("p", { className: "ui-text ui-text--muted", text: t("Help") }),
|
|
name.root,
|
|
...(pills.length
|
|
? [field.root, pill.root, note, add]
|
|
: [el("p", { className: "ui-text ui-text--error", text: t("NoPill") })]),
|
|
);
|
|
if (pills.length) void refresh();
|
|
}
|