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

100% Statements 42/42
100% Branches 16/16
100% Functions 6/6
100% Lines 42/42

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 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127            17x   17x   17x   85x         85x       81x       72x   72x   67x     5x   5x         47x       47x   47x   46x     1x   1x         69x     69x   69x       62x   62x   2x   2x     62x     7x   7x   3x   5x   5x         4x         7x   7x   3x           181x   178x   178x   264x   5x     259x     178x     181x      
/**
 * Establishes a relationship between a parent custom element and its children to
 * allow the parent to manage them
 * @param Base 
 * @returns 
 */
export default function ParentChildMixin(Base): any {
 
    return class ParentChild extends Base {
 
        static readonly _isCustomElement: boolean = true;
 
        private _adoptingParent = null;
 
        /**
         * The children elements of this one
         */
        protected adoptedChildren: Set<Node> = new Set<Node>();
 
        connectedCallback() {
 
            super.connectedCallback?.();
 
            const {
                adoptingParent
            } = this;
 
            if (adoptingParent === null) { // In slotted elements the parent is null when connected
 
                return;
            }
 
            (adoptingParent as any).adoptedChildren.add(this); // It might be null for the topmost custom element
 
            this.didAdoptChildCallback?.(adoptingParent, this);
        }
 
        disconnectedCallback() {
 
            super.disconnectedCallback?.();
 
            const {
                adoptingParent
            } = this;
 
            if (adoptingParent === null) {
 
                return;
            }
 
            this.willAbandonChildCallback?.(adoptingParent, this);
 
            (adoptingParent as any).adoptedChildren.delete(this); // It might be null for the topmost custom element
        }
 
        didMountCallback() {
 
            super.didMountCallback?.();
 
            // Add the slotted children
            const slot = this.document.querySelector('slot');
 
            if (slot === null) { // There is no slot to get the children from
 
                const {
                    adoptingParent
                } = this;
 
                if (adoptingParent !== null) {
 
                    (adoptingParent as any).adoptedChildren.add(this); // It might be null for the topmost custom element
 
                    this.didAdoptChildCallback?.(adoptingParent, this);
                }  
                
                return; // Nothing to do with the slot
            }
 
            const children = slot.assignedNodes();
 
            if (children.length > 0) { // The children have been already loaded
 
                children.forEach(child => {
                    
                    this.adoptedChildren.add(child);
 
                    this.didAdoptChildCallback?.(this, child);
                });
            }
            else { // Listen for any change in the slot
 
                slot.addEventListener('slotchange', this.handleSlotChange);
            }
 
            const {
                adoptedChildren
            } = this;
 
            if (adoptedChildren.size > 0) {
 
                this.didAdoptChildrenCallback?.(this, adoptedChildren);
            }
        }
 
        protected get adoptingParent(): Node {
 
            if (this._adoptingParent === null) {
 
                let parent = this.parentNode;
 
                while (parent !== null) {
 
                    if (parent.constructor._isCustomElement) {  // It is a custom element
 
                        break;
                    }
 
                    parent = parent.parentNode;
                }
 
                this._adoptingParent = parent;
            }
 
            return this._adoptingParent;
        }
    }
}