Skip to content

Commit

Permalink
feat: add support for filter components
Browse files Browse the repository at this point in the history
  • Loading branch information
trezy committed Jul 12, 2024
1 parent 5599bd8 commit 6562a1e
Show file tree
Hide file tree
Showing 19 changed files with 200 additions and 45 deletions.
28 changes: 17 additions & 11 deletions src/helpers/appendChild.ts
Original file line number Diff line number Diff line change
@@ -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<ContainerElement>, childInstance: Instance | null)
{
log('info', 'lifecycle::appendChild');

Expand All @@ -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);
}
}
8 changes: 3 additions & 5 deletions src/helpers/applyProps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,9 @@ const PIXI_EVENT_PROP_NAME_ERROR_HAS_BEEN_SHOWN: Record<string, boolean> = {};
*/
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;

Expand All @@ -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;
Expand Down Expand Up @@ -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];
Expand All @@ -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;
}
}
Expand Down
27 changes: 27 additions & 0 deletions src/helpers/attach.ts
Original file line number Diff line number Diff line change
@@ -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<ContainerElement>,
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;
}
}
13 changes: 8 additions & 5 deletions src/helpers/commitUpdate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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);
}
Expand Down
22 changes: 22 additions & 0 deletions src/helpers/detach.ts
Original file line number Diff line number Diff line change
@@ -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<ContainerElement>;

if (parentInstance)
{
const filterIndex = parentInstance.__pixireact.filters.indexOf(childInstance);

parentInstance.__pixireact.filters.splice(filterIndex, 1);
parentInstance.filters = parentInstance.__pixireact.filters;
}

childInstance.__pixireact.parent = null;
}
}
18 changes: 17 additions & 1 deletion src/helpers/hideInstance.ts
Original file line number Diff line number Diff line change
@@ -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;
}
}
33 changes: 28 additions & 5 deletions src/helpers/insertBefore.ts
Original file line number Diff line number Diff line change
@@ -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<ContainerElement>, 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<ContainerElement>;

if (childContainerInstance.parent === parentInstance)
{
parentInstance.removeChild(childContainerInstance);
}

const index = parentInstance.getChildIndex(beforeChildInstance as Instance<ContainerElement>);

parentInstance.addChildAt(childContainerInstance, index);
}
else if (childInstance instanceof Filter)
{
const childFilterInstance = childInstance as Instance<FilterElement>;
const instanceState = childFilterInstance.__pixireact;

const index = parentInstance.getChildIndex(beforeChildInstance);
const targetIndex = instanceState.filters.indexOf(beforeChildInstance as Instance<FilterElement>);

parentInstance.addChildAt(childInstance, index);
detach(childInstance);
attach(parentInstance, childInstance, targetIndex);
}
}
2 changes: 1 addition & 1 deletion src/helpers/prepareInstance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export function prepareInstance<T extends Container | PixiElement>(
const instance = component as Instance;

instance.__pixireact = {
attachedChildren: [],
filters: [],
parent: null,
root: null as unknown as Instance,
type: '',
Expand Down
12 changes: 7 additions & 5 deletions src/helpers/prepareUpdate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -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;
}
8 changes: 8 additions & 0 deletions src/helpers/removeChild.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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();
}
3 changes: 2 additions & 1 deletion src/helpers/switchInstance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -33,7 +34,7 @@ export function switchInstance(
newInstance.autoRemovedBeforeAppend = true;
}

appendChild(parent, newInstance);
appendChild(parent as Instance<ContainerElement>, newInstance);

// This evil hack switches the react-internal fiber node
// https://github.com/facebook/react/issues/14983
Expand Down
16 changes: 15 additions & 1 deletion src/helpers/unhideInstance.ts
Original file line number Diff line number Diff line change
@@ -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;
}
}
3 changes: 1 addition & 2 deletions src/typedefs/EventHandlers.ts
Original file line number Diff line number Diff line change
@@ -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
Expand Down
4 changes: 4 additions & 0 deletions src/typedefs/FilterElement.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import type { Filter } from 'pixi.js';
import type { ReactElement } from 'react';

export type FilterElement = Filter & ReactElement;
17 changes: 11 additions & 6 deletions src/typedefs/Instance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = PixiElement> = 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<ContainerElement>,
};
5 changes: 4 additions & 1 deletion src/typedefs/InstanceState.ts
Original file line number Diff line number Diff line change
@@ -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<ContainerElement>;
root: Instance;
type: string;
}
Loading

0 comments on commit 6562a1e

Please sign in to comment.