feat(org-chart): implement expandable layout and multi-company navigation
Integrate P5-1B through P5-4 shared Core layout, collapse, zoom/pan/fit, company roots and keyboard accessibility. Include React/HTML examples, virtual 20/100/300 employee fixtures, focused checks and the additive data contract.
This commit is contained in:
1 parent
a92913d6bc
commit
064517ec46
30 files changed
+7532
-183
No files matched your search
@@ -1,35 +1,50 @@
|
||||
import { StrictMode, useId, useState } from 'react';
|
||||
import type { Meta, StoryObj } from '@storybook/react-vite';
|
||||
import { Label, Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@hm-design/ui';
|
||||
import { Button, Checkbox, Label, Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@hm-design/ui';
|
||||
import { OrgChart } from '@hm-design/org-chart-react';
|
||||
import { companyColorPresets, getCompanyColorPreset, JsonOrgChartDataSource } from '@hm-design/org-chart';
|
||||
import { companyColorPresets, JsonOrgChartDataSource, type OrgChartConnectionDirection } from '@hm-design/org-chart';
|
||||
import engineUrl from '@hm-design/org-chart/engine.css?url';
|
||||
import coreUrl from '@hm-design/org-chart/style.css?url';
|
||||
import expandedFixture from '../../examples/org-chart/data-expanded.json';
|
||||
import small from '../../examples/org-chart/data-expanded-small.json';
|
||||
import medium from '../../examples/org-chart/data-expanded-medium.json';
|
||||
import large from '../../examples/org-chart/data-expanded-large.json';
|
||||
import './org-chart-stories.css';
|
||||
|
||||
const expandedSource = new JsonOrgChartDataSource(expandedFixture);
|
||||
const fixtures = { small, medium, large };
|
||||
const sources = { small: new JsonOrgChartDataSource(small), medium: new JsonOrgChartDataSource(medium), large: new JsonOrgChartDataSource(large) };
|
||||
const compositionStylesheets = { engine: engineUrl, core: coreUrl };
|
||||
const topNodes = { ptc: { title: '기술총괄', name: '가상책임자 5' } };
|
||||
const connections: Record<string, OrgChartConnectionDirection> = Object.fromEntries(large.organizations.map((org, index) => [org.id, (['top', 'left', 'right'] as const)[index % 3]! ]));
|
||||
|
||||
/** P5-1A establishes the Core rendering boundary and initial 1Depth groups.
|
||||
* Automatic hierarchy placement belongs to P5-1B; this host owns no layout. */
|
||||
/** Company state is passed to the shared Core without filtering/mutating JSON. */
|
||||
function ExpandedPreview() {
|
||||
const id = useId();
|
||||
const [companyId, setCompanyId] = useState('hanmac');
|
||||
const preset = getCompanyColorPreset(companyId);
|
||||
return <section className="hm-orgchart-story" aria-label="Expanded OrgChart · P5-1A">
|
||||
<p className="hm-orgchart-story-note"><strong>P5-1A · 확장형 조직도 초기 화면</strong> — 대표자와 예시 1Depth 그룹, 기존 카드 및 회사 컬러 프리셋을 표시합니다. 자동 계층 배치·재계산은 P5-1B, 접기·펼치기와 Zoom/Pan은 후속 작업입니다. 모든 직원 정보는 가상 데이터입니다.</p>
|
||||
const [size, setSize] = useState<keyof typeof fixtures>('small');
|
||||
const [selected, setSelected] = useState<string[] | undefined>();
|
||||
const companies = fixtures[size].affiliations;
|
||||
const visible = selected ?? companies.map(company => company.id);
|
||||
return <section className="hm-orgchart-story" aria-label="Expanded OrgChart · P5-4">
|
||||
<p className="hm-orgchart-story-note"><strong>확장형 조직도</strong> — 회사별 독립 조직 · 타이틀 클릭으로 접기·펼치기 · 확대 및 드래그 이동. 모든 직원은 가상 데이터입니다.</p>
|
||||
<div className="hm-orgchart-story-controls">
|
||||
<div className="hm-orgchart-story-instance">
|
||||
<Label id={`${id}-company-label`} htmlFor={`${id}-company`}>회사별 컬러 테마</Label>
|
||||
<Select value={companyId} onValueChange={setCompanyId}>
|
||||
<SelectTrigger id={`${id}-company`} aria-labelledby={`${id}-company-label`} size="medium"><SelectValue /></SelectTrigger>
|
||||
<SelectContent>{Object.values(companyColorPresets).map(company => <SelectItem key={company.companyId} value={company.companyId}>{company.companyName}</SelectItem>)}</SelectContent>
|
||||
<Label id={`${id}-data-label`} htmlFor={`${id}-data`}>가상 데이터</Label>
|
||||
<Select value={size} onValueChange={value => { setSize(value as keyof typeof fixtures); setSelected(undefined); }}>
|
||||
<SelectTrigger id={`${id}-data`} aria-labelledby={`${id}-data-label`} size="medium"><SelectValue /></SelectTrigger>
|
||||
<SelectContent><SelectItem value="small">Small · 20명</SelectItem><SelectItem value="medium">Medium · 100명</SelectItem><SelectItem value="large">Large · 300명</SelectItem></SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<Button variant="secondary" appearance="outline" onClick={() => setSelected(undefined)}>전체 회사</Button>
|
||||
<Button variant="secondary" appearance="outline" onClick={() => setSelected([])}>선택 해제</Button>
|
||||
</div>
|
||||
{preset && <p className="hm-orgchart-story-note" aria-live="polite">{preset.companyName} · Main {preset.primary} · 보조 컬러 {preset.secondary.length ? preset.secondary.join(', ') : '등록 없음'}</p>}
|
||||
<OrgChart dataSource={expandedSource} stylesheetUrls={compositionStylesheets} displayMode="readOnly" layoutMode="auto" companyId={companyId} className="hm-orgchart-story-chart" />
|
||||
<fieldset className="hm-orgchart-story-companies">
|
||||
<legend>표시할 회사 · 복수 선택 가능</legend>
|
||||
{companies.map(company => <div key={company.id} className="hm-orgchart-story-company">
|
||||
<Checkbox id={`${id}-${company.id}`} checked={visible.includes(company.id)} onCheckedChange={checked => setSelected(checked === true ? [...visible, company.id] : visible.filter(value => value !== company.id))} />
|
||||
<Label htmlFor={`${id}-${company.id}`}>{company.name}</Label>
|
||||
<span className="hm-orgchart-story-note">{companyColorPresets[company.id as keyof typeof companyColorPresets].primary}</span>
|
||||
</div>)}
|
||||
</fieldset>
|
||||
<OrgChart dataSource={sources[size]} stylesheetUrls={compositionStylesheets} displayMode="readOnly" layoutMode="auto" selectedCompanyIds={selected} companyTopNodes={topNodes} employeeConnections={connections} className="hm-orgchart-story-chart" />
|
||||
</section>;
|
||||
}
|
||||
|
||||
|
||||
@@ -37,6 +37,10 @@ body:has(.hm-orgchart-story-full) { background: #F5F8FB; }
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.hm-orgchart-story-companies { display: flex; flex-wrap: wrap; gap: 1rem; border: 0; padding: 0; min-width: 0; }
|
||||
.hm-orgchart-story-companies legend { margin-bottom: 0.5rem; }
|
||||
.hm-orgchart-story-company { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
|
||||
|
||||
.hm-orgchart-story-chart {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user