feat(org-chart): separate fixed and auto layout previews

This commit is contained in:
shkim4 committed 2026-10-12 18:02:39 +09:00
1 parent 80583eb4ca
commit a92913d6bc
18 files changed
+1211 -7

No files matched your search

+17 -4
View File
@@ -5,6 +5,9 @@ import type { EngineAdapter, EngineConstructor } from './engine.js';
import { decorateNode, engineTree } from './render.js';
import { createOrgChartTree } from './org-chart-tree.js';
import { createOrgChartContent } from './org-chart-content.js';
import { createExpandedOrgChartContent } from './expanded-content.js';
import { expandedPreviewRoots } from './expanded-layout.js';
import { getCompanyColorPreset } from '../company-color-presets.js';
import { createPositionSummary } from './position-summary.js';
import { createAffiliationSummary } from './affiliation-summary.js';
import { createStatusSummary } from './status-summary.js';
@@ -13,7 +16,7 @@ import type { EmployeeInfoDialog } from './employee-info-dialog.js';
import type { RenderContext } from './render.js';
import type { OrgChartEmployeeClickContext, OrgChartHandle, OrgChartOptions, OrgChartState } from './types.js';
export type { OrgChartEmployeeClickContext, OrgChartHandle, OrgChartOptions, OrgChartOrganizationLinks, OrgChartState } from './types.js';
export type { OrgChartLayoutMode, OrgChartEmployeeClickContext, OrgChartHandle, OrgChartOptions, OrgChartOrganizationLinks, OrgChartState } from './types.js';
const owners = new WeakMap<HTMLElement, symbol>();
let instanceSequence = 0;
@@ -47,6 +50,11 @@ export function createOrgChartView(options: OrgChartOptions, view: CoreView): Or
if (!options.stylesheetUrls?.core || (needsEngine && !options.stylesheetUrls.engine)) throw new TypeError('Provide the required OrgChart stylesheet URLs');
const stylesheetUrls = { ...options.stylesheetUrls };
if (options.displayMode !== undefined && options.displayMode !== 'interactive' && options.displayMode !== 'readOnly') throw new TypeError('displayMode must be "interactive" or "readOnly"');
if (options.layoutMode !== undefined && options.layoutMode !== 'fixed' && options.layoutMode !== 'auto') throw new TypeError('layoutMode must be "fixed" or "auto"');
const layoutMode = options.layoutMode ?? 'fixed';
const expanded = view === 'composition' && layoutMode === 'auto';
const expandedCompanyPreset = expanded ? getCompanyColorPreset(options.companyId ?? 'hanmac') : undefined;
if (expanded && !expandedCompanyPreset) throw new TypeError('companyId must be a registered company preset in Auto mode');
const readOnly = view === 'composition' && options.displayMode === 'readOnly';
const organizationLinks: Record<string, string> = Object.create(null);
if (options.onEmployeeClick !== undefined && typeof options.onEmployeeClick !== 'function') throw new TypeError('onEmployeeClick must be a function');
@@ -200,14 +208,16 @@ export function createOrgChartView(options: OrgChartOptions, view: CoreView): Or
const normalized = normalizeOrgChartDataset(data);
const summary = freeze(calculateSummary(data, policy));
const topLeaderIds = new Set(view === 'composition' ? normalized.roots.filter(node => node.organization.type === 'root' && node.organization.leaderId).map(node => node.organization.leaderId!) : []);
const nextContext: RenderContext = { data, summary, orderedOrganizations: [], employeesByOrganization: normalized.employeesByOrganization, topLeaderIds, readOnly, organizationLinks };
const nextContext: RenderContext = { data, summary, orderedOrganizations: [], employeesByOrganization: normalized.employeesByOrganization, topLeaderIds, readOnly: readOnly || expanded, organizationLinks, expandedCompanyPreset };
// Composition shows the root leader as the standalone top card, so the root's direct
// organizations become the horizontal engine roots (data and parentId stay untouched).
// A root with other direct employees keeps its node so those employees stay visible.
const flattenRoot = (node: typeof normalized.roots[number]) => view === 'composition' && node.organization.type === 'root' && !!node.organization.leaderId
&& (normalized.employeesByOrganization.get(node.organization.id) ?? []).every(employee => employee.id === node.organization.leaderId);
const flattened = normalized.roots.some(flattenRoot);
const tree = engineTree(flattened ? normalized.roots.flatMap(node => flattenRoot(node) ? node.children : [node]) : normalized.roots, prefix, nextContext, flattened ? 1 : 0);
const roots = expanded ? expandedPreviewRoots(normalized.roots)
: flattened ? normalized.roots.flatMap(node => flattenRoot(node) ? node.children : [node]) : normalized.roots;
const tree = engineTree(roots, prefix, nextContext, expanded || flattened ? 1 : 0);
const styleIssues = await styleReady;
if (!active(version)) return;
if (styleIssues) { fail(version, styleIssues); return; }
@@ -233,7 +243,9 @@ export function createOrgChartView(options: OrgChartOptions, view: CoreView): Or
});
}
// If regeneration removed keyboard focus, offer the stable viewport next.
const nextContent = view === 'composition' ? createOrgChartContent(document, nextContext, viewport!, contentGeneration.signal)
const nextContent = view === 'composition' ? expanded
? createExpandedOrgChartContent(document, nextContext, viewport!, contentGeneration.signal)
: createOrgChartContent(document, nextContext, viewport!, contentGeneration.signal)
: view === 'tree' ? viewport!
: view === 'positionSummary' ? createPositionSummary(document, nextContext)
: view === 'affiliationSummary' ? createAffiliationSummary(document, nextContext)
@@ -306,6 +318,7 @@ export function createOrgChartView(options: OrgChartOptions, view: CoreView): Or
cancelStyles = () => { for (const dispose of styleDisposers) dispose(); };
shell = document.createElement('section');
shell.className = 'hm-orgchart-shell';
shell.dataset.layoutMode = view === 'composition' ? layoutMode : 'fixed';
// The Core's Korean labels must shape identically in ko HTML and en Storybook hosts.
shell.lang = 'ko';
shell.tabIndex = -1;