The vanilla alternative for writing JSX-based React applications
git clone git://soophie.de/figure
blob: c2eed6aae06744169309a06793308a191fe15fcf (3404 bytes)[doc][blame][raw]
1
const parser = new DOMParser();2
let counter = 1000;4
export const createTlx = (createElement) => {5
function tlx(parts, ...props) {6
const [html, refs] = ltr(parts, props);7
const dom = parser.parseFromString(html, 'text/html');8
if(dom.body.childNodes.length !== 1) {9
throw 'invalid DOM structure!';10
}11
const node = dom.body.childNodes[0];12
const elements = render(node, refs);13
if(elements.length !== 1) {14
throw 'invalid VDOM structure!';15
}16
return elements[0];17
}19
function render(node, refs) {20
if(node.nodeType === Node.TEXT_NODE) {21
return apply(node.textContent, refs);22
}23
const tag = node.tagName.toLowerCase();24
const attributes = {};25
for(const attribute of node.attributes) {26
const key = attribute.name;27
const value = attribute.textContent;28
let isDynamic = false;29
for(const ref in refs) {30
if(value === ref) {31
let match = null;32
if((match = /^@(\w+)$/.exec(key)) !== null) {33
const event = match[1];34
attributes[`on${event.substr(0, 1).toUpperCase()}${event.substr(1)}`] = refs[ref];35
}36
else {37
attributes[key] = refs[ref];38
}39
isDynamic = true;40
break;41
}42
}43
if(!isDynamic) {44
if(key === 'style') {45
const styles = {};46
for(const item of value.split(';')) {47
if(item.trim().length === 0) {48
break;49
}50
let [key, value] = item.split(':');51
key = key.trim();52
let match = null;53
if((match = /-(\w)/.exec(key)) !== null) {54
const char = match[1];55
key = key.replace(`-${char}`, char.toUpperCase());56
}57
value = value.trim();58
styles[key] = apply(value, refs).join('');59
}60
attributes[key] = styles;61
}62
else {63
attributes[key] = apply(value, refs).join('');64
}65
}66
}67
const children = [];68
(node.childNodes ?? []).forEach((child) => children.push(...render(child, refs)));69
return [createElement(tag, attributes, ...children)];70
}72
return tlx;73
}75
export const css = (parts, ...props) => {76
const [css, refs] = ltr(parts, props);77
const styles = {};78
for(const item of css.split(';')) {79
if(item.trim().length === 0) {80
break;81
}82
let [key, value] = item.split(':');83
key = key.trim();84
let match = null;85
if((match = /-(\w)/.exec(key)) !== null) {86
const char = match[1];87
key = key.replace(`-${char}`, char.toUpperCase());88
}89
value = value.trim();90
styles[key] = apply(value, refs).join('');91
}92
return styles;93
}95
function apply(value, refs) {96
let values = [];97
const expr = /\$tlx-\d+/g;98
let match = null;99
let last = 0;100
while((match = expr.exec(value)) !== null) {101
const index = match.index;102
values.push(value.substring(last, index));103
values.push(refs[match[0]]);104
last = index + match[0].length;105
}106
values.push(value.substring(last));107
return values;108
}110
function ltr(parts, props) {111
let string = '';112
const refs = {};113
for(let i = 0; i < parts.length; i++) {114
string += parts[i];115
if(props[i] !== undefined) {116
const id = `$tlx-${counter}`;117
refs[id] = props[i];118
string += id ?? '';119
counter++;120
}121
}122
return [string.trim(), refs];123
}