springtype
Version:
1k TypeScript/TSX nano-framework for the web
142 lines (141 loc) • 6.66 kB
JavaScript
;
exports.__esModule = true;
var st_1 = require("../st/st");
var CLASS_ATTRIBUTE_NAME = 'class';
var XLINK_ATTRIBUTE_NAME = 'xlink';
var REF_ATTRIBUTE_NAME = 'ref';
var SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
// istanbul ignore else
if (!st_1.st.dom) {
// DOM abstraction layer for manipulation
st_1.st.dom = {
hasElNamespace: function (domElement) { return domElement.namespaceURI === SVG_NAMESPACE; },
hasSvgNamespace: function (parentElement, type) {
return st_1.st.dom.hasElNamespace(parentElement) && type !== 'STYLE' && type !== 'SCRIPT';
},
createElementOrElements: function (virtualNode, parentDomElement) {
if (Array.isArray(virtualNode)) {
return st_1.st.dom.createChildElements(virtualNode, parentDomElement);
}
if (typeof virtualNode !== 'undefined') {
return st_1.st.dom.createElement(virtualNode, parentDomElement);
}
// undefined virtualNode -> e.g. when a tsx variable is used in markup which is undefined
return st_1.st.dom.createTextNode('', parentDomElement);
},
createElement: function (virtualNode, parentDomElement) {
var newEl;
if (virtualNode.type.toUpperCase() === 'SVG' ||
(parentDomElement && st_1.st.dom.hasSvgNamespace(parentDomElement, virtualNode.type.toUpperCase()))) {
newEl = document.createElementNS(SVG_NAMESPACE, virtualNode.type);
}
else {
newEl = document.createElement(virtualNode.type);
}
// reference SpringType as a reference to every element created
// this allows microframework addition libs like st-query to re-use this instance
// with the correct domImpl the element belongs to
newEl[st_1.ST_KEY] = st_1.st;
// istanbul ignore else
if (virtualNode.attributes) {
st_1.st.dom.setAttributes(virtualNode.attributes, newEl);
}
// istanbul ignore else
if (virtualNode.children) {
st_1.st.dom.createChildElements(virtualNode.children, newEl);
}
// istanbul ignore else
if (parentDomElement) {
parentDomElement.appendChild(newEl);
// check for a lifecycle "onMount" hook and call it
if (typeof newEl.$onMount === 'function') {
newEl.$onMount();
}
}
return newEl;
},
createTextNode: function (text, domElement) {
var node = document.createTextNode(text.toString());
// istanbul ignore else
if (domElement) {
domElement.appendChild(node);
}
return node;
},
createChildElements: function (virtualChildren, domElement) {
var children = [];
for (var i = 0; i < virtualChildren.length; i++) {
var virtualChild = virtualChildren[i];
if (virtualChild === null || (typeof virtualChild !== 'object' && typeof virtualChild !== 'function')) {
children.push(st_1.st.dom.createTextNode((typeof virtualChild === 'undefined' || virtualChild === null ? '' : virtualChild).toString(), domElement));
}
else {
children.push(st_1.st.dom.createElement(virtualChild, domElement));
}
}
return children;
},
setAttribute: function (name, value, domElement) {
// attributes not set (undefined) are ignored; use null value to reset an attributes state
if (typeof value === 'undefined')
return;
// save ref as { current: DOMElement } in ref object
// allows for ref={someRef}
if (name === REF_ATTRIBUTE_NAME && typeof value !== 'function') {
value.current = domElement;
}
else if (name === REF_ATTRIBUTE_NAME && typeof value === 'function') {
// allow for functional ref's like: render(<div ref={(el) => console.log('got el', el)} />)
value(domElement);
}
if (name.startsWith('on') && typeof value === 'function') {
var eventName = name.substring(2).toLowerCase();
var capturePos = eventName.indexOf('capture');
var doCapture = capturePos > -1;
if (eventName === 'mount') {
domElement.$onMount = value;
}
// onClickCapture={...} support
if (doCapture) {
eventName = eventName.substring(0, capturePos);
}
domElement.addEventListener(eventName, value, doCapture);
return;
}
// transforms className="..." -> class="..."
// allows for React TSX to work seamlessly
if (name === 'className') {
name = CLASS_ATTRIBUTE_NAME;
}
// transforms class={['a', 'b']} into class="a b"
if (name === CLASS_ATTRIBUTE_NAME && Array.isArray(value)) {
value = value.join(' ');
}
if (st_1.st.dom.hasElNamespace(domElement) && name.startsWith(XLINK_ATTRIBUTE_NAME)) {
// allows for <svg><use xlinkHref ...></svg>
domElement.setAttributeNS('http://www.w3.org/1999/xlink', (XLINK_ATTRIBUTE_NAME + ":" + name.replace(XLINK_ATTRIBUTE_NAME, '')).toLowerCase(), value);
}
else if (name === 'style' && typeof value !== 'string') {
var propNames = Object.keys(value);
// allows for style={{ margin: 10 }} etc.
for (var i = 0; i < propNames.length; i++) {
domElement.style[propNames[i]] = value[propNames[i]];
}
}
else if (typeof value === 'boolean') {
// for cases like <button checked={false} />
domElement[name] = value;
}
else {
// for any other case
domElement.setAttribute(name, value);
}
},
setAttributes: function (attributes, domElement, forceNative) {
var attrNames = Object.keys(attributes);
for (var i = 0; i < attrNames.length; i++) {
st_1.st.dom.setAttribute(attrNames[i], attributes[attrNames[i]], domElement, forceNative);
}
}
};
}