Files
Pier_v1/도면작성/sheet-editor.ts
T
2026-11-04 13:34:51 +09:00

41 lines
5.7 KiB
TypeScript

import { defaultSheet, FIELD_LABELS, parseSheetConfig, REFERENCE_TEMPLATE } from './template';
import type { SheetConfig } from './types';
export function createSheetEditor(host:HTMLElement,apply:(config:SheetConfig)=>void):{open:(value:SheetConfig)=>void}{
const dialog=window.document.createElement('dialog');dialog.className='ga-sheet-dialog';dialog.setAttribute('aria-label','도곽 및 노트 편집');
dialog.innerHTML=`<header><div><strong>원본 도곽 · NOTE 편집</strong><p id="ga-template-name"></p></div><button type="button" id="ga-editor-close">닫기</button></header>
<p class="ga-editor-intro">첨부 DWG의 CXBLKA1 도곽·표제란과 NOTE 원문입니다. 강도·시공 조건은 현 프로젝트에 맞게 검토하여 수정하세요. {{project}}, {{bridge}}, {{pier}}, {{scale}}, {{paper}}는 현재 도면 값으로 치환됩니다.</p>
<div class="ga-editor-tools"><label><input type="checkbox" id="ga-frame-show"/> 도곽 표시</label><label><input type="checkbox" id="ga-logo-show"/> 원본 로고 표시</label><button type="button" id="ga-config-import">설정/도곽 JSON 불러오기</button><button type="button" id="ga-config-export">설정/도곽 JSON 저장</button><button type="button" id="ga-config-reset">원본 복원</button><input type="file" accept=".json,application/json" id="ga-config-file" hidden/></div>
<div class="ga-editor-columns"><section><h3>표제란 속성 · 원본 위치/글자 크기 유지</h3><div id="ga-frame-fields"></div></section><section><h3>NOTE 원문</h3><label>문자 높이 (A1 기준 mm)<input id="ga-note-height" type="number" min="1.5" max="6" step=".1"/></label><textarea id="ga-note-text" rows="22" aria-label="노트 내용"></textarea><h3>REFERENCE</h3><textarea id="ga-reference-text" rows="5" aria-label="참조 도면 내용"></textarea><div class="ga-setting-pair"><label>키플랜 시점 표기<input id="ga-start-label"/></label><label>키플랜 종점 표기<input id="ga-end-label"/></label></div></section></div>
<footer><span id="ga-editor-status" role="status">설정은 프로젝트별로 이 브라우저에 보관합니다. JSON으로 내보내 다른 환경에서 재사용할 수 있습니다.</span><button type="button" class="ga-primary" id="ga-config-apply">적용 · 도면 갱신</button></footer>`;
host.append(dialog);
const node=<T extends HTMLElement=HTMLElement>(id:string)=>dialog.querySelector<T>(`#${id}`)!;
let working=defaultSheet();
const populate=(config:SheetConfig)=>{
working=structuredClone(config);const template=working.template??REFERENCE_TEMPLATE;
node('ga-template-name').textContent=`${template.name} · ${template.source}`;
node<HTMLInputElement>('ga-frame-show').checked=working.showFrame;node<HTMLInputElement>('ga-logo-show').checked=working.showLogo;
node<HTMLInputElement>('ga-note-height').value=String(working.noteHeight);node<HTMLTextAreaElement>('ga-note-text').value=working.notes;node<HTMLTextAreaElement>('ga-reference-text').value=working.references;
node<HTMLInputElement>('ga-start-label').value=working.startLabel;node<HTMLInputElement>('ga-end-label').value=working.endLabel;
const fields=node('ga-frame-fields');fields.replaceChildren();
for(const field of template.fields){const row=window.document.createElement('label'),label=window.document.createElement('span'),input=window.document.createElement('input');
label.textContent=FIELD_LABELS[field.tag]??field.label??field.tag;input.value=working.fields[field.tag]??field.value;input.dataset.tag=field.tag;input.maxLength=500;input.setAttribute('aria-label',`${label.textContent} (${field.tag})`);
const small=window.document.createElement('small');small.textContent=field.tag;row.append(label,input,small);fields.append(row);
}
};
const collect=():SheetConfig=>{
const fields={...working.fields};for(const input of node('ga-frame-fields').querySelectorAll<HTMLInputElement>('[data-tag]'))fields[input.dataset.tag!]=input.value;
return parseSheetConfig({...working,fields,showFrame:node<HTMLInputElement>('ga-frame-show').checked,showLogo:node<HTMLInputElement>('ga-logo-show').checked,noteHeight:Number(node<HTMLInputElement>('ga-note-height').value),notes:node<HTMLTextAreaElement>('ga-note-text').value,references:node<HTMLTextAreaElement>('ga-reference-text').value,startLabel:node<HTMLInputElement>('ga-start-label').value,endLabel:node<HTMLInputElement>('ga-end-label').value});
};
const status=(s:string)=>node('ga-editor-status').textContent=s;
node('ga-editor-close').addEventListener('click',()=>dialog.close());node('ga-config-reset').addEventListener('click',()=>populate(defaultSheet()));
node('ga-config-apply').addEventListener('click',()=>{try{apply(collect());dialog.close();}catch(error){status((error as Error).message);}});
node('ga-config-import').addEventListener('click',()=>node<HTMLInputElement>('ga-config-file').click());
node<HTMLInputElement>('ga-config-file').addEventListener('change',async event=>{
const file=(event.target as HTMLInputElement).files?.[0];if(!file)return;
try{if(file.size>1500000)throw new Error('설정 파일은 1.5MB 이하만 지원합니다.');populate(parseSheetConfig(JSON.parse(await file.text())));status('불러왔습니다. 적용 버튼으로 현재 도면에 반영하세요.');}catch(error){status((error as Error).message);}(event.target as HTMLInputElement).value='';
});
node('ga-config-export').addEventListener('click',()=>{try{const c=collect(),url=URL.createObjectURL(new Blob([JSON.stringify({...c,template:c.template??REFERENCE_TEMPLATE},null,2)],{type:'application/json'})),link=window.document.createElement('a');link.href=url;link.download='Pierzainer_도곽_노트.json';link.click();setTimeout(()=>URL.revokeObjectURL(url),1000);}catch(error){status((error as Error).message);}});
return{open:value=>{populate(value);dialog.showModal();}};
}