UNPKG

@wordpress/interactivity

Version:

Package that provides a standard and simple way to handle the frontend interactivity of Gutenberg blocks.

129 lines (124 loc) 4.46 kB
/** * External dependencies */ import { h } from 'preact'; /** * Internal dependencies */ import { directivePrefix as p } from './constants'; const ignoreAttr = `data-${p}-ignore`; const islandAttr = `data-${p}-interactive`; const fullPrefix = `data-${p}-`; const namespaces = []; const currentNamespace = () => { var _namespaces; return (_namespaces = namespaces[namespaces.length - 1]) !== null && _namespaces !== void 0 ? _namespaces : null; }; // Regular expression for directive parsing. const directiveParser = new RegExp(`^data-${p}-` + // ${p} must be a prefix string, like 'wp'. // Match alphanumeric characters including hyphen-separated // segments. It excludes underscore intentionally to prevent confusion. // E.g., "custom-directive". '([a-z0-9]+(?:-[a-z0-9]+)*)' + // (Optional) Match '--' followed by any alphanumeric charachters. It // excludes underscore intentionally to prevent confusion, but it can // contain multiple hyphens. E.g., "--custom-prefix--with-more-info". '(?:--([a-z0-9_-]+))?$', 'i' // Case insensitive. ); // Regular expression for reference parsing. It can contain a namespace before // the reference, separated by `::`, like `some-namespace::state.somePath`. // Namespaces can contain any alphanumeric characters, hyphens, underscores or // forward slashes. References don't have any restrictions. const nsPathRegExp = /^([\w-_\/]+)::(.+)$/; export const hydratedIslands = new WeakSet(); // Recursive function that transforms a DOM tree into vDOM. export function toVdom(root) { const treeWalker = document.createTreeWalker(root, 205 // ELEMENT + TEXT + COMMENT + CDATA_SECTION + PROCESSING_INSTRUCTION ); function walk(node) { const { attributes, nodeType, localName } = node; if (nodeType === 3) return [node.data]; if (nodeType === 4) { const next = treeWalker.nextSibling(); node.replaceWith(new window.Text(node.nodeValue)); return [node.nodeValue, next]; } if (nodeType === 8 || nodeType === 7) { const next = treeWalker.nextSibling(); node.remove(); return [null, next]; } const props = {}; const children = []; const directives = []; let ignore = false; let island = false; for (let i = 0; i < attributes.length; i++) { const n = attributes[i].name; if (n[fullPrefix.length] && n.slice(0, fullPrefix.length) === fullPrefix) { if (n === ignoreAttr) { ignore = true; } else { var _nsPathRegExp$exec$sl; let [ns, value] = (_nsPathRegExp$exec$sl = nsPathRegExp.exec(attributes[i].value)?.slice(1)) !== null && _nsPathRegExp$exec$sl !== void 0 ? _nsPathRegExp$exec$sl : [null, attributes[i].value]; try { value = JSON.parse(value); } catch (e) {} if (n === islandAttr) { var _value$namespace; island = true; namespaces.push((_value$namespace = value?.namespace) !== null && _value$namespace !== void 0 ? _value$namespace : null); } else { directives.push([n, ns, value]); } } } else if (n === 'ref') { continue; } props[n] = attributes[i].value; } if (ignore && !island) return [h(localName, { ...props, innerHTML: node.innerHTML, __directives: { ignore: true } })]; if (island) hydratedIslands.add(node); if (directives.length) { props.__directives = directives.reduce((obj, [name, ns, value]) => { const [, prefix, suffix = 'default'] = directiveParser.exec(name); if (!obj[prefix]) obj[prefix] = []; obj[prefix].push({ namespace: ns !== null && ns !== void 0 ? ns : currentNamespace(), value, suffix }); return obj; }, {}); } if (localName === 'template') { props.content = [...node.content.childNodes].map(childNode => toVdom(childNode)); } else { let child = treeWalker.firstChild(); if (child) { while (child) { const [vnode, nextChild] = walk(child); if (vnode) children.push(vnode); child = nextChild || treeWalker.nextSibling(); } treeWalker.parentNode(); } } // Restore previous namespace. if (island) namespaces.pop(); return [h(localName, props, children)]; } return walk(treeWalker.currentNode); } //# sourceMappingURL=vdom.js.map