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

+2
View File
@@ -44,6 +44,8 @@ Named runtime export: `OrgChart`. Type exports: `OrgChartProps` and the Core's p
- Required `dataSource`: the same Core `OrgChartDataSource` interface, `load(options?: { signal?: AbortSignal }): Promise<unknown>`.
- Required `stylesheetUrls`: `{ engine: string; core: string }`, the exact Core asset contract.
- Optional `displayMode`: `'interactive'` (default) or `'readOnly'`, passed to the Core composition. Changing it recreates the Core handle.
- Optional `layoutMode`: `'fixed'` (default) or `'auto'`, independent of `displayMode`. Auto is the P5-1A initial-group preview; automatic recursive layout, collapse and Zoom/Pan remain deferred. Changing it recreates the same Core handle.
- Optional `companyId`: existing Expanded company preset ID (default `hanmac`). Changing it recreates the handle; Fixed ignores company colors. Unknown Auto IDs report a Core construction lifecycle error. All seven IDs and preview limits are documented in the [Core README](../org-chart/README.md#layout-boundary-p5-1a).
- Optional `organizationLinks`: existing organization-ID-to-URL map, applied only to original 1Depth titles. Missing/blank values retain plain titles. Core validates HTTP(S)/relative URLs and rejects invalid URLs, other schemes and credentials. Treat the map as immutable and memoize it; a new map recreates the handle.
- Optional `onEmployeeClick(employee, context)`: host-owned information UI, replacing the Core's built-in popup. `context` contains the existing `organization`, `affiliation`, focusable `trigger` card and `originalEvent`. It works in both modes and receives untouched Employee data.
- Optional `aggregationPolicy`: the Core's `ApprovedAggregationPolicy`.
+5 -1
View File
@@ -18,6 +18,8 @@ export function OrgChart({
aggregationPolicy,
stylesheetUrls,
displayMode,
layoutMode,
companyId,
organizationLinks,
onEmployeeClick,
className,
@@ -62,6 +64,8 @@ export function OrgChart({
aggregationPolicy,
stylesheetUrls: { engine: engineStylesheetUrl, core: coreStylesheetUrl },
displayMode,
layoutMode,
companyId,
organizationLinks,
onEmployeeClick: hasEmployeeCallback ? (employee, context) => {
if (generationRef.current === generation) committedRef.current.onEmployeeClick?.(employee, context);
@@ -101,7 +105,7 @@ export function OrgChart({
}
}
};
}, [aggregationPolicy, engineStylesheetUrl, coreStylesheetUrl, displayMode, organizationLinks, hasEmployeeCallback]);
}, [aggregationPolicy, engineStylesheetUrl, coreStylesheetUrl, displayMode, layoutMode, companyId, organizationLinks, hasEmployeeCallback]);
useLayoutEffect(() => {
const instance = instanceRef.current;
+33
View File
@@ -24,6 +24,39 @@ chart.destroy();
## Organization links and employee information (P3)
### Layout boundary (P5-1A)
`layoutMode?: 'fixed' | 'auto'` is independent of `displayMode`. The default
`'fixed'` keeps the existing Workdesk composition and its display-mode behavior.
`'auto'` currently selects the **P5-1A initial preview**, showing the representative
and immediate organization groups from the validated hierarchy. Descendants stay
in the dataset and statistics but are not placed in this preview. Automatic
recursive placement/recalculation/collision prevention belongs to P5-1B;
collapse controls and Zoom/Pan remain deferred. No preview readiness state claims
those later features are complete.
Use `companyId` with Auto to select an existing company preset (default `hanmac`).
The seven IDs are `hanmac`, `saman`, `jangheon-industry`, `jangheon`, `ptc`, `halla`,
and `baron`. Main colors style only Expanded group headers; indexed secondary
variables come from that same preset. Fixed ignores this option and preserves
its organization gradients. Unknown Auto IDs reject construction with `TypeError`.
The preview reuses approved Workdesk card presentation, the same data/engine and
employee dialog; it has an independent wrapper without Fixed coordinates or scaling.
Choose `displayMode: 'readOnly'` for the matching read-only popup name policy.
```js
const chart = createOrgChart({
...configuredBaseOptions,
layoutMode: 'auto',
displayMode: 'readOnly',
companyId: 'hanmac',
});
```
Options are captured at construction: create a new handle to change the layout or
company. Standalone Tree/Summary factories keep their existing rendering paths.
### P3.2 follow-up: representative badges
Representative cards never render job-title, role or status badges, empty attribute wrappers or a badge row, including standalone specimens. They display `대표이사` / `홍 길 동`, retaining the approved 146×74 asset and existing typography, with no gap between position and name. The fixture's raw representative position (`경영진`), employee data and dialog fields remain unchanged. Other 1Depth/2Depth badge policies are preserved. This display policy takes precedence over the historical P3.2/P3.1 notes below.
+1 -1
View File
@@ -30,7 +30,7 @@ execFileSync(process.execPath, [path.resolve(path.dirname(typescriptManifest), t
// Ship one self-contained public CSS asset. ?url and file:// consumers cannot
// rely on source-relative imports or a bundler to discover private style files.
const coreStyles = await readFile(path.join(root, 'src/styles/org-chart.css'), 'utf8');
const inlinedStyles = await Promise.all(['design-tokens.css', 'employee-card.css', 'summary-sections.css', 'org-chart-content.css', 'org-chart-readonly.css', 'org-chart-p3-summary.css', 'employee-info-dialog.css'].map(async file => [file, await readFile(path.join(root, 'src/styles', file), 'utf8')]));
const inlinedStyles = await Promise.all(['design-tokens.css', 'employee-card.css', 'summary-sections.css', 'org-chart-content.css', 'org-chart-readonly.css', 'org-chart-p3-summary.css', 'employee-info-dialog.css', 'org-chart-expanded.css'].map(async file => [file, await readFile(path.join(root, 'src/styles', file), 'utf8')]));
let publicStyles = coreStyles;
for (const [file, css] of inlinedStyles) publicStyles = publicStyles.replace(`@import './${file}';`, css);
await writeFile(path.join(root, 'dist/style.css'), publicStyles);
@@ -0,0 +1,47 @@
import { createEmployeeCard } from './employee-card.js';
import { createHeading, type RenderContext } from './render.js';
import { representativeCardAsset } from './representative-card-asset.js';
/** P5-1A presenter boundary. The shared Core supplies the initial engine groups;
* this presenter never assigns coordinates or interprets parentId. A later
* layout adapter can replace group placement without duplicating card or data
* rendering in React and Vanilla consumers. */
export function createExpandedOrgChartContent(
document: Document, context: RenderContext, tree: HTMLElement, signal: AbortSignal,
): HTMLElement {
const content = document.createElement('div');
content.className = 'hm-orgchart-expanded';
content.dataset.component = 'ExpandedOrgChartContent';
content.dataset.layoutMode = 'auto';
content.dataset.layoutStatus = 'preview';
const heading = createHeading(document, context);
const status = document.createElement('p');
status.className = 'hm-orgchart-expanded-stage-note';
status.textContent = 'P5-1A · 확장형 준비 화면';
const description = document.createElement('p');
description.className = 'hm-orgchart-expanded-description';
description.textContent = '대표자와 1Depth 예시 그룹을 표시합니다. 하위 계층 자동 배치와 접기·펼치기, Zoom/Pan은 후속 단계에서 구현합니다.';
content.append(heading, status, description);
const leaders = document.createElement('section');
leaders.className = 'hm-orgchart-expanded-top-leaders';
leaders.setAttribute('aria-label', '최상위 조직 리더');
for (const id of context.topLeaderIds) {
const employee = context.data.employees.find(item => item.id === id);
if (!employee) continue;
leaders.append(createEmployeeCard(document, employee, {
kind: 'topLeader',
workdeskPresentation: true,
cardAsset: representativeCardAsset,
affiliationName: context.data.affiliations.find(item => item.id === employee.affiliationId)?.name,
}, signal));
}
if (leaders.childElementCount) content.append(leaders);
// Keep the stable Core viewport for keyboard access and ordinary scrolling.
// This is a flow preview, with no fixed-stage scale observer or artwork.
tree.setAttribute('aria-label', '확장형 조직도 초기 그룹 영역');
content.append(tree);
return content;
}
@@ -0,0 +1,47 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import { expandedPreviewRoots } from './expanded-layout.js';
import { createOrgChart } from './index.js';
import type { OrganizationTreeNode } from '../data/index.js';
const node = (id: string, parentId: string | null, type: 'root' | 'office' = 'office', children: OrganizationTreeNode[] = []): OrganizationTreeNode => ({
organization: { id, parentId, name: id, type, leaderId: null, displayOrder: 0 }, children,
});
test('initial preview keeps actual first-depth ancestry and defers descendants without mutating hierarchy', () => {
const grandchild = node('depth-2', 'depth-1');
const child = node('depth-1', 'root', 'office', [grandchild]);
const roots = [node('root', null, 'root', [child])];
const before = structuredClone(roots);
const projected = expandedPreviewRoots(roots);
assert.deepEqual(projected.map(item => item.organization.id), ['depth-1']);
assert.equal(projected[0]!.organization.parentId, 'root');
assert.deepEqual(projected[0]!.children, []);
assert.deepEqual(roots, before);
});
test('independent roots and normalized order stay independent in the initial preview', () => {
const roots = [node('first', null), node('root', null, 'root', [node('second', 'root'), node('third', 'root')])];
assert.deepEqual(expandedPreviewRoots(roots).map(item => item.organization.id), ['first', 'second', 'third']);
assert.deepEqual(expandedPreviewRoots([]), []);
});
const options = { dataSource: { load: async () => ({}) }, stylesheetUrls: { engine: '/engine.css', core: '/style.css' } };
test('layout defaults remain compatible and displayMode is an independent public option', () => {
for (const displayMode of ['interactive', 'readOnly'] as const) {
for (const layoutMode of [undefined, 'fixed', 'auto'] as const) {
const handle = createOrgChart({ ...options, displayMode, layoutMode });
assert.equal(handle.getState().status, 'idle');
handle.destroy();
}
}
});
test('invalid layout and unknown Auto company fail explicitly, while Fixed ignores company configuration', () => {
assert.throws(() => createOrgChart({ ...options, layoutMode: 'invalid' as 'auto' }), /layoutMode/);
assert.throws(() => createOrgChart({ ...options, layoutMode: 'auto', companyId: 'unknown' }), /companyId/);
const fixed = createOrgChart({ ...options, layoutMode: 'fixed', companyId: 'unknown' });
assert.equal(fixed.getState().status, 'idle');
fixed.destroy();
});
@@ -0,0 +1,10 @@
import type { OrganizationTreeNode } from '../data/index.js';
/** P5-1A preview projection only. Keep the validated hierarchy intact and
* expose the root's immediate groups without pretending to place descendants.
* P5-1B can replace this boundary with a recursive layout result shared by
* every runtime; neither React nor the Story owns a layout algorithm. */
export function expandedPreviewRoots(roots: readonly OrganizationTreeNode[]): OrganizationTreeNode[] {
return roots.flatMap(root => root.organization.type === 'root' ? root.children : [root])
.map(node => ({ organization: node.organization, children: [] }));
}
+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;
+11
View File
@@ -4,6 +4,7 @@ import { createEmployeeCard } from './employee-card.js';
import { applyOrganizationStyle, organizationStylePresets } from './style-presets.js';
import { getDepartmentCardAsset } from './department-card-assets.js';
import type { OrgChartOrganizationLinks } from './types.js';
import type { CompanyColorPreset } from '../company-color-presets.js';
const organizationNames = { root: '최상위 조직', division: '부문', office: '실', div: 'Div', cell: '셀', team: '팀' } as const;
@@ -16,6 +17,8 @@ export interface RenderContext {
/** View-only composition: no collapse controls or diagnostic text are created. */
readOnly?: boolean;
organizationLinks?: OrgChartOrganizationLinks;
/** Only the Expanded presenter supplies company header colors. */
expandedCompanyPreset?: CompanyColorPreset;
}
export function engineTree(roots: OrganizationTreeNode[], prefix: string, context: RenderContext, startDepth = 0): RenderEngineNode[] {
@@ -61,6 +64,14 @@ export function decorateNode(
}
node.dataset.organizationDepth = String(organizationDepth);
applyOrganizationStyle(node, Number(value.groupVariant));
if (context.expandedCompanyPreset) {
const preset = context.expandedCompanyPreset;
node.dataset.companyId = preset.companyId;
node.style.setProperty('--hm-orgchart-current-gradient', preset.primary);
node.style.setProperty('--hm-orgchart-expanded-primary', preset.primary);
// Preserve every existing secondary value for the later Expanded theme.
preset.secondary.forEach((color, index) => node.style.setProperty(`--hm-orgchart-expanded-secondary-${index + 1}`, color));
}
node.dataset.organizationType = organization.type;
// Header role is styling-only: depth 0-1 are organization titles, deeper nodes are cell/team headers.
node.dataset.headerRole = (context.readOnly ? organizationDepth : Number(value.depth)) <= 1 ? 'organization' : 'group';
+9
View File
@@ -10,6 +10,10 @@ export type OrgChartState =
* controls or development text, compact summaries on a fitted design canvas. */
export type OrgChartDisplayMode = 'interactive' | 'readOnly';
/** Layout is independent of displayMode. Auto currently exposes only the
* P5-1A initial groups; recursive placement is reserved for P5-1B. */
export type OrgChartLayoutMode = 'fixed' | 'auto';
/** Links are keyed by the existing organization ID. Only 1Depth titles use them. */
export type OrgChartOrganizationLinks = Readonly<Record<string, string | undefined>>;
@@ -29,6 +33,11 @@ export interface OrgChartOptions {
stylesheetUrls: { engine: string; core: string };
/** Full composition presentation only; standalone views ignore it. */
displayMode?: OrgChartDisplayMode;
/** Full composition only. Omission preserves the existing Fixed renderer. */
layoutMode?: OrgChartLayoutMode;
/** Expanded-only company preset ID. Defaults to hanmac; ignored by Fixed.
* Unknown IDs are rejected in Auto rather than silently replacing colors. */
companyId?: string;
/** No entry (or an empty URL) keeps an ordinary title. HTTP(S) and relative URLs are supported. */
organizationLinks?: OrgChartOrganizationLinks;
/** Optional host-owned information UI. When supplied, it replaces the default read-only dialog.
+1 -1
View File
@@ -2,6 +2,6 @@ export * from './data/index.js';
export { companyColorPresets, getCompanyColorPreset } from './company-color-presets.js';
export type { CompanyColorPreset, CompanyId } from './company-color-presets.js';
export { createOrgChart } from './core/index.js';
export type { OrgChartDisplayMode, OrgChartEmployeeClickContext, OrgChartHandle, OrgChartOptions, OrgChartOrganizationLinks, OrgChartState } from './core/types.js';
export type { OrgChartDisplayMode, OrgChartLayoutMode, OrgChartEmployeeClickContext, OrgChartHandle, OrgChartOptions, OrgChartOrganizationLinks, OrgChartState } from './core/types.js';
export { createOrgChartTree, createPositionSummary, createAffiliationSummary, createStatusSummary } from './components.js';
export type { OrgChartSummaryComponentOptions } from './components.js';
@@ -0,0 +1,92 @@
/* P5-1A flow preview only. Every rule is under the new presenter namespace;
the fixed Workdesk composition and approved card internals stay unchanged.
Ordinary flex wrapping is not the P5-1B automatic hierarchy layout. */
.hm-orgchart-expanded {
display: grid;
grid-template-columns: minmax(0, 1fr);
gap: var(--hm-orgchart-resolved-spacing-sm);
width: 100%;
min-width: 0;
padding: var(--hm-orgchart-resolved-spacing-lg);
background: var(--hm-orgchart-resolved-bg-content);
}
.hm-orgchart-expanded .hm-orgchart-heading { margin: 0; }
.hm-orgchart-expanded .hm-orgchart-expanded-stage-note {
margin: 0;
color: var(--hm-orgchart-resolved-fg-primary);
font-weight: var(--hm-orgchart-resolved-font-weight-bold);
}
.hm-orgchart-expanded .hm-orgchart-expanded-description {
margin: 0;
color: var(--hm-orgchart-resolved-fg-secondary);
overflow-wrap: anywhere;
}
.hm-orgchart-expanded .hm-orgchart-expanded-top-leaders {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: var(--hm-orgchart-resolved-spacing-lg);
padding: var(--hm-orgchart-resolved-spacing-lg) 0;
}
.hm-orgchart-expanded .hm-orgchart-viewport {
padding: var(--hm-orgchart-resolved-spacing-lg);
overflow: auto;
background: var(--hm-orgchart-resolved-bg-surface);
}
.hm-orgchart-expanded .hm-orgchart-forest {
display: flex;
flex-wrap: wrap;
align-items: flex-start;
justify-content: center;
gap: var(--hm-orgchart-resolved-spacing-xxl);
width: 100%;
min-width: 0;
}
.hm-orgchart-expanded .hm-orgchart-tree { flex: 0 0 auto; }
.hm-orgchart-expanded .hm-orgchart-engine.orgchart {
min-width: 0;
padding: 0;
}
.hm-orgchart-expanded .hm-orgchart-engine.orgchart .node[data-depth] {
width: max-content;
min-width: 146px;
margin: 0;
}
.hm-orgchart-expanded .hm-orgchart-engine.orgchart .node[data-depth] .hm-orgchart-organization {
display: flex;
flex-direction: column;
align-items: center;
width: auto;
padding: 0;
border: 0;
border-radius: 0;
background: none;
box-shadow: none;
}
/* Same approved 1Depth title geometry; color resolves from the auto-only
company variables supplied by the shared node decorator. */
.hm-orgchart-expanded .hm-orgchart-engine.orgchart .node[data-depth] .hm-orgchart-organization-name {
align-self: stretch;
min-width: 146px;
height: 36px;
min-height: 36px;
margin: 0;
padding: 0 12px;
background: var(--hm-orgchart-current-gradient);
color: var(--hm-orgchart-resolved-on-action-primary);
font-size: 20px;
font-weight: 700;
line-height: 36px;
white-space: nowrap;
}
.hm-orgchart-expanded .hm-orgchart-engine.orgchart .node[data-depth] .hm-orgchart-employee { margin: 0; }
.hm-orgchart-expanded .hm-orgchart-engine.orgchart .node[data-depth] .hm-orgchart-employee.hm-orgchart-leader { margin: 8px 0 0; }
.hm-orgchart-expanded .hm-orgchart-engine.orgchart .node[data-depth] .hm-orgchart-employees {
display: flex;
flex-direction: column;
flex-wrap: nowrap;
align-items: center;
gap: 8px;
margin-top: 8px;
}
.hm-orgchart-expanded .hm-orgchart-engine.orgchart .node[data-depth] .hm-orgchart-employees:empty { display: none; }
@@ -5,6 +5,7 @@
@import './org-chart-readonly.css';
@import './org-chart-p3-summary.css';
@import './employee-info-dialog.css';
@import './org-chart-expanded.css';
/* Standalone defaults are scoped to the shell; only OrgChart public variables
inherit across the Shadow DOM boundary. No host HM Theme is required. */
:host { display: block; min-width: 0; max-width: 100%; font: inherit; }