feat(orgchart): integrate chart-only viewport and scoped guard fixes
Add initial fit and direct wheel zoom with preserved default presentation. Integrate five scoped GuardFix findings and AI generation preselection policy. Exclude Cell experiments and retain five historical guard failures.
This commit is contained in:
1 parent
0a8daf7ee7
commit
d56d587fd2
28 files changed
+1170
-77
No files matched your search
@@ -70,6 +70,14 @@ export function createOrgChartView(options: OrgChartOptions, view: CoreView): Or
|
||||
if (Object.values(employeeConnections).some(value => !['top', 'left', 'right'].includes(value))) throw new TypeError('employeeConnections must use top, left or right');
|
||||
const zoom = { min: options.zoom?.min ?? 0.05, max: options.zoom?.max ?? 3, step: options.zoom?.step ?? 1.2 };
|
||||
if (expanded && (!Number.isFinite(zoom.min) || !Number.isFinite(zoom.max) || !Number.isFinite(zoom.step) || zoom.min <= 0 || zoom.min > 1 || zoom.max < 1 || zoom.min >= zoom.max || zoom.step <= 1)) throw new TypeError('zoom requires 0 < min <= 1 <= max and step > 1');
|
||||
if (options.presentationMode !== undefined && options.presentationMode !== 'default' && options.presentationMode !== 'chart-only') throw new TypeError('presentationMode must be "default" or "chart-only"');
|
||||
if (options.initialView !== undefined && options.initialView !== 'center' && options.initialView !== 'fit') throw new TypeError('initialView must be "center" or "fit"');
|
||||
if (options.wheelZoom !== undefined && options.wheelZoom !== 'modifier' && options.wheelZoom !== 'direct') throw new TypeError('wheelZoom must be "modifier" or "direct"');
|
||||
// Fixed owns the approved Workdesk composition; never strip it silently.
|
||||
if (view === 'composition' && options.presentationMode === 'chart-only' && !expanded) throw new TypeError('presentationMode "chart-only" requires layoutMode "auto"');
|
||||
const chartOnly = expanded && options.presentationMode === 'chart-only';
|
||||
const initialView = options.initialView ?? (chartOnly ? 'fit' : 'center');
|
||||
const wheelZoom = options.wheelZoom ?? (chartOnly ? 'direct' : 'modifier');
|
||||
const copySelection = (ids?: readonly string[]) => {
|
||||
if (ids !== undefined && (!Array.isArray(ids) || ids.some(id => typeof id !== 'string' || !getCompanyColorPreset(id)))) throw new TypeError('selectedCompanyIds must contain registered company IDs');
|
||||
return ids === undefined ? undefined : [...new Set(ids)];
|
||||
@@ -327,6 +335,7 @@ export function createOrgChartView(options: OrgChartOptions, view: CoreView): Or
|
||||
if (LoadedEngine) Engine = LoadedEngine;
|
||||
const document = content!.ownerDocument;
|
||||
const previousViewport = expandedViewport?.state();
|
||||
const previousFitScale = expandedViewport?.fitScale();
|
||||
const previousManualPosition = expandedViewport?.hasManualPosition();
|
||||
employeeDialog?.close();
|
||||
contentGeneration?.abort();
|
||||
@@ -354,18 +363,24 @@ export function createOrgChartView(options: OrgChartOptions, view: CoreView): Or
|
||||
}
|
||||
// If regeneration removed keyboard focus, offer the stable viewport next.
|
||||
const nextContent = view === 'composition' ? expanded
|
||||
? createExpandedOrgChartContent(document, nextContext, viewport!, contentGeneration.signal, () => expandedViewport?.layoutReady())
|
||||
? createExpandedOrgChartContent(document, nextContext, viewport!, contentGeneration.signal, () => expandedViewport?.layoutReady(), { chartOnly, wheelZoom })
|
||||
: createOrgChartContent(document, nextContext, viewport!, contentGeneration.signal)
|
||||
: view === 'tree' ? viewport!
|
||||
: view === 'positionSummary' ? createPositionSummary(document, nextContext)
|
||||
: view === 'affiliationSummary' ? createAffiliationSummary(document, nextContext)
|
||||
: createStatusSummary(document, nextContext, contentGeneration.signal);
|
||||
if (expanded) {
|
||||
const toolbar = document.createElement('div');
|
||||
toolbar.className = 'hm-orgchart-expanded-toolbar';
|
||||
nextContent.insertBefore(toolbar, viewport!);
|
||||
// Chart Only keeps the same viewport controller without its visible toolbar.
|
||||
const toolbar = chartOnly ? undefined : document.createElement('div');
|
||||
if (toolbar) {
|
||||
toolbar.className = 'hm-orgchart-expanded-toolbar';
|
||||
nextContent.insertBefore(toolbar, viewport!);
|
||||
}
|
||||
// A fitted replacement starts from the new forest, not the previous Pan/Zoom.
|
||||
const resetView = fitNext && initialView === 'fit';
|
||||
expandedViewport = createExpandedViewport(viewport!, toolbar, contentGeneration.signal,
|
||||
{ ...zoom, zoomEnabled: interactionOptions.zoom, panEnabled: interactionOptions.pan }, previousViewport, fitNext, previousManualPosition);
|
||||
{ ...zoom, zoomEnabled: interactionOptions.zoom, panEnabled: interactionOptions.pan, wheelZoom, fitFloor: initialView === 'fit', initialFitScale: resetView ? undefined : previousFitScale },
|
||||
resetView ? undefined : previousViewport, fitNext ? initialView : undefined, !resetView && !!previousManualPosition);
|
||||
fitNext = false;
|
||||
const feedback = document.createElement('p');
|
||||
feedback.className = 'hm-orgchart-move-feedback'; feedback.id = `${prefix}-move-feedback`;
|
||||
@@ -383,13 +398,15 @@ export function createOrgChartView(options: OrgChartOptions, view: CoreView): Or
|
||||
else source.removeAttribute('aria-keyshortcuts');
|
||||
source.setAttribute('aria-describedby', feedback.id);
|
||||
}
|
||||
const selection = document.createElement('p');
|
||||
selection.className = 'hm-orgchart-expanded-selection';
|
||||
selection.setAttribute('aria-live', 'polite');
|
||||
const selected = new Set(roots.map(root => companyState!.companies.get(root.organization.id)!));
|
||||
const employeeCount = data.employees.filter(employee => selected.has(companyState!.companies.get(employee.organizationId)!)).length;
|
||||
selection.textContent = `선택 ${selected.size}개 회사 · ${employeeCount}명 / 원본 ${data.employees.length}명`;
|
||||
nextContent.insertBefore(selection, toolbar);
|
||||
if (toolbar) {
|
||||
const selection = document.createElement('p');
|
||||
selection.className = 'hm-orgchart-expanded-selection';
|
||||
selection.setAttribute('aria-live', 'polite');
|
||||
const selected = new Set(roots.map(root => companyState!.companies.get(root.organization.id)!));
|
||||
const employeeCount = data.employees.filter(employee => selected.has(companyState!.companies.get(employee.organizationId)!)).length;
|
||||
selection.textContent = `선택 ${selected.size}개 회사 · ${employeeCount}명 / 원본 ${data.employees.length}명`;
|
||||
nextContent.insertBefore(selection, toolbar);
|
||||
}
|
||||
if (!roots.length) {
|
||||
const empty = document.createElement('p');
|
||||
empty.className = 'hm-orgchart-expanded-empty';
|
||||
@@ -447,6 +464,7 @@ export function createOrgChartView(options: OrgChartOptions, view: CoreView): Or
|
||||
root = document.createElement('div');
|
||||
root.className = 'hm-orgchart-host';
|
||||
root.dataset.orgchartInstance = prefix;
|
||||
if (chartOnly) root.dataset.presentationMode = 'chart-only';
|
||||
const shadow = root.attachShadow({ mode: 'open' });
|
||||
const styleDisposers: (() => void)[] = [];
|
||||
const styles = [...(needsEngine ? [{ key: 'engine', href: stylesheetUrls.engine }] : []), { key: 'core', href: stylesheetUrls.core }];
|
||||
@@ -472,6 +490,7 @@ export function createOrgChartView(options: OrgChartOptions, view: CoreView): Or
|
||||
shell = document.createElement('section');
|
||||
shell.className = 'hm-orgchart-shell';
|
||||
shell.dataset.layoutMode = view === 'composition' ? layoutMode : 'fixed';
|
||||
if (chartOnly) shell.dataset.presentationMode = 'chart-only';
|
||||
// The Core's Korean labels must shape identically in ko HTML and en Storybook hosts.
|
||||
shell.lang = 'ko';
|
||||
shell.tabIndex = -1;
|
||||
|
||||
Reference in new issue
Block a user