From 6562a1eb7a8473f4b07c11843679e58a43e937ce Mon Sep 17 00:00:00 2001 From: Trezy Date: Fri, 12 Jul 2024 01:44:57 -0500 Subject: [PATCH] feat: add support for filter components --- src/helpers/appendChild.ts | 28 +++++++++++++++++----------- src/helpers/applyProps.ts | 8 +++----- src/helpers/attach.ts | 27 +++++++++++++++++++++++++++ src/helpers/commitUpdate.ts | 13 ++++++++----- src/helpers/detach.ts | 22 ++++++++++++++++++++++ src/helpers/hideInstance.ts | 18 +++++++++++++++++- src/helpers/insertBefore.ts | 33 ++++++++++++++++++++++++++++----- src/helpers/prepareInstance.ts | 2 +- src/helpers/prepareUpdate.ts | 12 +++++++----- src/helpers/removeChild.ts | 8 ++++++++ src/helpers/switchInstance.ts | 3 ++- src/helpers/unhideInstance.ts | 16 +++++++++++++++- src/typedefs/EventHandlers.ts | 3 +-- src/typedefs/FilterElement.ts | 4 ++++ src/typedefs/Instance.ts | 17 +++++++++++------ src/typedefs/InstanceState.ts | 5 ++++- src/typedefs/MaybeInstance.ts | 15 ++++++++++++++- src/typedefs/PixiElement.ts | 4 ++++ src/typedefs/UpdatePayload.ts | 7 +++++++ 19 files changed, 200 insertions(+), 45 deletions(-) create mode 100644 src/helpers/attach.ts create mode 100644 src/helpers/detach.ts create mode 100644 src/typedefs/FilterElement.ts create mode 100644 src/typedefs/PixiElement.ts create mode 100644 src/typedefs/UpdatePayload.ts diff --git a/src/helpers/appendChild.ts b/src/helpers/appendChild.ts index d4c24acd..10960026 100644 --- a/src/helpers/appendChild.ts +++ b/src/helpers/appendChild.ts @@ -1,16 +1,15 @@ +import { + Container, + Filter, +} from 'pixi.js'; +import { attach } from './attach.ts'; import { log } from './log.ts'; -import type { Instance } from '../typedefs/Instance.js'; +import type { ContainerElement } from '../typedefs/ContainerElement.ts'; +import type { Instance } from '../typedefs/Instance.ts'; -/** @typedef {import('../typedefs/Instance.ts').Instance} Instance */ - -/** - * Adds elements to our application. - * - * @param {Instance} parentInstance - * @param {Instance | null} childInstance - */ -export function appendChild(parentInstance: Instance, childInstance: Instance | null) +/** Adds elements to our application. */ +export function appendChild(parentInstance: Instance, childInstance: Instance | null) { log('info', 'lifecycle::appendChild'); @@ -19,5 +18,12 @@ export function appendChild(parentInstance: Instance, childInstance: Instance | return; } - parentInstance.addChild(childInstance); + if (childInstance instanceof Container) + { + parentInstance.addChild(childInstance); + } + else if (childInstance instanceof Filter) + { + attach(parentInstance, childInstance); + } } diff --git a/src/helpers/applyProps.ts b/src/helpers/applyProps.ts index d0b046bf..de65df91 100644 --- a/src/helpers/applyProps.ts +++ b/src/helpers/applyProps.ts @@ -33,10 +33,9 @@ const PIXI_EVENT_PROP_NAME_ERROR_HAS_BEEN_SHOWN: Record = {}; */ export function applyProps(instance: MaybeInstance, data: InstanceProps | DiffSet) { - const localState = instance.__pixireact; const { // eslint-disable-next-line @typescript-eslint/no-unused-vars - __pixireact, + __pixireact: instanceState, ...instanceProps } = instance; @@ -48,7 +47,7 @@ export function applyProps(instance: MaybeInstance, data: InstanceProps | DiffSe } else { - typedData = diffProps(data, instanceProps); + typedData = diffProps(data, instanceProps as InstanceProps); } const { changes } = typedData; @@ -147,7 +146,7 @@ export function applyProps(instance: MaybeInstance, data: InstanceProps | DiffSe } // Deal with events ... - if (isEvent && localState) + if (isEvent && instanceState) { const typedKey = key as keyof typeof ReactToPixiEventPropNames; const pixiKey = ReactToPixiEventPropNames[typedKey]; @@ -163,7 +162,6 @@ export function applyProps(instance: MaybeInstance, data: InstanceProps | DiffSe } else if (!isReadOnlyProperty(currentInstance, key)) { - // @ts-expect-error The key is cast to any property of Container, including read-only properties. The check above prevents us from setting read-only properties, but TS doesn't understand it. 🤷🏻‍♂️ currentInstance[key] = value; } } diff --git a/src/helpers/attach.ts b/src/helpers/attach.ts new file mode 100644 index 00000000..d454c47d --- /dev/null +++ b/src/helpers/attach.ts @@ -0,0 +1,27 @@ +import { Filter } from 'pixi.js'; + +import type { ContainerElement } from '../typedefs/ContainerElement.ts'; +import type { Instance } from '../typedefs/Instance.ts'; + +export function attach( + parentInstance: Instance, + childInstance: Instance, + targetIndex?: number +) +{ + if (childInstance instanceof Filter) + { + childInstance.__pixireact.parent = parentInstance; + + if (typeof targetIndex === 'number') + { + parentInstance.__pixireact.filters.splice(targetIndex, 0, childInstance); + } + else + { + parentInstance.__pixireact.filters.push(childInstance); + } + + parentInstance.filters = parentInstance.__pixireact.filters; + } +} diff --git a/src/helpers/commitUpdate.ts b/src/helpers/commitUpdate.ts index 80b6845d..4c212766 100644 --- a/src/helpers/commitUpdate.ts +++ b/src/helpers/commitUpdate.ts @@ -3,14 +3,14 @@ import { log } from '../helpers/log.ts'; import { switchInstance } from './switchInstance.ts'; import type { Fiber } from 'react-reconciler'; -import type { DiffSet } from '../typedefs/DiffSet.ts'; import type { HostConfig } from '../typedefs/HostConfig.ts'; import type { Instance } from '../typedefs/Instance.ts'; import type { InstanceProps } from '../typedefs/InstanceProps.ts'; +import type { UpdatePayload } from '../typedefs/UpdatePayload.ts'; export function commitUpdate( instance: Instance, - updatePayload: [boolean, DiffSet], + updatePayload: UpdatePayload, type: HostConfig['type'], _oldProps: InstanceProps, newProps: InstanceProps, @@ -19,13 +19,16 @@ export function commitUpdate( { log('info', 'lifecycle::commitUpdate'); - const [reconstruct, diff] = updatePayload; + const { + diff, + shouldReconstruct, + } = updatePayload; - if (reconstruct) + if (shouldReconstruct) { switchInstance(instance, type, newProps, fiber); } - else + else if (diff) { applyProps(instance, diff); } diff --git a/src/helpers/detach.ts b/src/helpers/detach.ts new file mode 100644 index 00000000..fa39e644 --- /dev/null +++ b/src/helpers/detach.ts @@ -0,0 +1,22 @@ +import { Filter } from 'pixi.js'; + +import type { ContainerElement } from 'typedefs/ContainerElement.ts'; +import type { Instance } from '../typedefs/Instance.ts'; + +export function detach(childInstance: Instance) +{ + if (childInstance instanceof Filter) + { + const parentInstance = childInstance.__pixireact.parent as Instance; + + if (parentInstance) + { + const filterIndex = parentInstance.__pixireact.filters.indexOf(childInstance); + + parentInstance.__pixireact.filters.splice(filterIndex, 1); + parentInstance.filters = parentInstance.__pixireact.filters; + } + + childInstance.__pixireact.parent = null; + } +} diff --git a/src/helpers/hideInstance.ts b/src/helpers/hideInstance.ts index 3de93012..f859a333 100644 --- a/src/helpers/hideInstance.ts +++ b/src/helpers/hideInstance.ts @@ -1,6 +1,22 @@ +import { catalogue } from './catalogue.ts'; + +import type { ContainerElement } from '../typedefs/ContainerElement.ts'; +import type { FilterElement } from '../typedefs/FilterElement.ts'; import type { Instance } from '../typedefs/Instance.ts'; export function hideInstance(instance: Instance) { - instance.visible = false; + const { + Container, + Filter, + } = catalogue; + + if (Container && instance instanceof Container) + { + (instance as ContainerElement).visible = false; + } + else if (Filter && instance instanceof Filter) + { + (instance as FilterElement).enabled = false; + } } diff --git a/src/helpers/insertBefore.ts b/src/helpers/insertBefore.ts index f5d1b570..2b5feb80 100644 --- a/src/helpers/insertBefore.ts +++ b/src/helpers/insertBefore.ts @@ -1,20 +1,43 @@ +import { + Container, + Filter, +} from 'pixi.js'; +import { attach } from './attach.ts'; +import { detach } from './detach.ts'; import { invariant } from './invariant.ts'; import { log } from './log.ts'; +import type { ContainerElement } from '../typedefs/ContainerElement.ts'; +import type { FilterElement } from '../typedefs/FilterElement.ts'; import type { Instance } from '../typedefs/Instance.ts'; -export function insertBefore(parentInstance: Instance, childInstance: Instance, beforeChildInstance: Instance) +export function insertBefore(parentInstance: Instance, childInstance: Instance, beforeChildInstance: Instance) { log('info', 'lifecycle::insertBefore'); invariant(childInstance !== beforeChildInstance, 'Cannot insert node before itself'); - if (parentInstance.children.indexOf(childInstance) === -1) + if (childInstance instanceof Container) { - parentInstance.removeChild(childInstance); + const childContainerInstance = childInstance as Instance; + + if (childContainerInstance.parent === parentInstance) + { + parentInstance.removeChild(childContainerInstance); + } + + const index = parentInstance.getChildIndex(beforeChildInstance as Instance); + + parentInstance.addChildAt(childContainerInstance, index); } + else if (childInstance instanceof Filter) + { + const childFilterInstance = childInstance as Instance; + const instanceState = childFilterInstance.__pixireact; - const index = parentInstance.getChildIndex(beforeChildInstance); + const targetIndex = instanceState.filters.indexOf(beforeChildInstance as Instance); - parentInstance.addChildAt(childInstance, index); + detach(childInstance); + attach(parentInstance, childInstance, targetIndex); + } } diff --git a/src/helpers/prepareInstance.ts b/src/helpers/prepareInstance.ts index f7fc402e..63f28ee7 100644 --- a/src/helpers/prepareInstance.ts +++ b/src/helpers/prepareInstance.ts @@ -12,7 +12,7 @@ export function prepareInstance( const instance = component as Instance; instance.__pixireact = { - attachedChildren: [], + filters: [], parent: null, root: null as unknown as Instance, type: '', diff --git a/src/helpers/prepareUpdate.ts b/src/helpers/prepareUpdate.ts index a3dbf385..ffffcdbb 100644 --- a/src/helpers/prepareUpdate.ts +++ b/src/helpers/prepareUpdate.ts @@ -3,6 +3,7 @@ import { log } from './log.ts'; import type { Instance } from '../typedefs/Instance.ts'; import type { InstanceProps } from '../typedefs/InstanceProps.ts'; +import type { UpdatePayload } from '../typedefs/UpdatePayload.ts'; export function prepareUpdate( _instance: Instance, @@ -13,7 +14,10 @@ export function prepareUpdate( { log('info', 'lifecycle::prepareUpdate'); - // This is a data object, let's extract critical information about it + const updatePayload: UpdatePayload = { + shouldReconstruct: false, + }; + const { // eslint-disable-next-line @typescript-eslint/no-unused-vars children: newChildren, @@ -25,14 +29,12 @@ export function prepareUpdate( ...oldPropsRest } = oldProps; - // Create a diff-set, flag if there are any changes const diff = diffProps(newPropsRest, oldPropsRest, true); if (diff.changes.length) { - return [false, diff]; + updatePayload.diff = diff; } - // Otherwise do not touch the instance - return null; + return updatePayload; } diff --git a/src/helpers/removeChild.ts b/src/helpers/removeChild.ts index 044e38cf..59f3d5d9 100644 --- a/src/helpers/removeChild.ts +++ b/src/helpers/removeChild.ts @@ -1,3 +1,5 @@ +import { Filter } from 'pixi.js'; +import { detach } from './detach.ts'; import { log } from './log.ts'; import type { Instance } from '../typedefs/Instance.ts'; @@ -6,5 +8,11 @@ import type { Instance } from '../typedefs/Instance.ts'; export function removeChild(_parentInstance: Instance, childInstance: Instance) { log('info', 'lifecycle::removeChild'); + + if (childInstance instanceof Filter) + { + detach(childInstance); + } + childInstance.destroy(); } diff --git a/src/helpers/switchInstance.ts b/src/helpers/switchInstance.ts index f2e39fe9..e116771a 100644 --- a/src/helpers/switchInstance.ts +++ b/src/helpers/switchInstance.ts @@ -3,6 +3,7 @@ import { createInstance } from './createInstance.ts'; import { removeChild } from './removeChild.ts'; import type { Fiber } from 'react-reconciler'; +import type { ContainerElement } from '../typedefs/ContainerElement.ts'; import type { HostConfig } from '../typedefs/HostConfig.ts'; import type { Instance } from '../typedefs/Instance.ts'; @@ -33,7 +34,7 @@ export function switchInstance( newInstance.autoRemovedBeforeAppend = true; } - appendChild(parent, newInstance); + appendChild(parent as Instance, newInstance); // This evil hack switches the react-internal fiber node // https://github.com/facebook/react/issues/14983 diff --git a/src/helpers/unhideInstance.ts b/src/helpers/unhideInstance.ts index 60d39a41..7a94d9e9 100644 --- a/src/helpers/unhideInstance.ts +++ b/src/helpers/unhideInstance.ts @@ -1,6 +1,20 @@ +import { + Container, + Filter, +} from 'pixi.js'; + +import type { ContainerElement } from '../typedefs/ContainerElement.ts'; +import type { FilterElement } from '../typedefs/FilterElement.ts'; import type { Instance } from '../typedefs/Instance.ts'; export function unhideInstance(instance: Instance) { - instance.visible = true; + if (Container && instance instanceof Container) + { + (instance as ContainerElement).visible = true; + } + else if (Filter && instance instanceof Filter) + { + (instance as FilterElement).enabled = true; + } } diff --git a/src/typedefs/EventHandlers.ts b/src/typedefs/EventHandlers.ts index d78c3650..2d4a4990 100644 --- a/src/typedefs/EventHandlers.ts +++ b/src/typedefs/EventHandlers.ts @@ -1,9 +1,8 @@ -import { type ReactToPixiEventPropNames } from '../constants/EventPropNames.ts'; - import type { FederatedPointerEvent, FederatedWheelEvent, } from 'pixi.js'; +import type { ReactToPixiEventPropNames } from '../constants/EventPropNames.ts'; export type EventHandlers = { -readonly [K in keyof typeof ReactToPixiEventPropNames]?: (event: FederatedPointerEvent | FederatedWheelEvent) => void diff --git a/src/typedefs/FilterElement.ts b/src/typedefs/FilterElement.ts new file mode 100644 index 00000000..4df48eec --- /dev/null +++ b/src/typedefs/FilterElement.ts @@ -0,0 +1,4 @@ +import type { Filter } from 'pixi.js'; +import type { ReactElement } from 'react'; + +export type FilterElement = Filter & ReactElement; diff --git a/src/typedefs/Instance.ts b/src/typedefs/Instance.ts index 220ed6dd..e6b343a1 100644 --- a/src/typedefs/Instance.ts +++ b/src/typedefs/Instance.ts @@ -2,12 +2,17 @@ import type { Graphics } from 'pixi.js'; import type { ContainerElement } from './ContainerElement.ts'; import type { EventHandlers } from './EventHandlers.ts'; import type { InstanceState } from './InstanceState.ts'; +import type { PixiElement } from './PixiElement.ts'; -export type Instance = ContainerElement & EventHandlers & +export type Instance = T & EventHandlers & { - BaseInstance: object - __pixireact?: InstanceState - autoRemovedBeforeAppend?: boolean - children?: ContainerElement | ContainerElement[] - draw?: (graphics: Graphics) => void + __pixireact: InstanceState; + autoRemovedBeforeAppend?: boolean; + children?: T extends ContainerElement + ? ContainerElement | ContainerElement[] + : never; + draw?: T extends Graphics + ? (graphics: Graphics) => void + : null; + parent?: Instance, }; diff --git a/src/typedefs/InstanceState.ts b/src/typedefs/InstanceState.ts index 12fe0639..339ed625 100644 --- a/src/typedefs/InstanceState.ts +++ b/src/typedefs/InstanceState.ts @@ -1,9 +1,12 @@ +import type { Filter } from 'pixi.js'; +import type { ContainerElement } from './ContainerElement.ts'; import type { Instance } from './Instance.ts'; export interface InstanceState { autoRemovedBeforeAppend?: boolean; - parent: null | Instance; + filters: Filter[], + parent: null | Instance; root: Instance; type: string; } diff --git a/src/typedefs/MaybeInstance.ts b/src/typedefs/MaybeInstance.ts index a66fee2a..d0018050 100644 --- a/src/typedefs/MaybeInstance.ts +++ b/src/typedefs/MaybeInstance.ts @@ -1,4 +1,17 @@ +import type { + FederatedEventHandler, + FederatedPointerEvent, + FederatedWheelEvent, +} from 'pixi.js'; +import type { PixiToReactEventPropNames } from '../constants/EventPropNames.ts'; import type { Instance } from './Instance.ts'; import type { PartialBy } from './PartialBy.ts'; -export type MaybeInstance = PartialBy; +export type StupidEventHandlers = { + -readonly [K in keyof typeof PixiToReactEventPropNames]?: + | FederatedEventHandler + | FederatedEventHandler + | null +}; + +export type MaybeInstance = PartialBy & Partial; diff --git a/src/typedefs/PixiElement.ts b/src/typedefs/PixiElement.ts new file mode 100644 index 00000000..7ab035c3 --- /dev/null +++ b/src/typedefs/PixiElement.ts @@ -0,0 +1,4 @@ +import type { ContainerElement } from './ContainerElement'; +import type { FilterElement } from './FilterElement'; + +export type PixiElement = FilterElement | ContainerElement; diff --git a/src/typedefs/UpdatePayload.ts b/src/typedefs/UpdatePayload.ts new file mode 100644 index 00000000..34da0f40 --- /dev/null +++ b/src/typedefs/UpdatePayload.ts @@ -0,0 +1,7 @@ +import type { DiffSet } from './DiffSet.ts'; + +export interface UpdatePayload +{ + diff?: DiffSet, + shouldReconstruct: boolean, +}