- 조합 고치기 [로직 더하기] 창이 M02 에서 화면 밖(페이지 아래)으로 뜨던 것 — 고르기 창 겉틀 CSS 를 같이 받음 - 조합 고치기 창을 고친 채 닫기 · Esc · 바깥 누르기 — 버릴지 물음(공용 창 틀에 beforeClose 더함) - 새 조합 이름: 빈 이름 · 60자 넘음은 까닭 글로 막음(서버 422 「HTTP 422」 대신) · Enter 로 만듦 · 자동 이름 겹침 없앰(구조물 이름이 조합 이름을 품으면 하나만) - 담은 로직 · 담은 자재를 다 빼고 [적용] — 서버 400 대신 미리 까닭 알림 - 옆 판에서 이은 자재 [빼기] — 확인 글대로 연결을 바로 풂(전엔 적용 전까지 알약이 연결된 채 남음) - 이미 담은 자재를 또 고르면 알림 · 메모 칸 전체 글을 title 로 - 알약 우클릭 메뉴가 누른 자리에서 부품 원점만큼 밀려 뜨던 것 바로잡음 - 저장 전 새 열(분류 더하기)의 [수정] — 집계표 고침을 버리고 빈 화면으로 가던 것 막고 「집계표를 먼저 저장」 알림 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019Kyw9qnkJ94pC1A4PfpDtA
52 lines
1.9 KiB
TypeScript
52 lines
1.9 KiB
TypeScript
/* =============================================================================
|
|
* ui_template_modal.ts
|
|
* 공용 모달 겉틀 — 배경 · 제목줄 · 닫기 · Esc · 포커스. 몸은 `mount` 가 채움.
|
|
* M01 · M02 가 함께 씀 — `dialogClass` · `backdropClass` 로 화면별 겉모양을 덧붙임.
|
|
* ========================================================================== */
|
|
|
|
import "./ui_template_modal.css";
|
|
import { createButton, el } from "./ui_template_elements";
|
|
|
|
export interface ModalOptions {
|
|
title: string;
|
|
closeLabel: string;
|
|
mount: (body: HTMLElement, close: () => void) => void;
|
|
dialogClass?: string;
|
|
backdropClass?: string;
|
|
/** 닫기 단추 · Esc · 바깥 누르기 전에 물음 — false 면 안 닫음(몸의 `close` 는 묻지 않고 닫음) */
|
|
beforeClose?: () => boolean | Promise<boolean>;
|
|
}
|
|
|
|
export function openModal(opts: ModalOptions): { close: () => void } {
|
|
const body = el("div", { className: "ui-modal__body" });
|
|
const close = (): void => backdrop.remove();
|
|
const ask = async (): Promise<void> => {
|
|
if (!opts.beforeClose || (await opts.beforeClose())) close();
|
|
};
|
|
const dialog = el("div", {
|
|
className: `ui-modal ${opts.dialogClass ?? ""}`.trim(),
|
|
attrs: { role: "dialog", "aria-label": opts.title },
|
|
children: [
|
|
el("div", {
|
|
className: "ui-modal__top",
|
|
children: [
|
|
el("h3", { text: opts.title }),
|
|
createButton({ label: opts.closeLabel, variant: "ghost", onClick: () => void ask() }),
|
|
],
|
|
}),
|
|
body,
|
|
],
|
|
});
|
|
const backdrop = el("div", {
|
|
className: `ui-modal__backdrop ${opts.backdropClass ?? ""}`.trim(),
|
|
children: [dialog],
|
|
});
|
|
backdrop.addEventListener("click", (ev) => ev.target === backdrop && void ask());
|
|
backdrop.addEventListener("keydown", (ev) => ev.key === "Escape" && void ask());
|
|
opts.mount(body, close);
|
|
document.body.append(backdrop);
|
|
dialog.tabIndex = -1;
|
|
dialog.focus();
|
|
return { close };
|
|
}
|