diff --git a/src/core/Layout.ts b/src/core/Layout.ts index f939729f..98de44c8 100644 --- a/src/core/Layout.ts +++ b/src/core/Layout.ts @@ -223,9 +223,14 @@ export class Layout { } } - /** Marks the root layout as needing recalculation */ - public invalidateRoot(): void { - const root = this.getRoot(); + /** + * Marks the root layout as needing recalculation + * @param start - Optional container to start searching for the root from + */ + public invalidateRoot(start?: Container): void { + const root = this.getRoot(start); + + if (root.destroyed) return; root._layout!._isDirty = true; root._onUpdate(); @@ -244,11 +249,12 @@ export class Layout { /** * Finds the root container by traversing up the layout tree + * @param start - Optional container to start searching for the root from * @returns The root container */ - public getRoot(): Container { + public getRoot(start?: Container): Container { // find the root node by traversing up the yoga tree - let root: Container = this.target as Container; + let root: Container = start || (this.target as Container); while (root.parent?._layout || (root.parent as OverflowContainer)?.isOverflowContainer) { root = root.parent; @@ -278,11 +284,11 @@ export class Layout { /** * @ignore */ - public _onChildRemoved(): void { + public _onChildRemoved(parent?: Container): void { if (!this.hasParent) return; this.hasParent = false; - this.invalidateRoot(); + this.invalidateRoot(parent); onChildRemoved(this); } diff --git a/tests/__tests__/index.test.tsx b/tests/__tests__/index.test.tsx index 384a0f3c..5bf4789f 100644 --- a/tests/__tests__/index.test.tsx +++ b/tests/__tests__/index.test.tsx @@ -46,6 +46,7 @@ import * as objectFitLayoutViewStories from '../stories/custom/objectFit/objectF import * as objectFitStories from '../stories/custom/objectFit/objectFit.stories'; import * as isLeafStories from '../stories/custom/leaf/isLeaf.stories'; import * as dynamicTextStories from '../stories/custom/dynamicChanges/dynamicText.stories'; +import * as dynamicDeleteStories from '../stories/custom/dynamicChanges/dynamicDelete.stories'; import * as layoutContainerBackgroundStories from '../stories/custom/components/layoutContainerBackground.stories'; import * as layoutContainerStories from '../stories/custom/components/layoutContainer.stories'; import * as applySizeStories from '../stories/custom/applySize/applySize.stories'; @@ -93,6 +94,7 @@ allStories.objectFitLayoutViewStories = composeStories(objectFitLayoutViewStorie allStories.objectFitStories = composeStories(objectFitStories); allStories.isLeafStories = composeStories(isLeafStories); allStories.dynamicTextStories = composeStories(dynamicTextStories); +allStories.dynamicDeleteStories = composeStories(dynamicDeleteStories); allStories.layoutContainerBackgroundStories = composeStories(layoutContainerBackgroundStories); allStories.layoutContainerStories = composeStories(layoutContainerStories); allStories.applySizeStories = composeStories(applySizeStories); diff --git a/tests/snapshots/yoga-dynamic-changes-delete--delete-webkit.png b/tests/snapshots/yoga-dynamic-changes-delete--delete-webkit.png new file mode 100644 index 00000000..a9317d70 Binary files /dev/null and b/tests/snapshots/yoga-dynamic-changes-delete--delete-webkit.png differ diff --git a/tests/stories/custom/dynamicChanges/dynamicDelete.stories.tsx b/tests/stories/custom/dynamicChanges/dynamicDelete.stories.tsx new file mode 100644 index 00000000..9a5be1eb --- /dev/null +++ b/tests/stories/custom/dynamicChanges/dynamicDelete.stories.tsx @@ -0,0 +1,55 @@ +import { type Text as PixiText } from 'pixi.js'; +import React, { type FC } from 'react'; +import { ReactStory } from '../../yoga/utils/reactStory'; + +import type { Meta, StoryObj } from '@storybook/react'; + +const LayoutComponent: FC = ({}) => { + const refs: Record> = {}; + + for (let i = 0; i < 4; i++) { + refs[`r${i + 1}`] = React.createRef(); + } + + setTimeout(() => { + Object.values(refs)[2]!.current?.removeFromParent(); + }, 100); + + return ( + + + + + + + + + ); +}; + +// 👇 This default export determines where your story goes in the story list +const meta: Meta = { + title: 'Yoga/Dynamic Changes/Delete', + component: LayoutComponent, + render: (args) => ( + + + + ), +}; + +export default meta; +type Story = StoryObj; + +export const Delete: Story = { + tags: ['delay:200'], + args: {}, +};