Added to fix mobile editor cursor and gutter when scaling the content, should be removed since it is an experimental property.
Edit:
After an investigation, it seems that there is a browser issue with "getBoundingClientRects"that returns inconsistent values when the parent node has the transform scale property.
This could be fixed wrapping the scaled content into an iframe. This could be a possible implementation that doesn't cover these issues:
- Stylesheet are not fully loaded. When switching a theme that uses @emotion/css the stylesheet must be loaded into the iframe.
- Iframe position must be changered.
- Frame handler size could be fixed
Possible implementation: create a new app package that handle only his style and the editor content
// IFrame.tsx
type IFrameProps = JSX.IntrinsicElements['iframe'] & {};
export function IFrame(props: IFrameProps): JSX.Element {
const [ref, setRef] = createSignal<HTMLIFrameElement>();
const [mountNode, setMountNode] = createSignal<HTMLElement>();
const [height, setHeight] = createSignal(0);
const [width, setWidth] = createSignal(0);
createEffect(
on(ref, frame => {
if (!frame || !frame.contentWindow) return;
const currentHead = window.document.head;
frame.contentDocument!.head.innerHTML = currentHead.innerHTML;
setMountNode(() => frame.contentWindow?.document.body);
setTimeout(() => {
const scrollHeight = ref()?.contentWindow?.document.body.scrollHeight;
const scrollWidth = ref()?.contentWindow?.document.body.scrollWidth;
setHeight(scrollHeight ?? 0);
setWidth(scrollWidth ?? 0);
});
}),
);
return (
<iframe {...props} ref={setRef} width={width()} height={height()}>
<Show when={mountNode()}>
{node => <Portal mount={node}>{props.children}</Portal>}
</Show>
</iframe>
);
}
// FrameHandler.tsx
export function FrameHandler(
props: PropsWithChildren<FrameHandlerProps>,
): JSXElement {
const [internalRef, setInternalRef] = createSignal<HTMLDivElement>();
const [canvasScale, setCanvasScale] = createSignal(1);
const ratio = 0.1;
createEffect(
on([internalRef], ([frame]) => {
setTimeout(() => {
const scale = getScaleByRatio(
// TODO: should be a ref (?)
frame?.parentElement,
frame,
1 + ratio,
);
props.onScaleChange(scale);
setCanvasScale(scale);
});
}),
);
return (
<Box class={styles.wrapper}>
<div
class={styles.handler}
style={assignInlineVars({
[styles.frameHandlerVars.scale]: canvasScale().toString(),
})}
ref={createRef<'div'>(props, e => {
setInternalRef(() => e);
})}
>
<div use:exportExclude={true} class={styles.squaredBackgroundOverlay} />
<IFrame>{props.children}</IFrame>
</div>
</Box>
);
}