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:
1 parent
0f1574f80f
commit
a460426535
5 files changed
+25
-35
No files matched your search
@@ -450,7 +450,7 @@ const LOGO_CASES: { title: string; props: Partial<SWHeaderProps> }[] = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
export const LogoSlot: Story = {
|
export const LogoSlot: Story = {
|
||||||
name: "Logo",
|
name: "로고",
|
||||||
parameters: { controls: { disable: true } },
|
parameters: { controls: { disable: true } },
|
||||||
render: () => (
|
render: () => (
|
||||||
<StoryPage>
|
<StoryPage>
|
||||||
@@ -780,17 +780,21 @@ function StyleHeader({ onLog, showState, ...props }: StyleHostProps) {
|
|||||||
const MORE_WIDTHS = [1920, 1440, 1280, 950, 860, 760]
|
const MORE_WIDTHS = [1920, 1440, 1280, 950, 860, 760]
|
||||||
|
|
||||||
export const ResponsiveMore: Story = {
|
export const ResponsiveMore: Story = {
|
||||||
name: "Responsive More",
|
name: "화면 너비",
|
||||||
parameters: { controls: { disable: true } },
|
parameters: { controls: { disable: true } },
|
||||||
render: () => (
|
render: () => (
|
||||||
<StoryPage>
|
<StoryPage>
|
||||||
<StoryPageHeader title="SW Header — 화면 너비" />
|
<StoryPageHeader title="SW Header — 화면 너비" />
|
||||||
<div className="sw-story-responsive">
|
<div className="sw-story-stack sw-story-responsive">
|
||||||
{MORE_WIDTHS.map(width => (
|
{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.
|
// 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}>
|
<div key={width} className={width === 1920 ? "sw-story-responsive-hidden" : undefined}>
|
||||||
<StorySection title={`${width}px`}>
|
<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>
|
</StorySection>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -141,7 +141,10 @@ const FIGMA_FRAMES: { node: string; step: string; documentTitle?: string }[] = [
|
|||||||
{ node: "873:17421", step: "body-plan" },
|
{ 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 = {
|
export const ProcessHeader: Story = {
|
||||||
name: "Step Header",
|
name: "Step Header",
|
||||||
render: () => (
|
render: () => (
|
||||||
@@ -151,11 +154,7 @@ export const ProcessHeader: Story = {
|
|||||||
<LiveHeader />
|
<LiveHeader />
|
||||||
</StorySection>
|
</StorySection>
|
||||||
<StorySection title="Multi Step">
|
<StorySection title="Multi Step">
|
||||||
<div className="sw-story-stack">
|
<Row><div data-figma-node={OPEN_PROJECT_FRAME.node}><LiveMultiHeader step={OPEN_PROJECT_FRAME.step} documentTitle={OPEN_PROJECT_FRAME.documentTitle} /></div></Row>
|
||||||
{FIGMA_FRAMES.map(frame => (
|
|
||||||
<Row key={frame.node}><div data-figma-node={frame.node}><LiveMultiHeader step={frame.step} documentTitle={frame.documentTitle} /></div></Row>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</StorySection>
|
</StorySection>
|
||||||
</StoryPage>
|
</StoryPage>
|
||||||
),
|
),
|
||||||
@@ -164,7 +163,7 @@ export const ProcessHeader: Story = {
|
|||||||
const WIDTHS = [1920, 1366, 1100]
|
const WIDTHS = [1920, 1366, 1100]
|
||||||
|
|
||||||
export const Responsive: Story = {
|
export const Responsive: Story = {
|
||||||
name: "Responsive More",
|
name: "화면 너비",
|
||||||
render: () => (
|
render: () => (
|
||||||
<StoryPage>
|
<StoryPage>
|
||||||
<StoryPageHeader title="SW Step Header — 화면 너비" />
|
<StoryPageHeader title="SW Step Header — 화면 너비" />
|
||||||
|
|||||||
@@ -12,6 +12,6 @@ const meta = {
|
|||||||
export default meta
|
export default meta
|
||||||
|
|
||||||
export const PropertyPanelCompare: StoryObj<typeof 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 />,
|
render: () => <PropertyPanelComparePage />,
|
||||||
}
|
}
|
||||||
@@ -46,5 +46,3 @@
|
|||||||
.sw-viewer-story-steps { margin: 0; padding-left: 20px; font-size: 14px; }
|
.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-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; }
|
.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; }
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useRef, useState, type ReactNode } from "react"
|
import { useRef, useState, type ReactNode } from "react"
|
||||||
import type { Meta, StoryObj } from "@storybook/react-vite"
|
import type { Meta, StoryObj } from "@storybook/react-vite"
|
||||||
import {
|
import {
|
||||||
SWHeader, SWHeaderWindowControls, SWViewerButton, SWViewerHeader, SWViewerHint, SWViewerMenu, SWViewerPanel, SWViewerSection,
|
SWHeader, SWViewerButton, SWViewerHeader, SWViewerHint, SWViewerMenu, SWViewerPanel, SWViewerSection,
|
||||||
type SWViewerHeaderGroup, type SWViewerMenuEntry,
|
type SWViewerHeaderGroup, type SWViewerMenuEntry,
|
||||||
} from "@hm-design/sw"
|
} from "@hm-design/sw"
|
||||||
import "@hm-design/sw/styles.css"
|
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.
|
// 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 })
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
<Log lines={demo.log} />
|
<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" />
|
<LayerPanel demo={demo} strategy="absolute" />
|
||||||
<OptionsPanel demo={demo} strategy="absolute" inset={{ side: 356 }} />
|
<OptionsPanel demo={demo} strategy="absolute" inset={{ side: 356 }} />
|
||||||
<PropertiesPanel demo={demo} strategy="absolute" inset={{ side: 24, top: 488 }} />
|
<PropertiesPanel demo={demo} strategy="absolute" inset={{ side: 24, top: 488 }} />
|
||||||
@@ -87,15 +78,12 @@ export const ViewerHeader: Story = {
|
|||||||
name: "Viewer Header",
|
name: "Viewer Header",
|
||||||
render: () => (
|
render: () => (
|
||||||
<StoryPage>
|
<StoryPage>
|
||||||
{/* 2026-10-22 (user request): page title and the two sections only. The minimize / close version keeps
|
{/* The minimize / close example was removed from this Story; the component keeps supporting it
|
||||||
SWHeaderWindowControls at the right end of `trailing`; the program owns the real window actions. */}
|
(SWHeaderWindowControls at the right end of `trailing`, VIEWER.md §2.1). */}
|
||||||
<StoryPageHeader title="SW Viewer Header" />
|
<StoryPageHeader title="SW Viewer Header" />
|
||||||
<StorySection title="기본형">
|
<StorySection title="기본형">
|
||||||
<ViewerScreen />
|
<ViewerScreen />
|
||||||
</StorySection>
|
</StorySection>
|
||||||
<StorySection title="최소화 · 닫기 버튼">
|
|
||||||
<div data-viewer-window-controls=""><ViewerScreen windowControls /></div>
|
|
||||||
</StorySection>
|
|
||||||
</StoryPage>
|
</StoryPage>
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
@@ -280,7 +268,7 @@ export const SettingsPanel: Story = {
|
|||||||
export const ContentVariants: Story = {
|
export const ContentVariants: Story = {
|
||||||
// 2026-10-21: the one settings-popup Story (former 설정창 and 같은 프레임, 다른 내부 콘텐츠 pages); ID kept.
|
// 2026-10-21: the one settings-popup Story (former 설정창 and 같은 프레임, 다른 내부 콘텐츠 pages); ID kept.
|
||||||
// 2026-10-22 (user request): the 축척 지정 demo popup was removed.
|
// 2026-10-22 (user request): the 축척 지정 demo popup was removed.
|
||||||
name: "Settings Popup",
|
name: "설정창",
|
||||||
render: () => {
|
render: () => {
|
||||||
const demo = useViewerDemo({ initialDocument: SAMPLES[1].name, initialPanels: ["layers", "properties", "options"] })
|
const demo = useViewerDemo({ initialDocument: SAMPLES[1].name, initialPanels: ["layers", "properties", "options"] })
|
||||||
return (
|
return (
|
||||||
@@ -327,7 +315,7 @@ export const VariableSize: Story = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const Logo: Story = {
|
export const Logo: Story = {
|
||||||
name: "Logo",
|
name: "로고",
|
||||||
render: () => {
|
render: () => {
|
||||||
const demo = useViewerDemo({ initialDocument: SAMPLES[1].name })
|
const demo = useViewerDemo({ initialDocument: SAMPLES[1].name })
|
||||||
const rows: { title: string; props: { logoSrc?: string; logo?: ReactNode; logoAlt?: string } }[] = [
|
const rows: { title: string; props: { logoSrc?: string; logo?: ReactNode; logoAlt?: string } }[] = [
|
||||||
@@ -355,7 +343,7 @@ export const Logo: Story = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const Responsive: Story = {
|
export const Responsive: Story = {
|
||||||
name: "Responsive More",
|
name: "화면 너비",
|
||||||
render: () => {
|
render: () => {
|
||||||
const demo = useViewerDemo({ initialDocument: SAMPLES[1].name })
|
const demo = useViewerDemo({ initialDocument: SAMPLES[1].name })
|
||||||
return (
|
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.
|
// 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`}>
|
<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. */}
|
{/* 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>
|
</StorySection></div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in new issue
Block a user