zmp-core
Version:
Full featured mobile HTML framework for building iOS & Android apps
419 lines (347 loc) • 12.5 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', {
value: true
});
exports['default'] = vdom;
var _h = _interopRequireDefault(require('./snabbdom/h'));
var _customComponents = _interopRequireDefault(require('./custom-components'));
var _utils = require('../../shared/utils');
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
function _toConsumableArray(arr) {
return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread();
}
function _nonIterableSpread() {
throw new TypeError('Invalid attempt to spread non-iterable instance');
}
function _iterableToArray(iter) {
if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === '[object Arguments]') return Array.from(iter);
}
function _arrayWithoutHoles(arr) {
if (Array.isArray(arr)) {
for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) {
arr2[i] = arr[i];
}
return arr2;
}
}
function _readOnlyError(name) {
throw new Error('"' + name + '" is read-only');
}
var SELF_CLOSING = 'area base br col command embed hr img input keygen link menuitem meta param source track wbr'.split(' ');
var PROPS_ATTRS = 'hidden checked disabled readonly selected autofocus autoplay required multiple value indeterminate routeProps innerHTML'.split(' ');
var BOOLEAN_PROPS = 'hidden checked disabled readonly selected autofocus autoplay required multiple readOnly indeterminate'.split(' ');
var getTagName = function getTagName(treeNode) {
return typeof treeNode.type === 'function' ? treeNode.type.name || 'CustomComponent' : treeNode.type;
};
var toCamelCase = function toCamelCase(name) {
return name.split('-').map(function (word, index) {
if (index === 0) return word.toLowerCase();
return word[0].toUpperCase() + word.substr(1);
}).join('');
};
var propsFromAttrs = function propsFromAttrs() {
var context = {};
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
args.forEach(function () {
var obj = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
Object.keys(obj).forEach(function (key) {
context[toCamelCase(key)] = obj[key];
});
});
return context;
};
var createCustomComponent = function createCustomComponent(_ref) {
var zmp = _ref.zmp,
treeNode = _ref.treeNode,
vnode = _ref.vnode,
data = _ref.data;
var component = typeof treeNode.type === 'function' ? treeNode.type : _customComponents['default'][treeNode.type];
zmp.component.create(component, propsFromAttrs(data.attrs || {}, data.props || {}), {
el: vnode.elm,
children: treeNode.children
}).then(function (c) {
if (vnode.data && vnode.data.on && c && c.$el) {
Object.keys(vnode.data.on).forEach(function (eventName) {
c.$el.on(eventName, vnode.data.on[eventName]);
});
} // eslint-disable-next-line
vnode.elm.__component__ = c;
});
};
var updateCustomComponent = function updateCustomComponent(vnode) {
// eslint-disable-next-line
var component = vnode && vnode.elm && vnode.elm.__component__;
if (!component) return;
var newProps = propsFromAttrs(vnode.data.attrs || {}, vnode.data.props || {});
component.children = vnode.data.treeNode.children;
Object.assign(component.props, newProps);
component.update();
};
var destroyCustomComponent = function destroyCustomComponent(vnode) {
// eslint-disable-next-line
var component = vnode && vnode.elm && vnode.elm.__component__;
if (component) {
var el = component.el,
$el = component.$el;
if (vnode.data && vnode.data.on && $el) {
Object.keys(vnode.data.on).forEach(function (eventName) {
$el.off(eventName, vnode.data.on[eventName]);
});
}
if (component.destroy) component.destroy();
if (el && el.parentNode) el.parentNode.removeChild(el);
delete vnode.elm.__component__; // eslint-disable-line
}
};
var isCustomComponent = function isCustomComponent(treeNodeType) {
return typeof treeNodeType === 'function' || treeNodeType && treeNodeType.indexOf('-') > 0 && _customComponents['default'][treeNodeType];
};
function getHooks(treeNode, data, zmp, initial, isRoot) {
var hooks = {};
var insert = [];
var destroy = [];
var update = [];
var postpatch = [];
var isFakeElement = false;
var tagName = getTagName(treeNode);
if (data && data.attrs && data.attrs.component) {
// eslint-disable-next-line
tagName = (_readOnlyError('tagName'), data.attrs.component);
delete data.attrs.component;
isFakeElement = true;
}
var isCustom = isCustomComponent(treeNode.type);
if (isCustom) {
insert.push(function (vnode) {
if (vnode.sel !== tagName && !isFakeElement) return;
createCustomComponent({
zmp: zmp,
treeNode: treeNode,
vnode: vnode,
data: data
});
});
destroy.push(function (vnode) {
destroyCustomComponent(vnode);
});
update.push(function (oldVnode, vnode) {
updateCustomComponent(vnode);
});
}
if (!isCustom) {
if (!data || !data.attrs || !data.attrs['class']) return hooks;
var classNames = data.attrs['class'];
classNames.split(' ').forEach(function (className) {
if (!initial) {
insert.push.apply(insert, _toConsumableArray(zmp.getVnodeHooks('insert', className)));
}
destroy.push.apply(destroy, _toConsumableArray(zmp.getVnodeHooks('destroy', className)));
update.push.apply(update, _toConsumableArray(zmp.getVnodeHooks('update', className)));
postpatch.push.apply(postpatch, _toConsumableArray(zmp.getVnodeHooks('postpatch', className)));
});
}
if (isRoot && !initial) {
postpatch.push(function (oldVnode, vnode) {
var vn = vnode || oldVnode;
if (!vn) return;
if (vn.data && vn.data.component) {
vn.data.component.hook('onUpdated');
}
});
}
if (insert.length === 0 && destroy.length === 0 && update.length === 0 && postpatch.length === 0) {
return hooks;
}
if (insert.length) {
hooks.insert = function (vnode) {
insert.forEach(function (f) {
return f(vnode);
});
};
}
if (destroy.length) {
hooks.destroy = function (vnode) {
destroy.forEach(function (f) {
return f(vnode);
});
};
}
if (update.length) {
hooks.update = function (oldVnode, vnode) {
update.forEach(function (f) {
return f(oldVnode, vnode);
});
};
}
if (postpatch.length) {
hooks.postpatch = function (oldVnode, vnode) {
postpatch.forEach(function (f) {
return f(oldVnode, vnode);
});
};
}
return hooks;
}
var getEventHandler = function getEventHandler(eventHandler) {
var _ref2 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
stop = _ref2.stop,
prevent = _ref2.prevent,
once = _ref2.once;
var fired = false;
function handler() {
var e = arguments.length <= 0 ? undefined : arguments[0];
if (once && fired) return;
if (stop) e.stopPropagation();
if (prevent) e.preventDefault();
fired = true;
eventHandler.apply(void 0, arguments);
}
return handler;
};
var getData = function getData(treeNode, component, zmp, initial, isRoot) {
var data = {
component: component,
treeNode: treeNode
};
var tagName = getTagName(treeNode);
Object.keys(treeNode.props).forEach(function (attrName) {
var attrValue = treeNode.props[attrName];
if (typeof attrValue === 'undefined') return;
if (PROPS_ATTRS.indexOf(attrName) >= 0) {
// Props
if (!data.props) data.props = {};
if (attrName === 'readonly') {
// eslint-disable-next-line
attrName = 'readOnly';
}
if (attrName === 'routeProps') {
// eslint-disable-next-line
attrName = 'zmpRouteProps';
}
if (tagName === 'option' && attrName === 'value') {
if (!data.attrs) data.attrs = {};
data.attrs.value = attrValue;
}
if (BOOLEAN_PROPS.indexOf(attrName) >= 0) {
// eslint-disable-next-line
data.props[attrName] = attrValue === false ? false : true;
} else {
data.props[attrName] = attrValue;
}
} else if (attrName === 'key') {
// Key
data.key = attrValue;
} else if (attrName.indexOf('@') === 0 || attrName.indexOf('on') === 0 && attrName.length > 2) {
// Events
if (!data.on) data.on = {};
var eventName = attrName.indexOf('@') === 0 ? attrName.substr(1) : (0, _utils.eventNameToColonCase)(attrName.substr(2));
var stop = false;
var prevent = false;
var once = false;
if (eventName.indexOf('.') >= 0) {
eventName.split('.').forEach(function (eventNamePart, eventNameIndex) {
if (eventNameIndex === 0) eventName = eventNamePart;else {
if (eventNamePart === 'stop') stop = true;
if (eventNamePart === 'prevent') prevent = true;
if (eventNamePart === 'once') once = true;
}
});
}
data.on[eventName] = getEventHandler(attrValue, {
stop: stop,
prevent: prevent,
once: once
});
} else if (attrName === 'style') {
// Style
if (typeof attrValue !== 'string') {
data.style = attrValue;
} else {
if (!data.attrs) data.attrs = {};
data.attrs.style = attrValue;
}
} else {
// Rest of attribures
if (!data.attrs) data.attrs = {};
data.attrs[attrName] = attrValue; // ID -> Key
if (attrName === 'id' && !data.key && !isRoot) {
data.key = attrValue;
}
}
});
var hooks = getHooks(treeNode, data, zmp, initial, isRoot);
hooks.prepatch = function (oldVnode, vnode) {
if (!oldVnode || !vnode) return;
if (oldVnode && oldVnode.data && oldVnode.data.props) {
Object.keys(oldVnode.data.props).forEach(function (key) {
if (BOOLEAN_PROPS.indexOf(key) < 0) return;
if (!vnode.data) vnode.data = {};
if (!vnode.data.props) vnode.data.props = {};
if (oldVnode.data.props[key] === true && !(key in vnode.data.props)) {
vnode.data.props[key] = false;
}
});
}
};
data.hook = hooks;
return data;
};
var getChildren = function getChildren(treeNode, component, zmp, initial) {
if (treeNode && treeNode.type && SELF_CLOSING.indexOf(treeNode.type) >= 0) {
return [];
}
var children = [];
var nodes = treeNode.children;
for (var i = 0; i < nodes.length; i += 1) {
var childNode = nodes[i];
var child = treeNodeToVNode(childNode, component, zmp, initial, false);
if (Array.isArray(child)) {
children.push.apply(children, _toConsumableArray(child));
} else if (child) {
children.push(child);
}
}
return children;
};
var getSlots = function getSlots(treeNode, component, zmp, initial) {
var slotName = treeNode.props.name || 'default';
var slotNodes = (component.children || []).filter(function (childTreeNode) {
var childSlotName = 'default';
if (childTreeNode.props) {
childSlotName = childTreeNode.props.slot || 'default';
}
return childSlotName === slotName;
});
if (slotNodes.length === 0) {
return getChildren(treeNode, component, zmp, initial);
}
return slotNodes.map(function (subTreeNode) {
return treeNodeToVNode(subTreeNode, component, zmp, initial);
});
};
var isTreeNode = function isTreeNode(treeNode) {
return (0, _utils.isObject)(treeNode) && 'props' in treeNode && 'type' in treeNode && 'children' in treeNode;
};
var treeNodeToVNode = function treeNodeToVNode(treeNode, component, zmp, initial, isRoot) {
if (!isTreeNode(treeNode)) {
return String(treeNode);
}
if (treeNode.type === 'slot') {
return getSlots(treeNode, component, zmp, initial);
}
var data = getData(treeNode, component, zmp, initial, isRoot);
var children = isCustomComponent(treeNode.type) ? [] : getChildren(treeNode, component, zmp, initial);
return (0, _h['default'])(getTagName(treeNode), data, children);
};
function vdom() {
var tree = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var component = arguments.length > 1 ? arguments[1] : undefined;
var initial = arguments.length > 2 ? arguments[2] : undefined;
return treeNodeToVNode(tree, component, component.zmp, initial, true);
}