All files / src/custom-element/mixins/core VirtualDomMixin.ts

90.32% Statements 28/31
91.67% Branches 11/12
90% Functions 9/10
89.66% Lines 26/29

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104  17x 17x             17x   17x                         85x       98x   98x   93x           98x   98x   78x   73x   73x           20x   20x   20x   20x                       98x               73x   73x   5x     73x               20x   20x   3x     20x      
import { NodePatchingData } from "../../../renderer/NodePatcher";
import mountNodes from "../../../renderer/mountNodes";
import updateNodes from "../../../renderer/updateNodes";
 
/**
 * Updates the element using a virtual DOM approach
 * @param Base 
 * @returns 
 */
export default function VirtualDomMixin(Base) : any {
 
    return class VirtualDom extends Base {
 
        didMountCallback() { }
 
        willUpdateCallback() { };
 
        didUpdateCallback() { };
 
        willUnmountCallback() { };
 
        /**
         * The old patching node data to patch against
         */
        private _oldPatchingData: NodePatchingData | NodePatchingData[] = null;
 
        protected updateDom() {
 
            let newPatchingData: NodePatchingData | NodePatchingData[] = this.render();
 
            if (newPatchingData !== null) { // Only if there is something to render
 
                newPatchingData = this.beforeRender(newPatchingData); // Modify the original patching data if needed
            }
 
            const {
                document,
                _oldPatchingData
            } = this;
 
            if (_oldPatchingData === null) {
 
                if (newPatchingData !== null) { // Mount
 
                    mountNodes(document, newPatchingData);
 
                    this._waitForChildrenToMount();
                }
                // else newPatchingData === null - Nothing to do
            }
            else { // this._oldPatchingData !== null
 
                Eif (newPatchingData !== null) { // Update
 
                    this.willUpdateCallback();
 
                    updateNodes(document, _oldPatchingData, newPatchingData);
 
                    this._waitForChildrenToUpdate();
                }
                else { // newPatchingData === null - Unmount
 
                    this.willUnmountCallback?.();
 
                    (this.document as HTMLElement).replaceChildren(); // Remove all the existing children
 
                    this.stylesAdded = false; // Need to re-add the styles
                }
            }
 
            this._oldPatchingData = newPatchingData;
        }
 
        /**
         * Wait for the children to mount before this (parent)
         */
        private async _waitForChildrenToMount() {
 
            const updatePromises = [...this.adoptedChildren].map(child => (child as any)._updatePromise);
 
            if (updatePromises.length > 0) {
 
                await Promise.all(updatePromises);
            }
 
            this.didMountCallback();
        }
 
        /**
         * Wait for the children to update before this (parent)
         */
        private async _waitForChildrenToUpdate() {
 
            const updatePromises = [...this.adoptedChildren].map(child => (child as any)._updatePromise);
 
            if (updatePromises.length > 0) {
 
                await Promise.all(updatePromises);
            }
 
            this.didUpdateCallback();
        }
    }
}