UNPKG

@furo/util

Version:
313 lines (291 loc) 9.18 kB
import { LitElement, html, css } from 'lit'; import { FBP } from '@furo/fbp'; import 'dagre/dist/dagre.min'; import './furo-graph-renderer.js'; /** * `furo-show-flow` * Renders a flow from dom node or html source * * @summary internal helper component * @customElement * @appliesMixin FBP */ export class FuroShowFlow extends FBP(LitElement) { /** * Parse html content * @param {string} source */ parseHtml(source) { const tpl = document.createElement('div'); tpl.innerHTML = source; this.parseTemplate(tpl); } /** * Parse a dom node * @param {dom} dom node */ parseTemplate(template) { // eslint-disable-next-line no-undef this.graph = new dagre.graphlib.Graph({ multigraph: false, compound: true }); // graph settings this.graph.setGraph({ rankdir: 'LR', align: 'UL', ranksep: 50, nodesep: 20, edgesep: 40, marginx: 40, marginy: 40, }); this._collectedWires = { methods: [], events: [] }; this._recursiveParse(template, ''); this._setWireEdges(); // eslint-disable-next-line no-undef dagre.layout(this.graph); this._FBPTriggerWire('--graph', this.graph); } /** * * @private */ _setWireEdges() { // extract the wire parts from all attributes const sendingWires = {}; const receivingWires = {}; this._collectedWires.events.forEach(attr => { const rawwires = attr.value.split(','); rawwires.forEach(w => { // eslint-disable-next-line no-useless-escape const match = w.trim().match(/^([^\(\^][a-z\-_]+)/gi); if (match !== null) { if (sendingWires[match[0]] === undefined) { sendingWires[match[0]] = []; } sendingWires[match[0]].push(attr); } // park a value if (w.trim().startsWith('((')) { this.graph.setNode(`park-${w.trim()}`, { width: 200, height: 30, type: 'park', label: w.trim(), }); this.graph.setEdge(attr._graphID, `park-${w.trim()}`, { weight: 1, type: 'park', }); } // bubbling nonbubbling a value if (w.trim().startsWith('^')) { const eventtype = w.trim().startsWith('^^') ? 'bubbling' : 'nonbubbling'; this.graph.setNode(`${attr._graphID}-${w.trim()}`, { width: 200, height: 30, type: eventtype, label: w.trim(), }); this.graph.setEdge(attr._graphID, `${attr._graphID}-${w.trim()}`, { weight: 1, type: 'event', }); } // bubbling nonbubbling a value if (w.trim().startsWith('-^')) { this.graph.setNode(`${attr._graphID}-${w.trim()}`, { width: 200, height: 30, type: 'hostevent', label: w.trim(), }); this.graph.setEdge(attr._graphID, `${attr._graphID}-${w.trim()}`, { weight: 1, type: 'event', }); } }); }); this._collectedWires.methods.forEach(attr => { const rawwires = attr.value.split(','); rawwires.forEach(w => { // eslint-disable-next-line no-useless-escape const match = w.trim().match(/^([^\(\(][a-z\-_]+)/gi); if (match !== null) { if (receivingWires[match[0]] === undefined) { receivingWires[match[0]] = []; } receivingWires[match[0]].push(attr); } }); }); // setEdges for every sendingWire element with receivingWire element // eslint-disable-next-line guard-for-in,no-restricted-syntax for (const wire in sendingWires) { sendingWires[wire].forEach(source => { if (receivingWires[wire]) { receivingWires[wire].forEach(target => { this.graph.setEdge(source._graphID, target._graphID, { weight: 1, source, target, wirename: wire, }); }); } else { // no target element // add node and set edge this.graph.setNode(`${source._graphID}-notarget`, { width: 30, height: 30, type: 'notarget', source, wirename: wire, }); this.graph.setEdge(source._graphID, `${source._graphID}-notarget`, { weight: 1, source, wirename: wire, }); } }); } // find edges without source (for things like --pageActivated) // eslint-disable-next-line guard-for-in,no-restricted-syntax for (const wire in receivingWires) { receivingWires[wire].forEach(source => { if (!sendingWires[wire]) { // no target element // add node and set edge this.graph.setNode(`${source._graphID}-nosource`, { width: 30, height: 30, type: 'nosource', source, wirename: wire, }); this.graph.setParent(`${source._graphID}-nosource`, source.parentComponentID); this.graph.setEdge(`${source._graphID}-nosource`, source._graphID, { weight: 1, source, wirename: wire, }); } }); } } /** * * @param node * @param parentNode * @private */ _recursiveParse(node, parentNode) { // todo switch to childNodes ignore TEXT_NODE and store COMMENT_NODE (8) to the next ELEMENT_NODE (1) // https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType if (node.childNodes && node.childNodes.length > 0) { // start with empty let description = ''; Array.from(node.childNodes).forEach((e, i) => { if (e.nodeType === 8) { description = e.textContent; } if (e.nodeType === 1) { const nodeID = `${parentNode}.${e.tagName}-${i}`; e._graphID = nodeID; e.description = description; // clear description for next loop description = ''; this.graph.setNode(nodeID, { label: e.tagName, width: 440, height: 100, type: 'component', node: e, }); if (parentNode !== '') { // set parent this.graph.setParent(nodeID, parentNode); } // Attributes Array.from(e.attributes).forEach(attr => { const attrNodeID = `${nodeID}-${attr.name}`; // eslint-disable-next-line no-param-reassign attr._graphID = attrNodeID; // eslint-disable-next-line no-param-reassign attr.parentComponentID = nodeID; this.graph.setNode(attrNodeID, { label: attr.name, width: 200, height: 30, type: 'attribute', attr, value: attr.value, }); this.graph.setParent(attrNodeID, nodeID); // add center node this.graph.setNode(`${nodeID}-center`, { type: 'center' }); this.graph.setParent(`${nodeID}-center`, nodeID); // collect the event wires if (attr.name.startsWith('at-') || attr.name.startsWith('at-')) { this._collectedWires.events.push(attr); // eslint-disable-next-line no-param-reassign attr._type = 'event'; // einen edge setzen um @ immer rechts zu haben this.graph.setEdge(`${nodeID}-center`, attrNodeID, { type: 'center', weight: 15 }); } else { // einen edge setzen um ƒ und alle anderen immer links zu haben this.graph.setEdge(attrNodeID, `${nodeID}-center`, { type: 'center', weight: 15 }); } // collect the method wires if (attr.name.startsWith('fn-')) { this._collectedWires.methods.push(attr); // eslint-disable-next-line no-param-reassign attr._type = 'method'; } if (attr.name.startsWith('fn-')) { this._collectedWires.methods.push(attr); // eslint-disable-next-line no-param-reassign attr._type = 'method'; } if (attr.name.startsWith('set-')) { this._collectedWires.methods.push(attr); // eslint-disable-next-line no-param-reassign attr._type = 'method'; } }); this._recursiveParse(e, nodeID); } }); } } /** * Themable Styles * @private * @return {CSSResult} */ static get styles() { // language=CSS return ( css` :host { display: block; height: 100%; } :host([hidden]) { display: none; } ` ); } /** * @private * @returns {TemplateResult} */ render() { // language=HTML return html` <furo-graph-renderer fn-draw="--graph"></furo-graph-renderer> `; } } window.customElements.define('furo-show-flow', FuroShowFlow);