Skip to content

Instantly share code, notes, and snippets.

@developit
Last active April 12, 2019 19:12
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save developit/ae8c7fc102030bb18f70a6583b8e49df to your computer and use it in GitHub Desktop.
Save developit/ae8c7fc102030bb18f70a6583b8e49df to your computer and use it in GitHub Desktop.

respond

this isn't a library, don't use this.

it's a 1.5kb react-ey thing but using HTML instead of VDOM. diffing done via a hacked up version of set-dom.

Important Counter Demo

// this is a standard react component, except render() returns an HTML string
class DemoApp extends Component {
  static get defaultProps() {
    return {
      title: ''
    }
  }
  constructor() {
    super();
    this.state = { count: 0 };
  }
  increment() {
    this.setState({ count: this.state.count + 1 });
  }
  render(props, state) {
    return `
      <div id="demo-app">
        <h1>${props.title}</h1>
        <${Counter} count=${state.count} onincrement="getRootNode().host.increment()" />
      </div>
    `;
  }
}

class Counter extends Component {
  static get defaultProps() {
    return {
      count: 0,
      onincrement() {}
    }
  }

  handleClick() {
    this.props.onincrement();
  }

  render({ count, increment }) {
    return `
      <div>counter: ${count}</div>
      <button onclick="getRootNode().host.handleClick()">Increment</button>
    `;
  }
}

render(`<${DemoApp} title="Hello, World!">`, document.body);

Does it support hooks?

I'm not sure.

