decca
Version:
Render interfaces using pure functions and virtual DOM, kinda
130 lines (96 loc) • 3.24 kB
JavaScript
'use strict';
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
var _id = require('./id');
var _id2 = _interopRequireDefault(_id);
var _createElement = require('virtual-dom/create-element');
var _createElement2 = _interopRequireDefault(_createElement);
var _diff = require('virtual-dom/diff');
var _diff2 = _interopRequireDefault(_diff);
var _patch = require('virtual-dom/patch');
var _patch2 = _interopRequireDefault(_patch);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/*
* A widget that represents a component.
* We need to do this to hook lifecycle hooks properly.
*
* Consumed in virtual-dom like so:
*
* h('div', {}, [ new Widget(el, model, build) ])
*
* widget.init()
* widget.update()
* widget.remove()
*/
function Widget(_ref, model, build) {
var component = _ref.component;
var props = _ref.props;
var children = _ref.children;
if (!props) props = {};
this.component = component;
this.build = build;
// The parameters to be passed onto the component's functions.
this.model = _extends({ props: props, children: children }, model);
}
Widget.prototype.type = 'Widget';
/*
* On widget creation, do the virtual-dom createElement() dance
*/
Widget.prototype.init = function () {
var id = setId(this, (0, _id2.default)());
// Create the virtual-dom tree
var el = this.component.render(this.model);
this.el = el;
this.tree = this.build(el); // virtual-dom vnode
this.rootNode = (0, _createElement2.default)(this.tree); // DOM element
this.rootNode._dekuId = id; // so future update() and destroy() can see it
// Trigger
trigger(this, 'onCreate');
// Export
return this.rootNode;
};
/*
* On update, diff with the previous (also a Widget)
*/
Widget.prototype.update = function (previous, domNode) {
setId(this, domNode._dekuId);
// Re-render the component
var el = this.component.render(this.model);
// If it was memoized, don't patch.
// Just make this widget a copy of the previous.
if (previous.el === el) {
this.tree = previous.tree;
this.rootNode = previous.rootNode;
this.el = el;
return;
}
this.tree = this.build(el);
// Patch the DOM node
var delta = (0, _diff2.default)(previous.tree, this.tree);
this.rootNode = (0, _patch2.default)(previous.rootNode, delta);
this.el = el;
trigger(this, 'onUpdate');
};
/*
* On destroy, trigger the onRemove hook.
*/
Widget.prototype.destroy = function (domNode) {
setId(this, domNode._dekuId);
trigger(this, 'onRemove');
};
/*
* Updates the model with things that it can have when `id` is available.
* This is because `id`'s aren't always available when Widget is initialized,
* so these can't be in the ctor.
*/
function setId(widget, id) {
widget.model.path = id;
return id;
}
/*
* Trigger a Component lifecycle event.
*/
function trigger(widget, hook, id) {
if (!widget.component[hook]) return;
return widget.component[hook](widget.model);
}
module.exports = Widget;