diff --git a/A00_Common/app_shell.ts b/A00_Common/app_shell.ts index 5ca816bc8..c086b2f8c 100644 --- a/A00_Common/app_shell.ts +++ b/A00_Common/app_shell.ts @@ -188,9 +188,15 @@ function buildDrawer(header: HTMLElement): HTMLElement[] { const brandRow = document.createElement("div"); brandRow.className = "app-drawer__group app-drawer__brand"; brandRow.append(buildBrand()); + // 홈페이지 메뉴 = 접는 묶음(기본 접힘 · 기억 안 함) — summary 는 단추가 아니라 눌러도 서랍이 안 닫힘 + const site = document.createElement("details"); + site.className = "app-drawer__group app-drawer__site"; + const siteTitle = document.createElement("summary"); + siteTitle.textContent = L("App_Nav_Site"); const navList = document.createElement("nav"); - navList.className = "app-drawer__group app-drawer__nav"; + navList.className = "app-drawer__nav"; navList.append(...buildNavButtons()); + site.append(siteTitle, navList); const prefs = document.createElement("div"); prefs.className = "app-drawer__row"; prefs.append( @@ -219,7 +225,7 @@ function buildDrawer(header: HTMLElement): HTMLElement[] { const settings = document.createElement("div"); settings.className = "app-drawer__group"; settings.append(prefs, auth, buildTouchModeSwitch()); - drawer.append(brandRow, steps, navList, settings); + drawer.append(brandRow, steps, site, settings); return [burger, scrim, drawer]; } @@ -458,10 +464,10 @@ html:is([data-burger="on"], .is-bm-route) .app-burger { display: inline-flex; } top: var(--app-header-h); right: 0; bottom: 0; - width: min(320px, 100vw); + width: min(240px, 100vw); padding: var(--spacing-16); overflow-y: auto; - background: var(--color-surface-raised); + background: var(--color-surface); border-left: 1px solid var(--color-border); box-shadow: var(--shadow-lg); } @@ -471,6 +477,9 @@ html:is([data-burger="on"], .is-bm-route) .app-burger { display: inline-flex; } gap: var(--spacing-4); } .app-drawer__nav > * { justify-content: flex-start; min-height: 44px; } +.app-drawer__site { display: block; } +.app-drawer__site > summary { min-height: 44px; display: flex; align-items: center; cursor: pointer; font-weight: 600; } +.app-drawer__site > nav { margin-top: var(--spacing-4); } .app-drawer__row { display: flex; flex-wrap: wrap; @@ -539,7 +548,7 @@ html.is-bm-route .app-header.is-drawer-open .app-drawer { left: 0; right: auto; border-left: 0; - border-right: 1px solid var(--color-border); + border-right: 2px solid var(--color-accent); } html.is-bm-route .app-drawer__brand { min-height: 44px; diff --git a/resources/tester/test_device_drawer.py b/resources/tester/test_device_drawer.py index c791d30d0..411b14690 100644 --- a/resources/tester/test_device_drawer.py +++ b/resources/tester/test_device_drawer.py @@ -77,10 +77,10 @@ def test_drawer_holds_steps_and_header_folds(): ) groups = page.evaluate(GROUPS) order = [ - next((k for k in ("brand", "steps", "nav", "touch") if k in c), "settings") + next((k for k in ("brand", "steps", "site", "touch") if k in c), "settings") for c in groups ] - assert order == ["brand", "steps", "nav", "settings"], groups + assert order == ["brand", "steps", "site", "settings"], groups assert page.evaluate( "document.querySelector('.app-drawer__steps').firstElementChild === window.__steps" ) @@ -88,6 +88,28 @@ def test_drawer_holds_steps_and_header_folds(): "document.querySelector('.app-drawer__steps .ui-workflow-layout__step.is-active')?.textContent" ) assert active and "상세" in active, active + # 378-1: 홈페이지 메뉴 = 접힘 기본 · summary 누름 = 펼침 + 서랍 열린 채 · 서랍 폭 < 패널 · 바탕 ≠ 패널 + site = "document.querySelector('.app-drawer__site')" + assert page.evaluate(f"!{site}.open") + page.evaluate(f"{site}.querySelector('summary').click()") + assert page.evaluate(f"{site}.open") + assert page.evaluate( + "document.querySelector('.app-header').classList.contains('is-drawer-open')" + ) + page.evaluate(f"{site}.querySelector('summary').click()") + assert page.evaluate(f"!{site}.open") + widths = page.evaluate( + "[document.querySelector('.app-drawer')," + " document.querySelector('.ui-workflow-overlay__panel--title')]" + ".map(e => Math.round(e.getBoundingClientRect().width))" + ) + assert widths[0] <= 240 and widths[0] < widths[1], widths + bgs = page.evaluate( + "[document.querySelector('.app-drawer')," + " document.querySelector('.ui-workflow-overlay__panel--title')]" + ".map(e => getComputedStyle(e).backgroundColor)" + ) + assert bgs[0] != bgs[1], bgs page.evaluate("document.querySelector('.app-drawer__scrim').click()") # 닫기 = 제자리 assert page.evaluate( "window.__steps.closest('.ui-workflow-overlay__panel--progress') !== null" @@ -149,6 +171,8 @@ def test_352_4_burger_in_panel_title_row_text(): base = shell.split("html.is-bm-route .app-burger {", 1)[1].split("}", 1)[0] assert "position: fixed;" in base and "left: var(--spacing-8);" in base assert "html.is-bm-route .app-drawer__brand .app-brand { align-self: flex-end; }" in shell + # 378-1: 프로젝트 화면 서랍 오른쪽 가장자리 = 강조색 2 px + assert "border-right: 2px solid var(--color-accent);" in shell overlay = (root / "ui_template/ui_template_overlay.css").read_text(encoding="utf-8") project = overlay.split(".ui-workflow-overlay__project {", 1)[1].split("}", 1)[0] assert "margin-left: auto;" in project diff --git a/ui_template/ui_template_locale_common.ts b/ui_template/ui_template_locale_common.ts index b7eb7c462..41d1c8620 100644 --- a/ui_template/ui_template_locale_common.ts +++ b/ui_template/ui_template_locale_common.ts @@ -182,4 +182,5 @@ export const ui_locales_common = { "© 2026 임도설계 및 견적 자동화. All rights reserved.", "© 2026 Forest Road Design & Estimation. All rights reserved.", ], + App_Nav_Site: ["홈페이지", "Website"], } as const satisfies Record;