// Example H — viewer mode: SWViewerHeader (data-driven menu) + SWViewerPanel popups with free content.
// Only public components. The program owns the state and the commands; the package owns the look and the
// open / close / focus / keyboard / drag behavior. Icons are the program's own (16px, currentColor).
import { useState } from "react"
import {
SWLayerVisibilityIcon, SWViewerBadge, SWViewerButton, SWViewerField, SWViewerHeader, SWViewerHeaderButton, SWViewerHint, SWViewerInput, SWViewerPanel,
SWViewerSection, SWViewerSlider, SWViewerStatus, SWViewerSwitch,
type SWViewerHeaderGroup,
} from "@hm-design/sw"
import "./example-layout.css"
const Icon = ({ d }: { d: string }) => (
)
const icons = {
fit: ,
layers: ,
options: ,
info: ,
}
const layers = [{ name: "0", color: "#ffffff", count: 120 }, { name: "WALL", color: "#ff3b30", count: 42 }, { name: "DIM", color: "#34c759", count: 18 }]
export function ExampleHViewerMode({ onEvent }: { onEvent?: (name: string) => void }) {
const [fileName, setFileName] = useState(null)
const [space, setSpace] = useState("model")
const [depth, setDepth] = useState(false)
const [panels, setPanels] = useState({ layers: false, options: false })
const [grid, setGrid] = useState(true)
const [zoomSpeed, setZoomSpeed] = useState(1)
const [about, setAbout] = useState(false)
// Program-owned visibility per layer; each bulb button toggles only its own layer.
const [hiddenLayers, setHiddenLayers] = useState>(new Set())
const toggleLayer = (name: string) => {
const next = new Set(hiddenLayers), visible = !next.has(name)
if (visible) next.add(name); else next.delete(name)
setHiddenLayers(next)
onEvent?.(`layer:${name}=${!visible}`)
}
// All-layers bulb: ON only when every layer is shown. A partial state reads OFF, so turning it on shows all.
const allVisible = hiddenLayers.size === 0
const [query, setQuery] = useState("")
const shownLayers = layers.filter(layer => layer.name.toLowerCase().includes(query.trim().toLowerCase()))
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).
const openFile = () => onEvent?.("file:open")
const samples = [{ id: "sample-a", name: "BasicSample.dwg", title: "기본 도면 · DWG" }, { id: "sample-b", name: "simple.dxf", title: "기본 도형 · DXF" }]
const groups: SWViewerHeaderGroup[] = [
{ id: "space", label: "도면 공간", surface: "field", hidden: !fileName, items: [
{ type: "tabs", id: "space", label: "모델과 배치 선택", collapseBelow: 1180, value: space, onValueChange: setSpace,
options: [{ value: "model", label: "Model", count: 1200 }, { value: "layout", label: "Layout", count: 40 }] },
] },
{ id: "view", label: "화면 보기", surface: "field", items: [
// Measured steps: full while it fits, icon only next, then into More (the numbers only give the order).
{ type: "button", id: "fit", label: "전체 보기", icon: icons.fit, iconOnlyBelow: 560, collapseBelow: 1180, disabled: !fileName },
{ type: "switch", id: "depth", label: "2.5D", checked: depth, collapseBelow: 1180, onCheckedChange: setDepth },
] },
{ id: "panels", label: "패널", placement: "end", items: [
{ type: "toggle", id: "layers", label: "레이어", icon: icons.layers, pressed: panels.layers, iconOnlyBelow: 900, onPressedChange: layers => setPanels({ ...panels, layers }) },
{ type: "toggle", id: "options", label: "설정", icon: icons.options, pressed: panels.options, iconOnlyBelow: 900, onPressedChange: options => setPanels({ ...panels, options }) },
] },
]
return (
<>
{/* No logo prop: nothing is drawn on the cap (no "HM" text). Pass logoSrc / logo to show the program's logo. */}
sample.name === fileName)?.id} onDocumentSelect={sample => setFileName(sample.name)} onDocumentOpen={openFile} documentOpenLabel="파일 열기"
onCommand={command => onEvent?.(`${command.itemId}/${command.id}${command.value === undefined ? "" : `=${String(command.value)}`}`)}
trailing={<>
setAbout(true)} />
>} />
setPanels({ ...panels, layers: false })}>
{/* Fixed: search, then the all-layers bulb (same button + SWLayerVisibilityIcon as the rows, role="switch").
Only the list below scrolls. Rows: swatch · name · count · bulb button, never a switch. */}
setQuery(event.currentTarget.value)} />
{shownLayers.length === 0 ? 검색 결과가 없습니다 ("{query}") :
{shownLayers.map(layer => {
const visible = !hiddenLayers.has(layer.name)
return (
-
{layer.name}
{layer.count}
)
})}
}
setPanels({ ...panels, options: false })} width={340} height="auto" inset={{ side: 356 }}>
{id => }
}>
{id => setZoomSpeed(Number(event.currentTarget.value))} />}
setAbout(false)} width={360} height="auto" resizable={false} collapsible={false}
footer={ setAbout(false)}>확인}>
모듈 구성과 라이선스 고지는 프로그램이 제공합니다.
>
)
}