49 lines
7.1 KiB
TypeScript
49 lines
7.1 KiB
TypeScript
import { sampleProjects } from './sample-projects';
|
||
import { sampleLocations } from './sample-locations';
|
||
import { renderSamplePreview } from './sample-preview';
|
||
import { createSampleMap } from './sample-map';
|
||
import './sample-explorer.css';
|
||
export type Sample=typeof sampleProjects[number];
|
||
export const sampleExplorerMarkup=`<aside class="sample-explorer" aria-label="기설계 교량" data-guide-target="sample">
|
||
<header class="sample-explorer-head"><div><span>EXPLORE PROJECTS</span><h2>교각·교대 설계 라이브러리</h2></div><b>12<span> PROJECTS</span></b></header>
|
||
<section class="sample-map-section"><div class="sample-section-title"><h3>설계 프로젝트 위치</h3><div class="sample-map-modes" role="group" aria-label="지도 종류"><button type="button" data-map-mode="street" aria-pressed="true">일반</button><button type="button" data-map-mode="satellite" aria-pressed="false">위성</button></div></div><div class="sample-map" aria-label="대한민국 기설계 교량 지도"><div class="sample-location"><div class="sample-location-heading"><strong>📍 <span class="sample-location-name"></span> 위치정보</strong></div><p class="sample-address"></p><dl><dt>좌표계</dt><dd class="sample-crs"></dd><dt>조사좌표</dt><dd class="sample-survey"></dd><dt>위도·경도</dt><dd class="sample-wgs84"></dd></dl><small class="sample-coordinate-basis"></small></div><div class="sample-map-tiles"></div><div class="sample-map-markers"></div><div class="sample-map-zoom"><button type="button" data-zoom="1" aria-label="지도 확대">+</button><button type="button" data-zoom="-1" aria-label="지도 축소">−</button><button type="button" data-zoom="0" aria-label="남한 중심 기본 지도">⌂</button></div><span class="sample-map-attribution"></span><span class="sample-map-error" hidden>배경지도 연결 대기 · 좌표 마커 표시</span></div></section>
|
||
<section class="sample-selection" aria-label="선택 교량 상세"><div class="sample-section-title"><h3>하부구조 3D 미리보기</h3><span class="sample-selection-index"></span></div>
|
||
<div class="sample-preview" role="region" aria-label="지도·지반·교각 통합 3D 미리보기"></div>
|
||
|
||
</section>
|
||
<section class="sample-list-section"><div class="sample-section-title"><h3>기존 설계 프로젝트</h3><span>총 12개 프로젝트</span></div><div class="sample-list" role="group" aria-label="교량 선택"></div><div class="sample-project-action"><div class="sample-selection-info"><h3></h3><p></p></div><p id="landing-sample-status" role="status" aria-live="polite"></p><button type="button" class="sample-load">설계 프로젝트 불러오기 ↗</button></div></section>
|
||
</aside>`;
|
||
export function createSampleExplorer(root:HTMLElement,load:(sample:Sample)=>Promise<void>):void{
|
||
const panel=root.querySelector<HTMLElement>('.sample-explorer')!,list=panel.querySelector<HTMLElement>('.sample-list')!,status=panel.querySelector<HTMLElement>('#landing-sample-status')!,button=panel.querySelector<HTMLButtonElement>('.sample-load')!;
|
||
let selected:Sample=sampleProjects[0],busy=false,previewToken=0;
|
||
let map:ReturnType<typeof createSampleMap>|undefined;
|
||
const select=(sample:Sample,focus=true)=>{
|
||
if(busy)return;selected=sample;panel.dataset.selected=String(sample.sample);button.dataset.sample=String(sample.sample);status.textContent=sample.pierCount?'':'원본 프로젝트에 교각 형상이 없습니다. 실제 지반·지도 자료를 엽니다.';
|
||
button.disabled=false;button.textContent=sample.pierCount?'설계 프로젝트 불러오기 ↗':'설계 프로젝트 불러오기 ↗';
|
||
panel.querySelectorAll<HTMLElement>('[data-sample-row]').forEach(row=>{const active=Number(row.dataset.sampleRow)===sample.sample;row.classList.toggle('is-selected',active);row.setAttribute('aria-pressed',String(active));});
|
||
panel.querySelector('.sample-selection-info h3')!.textContent=sample.bridge;
|
||
panel.querySelector('.sample-selection-info p')!.textContent=`${sampleLocations.find(p=>p.sample===sample.sample)?.types??'형식 미확인'} · 원본 교각 ${sample.pierCount}기`;
|
||
panel.querySelector('.sample-selection-index')!.textContent=`PROJECT ${String(sample.sample).padStart(2,'0')}`;
|
||
const loc=sampleLocations.find(p=>p.sample===sample.sample);
|
||
panel.querySelector('.sample-location-name')!.textContent='프로젝트';const address=panel.querySelector<HTMLElement>('.sample-address')!;address.textContent=loc?.address??'주소 확인 불가';address.title='ArcGIS 역지오코딩 · 지도 좌표 기준 시·도 / 시·군 · 상세주소 제외';
|
||
panel.querySelector('.sample-crs')!.textContent=loc?`${loc.crs}${loc.crsAssumed?' · 원본 CRS 미확정':''}`:'좌표계 미확인';
|
||
panel.querySelector('.sample-survey')!.textContent=loc?`X(N) ${loc.surveyX.toFixed(3)} / Y(E) ${loc.surveyY.toFixed(3)} m`:'좌표 확인 불가';
|
||
panel.querySelector('.sample-wgs84')!.textContent=loc?`${loc.lat.toFixed(6)}° N / ${loc.lon.toFixed(6)}° E`:'변환 불가';
|
||
panel.querySelector('.sample-coordinate-basis')!.textContent=loc?`${loc.basis} · 프로그램 x=E/y=N`:'대표 좌표 없음';
|
||
const token=++previewToken;void renderSamplePreview(panel.querySelector('.sample-preview')!,sample,()=>token===previewToken);
|
||
map?.highlight();if(focus)map?.focus(sample.sample);
|
||
};
|
||
for(const sample of sampleProjects){const row=document.createElement('button');row.type='button';row.dataset.sampleRow=String(sample.sample);row.className='sample-row';
|
||
row.innerHTML=`<span class="sample-row-number">${String(sample.sample).padStart(2,'0')}</span><span><strong>${sample.bridge}</strong><small>${sampleLocations.find(p=>p.sample===sample.sample)?.types??'미확인'} · 교각 ${sample.pierCount}기</small></span><span class="sample-row-arrow">↗</span>`;
|
||
row.addEventListener('click',()=>select(sample));for(const event of ['pointerenter','focus'])row.addEventListener(event,()=>{row.classList.add('is-hovered');map?.hover(sample.sample);});for(const event of ['pointerleave','blur'])row.addEventListener(event,()=>{row.classList.remove('is-hovered');map?.hover(null);});list.append(row);
|
||
}
|
||
map=createSampleMap(panel,id=>{const sample=sampleProjects.find(s=>s.sample===id);if(sample){select(sample);list.querySelector(`[data-sample-row="${id}"]`)?.scrollIntoView({block:'nearest'});}},()=>selected.sample,()=>busy);
|
||
|
||
button.addEventListener('click',async()=>{
|
||
if(busy)return;const target=selected;busy=true;button.disabled=true;button.textContent=`${target.bridge} 불러오는 중…`;list.querySelectorAll('button').forEach(b=>b.disabled=true);map!.refresh();status.textContent=`${target.bridge}: 실제 프로젝트 파일을 여는 중…`;
|
||
try{await load(target);status.textContent=`${target.bridge}: 교각 ${target.pierCount}기 · 실제 지반/지도 복원 완료`;}
|
||
catch(error){status.textContent=`${target.bridge}: ${(error as Error).message}`;panel.dataset.loadError=String(target.sample);}
|
||
finally{busy=false;button.disabled=false;button.textContent=target.pierCount?'설계 프로젝트 불러오기 ↗':'설계 프로젝트 불러오기 ↗';list.querySelectorAll('button').forEach(b=>b.disabled=false);map!.refresh();}
|
||
});select(selected,false);
|
||
}
|