Improve drawing symbols and sample popup
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
1 parent
7efe9097ce
commit
a0ec74b09b
11 files changed
+108
-21
No files matched your search
@@ -54,7 +54,7 @@ export function createLandingGuide(root: HTMLElement): {close:(restoreFocus?:boo
|
||||
'종평면도·받침배치도·ELEVATION DWG가 들어 있는 폴더를 선택하고 자동 추출 시작을 누르세요. 진행 내용이 팝업에 표시됩니다.',
|
||||
'결과 목록의 교각 불러오기를 눌러 Excel 인식 결과를 확인하고 형상을 생성합니다. Excel 저장으로 파일만 받을 수도 있습니다.'],
|
||||
sample:['파일 준비 없이 실제 교각과 지반 모델을 살펴보려면 샘플 프로젝트를 선택하세요.',
|
||||
'오른쪽 고정 패널에서 산외교·운곡교 등 12개 교량의 이름과 교각 수를 확인하고 원하는 교량을 선택합니다.',
|
||||
'설계 라이브러리 팝업에서 산외교·운곡교 등 12개 교량의 이름과 교각 수를 확인하고 원하는 교량을 선택합니다.',
|
||||
`현재 ${sampleProjects.filter(s=>s.pierCount>0).length}개 교량은 교각과 원도면 지반면, 위성사진, 2D 지도를 함께 엽니다. 교각 데이터가 없는 승천천교는 지반·지도 자료를 표시합니다. 설계 지반고는 Excel 원래 값을 유지하며, 기존 검증 알림은 확인이 필요합니다.`]
|
||||
};
|
||||
const facts: Record<StartMode,string[]>={
|
||||
@@ -117,7 +117,7 @@ export function createLandingGuide(root: HTMLElement): {close:(restoreFocus?:boo
|
||||
dialog.querySelector('.landing-guide-prev')!.addEventListener('click',()=>{step=Math.max(0,step-1);render();if(step===0)dialog.querySelector<HTMLButtonElement>('[data-guide-step="0"]')!.focus();});
|
||||
dialog.querySelector('.landing-guide-next')!.addEventListener('click',()=>{
|
||||
if(step<2){step++;render();return;}
|
||||
close(false);if(mode==='sample'){root.querySelector<HTMLButtonElement>('.sample-load')?.focus();return;}root.querySelector<HTMLButtonElement>(`[data-card="${mode}"] button`)?.click();
|
||||
close(false);if(mode==='sample'){root.querySelector<HTMLButtonElement>('[data-card=sample] button')?.click();return;}root.querySelector<HTMLButtonElement>(`[data-card="${mode}"] button`)?.click();
|
||||
});
|
||||
// Main-app functions may hide this overlay without calling landing functions.
|
||||
new MutationObserver(()=>{if(root.hidden)close(false);}).observe(root,{attributes:true,attributeFilter:['hidden']});
|
||||
|
||||
+11
-1
@@ -5,6 +5,7 @@ import { createLandingGuide } from './landing-guide';
|
||||
import { createHeroMotion, heroMotionMarkup } from './landing-motion';
|
||||
import './landing-white.css';
|
||||
import './landing-typography.css';
|
||||
import './sample-library-popup.css';
|
||||
import { createDrawingPopup, drawingPopupMarkup } from './drawing-popup';
|
||||
|
||||
const appRoot = document.querySelector<HTMLDivElement>('.app');
|
||||
@@ -81,6 +82,10 @@ const landingPanelMarkup = `
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="landing-card" data-card="sample">
|
||||
<div><div class="landing-card-top"><div class="landing-card-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M3 20V8l6-4 6 4 6-4v16H3zM9 4v16M15 8v12"/></svg></div><span class="landing-card-number">03 / EXPLORE</span></div><span class="landing-card-eyebrow">Design Library</span><h3>기설계교량 라이브러리</h3><p>기존 설계 프로젝트의 교각·지형·지도를 살펴보고 불러옵니다.</p></div>
|
||||
<div class="landing-card-actions"><span class="landing-card-note">12개 설계 프로젝트</span><button class="landing-primary" type="button" aria-haspopup="dialog">설계 라이브러리 불러오기</button></div>
|
||||
</article>
|
||||
|
||||
</section>
|
||||
<footer class="landing-footer">
|
||||
@@ -89,7 +94,7 @@ const landingPanelMarkup = `
|
||||
</footer>
|
||||
</section>
|
||||
|
||||
${sampleExplorerMarkup}
|
||||
<dialog class="sample-library-popup" aria-label="기설계교량 라이브러리"><button type="button" class="sample-library-close" aria-label="설계 라이브러리 닫기">×</button>${sampleExplorerMarkup}</dialog>
|
||||
<section id="landing-excel-panel" class="landing-panel" aria-label="엑셀 입력 패널">
|
||||
<div class="landing-panel-head">
|
||||
<h3>엑셀 입력</h3>
|
||||
@@ -135,6 +140,7 @@ const closePanels = (): void => {
|
||||
};
|
||||
|
||||
const showApp = (): void => {
|
||||
landingOverlay.querySelector<HTMLDialogElement>('.sample-library-popup')?.close();
|
||||
landingGuide.close(false);
|
||||
if (appRoot) {
|
||||
appRoot.hidden = false;
|
||||
@@ -262,6 +268,10 @@ document.querySelectorAll<HTMLButtonElement>('[data-pick-file]').forEach((button
|
||||
|
||||
bindFilePicker(document.querySelector('[data-pick-file="excel"]') as HTMLButtonElement, 'landing-excel-input', 'landing-excel-name');
|
||||
|
||||
const libraryPopup=landingOverlay.querySelector<HTMLDialogElement>('.sample-library-popup')!;
|
||||
landingOverlay.querySelector<HTMLButtonElement>('[data-card="sample"] button')!.addEventListener('click',()=>{landingGuide.close(false);landingGuide.dismissHint();if(!libraryPopup.open)libraryPopup.showModal();});
|
||||
libraryPopup.querySelector('.sample-library-close')!.addEventListener('click',()=>libraryPopup.close());
|
||||
libraryPopup.addEventListener('close',()=>{requestAnimationFrame(()=>{landingOverlay.querySelectorAll<HTMLElement>('.landing-shell,.landing-hero').forEach(element=>element.scrollTo(0,0));landingOverlay.querySelector<HTMLButtonElement>('[data-card=sample] button')?.focus({preventScroll:true});});});
|
||||
createSampleExplorer(landingOverlay,(sample:Sample)=>loadSampleIntoApp(sample,showApp,showLanding));
|
||||
|
||||
if (homeButton) {
|
||||
|
||||
@@ -6,10 +6,10 @@ 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-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-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>
|
||||
<div class="sample-location"><div class="sample-location-heading"><strong>📍 <span class="sample-location-name"></span> 위치정보</strong><button type="button" class="sample-locate" title="선택 교량 위치 확대">지도에서 보기 ↗</button></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>
|
||||
|
||||
</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>`;
|
||||
@@ -38,7 +38,7 @@ export function createSampleExplorer(root:HTMLElement,load:(sample:Sample)=>Prom
|
||||
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);
|
||||
panel.querySelector('.sample-locate')!.addEventListener('click',()=>map!.focus(selected.sample,true));
|
||||
|
||||
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}기 · 실제 지반/지도 복원 완료`;}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
.landing-white .landing-shell{grid-template-columns:minmax(0,1fr)}
|
||||
.landing-white .landing-cards{grid-template-columns:repeat(3,minmax(0,1fr));width:min(1280px,92%)}
|
||||
.landing-white .landing-card h3{font-size:clamp(16px,1.25vw,21px)}
|
||||
.sample-library-popup{box-sizing:border-box;width:min(1240px,90vw);height:90dvh;max-width:94vw;max-height:94dvh;padding:0;border:1px solid #d6e5f1;border-radius:20px;background:#f8fbff;color:#14263d;overflow:hidden;box-shadow:0 30px 100px #14263d40}
|
||||
.sample-library-popup::backdrop{background:#14263d66;backdrop-filter:blur(6px)}
|
||||
.sample-library-close{position:absolute;right:22px;top:18px;z-index:10;width:38px;height:38px;border:1px solid #d6e5f1;border-radius:10px;background:white;color:#325670;font-size:24px;cursor:pointer}
|
||||
.sample-library-popup .sample-explorer{height:100%;min-height:0;padding:24px;gap:18px 24px;grid-template-columns:minmax(0,1fr) minmax(300px,.48fr);grid-template-rows:auto minmax(0,1fr) minmax(0,1fr);border:0;box-shadow:none;background:linear-gradient(140deg,#fff,#f2f8fe)}
|
||||
.sample-library-popup .sample-explorer-head{grid-column:1/-1;padding-right:62px;display:grid;grid-template-columns:minmax(0,1fr) minmax(280px,360px) auto;gap:20px;align-items:center}
|
||||
.sample-library-popup .sample-map-section{grid-column:1;grid-row:2}
|
||||
.sample-library-popup .sample-selection{grid-column:1;grid-row:3;overflow:hidden;display:grid;grid-template-rows:auto minmax(0,1fr);gap:0}
|
||||
.sample-library-popup .sample-list-section{grid-column:2;grid-row:2/4;min-height:0;border-top:0;border-left:1px solid #dce6f0;padding:0 0 0 24px;grid-template-rows:auto minmax(0,1fr) auto;gap:14px}
|
||||
.sample-library-popup .sample-list{max-height:none;grid-auto-rows:64px;gap:8px;grid-template-columns:minmax(0,1fr)}
|
||||
.sample-library-popup .sample-row{min-height:64px;padding:10px;gap:10px;grid-template-columns:28px minmax(0,1fr)}
|
||||
.sample-library-popup .sample-row-number{width:28px;height:30px;font-size:11px}
|
||||
.sample-library-popup .sample-row strong{font-size:14px}.sample-library-popup .sample-row small{font-size:11px;line-height:1.5}
|
||||
.sample-library-popup .sample-selection .sample-preview{height:100%;min-height:0;flex:initial}
|
||||
.sample-library-popup .sample-location{min-width:0;margin:0;padding:8px 10px;background:#eff6fc;border:0}.sample-library-popup .sample-location dl{margin:3px 0}.sample-library-popup .sample-coordinate-basis{display:none}
|
||||
.sample-library-popup .sample-section-title h3{font-size:15px}
|
||||
.sample-library-popup .sample-project-action{padding-top:14px;border-top:1px solid #dce6f0}
|
||||
.sample-library-popup .sample-project-action .sample-load{min-height:46px;font-size:14px;margin-top:12px}
|
||||
.sample-library-popup .sample-project-action .sample-selection-info h3{font-size:18px}
|
||||
@media(max-width:900px){.sample-library-popup{width:96vw;max-width:96vw;height:94dvh}.sample-library-popup .sample-explorer{padding:18px;gap:12px;grid-template-columns:minmax(0,1fr) minmax(0,1fr)}.sample-library-popup .sample-list-section{padding-left:12px}.sample-library-popup .sample-list{grid-template-columns:minmax(0,1fr)}.landing-white .landing-cards{gap:12px;width:95%}.landing-white .landing-card{padding:12px}}
|
||||
@media(max-width:600px){.sample-library-popup{overflow:auto}.sample-library-popup .sample-explorer{height:auto;display:flex;flex-direction:column}.sample-library-popup .sample-map-section{height:260px;flex-shrink:0}.sample-library-popup .sample-selection{min-height:280px}.sample-library-popup .sample-list-section{height:420px;flex-shrink:0;border-left:0;padding-left:0}.sample-library-popup .sample-list{grid-template-columns:repeat(2,minmax(0,1fr))}.landing-white .landing-cards{grid-template-columns:1fr}}
|
||||
|
||||
@media(max-width:1100px){.sample-library-popup .sample-explorer-head{grid-template-columns:minmax(0,1fr) minmax(240px,300px) auto;gap:12px}.sample-library-popup .sample-explorer{grid-template-columns:minmax(0,1fr) 280px;gap:14px}.sample-library-popup .sample-list-section{padding-left:14px}.sample-library-popup .sample-explorer-head h2{font-size:18px}}
|
||||
@media(max-width:700px){.sample-library-popup .sample-explorer-head{grid-template-columns:1fr auto}.sample-library-popup .sample-location{grid-column:1/-1;grid-row:2}.sample-library-popup .sample-explorer{grid-template-columns:minmax(0,1fr) 220px}}
|
||||
@media(max-width:600px){.sample-library-popup .sample-selection{height:260px;min-height:260px;flex-shrink:0}}
|
||||
|
||||
/* Location details sit beside the library title, directly above the left map. */
|
||||
@media(min-width:701px){
|
||||
.sample-library-popup .sample-explorer-head{position:relative;display:flex;min-height:112px;align-items:flex-start;padding-top:12px}
|
||||
.sample-library-popup .sample-explorer-head>div:first-child{max-width:310px}
|
||||
.sample-library-popup .sample-explorer-head>b{margin-left:auto}
|
||||
.sample-library-popup .sample-explorer-head .sample-location{position:absolute;left:29%;right:calc(32.5% + 24px);bottom:0;width:auto;padding:6px 9px}
|
||||
}
|
||||
|
||||
/* Compact location overlay stays inside the map without consuming a grid row. */
|
||||
.sample-library-popup .sample-explorer-head{min-height:0;align-items:center;padding-top:0}
|
||||
.sample-library-popup .sample-map .sample-location{position:absolute;top:10px;left:10px;right:auto;bottom:auto;z-index:5;width:min(340px,calc(100% - 60px));padding:8px 10px;margin:0;border:1px solid #d6e5f1;border-radius:8px;background:#ffffffed;box-shadow:0 3px 12px #14263d14;pointer-events:none;grid-column:auto;grid-row:auto}
|
||||
.sample-library-popup .sample-map .sample-location-heading{margin:0}
|
||||
.sample-library-popup .sample-map .sample-location p{margin:3px 0}
|
||||
|
||||
.sample-library-popup .sample-map .sample-location{width:max-content;max-width:calc(100% - 60px);padding:7px 10px;background:rgba(248,252,255,.88);border:1px solid rgba(187,209,226,.7);border-radius:8px;box-shadow:0 2px 9px #234a6814;backdrop-filter:blur(6px);text-shadow:none}
|
||||
.sample-library-popup .sample-map .sample-location dl{grid-template-columns:max-content minmax(0,1fr);column-gap:8px}
|
||||
Reference in new issue
Block a user