@hm-design/org-chart v0.1
Framework-independent TypeScript/DOM Core backed by dabeng orgchart 6.0.0. No React runtime, wrapper, Storybook, navigation or API client. This version renders organizational hierarchy and employee/leader cards; additional relations are validated but are not drawn.
P1 composes a separate top leader, presentation-only hub, hierarchy, position/affiliation summaries and dispatched/onLeave groups. The same presentation.groups fields hide each group independently. The hub does not create a fictitious organization or change parentId.
import { createOrgChart, JsonOrgChartDataSource } from '@hm-design/org-chart';
const chart = createOrgChart({
container: document.getElementById('chart'),
dataSource: new JsonOrgChartDataSource(configuredJsonUrl),
// Host/bundler resolves the two exported CSS assets to absolute URLs.
stylesheetUrls: { engine: engineCssUrl, core: coreCssUrl },
onStateChange(state) { /* idle/loading/ready/error/destroyed */ },
});
await chart.mount();
await chart.update(nextDataset);
await chart.reload();
chart.destroy();
displayMode (optional, default 'interactive') selects the full composition presentation. 'readOnly' is the view-only Workdesk layout: the hierarchy is fully expanded with no collapse buttons or diagnostic text (kind, ID, raw counts, vacancy, description), summaries use compact presenters of the same Summary (organization-by-position table, untitled affiliation bar and status groups), and a 1920 × 914.5 design canvas scales proportionally to the available container width and height. Standalone components ignore it.
Organization links and employee information (P3)
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.
P3.2 compact typography and card logos
Large department cards use 24px names / 18px positions; compact cards use 18px names / 15px positions. Representative typography and the existing weights, line heights and colors are retained. Read-only dispatched/onLeave cards share the compact content layout (108×52 card, 24×44 photo slot), with their original gray surface and text colors. Status headers share the standard 2Depth 20px/700/19.7px typography; existing department-specific exceptions remain. Position and badge whitespace is removed only for display.
Employee-card logo contain boxes scale exactly 1.5×: small 12→18px; representative/large/status 14→21px. Original assets and footer logos stay unchanged. See P3.2 report for before/after browser measurements and preservation evidence. Historical P3.1 geometry below describes that earlier stage.
P3.1 display policy and local font loading
The OrgChart-scoped default family is Noto Sans KR; existing --hm-orgchart-font-family overrides remain available. The host must register its font-face once: HM Storybook already loads the existing HM Theme face. The HTML P1 host loads the unchanged public/fonts/NotoSansKR-variable.ttf copied to assets/fonts/; the React P1 host references that same source through its bundler. Global HM Theme files are unchanged. The Korean Core shell declares lang="ko" for consistent glyph shaping across host languages.
Read-only Full Composition suppresses only the 경영진 job-title badge. Positions (including a literal 경영진 position), other raw badges, employee records, popup fields and statistics are retained. Position whitespace is removed only in the card display. Other renderer modes keep their badge policy. All card types share one rectangular neutral SVG for a missing or failed profile image; an available profile image takes precedence. Approved cards, photo-slot dimensions and P3 status-card geometry are unchanged. See P3.1 report for focused browser evidence and limits.
Optional organizationLinks: Readonly<Record<string, string | undefined>> maps existing organization IDs to host-configured URLs. Only original hierarchy depth 1 titles become native links. Missing, undefined or blank URLs keep ordinary titles; no destination is generated. HTTP(S) and relative URLs are accepted; invalid URLs, other schemes and credentials reject construction with TypeError. Group links retain native keyboard navigation and a visible focus outline, separately from employee activation.
All employee cards, including the representative and dispatched/onLeave cards, support click, Enter and Space in both modes. By default they open a read-only native information dialog in the unscaled shell, outside the Stage. It shows only existing name, position, organization, job title, affiliation and work-status values; unavailable optional values are omitted. The readOnly fictitious display name matches the activated card (홍 길 동); underlying employee data stays unchanged. Close button, ESC, modal focus trapping and card focus restoration are provided. A name-overflow tooltip closes when information UI opens.
To use a host-owned popup, provide the additive callback below. Supplying it replaces the built-in dialog; the host then owns popup accessibility and cleanup. The callback receives the unchanged Employee and resolved existing organization/affiliation, the focusable trigger card and original event.
const chart = createOrgChart({
...configuredBaseOptions,
organizationLinks: configuredOrganizationLinks,
onEmployeeClick(employee, { organization, affiliation, trigger, originalEvent }) {
openEmployeeInformation({ employee, organization, affiliation, trigger, originalEvent });
},
});
Omit onEmployeeClick to use the default popup. Vanilla options are captured at construction; use a new handle to change the URL map or callback. There is no public Core setOptions method.
mount(container) also accepts the container at mount time. Constructing a handle and importing the ESM facade require no DOM. Mount requires a connected HTMLElement. One handle owns one container, preserving host siblings. Duplicate mount and update/reload before mount or after destroy reject with a usage error. Data/Source failures become structured error states and resolve the operation. Destroy is synchronous, idempotent and terminal; use a new handle to mount again. subscribe(listener) returns an unsubscribe function; getState() returns the immutable current state. Consumer callback errors do not break the Core.
update captures its input immediately and preserves the current Source. reload(source?) optionally replaces it. The last started operation wins; earlier requests are aborted, and stale responses cannot commit. Call completion means the operation was applied, superseded, or produced a controlled error, rather than that every image/font finished loading. Regeneration resets expand/scroll state; removed keyboard focus moves to the scroll region.
Data contract
Public OrgChartDataset, Organization, Employee, Affiliation, Metadata, PresentationConfig and Source types are exported with declarations. Preserve documented names: displayOrder, jobTitle, profileImage, workStatus. The supported schema version remains 0.1-draft; no silent migration is performed. The latest implementation request requires top-level schemaVersion; metadata.schemaVersion is an optional compatibility mirror and must match when supplied. metadata.updatedAt/updateNote and dataset relations/presentation are optional extensions. The original seven design documents are historical drafts and are preserved.
Organization and employee IDs have independent namespaces. All references are checked; duplicate IDs within a collection, unknown fields, missing values, type errors, invalid dates/versions/statuses, parent cycles, unsafe markup/URLs and empty organizations produce {valid:false, issues:[{code,path,message}]} from validateOrgChartDataset. Zero employees is valid. Leaders outside their assigned organization require a future approved rule and are rejected with LEADER_APPROVAL_REQUIRED. Multiple roots remain separate trees, without a fabricated parent. Order is presentation order, then displayOrder, then code-unit ID order.
normalizeOrgChartDataset and calculateSummary accept validated datasets. calculateSummary exposes raw counts for unique original employee IDs: total, direct/inclusive per organization, position, affiliation and status. These literal source counts are not an approved business headcount. No policy gives aggregation.status='unavailable'/policyRequired. A caller may provide an explicitly approved policy (policyVersion, includedStatuses, activeStatuses, includeLeaders:true, membershipMode:'primaryOnly'). Unsupported leader exclusion/multiple-membership rules or invalid policies return unsupportedPolicy. This release does not infer business meaning for dispatched/onLeave status.
JsonOrgChartDataSource accepts an inline dataset or an HTTP(S)/relative URL, optionally {fetch}. Inline input is captured at construction; every load returns a fresh immutable snapshot. External HTTP/JSON errors are sanitized and distinguished by OrgChartSourceError.code. Source output is unknown until Core validation. Future API sources implement load(options?:{signal?:AbortSignal}):Promise<unknown> and do not change Core APIs. URL paths are injected by the host, not embedded in Core. External JSON Fetch is an HTTP example; it is not supported as a file:// transport.
Theme and CSS ownership
P1 ships an independent OrgChart design. It requires no HM Theme, React/UI peer or font download. Resolve the two exported CSS assets to URLs; the Core loads them only into its dedicated open Shadow DOM. style.css includes its own scoped defaults and contains no private CSS imports. Summary-only components require only this CSS. /browser is a classic IIFE exposing window.HMOrgChart; dist/index.js is bundled ESM with no bare package imports.
All colors, typography, spacing, borders, radius and gradients belong to src/styles/design-tokens.css and src/core/style-presets.ts. Public --hm-orgchart-* inputs inherit from the caller's container; internal --hm-orgchart-resolved-* aliases resolve defaults without overriding inherited inputs. HM Theme variables do not affect this design. Workdesk organization header gradients and white heading text are measured; neutral/card styling is a documented design adaptation, not an official Workdesk value. Existing v0.1 demos and historical validation records are preserved. P1 theme-free examples are dist/p1.html (HTTP/ESM) and dist/p1-classic.html (direct file/inline JSON). See standalone component integration.
Engine disposal boundary
Upstream provides no official destroy API. The facade clears its DOM, aborts requests and generation event signals, clears callback/data/engine references and disconnects the init observer through a narrow managed subclass hook. pan/zoom/minimap/drag/export are disabled to bound resource ownership; hierarchy expansion uses native buttons and engine show/hide methods. Transition listeners are removed with their discarded DOM. Browser verification records repeated lifecycle DOM, global event listener and active observer stability. It does not establish complete heap/GC recovery. The hook depends on pinned 6.0.0 internals and must be re-audited before an engine upgrade.
Build and examples
From the repository root:
npm run typecheck:org-chart
npm run test:org-chart
npm run build:org-chart-example
npm run dev:org-chart-example
Open http://127.0.0.1:4178/index.html for bundled ESM + external JSON. Open examples/org-chart/dist/classic.html directly for classic bundle + inline fictitious JSON. Both use package build outputs, never Spike code. Example controls demonstrate reload, employee movement/addition, destroy and recreation. Build artifacts are Git-ignored; source fixtures are checked in.
npm run test:org-chart-browser expects Playwright supplied by the verification environment. If not installed locally, set ORGCHART_PLAYWRIGHT_MODULE to its absolute index.mjs/index.js and optionally ORGCHART_CHROME to a Chrome executable. Production has no browser-test dependency. Evidence is written under test-results/org-chart-browser/.
The React wrapper owns only ref/effect lifecycle, source/props updates and cleanup, including forwarding the additive P3 lookup options; HTML and React render the same Core. Standalone createOrgChartTree, createPositionSummary, createAffiliationSummary and createStatusSummary factories are exported at the package root and return the existing OrgChartHandle. The three public OrgChart Stories are Logo · Personnel Summary, Department Colors and Full Composition. Original verification Stories and independent component Stories remain available under the excluded internal tag.