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