fix(sw): align header logo styles and refine viewer UI
Deploy Storybook / deploy (push) Successful in 1m13s
Deploy Storybook / deploy (push) Successful in 1m13s
This commit is contained in:
1 parent
1d206c86cf
commit
205864b8a0
15 files changed
+236
-96
No files matched your search
@@ -367,11 +367,14 @@ function ReferenceFooter() {
|
||||
const [open, setOpen] = useState(false)
|
||||
const [aux, setAux] = useState(REFERENCE_AUX)
|
||||
const [active, setActive] = useState<string | null>(null)
|
||||
// 2026-10-23: onRemove makes the layout tabs removable, so the × shows on hover as in SW Footer (no onRemove =
|
||||
// a disabled, invisible ×). Removal is per screen row.
|
||||
const [layouts, setLayouts] = useState(LAYOUTS_2)
|
||||
return (
|
||||
<SWFooter categories={REFERENCE_CATEGORIES} auxItems={aux} onAuxPressedChange={(id, pressed) => setAux(items => items.map(item => (item.id === id ? { ...item, pressed } : item)))}
|
||||
activeToolId={active} onToolSelect={id => setActive(id)} onSlotAdd={noop} onCategoryAdd={noop} status="10.5939, 6.0728, 0.0000"
|
||||
command={<SWFooterCommandLine history={REFERENCE_HISTORY} expanded={open} onExpandedChange={setOpen} />}
|
||||
layoutTabs={<SWFooterLayoutTabs layouts={LAYOUTS_2} value={SW_FOOTER_MODEL} onAdd={noop} />} />
|
||||
layoutTabs={<SWFooterLayoutTabs layouts={layouts} value={SW_FOOTER_MODEL} onAdd={noop} onRemove={id => setLayouts(items => items.filter(item => item.id !== id))} />} />
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
import "@hm-design/sw/styles.css"
|
||||
import { StoryPage, StoryPageHeader, StorySection } from "@/components/story-page"
|
||||
import { AUX_ITEMS, CATEGORIES, HISTORY } from "./sw-footer-fixture"
|
||||
import { LayerPanel, OptionsPanel, SAMPLES, useViewerDemo } from "./sw-viewer-fixture"
|
||||
import { LayerPanel, OptionsPanel, SAMPLES, VIEWER_STORY_LOGO, useViewerDemo } from "./sw-viewer-fixture"
|
||||
import "./sw-stories.css"
|
||||
import "./sw-viewer-stories.css"
|
||||
|
||||
@@ -42,7 +42,7 @@ function ViewerArea({ width, panels, ...rest }: { width: number; panels?: boolea
|
||||
const demo = useViewerDemo({ initialDocument: SAMPLES[1].name, initialPanels: panels ? ["layers", "options"] : [] })
|
||||
return (
|
||||
<Frame width={width} height={panels ? 600 : 220} {...rest}
|
||||
header={<SWViewerHeader documentTitle={demo.documentName} {...demo.fileProps} groups={demo.groups} trailing={demo.trailing} onCommand={demo.onCommand} />}>
|
||||
header={<SWViewerHeader {...VIEWER_STORY_LOGO} documentTitle={demo.documentName} {...demo.fileProps} groups={demo.groups} trailing={demo.trailing} onCommand={demo.onCommand} />}>
|
||||
{panels && <>
|
||||
<LayerPanel demo={demo} strategy="absolute" defaultPosition="top-start" />
|
||||
<OptionsPanel demo={demo} strategy="absolute" />
|
||||
|
||||
@@ -6,6 +6,11 @@ import {
|
||||
type SWHeaderDocument, type SWViewerCommand, type SWViewerHeaderGroup, type SWViewerHeaderProps, type SWViewerPanelProps,
|
||||
} from "@hm-design/sw"
|
||||
import { Info, Layers, List, MousePointerClick, RotateCcw, Scan, SlidersHorizontal, ZoomIn } from "lucide-react"
|
||||
import placeholderLogo from "./assets/sw-header-logo-placeholder.svg"
|
||||
|
||||
// 2026-10-23: the regular viewer Stories show the TEMPORARY placeholder logo of the SW Header Stories (not a product
|
||||
// logo); only SW/Viewer › Logo › 로고 없음 renders without one.
|
||||
export const VIEWER_STORY_LOGO = { logoSrc: placeholderLogo, logoAlt: "임시 로고" } satisfies Partial<SWViewerHeaderProps>
|
||||
|
||||
export const SAMPLES = [
|
||||
{ id: "sample-basic", label: "기본 도면 · DWG", name: "BasicSample.dwg" },
|
||||
|
||||
@@ -8,7 +8,7 @@ import "@hm-design/sw/styles.css"
|
||||
import { Copy, Download, FileText, FolderOpen, Layers, Printer, Ruler, Scan, Search } from "lucide-react"
|
||||
import { StoryPage, StoryPageHeader, StorySection } from "@/components/story-page"
|
||||
import { showVerificationControls } from "./sw-story-flags"
|
||||
import { LAYERS, LayerPanel, OptionsPanel, PropertiesPanel, SAMPLES, useViewerDemo, type ViewerDemo } from "./sw-viewer-fixture"
|
||||
import { LAYERS, LayerPanel, OptionsPanel, PropertiesPanel, SAMPLES, VIEWER_STORY_LOGO, useViewerDemo, type ViewerDemo } from "./sw-viewer-fixture"
|
||||
import "./sw-stories.css"
|
||||
import "./sw-viewer-stories.css"
|
||||
import placeholderLogo from "./assets/sw-header-logo-placeholder.svg"
|
||||
@@ -73,7 +73,7 @@ function ViewerScreen({ width = null as number | null, initialDocument = SAMPLES
|
||||
return (
|
||||
<>
|
||||
<Log lines={demo.log} />
|
||||
<ViewerFrame width={width} header={<SWViewerHeader 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={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 }} />
|
||||
@@ -121,7 +121,7 @@ export const MenuGroups: Story = {
|
||||
<div className="sw-story-stack">
|
||||
{rows.map(row => (
|
||||
<StorySection key={row.title} title={row.title}>
|
||||
<div className="sw-story-scroller"><div style={{ width: 1440 }}><SWViewerHeader documentTitle={demo.documentName} {...demo.fileProps} groups={pick(row.ids)} trailing={row.ids.includes("panels") ? demo.trailing : undefined} onCommand={demo.onCommand} /></div></div>
|
||||
<div className="sw-story-scroller"><div style={{ width: 1440 }}><SWViewerHeader {...VIEWER_STORY_LOGO} documentTitle={demo.documentName} {...demo.fileProps} groups={pick(row.ids)} trailing={row.ids.includes("panels") ? demo.trailing : undefined} onCommand={demo.onCommand} /></div></div>
|
||||
</StorySection>
|
||||
))}
|
||||
</div>
|
||||
@@ -162,7 +162,7 @@ export const States: Story = {
|
||||
<StoryPageHeader kicker={KICKER} title="SW Viewer — Active / Hover / Disabled" />
|
||||
<Log lines={log} />
|
||||
<div className="sw-story-scroller"><div style={{ width: 1440 }}>
|
||||
<SWViewerHeader documentTitle="상태 확인용.dwg" groups={groups} onCommand={command => record(`${command.id}${command.value !== undefined ? `=${String(command.value)}` : ""}`)} />
|
||||
<SWViewerHeader {...VIEWER_STORY_LOGO} documentTitle="상태 확인용.dwg" groups={groups} onCommand={command => record(`${command.id}${command.value !== undefined ? `=${String(command.value)}` : ""}`)} />
|
||||
</div></div>
|
||||
</StoryPage>
|
||||
)
|
||||
@@ -216,7 +216,7 @@ export const Dropdowns: Story = {
|
||||
<StoryPageHeader kicker={KICKER} title="SW Viewer — 상단 메뉴 + Dropdown" />
|
||||
<Log lines={log} />
|
||||
<div className="sw-story-scroller sw-viewer-story-room"><div style={{ width: 1440 }}>
|
||||
<SWViewerHeader documentTitle="C0060202-001-평면및종단면도.dwg" groups={groups}
|
||||
<SWViewerHeader {...VIEWER_STORY_LOGO} documentTitle="C0060202-001-평면및종단면도.dwg" groups={groups}
|
||||
onCommand={command => { const entry = findEntry(entries, command.id); if (entry) apply(entry); record(`${command.itemId}/${command.id}${command.value !== undefined ? `=${String(command.value)}` : ""}`) }} />
|
||||
</div></div>
|
||||
{showVerificationControls() && <StorySection title="SWViewerMenu 단독 (Context Menu)">
|
||||
@@ -263,7 +263,7 @@ export const SettingsPanel: Story = {
|
||||
<StoryPage>
|
||||
<StoryPageHeader kicker={KICKER} title="SW Viewer — 설정창 (SWViewerPanel)" />
|
||||
<Log lines={demo.log} />
|
||||
<ViewerFrame header={<SWViewerHeader documentTitle={demo.documentName} {...demo.fileProps} groups={demo.groups} trailing={demo.trailing} onCommand={demo.onCommand} />}>
|
||||
<ViewerFrame header={<SWViewerHeader {...VIEWER_STORY_LOGO} documentTitle={demo.documentName} {...demo.fileProps} groups={demo.groups} trailing={demo.trailing} onCommand={demo.onCommand} />}>
|
||||
<OptionsPanel demo={demo} strategy="absolute" />
|
||||
</ViewerFrame>
|
||||
</StoryPage>
|
||||
@@ -282,7 +282,7 @@ export const ContentVariants: Story = {
|
||||
<StoryPageHeader title="SW Viewer — 설정창 (SWViewerPanel)" />
|
||||
<Log lines={demo.log} />
|
||||
{/* 2026-10-22: 620px fits the tallest popup (옵션, 520px) now that 축척 지정 is gone. */}
|
||||
<ViewerFrame height={620} header={<SWViewerHeader documentTitle={demo.documentName} {...demo.fileProps} groups={demo.groups} trailing={demo.trailing} onCommand={demo.onCommand} />}>
|
||||
<ViewerFrame height={620} header={<SWViewerHeader {...VIEWER_STORY_LOGO} documentTitle={demo.documentName} {...demo.fileProps} groups={demo.groups} trailing={demo.trailing} onCommand={demo.onCommand} />}>
|
||||
<LayerPanel demo={demo} strategy="absolute" defaultPosition={{ left: 24, top: 16 }} />
|
||||
<PropertiesPanel demo={demo} strategy="absolute" defaultPosition={{ left: 368, top: 16 }} />
|
||||
<OptionsPanel demo={demo} strategy="absolute" defaultPosition={{ left: 712, top: 16 }} />
|
||||
@@ -307,7 +307,7 @@ export const VariableSize: Story = {
|
||||
return (
|
||||
<StoryPage>
|
||||
<StoryPageHeader kicker={KICKER} title="SW Viewer — 가변 크기 팝업" />
|
||||
<ViewerFrame height={520} header={<SWViewerHeader documentTitle="크기 비교.dwg" groups={[]} />}>
|
||||
<ViewerFrame height={520} header={<SWViewerHeader {...VIEWER_STORY_LOGO} documentTitle="크기 비교.dwg" groups={[]} />}>
|
||||
{sizes.map(size => (
|
||||
<SWViewerPanel key={size.id} strategy="absolute" defaultPosition={{ left: size.left, top: 16 }} title={size.title} open={open[size.id]} onClose={() => setOpen({ ...open, [size.id]: false })} width={size.width} height={size.height}>
|
||||
<SWViewerHint>{size.note}</SWViewerHint>
|
||||
@@ -334,7 +334,7 @@ export const Responsive: Story = {
|
||||
// like the other rows it is a maximum inside the Story column.
|
||||
<div key={width}><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 documentTitle={demo.documentName} {...demo.fileProps} groups={demo.groups} trailing={demo.trailing} onCommand={demo.onCommand} /></div></div>
|
||||
<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>
|
||||
</StorySection></div>
|
||||
))}
|
||||
</div>
|
||||
@@ -373,7 +373,7 @@ export const CompareWithSWHeader: Story = {
|
||||
<StoryPageHeader title="SW Viewer — 일반 SW Header와 비교" />
|
||||
<StorySection title="나란히 비교 (1440px)">
|
||||
<div className="sw-story-stack">
|
||||
<div className="sw-story-scroller"><div style={{ width: 1440 }}><SWHeader documentTitle="C0060202-001-평면및종단면도" /></div></div>
|
||||
<div className="sw-story-scroller"><div style={{ width: 1440 }}><SWHeader logoSrc={placeholderLogo} logoAlt="임시 로고" documentTitle="C0060202-001-평면및종단면도" /></div></div>
|
||||
<div className="sw-story-scroller"><div style={{ width: 1440 }}><ViewerOnly /></div></div>
|
||||
</div>
|
||||
</StorySection>
|
||||
@@ -414,5 +414,6 @@ export const Logo: Story = {
|
||||
|
||||
function ViewerOnly() {
|
||||
const demo = useViewerDemo({ initialDocument: SAMPLES[1].name })
|
||||
return <SWViewerHeader documentTitle={demo.documentName} {...demo.fileProps} groups={demo.groups} trailing={demo.trailing} onCommand={demo.onCommand} />
|
||||
// 2026-10-23: the same placeholder logo as the SWHeader row, so the two logo slots compare at the same width.
|
||||
return <SWViewerHeader logoSrc={placeholderLogo} logoAlt="임시 로고" documentTitle={demo.documentName} {...demo.fileProps} groups={demo.groups} trailing={demo.trailing} onCommand={demo.onCommand} />
|
||||
}
|
||||
Reference in new issue
Block a user