Files
HMDesign/packages/sw/ai/examples/ExampleE-CustomPopup.tsx
shkim4 454472d344 docs(sw): AI rules, example E, tests and evidence for the popup / footer / header changes
AI documents (packages/sw/ai)
- SWPopupPropertyRow, SWSelect, SWSlider in the catalog, usage table,
  composition patterns and design rules; SWViewerSlider noted as SWSlider panel.
- Rule: the SWHeader layer / color dropdowns are header-only; popup layer /
  color / line style use SWPopupPropertyRow (property window pickers).
- Footer rules: gra-border-orange surfaces, Model / Layout text unchanged on
  hover, side button hover, vacant surface limited to the slot row.
- Header rules: measured More growing behavior (hysteresis fix A).
- Example E (custom popup) uses SWPopupPropertyRow, SWSelect and SWSlider
  instead of SWHeaderLayerMenu in the popup.

Tests (packages/sw/tests)
- New footer-popup-viewer-refinement.browser.mjs (47 checks: footer stories,
  hover / gradient / tab text / pocket, popup pickers vs property window rows,
  SWSlider, unit SWSelect parity, Popup B, header trace, viewer title art).
- integrated-popup / integrated-consumer: popup pickers and SWSelect instead of
  the header dropdown and the HM UI Select (same overlay / callback / Escape
  requirements).
- integrated-improvement: default sections skip the retired SW/Tool story
  ('tool' still runs by name); storybook-inventory / simplification record the
  retired footer IDs; theme-inheritance widths use sw-footer--full-footer.

Docs (docs/sw)
- sw-footer-popup-viewer-refinement-2026-10-20.md: causes, decisions (the
  first-pass "header dropdown in popup" change is marked withdrawn),
  verification and baseline failures; evidence footer-popup-viewer-refinement-qa
  (47/47). Footer mapping document updated for the removed sections.

Baseline failures (unchanged by this work): home-menu-integration 1,
storybook-unused-removal 5 (10-19 snapshot comparisons), range-cross-browser and
property-panel-comparison open stories retired on 2026-10-19.
2026-10-20 15:20:27 +09:00

54 lines
3.4 KiB
TypeScript

// Example E — the SW popup frame with project content (no Header). SWPopup keeps the property window design;
// children are free: SWPopupSection / SWPopupRow rows with SWHeaderInput, the unit SWSelect, the SWSlider gauge and the
// popup layer / color pickers (SWPopupPropertyRow). The SWHeader field dropdowns are header-only: never in a popup.
import { useId, useState } from "react"
import { SWHeaderInput, SWPopup, SWPopupPropertyRow, SWPopupRow, SWPopupSection, SWSelect, SWSlider, SWTheme } from "@hm-design/sw"
import { colors, layers } from "./sample-data"
export function ExampleECustomPopup({ onEvent }: { onEvent?: (name: string) => void }) {
const [open, setOpen] = useState(true)
const nameId = useId()
const [name, setName] = useState("평면도-01")
const [scale, setScale] = useState("100")
const [x, setX] = useState("1200.5")
const unitId = useId()
const [unit, setUnit] = useState("mm")
const [opacity, setOpacity] = useState(60)
const [activeLayerId, setActiveLayerId] = useState("0")
const [color, setColor] = useState("255,0,0")
if (!open) return <button type="button" onClick={() => setOpen(true)}>설정 열기</button>
return (
<SWTheme>
<SWPopup title="도면 설정" onClose={() => { setOpen(false); onEvent?.("popup:close") }}>
<SWPopupSection title="도면">
<SWPopupRow label="이름" htmlFor={nameId}>
<SWHeaderInput id={nameId} value={name} onCommit={next => { setName(next); onEvent?.(`name:${next}`) }} />
</SWPopupRow>
<SWPopupRow label="축척" unit="%" numeric>
<SWHeaderInput aria-label="축척" valueType="number" value={scale} onCommit={next => { setScale(next); onEvent?.(`scale:${next}`) }} />
</SWPopupRow>
<SWPopupRow label="단위" htmlFor={unitId} field="none">
<SWSelect id={unitId} value={unit} options={[{ value: "mm" }, { value: "cm" }, { value: "m" }]} onValueChange={next => { setUnit(next); onEvent?.(`unit:${next}`) }} />
</SWPopupRow>
<SWPopupRow label="투명도" field="none" unit="%">
<SWSlider aria-label="투명도" value={opacity} step={10} onValueChange={setOpacity} onValueCommit={next => onEvent?.(`opacity:${next}`)} />
</SWPopupRow>
</SWPopupSection>
<SWPopupSection title="객체 정보">
<SWPopupRow label="타입" readOnly><SWHeaderInput aria-label="타입" value="Line" readOnly /></SWPopupRow>
<SWPopupRow label="X" unit="mm" numeric>
<SWHeaderInput aria-label="X" valueType="number" value={x} onCommit={next => { setX(next); onEvent?.(`x:${next}`) }} />
</SWPopupRow>
</SWPopupSection>
<SWPopupSection title="레이어 · 색상">
{/* The property window pickers; their lists open as a fixed overlay, so the scrolling body never clips them. */}
<SWPopupPropertyRow property={{ id: "layer", name: "레이어", value: activeLayerId, type: "select", groupId: "popup", picker: "layer", options: layers.map(({ id, name }) => ({ id, name })) }}
menus={{ layers }} onPropertyChange={(_, value) => { setActiveLayerId(value); onEvent?.(`layer:${value}`) }} />
<SWPopupPropertyRow property={{ id: "color", name: "색상", value: color, type: "color", groupId: "popup" }}
menus={{ colors }} onPropertyChange={(_, value) => { setColor(value); onEvent?.(`color:${value}`) }} />
</SWPopupSection>
</SWPopup>
</SWTheme>
)
}