feat(sw): viewer layer bulb, popup toggle switch, footer layout and 1440px story inner
Deploy Storybook / deploy (push) Successful in 1m11s

- SW Viewer layer popup: search + 전체 레이어 fixed, only the list scrolls; the all-layers
  control is the SWLayerVisibilityIcon bulb (role="switch"), one light #d2c8b4 line under it.
- Example H and the layer popup AI docs follow the bulb layout (VIEWER, DESIGN_RULES,
  COMPONENT_CATALOG, COMPOSITION_PATTERNS, AI_GUIDE sync).
- Popup toggle switch (Figma toggle_btn_new 633:75188 / 633:75902): one sw.css face for the
  HM UI Switch in SWPopup and SWViewerSwitch in SWViewerPanel; SW Header switches unchanged.
- SW Footer stories: no inner scrollers, fluid screens; workspace fits the viewport.
- SW Footer category tabs keep their positions when selected (divider slots kept, pocket
  edges drawn over the neighbours' padding).
- Storybook .sb-page-inner: 1440px max from 1920px (was 80vw).
- SW Header equal-split change was cancelled; header CSS unchanged.

Verified: check:ai-docs PASS, viewer test 49 PASS (1 FAIL hover face + timeout pre-existing),
footer-design-review 21 PASS, browser checks of switch/footer/inner.
Not verified: footer-popup-viewer-refinement (needs full Storybook build), text-weight/hover
tests, Switch 37x16 AI docs (deferred).
This commit is contained in:
shkim4 committed 2026-10-22 20:20:00 +09:00
1 parent 258e0a4b68
commit c1549b2b4f
22 files changed
+121 -63

No files matched your search

+8 -5
View File
@@ -109,17 +109,20 @@ export function LayerPanel({ demo, ...placement }: { demo: ViewerDemo } & PanelP
const [query, setQuery] = useState("")
const filtered = LAYERS.filter(layer => layer.name.toLowerCase().includes(query.trim().toLowerCase()))
const set = (names: string[]) => demo.setHidden(new Set(names))
// 2026-10-22: one switch for all layers. ON only when every layer is shown; a partial state reads OFF, so a click turns all on.
// 2026-10-22: one bulb for all layers (was a switch). ON only when every layer is shown; a partial state reads OFF, so a click turns all on.
const allVisible = demo.hidden.size === 0
const setAll = (next: boolean) => { set(next ? [] : LAYERS.map(layer => layer.name)); demo.record(`layer:all=${next}`) }
return (
<SWViewerPanel title={`레이어 (${LAYERS.length}개)`} open={demo.panels.layers} onClose={() => demo.toggle("layers", false)} {...placement}>
<SWViewerSection surface="plain" aria-label="레이어 목록">
{/* 2026-10-22: search + 전체 레이어 stay fixed; only the layer list scrolls (.sw-viewer-story-layers). */}
<SWViewerSection surface="plain" aria-label="레이어 목록" className="sw-viewer-story-layers">
<div className="sw-viewer-story-toolbar">
<SWViewerInput type="search" placeholder="레이어 검색" aria-label="레이어 검색" value={query} onChange={event => setQuery(event.currentTarget.value)} />
{/* 2026-10-22: the section title (.hm-sw-viewer-section__title) names the switch row; the switch stays on that line. */}
{/* 2026-10-22: the section title (.hm-sw-viewer-section__title) names the row; the bulb (same button as the rows)
stays on that line and keeps the former switch semantics (role="switch", name 전체 레이어). */}
<SWViewerSection surface="plain" title="전체 레이어" className="sw-viewer-story-all">
<SWViewerSwitch aria-label="전체 레이어" checked={allVisible}
onCheckedChange={next => { set(next ? [] : LAYERS.map(layer => layer.name)); demo.record(`layer:all=${next}`) }} />
<button type="button" role="switch" aria-checked={allVisible} aria-label="전체 레이어" className="sw-viewer-story-bulb"
title={allVisible ? "전체 숨기기" : "전체 표시하기"} onClick={() => setAll(!allVisible)}><SWLayerVisibilityIcon on={allVisible} /></button>
</SWViewerSection>
</div>
{filtered.length === 0 ? <SWViewerHint>검색 결과가 없습니다 ("{query}")</SWViewerHint> : (