@ramstack/alpinegear-fragment
Version: 
@ramstack/alpinegear-fragment provides 'x-format' Alpine.js directive, allowing for fragment-like behavior similar to what's available in frameworks like 'Vue.js' or 'React', where multiple root elements can be grouped together.
76 lines (63 loc) • 2.4 kB
JavaScript
(function () {
    'use strict';
    const warn = (...args) => console.warn("alpinegear.js:", ...args);
    const is_template = el => el instanceof HTMLTemplateElement;
    const is_element = el => el.nodeType === Node.ELEMENT_NODE;
    function anchor_block(el, template, { addScopeToNode, cleanup, initTree, mutateDom, scope = {} }) {
        if (el._r_block) {
            return;
        }
        initialize();
        let nodes = is_template(template)
            ? [...template.content.cloneNode(true).childNodes]
            : [template.cloneNode(true)];
        mutateDom(() => {
            for (let node of nodes) {
                is_element(node) && addScopeToNode(node, scope, el);
                el.parentElement.insertBefore(node, el);
                is_element(node) && initTree(node);
            }
        });
        el._r_block = {
            template,
            update() {
                mutateDom(() => {
                    for (let node of nodes ?? []) {
                        el.parentElement.insertBefore(node, el);
                    }
                });
            },
            delete() {
                el._r_block = null;
                for (let node of nodes ?? []) {
                    node.remove();
                }
                nodes = null;
            }
        };
        cleanup(() => el._r_block?.delete());
    }
    function initialize() {
        document.body._r_block ??= (() => {
            const observer = new MutationObserver(mutations => {
                for (let mutation of mutations) {
                    for (let node of mutation.addedNodes) {
                        node._r_block?.update();
                    }
                }
            });
            observer.observe(document.body, { childList: true, subtree: true });
            return observer;
        })();
    }
    function plugin({ addScopeToNode, directive, initTree, mutateDom }) {
        directive("fragment", (el, {}, { cleanup }) => {
            if (!is_template(el)) {
                warn("x-fragment can only be used on a 'template' tag");
                return;
            }
            anchor_block(el, el, { addScopeToNode, cleanup, initTree, mutateDom });
        });
    }
    document.addEventListener("alpine:init", () => { Alpine.plugin(plugin); });
})();