Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/components/Application.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ import { type ApplicationProps } from '../typedefs/ApplicationProps';
import { type ApplicationRef } from '../typedefs/ApplicationRef';

const originalDefaultTextStyle = { ...TextStyle.defaultTextStyle };
const isElementNode = (value: unknown): value is HTMLElement => !!value
&& typeof value === 'object'
&& 'nodeType' in value
&& value.nodeType === 1;

const ApplicationImplementation = forwardRef<ApplicationRef, ApplicationProps>(function Application(
props,
Expand Down Expand Up @@ -71,7 +75,7 @@ const ApplicationImplementation = forwardRef<ApplicationRef, ApplicationProps>(f
{
if ('current' in resizeTo)
{
if (resizeTo.current instanceof HTMLElement)
if (isElementNode(resizeTo.current))
{
application.resizeTo = resizeTo.current;
}
Expand Down
16 changes: 13 additions & 3 deletions src/core/createRoot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ export function createRoot(
options: CreateRootOptions = {},
)
{
const ownerDocument = target.ownerDocument ?? document;
const ownerWindow = ownerDocument.defaultView ?? window;

// Check against mistaken use of createRoot
let root = roots.get(target);
let applicationState = (root?.applicationState ?? {
Expand Down Expand Up @@ -58,21 +61,28 @@ export function createRoot(

if (!root)
{
let canvas;
let canvas: HTMLCanvasElement | undefined;
const ownerCanvasConstructor = ownerWindow.HTMLCanvasElement;

if (target instanceof HTMLCanvasElement)
if (ownerCanvasConstructor && target instanceof ownerCanvasConstructor)
{
canvas = target;
}
else if (target.nodeName === 'CANVAS')
{
canvas = target as HTMLCanvasElement;
}

if (!canvas)
{
canvas = document.createElement('canvas');
canvas = ownerDocument.createElement('canvas');
target.innerHTML = '';
target.appendChild(canvas);
}

internalState.canvas = canvas;
internalState.ownerDocument = ownerDocument;
internalState.ownerWindow = ownerWindow;

const render = async (
children: ReactNode,
Expand Down
2 changes: 2 additions & 0 deletions src/typedefs/InternalState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,7 @@ import { type HostConfig } from './HostConfig';
export interface InternalState
{
canvas?: HTMLCanvasElement;
ownerDocument?: Document;
ownerWindow?: Window;
rootContainer: HostConfig['containerInstance'];
}
27 changes: 27 additions & 0 deletions test/e2e/components/Application.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Application as PixiApplication, type DestroyOptions, extensions as Pixi
import {
createContext,
createRef,
type RefObject,
useContext,
useEffect,
} from 'react';
Expand Down Expand Up @@ -47,6 +48,32 @@ describe('Application', () =>
expect(ref.current?.getCanvas()).toBeInstanceOf(HTMLCanvasElement);
});

it('supports resizeTo refs from another window', async () =>
{
const onInitSpy = vi.fn();
const appRef = createRef<ApplicationRef>();
const iframe = document.createElement('iframe');
document.body.appendChild(iframe);

const iframeDocument = iframe.contentDocument!;
const resizeTarget = iframeDocument.createElement('div');
iframeDocument.body.appendChild(resizeTarget);

const resizeToRef = { current: resizeTarget } as RefObject<HTMLElement | null>;

await act(async () => render((
<Application
ref={appRef}
resizeTo={resizeToRef}
onInit={onInitSpy} />
)));

await expect.poll(() => onInitSpy.mock.calls.length).toEqual(1);
await expect.poll(() => appRef.current?.getApplication()?.resizeTo).toBe(resizeTarget);

iframe.remove();
});

it('forwards context', async () =>
{
const onInitSpy = vi.fn();
Expand Down
26 changes: 26 additions & 0 deletions test/unit/core/createRoot.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,30 @@ describe('createRoot', () =>
expect(root.applicationState.rendererDestroyOptions).toEqual({ removeView: true });
});
});

it('creates roots for iframe-owned elements', () =>
{
const iframe = document.createElement('iframe');
document.body.appendChild(iframe);

const iframeDocument = iframe.contentDocument!;
const iframeCanvas = iframeDocument.createElement('canvas');
const iframeContainer = iframeDocument.createElement('div');

iframeDocument.body.appendChild(iframeCanvas);
iframeDocument.body.appendChild(iframeContainer);

const canvasRoot = createRoot(iframeCanvas as unknown as HTMLCanvasElement);
const containerRoot = createRoot(iframeContainer as unknown as HTMLElement);

const createdCanvas = iframeContainer.querySelector('canvas');

expect(canvasRoot.applicationState.app).toBeInstanceOf(Application);
expect(containerRoot.applicationState.app).toBeInstanceOf(Application);
expect(createdCanvas?.nodeName).toBe('CANVAS');
expect(createdCanvas?.ownerDocument).toBe(iframeDocument);
expect(containerRoot.internalState.canvas).toBe(createdCanvas);

iframe.remove();
});
});