node_modules
package-lock.json
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e(t.respondjs={})}(this,function(t){var e,n=(e={default:function(t,e){var n=parser.parseFromString(t,htmlType);return"HTML"===e?n.documentElement:n.body.firstChild}})&&e.default||e;u.KEY="data-key",u.IGNORE="data-ignore",u.CHECKSUM="data-checksum";var o="_set-dom-",i=o+"mounted",r=1,s=9,a=11,d=u;function u(t,e){!function(e,n){if(!t||!t.nodeType)throw new Error("set-dom: You must provide a valid node to update.")}(),t.nodeType===s&&(t=t.documentElement),e.nodeType===a?l(t,e):c(t,"string"==typeof e?n(e,t.nodeName):e),t[i]||(t[i]=!0,m(t))}function c(t,e){if(t.nodeType===e.nodeType)if(t.nodeType===r){if(i=e,p(o=t)&&p(i)||h(o)===h(i)||o.isEqualNode(i))return;if(l(t,e),t.nodeName===e.nodeName)!function(t,e){var n,o,i,r,s;for(n=t.length;n--;)(i=e.getNamedItemNS(r=(o=t[n]).namespaceURI,s=o.localName))||t.removeNamedItemNS(r,s);for(n=e.length;n--;)(i=t.getNamedItemNS(r=(o=e[n]).namespaceURI,s=o.localName))?i.value!==o.value&&(i.value=o.value):(e.removeNamedItemNS(r,s),t.setNamedItemNS(o))}(t.attributes,e.attributes);else{for(var n=e.cloneNode();t.firstChild;)n.appendChild(t.firstChild);t.parentNode.replaceChild(n,t)}}else t.nodeValue!==e.nodeValue&&(t.nodeValue=e.nodeValue);else t.parentNode.replaceChild(e,v(t)),m(e);var o,i}function l(t,e){for(var n,o,i,r,s,a,d=t.firstChild,u=e.firstChild,l=0;d;)l++,o=f(n=d),d=d.nextSibling,o&&(a||(a={}),a[o]=n);for(d=t.firstChild;u;)l--,i=u,u=u.nextSibling,a&&(r=f(i))&&(s=a[r])?(delete a[r],s!==d?t.insertBefore(s,d):d=d.nextSibling,c(s,i)):d?(n=d,d=d.nextSibling,f(n)?(t.insertBefore(i,n),m(i)):c(n,i)):(t.appendChild(i),m(i));for(o in a)l--,t.removeChild(v(a[o]));for(;--l>=0;)t.removeChild(v(t.lastChild))}function f(t){if(t.nodeType===r){var e=t.getAttribute(u.KEY)||t.id;return e?o+e:void 0}}function h(t){return t.getAttribute(u.CHECKSUM)||NaN}function p(t){return null!=t.getAttribute(u.IGNORE)}function m(t){return b(t,"mount")}function v(t){return b(t,"dismount")}function b(t,e){if(f(t)){var n=document.createEvent("Event"),o={value:t};n.initEvent(e,!1,!1),Object.defineProperty(n,"target",o),Object.defineProperty(n,"srcElement",o),t.dispatchEvent(n)}for(var i=t.firstChild;i;)i=b(i,e).nextSibling;return t}var g=function(t){function e(){var e=this;t.call(this),this.props={};for(var n=function(){var t=i[o];Object.defineProperty(e,t,{get:function(){return e.props[t]},set:function(n){var o=n;"o"==t[0]&&"n"==t[1]&&(o=new Function("event","with(this){return "+n+"}").bind(e)),e.props[t]=o;var i=e.__lockattrs;e.__lockattrs=!0,e.setAttribute(t,n+""),e.__lockattrs=i}})},o=0,i=e.constructor.observedAttributes;o<i.length;o+=1)n();this.state={}}t&&(e.__proto__=t),(e.prototype=Object.create(t&&t.prototype)).constructor=e;var n={is:{configurable:!0},observedAttributes:{configurable:!0}};return n.is.get=function(){return this.name.replace(/(.)([A-Z])/g,"$1-$2").toLowerCase()},e.toString=function(){var t=this.is.match(/-/)?this.is:this.is+"-component";return customElements.get(t)||customElements.define(t,this),t},n.observedAttributes.get=function(){return Object.keys(this.defaultProps||{})},e.prototype.connectedCallback=function(){for(var t=0;t<this.attributes.length;t++){var e=this.attributes[t];this[e.name]=e.value}this.attachShadow({mode:"open"}),this._connected=!0,this._render(),this.componentDidMount&&this.componentDidMount()},e.prototype.disconnectedCallback=function(){this.componentWillUnmount&&this.componentWillUnmount(),this._connected=!1},e.prototype.attributeChangedCallback=function(t,e,n){this.__lockattrs||(this[t]=n,this._render())},e.prototype.setState=function(t){this.state=Object.assign({},this.state,t),this._render()},e.prototype._render=function(){if(this._connected&&(!this.shouldComponentUpdate||!1!==this.shouldComponentUpdate(this.props,this.state))){var t=this.render(this.props,this.state);this.shadowRoot.firstChild?d(this.shadowRoot.firstChild,t.trim()):this.shadowRoot.innerHTML=t.trim()}},Object.defineProperties(e,n),e}(HTMLElement);t.render=function(t,e){e.innerHTML=t},t.Component=g});
var t,e=(t={default:function(t,e){var n=parser.parseFromString(t,htmlType);return"HTML"===e?n.documentElement:n.body.firstChild}})&&t.default||t;d.KEY="data-key",d.IGNORE="data-ignore",d.CHECKSUM="data-checksum";var n="_set-dom-",o=n+"mounted",i=1,r=9,s=11,a=d;function d(t,n){!function(e,n){if(!t||!t.nodeType)throw new Error("set-dom: You must provide a valid node to update.")}(),t.nodeType===r&&(t=t.documentElement),n.nodeType===s?c(t,n):u(t,"string"==typeof n?e(n,t.nodeName):n),t[o]||(t[o]=!0,p(t))}function u(t,e){if(t.nodeType===e.nodeType)if(t.nodeType===i){if(r=e,f(o=t)&&f(r)||h(o)===h(r)||o.isEqualNode(r))return;if(c(t,e),t.nodeName===e.nodeName)!function(t,e){var n,o,i,r,s;for(n=t.length;n--;)(i=e.getNamedItemNS(r=(o=t[n]).namespaceURI,s=o.localName))||t.removeNamedItemNS(r,s);for(n=e.length;n--;)(i=t.getNamedItemNS(r=(o=e[n]).namespaceURI,s=o.localName))?i.value!==o.value&&(i.value=o.value):(e.removeNamedItemNS(r,s),t.setNamedItemNS(o))}(t.attributes,e.attributes);else{for(var n=e.cloneNode();t.firstChild;)n.appendChild(t.firstChild);t.parentNode.replaceChild(n,t)}}else t.nodeValue!==e.nodeValue&&(t.nodeValue=e.nodeValue);else t.parentNode.replaceChild(e,m(t)),p(e);var o,r}function c(t,e){for(var n,o,i,r,s,a,d=t.firstChild,c=e.firstChild,h=0;d;)h++,o=l(n=d),d=d.nextSibling,o&&(a||(a={}),a[o]=n);for(d=t.firstChild;c;)h--,i=c,c=c.nextSibling,a&&(r=l(i))&&(s=a[r])?(delete a[r],s!==d?t.insertBefore(s,d):d=d.nextSibling,u(s,i)):d?(n=d,d=d.nextSibling,l(n)?(t.insertBefore(i,n),p(i)):u(n,i)):(t.appendChild(i),p(i));for(o in a)h--,t.removeChild(m(a[o]));for(;--h>=0;)t.removeChild(m(t.lastChild))}function l(t){if(t.nodeType===i){var e=t.getAttribute(d.KEY)||t.id;return e?n+e:void 0}}function h(t){return t.getAttribute(d.CHECKSUM)||NaN}function f(t){return null!=t.getAttribute(d.IGNORE)}function p(t){return v(t,"mount")}function m(t){return v(t,"dismount")}function v(t,e){if(l(t)){var n=document.createEvent("Event"),o={value:t};n.initEvent(e,!1,!1),Object.defineProperty(n,"target",o),Object.defineProperty(n,"srcElement",o),t.dispatchEvent(n)}for(var i=t.firstChild;i;)i=v(i,e).nextSibling;return t}function b(t,e){e.innerHTML=t}var g=function(t){function e(){var e=this;t.call(this),this.props={};for(var n=function(){var t=i[o];Object.defineProperty(e,t,{get:function(){return e.props[t]},set:function(n){var o=n;"o"==t[0]&&"n"==t[1]&&(o=new Function("event","with(this){return "+n+"}").bind(e)),e.props[t]=o;var i=e.__lockattrs;e.__lockattrs=!0,e.setAttribute(t,n+""),e.__lockattrs=i}})},o=0,i=e.constructor.observedAttributes;o<i.length;o+=1)n();this.state={}}t&&(e.__proto__=t),(e.prototype=Object.create(t&&t.prototype)).constructor=e;var n={is:{configurable:!0},observedAttributes:{configurable:!0}};return n.is.get=function(){return this.name.replace(/(.)([A-Z])/g,"$1-$2").toLowerCase()},e.toString=function(){var t=this.is.match(/-/)?this.is:this.is+"-component";return customElements.get(t)||customElements.define(t,this),t},n.observedAttributes.get=function(){return Object.keys(this.defaultProps||{})},e.prototype.connectedCallback=function(){for(var t=0;t<this.attributes.length;t++){var e=this.attributes[t];this[e.name]=e.value}this.attachShadow({mode:"open"}),this._connected=!0,this._render(),this.componentDidMount&&this.componentDidMount()},e.prototype.disconnectedCallback=function(){this.componentWillUnmount&&this.componentWillUnmount(),this._connected=!1},e.prototype.attributeChangedCallback=function(t,e,n){this.__lockattrs||(this[t]=n,this._render())},e.prototype.setState=function(t){this.state=Object.assign({},this.state,t),this._render()},e.prototype._render=function(){if(this._connected&&(!this.shouldComponentUpdate||!1!==this.shouldComponentUpdate(this.props,this.state))){var t=this.render(this.props,this.state);this.shadowRoot.firstChild?a(this.shadowRoot.firstChild,t.trim()):this.shadowRoot.innerHTML=t.trim()}},Object.defineProperties(e,n),e}(HTMLElement);export{b as render,g as Component};
{
"name": "respondjs",
"version": "0.1.0",
"description": "",
"umd:main": "dist.js",
"main": "dist.js",
"module": "dist.mjs",
"source": "respond.js",
"scripts": {
"prepare": "microbundle --external none -f umd,es --no-sourcemap --alias ./parse-html=$PWD/parse-html.js"
},
"license": "MIT",
"devDependencies": {
"microbundle": "^0.11.0"
},
"dependencies": {
"set-dom": "^7.5.2"
}
}
export default (markup, rootName) => {
const doc = parser.parseFromString(markup, htmlType);
return rootName === 'HTML' ? doc.documentElement : doc.body.firstChild;
};
import setDOM from 'set-dom/src';
/**
* "Respond.js"
* A sortof implementation of React
* using Web Components & Shadow DOM.
*/
export function render(html, parent) {
parent.innerHTML = html;
}
export class Component extends HTMLElement {
static get is() {
return this.name.replace(/(.)([A-Z])/g, '$1-$2').toLowerCase();
}
static toString() {
const name = this.is.match(/-/) ? this.is : `${this.is}-component`;
if (!customElements.get(name)) customElements.define(name, this);
return name;
}
static get observedAttributes() {
return Object.keys(this.defaultProps || {});
}
constructor() {
super();
this.props = {};
for (const name of this.constructor.observedAttributes) {
Object.defineProperty(this, name, {
get: () => this.props[name],
set: value => {
let normalized = value;
if (name[0] == 'o' && name[1] == 'n') {
normalized = new Function('event', 'with(this){return ' + value + '}').bind(this);
}
this.props[name] = normalized;
const old = this.__lockattrs;
this.__lockattrs = true;
this.setAttribute(name, value + '');
this.__lockattrs = old;
}
});
}
this.state = {};
}
connectedCallback() {
for (let i = 0; i < this.attributes.length; i++) {
const attr = this.attributes[i];
this[attr.name] = attr.value;
}
this.attachShadow({ mode: 'open' });
this._connected = true;
this._render();
if (this.componentDidMount) this.componentDidMount();
}
disconnectedCallback() {
if (this.componentWillUnmount) this.componentWillUnmount();
this._connected = false;
}
attributeChangedCallback(name, old, value) {
if (this.__lockattrs) return;
this[name] = value;
this._render();
}
setState(update) {
this.state = Object.assign({}, this.state, update);
this._render();
}
/* deferred rendering here if needed (pointless for demo) */
//_render() {
// if (this._nextRender) return;
// this._nextRender = Promise.resolve().then(this._doRender.bind(this));
//}
_render() {
if (!this._connected) return;
if (this.shouldComponentUpdate && this.shouldComponentUpdate(this.props, this.state) === false)
return;
//this._nextRender = null;
const html = this.render(this.props, this.state);
if (!this.shadowRoot.firstChild)
// here's the naive version we have on the platform today:
this.shadowRoot.innerHTML = html.trim();
// using a library to accomplish this, like set-dom:
else setDOM(this.shadowRoot.firstChild, html.trim());
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment