feat(sw): viewer layer popup bulb icon and all-layers switch
User-approved 2026-10-22 change to the SW Viewer layer popup.
- New public export SWLayerVisibilityIcon({ on }) + SWLayerVisibilityIconProps:
the SW Header layer bulb art reused as is (header design and behavior unchanged).
- Layer rows: eye (Eye / EyeOff) icon replaced by the bulb, 24 x 24 button kept.
- All layers: the 전체 켜기 / 전체 끄기 buttons replaced by one SWViewerField +
SWViewerSwitch on top. ON only when every layer is shown; a partial state reads
OFF and a click shows all. Derived from the hidden-layer state, so it stays synced.
- AI docs (VIEWER.md 4.1, catalog, design rules, composition, AI_GUIDE sync) and
example H follow the new rule, replacing the 2026-10-21 eye-icon rule.
- viewer.browser.mjs: layer popup checks moved to the bulb / switch behavior.
This commit is contained in:
1 parent
1dd8fb3aad
commit
8cb98b373f
16 files changed
+79
-52
No files matched your search
@@ -3,7 +3,7 @@
|
||||
// open / close / focus / keyboard / drag behavior. Icons are the program's own (16px, currentColor).
|
||||
import { useState } from "react"
|
||||
import {
|
||||
SWViewerBadge, SWViewerButton, SWViewerField, SWViewerHeader, SWViewerHeaderButton, SWViewerHint, SWViewerInput, SWViewerPanel,
|
||||
SWLayerVisibilityIcon, SWViewerBadge, SWViewerButton, SWViewerField, SWViewerHeader, SWViewerHeaderButton, SWViewerHint, SWViewerInput, SWViewerPanel,
|
||||
SWViewerSection, SWViewerSlider, SWViewerStatus, SWViewerSwitch,
|
||||
type SWViewerHeaderGroup,
|
||||
} from "@hm-design/sw"
|
||||
@@ -17,9 +17,6 @@ const icons = {
|
||||
layers: <Icon d="M12 3l9 5-9 5-9-5 9-5zM3 13l9 5 9-5" />,
|
||||
options: <Icon d="M4 6h10M18 6h2M4 12h4M12 12h8M4 18h12M20 18h0" />,
|
||||
info: <Icon d="M12 8h.01M11 12h1v5h1M12 3a9 9 0 1 0 0 18 9 9 0 0 0 0-18z" />,
|
||||
// Layer visibility: eye (shown) / eye with a slash (hidden). Never a switch or checkbox (VIEWER.md §4.1).
|
||||
eye: <Icon d="M2.06 12.35a1 1 0 0 1 0-.7 10.75 10.75 0 0 1 19.88 0 1 1 0 0 1 0 .7 10.75 10.75 0 0 1-19.88 0M12 9a3 3 0 1 0 0 6 3 3 0 0 0 0-6z" />,
|
||||
eyeOff: <Icon d="M2.06 12.35a1 1 0 0 1 0-.7 10.75 10.75 0 0 1 19.88 0 1 1 0 0 1 0 .7 10.75 10.75 0 0 1-19.88 0M12 9a3 3 0 1 0 0 6 3 3 0 0 0 0-6zM3 3l18 18" />,
|
||||
}
|
||||
const layers = [{ name: "0", color: "#ffffff", count: 120 }, { name: "WALL", color: "#ff3b30", count: 42 }, { name: "DIM", color: "#34c759", count: 18 }]
|
||||
|
||||
@@ -31,7 +28,7 @@ export function ExampleHViewerMode({ onEvent }: { onEvent?: (name: string) => vo
|
||||
const [grid, setGrid] = useState(true)
|
||||
const [zoomSpeed, setZoomSpeed] = useState(1)
|
||||
const [about, setAbout] = useState(false)
|
||||
// Program-owned visibility per layer; each eye button toggles only its own layer.
|
||||
// Program-owned visibility per layer; each bulb button toggles only its own layer.
|
||||
const [hiddenLayers, setHiddenLayers] = useState<ReadonlySet<string>>(new Set())
|
||||
const toggleLayer = (name: string) => {
|
||||
const next = new Set(hiddenLayers), visible = !next.has(name)
|
||||
@@ -39,6 +36,12 @@ export function ExampleHViewerMode({ onEvent }: { onEvent?: (name: string) => vo
|
||||
setHiddenLayers(next)
|
||||
onEvent?.(`layer:${name}=${!visible}`)
|
||||
}
|
||||
// All-layers switch: ON only when every layer is shown. A partial state reads OFF, so turning it on shows all.
|
||||
const allVisible = hiddenLayers.size === 0
|
||||
const setAllLayers = (visible: boolean) => {
|
||||
setHiddenLayers(new Set(visible ? [] : layers.map(layer => layer.name)))
|
||||
onEvent?.(`layer:all=${visible}`)
|
||||
}
|
||||
|
||||
// File open: the header button after the logo (icon + label, icon only when the header is narrow). File name click:
|
||||
// the sample list only (no Open / + New).
|
||||
@@ -73,7 +76,8 @@ export function ExampleHViewerMode({ onEvent }: { onEvent?: (name: string) => vo
|
||||
|
||||
<SWViewerPanel title="레이어" open={panels.layers} onClose={() => setPanels({ ...panels, layers: false })}>
|
||||
<SWViewerSection surface="plain" aria-label="레이어 목록">
|
||||
{/* Layer list: swatch · name · count · eye button. Visibility is the eye icon button, not SWViewerSwitch. */}
|
||||
{/* Top: all-layers SWViewerSwitch. Rows: swatch · name · count · bulb button (SWLayerVisibilityIcon), not a switch. */}
|
||||
<SWViewerField label="전체 레이어">{id => <SWViewerSwitch id={id} checked={allVisible} onCheckedChange={setAllLayers} />}</SWViewerField>
|
||||
<ul className="app-viewer-layers">
|
||||
{layers.map(layer => {
|
||||
const visible = !hiddenLayers.has(layer.name)
|
||||
@@ -82,9 +86,9 @@ export function ExampleHViewerMode({ onEvent }: { onEvent?: (name: string) => vo
|
||||
<span className="app-viewer-layer-swatch" style={{ background: layer.color }} aria-hidden="true" />
|
||||
<span className="app-viewer-layer-name" title={layer.name}>{layer.name}</span>
|
||||
<SWViewerBadge>{layer.count}</SWViewerBadge>
|
||||
<button type="button" className="app-viewer-layer-eye"
|
||||
<button type="button" className="app-viewer-layer-bulb"
|
||||
aria-label={`${layer.name} ${visible ? "숨기기" : "표시하기"}`} title={visible ? "숨기기" : "표시하기"}
|
||||
onClick={() => toggleLayer(layer.name)}>{visible ? icons.eye : icons.eyeOff}</button>
|
||||
onClick={() => toggleLayer(layer.name)}><SWLayerVisibilityIcon on={visible} /></button>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
|
||||
Reference in new issue
Block a user