import { createIcons, X } from 'lucide'; import { setEditLock } from './ModalUtils'; import './modal.css'; /** * 모든 모달의 공통 기능을 관리하는 베이스 추상 클래스입니다. */ export abstract class BaseModal { protected idPrefix: string; protected title: string; protected currentAsset: any | null = null; protected isEditMode: boolean = false; protected currentMode: 'view' | 'edit' | 'add' = 'view'; protected modalEl: HTMLElement | null = null; protected formEl: HTMLFormElement | null = null; constructor(idPrefix: string, title: string) { this.idPrefix = idPrefix; this.title = title; } /** * 모달 초기화: HTML 삽입 및 공통 이벤트 바인딩 */ public init(onSave: () => void, closeModalsFn: () => void) { // 1. 프레임 HTML 삽입 (자식 클래스에서 정의한 HTML 사용) if (!document.getElementById(`${this.idPrefix}-asset-modal`)) { document.body.insertAdjacentHTML('beforeend', this.renderFrameHTML()); } this.modalEl = document.getElementById(`${this.idPrefix}-asset-modal`); this.formEl = document.getElementById(`${this.idPrefix}-asset-form`) as HTMLFormElement; // 2. 공통 버튼 이벤트 바인딩 (닫기, 취소 등) const btnCloseHeader = document.getElementById(`btn-close-${this.idPrefix}-modal`); const btnCancelFooter = document.getElementById(`btn-cancel-${this.idPrefix}-modal`); const closeAction = () => { this.close(); closeModalsFn(); // 전역 모달 상태 해제 콜백 }; btnCloseHeader?.addEventListener('click', closeAction); btnCancelFooter?.addEventListener('click', closeAction); // 3. 자식 클래스 전용 초기화 로직 실행 this.initChildLogic(onSave, closeModalsFn); // 4. 아이콘 초기화 createIcons({ icons: { X } }); } /** * 모달 열기: 데이터 바인딩 및 모드 설정 */ public open(asset: any, mode: 'view' | 'edit' | 'add' = 'view') { this.currentAsset = asset; this.currentMode = mode; this.isEditMode = (mode === 'add' || mode === 'edit'); // 폼 초기화 추가 if (this.formEl) this.formEl.reset(); // fillFormData를 먼저 호출하여 동적 요소들을 생성한 후 잠금 처리 this.fillFormData(asset); this.setEditLockMode(mode); if (this.modalEl) { this.modalEl.classList.remove('hidden'); const content = this.modalEl.querySelector('.modal-content'); if (content) { if (mode === 'view') content.classList.add('is-view-mode'); else content.classList.remove('is-view-mode'); } } this.onAfterOpen(asset, mode); } /** * 모달 닫기: 상태 초기화 */ public close() { if (this.modalEl) { this.modalEl.classList.add('hidden'); } this.isEditMode = false; this.currentAsset = null; this.onAfterClose(); } /** * 조회/수정 모드에 따른 UI 잠금 및 버튼 제어 */ protected setEditLockMode(mode: 'view' | 'edit' | 'add') { setEditLock(`${this.idPrefix}-asset-form`, mode, { saveBtnId: `btn-save-${this.idPrefix}-asset`, revertBtnId: `btn-revert-${this.idPrefix}-edit`, addLogBtnId: `btn-add-${this.idPrefix}-log` }); } // --- 추상 메서드: 자식 클래스에서 구현해야 함 --- protected abstract renderFrameHTML(): string; protected abstract initChildLogic(onSave: () => void, closeModals: () => void): void; protected abstract fillFormData(asset: any): void; protected abstract onAfterOpen(asset: any, mode: string): void; // --- 훅(Hook) 메서드: 필요 시 오버라이드 --- protected onAfterClose(): void {} } /** * --- 레거시 호환성을 위한 함수형 익스포트 --- * 기존 코드들이 참조하고 있는 함수들을 유지합니다. */ export function closeModals() { const modals = document.querySelectorAll('.modal-overlay'); modals.forEach(modal => modal.classList.add('hidden')); } export function initBaseModal() { // ESC 키로 모든 모달 닫기 (위치보기 팝업이 있으면 그것부터 닫음) window.addEventListener('keydown', (e) => { if (e.key === 'Escape') { const picker = document.querySelector('.image-picker-overlay'); if (picker) { picker.remove(); } else { closeModals(); } } }); return { closeAllModals: closeModals }; } export function openModal(modalId: string) { const modal = document.getElementById(modalId); if (modal) { modal.classList.remove('hidden'); } }