Merge pull request 'Feat/storybook ui refinement' (#20) from feat/storybook-ui-refinement into main
Deploy Storybook / deploy (push) Successful in 57s
Deploy Storybook / deploy (push) Successful in 57s
Reviewed-on: #20
This commit was merged in pull request #20.
This commit is contained in:
commit
0be30d34ab
25 files changed
+104
-91
No files matched your search
@@ -38,7 +38,7 @@ addons.setConfig({
|
|||||||
},
|
},
|
||||||
theme: create({
|
theme: create({
|
||||||
base: 'light',
|
base: 'light',
|
||||||
brandTitle: 'Hanmac Design System',
|
brandTitle: '한맥 디자인 시스템',
|
||||||
brandUrl: './',
|
brandUrl: './',
|
||||||
brandTarget: '_self',
|
brandTarget: '_self',
|
||||||
fontBase: "'Hanmac Manager Noto Sans KR', 'Noto Sans KR', 'Malgun Gothic', sans-serif",
|
fontBase: "'Hanmac Manager Noto Sans KR', 'Noto Sans KR', 'Malgun Gothic', sans-serif",
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ const preview: Preview = {
|
|||||||
includeNames: true,
|
includeNames: true,
|
||||||
order: [
|
order: [
|
||||||
"Foundation",
|
"Foundation",
|
||||||
["Hanmac Color", "Company Colors", "Typography", "Layout"],
|
["한맥 컬러", "가족사 컬러", "Typography"],
|
||||||
"Components",
|
"Components",
|
||||||
[
|
[
|
||||||
"Form",
|
"Form",
|
||||||
@@ -47,8 +47,8 @@ const preview: Preview = {
|
|||||||
["Data", ["Data Table", "Filter Bar"], "Layout", ["GNB", "LNB", "Dashboard Layout", "Loading Screen"]],
|
["Data", ["Data Table", "Filter Bar"], "Layout", ["GNB", "LNB", "Dashboard Layout", "Loading Screen"]],
|
||||||
"SW",
|
"SW",
|
||||||
["Header", ["SW Header", "*"], "Footer", ["SW Footer", "*"], "Popup", "Viewer", "Theme"],
|
["Header", ["SW Header", "*"], "Footer", ["SW Footer", "*"], "Popup", "Viewer", "Theme"],
|
||||||
"OrgChart",
|
"조직도",
|
||||||
["Overview", "Department Colors", "Full Composition", "Expanded OrgChart"],
|
["개요", "부서별 색상", "전체 구성", "확장 조직도"],
|
||||||
"노선도",
|
"노선도",
|
||||||
"*",
|
"*",
|
||||||
// With includeNames, keeps the Loader group after the Runtime Contract Stories (its place without names).
|
// With includeNames, keeps the Loader group after the Runtime Contract Stories (its place without names).
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import { withRepresentation } from "./center-shared"
|
|||||||
// Owners: ui-viz docs/visualization/facility.md and facility-assets.md (128 approved SVG references, shipped unchanged).
|
// Owners: ui-viz docs/visualization/facility.md and facility-assets.md (128 approved SVG references, shipped unchanged).
|
||||||
// The display scale (Q2b) is HOLD and Facility has no Selected / Hover state.
|
// The display scale (Q2b) is HOLD and Facility has no Selected / Hover state.
|
||||||
const meta = {
|
const meta = {
|
||||||
title: "노선도/Facility",
|
title: "노선도/시설물",
|
||||||
id: "center-graphic-facility",
|
id: "center-graphic-facility",
|
||||||
component: TunnelFacility,
|
component: TunnelFacility,
|
||||||
parameters: { layout: "padded" },
|
parameters: { layout: "padded" },
|
||||||
@@ -31,23 +31,25 @@ const page = (name: string, Demo: ComponentType): Story => ({
|
|||||||
render: () => (
|
render: () => (
|
||||||
<StoryPage>
|
<StoryPage>
|
||||||
<StoryPageHeader title={name} />
|
<StoryPageHeader title={name} />
|
||||||
<StorySection title="Overview">
|
<StorySection title="개요">
|
||||||
{withRepresentation(() => <Demo />)}
|
{withRepresentation(() => <Demo />)}
|
||||||
</StorySection>
|
</StorySection>
|
||||||
</StoryPage>
|
</StoryPage>
|
||||||
),
|
),
|
||||||
})
|
})
|
||||||
|
|
||||||
export const Tunnel: Story = page("터널", TunnelReferenceDemo)
|
// `name` is repeated as a literal because Storybook's static indexer cannot read it through page(); it drives the sidebar label.
|
||||||
export const Bridge: Story = page("교량", BridgeReferenceDemo)
|
export const Tunnel: Story = { ...page("터널", TunnelReferenceDemo), name: "터널" }
|
||||||
export const Intersection: Story = page("교차로", IntersectionReferenceDemo)
|
export const Bridge: Story = { ...page("교량", BridgeReferenceDemo), name: "교량" }
|
||||||
export const Junction: Story = page("분기점", JunctionReferenceDemo)
|
export const Intersection: Story = { ...page("교차로", IntersectionReferenceDemo), name: "교차로" }
|
||||||
export const Interchange: Story = page("나들목", InterchangeReferenceDemo)
|
export const Junction: Story = { ...page("분기점", JunctionReferenceDemo), name: "분기점" }
|
||||||
export const Roadside: Story = page("휴게소 · 졸음쉼터", RoadsideReferenceDemo)
|
export const Interchange: Story = { ...page("나들목", InterchangeReferenceDemo), name: "나들목" }
|
||||||
|
export const Roadside: Story = { ...page("휴게소 · 졸음쉼터", RoadsideReferenceDemo), name: "휴게소 · 졸음쉼터" }
|
||||||
// Linked facilities are a standalone section heading followed by item cards.
|
// Linked facilities are a standalone section heading followed by item cards.
|
||||||
// Keep the reference data/artwork and road-class grouping owned by the package.
|
// Keep the reference data/artwork and road-class grouping owned by the package.
|
||||||
export const Linked: Story = {
|
export const Linked: Story = {
|
||||||
...page("연계 시설물", LinkedFacilityReferenceDemo),
|
...page("연계 시설물", LinkedFacilityReferenceDemo),
|
||||||
|
name: "연계 시설물",
|
||||||
render: () => (
|
render: () => (
|
||||||
<StoryPage className="sb-center-linked-page">
|
<StoryPage className="sb-center-linked-page">
|
||||||
<StoryPageHeader title="연계 시설물" />
|
<StoryPageHeader title="연계 시설물" />
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { withRepresentation } from "./center-shared"
|
|||||||
// Owner: ui-viz docs/patterns/center-information-presentation.md and center-information-{box,table,emphasis}.md.
|
// Owner: ui-viz docs/patterns/center-information-presentation.md and center-information-{box,table,emphasis}.md.
|
||||||
// Single-line ellipsis is resolved; multi-line overflow, exact placement and the presentation scale (Q3) are not.
|
// Single-line ellipsis is resolved; multi-line overflow, exact placement and the presentation scale (Q3) are not.
|
||||||
const meta = {
|
const meta = {
|
||||||
title: "노선도/Information",
|
title: "노선도/정보",
|
||||||
id: "center-graphic-information",
|
id: "center-graphic-information",
|
||||||
parameters: { layout: "fullscreen", controls: { disable: true } },
|
parameters: { layout: "fullscreen", controls: { disable: true } },
|
||||||
} satisfies Meta
|
} satisfies Meta
|
||||||
@@ -15,11 +15,11 @@ type Story = StoryObj<typeof meta>
|
|||||||
|
|
||||||
// Common Story page. The CENTER representation wraps only the specimen, so the page title is never scaled with the graphic.
|
// Common Story page. The CENTER representation wraps only the specimen, so the page title is never scaled with the graphic.
|
||||||
export const Information: Story = {
|
export const Information: Story = {
|
||||||
name: "Information",
|
name: "정보",
|
||||||
render: () => (
|
render: () => (
|
||||||
<StoryPage>
|
<StoryPage>
|
||||||
<StoryPageHeader title="Information" />
|
<StoryPageHeader title="정보" />
|
||||||
<StorySection title="Overview">
|
<StorySection title="개요">
|
||||||
{withRepresentation(() => (
|
{withRepresentation(() => (
|
||||||
<div className="sb-center-information">
|
<div className="sb-center-information">
|
||||||
<CenterInformationDisplayReferenceDemo />
|
<CenterInformationDisplayReferenceDemo />
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { withRepresentation } from "./center-shared"
|
|||||||
// Owners: ui-viz docs/visualization/route.md, spatial-map.md, spatial-section.md, composition.md.
|
// Owners: ui-viz docs/visualization/route.md, spatial-map.md, spatial-section.md, composition.md.
|
||||||
// Route layer order Shadow -> Outline -> Primary Stroke is CONFIRMED; the whole-map layer order is Candidate and line join is TBD.
|
// Route layer order Shadow -> Outline -> Primary Stroke is CONFIRMED; the whole-map layer order is Candidate and line join is TBD.
|
||||||
const meta = {
|
const meta = {
|
||||||
title: "노선도/Spatial",
|
title: "노선도/노선·공간",
|
||||||
id: "center-graphic-spatial",
|
id: "center-graphic-spatial",
|
||||||
component: RouteLine,
|
component: RouteLine,
|
||||||
parameters: { layout: "fullscreen", controls: { disable: true } },
|
parameters: { layout: "fullscreen", controls: { disable: true } },
|
||||||
@@ -17,11 +17,11 @@ type Story = StoryObj<typeof meta>
|
|||||||
|
|
||||||
// Common Story page. The CENTER representation wraps only the specimen, so the page title is never scaled with the graphic.
|
// Common Story page. The CENTER representation wraps only the specimen, so the page title is never scaled with the graphic.
|
||||||
export const Spatial: Story = {
|
export const Spatial: Story = {
|
||||||
name: "Spatial",
|
name: "노선·공간",
|
||||||
render: () => (
|
render: () => (
|
||||||
<StoryPage className="sb-center-spatial-page">
|
<StoryPage className="sb-center-spatial-page">
|
||||||
<StoryPageHeader title="Spatial" />
|
<StoryPageHeader title="노선·공간" />
|
||||||
<StorySection title="Overview">
|
<StorySection title="개요">
|
||||||
{withRepresentation(() => <CenterSpatialInformationReferenceDemo />)}
|
{withRepresentation(() => <CenterSpatialInformationReferenceDemo />)}
|
||||||
</StorySection>
|
</StorySection>
|
||||||
</StoryPage>
|
</StoryPage>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { withRepresentation } from "./center-shared"
|
|||||||
|
|
||||||
// Owner: ui-viz docs/components/center-title.md. Title Resolution B numeric output is TBD; the per-Type palette is SCOPED_DESIGN_APPROVAL.
|
// Owner: ui-viz docs/components/center-title.md. Title Resolution B numeric output is TBD; the per-Type palette is SCOPED_DESIGN_APPROVAL.
|
||||||
const meta = {
|
const meta = {
|
||||||
title: "노선도/Title",
|
title: "노선도/제목",
|
||||||
id: "center-graphic-title",
|
id: "center-graphic-title",
|
||||||
component: CenterTitle,
|
component: CenterTitle,
|
||||||
parameters: { layout: "padded" },
|
parameters: { layout: "padded" },
|
||||||
@@ -17,12 +17,12 @@ type Story = StoryObj<typeof meta>
|
|||||||
// Representative Story: the four title types. Named after the area so Storybook shows it as a single sidebar item.
|
// Representative Story: the four title types. Named after the area so Storybook shows it as a single sidebar item.
|
||||||
// Common Story page. The CENTER representation wraps only the specimen, so the page title is never scaled with the graphic.
|
// Common Story page. The CENTER representation wraps only the specimen, so the page title is never scaled with the graphic.
|
||||||
export const Compositions: Story = {
|
export const Compositions: Story = {
|
||||||
name: "Title",
|
name: "제목",
|
||||||
parameters: { layout: "fullscreen", controls: { disable: true } },
|
parameters: { layout: "fullscreen", controls: { disable: true } },
|
||||||
render: () => (
|
render: () => (
|
||||||
<StoryPage>
|
<StoryPage>
|
||||||
<StoryPageHeader title="Title" />
|
<StoryPageHeader title="제목" />
|
||||||
<StorySection title="Overview">
|
<StorySection title="개요">
|
||||||
{withRepresentation(() => <CenterTitleReferenceDemo />)}
|
{withRepresentation(() => <CenterTitleReferenceDemo />)}
|
||||||
</StorySection>
|
</StorySection>
|
||||||
</StoryPage>
|
</StoryPage>
|
||||||
|
|||||||
@@ -35,13 +35,13 @@ function ColorChip({ prefix, stage, values }: { prefix: PalettePrefix; stage: Co
|
|||||||
|
|
||||||
function ColorReferencePage() {
|
function ColorReferencePage() {
|
||||||
const values = useResolvedProperties(colorProperties)
|
const values = useResolvedProperties(colorProperties)
|
||||||
return <ReferencePage title="Hanmac Color">
|
return <ReferencePage title="한맥 컬러">
|
||||||
{palettes.map(palette => <ReferenceSection key={palette.name} title={palette.name} note="">
|
{palettes.map(palette => <ReferenceSection key={palette.name} title={palette.name} note="">
|
||||||
<div className="hm-brand-color-row">{stages.map(stage => <ColorChip key={stage} prefix={palette.prefix} stage={stage} values={values} />)}</div>
|
<div className="hm-brand-color-row">{stages.map(stage => <ColorChip key={stage} prefix={palette.prefix} stage={stage} values={values} />)}</div>
|
||||||
</ReferenceSection>)}
|
</ReferenceSection>)}
|
||||||
</ReferencePage>
|
</ReferencePage>
|
||||||
}
|
}
|
||||||
// The sidebar name is "Hanmac Color"; `id` keeps the existing Story ID (foundation-color--all) stable.
|
// The sidebar name is "한맥 컬러"; `id` keeps the existing Story ID (foundation-color--all) stable.
|
||||||
const meta = { title: "Foundation/Hanmac Color", id: "foundation-color", parameters: { layout: "fullscreen" } } satisfies Meta
|
const meta = { title: "Foundation/한맥 컬러", id: "foundation-color", parameters: { layout: "fullscreen" } } satisfies Meta
|
||||||
export default meta
|
export default meta
|
||||||
export const All: StoryObj<typeof meta> = { name: "Hanmac Color", render: () => <ColorReferencePage /> }
|
export const All: StoryObj<typeof meta> = { name: "한맥 컬러", render: () => <ColorReferencePage /> }
|
||||||
@@ -4,7 +4,15 @@ import { ReferencePage, ReferenceSection } from "./reference"
|
|||||||
|
|
||||||
// Company colors come from the existing OrgChart presets; HEX values are never duplicated here.
|
// Company colors come from the existing OrgChart presets; HEX values are never duplicated here.
|
||||||
// The chip markup and classes are shared with Foundation/Color (foundation.css).
|
// The chip markup and classes are shared with Foundation/Color (foundation.css).
|
||||||
const companies = Object.values(companyColorPresets)
|
// Display order only. PC rows: 한맥기술/삼안 · 장헌/피티씨 · 장헌산업/한라산업개발/바론컨설턴트.
|
||||||
|
// Row starts (jangheon, jangheon-industry) are pinned to column 1 in foundation.css via the section ids; companies not listed follow in preset order.
|
||||||
|
const companyRows = [["hanmac", "saman"], ["jangheon", "ptc"], ["jangheon-industry", "halla", "baron"]]
|
||||||
|
const presetCompanies = Object.values(companyColorPresets)
|
||||||
|
const listedIds = companyRows.flat()
|
||||||
|
const companies = [
|
||||||
|
...companyRows.flatMap(ids => ids.flatMap(id => presetCompanies.filter(company => company.companyId === id))),
|
||||||
|
...presetCompanies.filter(company => !listedIds.includes(company.companyId)),
|
||||||
|
]
|
||||||
|
|
||||||
function CompanyColorChip({ label, hex, main }: { label: string; hex: string; main: boolean }) {
|
function CompanyColorChip({ label, hex, main }: { label: string; hex: string; main: boolean }) {
|
||||||
return <article className="hm-brand-color-chip" data-company-color={hex} data-base={main ? "" : undefined}>
|
return <article className="hm-brand-color-chip" data-company-color={hex} data-base={main ? "" : undefined}>
|
||||||
@@ -14,9 +22,9 @@ function CompanyColorChip({ label, hex, main }: { label: string; hex: string; ma
|
|||||||
}
|
}
|
||||||
|
|
||||||
function CompanyColorsPage() {
|
function CompanyColorsPage() {
|
||||||
return <ReferencePage title="Company Colors">
|
return <ReferencePage title="가족사 컬러">
|
||||||
<div className="hm-company-grid">
|
<div className="hm-company-grid">
|
||||||
{companies.map(company => <ReferenceSection key={company.companyId} title={company.companyName} note="">
|
{companies.map(company => <ReferenceSection key={company.companyId} title={company.companyName} note="" id={`company-${company.companyId}`}>
|
||||||
<div className="hm-brand-color-row" data-company-id={company.companyId}>
|
<div className="hm-brand-color-row" data-company-id={company.companyId}>
|
||||||
<CompanyColorChip label="메인" hex={company.primary} main />
|
<CompanyColorChip label="메인" hex={company.primary} main />
|
||||||
{company.secondary.map((hex, index) => <CompanyColorChip key={hex} label={`서브 ${index + 1}`} hex={hex} main={false} />)}
|
{company.secondary.map((hex, index) => <CompanyColorChip key={hex} label={`서브 ${index + 1}`} hex={hex} main={false} />)}
|
||||||
@@ -25,6 +33,7 @@ function CompanyColorsPage() {
|
|||||||
</div>
|
</div>
|
||||||
</ReferencePage>
|
</ReferencePage>
|
||||||
}
|
}
|
||||||
const meta = { title: "Foundation/Company Colors", parameters: { layout: "fullscreen" } } satisfies Meta
|
// The sidebar name is "가족사 컬러"; `id` keeps the existing Story ID (foundation-company-colors--all) stable.
|
||||||
|
const meta = { title: "Foundation/가족사 컬러", id: "foundation-company-colors", parameters: { layout: "fullscreen" } } satisfies Meta
|
||||||
export default meta
|
export default meta
|
||||||
export const All: StoryObj<typeof meta> = { name: "Company Colors", render: () => <CompanyColorsPage /> }
|
export const All: StoryObj<typeof meta> = { name: "가족사 컬러", render: () => <CompanyColorsPage /> }
|
||||||
Vendored
+3
@@ -41,6 +41,9 @@
|
|||||||
chips up to 3 per row (min 100px each, 3 -> 2 -> 1). Chip size stays at one third even when a company has fewer chips. */
|
chips up to 3 per row (min 100px each, 3 -> 2 -> 1). Chip size stays at one third even when a company has fewer chips. */
|
||||||
.hm-foundation .hm-company-grid { display: grid; gap: 48px 32px; grid-template-columns: repeat(auto-fill, minmax(max(316px, (100% - 64px) / 3), 1fr)); }
|
.hm-foundation .hm-company-grid { display: grid; gap: 48px 32px; grid-template-columns: repeat(auto-fill, minmax(max(316px, (100% - 64px) / 3), 1fr)); }
|
||||||
.hm-foundation .hm-company-grid > .sb-page-section { min-width: 0; margin-top: 0; }
|
.hm-foundation .hm-company-grid > .sb-page-section { min-width: 0; margin-top: 0; }
|
||||||
|
/* Row starts for the 2 / 2 / 3 company layout (한맥·삼안 / 장헌·피티씨 / 장헌산업·한라산업개발·바론컨설턴트). */
|
||||||
|
.hm-foundation .hm-company-grid > #company-jangheon,
|
||||||
|
.hm-foundation .hm-company-grid > #company-jangheon-industry { grid-column-start: 1; }
|
||||||
.hm-foundation .hm-brand-color-row[data-company-id] { display: grid; gap: 8px; overflow: visible; padding-bottom: 0; grid-template-columns: repeat(auto-fill, minmax(max(100px, (100% - 16px) / 3), 1fr)); }
|
.hm-foundation .hm-brand-color-row[data-company-id] { display: grid; gap: 8px; overflow: visible; padding-bottom: 0; grid-template-columns: repeat(auto-fill, minmax(max(100px, (100% - 16px) / 3), 1fr)); }
|
||||||
|
|
||||||
/* Typography: compact specification page. Font Family one line, Font Weight small cards, Specimens as comparison tables
|
/* Typography: compact specification page. Font Family one line, Font Weight small cards, Specimens as comparison tables
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ function LayoutReferencePage() {
|
|||||||
</div>
|
</div>
|
||||||
</ReferencePage>
|
</ReferencePage>
|
||||||
}
|
}
|
||||||
const meta = { title: "Foundation/Layout", parameters: { layout: "fullscreen" } } satisfies Meta
|
// Hidden from the sidebar with the verification tag; the Story ID and direct URL (foundation-layout--all) stay available.
|
||||||
|
const meta = { title: "Foundation/Layout", tags: ["verification"], parameters: { layout: "fullscreen" } } satisfies Meta
|
||||||
export default meta
|
export default meta
|
||||||
export const All: StoryObj<typeof meta> = { name: "Layout", render: () => <LayoutReferencePage /> }
|
export const All: StoryObj<typeof meta> = { name: "Layout", render: () => <LayoutReferencePage /> }
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
/* Department Colors Storybook adapter only. The actual Tunnel sample/stage
|
/* Department Colors Storybook adapter only. The actual Tunnel sample/stage
|
||||||
frame comes from center-stories.css; this sheet never selects employee cards. */
|
frame comes from center-stories.css; this sheet never selects employee cards. */
|
||||||
.hm-orgchart-department-preview .hm-orgchart-department-colors-design {
|
.hm-orgchart-department-preview .hm-orgchart-department-colors-design {
|
||||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||||
gap: var(--sb-center-column-gap);
|
gap: var(--sb-center-column-gap);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,6 +43,12 @@
|
|||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: 960px) {
|
||||||
|
.hm-orgchart-department-preview .hm-orgchart-department-colors-design {
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 760px) {
|
@media (max-width: 760px) {
|
||||||
.hm-orgchart-department-preview .hm-orgchart-department-colors-design {
|
.hm-orgchart-department-preview .hm-orgchart-department-colors-design {
|
||||||
grid-template-columns: minmax(0, 1fr);
|
grid-template-columns: minmax(0, 1fr);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react-vite';
|
|||||||
import { CoreComponentPreview } from './core-component-preview';
|
import { CoreComponentPreview } from './core-component-preview';
|
||||||
import { organizationStylePresets } from '../../packages/org-chart/src/core/style-presets';
|
import { organizationStylePresets } from '../../packages/org-chart/src/core/style-presets';
|
||||||
|
|
||||||
const meta = { title: 'OrgChart/Components/Cards', tags: ['internal'], component: CoreComponentPreview, parameters: { layout: 'padded', controls: { disable: true } } } satisfies Meta<typeof CoreComponentPreview>;
|
const meta = { title: '조직도/Components/Cards', id: 'orgchart-components-cards', tags: ['internal'], component: CoreComponentPreview, parameters: { layout: 'padded', controls: { disable: true } } } satisfies Meta<typeof CoreComponentPreview>;
|
||||||
export default meta;
|
export default meta;
|
||||||
type Story = StoryObj<typeof meta>;
|
type Story = StoryObj<typeof meta>;
|
||||||
function organization(variant: number): Story {
|
function organization(variant: number): Story {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { StrictMode } from 'react';
|
|||||||
import type { Meta, StoryObj } from '@storybook/react-vite';
|
import type { Meta, StoryObj } from '@storybook/react-vite';
|
||||||
import { DepartmentTypographyPreview } from './org-chart-page-previews';
|
import { DepartmentTypographyPreview } from './org-chart-page-previews';
|
||||||
|
|
||||||
const meta = { title: 'OrgChart/Department Colors', component: DepartmentTypographyPreview, parameters: { layout: 'fullscreen', controls: { disable: true } }, decorators: [(Story) => <StrictMode><Story /></StrictMode>] } satisfies Meta<typeof DepartmentTypographyPreview>;
|
const meta = { title: '조직도/부서별 색상', component: DepartmentTypographyPreview, parameters: { layout: 'fullscreen', controls: { disable: true } }, decorators: [(Story) => <StrictMode><Story /></StrictMode>] } satisfies Meta<typeof DepartmentTypographyPreview>;
|
||||||
export default meta;
|
export default meta;
|
||||||
type Story = StoryObj<typeof meta>;
|
type Story = StoryObj<typeof meta>;
|
||||||
export const DepartmentColors: Story = { name: 'Department Colors', parameters: { __id: 'orgchart--department-colors' } };
|
export const DepartmentColors: Story = { name: '부서별 색상', parameters: { __id: 'orgchart--department-colors' } };
|
||||||
@@ -50,7 +50,7 @@ function ExpandedPreview() {
|
|||||||
})), []);
|
})), []);
|
||||||
|
|
||||||
return <StoryPage className="hm-orgchart-story-page hm-orgchart-story-expanded">
|
return <StoryPage className="hm-orgchart-story-page hm-orgchart-story-expanded">
|
||||||
<StoryPageHeader title="Expanded OrgChart" />
|
<StoryPageHeader title="확장 조직도" />
|
||||||
{/* Preview first: the chart sits right under the page title; its own header already names it, so no section header. */}
|
{/* Preview first: the chart sits right under the page title; its own header already names it, so no section header. */}
|
||||||
<div className="hm-orgchart-story hm-orgchart-story-lead">
|
<div className="hm-orgchart-story hm-orgchart-story-lead">
|
||||||
{error ? <p role="alert" className="hm-orgchart-story-note">{error}</p> : null}
|
{error ? <p role="alert" className="hm-orgchart-story-note">{error}</p> : null}
|
||||||
@@ -61,7 +61,7 @@ function ExpandedPreview() {
|
|||||||
</StoryPage>;
|
</StoryPage>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const meta = { title: 'OrgChart/Expanded OrgChart', component: ExpandedPreview, parameters: { layout: 'fullscreen', controls: { disable: true } }, decorators: [(Story) => <StrictMode><Story /></StrictMode>] } satisfies Meta<typeof ExpandedPreview>;
|
const meta = { title: '조직도/확장 조직도', component: ExpandedPreview, parameters: { layout: 'fullscreen', controls: { disable: true } }, decorators: [(Story) => <StrictMode><Story /></StrictMode>] } satisfies Meta<typeof ExpandedPreview>;
|
||||||
export default meta;
|
export default meta;
|
||||||
type Story = StoryObj<typeof meta>;
|
type Story = StoryObj<typeof meta>;
|
||||||
export const ExpandedOrgChart: Story = { name: 'Expanded OrgChart', parameters: { __id: 'orgchart--expanded-org-chart' } };
|
export const ExpandedOrgChart: Story = { name: '확장 조직도', parameters: { __id: 'orgchart--expanded-org-chart' } };
|
||||||
@@ -2,7 +2,7 @@ import { StrictMode } from 'react';
|
|||||||
import type { Meta, StoryObj } from '@storybook/react-vite';
|
import type { Meta, StoryObj } from '@storybook/react-vite';
|
||||||
import { CompositionPreview } from './org-chart-page-previews';
|
import { CompositionPreview } from './org-chart-page-previews';
|
||||||
|
|
||||||
const meta = { title: 'OrgChart/Full Composition', component: CompositionPreview, parameters: { layout: 'fullscreen', controls: { disable: true } }, decorators: [(Story) => <StrictMode><Story /></StrictMode>] } satisfies Meta<typeof CompositionPreview>;
|
const meta = { title: '조직도/전체 구성', component: CompositionPreview, parameters: { layout: 'fullscreen', controls: { disable: true } }, decorators: [(Story) => <StrictMode><Story /></StrictMode>] } satisfies Meta<typeof CompositionPreview>;
|
||||||
export default meta;
|
export default meta;
|
||||||
type Story = StoryObj<typeof meta>;
|
type Story = StoryObj<typeof meta>;
|
||||||
export const FullComposition: Story = { name: 'Full Composition', parameters: { __id: 'orgchart--full-composition' } };
|
export const FullComposition: Story = { name: '전체 구성', parameters: { __id: 'orgchart--full-composition' } };
|
||||||
@@ -3,7 +3,7 @@ import type { Meta, StoryObj } from '@storybook/react-vite';
|
|||||||
import { CoreComponentPreview } from './core-component-preview';
|
import { CoreComponentPreview } from './core-component-preview';
|
||||||
import './org-chart-stories.css';
|
import './org-chart-stories.css';
|
||||||
|
|
||||||
const meta = { title: 'OrgChart/Verification/Overview', component: CoreComponentPreview, tags: ['internal'], parameters: { layout: 'padded', controls: { disable: true } }, decorators: [(Story) => <StrictMode><Story /></StrictMode>] } satisfies Meta<typeof CoreComponentPreview>;
|
const meta = { title: '조직도/Verification/Overview', component: CoreComponentPreview, tags: ['internal'], parameters: { layout: 'padded', controls: { disable: true } }, decorators: [(Story) => <StrictMode><Story /></StrictMode>] } satisfies Meta<typeof CoreComponentPreview>;
|
||||||
export default meta;
|
export default meta;
|
||||||
type Story = StoryObj<typeof meta>;
|
type Story = StoryObj<typeof meta>;
|
||||||
// Literal IDs preserve every existing direct URL after moving the hidden CSF entries.
|
// Literal IDs preserve every existing direct URL after moving the hidden CSF entries.
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { StrictMode } from 'react';
|
|||||||
import type { Meta, StoryObj } from '@storybook/react-vite';
|
import type { Meta, StoryObj } from '@storybook/react-vite';
|
||||||
import { OverviewPreview } from './org-chart-page-previews';
|
import { OverviewPreview } from './org-chart-page-previews';
|
||||||
|
|
||||||
const meta = { title: 'OrgChart/Overview', component: OverviewPreview, parameters: { layout: 'fullscreen', controls: { disable: true } }, decorators: [(Story) => <StrictMode><Story /></StrictMode>] } satisfies Meta<typeof OverviewPreview>;
|
const meta = { title: '조직도/개요', component: OverviewPreview, parameters: { layout: 'fullscreen', controls: { disable: true } }, decorators: [(Story) => <StrictMode><Story /></StrictMode>] } satisfies Meta<typeof OverviewPreview>;
|
||||||
export default meta;
|
export default meta;
|
||||||
type Story = StoryObj<typeof meta>;
|
type Story = StoryObj<typeof meta>;
|
||||||
export const LogoPersonnelSummary: Story = { name: 'Overview', parameters: { __id: 'orgchart--logo-personnel-summary' } };
|
export const LogoPersonnelSummary: Story = { name: '개요', parameters: { __id: 'orgchart--logo-personnel-summary' } };
|
||||||
@@ -19,7 +19,7 @@ export function CompositionPreview() {
|
|||||||
const host = useRef<HTMLElement>(null);
|
const host = useRef<HTMLElement>(null);
|
||||||
useLayoutEffect(() => centerCompositionPreview(host.current!), []);
|
useLayoutEffect(() => centerCompositionPreview(host.current!), []);
|
||||||
return <StoryPage className="hm-orgchart-story-page">
|
return <StoryPage className="hm-orgchart-story-page">
|
||||||
<StoryPageHeader title="Full Composition" />
|
<StoryPageHeader title="전체 구성" />
|
||||||
<StorySection title="고정형 조직도">
|
<StorySection title="고정형 조직도">
|
||||||
<section ref={host} className="hm-orgchart-story hm-orgchart-story-full" aria-label="Full Composition">
|
<section ref={host} className="hm-orgchart-story hm-orgchart-story-full" aria-label="Full Composition">
|
||||||
<OrgChart dataSource={compositionSource} stylesheetUrls={compositionStylesheets} displayMode="readOnly" className="hm-orgchart-story-chart" />
|
<OrgChart dataSource={compositionSource} stylesheetUrls={compositionStylesheets} displayMode="readOnly" className="hm-orgchart-story-chart" />
|
||||||
@@ -30,7 +30,7 @@ export function CompositionPreview() {
|
|||||||
|
|
||||||
export function DepartmentTypographyPreview() {
|
export function DepartmentTypographyPreview() {
|
||||||
return <StoryPage className="hm-orgchart-story-page">
|
return <StoryPage className="hm-orgchart-story-page">
|
||||||
<StoryPageHeader title="Department Colors" />
|
<StoryPageHeader title="부서별 색상" />
|
||||||
<StorySection title="조직별 컬러 · 카드 미리보기">
|
<StorySection title="조직별 컬러 · 카드 미리보기">
|
||||||
<div className="hm-orgchart-story-preview"><CoreComponentPreview kind="DepartmentColors" presentation="design" /></div>
|
<div className="hm-orgchart-story-preview"><CoreComponentPreview kind="DepartmentColors" presentation="design" /></div>
|
||||||
</StorySection>
|
</StorySection>
|
||||||
@@ -39,7 +39,7 @@ export function DepartmentTypographyPreview() {
|
|||||||
|
|
||||||
export function OverviewPreview() {
|
export function OverviewPreview() {
|
||||||
return <StoryPage className="hm-orgchart-story-page">
|
return <StoryPage className="hm-orgchart-story-page">
|
||||||
<StoryPageHeader title="Overview" />
|
<StoryPageHeader title="개요" />
|
||||||
<StorySection title="관계사·회사 로고">
|
<StorySection title="관계사·회사 로고">
|
||||||
<div className="hm-orgchart-story-preview"><CoreComponentPreview kind="Logo" hideLogoHeading /></div>
|
<div className="hm-orgchart-story-preview"><CoreComponentPreview kind="Logo" hideLogoHeading /></div>
|
||||||
</StorySection>
|
</StorySection>
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ import { CoreComponentPreview } from "./core-component-preview"
|
|||||||
// and automatic Raw Count. Acquired logo mapping stays in the shared Core/asset
|
// and automatic Raw Count. Acquired logo mapping stays in the shared Core/asset
|
||||||
// layer; Stories do not duplicate summary UI or add employee data fields.
|
// layer; Stories do not duplicate summary UI or add employee data fields.
|
||||||
const meta = {
|
const meta = {
|
||||||
title: "OrgChart/Components/Summaries",
|
title: "조직도/Components/Summaries",
|
||||||
|
id: "orgchart-components-summaries",
|
||||||
tags: ["internal"],
|
tags: ["internal"],
|
||||||
component: CoreComponentPreview,
|
component: CoreComponentPreview,
|
||||||
args: { kind: "PositionSummary" },
|
args: { kind: "PositionSummary" },
|
||||||
|
|||||||
@@ -167,7 +167,8 @@ function MultipleContent() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const meta = {
|
const meta = {
|
||||||
title: "OrgChart/Content",
|
title: "조직도/콘텐츠",
|
||||||
|
id: "orgchart-content",
|
||||||
tags: ["internal"],
|
tags: ["internal"],
|
||||||
component: OrgChart,
|
component: OrgChart,
|
||||||
parameters: {
|
parameters: {
|
||||||
@@ -190,28 +191,28 @@ export default meta
|
|||||||
type Story = StoryObj<typeof meta>
|
type Story = StoryObj<typeof meta>
|
||||||
|
|
||||||
export const BasicOrganization: Story = {
|
export const BasicOrganization: Story = {
|
||||||
name: "Basic Organization",
|
name: "기본 조직도",
|
||||||
render: args => <Content note="HTML 공통 v1: 조직 3개, 고유 직원 4명 (Root 1 / Team A 2 / Team B 1). 정책 미주입 시 원본 집계만 표시하고 업무상 총원은 미확정입니다."><OrgChart {...args} className="hm-orgchart-story-chart" /></Content>,
|
render: args => <Content note="HTML 공통 v1: 조직 3개, 고유 직원 4명 (Root 1 / Team A 2 / Team B 1). 정책 미주입 시 원본 집계만 표시하고 업무상 총원은 미확정입니다."><OrgChart {...args} className="hm-orgchart-story-chart" /></Content>,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const OrganizationHierarchy: Story = {
|
export const OrganizationHierarchy: Story = {
|
||||||
name: "Organization Hierarchy",
|
name: "조직 계층",
|
||||||
args: { dataSource: hierarchySource },
|
args: { dataSource: hierarchySource },
|
||||||
render: args => <Content note="공통 v1의 parentId 계층·조직장과 연결선을 확인합니다. showEmployees/showSummary만 숨기며 원본 소속과 집계는 유지합니다. 하위 조직 접기/펼치기는 Core 버튼의 키보드 Enter/Space로 실행합니다."><OrgChart {...args} className="hm-orgchart-story-chart" /></Content>,
|
render: args => <Content note="공통 v1의 parentId 계층·조직장과 연결선을 확인합니다. showEmployees/showSummary만 숨기며 원본 소속과 집계는 유지합니다. 하위 조직 접기/펼치기는 Core 버튼의 키보드 Enter/Space로 실행합니다."><OrgChart {...args} className="hm-orgchart-story-chart" /></Content>,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const EmployeeCards: Story = {
|
export const EmployeeCards: Story = {
|
||||||
name: "Employee Cards",
|
name: "직원 카드",
|
||||||
args: { dataSource: employeeSource },
|
args: { dataSource: employeeSource },
|
||||||
render: args => <Content note="공통 v1에서 긴 조직명·직원명·직위·직책만 파생한 가상 데이터입니다. null 프로필 이미지는 Core의 첫 글자 대체 표시를 사용합니다. 재직·파견·휴직 상태와 조직장 구분은 동일 Core 노드 UI입니다."><OrgChart {...args} className="hm-orgchart-story-chart" /></Content>,
|
render: args => <Content note="공통 v1에서 긴 조직명·직원명·직위·직책만 파생한 가상 데이터입니다. null 프로필 이미지는 Core의 첫 글자 대체 표시를 사용합니다. 재직·파견·휴직 상태와 조직장 구분은 동일 Core 노드 UI입니다."><OrgChart {...args} className="hm-orgchart-story-chart" /></Content>,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const OrganizationSummary: Story = {
|
export const OrganizationSummary: Story = {
|
||||||
name: "Organization Summary",
|
name: "조직 요약",
|
||||||
render: args => <Content note="공통 v1 원본 직원 전체 4명, 직접 인원 Root 1 / Team A 2 / Team B 1. 하위 포함 인원은 서로 합산하지 않습니다. 승인 집계 정책을 임의 생성하지 않으며 업무상 총원은 정책 승인 필요로 표시합니다."><OrgChart {...args} className="hm-orgchart-story-chart" /></Content>,
|
render: args => <Content note="공통 v1 원본 직원 전체 4명, 직접 인원 Root 1 / Team A 2 / Team B 1. 하위 포함 인원은 서로 합산하지 않습니다. 승인 집계 정책을 임의 생성하지 않으며 업무상 총원은 정책 승인 필요로 표시합니다."><OrgChart {...args} className="hm-orgchart-story-chart" /></Content>,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const JSONDataUpdate: Story = { name: "JSON Data Update", render: () => <DataUpdateContent /> }
|
export const JSONDataUpdate: Story = { name: "JSON Data Update", render: () => <DataUpdateContent /> }
|
||||||
export const LoadingEmptyError: Story = { name: "Loading / Empty / Error", render: () => <LoadingContent /> }
|
export const LoadingEmptyError: Story = { name: "로딩 / 비어 있음 / 오류", render: () => <LoadingContent /> }
|
||||||
export const ResponsiveContainer: Story = { name: "Responsive Container", render: () => <ResponsiveContent /> }
|
export const ResponsiveContainer: Story = { name: "Responsive Container", render: () => <ResponsiveContent /> }
|
||||||
export const MultipleInstances: Story = { name: "Multiple Instances", render: () => <MultipleContent /> }
|
export const MultipleInstances: Story = { name: "Multiple Instances", render: () => <MultipleContent /> }
|
||||||
@@ -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