import { useId, useState } from "react" import type { Meta, StoryObj } from "@storybook/react-vite" import { SWHeaderInput, SWHeaderPropertyPanel, SWPopup, SWPopupPropertyRow, SWPopupRow, SWPopupSection, SWSelect, SWSlider, SWTheme, type SWHeaderColorOption, type SWHeaderLayer, type SWHeaderProperty, } from "@hm-design/sw" import "@hm-design/sw/styles.css" import { Button, Switch } from "@hm-design/ui" import { StoryPage, StoryPageHeader, StorySection } from "@/components/story-page" import "./sw-stories.css" import "./sw-integrated-stories.css" // SWPopup (2026-10-19): the SW property window frame with free children. Public API only, like a consuming // project. HM UI components are placed inside to show that the content is not restricted. const LAYERS: SWHeaderLayer[] = [ { id: "0", name: "0", color: "#ffffff" }, { id: "wall", name: "WALL", color: "#ff4242" }, { id: "dim", name: "DIM", color: "#47af91" }, { id: "hatch", name: "HATCH", color: "#5aa0ff" }, ] const COLORS: SWHeaderColorOption[] = [ { id: "red", color: "#ff0000", label: "빨강" }, { id: "yellow", color: "#ffff00", label: "노랑" }, { id: "green", color: "#00ff00", label: "초록" }, { id: "blue", color: "#0000ff", label: "파랑" }, ] const UNITS = ["mm", "cm", "m"].map(value => ({ value })) const MENUS = { layers: LAYERS, colors: COLORS } const layerProperty = (value: string): SWHeaderProperty => ({ id: "layer", name: "레이어", value, type: "select", groupId: "popup", picker: "layer", options: LAYERS.map(({ id, name }) => ({ id, name })) }) const meta = { title: "SW/Popup", parameters: { layout: "fullscreen", controls: { disable: true } }, } satisfies Meta export default meta type Story = StoryObj function useLog() { const [log, setLog] = useState([]) const view =

Callback: {log.length ? log.join(" → ") : "없음"}

return [view, (line: string) => setLog(lines => [...lines.slice(-9), line])] as const } function SettingsPopup({ record }: { record: (line: string) => void }) { const ids = { name: useId(), scale: useId(), unit: useId(), snap: useId() } const [name, setName] = useState("평면도-01") const [scale, setScale] = useState("100") const [layerId, setLayerId] = useState("wall") const [color, setColor] = useState("255,0,0") const [snap, setSnap] = useState(true) const [opacity, setOpacity] = useState(60) const [unit, setUnit] = useState("mm") return ( record("settings.onClose")} data-popup="settings"> { setName(next); record(`name=${next}`) }} /> { setScale(next); record(`scale=${next}`) }} /> { setUnit(next); record(`unit=${next}`) }} data-sw-select="unit" /> {/* 2026-10-20: popup layer / color controls are the property window pickers (SWPopupPropertyRow), not the SWHeader field dropdowns (header-only design). */} { setLayerId(value); record(`layer=${value}`) }} /> { setColor(value); record(`color=${value}`) }} /> {/* 2026-10-20: the gauge is the SW gauge (SWSlider; 2026-10-22: style B, appearance="property", on this page). SW has no normal-mode switch, so 스냅 stays the HM UI Switch. */} { setSnap(next); record(`snap=${next}`) }} data-ui-switch="" /> record(`opacity=${next}`)} aria-label="투명도" data-popup-slider="" />
) } function ObjectInfoPopup({ record }: { record: (line: string) => void }) { const [x, setX] = useState("1200.5") return ( record("object.onClose")} data-popup="object"> { setX(next); record(`x=${next}`) }} /> record(`y=${next}`)} /> ) } function PanelWithChildren({ record }: { record: (line: string) => void }) { const [properties, setProperties] = useState([ { id: "layer", name: "레이어", value: "wall", type: "select", groupId: "common", picker: "layer", options: LAYERS.map(({ id, name }) => ({ id, name })) }, { id: "color", name: "색상", value: "255,66,66", type: "color", groupId: "common" }, { id: "width", name: "선가중치", value: "0.25", type: "number", groupId: "common" }, ]) return ( record("panel.onClose")} onPropertyChange={(property, value) => { setProperties(list => list.map(item => item.id === property.id ? { ...item, value } : item)); record(`${property.id}=${value}`) }}> record(`memo=${next}`)} /> ) } /** SWSlider style B (appearance="property", 2026-10-22: the only gauge look on this page): value, entry and disabled. */ function OpacityPopup({ record }: { record: (line: string) => void }) { const [opacity, setOpacity] = useState(40) return ( record(`opacity(property)=${next}`)} /> { setOpacity(Math.min(100, Math.max(0, Number(next)))); record(`opacity=${next}`) }} /> ) } function PopupPage() { const [log, record] = useLog() return ( {log} ) } // 2026-10-22 (user request): SW/Popup keeps only Property Panel in the sidebar; these two stay for the browser tests // (verification tag: ID and direct URL kept). export const CustomContent: Story = { name: "Custom Content", tags: ["verification"], render: () => } function StackPage() { const [log, record] = useLog() const [open, setOpen] = useState(["a", "b", "c"]) const [layerB, setLayerB] = useState("wall") const close = (id: string) => { setOpen(list => list.filter(item => item !== id)); record(`close(${id})`) } const rows = Array.from({ length: 30 }, (_, index) => index + 1) return ( {log} {open.includes("a") && close("a")} data-popup="a">A} {open.includes("b") && close("b")} data-popup="b"> { setLayerB(value); record(`b.layer=${value}`) }} /> } {open.includes("c") && close("c")} data-popup="c" style={{ maxHeight: 360 }}> {rows.map(row => )} } ) } export const MultiplePopups: Story = { name: "Multiple Popups", tags: ["verification"], render: () => }