Created
October 21, 2020 17:18
-
-
Save jaredcwhite/9ca35e07c3ec421ab1ba1772625f907d to your computer and use it in GitHub Desktop.
Ruby2JS output
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { LitElement, html } from "lit-element"; | |
// Lambda for determining default node action | |
let defaultActionForNode = (node) => { | |
switch (node.nodeName.toLowerCase()) { | |
case "form": | |
return "submit"; | |
case "input": | |
case "textarea": | |
return node.getAttribute("type") == "submit" ? "click" : "input"; | |
case "select": | |
return "change"; | |
default: | |
return "click" | |
} | |
}; | |
export class CrystallineElement extends LitElement { | |
static define(name, options={}) { | |
if (options.shadowDom == false) { | |
this.prototype.createRenderRoot = function() { return this } | |
}; | |
if (options.passThrough) this.render = () => () => null; | |
customElements.define(name, this) | |
}; | |
constructor() { | |
super(); | |
// Set initial default values | |
if (this.constructor.properties) { | |
for (let [property, config] of Object.entries(this.constructor.properties)) { | |
this[property] = config.default | |
} | |
}; | |
// button@identifier => button[custom-element-id='identifier'] | |
let swapInId = selector => ( | |
selector.replace(/@([a-z-]+)/g, `[${this.nodeName}-id='$1']`) | |
); | |
// Add queries as instance properties | |
if (this.constructor.queries) { | |
for (let [name, selector] of Object.entries(this.constructor.queries)) { | |
if (Array.isArray(selector)) { | |
selector = swapInId(selector[0]); | |
Object.defineProperty(this, `_${name}`, {get: () => ( | |
Array.from(this.querySelectorAll(selector)).filter(node => ( | |
this._nestedNodes.filter(nestedNode => nestedNode.contains(node)).length == 0 | |
)) | |
)}) | |
} else { | |
selector = swapInId(selector); | |
Object.defineProperty(this, `_${name}`, {get: () => { | |
let node = this.querySelector(selector); | |
if (this._nestedNodes.filter(nestedNode => nestedNode.contains(node)).length == 0) { | |
return node | |
} | |
}}) | |
} | |
} | |
}; | |
return this | |
}; | |
// Set up MutationObserver and get ready to look for event definitions | |
connectedCallback() { | |
super.connectedCallback(); | |
this._registeredActions = []; | |
this._nestedNodes = []; | |
this.handleNodechanges([{type: "attributes", target: this}]); | |
this._nodeObserver = new MutationObserver(this.handleNodechanges.bind(this)); | |
let config = {attributes: true, childList: true, subtree: true}; | |
return this._nodeObserver.observe(this, config) | |
}; | |
disconnectedCallback() { | |
super.disconnectedCallback(); | |
this._nodeObserver.disconnect(); | |
this._registeredActions = []; | |
this._nestedNodes = []; | |
return this._nestedNodes | |
}; | |
// Callback for MutationObserver | |
handleNodechanges(changes) { | |
let selfName = this.nodeName.toLowerCase(); | |
let actionAttr = `${selfName}-action`; | |
// Lambda to set up event listeners | |
let setupListener = (node, includeSelfNode) => { | |
if (!includeSelfNode && node.nodeName == this.nodeName) { | |
this._nestedNodes.push(node); | |
return | |
}; | |
// make sure node isn't inside a nested node | |
if (this._nestedNodes.find(nestedNode => nestedNode.contains(node))) return; | |
if (node.hasAttribute(actionAttr)) { | |
for (let actionPair of node.getAttribute(actionAttr).split(" ")) { | |
let [actionEvent, actionName] = actionPair.split("->"); | |
if (typeof actionName === 'undefined') { | |
actionName = actionEvent; | |
actionEvent = defaultActionForNode(node) | |
}; | |
actionEvent = actionEvent.trim(); | |
if (this._registeredActions.find(action => ( | |
action.node == node && action.event == actionEvent && action.name == actionName | |
))) continue; | |
node.addEventListener(actionEvent, this[actionName].bind(this)); | |
this._registeredActions.push({ | |
node, | |
event: actionEvent, | |
name: actionName | |
}) | |
} | |
} | |
}; | |
if (!this._nodeObserver) { | |
// First run situation, check all child nodes | |
for (let node of this.querySelectorAll("*")) { | |
setupListener(node, false) | |
} | |
}; | |
// Loop through all the mutations | |
for (let change of changes) { | |
if (change.type == "childList") { | |
for (let node of change.addedNodes) { | |
if (node.nodeType != 1) continue; | |
setupListener(node, false) | |
}; | |
for (let node of change.removedNodes) { | |
// clear out removed nested nodes | |
if (node.nodeName != this.nodeName) { | |
// only process element nodes | |
continue | |
}; | |
this._nestedNodes = this._nestedNodes.filter(nestedNode => nestedNode != node) | |
} | |
} else if (change.type == "attributes") { | |
setupListener(change.target, true) | |
} | |
} | |
}; | |
render() { | |
return html`<slot></slot>` | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment