# OrgChart 조합 패턴 모든 패턴은 공개 Export만 씁니다([COMPONENT_CATALOG.md](./COMPONENT_CATALOG.md)). 예시 데이터는 가상이며 회사·분기·인원을 기본값으로 쓰지 않습니다. ## 0. 어떤 패턴을 고를까 | 요청 | 패턴 | |---|---| | 조직도만 기존 페이지에 삽입 | 1. Chart Only (Auto) | | 조직도가 들어간 새 페이지 전체 | 2. Full Page (Web + OrgChart) | | 직원 정보 팝업을 Web 디자인으로 | 3. HM Dialog 팝업 | | 다른 디자인 시스템의 팝업 | 4. 팝업 어댑터 | | 자연어·CSV·Excel → 데이터 | 5. 데이터 만들기 | | Web·SW 등 다른 영역과 혼합 | 2. Full Page의 영역 분리 원칙. SW 화면 + 조직도는 NOT VERIFIED([AI_USAGE.md §0](./AI_USAGE.md)) | | 기존 Workdesk(고정) 구성 | `layoutMode="fixed"`(기본), `displayMode="readOnly"` 등 — Chart Only 불가 | ## 1. Chart Only (Auto) — 조직도 영역만 **React** ```tsx import { useMemo } from "react" import { OrgChart } from "@hm-design/org-chart-react" import { JsonOrgChartDataSource, type OrgChartDataset } 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" const stylesheetUrls = { engine: engineUrl, core: coreUrl } export function OrganizationChart({ data }: { data: OrgChartDataset }) { const dataSource = useMemo(() => new JsonOrgChartDataSource(data), [data]) return ( console.error(error)} /> ) } ``` **Vanilla (React 없음)** ```ts import { createOrgChart, JsonOrgChartDataSource, type OrgChartDataset } 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" export async function mountOrganizationChart(container: HTMLElement, data: OrgChartDataset): Promise<() => void> { const chart = createOrgChart({ container, dataSource: new JsonOrgChartDataSource(data), stylesheetUrls: { engine: engineUrl, core: coreUrl }, layoutMode: "auto", presentationMode: "chart-only", dragDropEnabled: false, }) try { await chart.mount() if (chart.getState().status !== "ready") throw new Error("조직도를 표시할 수 없습니다.") return () => chart.destroy() } catch (error) { chart.destroy() throw error } } ``` - 이 컴포넌트에 제목·통계·버튼 바·설명·바깥 회색 영역을 **추가하지 않습니다.** 호스트 페이지의 기존 UI도 바꾸지 않습니다. - 독립 미리보기가 필요하면 흰색 조직도 영역을 최상위로 두는 별도 entry만 만듭니다(`html, body, #root { height: 100%; margin: 0 }` 같은 페이지 설정은 **미리보기 entry의 index.html/CSS**에만). - 레이아웃 방향 등 요청된 조직도 내부 옵션만 추가: `autoLayout={{ type: "lr" }}`, `selectedCompanyIds`, `employeePopup` … ## 2. Full Page (Web + OrgChart) 영역: 페이지·Navigation·제목·필터 = Web(`@hm-design/ui`, `node_modules/@hm-design/ui/ai/` 규칙), 조직도 영역 = OrgChart. ```tsx import { useId, useMemo, useState } from "react" import { Card, CardContent, Label, PageHeader, PageHeaderContent, PageHeaderTitle, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@hm-design/ui" import { OrgChart } from "@hm-design/org-chart-react" import { JsonOrgChartDataSource, companyColorPresets, type CompanyId, type OrgChartDataset } 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" const stylesheetUrls = { engine: engineUrl, core: coreUrl } export function OrganizationPage({ data, companyIds }: { data: OrgChartDataset; companyIds: CompanyId[] }) { const selectId = useId() const [company, setCompany] = useState<"all" | CompanyId>("all") const dataSource = useMemo(() => new JsonOrgChartDataSource(data), [data]) const selected = useMemo(() => (company === "all" ? undefined : [company]), [company]) return (
조직도
) } ``` ```css /* 소비 레이아웃만. 조직도 안쪽 디자인은 건드리지 않음 */ .app-org-page { display: flex; flex-direction: column; gap: 16px; } .app-org-canvas { height: 640px; margin-top: 16px; } ``` - 위 회사 선택 Select는 **요청된 경우의 예**입니다. 요청에 없는 필터·통계·설명을 넣지 않습니다. - Full Page의 Navigation(GNB/LNB/DASHBOARD 중 하나)과 Web 규칙은 Web 문서를 따릅니다. - Web 토큰(`--hm-sem-*`)을 조직도에 넣지 않습니다. 조직도 색을 맞춰야 하면 [DESIGN_RULES.md](./DESIGN_RULES.md) §3의 `--hm-orgchart-*` 입력으로 따로 연결합니다(자동 동기화 없음). - `presentationMode`를 생략하면 Auto 기본 화면(조직도 내부 제목·요약·툴바 포함)입니다. Full Page에서 그 내장 UI가 요청 UI와 겹치면 `presentationMode="chart-only"`로 조직도 영역만 쓰고 바깥 UI는 Web으로 만듭니다. ## 3. HM Dialog 팝업 (Web Dialog) ```tsx import { OrgChart } from "@hm-design/org-chart-react/hm-dialog" ``` - `@hm-design/ui` 0.1.0 설치 + 앱 CSS 진입점에 Web `tailwind.css`·`theme.css` 필요(`/hm-dialog`가 CSS를 자동 import하지 않음). - `@hm-design/ui` 설치만으로 팝업이 바뀌지 않습니다. Entry와 `employeePopup`을 모두 명시합니다. Fixed는 항상 Core 팝업입니다. ## 4. 팝업 어댑터 (다른 디자인 시스템) ```tsx import { OrgChart, type OrgChartEmployeePopupProps } from "@hm-design/org-chart-react" function MyEmployeePopup({ selection, onClose }: OrgChartEmployeePopupProps) { if (!selection) return null // selection.employee / selection.context를 읽기만 한다. 모달 접근성·Escape·context.trigger로 Focus 복귀는 어댑터 책임. return
…
} ``` `onEmployeeClick`을 주면 어떤 팝업 설정보다 우선합니다. ## 5. 데이터 만들기 ```ts import { validateOrgChartDataset, type OrgChartDataset } from "@hm-design/org-chart" const data: OrgChartDataset = { schemaVersion: "0.1-draft", organizations: [ { id: "hanmac-root", companyId: "hanmac", parentId: null, name: "한맥기술", type: "root", leaderId: "e-ceo", displayOrder: 0 }, { id: "hanmac-office-1", parentId: "hanmac-root", name: "1본부", type: "office", leaderId: "e-1", displayOrder: 0 }, { id: "hanmac-team-1", parentId: "hanmac-office-1", name: "1팀", type: "team", leaderId: null, displayOrder: 0 }, ], employees: [ { id: "e-ceo", organizationId: "hanmac-root", affiliationId: "hanmac", name: "가상 대표", position: "대표이사", jobTitle: null, profileImage: null, workStatus: "active" }, { id: "e-1", organizationId: "hanmac-office-1", affiliationId: "hanmac", name: "가상 본부장", position: "본부장", jobTitle: null, profileImage: null, workStatus: "active" }, { id: "e-2", organizationId: "hanmac-team-1", affiliationId: "hanmac", name: "가상 사원", position: "사원", jobTitle: null, profileImage: null, workStatus: "active" }, ], affiliations: [{ id: "hanmac", name: "한맥기술" }], metadata: { asOfDate: "2026-10-19", dataVersion: "example-1" }, } const checked = validateOrgChartDataset(data) if (!checked.valid) throw new Error(JSON.stringify(checked.issues)) ``` - 필드는 위가 전부입니다(알 수 없는 필드는 Validator가 거절). 이름 규칙: `displayOrder`, `jobTitle`, `profileImage`, `workStatus`. - 업무 명칭 "본부"는 `name`에 두고 `type: "office"`로 대응했습니다 — 이런 대응은 결과 보고에 적습니다. - 회사 추가 = `parentId: null`인 Root를 하나 더(+ 그 회사 `affiliations` 항목). 가짜 상위 조직 금지. - 검증 후 보고: 요청 대비 회사 수, 조직 수, Depth, 고유 직원 수(대표·조직장 포함 여부), 조직별 직속/하위 포함 인원.