diff --git a/README.md b/README.md index ed9d70c8..b336370c 100644 --- a/README.md +++ b/README.md @@ -120,10 +120,6 @@ const MyComponent = () => { } ``` -###### `attachToDevtools` - -Setting `attachToDevtools` to `true` will automatically attach the application to the [Official Pixi.js Devtools](https://chromewebstore.google.com/detail/pixijs-devtools/dlkffcaaoccbofklocbjcmppahjjboce). - ###### `defaultTextStyle` `defaultTextStyle` is a convenience property. Whatever is passed will automatically be assigned to Pixi.js's [`TextStyle.defaultTextStyle`](https://pixijs.download/release/docs/text.TextStyle.html#defaultTextStyle). @@ -207,8 +203,8 @@ const MyComponent = () => { If you're using Typescript, this new `` component will throw type errors. Pixi React exports a `PixiReactElementProps` type that can be used to solve this. You'll need to pass the `Viewport` into `PixiReactElementProps` and inject it into JSX: ```ts -import type { PixiReactElementProps } from '@pixi/react' -import type { Viewport } from 'pixi-viewport' +import { type PixiReactElementProps } from '@pixi/react' +import { type Viewport } from 'pixi-viewport' declare global { namespace JSX { diff --git a/package-lock.json b/package-lock.json index b8e13eb1..dc26aa6d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.0-development", "license": "MIT", "dependencies": { - "react-reconciler": "0.29.2" + "react-reconciler": "0.31.0" }, "devDependencies": { "@pixi/extension-scripts": "^4.0.0", @@ -17,18 +17,18 @@ "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^15.2.3", "@testing-library/jest-dom": "^6.4.8", - "@testing-library/react": "^16.0.0", + "@testing-library/react": "^16.1.0", "@testing-library/user-event": "^14.5.2", - "@types/react": "^18.3.2", - "@types/react-reconciler": "0.28.8", + "@types/react": "^19.0.0", + "@types/react-reconciler": "^0.28.9", "@vitejs/plugin-react": "^4.3.1", "@vitest/browser": "^2.0.4", "husky": "^8.0.0", "jsdom": "^25.0.0", "pixi.js": "8.2.6", "playwright": "^1.45.3", - "react": "^18.3.1", - "react-dom": "^18.3.1", + "react": "^19.0.0", + "react-dom": "^19.0.0", "rollup": "^4.18.0", "rollup-plugin-esbuild": "^6.1.1", "rollup-plugin-sourcemaps": "^0.6.3", @@ -37,13 +37,7 @@ }, "peerDependencies": { "pixi.js": "^8.2.6", - "react": ">=18.0.0", - "react-dom": ">=18.0.0" - }, - "peerDependenciesMeta": { - "react-dom": { - "optional": true - } + "react": ">=19.0.0" } }, "node_modules/@adobe/css-tools": { @@ -3836,9 +3830,9 @@ "license": "MIT" }, "node_modules/@testing-library/react": { - "version": "16.0.1", - "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.0.1.tgz", - "integrity": "sha512-dSmwJVtJXmku+iocRhWOUFbrERC76TX2Mnf0ATODz8brzAZrMBbzLwQixlBSanZxR6LddK3eiwpSFZgDET1URg==", + "version": "16.1.0", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.1.0.tgz", + "integrity": "sha512-Q2ToPvg0KsVL0ohND9A3zLJWcOXXcO8IDu3fj11KhNt0UlCWyFyvnCIBkd12tidB2lkiVRG8VFqdhcqhqnAQtg==", "dev": true, "license": "MIT", "dependencies": { @@ -3849,10 +3843,10 @@ }, "peerDependencies": { "@testing-library/dom": "^10.0.0", - "@types/react": "^18.0.0", - "@types/react-dom": "^18.0.0", - "react": "^18.0.0", - "react-dom": "^18.0.0" + "@types/react": "^18.0.0 || ^19.0.0", + "@types/react-dom": "^18.0.0 || ^19.0.0", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@types/react": { @@ -4118,31 +4112,35 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/prop-types": { - "version": "15.7.12", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", - "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==", - "devOptional": true, - "license": "MIT" - }, "node_modules/@types/react": { - "version": "18.3.5", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.5.tgz", - "integrity": "sha512-WeqMfGJLGuLCqHGYRGHxnKrXcTitc6L/nBUWfWPcTarG3t9PsquqUMuVeXZeca+mglY4Vo5GZjCi0A3Or2lnxA==", - "devOptional": true, + "version": "19.0.2", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.2.tgz", + "integrity": "sha512-USU8ZI/xyKJwFTpjSVIrSeHBVAGagkHQKPNbxeWwql/vDmnTIBgx+TJnhFnj1NXgz8XfprU0egV2dROLGpsBEg==", + "dev": true, "license": "MIT", "dependencies": { - "@types/prop-types": "*", "csstype": "^3.0.2" } }, + "node_modules/@types/react-dom": { + "version": "19.0.2", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.0.2.tgz", + "integrity": "sha512-c1s+7TKFaDRRxr1TxccIX2u7sfCnc3RxkVyBIUA2lCpyqCF+QoAwQ/CBg7bsMdVwP120HEH143VQezKtef5nCg==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "peerDependencies": { + "@types/react": "^19.0.0" + } + }, "node_modules/@types/react-reconciler": { - "version": "0.28.8", - "resolved": "https://registry.npmjs.org/@types/react-reconciler/-/react-reconciler-0.28.8.tgz", - "integrity": "sha512-SN9c4kxXZonFhbX4hJrZy37yw9e7EIxcpHCxQv5JUS18wDE5ovkQKlqQEkufdJCCMfuI9BnjUJvhYeJ9x5Ra7g==", + "version": "0.28.9", + "resolved": "https://registry.npmjs.org/@types/react-reconciler/-/react-reconciler-0.28.9.tgz", + "integrity": "sha512-HHM3nxyUZ3zAylX8ZEyrDNd2XZOnQ0D5XfunJF5FLQnZbHHYq4UWvW1QfelQNXv1ICNkwYhfxjwfnqivYB6bFg==", "dev": true, "license": "MIT", - "dependencies": { + "peerDependencies": { "@types/react": "*" } }, @@ -13996,6 +13994,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, "license": "MIT" }, "node_modules/js-yaml": { @@ -14451,18 +14450,6 @@ "dev": true, "license": "MIT" }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "license": "MIT", - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, "node_modules/loupe": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.1.tgz", @@ -16474,29 +16461,25 @@ } }, "node_modules/react": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", - "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz", + "integrity": "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==", "license": "MIT", - "dependencies": { - "loose-envify": "^1.1.0" - }, "engines": { "node": ">=0.10.0" } }, "node_modules/react-dom": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", - "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0.tgz", + "integrity": "sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==", "dev": true, "license": "MIT", "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.2" + "scheduler": "^0.25.0" }, "peerDependencies": { - "react": "^18.3.1" + "react": "^19.0.0" } }, "node_modules/react-is": { @@ -16507,19 +16490,18 @@ "license": "MIT" }, "node_modules/react-reconciler": { - "version": "0.29.2", - "resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.29.2.tgz", - "integrity": "sha512-zZQqIiYgDCTP/f1N/mAR10nJGrPD2ZR+jDSEsKWJHYC7Cm2wodlwbR3upZRdC3cjIjSlTLNVyO7Iu0Yy7t2AYg==", + "version": "0.31.0", + "resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.31.0.tgz", + "integrity": "sha512-7Ob7Z+URmesIsIVRjnLoDGwBEG/tVitidU0nMsqX/eeJaLY89RISO/10ERe0MqmzuKUUB1rmY+h1itMbUHg9BQ==", "license": "MIT", "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.2" + "scheduler": "^0.25.0" }, "engines": { "node": ">=0.10.0" }, "peerDependencies": { - "react": "^18.3.1" + "react": "^19.0.0" } }, "node_modules/react-refresh": { @@ -17251,13 +17233,10 @@ } }, "node_modules/scheduler": { - "version": "0.23.2", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", - "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.1.0" - } + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0.tgz", + "integrity": "sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==", + "license": "MIT" }, "node_modules/secure-compare": { "version": "3.0.1", diff --git a/package.json b/package.json index d272b632..525c1b42 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ ] }, "dependencies": { - "react-reconciler": "0.29.2" + "react-reconciler": "0.31.0" }, "devDependencies": { "@pixi/extension-scripts": "^4.0.0", @@ -69,18 +69,18 @@ "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^15.2.3", "@testing-library/jest-dom": "^6.4.8", - "@testing-library/react": "^16.0.0", + "@testing-library/react": "^16.1.0", "@testing-library/user-event": "^14.5.2", - "@types/react": "^18.3.2", - "@types/react-reconciler": "0.28.8", + "@types/react": "^19.0.0", + "@types/react-reconciler": "^0.28.9", "@vitejs/plugin-react": "^4.3.1", "@vitest/browser": "^2.0.4", "husky": "^8.0.0", "jsdom": "^25.0.0", "pixi.js": "8.2.6", "playwright": "^1.45.3", - "react": "^18.3.1", - "react-dom": "^18.3.1", + "react": "^19.0.0", + "react-dom": "^19.0.0", "rollup": "^4.18.0", "rollup-plugin-esbuild": "^6.1.1", "rollup-plugin-sourcemaps": "^0.6.3", @@ -89,13 +89,7 @@ }, "peerDependencies": { "pixi.js": "^8.2.6", - "react": ">=18.0.0", - "react-dom": ">=18.0.0" - }, - "peerDependenciesMeta": { - "react-dom": { - "optional": true - } + "react": ">=19.0.0" }, "overrides": { "rollup": "^4.18.0" diff --git a/src/components/Application.ts b/src/components/Application.tsx similarity index 79% rename from src/components/Application.ts rename to src/components/Application.tsx index be080259..52f3e7f7 100644 --- a/src/components/Application.ts +++ b/src/components/Application.tsx @@ -6,10 +6,10 @@ import { import { createElement, forwardRef, - type ForwardRefRenderFunction, - type MutableRefObject, + type RefObject, useCallback, useEffect, + useImperativeHandle, useRef, } from 'react'; import { createRoot } from '../core/createRoot'; @@ -19,17 +19,16 @@ import { queueForUnmount } from '../helpers/queueForUnmount'; import { unqueueForUnmount } from '../helpers/unqueueForUnmount'; import { useIsomorphicLayoutEffect } from '../hooks/useIsomorphicLayoutEffect'; import { type ApplicationProps } from '../typedefs/ApplicationProps'; +import { type ApplicationRef } from '../typedefs/ApplicationRef'; const originalDefaultTextStyle = { ...TextStyle.defaultTextStyle }; -/** Creates a React root and renders a Pixi application. */ -export const ApplicationFunction: ForwardRefRenderFunction = ( +export const Application = forwardRef(function Application( props, forwardedRef, -) => +) { const { - attachToDevTools, children, className, defaultTextStyle, @@ -39,9 +38,20 @@ export const ApplicationFunction: ForwardRefRenderFunction = useRef(null); - const canvasRef: MutableRefObject = useRef(null); - const extensionsRef: MutableRefObject> = useRef(new Set()); + const applicationRef: RefObject = useRef(null); + const canvasRef = useRef(null); + const extensionsRef = useRef>(new Set()); + + useImperativeHandle(forwardedRef, () => ({ + getApplication() + { + return applicationRef.current; + }, + getCanvas() + { + return canvasRef.current; + }, + })); const updateResizeTo = useCallback(() => { @@ -75,31 +85,9 @@ export const ApplicationFunction: ForwardRefRenderFunction - { - globalScope.__PIXI_DEVTOOLS__ = { - app: application, - pixi, - renderer: application.renderer, - stage: application.stage, - }; - }); - } }, [onInit]); useIsomorphicLayoutEffect(() => @@ -193,8 +181,4 @@ export const ApplicationFunction: ForwardRefRenderFunction({} as ApplicationState); diff --git a/src/constants/NO_CONTEXT.ts b/src/constants/NO_CONTEXT.ts new file mode 100644 index 00000000..52dad511 --- /dev/null +++ b/src/constants/NO_CONTEXT.ts @@ -0,0 +1,3 @@ +import { type HostConfig } from '../typedefs/HostConfig'; + +export const NO_CONTEXT: HostConfig['hostContext'] = {}; diff --git a/src/core/createRoot.ts b/src/core/createRoot.ts index d2d4c601..22165eee 100644 --- a/src/core/createRoot.ts +++ b/src/core/createRoot.ts @@ -1,20 +1,19 @@ import { Application } from 'pixi.js'; +import { type ApplicationOptions } from 'pixi.js'; import { createElement } from 'react'; +import { type ReactNode } from 'react'; import { ConcurrentRoot } from 'react-reconciler/constants.js'; import { ContextProvider } from '../components/Context'; import { isReadOnlyProperty } from '../helpers/isReadOnlyProperty'; import { log } from '../helpers/log'; import { prepareInstance } from '../helpers/prepareInstance'; +import { type ApplicationState } from '../typedefs/ApplicationState'; +import { type CreateRootOptions } from '../typedefs/CreateRootOptions'; +import { type HostConfig } from '../typedefs/HostConfig'; +import { type InternalState } from '../typedefs/InternalState'; import { reconciler } from './reconciler'; import { roots } from './roots'; -import type { ApplicationOptions } from 'pixi.js'; -import type { ReactNode } from 'react'; -import type { ApplicationState } from '../typedefs/ApplicationState'; -import type { CreateRootOptions } from '../typedefs/CreateRootOptions'; -import type { HostConfig } from '../typedefs/HostConfig'; -import type { InternalState } from '../typedefs/InternalState'; - /** Creates a new root for a Pixi React app. */ export function createRoot( /** @description The DOM node which will serve as the root for this tree. */ @@ -49,15 +48,17 @@ export function createRoot( internalState.rootContainer = prepareInstance(applicationState.app.stage) as HostConfig['containerInstance']; } - const fiber = root?.fiber ?? reconciler.createContainer( - internalState.rootContainer, - ConcurrentRoot, - null, - false, - null, - '', - console.error, - null, + const fiber = root?.fiber ?? (reconciler as any).createContainer( + internalState.rootContainer, // container + ConcurrentRoot, // tag + null, // hydration callbacks + false, // isStrictMode + null, // concurrentUpdatesByDefaultOverride + '', // identifierPrefix + console.error, // onUncaughtError + console.error, // onCaughtError + console.error, // onRecoverableError + null, // transitionCallbacks ); if (!root) @@ -98,7 +99,7 @@ export function createRoot( Object.entries(applicationOptions).forEach(([key, value]) => { - const typedKey = /** @type {keyof ApplicationOptions} */ (key); + const typedKey = key as keyof ApplicationOptions; if (isReadOnlyProperty( applicationOptions as unknown as Record, diff --git a/src/core/reconciler.ts b/src/core/reconciler.ts index 37a52dd2..e5d32937 100644 --- a/src/core/reconciler.ts +++ b/src/core/reconciler.ts @@ -1,6 +1,3 @@ -/* eslint-disable no-empty-function */ - -import Reconciler from 'react-reconciler'; import packageData from '../../package.json' with { type: 'json' }; import { afterActiveInstanceBlur } from '../helpers/afterActiveInstanceBlur'; import { appendChild } from '../helpers/appendChild'; @@ -8,48 +5,50 @@ import { beforeActiveInstanceBlur } from '../helpers/beforeActiveInstanceBlur'; import { clearContainer } from '../helpers/clearContainer'; import { commitUpdate } from '../helpers/commitUpdate'; import { createInstance } from '../helpers/createInstance'; +import { createReconciler } from '../helpers/createReconciler'; import { createTextInstance } from '../helpers/createTextInstance'; import { detachDeletedInstance } from '../helpers/detachDeletedInstance'; import { finalizeInitialChildren } from '../helpers/finalizeInitialChildren'; import { getChildHostContext } from '../helpers/getChildHostContext'; -import { getCurrentEventPriority } from '../helpers/getCurrentEventPriority'; +import { getCurrentUpdatePriority } from '../helpers/getCurrentUpdatePriority'; import { getInstanceFromNode } from '../helpers/getInstanceFromNode'; import { getInstanceFromScope } from '../helpers/getInstanceFromScope'; import { getPublicInstance } from '../helpers/getPublicInstance'; import { getRootHostContext } from '../helpers/getRootHostContext'; import { hideInstance } from '../helpers/hideInstance'; +import { hideTextInstance } from '../helpers/hideTextInstance'; import { insertBefore } from '../helpers/insertBefore'; +import { maySuspendCommit } from '../helpers/maySuspendCommit'; +import { preloadInstance } from '../helpers/preloadInstance'; import { prepareForCommit } from '../helpers/prepareForCommit'; import { preparePortalMount } from '../helpers/preparePortalMount'; import { prepareScopeUpdate } from '../helpers/prepareScopeUpdate'; -import { prepareUpdate } from '../helpers/prepareUpdate'; import { removeChild } from '../helpers/removeChild'; +import { requestPostPaintCallback } from '../helpers/requestPostPaintCallback'; import { resetAfterCommit } from '../helpers/resetAfterCommit'; +import { resetFormInstance } from '../helpers/resetFormInstance'; +import { resolveEventTimeStamp } from '../helpers/resolveEventTimeStamp'; +import { resolveEventType } from '../helpers/resolveEventType'; +import { resolveUpdatePriority } from '../helpers/resolveUpdatePriority'; +import { setCurrentUpdatePriority } from '../helpers/setCurrentUpdatePriority'; +import { shouldAttemptEagerTransition } from '../helpers/shouldAttemptEagerTransition'; import { shouldSetTextContent } from '../helpers/shouldSetTextContent'; +import { startSuspendingCommit } from '../helpers/startSuspendingCommit'; +import { suspendInstance } from '../helpers/suspendInstance'; +import { trackSchedulerEvent } from '../helpers/trackSchedulerEvent'; import { unhideInstance } from '../helpers/unhideInstance'; +import { unhideTextInstance } from '../helpers/unhideTextInstance'; +import { waitForCommitToBeReady } from '../helpers/waitForCommitToBeReady'; +import { type HostConfig } from '../typedefs/HostConfig'; -import type { HostConfig } from '../typedefs/HostConfig'; - -const reconcilerConfig: Reconciler.HostConfig< -HostConfig['type'], -HostConfig['props'], -HostConfig['containerInstance'], -HostConfig['instance'], -HostConfig['textInstance'], -HostConfig['suspenseInstance'], -HostConfig['hydratableInstance'], -HostConfig['publicInstance'], -HostConfig['hostContext'], -HostConfig['updatePayload'], -HostConfig['childSet'], -HostConfig['timeoutHandle'], -HostConfig['noTimeout'] -> = { +const reconcilerConfig = { isPrimaryRenderer: false, noTimeout: -1, + NotPendingTransition: null, supportsHydration: false, supportsMutation: true, supportsPersistence: false, + warnsIfNotActing: false, afterActiveInstanceBlur, appendChild, @@ -61,10 +60,12 @@ HostConfig['noTimeout'] commitUpdate, createInstance, createTextInstance, + hideTextInstance, + unhideTextInstance, detachDeletedInstance, finalizeInitialChildren, getChildHostContext, - getCurrentEventPriority, + getCurrentUpdatePriority, getInstanceFromNode, getInstanceFromScope, getPublicInstance, @@ -72,19 +73,46 @@ HostConfig['noTimeout'] hideInstance, insertBefore, insertInContainerBefore: insertBefore, + maySuspendCommit, + preloadInstance, prepareForCommit, preparePortalMount, prepareScopeUpdate, - prepareUpdate, removeChild, removeChildFromContainer: removeChild, + requestPostPaintCallback, resetAfterCommit, + resetFormInstance, + resolveEventTimeStamp, + resolveEventType, + resolveUpdatePriority, scheduleTimeout: setTimeout, + shouldAttemptEagerTransition, + setCurrentUpdatePriority, shouldSetTextContent, + startSuspendingCommit, + suspendInstance, + trackSchedulerEvent, unhideInstance, + waitForCommitToBeReady, }; -const reconciler = Reconciler(reconcilerConfig); +const reconciler = createReconciler< + HostConfig['type'], + HostConfig['props'], + HostConfig['containerInstance'], + HostConfig['instance'], + HostConfig['textInstance'], + HostConfig['suspenseInstance'], + HostConfig['hydratableInstance'], + HostConfig['formInstance'], + HostConfig['publicInstance'], + HostConfig['hostContext'], + HostConfig['childSet'], + HostConfig['timeoutHandle'], + HostConfig['noTimeout'], + HostConfig['transitionStatus'] +>(reconcilerConfig); reconciler.injectIntoDevTools({ bundleType: process.env.NODE_ENV === 'production' ? 0 : 1, diff --git a/src/core/roots.ts b/src/core/roots.ts index 26f6f480..d499ca20 100644 --- a/src/core/roots.ts +++ b/src/core/roots.ts @@ -1,4 +1,4 @@ -import type { Root } from '../typedefs/Root'; +import { type Root } from '../typedefs/Root'; /** We store roots here since we can render to multiple canvases. */ export const roots: Map = new Map(); diff --git a/src/global.ts b/src/global.ts index f7763403..9a1651fb 100644 --- a/src/global.ts +++ b/src/global.ts @@ -1,7 +1,29 @@ -import type { NamespacedPixiElements } from './typedefs/NamespacedPixiElements'; -import type { PixiElements } from './typedefs/PixiElements'; +import { type NamespacedPixiElements } from './typedefs/NamespacedPixiElements'; +import { type PixiElements } from './typedefs/PixiElements'; -declare global +import type {} from 'react'; +import type {} from 'react/jsx-dev-runtime'; +import type {} from 'react/jsx-runtime'; + +declare module 'react' +{ + // eslint-disable-next-line @typescript-eslint/no-namespace + namespace JSX + { + interface IntrinsicElements extends PixiElements, NamespacedPixiElements {} + } +} + +declare module 'react/jsx-runtime' +{ + // eslint-disable-next-line @typescript-eslint/no-namespace + namespace JSX + { + interface IntrinsicElements extends PixiElements, NamespacedPixiElements {} + } +} + +declare module 'react/jsx-dev-runtime' { // eslint-disable-next-line @typescript-eslint/no-namespace namespace JSX diff --git a/src/helpers/appendChild.ts b/src/helpers/appendChild.ts index 22170daa..5af1a405 100644 --- a/src/helpers/appendChild.ts +++ b/src/helpers/appendChild.ts @@ -2,11 +2,10 @@ import { Container, Filter, } from 'pixi.js'; +import { type HostConfig } from '../typedefs/HostConfig'; import { attach } from './attach'; import { log } from './log'; -import type { HostConfig } from '../typedefs/HostConfig'; - /** Adds elements to our application. */ export function appendChild( parentNode: HostConfig['containerInstance'], diff --git a/src/helpers/applyProps.ts b/src/helpers/applyProps.ts index babe46a3..2073845a 100644 --- a/src/helpers/applyProps.ts +++ b/src/helpers/applyProps.ts @@ -2,10 +2,17 @@ import { Container, Graphics, } from 'pixi.js'; +import { + type FederatedPointerEvent, + type FederatedWheelEvent, +} from 'pixi.js'; import { PixiToReactEventPropNames, ReactToPixiEventPropNames, } from '../constants/EventPropNames'; +import { type DiffSet } from '../typedefs/DiffSet'; +import { type HostConfig } from '../typedefs/HostConfig'; +import { type InstanceState } from '../typedefs/InstanceState'; import { isNull, isUndefined, @@ -15,14 +22,6 @@ import { isDiffSet } from './isDiffSet'; import { isReadOnlyProperty } from './isReadOnlyProperty'; import { log } from './log'; -import type { - FederatedPointerEvent, - FederatedWheelEvent, -} from 'pixi.js'; -import type { DiffSet } from '../typedefs/DiffSet'; -import type { HostConfig } from '../typedefs/HostConfig'; -import type { InstanceState } from '../typedefs/InstanceState'; - const DEFAULT = '__default'; const DEFAULTS_CONTAINERS = new Map(); @@ -52,7 +51,6 @@ export function applyProps( ) { const { - // eslint-disable-next-line @typescript-eslint/no-unused-vars __pixireact: instanceState = {} as InstanceState, ...instanceProps } = instance; @@ -144,8 +142,7 @@ export function applyProps( if (!ctor) { - /** @type {Container} */ - ctor = /** @type {*} */ (currentInstance.constructor); + ctor = currentInstance.constructor; // eslint-disable-next-line new-cap ctor = new ctor(); diff --git a/src/helpers/attach.ts b/src/helpers/attach.ts index 31d6f880..395a118f 100644 --- a/src/helpers/attach.ts +++ b/src/helpers/attach.ts @@ -1,6 +1,5 @@ import { Filter } from 'pixi.js'; - -import type { HostConfig } from '../typedefs/HostConfig'; +import { type HostConfig } from '../typedefs/HostConfig'; export function attach( parentInstance: HostConfig['containerInstance'], diff --git a/src/helpers/catalogue.ts b/src/helpers/catalogue.ts index d93c2092..6416f460 100644 --- a/src/helpers/catalogue.ts +++ b/src/helpers/catalogue.ts @@ -1,4 +1,4 @@ -import type { HostConfig } from '../typedefs/HostConfig'; +import { type HostConfig } from '../typedefs/HostConfig'; export const catalogue: { [name: string]: { diff --git a/src/helpers/commitUpdate.ts b/src/helpers/commitUpdate.ts index 683141de..0ef1396a 100644 --- a/src/helpers/commitUpdate.ts +++ b/src/helpers/commitUpdate.ts @@ -1,32 +1,25 @@ import { applyProps } from '../helpers/applyProps'; import { log } from '../helpers/log'; -import { switchInstance } from './switchInstance'; - -import type { Fiber } from 'react-reconciler'; -import type { HostConfig } from '../typedefs/HostConfig'; -import type { UpdatePayload } from '../typedefs/UpdatePayload'; +import { type HostConfig } from '../typedefs/HostConfig'; +import { prepareUpdate } from './prepareUpdate'; export function commitUpdate( instance: HostConfig['instance'], - updatePayload: UpdatePayload, type: HostConfig['type'], - _oldProps: HostConfig['props'], + oldProps: HostConfig['props'], newProps: HostConfig['props'], - fiber: Fiber, ) { log('info', 'lifecycle::commitUpdate'); - const { - diff, - shouldReconstruct, - } = updatePayload; + const diff = prepareUpdate( + instance, + type, + oldProps, + newProps, + ); - if (shouldReconstruct) - { - switchInstance(instance, type, newProps, fiber); - } - else if (diff) + if (diff) { applyProps(instance, diff); } diff --git a/src/helpers/createInstance.ts b/src/helpers/createInstance.ts index d065fd27..7c1895ed 100644 --- a/src/helpers/createInstance.ts +++ b/src/helpers/createInstance.ts @@ -1,5 +1,6 @@ import { ReactToPixiEventPropNames } from '../constants/EventPropNames'; import { PixiReactIgnoredProps } from '../constants/PixiReactIgnoredProps'; +import { type HostConfig } from '../typedefs/HostConfig'; import { applyProps } from './applyProps'; import { catalogue } from './catalogue'; import { convertStringToPascalCase } from './convertStringToPascalCase'; @@ -8,8 +9,6 @@ import { log } from './log'; import { parseComponentType } from './parseComponentType'; import { prepareInstance } from './prepareInstance'; -import type { HostConfig } from '../typedefs/HostConfig'; - export function createInstance( type: HostConfig['type'], props: HostConfig['props'], diff --git a/src/helpers/createReconciler.ts b/src/helpers/createReconciler.ts new file mode 100644 index 00000000..ff953233 --- /dev/null +++ b/src/helpers/createReconciler.ts @@ -0,0 +1,109 @@ +import Reconciler from 'react-reconciler'; +import { type EventPriority } from '../typedefs/EventPriority'; + +export const createReconciler = Reconciler as unknown as < + Type, + Props, + Container, + Instance, + TextInstance, + SuspenseInstance, + HydratableInstance, + FormInstance, + PublicInstance, + HostContext, + ChildSet, + TimeoutHandle, + NoTimeout, + TransitionStatus, +>( + config: Omit< + Reconciler.HostConfig< + Type, + Props, + Container, + Instance, + TextInstance, + SuspenseInstance, + HydratableInstance, + PublicInstance, + HostContext, + null, // updatePayload + ChildSet, + TimeoutHandle, + NoTimeout + >, + 'getCurrentEventPriority' | 'prepareUpdate' | 'commitUpdate' + > & { + /** + * This method should mutate the `instance` and perform prop diffing if needed. + * + * The `internalHandle` data structure is meant to be opaque. If you bend the rules and rely on its internal fields, be aware that it may change significantly between versions. You're taking on additional maintenance risk by reading from it, and giving up all guarantees if you write something to it. + */ + commitUpdate?( + instance: Instance, + type: Type, + prevProps: Props, + nextProps: Props, + internalHandle: Reconciler.OpaqueHandle, + ): void + + // Undocumented + // https://github.com/facebook/react/pull/26722 + NotPendingTransition: TransitionStatus | null + + // https://github.com/facebook/react/pull/28751 + setCurrentUpdatePriority(newPriority: EventPriority): void + + getCurrentUpdatePriority(): EventPriority + + resolveUpdatePriority(): EventPriority + + // https://github.com/facebook/react/pull/28804 + resetFormInstance(form: FormInstance): void + + // https://github.com/facebook/react/pull/25105 + requestPostPaintCallback(callback: (time: number) => void): void + + // https://github.com/facebook/react/pull/26025 + shouldAttemptEagerTransition(): boolean + + // https://github.com/facebook/react/pull/31528 + trackSchedulerEvent(): void + + // https://github.com/facebook/react/pull/31008 + resolveEventType(): null | string + + resolveEventTimeStamp(): number + + /** + * This method is called during render to determine if the Host Component type and props require some kind of loading process to complete before committing an update. + */ + maySuspendCommit(type: Type, props: Props): boolean + + /** + * This method may be called during render if the Host Component type and props might suspend a commit. It can be used to initiate any work that might shorten the duration of a suspended commit. + */ + preloadInstance(type: Type, props: Props): boolean + + /** + * This method is called just before the commit phase. Use it to set up any necessary state while any Host Components that might suspend this commit are evaluated to determine if the commit must be suspended. + */ + startSuspendingCommit(): void + + /** + * This method is called after `startSuspendingCommit` for each Host Component that indicated it might suspend a commit. + */ + suspendInstance(type: Type, props: Props): void + + /** + * This method is called after all `suspendInstance` calls are complete. + * + * Return `null` if the commit can happen immediately. + * + * Return `(initiateCommit: Function) => Function` if the commit must be suspended. The argument to this callback will initiate the commit when called. The return value is a cancellation function that the Reconciler can use to abort the commit. + * + */ + waitForCommitToBeReady(): ((initiateCommit: (...args: unknown[]) => unknown) => (...args: unknown[]) => unknown) | null + }, +) => Reconciler.Reconciler; diff --git a/src/helpers/createTextInstance.ts b/src/helpers/createTextInstance.ts index fd9fc2d8..90013932 100644 --- a/src/helpers/createTextInstance.ts +++ b/src/helpers/createTextInstance.ts @@ -1,17 +1,15 @@ +import { type PixiReactNode } from 'typedefs/PixiReactNode'; +import { type HostConfig } from '../typedefs/HostConfig'; import { log } from './log'; -import type { HostConfig } from '../typedefs/HostConfig'; - /** Always throws, because we don't support this (yet). */ export function createTextInstance( _text: string, _rootContainer: HostConfig['containerInstance'], - _hostContext: null, + _hostContext: HostConfig['hostContext'], _internalHandle: any, -) +): PixiReactNode { log('info', 'lifecycle::createTextInstance'); throw new Error('Text instances are not yet supported. Please use a `` component.'); - - return _rootContainer; } diff --git a/src/helpers/detach.ts b/src/helpers/detach.ts index d8ac3c90..85caefaf 100644 --- a/src/helpers/detach.ts +++ b/src/helpers/detach.ts @@ -1,6 +1,5 @@ import { Filter } from 'pixi.js'; - -import type { HostConfig } from '../typedefs/HostConfig'; +import { type HostConfig } from '../typedefs/HostConfig'; export function detach( childInstance: HostConfig['instance'], diff --git a/src/helpers/diffProps.ts b/src/helpers/diffProps.ts index f309ab16..ad54d2f2 100644 --- a/src/helpers/diffProps.ts +++ b/src/helpers/diffProps.ts @@ -1,10 +1,9 @@ import { ReactToPixiEventPropNames } from '../constants/EventPropNames'; +import { type Change } from '../typedefs/Change'; +import { type HostConfig } from '../typedefs/HostConfig'; import { isEqual } from './compare'; import { gentleCloneProps } from './gentleCloneProps'; -import type { Change } from '../typedefs/Change'; -import type { HostConfig } from '../typedefs/HostConfig'; - const DEFAULT = '__default'; export function diffProps( diff --git a/src/helpers/getAssetKey.ts b/src/helpers/getAssetKey.ts index ef58e123..b075780b 100644 --- a/src/helpers/getAssetKey.ts +++ b/src/helpers/getAssetKey.ts @@ -1,4 +1,4 @@ -import type { UnresolvedAsset } from '../typedefs/UnresolvedAsset'; +import { type UnresolvedAsset } from '../typedefs/UnresolvedAsset'; /** Retrieves the key from an unresolved asset. */ export function getAssetKey(asset: UnresolvedAsset) diff --git a/src/helpers/getChildHostContext.ts b/src/helpers/getChildHostContext.ts index 62bdc1e0..cda6b391 100644 --- a/src/helpers/getChildHostContext.ts +++ b/src/helpers/getChildHostContext.ts @@ -1,8 +1,9 @@ +import { NO_CONTEXT } from '../constants/NO_CONTEXT'; import { log } from './log'; -export function getChildHostContext(parentHostContext: T) +export function getChildHostContext(childHostContext: T) { log('info', 'lifecycle::getChildHostContext'); - return parentHostContext; + return childHostContext ?? NO_CONTEXT; } diff --git a/src/helpers/getCurrentUpdatePriority.ts b/src/helpers/getCurrentUpdatePriority.ts new file mode 100644 index 00000000..777729c7 --- /dev/null +++ b/src/helpers/getCurrentUpdatePriority.ts @@ -0,0 +1,9 @@ +import { store } from '../store'; +import { log } from './log'; + +export function getCurrentUpdatePriority() +{ + log('info', 'lifecycle::getCurrentUpdatePriority'); + + return store.currentUpdatePriority; +} diff --git a/src/helpers/getRootHostContext.ts b/src/helpers/getRootHostContext.ts index 5ea0367e..8747d9a2 100644 --- a/src/helpers/getRootHostContext.ts +++ b/src/helpers/getRootHostContext.ts @@ -1,3 +1,4 @@ +import { NO_CONTEXT } from '../constants/NO_CONTEXT'; import { log } from './log'; /** Retrieves the host context from the root container. */ @@ -5,5 +6,5 @@ export function getRootHostContext() { log('info', 'lifecycle::getRootHostContext'); - return null; + return NO_CONTEXT; } diff --git a/src/helpers/hideInstance.ts b/src/helpers/hideInstance.ts index 8c7d0d73..2156c92a 100644 --- a/src/helpers/hideInstance.ts +++ b/src/helpers/hideInstance.ts @@ -2,8 +2,7 @@ import { Container, Filter, } from 'pixi.js'; - -import type { HostConfig } from '../typedefs/HostConfig'; +import { type HostConfig } from '../typedefs/HostConfig'; export function hideInstance( instance: HostConfig['instance'] diff --git a/src/helpers/hideTextInstance.ts b/src/helpers/hideTextInstance.ts new file mode 100644 index 00000000..d1fa5076 --- /dev/null +++ b/src/helpers/hideTextInstance.ts @@ -0,0 +1,11 @@ +import { type HostConfig } from '../typedefs/HostConfig'; +import { log } from './log'; + +/** Always throws, because we don't support this (yet). */ +export function hideTextInstance( + _textInstance: HostConfig['textInstance'], +) +{ + log('info', 'lifecycle::hideTextInstance'); + throw new Error('Text instances are not yet supported. Please use a `` component.'); +} diff --git a/src/helpers/insertBefore.ts b/src/helpers/insertBefore.ts index e0bb0a53..78b4c3be 100644 --- a/src/helpers/insertBefore.ts +++ b/src/helpers/insertBefore.ts @@ -2,13 +2,12 @@ import { Container, Filter, } from 'pixi.js'; +import { type HostConfig } from '../typedefs/HostConfig'; import { attach } from './attach'; import { detach } from './detach'; import { invariant } from './invariant'; import { log } from './log'; -import type { HostConfig } from '../typedefs/HostConfig'; - export function insertBefore( parentInstance: HostConfig['containerInstance'], childInstance: HostConfig['instance'], diff --git a/src/helpers/isDiffSet.ts b/src/helpers/isDiffSet.ts index 0d5f2c19..42702215 100644 --- a/src/helpers/isDiffSet.ts +++ b/src/helpers/isDiffSet.ts @@ -1,4 +1,4 @@ -import type { DiffSet } from '../typedefs/DiffSet'; +import { type DiffSet } from '../typedefs/DiffSet'; /** Whether the input is a diff set. */ export function isDiffSet(input: any): input is DiffSet diff --git a/src/helpers/log.ts b/src/helpers/log.ts index e1bd3407..3ce6dfe0 100644 --- a/src/helpers/log.ts +++ b/src/helpers/log.ts @@ -14,7 +14,6 @@ export function log(logType: LogType, ...args: any[]) if (!(logMethod instanceof Function)) { - // eslint-disable-next-line no-console console.warn(`Attempted to create an invalid log type: "${logType}"`); return; diff --git a/src/helpers/maySuspendCommit.ts b/src/helpers/maySuspendCommit.ts new file mode 100644 index 00000000..77f89895 --- /dev/null +++ b/src/helpers/maySuspendCommit.ts @@ -0,0 +1,8 @@ +import { log } from './log'; + +export function maySuspendCommit() +{ + log('info', 'lifecycle::maySuspendCommit'); + + return false; +} diff --git a/src/helpers/preloadInstance.ts b/src/helpers/preloadInstance.ts new file mode 100644 index 00000000..553a5bc3 --- /dev/null +++ b/src/helpers/preloadInstance.ts @@ -0,0 +1,8 @@ +import { log } from './log'; + +export function preloadInstance() +{ + log('info', 'lifecycle::preloadInstance'); + + return true; +} diff --git a/src/helpers/prepareInstance.ts b/src/helpers/prepareInstance.ts index d5e29bdf..b18d37b6 100644 --- a/src/helpers/prepareInstance.ts +++ b/src/helpers/prepareInstance.ts @@ -1,9 +1,9 @@ -import type { - Container, - Filter, +import { + type Container, + type Filter, } from 'pixi.js'; -import type { HostConfig } from '../typedefs/HostConfig'; -import type { InstanceState } from '../typedefs/InstanceState'; +import { type HostConfig } from '../typedefs/HostConfig'; +import { type InstanceState } from '../typedefs/InstanceState'; /** Create the instance with the provided sate and attach the component to it. */ export function prepareInstance( diff --git a/src/helpers/prepareUpdate.ts b/src/helpers/prepareUpdate.ts index 317f033a..73fe8aa2 100644 --- a/src/helpers/prepareUpdate.ts +++ b/src/helpers/prepareUpdate.ts @@ -1,9 +1,7 @@ +import { type HostConfig } from '../typedefs/HostConfig'; import { diffProps } from './diffProps'; import { log } from './log'; -import type { HostConfig } from '../typedefs/HostConfig'; -import type { UpdatePayload } from '../typedefs/UpdatePayload'; - export function prepareUpdate( _instance: HostConfig['instance'], _type: HostConfig['type'], @@ -13,17 +11,11 @@ export function prepareUpdate( { log('info', 'lifecycle::prepareUpdate'); - const updatePayload: UpdatePayload = { - shouldReconstruct: false, - }; - const { - // eslint-disable-next-line @typescript-eslint/no-unused-vars children: newChildren, ...newPropsRest } = newProps; const { - // eslint-disable-next-line @typescript-eslint/no-unused-vars children: oldChildren, ...oldPropsRest } = oldProps; @@ -32,8 +24,8 @@ export function prepareUpdate( if (diff.changes.length) { - updatePayload.diff = diff; + return diff; } - return updatePayload; + return null; } diff --git a/src/helpers/removeChild.ts b/src/helpers/removeChild.ts index 032fe6b5..0409d5a8 100644 --- a/src/helpers/removeChild.ts +++ b/src/helpers/removeChild.ts @@ -1,9 +1,8 @@ import { Filter } from 'pixi.js'; +import { type HostConfig } from '../typedefs/HostConfig'; import { detach } from './detach'; import { log } from './log'; -import type { HostConfig } from '../typedefs/HostConfig'; - /** Removes elements from our scene and disposes of them. */ export function removeChild( _parentInstance: HostConfig['containerInstance'], diff --git a/src/helpers/requestPostPaintCallback.ts b/src/helpers/requestPostPaintCallback.ts new file mode 100644 index 00000000..522e543a --- /dev/null +++ b/src/helpers/requestPostPaintCallback.ts @@ -0,0 +1,8 @@ +import { log } from './log'; + +export function requestPostPaintCallback( + _callback: (time: number) => void, +) +{ + log('info', 'lifecycle::requestPostPaintCallback'); +} diff --git a/src/helpers/resetFormInstance.ts b/src/helpers/resetFormInstance.ts new file mode 100644 index 00000000..9c049885 --- /dev/null +++ b/src/helpers/resetFormInstance.ts @@ -0,0 +1,6 @@ +import { log } from './log'; + +export function resetFormInstance() +{ + log('info', 'lifecycle::resetFormInstance'); +} diff --git a/src/helpers/resolveEventTimeStamp.ts b/src/helpers/resolveEventTimeStamp.ts new file mode 100644 index 00000000..b02a55c2 --- /dev/null +++ b/src/helpers/resolveEventTimeStamp.ts @@ -0,0 +1,8 @@ +import { log } from './log'; + +export function resolveEventTimeStamp() +{ + log('info', 'lifecycle::resolveEventTimeStamp'); + + return -1.1; +} diff --git a/src/helpers/resolveEventType.ts b/src/helpers/resolveEventType.ts new file mode 100644 index 00000000..35062bec --- /dev/null +++ b/src/helpers/resolveEventType.ts @@ -0,0 +1,8 @@ +import { log } from './log'; + +export function resolveEventType() +{ + log('info', 'lifecycle::resolveEventType'); + + return null; +} diff --git a/src/helpers/getCurrentEventPriority.ts b/src/helpers/resolveUpdatePriority.ts similarity index 76% rename from src/helpers/getCurrentEventPriority.ts rename to src/helpers/resolveUpdatePriority.ts index b815daf5..d78c1d1d 100644 --- a/src/helpers/getCurrentEventPriority.ts +++ b/src/helpers/resolveUpdatePriority.ts @@ -2,12 +2,18 @@ import { ContinuousEventPriority, DefaultEventPriority, DiscreteEventPriority, -} from 'react-reconciler/constants.js'; +} from 'react-reconciler/constants'; +import { store } from '../store'; import { log } from './log'; -export function getCurrentEventPriority() +export function resolveUpdatePriority() { - log('info', 'lifecycle::getCurrentEventPriority'); + log('info', 'lifecycle::resolveUpdatePriority'); + + if (store.currentUpdatePriority) + { + return store.currentUpdatePriority; + } const globalScope = (typeof self !== 'undefined' && self) || (typeof window !== 'undefined' && window); diff --git a/src/helpers/setCurrentUpdatePriority.ts b/src/helpers/setCurrentUpdatePriority.ts new file mode 100644 index 00000000..f872a3ec --- /dev/null +++ b/src/helpers/setCurrentUpdatePriority.ts @@ -0,0 +1,9 @@ +import { store } from '../store'; +import { log } from './log'; + +export function setCurrentUpdatePriority(newPriority: number) +{ + log('info', 'lifecycle::setCurrentUpdatePriority'); + + store.currentUpdatePriority = newPriority; +} diff --git a/src/helpers/shouldAttemptEagerTransition.ts b/src/helpers/shouldAttemptEagerTransition.ts new file mode 100644 index 00000000..9fd8909b --- /dev/null +++ b/src/helpers/shouldAttemptEagerTransition.ts @@ -0,0 +1,8 @@ +import { log } from './log'; + +export function shouldAttemptEagerTransition() +{ + log('info', 'lifecycle::shouldAttemptEagerTransition'); + + return false; +} diff --git a/src/helpers/startSuspendingCommit.ts b/src/helpers/startSuspendingCommit.ts new file mode 100644 index 00000000..e440638b --- /dev/null +++ b/src/helpers/startSuspendingCommit.ts @@ -0,0 +1,6 @@ +import { log } from './log'; + +export function startSuspendingCommit() +{ + log('info', 'lifecycle::startSuspendingCommit'); +} diff --git a/src/helpers/suspendInstance.ts b/src/helpers/suspendInstance.ts new file mode 100644 index 00000000..0cfb758f --- /dev/null +++ b/src/helpers/suspendInstance.ts @@ -0,0 +1,6 @@ +import { log } from './log'; + +export function suspendInstance() +{ + log('info', 'lifecycle::suspendInstance'); +} diff --git a/src/helpers/switchInstance.ts b/src/helpers/switchInstance.ts deleted file mode 100644 index cb405557..00000000 --- a/src/helpers/switchInstance.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { appendChild } from './appendChild'; -import { createInstance } from './createInstance'; -import { removeChild } from './removeChild'; - -import type { Fiber } from 'react-reconciler'; -import type { HostConfig } from '../typedefs/HostConfig'; - -export function switchInstance( - instance: HostConfig['instance'], - type: HostConfig['type'], - newProps: HostConfig['props'], - fiber: Fiber, -) -{ - const parent = instance.__pixireact?.parent; - - if (!parent) - { - return; - } - - const root = instance.__pixireact.root as HostConfig['containerInstance']; - const newInstance = createInstance(type, newProps, root); - - if (!instance.__pixireact.autoRemovedBeforeAppend) - { - removeChild(parent, instance); - } - - if (newInstance.parent) - { - newInstance.__pixireact.autoRemovedBeforeAppend = true; - } - - appendChild(parent as HostConfig['containerInstance'], newInstance); - - // This evil hack switches the react-internal fiber node - // https://github.com/facebook/react/issues/14983 - // https://github.com/facebook/react/pull/15021 - const fibers = [fiber, fiber.alternate]; - - fibers.forEach((fiber) => - { - if (fiber !== null) - { - fiber.stateNode = newInstance; - - if (fiber.ref) - { - if (typeof fiber.ref === 'function') - { - fiber.ref(newInstance); - } - else - { - const ref = fiber.ref; - - ref.current = newInstance; - } - } - } - }); -} diff --git a/src/helpers/trackSchedulerEvent.ts b/src/helpers/trackSchedulerEvent.ts new file mode 100644 index 00000000..5e008949 --- /dev/null +++ b/src/helpers/trackSchedulerEvent.ts @@ -0,0 +1,6 @@ +import { log } from './log'; + +export function trackSchedulerEvent() +{ + log('info', 'lifecycle::trackSchedulerEvent'); +} diff --git a/src/helpers/unhideInstance.ts b/src/helpers/unhideInstance.ts index 598703d0..55f90bec 100644 --- a/src/helpers/unhideInstance.ts +++ b/src/helpers/unhideInstance.ts @@ -2,8 +2,7 @@ import { Container, Filter, } from 'pixi.js'; - -import type { HostConfig } from '../typedefs/HostConfig'; +import { type HostConfig } from '../typedefs/HostConfig'; export function unhideInstance( instance: HostConfig['instance'], diff --git a/src/helpers/unhideTextInstance.ts b/src/helpers/unhideTextInstance.ts new file mode 100644 index 00000000..f95a9c27 --- /dev/null +++ b/src/helpers/unhideTextInstance.ts @@ -0,0 +1,11 @@ +import { type HostConfig } from '../typedefs/HostConfig'; +import { log } from './log'; + +/** Always throws, because we don't support this (yet). */ +export function unhideTextInstance( + _textInstance: HostConfig['textInstance'], +) +{ + log('info', 'lifecycle::unhideTextInstance'); + throw new Error('Text instances are not yet supported. Please use a `` component.'); +} diff --git a/src/helpers/waitForCommitToBeReady.ts b/src/helpers/waitForCommitToBeReady.ts new file mode 100644 index 00000000..4da7922d --- /dev/null +++ b/src/helpers/waitForCommitToBeReady.ts @@ -0,0 +1,8 @@ +import { log } from './log'; + +export function waitForCommitToBeReady() +{ + log('info', 'lifecycle::waitForCommitToBeReady'); + + return null; +} diff --git a/src/hooks/useAsset.ts b/src/hooks/useAsset.ts index 20a0b30d..57076b45 100644 --- a/src/hooks/useAsset.ts +++ b/src/hooks/useAsset.ts @@ -2,15 +2,14 @@ import { Assets, Cache, } from 'pixi.js'; -import { getAssetKey } from '../helpers/getAssetKey'; - -import type { - ProgressCallback, - UnresolvedAsset, +import { + type ProgressCallback, + type UnresolvedAsset, } from 'pixi.js'; -import type { AssetRetryOptions } from '../typedefs/AssetRetryOptions'; -import type { AssetRetryState } from '../typedefs/AssetRetryState'; -import type { ErrorCallback } from '../typedefs/ErrorCallback'; +import { getAssetKey } from '../helpers/getAssetKey'; +import { type AssetRetryOptions } from '../typedefs/AssetRetryOptions'; +import { type AssetRetryState } from '../typedefs/AssetRetryState'; +import { type ErrorCallback } from '../typedefs/ErrorCallback'; const errorCache: Map = new Map(); diff --git a/src/hooks/useAssets.ts b/src/hooks/useAssets.ts index b88a37cd..47b1c42e 100644 --- a/src/hooks/useAssets.ts +++ b/src/hooks/useAssets.ts @@ -5,11 +5,10 @@ import { import { useState } from 'react'; import { UseAssetsStatus } from '../constants/UseAssetsStatus'; import { getAssetKey } from '../helpers/getAssetKey'; - -import type { AssetRetryState } from '../typedefs/AssetRetryState'; -import type { UnresolvedAsset } from '../typedefs/UnresolvedAsset'; -import type { UseAssetsOptions } from '../typedefs/UseAssetsOptions'; -import type { UseAssetsResult } from '../typedefs/UseAssetsResult'; +import { type AssetRetryState } from '../typedefs/AssetRetryState'; +import { type UnresolvedAsset } from '../typedefs/UnresolvedAsset'; +import { type UseAssetsOptions } from '../typedefs/UseAssetsOptions'; +import { type UseAssetsResult } from '../typedefs/UseAssetsResult'; const errorCache: Map = new Map(); diff --git a/src/hooks/useSuspenseAssets.ts b/src/hooks/useSuspenseAssets.ts index 992a8904..4a1eb0e8 100644 --- a/src/hooks/useSuspenseAssets.ts +++ b/src/hooks/useSuspenseAssets.ts @@ -3,10 +3,9 @@ import { Cache, } from 'pixi.js'; import { getAssetKey } from '../helpers/getAssetKey'; - -import type { AssetRetryState } from '../typedefs/AssetRetryState'; -import type { UnresolvedAsset } from '../typedefs/UnresolvedAsset'; -import type { UseAssetsOptions } from '../typedefs/UseAssetsOptions'; +import { type AssetRetryState } from '../typedefs/AssetRetryState'; +import { type UnresolvedAsset } from '../typedefs/UnresolvedAsset'; +import { type UseAssetsOptions } from '../typedefs/UseAssetsOptions'; const errorCache: Map = new Map(); @@ -58,12 +57,12 @@ export function useSuspenseAssets( throw Assets .load(assets, (progressValue) => - { - if (typeof onProgress === 'function') { - onProgress(progressValue); - } - }) + if (typeof onProgress === 'function') + { + onProgress(progressValue); + } + }) .catch((error) => { if (!cachedState) diff --git a/src/hooks/useTick.ts b/src/hooks/useTick.ts index f5478dd1..a963bb6e 100644 --- a/src/hooks/useTick.ts +++ b/src/hooks/useTick.ts @@ -1,10 +1,9 @@ +import { type TickerCallback } from 'pixi.js'; import { invariant } from '../helpers/invariant'; +import { type UseTickOptions } from '../typedefs/UseTickOptions'; import { useApplication } from './useApplication'; import { useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect'; -import type { TickerCallback } from 'pixi.js'; -import type { UseTickOptions } from '../typedefs/UseTickOptions'; - /** Attaches a callback to the application's Ticker. */ export function useTick( /** @description The function to be called on each tick. */ diff --git a/src/index.ts b/src/index.ts index fdf7933a..75a8f79f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -17,4 +17,4 @@ export { useAssets } from './hooks/useAssets'; export { useExtend } from './hooks/useExtend'; export { useSuspenseAssets } from './hooks/useSuspenseAssets'; export { useTick } from './hooks/useTick'; -export type { PixiReactElementProps } from './typedefs/PixiReactNode'; +export { type PixiReactElementProps } from './typedefs/PixiReactNode'; diff --git a/src/store.ts b/src/store.ts index 983b9054..a6d886c1 100644 --- a/src/store.ts +++ b/src/store.ts @@ -1,9 +1,12 @@ +import { DefaultEventPriority } from 'react-reconciler/constants'; import { type Root } from './typedefs/Root'; const store: { + currentUpdatePriority: number, debug: boolean, unmountQueue: Set, } = { + currentUpdatePriority: DefaultEventPriority, debug: false, unmountQueue: new Set(), }; diff --git a/src/typedefs/ApplicationProps.ts b/src/typedefs/ApplicationProps.ts index 91973026..ff0dffba 100644 --- a/src/typedefs/ApplicationProps.ts +++ b/src/typedefs/ApplicationProps.ts @@ -1,19 +1,22 @@ -import type { - Application, - ApplicationOptions, - ExtensionFormatLoose, - TextStyle, - TextStyleOptions, +import { + type Application, + type ApplicationOptions, + type ExtensionFormatLoose, + type TextStyle, + type TextStyleOptions, } from 'pixi.js'; -import type { - Key, - RefObject, +import { + type Key, + type RefObject, } from 'react'; -import type { PixiReactChildNode } from './PixiReactChildNode'; +import { type PixiReactChildNode } from './PixiReactChildNode'; export interface BaseApplicationProps { - /** @description Whether this application chould be attached to the dev tools. NOTE: This should only be enabled on one application at a time. */ + /** + * @description Whether this application chould be attached to the dev tools. NOTE: This should only be enabled on one application at a time. + * @deprecated Pixi.js handles this automatically, making this option superfluous. + */ attachToDevTools?: boolean /** @description CSS classes to be applied to the Pixi Application's canvas element. */ diff --git a/src/typedefs/ApplicationRef.ts b/src/typedefs/ApplicationRef.ts new file mode 100644 index 00000000..be665bed --- /dev/null +++ b/src/typedefs/ApplicationRef.ts @@ -0,0 +1,7 @@ +import { type Application as PixiApplication } from 'pixi.js'; + +export interface ApplicationRef +{ + getApplication(): PixiApplication | null, + getCanvas(): HTMLCanvasElement | null, +} diff --git a/src/typedefs/ApplicationState.ts b/src/typedefs/ApplicationState.ts index 6c56d912..397f9699 100644 --- a/src/typedefs/ApplicationState.ts +++ b/src/typedefs/ApplicationState.ts @@ -1,4 +1,4 @@ -import type { Application } from 'pixi.js'; +import { type Application } from 'pixi.js'; export interface ApplicationState { diff --git a/src/typedefs/BasePixiReactNode.ts b/src/typedefs/BasePixiReactNode.ts index da678597..60c9774c 100644 --- a/src/typedefs/BasePixiReactNode.ts +++ b/src/typedefs/BasePixiReactNode.ts @@ -1,7 +1,7 @@ -import type { - Container, - Filter, +import { + type Container, + type Filter, } from 'pixi.js'; -import type { PixiReactNode } from './PixiReactNode'; +import { type PixiReactNode } from './PixiReactNode'; export type BasePixiReactNode any = typeof Container | typeof Filter> = PixiReactNode; diff --git a/src/typedefs/ConstructorOptions.ts b/src/typedefs/ConstructorOptions.ts index 441892f4..ec457644 100644 --- a/src/typedefs/ConstructorOptions.ts +++ b/src/typedefs/ConstructorOptions.ts @@ -1,4 +1,4 @@ -import type { ConstructorOverrides } from './ConstructorOverrides'; +import { type ConstructorOverrides } from './ConstructorOverrides'; /** * We're adding a specific options type overrides for some components because their deprecated overloads get in the way. @@ -10,4 +10,3 @@ export type ConstructorOptions any> = ? ConstructorParameters[0] : R : unknown; - diff --git a/src/typedefs/CreateRootOptions.ts b/src/typedefs/CreateRootOptions.ts index b46df018..5a5de358 100644 --- a/src/typedefs/CreateRootOptions.ts +++ b/src/typedefs/CreateRootOptions.ts @@ -1,4 +1,4 @@ -import type { Application } from 'pixi.js'; +import { type Application } from 'pixi.js'; export interface CreateRootOptions { diff --git a/src/typedefs/DiffSet.ts b/src/typedefs/DiffSet.ts index 843cf312..c62513e5 100644 --- a/src/typedefs/DiffSet.ts +++ b/src/typedefs/DiffSet.ts @@ -1,4 +1,4 @@ -import type { Change } from './Change'; +import { type Change } from './Change'; export interface DiffSet { diff --git a/src/typedefs/DrawCallback.ts b/src/typedefs/DrawCallback.ts index 52be2a04..afa3b036 100644 --- a/src/typedefs/DrawCallback.ts +++ b/src/typedefs/DrawCallback.ts @@ -1,3 +1,3 @@ -import type { Graphics } from 'pixi.js'; +import { type Graphics } from 'pixi.js'; export type DrawCallback = (graphics: Graphics) => void; diff --git a/src/typedefs/EventHandlers.ts b/src/typedefs/EventHandlers.ts index 4f4f62cd..ca8a270d 100644 --- a/src/typedefs/EventHandlers.ts +++ b/src/typedefs/EventHandlers.ts @@ -1,9 +1,9 @@ -import type { - FederatedEventHandler, - FederatedPointerEvent, - FederatedWheelEvent, +import { + type FederatedEventHandler, + type FederatedPointerEvent, + type FederatedWheelEvent, } from 'pixi.js'; -import type { ReactToPixiEventPropNames } from '../constants/EventPropNames'; +import { type ReactToPixiEventPropNames } from '../constants/EventPropNames'; export type EventHandlers = { -readonly [K in keyof typeof ReactToPixiEventPropNames]?: diff --git a/src/typedefs/EventPriority.ts b/src/typedefs/EventPriority.ts new file mode 100644 index 00000000..83ee1a64 --- /dev/null +++ b/src/typedefs/EventPriority.ts @@ -0,0 +1 @@ +export type EventPriority = number; diff --git a/src/typedefs/HostConfig.ts b/src/typedefs/HostConfig.ts index ab96fce3..97bf6b33 100644 --- a/src/typedefs/HostConfig.ts +++ b/src/typedefs/HostConfig.ts @@ -2,20 +2,20 @@ import { type Container, type Filter, } from 'pixi.js'; - -import type { NamespacedPixiElements } from './NamespacedPixiElements'; -import type { PixiElements } from './PixiElements'; -import type { PixiReactNode } from './PixiReactNode'; +import { type NamespacedPixiElements } from './NamespacedPixiElements'; +import { type PixiElements } from './PixiElements'; +import { type PixiReactNode } from './PixiReactNode'; export interface HostConfig { childSet: never; containerInstance: PixiReactNode; filterInstance: PixiReactNode; - hostContext: null; + formInstance: never, + hostContext: Record; hydratableInstance: never; instance: PixiReactNode; - noTimeout: -1; + noTimeout: number; props: Record; publicInstance: PixiReactNode; suspenseInstance: PixiReactNode; @@ -23,4 +23,5 @@ export interface HostConfig timeoutHandle: number; type: keyof PixiElements | keyof NamespacedPixiElements; updatePayload: object; + transitionStatus: null, } diff --git a/src/typedefs/InstanceState.ts b/src/typedefs/InstanceState.ts index 9aaa0f38..f40c6c2f 100644 --- a/src/typedefs/InstanceState.ts +++ b/src/typedefs/InstanceState.ts @@ -1,5 +1,5 @@ -import type { Filter } from 'pixi.js'; -import type { HostConfig } from './HostConfig'; +import { type Filter } from 'pixi.js'; +import { type HostConfig } from './HostConfig'; export interface InstanceState { diff --git a/src/typedefs/InternalState.ts b/src/typedefs/InternalState.ts index bd4ecbe6..4943bee4 100644 --- a/src/typedefs/InternalState.ts +++ b/src/typedefs/InternalState.ts @@ -1,4 +1,4 @@ -import type { HostConfig } from './HostConfig'; +import { type HostConfig } from './HostConfig'; export interface InternalState { diff --git a/src/typedefs/NamespacedPixiElements.ts b/src/typedefs/NamespacedPixiElements.ts index 9526d38c..e413cefe 100644 --- a/src/typedefs/NamespacedPixiElements.ts +++ b/src/typedefs/NamespacedPixiElements.ts @@ -1,4 +1,4 @@ -import type { PixiElements } from './PixiElements'; +import { type PixiElements } from './PixiElements'; export type NamespacedPixiElements = { [K in keyof PixiElements as `pixi${Capitalize}`]: PixiElements[K]; diff --git a/src/typedefs/PixiElements.ts b/src/typedefs/PixiElements.ts index 6a6e0ef4..72b908a1 100644 --- a/src/typedefs/PixiElements.ts +++ b/src/typedefs/PixiElements.ts @@ -1,7 +1,8 @@ +import { type NameOverrides } from '../constants/NameOverrides'; +import { type PixiComponents } from './PixiComponents'; +import { type PixiReactElementProps } from './PixiReactNode'; + import type * as PIXI from 'pixi.js'; -import type { NameOverrides } from '../constants/NameOverrides'; -import type { PixiComponents } from './PixiComponents'; -import type { PixiReactElementProps } from './PixiReactNode'; export type PixiElements = { [K in PixiComponents as K extends keyof typeof NameOverrides ? typeof NameOverrides[K] : Uncapitalize]: diff --git a/src/typedefs/PixiReactChildNode.ts b/src/typedefs/PixiReactChildNode.ts index 3e1d20f6..40efee79 100644 --- a/src/typedefs/PixiReactChildNode.ts +++ b/src/typedefs/PixiReactChildNode.ts @@ -1,4 +1,4 @@ -import type { ReactNode } from 'react'; -import type { BasePixiReactNode } from './BasePixiReactNode'; +import { type ReactNode } from 'react'; +import { type BasePixiReactNode } from './BasePixiReactNode'; export type PixiReactChildNode = BasePixiReactNode | Iterable | ReactNode; diff --git a/src/typedefs/PixiReactNode.ts b/src/typedefs/PixiReactNode.ts index fae78584..f183e6b8 100644 --- a/src/typedefs/PixiReactNode.ts +++ b/src/typedefs/PixiReactNode.ts @@ -1,19 +1,21 @@ -import { type PixiToReactEventPropNames } from '../constants/EventPropNames'; -import { type ConstructorOptions } from './ConstructorOptions'; -import { type ExcludeFunctionProps, type OmitKeys } from './UtilityTypes'; - -import type { - Container, - Graphics, +import { + type Container, + type Graphics, } from 'pixi.js'; -import type { - Key, - Ref, +import { + type Key, + type Ref, } from 'react'; -import type { DrawCallback } from './DrawCallback'; -import type { EventHandlers } from './EventHandlers'; -import type { InstanceState } from './InstanceState'; -import type { PixiReactChildNode } from './PixiReactChildNode'; +import { type PixiToReactEventPropNames } from '../constants/EventPropNames'; +import { type ConstructorOptions } from './ConstructorOptions'; +import { type DrawCallback } from './DrawCallback'; +import { type EventHandlers } from './EventHandlers'; +import { type InstanceState } from './InstanceState'; +import { type PixiReactChildNode } from './PixiReactChildNode'; +import { + type ExcludeFunctionProps, + type OmitKeys, +} from './UtilityTypes'; export interface BaseNodeProps any = typeof Container> { diff --git a/src/typedefs/Root.ts b/src/typedefs/Root.ts index 234b0ac5..83405517 100644 --- a/src/typedefs/Root.ts +++ b/src/typedefs/Root.ts @@ -1,7 +1,7 @@ -import type { Container } from 'pixi.js'; -import type { Fiber } from 'react-reconciler'; -import type { ApplicationState } from './ApplicationState'; -import type { InternalState } from './InternalState'; +import { type Container } from 'pixi.js'; +import { type Fiber } from 'react-reconciler'; +import { type ApplicationState } from './ApplicationState'; +import { type InternalState } from './InternalState'; export interface Root { diff --git a/src/typedefs/UnresolvedAsset.ts b/src/typedefs/UnresolvedAsset.ts index fdfa5cb1..26f1b5ea 100644 --- a/src/typedefs/UnresolvedAsset.ts +++ b/src/typedefs/UnresolvedAsset.ts @@ -1,3 +1,3 @@ -import type { UnresolvedAsset as PixiUnresolvedAsset } from 'pixi.js'; +import { type UnresolvedAsset as PixiUnresolvedAsset } from 'pixi.js'; export type UnresolvedAsset = PixiUnresolvedAsset | string; diff --git a/src/typedefs/UpdatePayload.ts b/src/typedefs/UpdatePayload.ts index 8a0c0f7d..0e32c5fd 100644 --- a/src/typedefs/UpdatePayload.ts +++ b/src/typedefs/UpdatePayload.ts @@ -1,4 +1,4 @@ -import type { DiffSet } from './DiffSet'; +import { type DiffSet } from './DiffSet'; export interface UpdatePayload { diff --git a/src/typedefs/UseAssetsOptions.ts b/src/typedefs/UseAssetsOptions.ts index dbde377c..ccce5c0b 100644 --- a/src/typedefs/UseAssetsOptions.ts +++ b/src/typedefs/UseAssetsOptions.ts @@ -1,7 +1,6 @@ +import { type ProgressCallback } from 'pixi.js'; import { type ErrorCallback } from './ErrorCallback'; -import type { ProgressCallback } from 'pixi.js'; - export interface UseAssetsOptions { /** @description The maximum number of retries allowed before we give up on loading this asset. */ diff --git a/src/typedefs/UseAssetsResult.ts b/src/typedefs/UseAssetsResult.ts index b525ed2a..a3e92300 100644 --- a/src/typedefs/UseAssetsResult.ts +++ b/src/typedefs/UseAssetsResult.ts @@ -1,4 +1,4 @@ -import type { UseAssetsStatus } from '../constants/UseAssetsStatus'; +import { type UseAssetsStatus } from '../constants/UseAssetsStatus'; export interface UseAssetsResult { diff --git a/src/typedefs/UseTickOptions.ts b/src/typedefs/UseTickOptions.ts index bf9fba38..244bdf06 100644 --- a/src/typedefs/UseTickOptions.ts +++ b/src/typedefs/UseTickOptions.ts @@ -1,4 +1,4 @@ -import type { TickerCallback } from 'pixi.js'; +import { type TickerCallback } from 'pixi.js'; export interface UseTickOptions { diff --git a/test/e2e/hooks/useApplication.test.tsx b/test/e2e/hooks/useApplication.test.tsx index 1cff66dc..5682a962 100644 --- a/test/e2e/hooks/useApplication.test.tsx +++ b/test/e2e/hooks/useApplication.test.tsx @@ -4,8 +4,8 @@ import { expect, it, } from 'vitest'; -import { Application } from '../../../src/components/Application.ts'; -import { useApplication } from '../../../src/hooks/useApplication.ts'; +import { Application } from '../../../src/components/Application'; +import { useApplication } from '../../../src/hooks/useApplication'; import { render, renderHook,