{"version":3,"file":"Layout.mjs","sources":["../../src/core/Layout.ts"],"sourcesContent":["import { type Container } from 'pixi.js';\nimport { type Node } from 'yoga-layout';\nimport { type OverflowContainer } from '../components/LayoutContainer';\nimport { getYoga, getYogaConfig } from '../yoga';\nimport { applyStyle } from './style/applyStyle';\nimport { formatStyles } from './style/formatStyles';\nimport { type LayoutStyles } from './style/layoutStyles';\nimport { type ComputedLayout, type ComputedPixiLayout, type InternalStyles } from './types';\nimport { onChildAdded, onChildRemoved } from './utils/sort-children';\n\nexport interface LayoutOptions extends LayoutStyles {\n    target: Container;\n}\n\n/**\n * Main Layout class that handles the combination of PixiJS containers and their layout\n * using the Yoga layout engine\n */\nexport class Layout {\n    /** Default style values to apply to the layout */\n    public static defaultStyle: { container: LayoutStyles; leaf: LayoutStyles; shared: LayoutStyles } = {\n        leaf: {\n            width: 'intrinsic',\n            height: 'intrinsic',\n        },\n        container: {\n            width: 'auto',\n            height: 'auto',\n        },\n        shared: {\n            transformOrigin: '50%',\n            objectPosition: 'center',\n            flexShrink: 1,\n            flexDirection: 'row',\n            alignContent: 'stretch',\n            flexWrap: 'nowrap',\n            overflow: 'visible',\n        },\n    };\n    /** The Yoga node instance for this layout */\n    public yoga: Readonly<Node>;\n    /** The target PixiJS container */\n    public readonly target: Container;\n\n    /**\n     * Whether this object has been destroyed. If true, the object should no longer be used.\n     * After an object is destroyed, all of its functionality is disabled and references are removed.\n     */\n    public destroyed = false;\n\n    /**\n     * Flag indicating if layout needs recalculation\n     * @ignore\n     */\n    public _isDirty = false;\n\n    /**\n     * The computed pixi layout that is applied to the target container in the updateLocalTransform step\n     * @ignore\n     */\n    public _computedPixiLayout: Required<ComputedPixiLayout> = {\n        /** The left value of the view */\n        x: 0,\n        /** The top value of the view */\n        y: 0,\n        /** The offset x value of the view within its box */\n        offsetX: 0,\n        /** The offset y value of the view within its box */\n        offsetY: 0,\n        /** The scale x value of the view within its box */\n        scaleX: 1,\n        /** The scale y value of the view within its box */\n        scaleY: 1,\n        /** The x origin of the view */\n        originX: 0,\n        /** The y origin of the view */\n        originY: 0,\n    };\n\n    /**\n     * The computed bounds of the yoga node\n     * @ignore\n     */\n    public _computedLayout: ComputedLayout = {\n        /** The left value of the view */\n        left: 0,\n        /** The right value of the view */\n        right: 0,\n        /** The top value of the view */\n        top: 0,\n        /** The bottom value of the view */\n        bottom: 0,\n        /** The width of the view */\n        width: 0,\n        /** The height of the view */\n        height: 0,\n    };\n\n    /**\n     * The styles used for layout calculation\n     * @ignore\n     */\n    public _styles: InternalStyles = {\n        custom: {},\n        yoga: {},\n    };\n\n    /**\n     * The number of times the layout has been modified\n     * @ignore\n     */\n    public _modificationCount = 0;\n\n    /**\n     * Flag indicating if the layout should be recalculated even if it hasn't changed the yoga node.\n     * This is used to force an update when certain style properties change such as `objectFit`.\n     * @ignore\n     */\n    public _forceUpdate = false;\n\n    /**\n     * Flag indicating if the layout has a parent node\n     */\n    public hasParent = false;\n\n    /**\n     * The keys to track for changes to force an update\n     * @ignore\n     */\n    protected _trackedStyleKeys: (keyof LayoutStyles)[] = [\n        'borderRadius',\n        'borderColor',\n        'backgroundColor',\n        'objectFit',\n        'objectPosition',\n        'transformOrigin',\n        'isLeaf',\n    ];\n\n    constructor({ target }: LayoutOptions) {\n        this.target = target;\n        this.yoga = getYoga().Node.create(getYogaConfig());\n\n        target.on('added', this._onChildAdded, this);\n        target.on('removed', this._onChildRemoved, this);\n        target.on('destroyed', this.destroy, this);\n    }\n\n    /** Returns the layout style */\n    public get style(): Readonly<LayoutStyles> {\n        return this._styles.custom;\n    }\n\n    /** Returns the computed layout of the yoga node */\n    public get computedLayout(): Readonly<ComputedLayout> {\n        return this._computedLayout;\n    }\n\n    /** Returns the computed layout of the pixi node */\n    public get computedPixiLayout(): Readonly<Required<ComputedPixiLayout>> {\n        return this._computedPixiLayout;\n    }\n\n    /**\n     * Returns the true x position of the target.\n     *\n     * When an element is in layout, the x/y position is an offset from where it is laid out.\n     * This is the true x position of the element in the parent container.\n     */\n    public get realX() {\n        return this.target.localTransform.tx;\n    }\n    /**\n     * Returns the true y position of the target.\n     *\n     * When an element is in layout, the x/y position is an offset from where it is laid out.\n     * This is the true y position of the element in the parent container.\n     */\n    public get realY() {\n        return this.target.localTransform.ty;\n    }\n    /**\n     * Returns the true x scale of the target.\n     *\n     * When an element is in layout, the scale is an offset from 1.\n     * This is the true x scale of the element.\n     */\n    public get realScaleX() {\n        return this.target.localTransform.a;\n    }\n    /**\n     * Returns the true y scale of the target.\n     *\n     * When an element is in layout, the scale is an offset from 1.\n     * This is the true y scale of the element.\n     */\n    public get realScaleY() {\n        return this.target.localTransform.d;\n    }\n\n    /**\n     * Updates the layout style and triggers recalculation\n     * @param style - New layout style to apply\n     */\n    public setStyle(style: LayoutStyles): void {\n        const styles = formatStyles(this, style);\n        const differentCustom = JSON.stringify(this._styles.custom) !== JSON.stringify(styles.custom);\n        const differentYoga = JSON.stringify(this._styles.yoga) !== JSON.stringify(styles.yoga);\n        const different = differentCustom || differentYoga;\n        // Check if any tracked style keys have changed\n        const hasTrackedChanges = this._trackedStyleKeys.some((key) => styles.custom[key] !== this._styles.custom[key]);\n\n        this._styles = styles;\n\n        if (hasTrackedChanges) {\n            this._forceUpdate = true;\n        }\n\n        if (different) {\n            applyStyle(this.yoga, this._styles.yoga);\n            this.target._onUpdate();\n            this.invalidateRoot();\n        }\n    }\n\n    /**\n     * Marks the root layout as needing recalculation\n     * @param start - Optional container to start searching for the root from\n     */\n    public invalidateRoot(start?: Container): void {\n        const root = this.getRoot(start);\n\n        if (root.destroyed) return;\n\n        root._layout!._isDirty = true;\n        root._onUpdate();\n\n        this._modificationCount++;\n    }\n\n    /**\n     * Forces an update of the layout even if it hasn't changed the yoga node.\n     * This is used to force an update when certain style properties change such as `objectFit`.\n     * Or when you have changed something inside of Pixi that is not tracked by the layout system.\n     */\n    public forceUpdate(): void {\n        this._forceUpdate = true;\n    }\n\n    /**\n     * Finds the root container by traversing up the layout tree\n     * @param start - Optional container to start searching for the root from\n     * @returns The root container\n     */\n    public getRoot(start?: Container): Container {\n        // find the root node by traversing up the yoga tree\n        let root: Container = start || (this.target as Container);\n\n        while (true) {\n            const parent = root.parent;\n\n            if (!parent || (!parent._layout && !(parent as OverflowContainer).isOverflowContainer)) {\n                break;\n            }\n            root = parent;\n\n            if ((root as OverflowContainer).isOverflowContainer) {\n                root = root.parent!;\n            }\n        }\n\n        return root;\n    }\n\n    /**\n     * @ignore\n     */\n    public _onChildAdded(pixiParent: Container): void {\n        if (this.hasParent) return;\n\n        if (onChildAdded(this, pixiParent) === false) {\n            return;\n        }\n\n        this.hasParent = true;\n        this.invalidateRoot();\n    }\n\n    /**\n     * @ignore\n     */\n    public _onChildRemoved(parent?: Container): void {\n        if (!this.hasParent) return;\n\n        this.hasParent = false;\n        this.invalidateRoot(parent);\n        onChildRemoved(this);\n    }\n\n    public destroy(): void {\n        if (this.destroyed) return;\n        this.destroyed = true;\n\n        this.invalidateRoot();\n        this.yoga.free();\n        this.target.off('added', this._onChildAdded, this);\n        this.target.off('removed', this._onChildRemoved, this);\n        this._styles = null!;\n        this._computedPixiLayout = null!;\n        this._computedLayout = null!;\n        (this as any).target = null!;\n        this.hasParent = false;\n    }\n}\n"],"names":[],"mappings":";;;;;;;AAkBO,MAAM,OAAO;AAAA,EAyHhB,YAAY,EAAE,UAAyB;AAnGhC;AAAA;AAES;AAAA;AAMT;AAAA;AAAA;AAAA;AAAA,qCAAY;AAMZ;AAAA;AAAA;AAAA;AAAA,oCAAW;AAMX;AAAA;AAAA;AAAA;AAAA,+CAAoD;AAAA;AAAA,MAEvD,GAAG;AAAA;AAAA,MAEH,GAAG;AAAA;AAAA,MAEH,SAAS;AAAA;AAAA,MAET,SAAS;AAAA;AAAA,MAET,QAAQ;AAAA;AAAA,MAER,QAAQ;AAAA;AAAA,MAER,SAAS;AAAA;AAAA,MAET,SAAS;AAAA,IACb;AAMO;AAAA;AAAA;AAAA;AAAA,2CAAkC;AAAA;AAAA,MAErC,MAAM;AAAA;AAAA,MAEN,OAAO;AAAA;AAAA,MAEP,KAAK;AAAA;AAAA,MAEL,QAAQ;AAAA;AAAA,MAER,OAAO;AAAA;AAAA,MAEP,QAAQ;AAAA,IACZ;AAMO;AAAA;AAAA;AAAA;AAAA,mCAA0B;AAAA,MAC7B,QAAQ,CAAC;AAAA,MACT,MAAM,CAAA;AAAA,IACV;AAMO;AAAA;AAAA;AAAA;AAAA,8CAAqB;AAOrB;AAAA;AAAA;AAAA;AAAA;AAAA,wCAAe;AAKf;AAAA;AAAA;AAAA,qCAAY;AAMT;AAAA;AAAA;AAAA;AAAA,6CAA4C;AAAA,MAClD;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACJ;AAGI,SAAK,SAAS;AACd,SAAK,OAAO,QAAQ,EAAE,KAAK,OAAO,eAAe;AAEjD,WAAO,GAAG,SAAS,KAAK,eAAe,IAAI;AAC3C,WAAO,GAAG,WAAW,KAAK,iBAAiB,IAAI;AAC/C,WAAO,GAAG,aAAa,KAAK,SAAS,IAAI;AAAA,EAAA;AAAA;AAAA,EAI7C,IAAW,QAAgC;AACvC,WAAO,KAAK,QAAQ;AAAA,EAAA;AAAA;AAAA,EAIxB,IAAW,iBAA2C;AAClD,WAAO,KAAK;AAAA,EAAA;AAAA;AAAA,EAIhB,IAAW,qBAA6D;AACpE,WAAO,KAAK;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAShB,IAAW,QAAQ;AACR,WAAA,KAAK,OAAO,eAAe;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQtC,IAAW,QAAQ;AACR,WAAA,KAAK,OAAO,eAAe;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQtC,IAAW,aAAa;AACb,WAAA,KAAK,OAAO,eAAe;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQtC,IAAW,aAAa;AACb,WAAA,KAAK,OAAO,eAAe;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAO/B,SAAS,OAA2B;AACjC,UAAA,SAAS,aAAa,MAAM,KAAK;AACjC,UAAA,kBAAkB,KAAK,UAAU,KAAK,QAAQ,MAAM,MAAM,KAAK,UAAU,OAAO,MAAM;AACtF,UAAA,gBAAgB,KAAK,UAAU,KAAK,QAAQ,IAAI,MAAM,KAAK,UAAU,OAAO,IAAI;AACtF,UAAM,YAAY,mBAAmB;AAErC,UAAM,oBAAoB,KAAK,kBAAkB,KAAK,CAAC,QAAQ,OAAO,OAAO,GAAG,MAAM,KAAK,QAAQ,OAAO,GAAG,CAAC;AAE9G,SAAK,UAAU;AAEf,QAAI,mBAAmB;AACnB,WAAK,eAAe;AAAA,IAAA;AAGxB,QAAI,WAAW;AACX,iBAAW,KAAK,MAAM,KAAK,QAAQ,IAAI;AACvC,WAAK,OAAO,UAAU;AACtB,WAAK,eAAe;AAAA,IAAA;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA;AAAA,EAOG,eAAe,OAAyB;AACrC,UAAA,OAAO,KAAK,QAAQ,KAAK;AAE/B,QAAI,KAAK,UAAW;AAEpB,SAAK,QAAS,WAAW;AACzB,SAAK,UAAU;AAEV,SAAA;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQF,cAAoB;AACvB,SAAK,eAAe;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQjB,QAAQ,OAA8B;AAErC,QAAA,OAAkB,SAAU,KAAK;AAErC,WAAO,MAAM;AACT,YAAM,SAAS,KAAK;AAEpB,UAAI,CAAC,UAAW,CAAC,OAAO,WAAW,CAAE,OAA6B,qBAAsB;AACpF;AAAA,MAAA;AAEG,aAAA;AAEP,UAAK,KAA2B,qBAAqB;AACjD,eAAO,KAAK;AAAA,MAAA;AAAA,IAChB;AAGG,WAAA;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMJ,cAAc,YAA6B;AAC9C,QAAI,KAAK,UAAW;AAEpB,QAAI,aAAa,MAAM,UAAU,MAAM,OAAO;AAC1C;AAAA,IAAA;AAGJ,SAAK,YAAY;AACjB,SAAK,eAAe;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMjB,gBAAgB,QAA0B;AACzC,QAAA,CAAC,KAAK,UAAW;AAErB,SAAK,YAAY;AACjB,SAAK,eAAe,MAAM;AAC1B,mBAAe,IAAI;AAAA,EAAA;AAAA,EAGhB,UAAgB;AACnB,QAAI,KAAK,UAAW;AACpB,SAAK,YAAY;AAEjB,SAAK,eAAe;AACpB,SAAK,KAAK,KAAK;AACf,SAAK,OAAO,IAAI,SAAS,KAAK,eAAe,IAAI;AACjD,SAAK,OAAO,IAAI,WAAW,KAAK,iBAAiB,IAAI;AACrD,SAAK,UAAU;AACf,SAAK,sBAAsB;AAC3B,SAAK,kBAAkB;AACtB,SAAa,SAAS;AACvB,SAAK,YAAY;AAAA,EAAA;AAEzB;AAAA;AArSI,cAFS,QAEK,gBAAsF;AAAA,EAChG,MAAM;AAAA,IACF,OAAO;AAAA,IACP,QAAQ;AAAA,EACZ;AAAA,EACA,WAAW;AAAA,IACP,OAAO;AAAA,IACP,QAAQ;AAAA,EACZ;AAAA,EACA,QAAQ;AAAA,IACJ,iBAAiB;AAAA,IACjB,gBAAgB;AAAA,IAChB,YAAY;AAAA,IACZ,eAAe;AAAA,IACf,cAAc;AAAA,IACd,UAAU;AAAA,IACV,UAAU;AAAA,EAAA;AAElB;"}