59 lines
3.1 KiB
TypeScript
59 lines
3.1 KiB
TypeScript
/** Story-only alignment of the entire transformed Workdesk stage in the visible
|
|
* Content Wrapper's content box. Core retains its proportional width/height fit. */
|
|
export function centerCompositionPreview(host: HTMLElement): () => void {
|
|
let frame: number | undefined;
|
|
let shadow: ShadowRoot | undefined;
|
|
let stage: HTMLElement | undefined;
|
|
const schedule = () => { frame ??= requestAnimationFrame(align); };
|
|
const resize = new ResizeObserver(schedule);
|
|
const mutation = new MutationObserver(schedule);
|
|
const outer = new MutationObserver(schedule);
|
|
const align = () => {
|
|
frame = undefined;
|
|
const nextShadow = host.querySelector('.hm-orgchart-host')?.shadowRoot;
|
|
if (!nextShadow) return;
|
|
if (shadow !== nextShadow) {
|
|
mutation.disconnect();
|
|
shadow = nextShadow;
|
|
mutation.observe(shadow, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'data-layout'] });
|
|
}
|
|
const wrapper = shadow.querySelector<HTMLElement>('.hm-orgchart-composition');
|
|
const nextStage = shadow.querySelector<HTMLElement>('.hm-orgchart-stage[data-workdesk-artwork]');
|
|
if (!wrapper || !nextStage) return;
|
|
if (stage !== nextStage) {
|
|
if (stage) resize.unobserve(stage);
|
|
stage = nextStage;
|
|
resize.observe(stage);
|
|
}
|
|
const container = host.getBoundingClientRect();
|
|
const css = getComputedStyle(host);
|
|
const inset = (key: 'borderLeftWidth' | 'borderRightWidth' | 'paddingLeft' | 'paddingRight') => parseFloat(css[key]) || 0;
|
|
const left = container.left + inset('borderLeftWidth') + inset('paddingLeft');
|
|
const right = container.right - inset('borderRightWidth') - inset('paddingRight');
|
|
const bounds = stage.getBoundingClientRect();
|
|
if (right <= left || !bounds.width) return;
|
|
// The stage keeps its unscaled layout width, so centering that width with
|
|
// flex/grid would misplace Core's transformed rectangle. Measure the actual
|
|
// rendered rectangle instead; translate it as one unit without changing the
|
|
// transform, scale, 1920 x 914.5 canvas or any stage-relative coordinates.
|
|
const difference = (left + right - bounds.left - bounds.right) / 2;
|
|
// Fractional proportional scales can alternate the rounded correction by
|
|
// 0.0001px. Keep the same content-box calculation without a feedback loop.
|
|
if (Math.abs(difference) < 0.001) return;
|
|
const translation = `${Number(((parseFloat(stage.style.translate) || 0) + difference).toFixed(4))}px`;
|
|
if (stage.style.translate !== translation) stage.style.translate = translation;
|
|
};
|
|
resize.observe(host);
|
|
// A padding redistribution can move the content center without resizing it.
|
|
outer.observe(host, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });
|
|
window.addEventListener('resize', schedule);
|
|
document.fonts.addEventListener('loadingdone', schedule);
|
|
schedule();
|
|
return () => {
|
|
resize.disconnect(); mutation.disconnect(); outer.disconnect();
|
|
window.removeEventListener('resize', schedule);
|
|
document.fonts.removeEventListener('loadingdone', schedule);
|
|
if (frame !== undefined) cancelAnimationFrame(frame);
|
|
};
|
|
}
|