style(storybook): tidy SW header, step header and viewer stories

- Responsive More / Logo / Settings Popup / Property Panel names in Korean (IDs unchanged)
- SW Step Header representative page keeps one Multi Step example
- Viewer Header drops the minimize/close example (component support unchanged)
- Header Responsive page uses the Logo page structure
This commit is contained in:
shkim4 committed 2026-11-09 18:26:12 +09:00
1 parent 0f1574f80f
commit a460426535
5 files changed
+25 -35

No files matched your search

+8 -4
View File
@@ -450,7 +450,7 @@ const LOGO_CASES: { title: string; props: Partial<SWHeaderProps> }[] = [
]
export const LogoSlot: Story = {
name: "Logo",
name: "로고",
parameters: { controls: { disable: true } },
render: () => (
<StoryPage>
@@ -780,17 +780,21 @@ function StyleHeader({ onLog, showState, ...props }: StyleHostProps) {
const MORE_WIDTHS = [1920, 1440, 1280, 950, 860, 760]
export const ResponsiveMore: Story = {
name: "Responsive More",
name: "화면 너비",
parameters: { controls: { disable: true } },
render: () => (
<StoryPage>
<StoryPageHeader title="SW Header — 화면 너비" />
<div className="sw-story-responsive">
<div className="sw-story-stack sw-story-responsive">
{MORE_WIDTHS.map(width => (
// 2026-10-23: the 1920 row stays inside the Story column (as SW Viewer › Responsive) and is hidden on narrow pages.
<div key={width} className={width === 1920 ? "sw-story-responsive-hidden" : undefined}>
<StorySection title={`${width}px`}>
<MenuFrame width={width} fluid><StyleHeader /></MenuFrame>
{/* Same structure as the Logo page (no black frame). This page only shows widths, so the five field dropdowns are
held closed with the public controlled props; the open callbacks and header data are unchanged. */}
<div style={{ width: "100%", maxWidth: width }} data-frame-width={width}>
<StyleHeader layerMenuOpen={false} colorMenuOpen={false} lineStyleMenuOpen={false} textStyleMenuOpen={false} dimensionStyleMenuOpen={false} />
</div>
</StorySection>
</div>
))}
+6 -7
View File
@@ -141,7 +141,10 @@ const FIGMA_FRAMES: { node: string; step: string; documentTitle?: string }[] = [
{ node: "873:17421", step: "body-plan" },
]
// One page (2026-11-03): the Default header, then the three Multi Step frames; each header keeps its own state.
// The representative page keeps only the middle frame (project open); the three frames stay in the MultiStep verification story.
const OPEN_PROJECT_FRAME = FIGMA_FRAMES[1]
// One page: the Default header, then the project-open Multi Step frame; each header keeps its own state.
export const ProcessHeader: Story = {
name: "Step Header",
render: () => (
@@ -151,11 +154,7 @@ export const ProcessHeader: Story = {
<LiveHeader />
</StorySection>
<StorySection title="Multi Step">
<div className="sw-story-stack">
{FIGMA_FRAMES.map(frame => (
<Row key={frame.node}><div data-figma-node={frame.node}><LiveMultiHeader step={frame.step} documentTitle={frame.documentTitle} /></div></Row>
))}
</div>
<Row><div data-figma-node={OPEN_PROJECT_FRAME.node}><LiveMultiHeader step={OPEN_PROJECT_FRAME.step} documentTitle={OPEN_PROJECT_FRAME.documentTitle} /></div></Row>
</StorySection>
</StoryPage>
),
@@ -164,7 +163,7 @@ export const ProcessHeader: Story = {
const WIDTHS = [1920, 1366, 1100]
export const Responsive: Story = {
name: "Responsive More",
name: "화면 너비",
render: () => (
<StoryPage>
<StoryPageHeader title="SW Step Header — 화면 너비" />
+1 -1
View File
@@ -12,6 +12,6 @@ const meta = {
export default meta
export const PropertyPanelCompare: StoryObj<typeof meta> = {
name: "Property Panel", // 2026-10-22 (user request): was "Property Panel · 4개 비교"; ID unchanged
name: "속성창", // 2026-10-22 (user request): was "Property Panel · 4개 비교"; ID unchanged
render: () => <PropertyPanelComparePage />,
}
-2
View File
@@ -46,5 +46,3 @@
.sw-viewer-story-steps { margin: 0; padding-left: 20px; font-size: 14px; }
.sw-viewer-story-context { display: flex; align-items: center; justify-content: center; height: 160px; border: 1px dashed #9a9284; border-radius: 4px; background: #f5f0e5; color: #3e3523; font-size: 13px; }
.sw-viewer-story-mode { display: flex; gap: 16px; margin-bottom: 8px; font-size: 13px; }
/* Room below a single header row so its logo menu / document list are not cut by the scroller. */
.sw-viewer-story-menu-row { padding-bottom: 120px; }
+10 -21
View File
@@ -1,7 +1,7 @@
import { useRef, useState, type ReactNode } from "react"
import type { Meta, StoryObj } from "@storybook/react-vite"
import {
SWHeader, SWHeaderWindowControls, SWViewerButton, SWViewerHeader, SWViewerHint, SWViewerMenu, SWViewerPanel, SWViewerSection,
SWHeader, SWViewerButton, SWViewerHeader, SWViewerHint, SWViewerMenu, SWViewerPanel, SWViewerSection,
type SWViewerHeaderGroup, type SWViewerMenuEntry,
} from "@hm-design/sw"
import "@hm-design/sw/styles.css"
@@ -59,21 +59,12 @@ function AboutPanel({ demo }: { demo: ViewerDemo }) {
}
// 2026-10-22 (user request): the Viewer Header screens have no 1440px maximum; they fill the Story column.
function ViewerScreen({ width = null as number | null, initialDocument = SAMPLES[1].name as string | null, open = [] as ("layers" | "options" | "properties")[], windowControls = false }) {
function ViewerScreen({ width = null as number | null, initialDocument = SAMPLES[1].name as string | null, open = [] as ("layers" | "options" | "properties")[] }) {
const demo = useViewerDemo({ initialDocument, initialPanels: open })
// 2026-10-21 new viewer version: SWHeader's own minimize / close (SWHeaderWindowControls, same icons, size, hover)
// at the right end after the existing trailing items (the `trailing` slot, no new API). The program owns the real
// window actions (onMinimize / onClose, as in SWHeader); the Story only logs them.
const trailing = windowControls ? (
<>
{demo.trailing}
<SWHeaderWindowControls showWindowMove={false} onMinimize={() => demo.record("minimize")} onClose={() => demo.record("close")} />
</>
) : demo.trailing
return (
<>
<Log lines={demo.log} />
<ViewerFrame width={width} header={<SWViewerHeader {...VIEWER_STORY_LOGO} documentTitle={demo.documentName} {...demo.fileProps} groups={demo.groups} trailing={trailing} onCommand={demo.onCommand} />}>
<ViewerFrame width={width} header={<SWViewerHeader {...VIEWER_STORY_LOGO} documentTitle={demo.documentName} {...demo.fileProps} groups={demo.groups} trailing={demo.trailing} onCommand={demo.onCommand} />}>
<LayerPanel demo={demo} strategy="absolute" />
<OptionsPanel demo={demo} strategy="absolute" inset={{ side: 356 }} />
<PropertiesPanel demo={demo} strategy="absolute" inset={{ side: 24, top: 488 }} />
@@ -87,15 +78,12 @@ export const ViewerHeader: Story = {
name: "Viewer Header",
render: () => (
<StoryPage>
{/* 2026-10-22 (user request): page title and the two sections only. The minimize / close version keeps
SWHeaderWindowControls at the right end of `trailing`; the program owns the real window actions. */}
{/* The minimize / close example was removed from this Story; the component keeps supporting it
(SWHeaderWindowControls at the right end of `trailing`, VIEWER.md §2.1). */}
<StoryPageHeader title="SW Viewer Header" />
<StorySection title="기본형">
<ViewerScreen />
</StorySection>
<StorySection title="최소화 · 닫기 버튼">
<div data-viewer-window-controls=""><ViewerScreen windowControls /></div>
</StorySection>
</StoryPage>
),
}
@@ -280,7 +268,7 @@ export const SettingsPanel: Story = {
export const ContentVariants: Story = {
// 2026-10-21: the one settings-popup Story (former 설정창 and 같은 프레임, 다른 내부 콘텐츠 pages); ID kept.
// 2026-10-22 (user request): the 축척 지정 demo popup was removed.
name: "Settings Popup",
name: "설정창",
render: () => {
const demo = useViewerDemo({ initialDocument: SAMPLES[1].name, initialPanels: ["layers", "properties", "options"] })
return (
@@ -327,7 +315,7 @@ export const VariableSize: Story = {
}
export const Logo: Story = {
name: "Logo",
name: "로고",
render: () => {
const demo = useViewerDemo({ initialDocument: SAMPLES[1].name })
const rows: { title: string; props: { logoSrc?: string; logo?: ReactNode; logoAlt?: string } }[] = [
@@ -355,7 +343,7 @@ export const Logo: Story = {
}
export const Responsive: Story = {
name: "Responsive More",
name: "화면 너비",
render: () => {
const demo = useViewerDemo({ initialDocument: SAMPLES[1].name })
return (
@@ -369,7 +357,8 @@ export const Responsive: Story = {
// as the SW Header / Footer 1920 rows (sw-story-responsive-hidden, sw-stories.css), e.g. a 1920px screen.
<div key={width} className={width === 1920 ? "sw-story-responsive-hidden" : undefined}><StorySection title={`${width}px`}>
{/* The row width is a maximum (2026-10-22): on a narrower screen the header shrinks with it instead of scrolling. */}
<div className="sw-story-scroller sw-viewer-story-menu-row"><div style={{ width: "100%", maxWidth: width }} data-width={width}><SWViewerHeader {...VIEWER_STORY_LOGO} documentTitle={demo.documentName} {...demo.fileProps} groups={demo.groups} trailing={demo.trailing} onCommand={demo.onCommand} /></div></div>
{/* Same structure as the Logo page (no scroller / bottom padding), so the spacing matches it. */}
<div style={{ width: "100%", maxWidth: width }} data-width={width}><SWViewerHeader {...VIEWER_STORY_LOGO} documentTitle={demo.documentName} {...demo.fileProps} groups={demo.groups} trailing={demo.trailing} onCommand={demo.onCommand} /></div>
</StorySection></div>
))}
</div>