{"version":3,"file":"LayoutSystem.cjs","sources":["../../src/core/LayoutSystem.ts"],"sourcesContent":["import { type Container, ExtensionType, type System } from 'pixi.js';\nimport { Direction, loadYoga } from 'yoga-layout/load';\nimport { type OverflowContainer } from '../components/LayoutContainer';\nimport { getYoga, setYoga, setYogaConfig } from '../yoga';\nimport { calculatePositionSpecifier } from './mixins/utils/calculatePositionSpecifier';\nimport { getPixiSize } from './utils/getPixiSize';\nimport { nearlyEqual } from './utils/nearlyEqual';\nimport { throttle as throttleFn } from './utils/throttle';\n\nimport type { DebugRenderer } from './debug/DebugRenderer';\n\n/**\n * Options for the layout system\n */\nexport interface LayoutSystemOptions {\n    layout: {\n        /** Whether the layout system should automatically update the layout when it detects changes */\n        autoUpdate: boolean;\n        /** Whether to enable debug mode */\n        enableDebug: boolean;\n        /** The number of modifications to trigger rendering the heatmap */\n        debugModificationCount: number;\n        /** The length of time in milliseconds to throttle the calculating auto layout values */\n        throttle: number;\n    };\n}\n\n/**\n * The layout system is responsible for updating the layout of the containers\n * @memberof rendering\n */\nexport class LayoutSystem implements System<LayoutSystemOptions> {\n    /** @ignore */\n    public static extension = {\n        type: [ExtensionType.WebGLSystem, ExtensionType.WebGPUSystem],\n        name: 'layout',\n    } as const;\n\n    /**\n     * Whether the layout system should automatically update the layout when it detects changes\n     * @default true\n     */\n    public autoUpdate = true;\n\n    private _debugEnabled = false;\n    private _debugRenderer: DebugRenderer | null = null;\n\n    private _throttledUpdateSize!: (container: Container) => void;\n    private _throttle = 100;\n    private _modificationCount = 50;\n\n    /**\n     * Initializes the layout system by loading the Yoga library asynchronously\n     * @returns A promise that resolves when the system is ready\n     */\n    public async init(options?: LayoutSystemOptions) {\n        setYoga(await loadYoga());\n        setYogaConfig(getYoga().Config.create());\n        const { layout } = options ?? {};\n        const { autoUpdate, enableDebug, throttle, debugModificationCount } = layout ?? {};\n\n        if (enableDebug) {\n            void this.enableDebug(true);\n        }\n\n        if (autoUpdate !== undefined) {\n            this.autoUpdate = autoUpdate;\n        }\n\n        this._throttle = throttle ?? this._throttle;\n        this._throttledUpdateSize = throttleFn((container: Container) => this._updateSize(container), this._throttle, {\n            leading: true,\n            trailing: true,\n        });\n\n        this._modificationCount = debugModificationCount ?? this._modificationCount;\n    }\n\n    /**\n     * Toggles the debug mode for the layout system\n     * @param value - Whether to enable or disable debug mode\n     */\n    public async enableDebug(value = !this._debugEnabled) {\n        this._debugEnabled = value;\n\n        if (!this._debugRenderer) {\n            const res = await import('./debug/DebugRenderer');\n\n            this._debugRenderer = new res.DebugRenderer();\n        }\n\n        if (!this._debugEnabled) {\n            this._debugRenderer!.reset();\n        }\n    }\n\n    /**\n     * Updates the layout of the container and its children\n     * @param container - The container to update the layout for\n     */\n    public update(container: Container) {\n        if (this._debugEnabled && this._debugRenderer) {\n            this._debugRenderer.reset();\n            container.addChild(this._debugRenderer.holder);\n        }\n\n        // Before we start updating the layout, we need to ensure that the size of the yoga nodes are up to date\n        this._throttle === 0 ? this._updateSize(container) : this._throttledUpdateSize(container);\n\n        // loop through entire scene and check for any layout updates!\n        this.updateLayout(container);\n    }\n\n    public prerender({ container }: { container: Container }) {\n        if (this.autoUpdate) {\n            this.update(container);\n        }\n    }\n\n    /**\n     * Updates the size of the yoga nodes for the containers that use pixi size\n     * @param container - The container to update the size for\n     */\n    private _updateSize(container: Container) {\n        if (container.destroyed) return;\n        const layout = container._layout;\n\n        if (layout) {\n            const layoutStyles = layout.style;\n\n            if (layoutStyles.width === 'intrinsic' || layoutStyles.height === 'intrinsic') {\n                const size = getPixiSize(layout);\n\n                if (layoutStyles.width === 'intrinsic') {\n                    const currentWidth = layout.yoga.getWidth().value;\n\n                    if (!nearlyEqual(currentWidth, size.width)) {\n                        layout.yoga.setWidth(size.width);\n                        layout.invalidateRoot();\n                    }\n                }\n                if (layoutStyles.height === 'intrinsic') {\n                    const currentHeight = layout.yoga.getHeight().value;\n\n                    if (!nearlyEqual(currentHeight, size.height)) {\n                        layout.yoga.setHeight(size.height);\n                        layout.invalidateRoot();\n                    }\n                }\n            }\n\n            // if the container is not visible, we need to remove it from the layout\n            if (!container.visible) {\n                layout._onChildRemoved();\n\n                return;\n            }\n        }\n\n        for (let i = 0; i < container.children.length; i++) {\n            this._updateSize(container.children[i]!);\n        }\n    }\n\n    /**\n     * Updates the layout of the container and its children\n     * @param container - The container to update the layout for\n     */\n    private updateLayout(container: Container) {\n        const layout = container._layout;\n\n        // return early if the container is not visible\n        if (!container.visible) {\n            return;\n        }\n\n        if (layout) {\n            const yogaNode = layout.yoga;\n            const layoutStyles = layout.style;\n\n            const isOverflowContainer = (container.parent as OverflowContainer)?.isOverflowContainer;\n            const hasParentLayout = container.parent?._layout;\n\n            if (!hasParentLayout && !isOverflowContainer) {\n                if (layout._isDirty) {\n                    layout._isDirty = false;\n                    yogaNode.calculateLayout(\n                        layoutStyles.width as number, // TODO: if this is not a number, it will not work\n                        layoutStyles.height as number,\n                        yogaNode.getDirection() ?? Direction.LTR,\n                    );\n                }\n            }\n\n            if (yogaNode.hasNewLayout() || layout._forceUpdate) {\n                // Reset the flag\n                yogaNode.markLayoutSeen();\n                layout._forceUpdate = false;\n\n                layout._computedLayout = yogaNode.getComputedLayout();\n                const res = calculatePositionSpecifier(layoutStyles.transformOrigin, layout._computedLayout, {\n                    width: 0,\n                    height: 0,\n                });\n\n                layout._computedPixiLayout = {\n                    ...container.computeLayoutData!(layout._computedLayout),\n                    originX: res.x,\n                    originY: res.y,\n                };\n\n                container.emit('layout', layout);\n                container.onLayout?.(layout);\n                container._onUpdate();\n            }\n\n            if (this._debugEnabled) {\n                if (\n                    layout._styles.custom.debug ||\n                    (layout._modificationCount > this._modificationCount && layout._styles.custom.debugHeat !== false)\n                ) {\n                    this._debugRenderer?.render(layout);\n                }\n            }\n        }\n\n        // update the children!\n        for (let i = 0; i < container.children.length; i++) {\n            this.updateLayout(container.children[i]!);\n        }\n    }\n\n    /**\n     * @ignore\n     */\n    public destroy(): void {\n        if (!this._debugEnabled && this._debugRenderer) {\n            this._debugRenderer!.destroy();\n        }\n    }\n}\n"],"names":["setYoga","loadYoga","setYogaConfig","getYoga","throttle","throttleFn","getPixiSize","nearlyEqual","Direction","calculatePositionSpecifier","ExtensionType"],"mappings":";;;;;;;;;;;;AA+BO,MAAM,aAAoD;AAAA,EAA1D;AAWI;AAAA;AAAA;AAAA;AAAA,sCAAa;AAEZ,yCAAgB;AAChB,0CAAuC;AAEvC;AACA,qCAAY;AACZ,8CAAqB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAM7B,MAAa,KAAK,SAA+B;AACrCA,SAAA,QAAA,MAAMC,KAAAA,UAAU;AACxBC,SAAAA,cAAcC,KAAQ,QAAA,EAAE,OAAO,OAAA,CAAQ;AACvC,UAAM,EAAE,WAAW,WAAW,CAAC;AAC/B,UAAM,EAAE,YAAY,aAAA,UAAaC,YAAU,uBAAuB,IAAI,UAAU,CAAC;AAEjF,QAAI,aAAa;AACR,WAAA,KAAK,YAAY,IAAI;AAAA,IAAA;AAG9B,QAAI,eAAe,QAAW;AAC1B,WAAK,aAAa;AAAA,IAAA;AAGjB,SAAA,YAAYA,cAAY,KAAK;AAC7B,SAAA,uBAAuBC,kBAAW,CAAC,cAAyB,KAAK,YAAY,SAAS,GAAG,KAAK,WAAW;AAAA,MAC1G,SAAS;AAAA,MACT,UAAU;AAAA,IAAA,CACb;AAEI,SAAA,qBAAqB,0BAA0B,KAAK;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAO7D,MAAa,YAAY,QAAQ,CAAC,KAAK,eAAe;AAClD,SAAK,gBAAgB;AAEjB,QAAA,CAAC,KAAK,gBAAgB;AAChB,YAAA,MAAM,MAAM,QAAA,QAAA,EAAA,KAAA,MAAA,QAAO,2BAAuB,CAAA;AAE3C,WAAA,iBAAiB,IAAI,IAAI,cAAc;AAAA,IAAA;AAG5C,QAAA,CAAC,KAAK,eAAe;AACrB,WAAK,eAAgB,MAAM;AAAA,IAAA;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA;AAAA,EAOG,OAAO,WAAsB;AAC5B,QAAA,KAAK,iBAAiB,KAAK,gBAAgB;AAC3C,WAAK,eAAe,MAAM;AAChB,gBAAA,SAAS,KAAK,eAAe,MAAM;AAAA,IAAA;AAI5C,SAAA,cAAc,IAAI,KAAK,YAAY,SAAS,IAAI,KAAK,qBAAqB,SAAS;AAGxF,SAAK,aAAa,SAAS;AAAA,EAAA;AAAA,EAGxB,UAAU,EAAE,aAAuC;AACtD,QAAI,KAAK,YAAY;AACjB,WAAK,OAAO,SAAS;AAAA,IAAA;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA;AAAA,EAOI,YAAY,WAAsB;AACtC,QAAI,UAAU,UAAW;AACzB,UAAM,SAAS,UAAU;AAEzB,QAAI,QAAQ;AACR,YAAM,eAAe,OAAO;AAE5B,UAAI,aAAa,UAAU,eAAe,aAAa,WAAW,aAAa;AACrE,cAAA,OAAOC,wBAAY,MAAM;AAE3B,YAAA,aAAa,UAAU,aAAa;AACpC,gBAAM,eAAe,OAAO,KAAK,SAAW,EAAA;AAE5C,cAAI,CAACC,YAAAA,YAAY,cAAc,KAAK,KAAK,GAAG;AACjC,mBAAA,KAAK,SAAS,KAAK,KAAK;AAC/B,mBAAO,eAAe;AAAA,UAAA;AAAA,QAC1B;AAEA,YAAA,aAAa,WAAW,aAAa;AACrC,gBAAM,gBAAgB,OAAO,KAAK,UAAY,EAAA;AAE9C,cAAI,CAACA,YAAAA,YAAY,eAAe,KAAK,MAAM,GAAG;AACnC,mBAAA,KAAK,UAAU,KAAK,MAAM;AACjC,mBAAO,eAAe;AAAA,UAAA;AAAA,QAC1B;AAAA,MACJ;AAIA,UAAA,CAAC,UAAU,SAAS;AACpB,eAAO,gBAAgB;AAEvB;AAAA,MAAA;AAAA,IACJ;AAGJ,aAAS,IAAI,GAAG,IAAI,UAAU,SAAS,QAAQ,KAAK;AAChD,WAAK,YAAY,UAAU,SAAS,CAAC,CAAE;AAAA,IAAA;AAAA,EAC3C;AAAA;AAAA;AAAA;AAAA;AAAA,EAOI,aAAa,WAAsB;;AACvC,UAAM,SAAS,UAAU;AAGrB,QAAA,CAAC,UAAU,SAAS;AACpB;AAAA,IAAA;AAGJ,QAAI,QAAQ;AACR,YAAM,WAAW,OAAO;AACxB,YAAM,eAAe,OAAO;AAEtB,YAAA,uBAAuB,eAAU,WAAV,mBAAwC;AAC/D,YAAA,mBAAkB,eAAU,WAAV,mBAAkB;AAEtC,UAAA,CAAC,mBAAmB,CAAC,qBAAqB;AAC1C,YAAI,OAAO,UAAU;AACjB,iBAAO,WAAW;AACT,mBAAA;AAAA,YACL,aAAa;AAAA;AAAA,YACb,aAAa;AAAA,YACb,SAAS,aAAa,KAAKC,eAAU;AAAA,UACzC;AAAA,QAAA;AAAA,MACJ;AAGJ,UAAI,SAAS,kBAAkB,OAAO,cAAc;AAEhD,iBAAS,eAAe;AACxB,eAAO,eAAe;AAEf,eAAA,kBAAkB,SAAS,kBAAkB;AACpD,cAAM,MAAMC,2BAAAA,2BAA2B,aAAa,iBAAiB,OAAO,iBAAiB;AAAA,UACzF,OAAO;AAAA,UACP,QAAQ;AAAA,QAAA,CACX;AAED,eAAO,sBAAsB;AAAA,UACzB,GAAG,UAAU,kBAAmB,OAAO,eAAe;AAAA,UACtD,SAAS,IAAI;AAAA,UACb,SAAS,IAAI;AAAA,QACjB;AAEU,kBAAA,KAAK,UAAU,MAAM;AAC/B,wBAAU,aAAV,mCAAqB;AACrB,kBAAU,UAAU;AAAA,MAAA;AAGxB,UAAI,KAAK,eAAe;AACpB,YACI,OAAO,QAAQ,OAAO,SACrB,OAAO,qBAAqB,KAAK,sBAAsB,OAAO,QAAQ,OAAO,cAAc,OAC9F;AACO,qBAAA,mBAAA,mBAAgB,OAAO;AAAA,QAAM;AAAA,MACtC;AAAA,IACJ;AAIJ,aAAS,IAAI,GAAG,IAAI,UAAU,SAAS,QAAQ,KAAK;AAChD,WAAK,aAAa,UAAU,SAAS,CAAC,CAAE;AAAA,IAAA;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA,EAMG,UAAgB;AACnB,QAAI,CAAC,KAAK,iBAAiB,KAAK,gBAAgB;AAC5C,WAAK,eAAgB,QAAQ;AAAA,IAAA;AAAA,EACjC;AAER;AAAA;AA/MI,cAFS,cAEK,aAAY;AAAA,EACtB,MAAM,CAACC,QAAc,cAAA,aAAaA,QAAAA,cAAc,YAAY;AAAA,EAC5D,MAAM;AACV;;"}