feat(org-chart): finalize chart features and AI generation rules
This commit is contained in:
1 parent
7614507583
commit
d0828a7ff4
80 files changed
+8152
-221
No files matched your search
@@ -0,0 +1,59 @@
|
||||
import type { Organization } from '../data/index.js';
|
||||
import type { OrgChartAutoLayoutOptions, OrgChartAutoLayoutType, OrgChartInteractionOptions } from './types.js';
|
||||
|
||||
export function resolveInteractions(options: OrgChartInteractionOptions = {}): Readonly<Required<OrgChartInteractionOptions>> {
|
||||
for (const key of ['collapse', 'zoom', 'pan'] as const) {
|
||||
if (options[key] !== undefined && typeof options[key] !== 'boolean') throw new TypeError(`interactions.${key} must be boolean`);
|
||||
}
|
||||
return Object.freeze({ collapse: options.collapse ?? true, zoom: options.zoom ?? true, pan: options.pan ?? true });
|
||||
}
|
||||
|
||||
export interface ResolvedAutoLayout {
|
||||
readonly type: OrgChartAutoLayoutType;
|
||||
readonly hybridStartDepth: 2 | 3 | 4;
|
||||
readonly initialVisibleDepth: 1 | 2 | 3 | 'all';
|
||||
readonly hybridOrganizationIds: readonly string[];
|
||||
}
|
||||
|
||||
/** Match installed dabeng 6's mutually exclusive directional/vertical policies. */
|
||||
export function resolveAutoLayout(options: OrgChartAutoLayoutOptions = {}): ResolvedAutoLayout {
|
||||
const type = options.type ?? 'tb';
|
||||
const hybridStartDepth = options.hybridStartDepth ?? 3;
|
||||
const initialVisibleDepth = options.initialVisibleDepth ?? 'all';
|
||||
if (!['tb', 'lr', 'hybrid'].includes(type)) throw new TypeError('autoLayout.type must be tb, lr or hybrid');
|
||||
if (![2, 3, 4].includes(hybridStartDepth)) throw new TypeError('autoLayout.hybridStartDepth must be 2, 3 or 4');
|
||||
if (![1, 2, 3, 'all'].includes(initialVisibleDepth)) throw new TypeError('autoLayout.initialVisibleDepth must be 1, 2, 3 or all');
|
||||
if (options.hybridOrganizationIds !== undefined && (!Array.isArray(options.hybridOrganizationIds) || options.hybridOrganizationIds.some(id => typeof id !== 'string' || !id.trim()))) throw new TypeError('autoLayout.hybridOrganizationIds must contain nonempty organization IDs');
|
||||
const hybridOrganizationIds = Object.freeze([...new Set(options.hybridOrganizationIds ?? [])]);
|
||||
if (type !== 'tb' && hybridOrganizationIds.length) throw new TypeError('Per-group hybridOrganizationIds are supported only with TB; clear them before selecting LR or Hybrid');
|
||||
return Object.freeze({ type, hybridStartDepth, initialVisibleDepth, hybridOrganizationIds });
|
||||
}
|
||||
|
||||
export function autoEngineOptions(layout: ResolvedAutoLayout): { direction: 't2b' | 'l2r'; verticalLevel?: number; visibleLevel: number } {
|
||||
// Core owns collapse state and prunes its projection. Upstream visibleLevel
|
||||
// would retain hidden descendants that disagree with Core keyboard/state APIs.
|
||||
return { direction: layout.type === 'lr' ? 'l2r' : 't2b', visibleLevel: 999,
|
||||
...(layout.type === 'hybrid' ? { verticalLevel: layout.hybridStartDepth } : {}) };
|
||||
}
|
||||
|
||||
/** Apply the visibleLevel policy to new IDs only, keeping user changes stable. */
|
||||
export function applyInitialVisibility(organizations: readonly Organization[], collapsed: Set<string>, initialized: Set<string>, visibleDepth: ResolvedAutoLayout['initialVisibleDepth'], reset = false): void {
|
||||
const valid = new Set(organizations.map(organization => organization.id));
|
||||
for (const id of collapsed) if (!valid.has(id) || reset) collapsed.delete(id);
|
||||
for (const id of initialized) if (!valid.has(id) || reset) initialized.delete(id);
|
||||
const byId = new Map(organizations.map(organization => [organization.id, organization]));
|
||||
for (const organization of organizations) {
|
||||
if (initialized.has(organization.id)) continue;
|
||||
initialized.add(organization.id);
|
||||
if (visibleDepth === 'all') continue;
|
||||
let depth = 1;
|
||||
let current = organization;
|
||||
const seen = new Set([current.id]);
|
||||
while (current.parentId) {
|
||||
const parent = byId.get(current.parentId);
|
||||
if (!parent || seen.has(parent.id)) break;
|
||||
seen.add(parent.id); current = parent; depth++;
|
||||
}
|
||||
if (depth >= visibleDepth) collapsed.add(organization.id);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user