feat(org-chart): finalize chart features and AI generation rules

This commit is contained in:
shkim4 committed 2026-10-13 14:39:42 +09:00
1 parent 7614507583
commit d0828a7ff4
80 files changed
+8152 -221

No files matched your search

+136 -8
View File
@@ -1,4 +1,4 @@
# Expanded OrgChart 사용 및 스타일 — P5-4
# Expanded OrgChart 사용 및 스타일 — P7
2026-10-12 사용자 변경 요구사항으로 **현재 확장형 카드 외형과 크기를 유지**한다.
`Group 3391.svg`, `Group 1171288761.svg` 검색·재현·용도 배정은 이번 완료 조건에서 제외한다.
@@ -42,7 +42,8 @@ console.log(chart.getViewportState(), getCompanyColorPreset('hanmac')?.primary);
`JsonOrgChartDataSource`는 인라인 JSON 또는 호스트가 제공하는 HTTP(S)/상대 URL을 받는다.
URL을 사용할 경우 `new JsonOrgChartDataSource('./data-expanded-large.json')`처럼 지정한다.
file:// Fetch는 지원하지 않는다. 이번 작업에 외부 API·서버·DB 구현은 추가하지 않았다.
현재 JSON 예시는 Small 20명/3회사, Medium 100명/5회사, Large 300명/7회사다.
기존 JSON 예시는 Small 20명/3회사, Medium 100명/5회사, Large 300명/7회사다.
P7의 7회사 합산·회사별 선택은 Large를 기반으로 아래 공개 helper를 사용한다.
## React
@@ -72,8 +73,9 @@ export function ExpandedExample() {
위 native 버튼은 React 계약만 설명하는 최소 예시다. HM 화면에서는 실제 기존
`Button`, `Checkbox`, `Label`, `Select`를 조합한다. Expanded Story가 그 사용 예다.
Source·옵션 객체를 안정적으로 유지한다. Core 생성 옵션(모드·TopNode·연결 방향·Zoom 설정)을
Source·옵션 객체를 안정적으로 유지한다. Core 생성 옵션(layoutMode·TopNode·연결 방향·Zoom 범위)을
변경하면 React는 handle을 다시 생성한다. Vanilla는 새 handle을 생성한다.
`autoLayout`·`interactions`·회사 선택·팝업 선택은 기존 인스턴스에서 갱신한다.
회사 선택은 기존 setter/React prop으로 변경하며 원본 JSON과 parentId를 수정하지 않는다.
React에는 별도 레이아웃 알고리즘이나 imperative ref API가 없다.
@@ -95,7 +97,9 @@ Employee 관계를 유지한다. `companyId` 생성 옵션은 기존 JSON의 회
표의 값은 기존 `getCompanyColorPreset(companyId)`에서 조회한다. 카드에 중복 하드코딩하지 않는다.
타이틀은 조직 회사, 카드 강조색은 알려진 직원 affiliation(미등록 시 조직 회사)의 프리셋을 쓴다.
삼안·장헌산업 타이틀은 기존 짙은 중립 텍스트 정책을 유지한다.
현재 사용자 확정에 따라 모든 회사 타이틀은 normal/hover/focus/접힘/펼침에서 흰색을 유지한다.
이는 P7 시작 전 기존 미커밋 변경을 보존한 결과다. 삼안·장헌산업 흰 글자의 대비 진단은
접근성 승인으로 재분류하지 않으며, 이번 기능 확장으로 메인 컬러를 바꾸지 않는다.
`companyTopNodes`는 `employeeId`, `title`, `name`, `profileImage`, `organizationName`,
`display: 'card' | 'titleOnly' | 'hidden'`을 지원한다. 생략 시 Root의 leaderId를 사용하며,
@@ -104,7 +108,8 @@ employeeId는 그 Root에 소속된 기존 직원이어야 한다. null은 최
`employeeConnections`의 지원 값은 `top | left | right`다. Depth를 기준으로 정하지 않는다.
직원 연결선의 실제 도착점과 같은 면에만 회사 Border를 표시하며, 조직장은 타이틀 아래
상단 연결을 사용한다. 다른 면은 중립, 강조는 inset이라 외형 크기가 변하지 않는다.
TB/Hybrid에서는 상단 연결을 사용한다. LR은 조직장·일반 직원 모두 왼쪽에서 연결되며
직원별 실제 도착 면과 왼쪽 Border가 일치한다. 다른 면은 중립, 강조는 inset이라 외형 크기가 변하지 않는다.
연결 없는 카드에 임의로 accent를 붙이는 별도 `none` API는 없다. 현재 Auto presenter의
렌더링된 카드는 모두 타이틀 또는 이전 카드/분기 rail과 연결된다.
@@ -116,7 +121,7 @@ employeeId는 그 Root에 소속된 기존 직원이어야 한다. null은 최
그룹 링크는 별도 아이콘으로 페이지 이동과 토글 동작을 분리한다.
공개 프로그래밍 방식 collapse setter는 제공하지 않는다.
직원 카드는 click·Enter·Space로 공통 직원 정보 dialog를 연다. dialog는 변환 forest 밖의
Vanilla 직원 카드는 click·Enter·Space로 공통 직원 정보 dialog를 연다. dialog는 변환 forest 밖의
native top layer에 표시되므로 Zoom과 Stage overflow의 영향을 받지 않는다.
ESC/닫기 버튼과 focus trap·카드 focus 복귀를 제공한다.
`onEmployeeClick(employee, context)` 지정 시 기본 팝업 대신 callback을 호출하며
@@ -162,6 +167,129 @@ CSS 자산을 만들고 그 URL을 `stylesheetUrls.core`로 제공할 수 있다
크기 변경 시 기존 ResizeObserver가 article의 실측 높이와 실제 endpoint를 재계산한다.
향후 변경에는 Fit·형제/하위 트리 겹침·텍스트 overflow를 다시 확인해야 한다.
이번 P5-4는 기본 geometry를 바꾸지 않으며 대용량 성능의 과거 수치를 재측정하지 않는다.
P7도 기본 카드 geometry를 바꾸지 않는다. LR/Hybrid 300명과 2,100명의 실측은
과거 P5/P6 성능과 분리하여 P7 보고서에 기록한다.
검증 범위와 실행 URL·캡처는 [P5-4 보고서](implementation-report-p5-4.md)를 참고한다.
과거 검증은 [P5-4 보고서](implementation-report-p5-4.md), P7 변경과 실측은
[P7 보고서](implementation-report-p7.md)를 참고한다.
## P7 데이터·레이아웃·독립 옵션
```js
import { createOrgChartExampleDataset } from '@hm-design/org-chart';
import template from './data-expanded-large.json';
const dataset = createOrgChartExampleDataset(template, {
scope: 'per-company', // 'total'이면 전체 7회사 합산 employeeCount
employeeCount: 300, // 20 | 100 | 300
companyIds: ['hanmac', 'saman'], // 생략하면 7회사, 이 예시는 600명
});
const chart = createOrgChart({
...configuredBaseOptions,
dataSource: new JsonOrgChartDataSource(dataset),
layoutMode: 'auto',
autoLayout: { type: 'lr', initialVisibleDepth: 'all' },
interactions: { collapse: false, zoom: true, pan: false },
});
await chart.mount();
await chart.setAutoLayout({ type: 'hybrid', hybridStartDepth: 3 });
await chart.setInteractions({ collapse: true }); // 다른 설정은 그대로
```
총량 모드는 회사별 가중 분배 후 선택한 회사만 필터링하며 재분배하지 않는다.
회사별 모드는 선택 회사마다 정확히 N명, 전체 7회사 × 300명 = 2,100명이다.
ID는 deterministic 가상 namespace로 생성하며 기존 직원 기록·조직 ID·부모 관계를 재사용한다.
원본은 변경하지 않고 반환 snapshot은 deep freeze한다. helper의 빈 회사 선택은 유효한
조직 데이터가 될 수 없어 거절하며, 화면의 빈 선택은 `setSelectedCompanies([])`로 표시한다.
| 옵션 | TB | LR | Hybrid | 제약 및 전환 |
|---|---|---|---|---|
| 레이아웃 `type` | 지원 | 지원 | 지원 | 다른 기능을 켤 필요 없음 |
| `hybridStartDepth: 2/3/4` | 적용 안 함 | 적용 안 함 | 지원 | Root=1Depth, 기본 3 |
| `hybridOrganizationIds` | 지정 그룹 자손 세로 배치 | 조합 거절 | 조합 거절 | LR/Hybrid 전환 전 목록 해제; Story는 자동 해제·disabled |
| `initialVisibleDepth: 1/2/3/'all'` | 지원 | 지원 | 지원 | 기본 all; 값을 바꾸면 접힘 상태를 초기 정책으로 재설정 |
| `interactions.collapse/zoom/pan` | 각각 지원 | 각각 지원 | 각각 지원 | 각각 기본 true; false도 다른 기능 사용을 막지 않음 |
| 회사 선택·직원 팝업 | 지원 | 지원 | 지원 | 특정 레이아웃·Zoom·Pan·접기 사용을 요구하지 않음 |
| Compact | 미지원 | 미지원 | 미지원 | 엔진 compact DOM/140×50 grid가 기존 HM 카드·접기 계약과 충돌 |
TB는 기존 경로를 유지한다. LR은 실제 dabeng `direction: 'l2r'`와 재귀 hierarchy를 사용한다.
원본 `.title/.content` 전용 회전 CSS는 HM 복합 article에 맞지 않아 Auto 범위에서
회전을 해제하고 부모 좌측·자식 우측·형제 세로 배치를 구성한다. Hybrid는 실제
`verticalLevel`/그룹 `hybrid`를 사용한다. 측정한 부모·자식 좌표로 연결선을 갱신하며
Fixed의 CSS·엔진 옵션·기존 native 팝업은 보존한다.
접기·레이아웃 전환은 유효한 조직 ID의 접힘 상태를 유지한다. 데이터·회사·레이아웃
전환은 표시된 카드·타이틀·연결선 전체 경계를 다시 측정하여 가로 중앙을 갱신한다.
자동 배치는 현재 배율을 유지하며 큰 조직도를 임의로 축소하지 않는다. 사용자가 Pan·Zoom·
100% 복원 또는 화면 밖 focus 노출로 이동한 뒤에는 데이터·회사·레이아웃 전환과 Resize에서도
배율·이동 위치를 보존한다. 명시적 Fit to View는 전체 표시 영역을 축소해 담고 가로·세로 중앙에
배치하며 자동 중앙 배치를 다시 허용한다. 개별 interaction/popup 변경은 기존 viewport와
접힘 상태를 보존한다. Pan을 꺼도 키보드로 화면 밖 카드에 도달하면 배율 유지·focus
노출을 제공한다. Fit/100% 복원은 Zoom/Pan 사용 여부와 독립적으로 가능하다.
2,100명 전체 Fit은 개요용으로 작게 축소되므로 카드 읽기에는 Zoom 또는 초기 Depth를 사용한다.
React에서 `employeePopup="hm-dialog"`는 실제 공개 HM Dialog·Avatar·Button을 사용한다.
`'core'`가 기본, `'none'`은 카드의 클릭·button 의미를 제거하되 키보드 이동용 초점은 유지한다. `onEmployeeClick`을
지정하면 선택 모드와 관계없이 callback이 우선한다. HM 모드는
`@hm-design/ui@0.1.0` optional peer와 호스트 `index.css → hm-theme.css` 연결이 필요하다.
Core/Vanilla에는 React/HM 의존성을 추가하지 않았다. peer 없는 호스트의 모든 bundler
조합 및 fresh install은 별도 미검증이다.
```tsx
<OrgChart dataSource={source} stylesheetUrls={stylesheets} layoutMode="auto"
autoLayout={{ type: 'lr', initialVisibleDepth: 2 }}
interactions={{ collapse: false, zoom: true, pan: false }}
employeePopup="hm-dialog" selectedCompanyIds={['hanmac', 'saman']} />
```
## P9.3 브라우저 소속 변경
P9.4의 공통 Core/React 옵션 `dragDropEnabled?: boolean`은 기본 OFF다. 아래 소속 변경은
ON에서 허용한다. React는 `dragDropEnabled={true}`, Vanilla는 생성 옵션 또는
`handle.setDragDropEnabled(true)`로 활성화한다. `handle.getDragDropEnabled()`로
현재 설정을 조회한다. OFF의 `handle.move()`는 `EDITING_DISABLED`를 반환하며 포인터와
Alt+M도 시작하지 않는다. 전환 시 진행 중 이동·Pointer capture·Drop 강조를 취소하고
이미 완료한 이동을 보존한다. 설정만 바꿀 때 데이터 재요청·재생성·레이아웃 갱신은 없다.
직원 정보, 그룹 접기·펼치기, Zoom/Pan/Fit은 기존 독립 설정을 따른다. Fixed는 변경하지 않는다.
Expanded Story의 데이터 범위 UI는 제거했고 예시 직원 수20/100/300명은 회사별 기준만
표시한다. 전체 합산 `createOrgChartExampleDataset` 공개 helper 계약은 유지한다.
회사 선택은 즉시 표시를 갱신하고 숨김·복귀에도 완료한 이동을 유지한다. 직원 수는 기존
**데이터 적용**으로 새 예시를 생성하며 임시 이동과 새 Fixture를 섞지 않는다. 초기 설정은
7개 회사×20명=140명, 선택2개×100명=200명, 선택7개×300명=2,100명이다.
Expanded Story의 외곽 미리보기는 실제 Center Storybook 공통 카드 CSS의
`--sb-center-card-background` / `--sb-center-card-border` / `--sb-center-card-radius`를
재사용한다. 전용 `--hm-orgchart-expanded-preview-background`, `-border`, `-radius`
입력은 `.hm-orgchart-expanded` 외곽에만 적용한다. 직원 카드·회사 타이틀·Viewport의
색상 입력은 그대로이며, 입력을 생략한 Vanilla의 기존 기본 외곽 스타일은 유지한다.
Auto의 빈 공간 드래그는 기존 Pan이며 직원 카드·그룹 타이틀 드래그는 회사 내부 또는
회사 간 소속 변경이다. 직원 이동은 `organizationId`, 조직 그룹 이동은 `parentId`를
갱신한다. 같은 회사 내부에서는 파견 직원을 포함한 기존 `affiliationId`를 보존한다.
회사 간 직원 이동은 `affiliationId`도 대상 회사로 변경하고, 회사 간 그룹 이동은 이동
그룹과 하위 조직 전체의 `companyId`, 소속 직원 전체의 `affiliationId`도 대상 회사로
변경한 새 snapshot을 현재 Core 인스턴스에 적용한다. ID·하위 계층·
이름·직위·사진·조직장·정렬은 보존하며 회사 색상과 인원 집계는 대상 회사에 맞춰 갱신한다.
원본 JSON·회사 프리셋·서버 데이터는 변경하지 않는다.
그룹 클릭의 접기·펼치기와 직원 클릭의 정보 팝업은 유지하며 실제 드래그 뒤의 포인터
click은 억제한다. 자기 자신·자손을 부모로 지정하는 이동, 회사 Root 이동,
조직장 참조를 손상시키는 직원 이동은 거절하며 이유를 안내한다.
직원 카드 또는 그룹 타이틀에 초점을 둔 뒤 `Alt+M`으로 이동을 시작할 수 있다.
방향키로 대상을 선택하고 `Enter`로 확인하거나 `ESC`로 취소한다. 팝업을 꺼도 직원
카드는 이동 조작을 위한 초점을 제공한다. Fixed에는 소속 변경을 추가하지 않는다.
Core와 React의 `onMove(result: OrgChartMoveResult)`는 이동 성공·실패를 알린다.
`result.moved`로 성공을 구분하며, React는 성공한 이동 후 열린 HM 직원 Dialog를 닫는다.
React callback 변경은 최신 committed callback만 갱신하고 Core를 재생성하거나 reload하지 않는다.
Expanded Story는 모든 회사의 원본 예시를 하나의 세션 source로 유지하고 회사 선택은
`selectedCompanyIds`로 처리한다. 회사 숨김·다시 표시·빈 선택은 성공한 소속 변경을
보존한다. **데이터 적용**은 현재 draft 조건으로 새 예시 source를 만들고 이동 내용을
초기화한다. **초기화**는 최초 설정과 최초 소속으로 돌아가며 설정값이 이미 최초값이어도
이동 내용은 되돌린다. 파일을 다시 작성하거나 React key로 강제 remount하지 않는다.
Story의 직원 합계와 그룹 선택은 성공한 `onMove`의 `result.data`를 별도 read model로
읽으며, source identity는 유지한다. 표시 직원 수는 최초 예시 인원이 아닌 현재 조직의
회사 소속을 기준으로 계산하므로 회사 간 이동 직후와 회사 숨김·다시 표시에도 일치한다.
미리보기에는 선택 회사별 현재 직원 수도 표시하며 데이터 설정의 회사당 인원은 예시 생성 기준이다.