The vanilla alternative for writing JSX-based React applications
git clone git://soophie.de/figure
blob: 1001c6a9390d2e06834741ac105f9a869d1cb325 (4482 bytes)[doc][blame][raw]
1
const parser = new DOMParser();2
let count = 1000;3
let create = null;5
const figure = ({createElement}) => {6
create = createElement;7
}9
const html = (strings, ...props) => {10
const elements = parse(strings, ...props);11
if(elements.length === 0) {12
throw 'No DOM element was returned!';13
}14
if(elements.length > 1) {15
console.warn('Only one DOM element can be returned!');16
}17
return elements[0];18
}20
const parse = (strings, ...props) => {21
const [html, refs] = compose(strings, props);22
let dom;23
try {24
dom = parser.parseFromString(html, 'text/html');25
}26
catch(error) {27
console.error(error);28
throw 'Invalid DOM structure!';29
}30
const nodes = [...dom.head.childNodes, ...dom.body.childNodes];31
return nodes.map((node) => render(node, refs));32
}34
/**35
* Joins the template literal strings together and replaces the properties with references.36
* The properties are being mapped to there corresponding references and with the populated37
* HTML string returned.38
*39
* @param {string[]} strings - The template literal strings40
* @param {any[]} props - The template literal properties41
* @return {any[]} The joined HTML string and the properties mapped to there references.42
*/43
const compose = (strings, props) => {44
if(strings === undefined) {45
// handles dyn function without body46
return ['', {}];47
}48
const refs = {};49
let string = '';50
for(let i = 0; i < strings.length; i++) {51
string += strings[i];52
if(props[i] !== undefined) {53
const uid = `$seg-${count++}`;54
refs[uid] = props[i];55
string += uid ?? '';56
}57
}58
return [string.trim(), refs];59
}61
/**62
* Injects the properties into the corresponding reference locations of the string.63
*64
* @param {string} string - The string containing references65
* @param {any[]} refs - The properties mapped to there references66
* @return {any[]} The string populate with the passed properties67
*/68
const feed = (string, refs) => {69
const expr = /\$seg-\d+/g;70
let values = [];71
let match = null;72
let last = 0;73
while((match = expr.exec(string)) !== null) {74
const index = match.index;75
const uid = match[0];76
values.push(string.substring(last, index));77
let value = refs[uid];78
if(value instanceof Function) {79
value = value();80
}81
values.push(value);82
last = index + uid.length;83
}84
values.push(string.substring(last));85
return values;86
}88
const dyn = (element, props) => {89
return (strings, ...childProps) => {90
return create(element, props, ...parse(strings, ...childProps));91
};92
}94
const render = (node, refs) => {95
if(node.nodeType === Node.TEXT_NODE) {96
return feed(node.textContent, refs);97
}98
if(node.nodeType === Node.COMMENT_NODE) {99
return [];100
}101
const tag = node.tagName;102
const attributes = {};103
for(const attribute of node.attributes) {104
const key = attribute.name;105
const value = attribute.textContent;106
let isDynamic = false;107
for(const ref in refs) {108
if(value === ref) {109
let match = null;110
if((match = /^@(\w+)$/.exec(key)) !== null) {111
const event = match[1];112
attributes[`on${event.substring(0, 1).toUpperCase()}${event.substring(1)}`] = refs[ref];113
}114
else if((match = /^\[(\w+)\]$/.exec(key)) !== null) {115
const property = match[1];116
attributes[property] = refs[ref];117
}118
else {119
attributes[key] = refs[ref];120
}121
isDynamic = true;122
break;123
}124
}125
if(!isDynamic) {126
if(key === 'style') {127
const styles = {};128
for(const item of value.split(';')) {129
if(item.trim().length === 0) {130
break;131
}132
let [key, value] = item.split(':');133
key = key.trim();134
let match = null;135
if((match = /-(\w)/.exec(key)) !== null) {136
const char = match[1];137
key = key.replace(`-${char}`, char.toUpperCase());138
}139
value = value.trim();140
styles[key] = feed(value, refs).join('');141
}142
attributes[key] = styles;143
}144
else {145
attributes[key] = feed(value, refs).join('');146
}147
}148
}149
const children = [];150
(node.childNodes ?? []).forEach((child) => children.push(...render(child, refs)));151
return [create(tag, attributes, ...children)];152
}154
import {defCss, defStyle} from './mods.js';156
const css = defCss({html, compose, feed});157
const style = defStyle({html, compose, feed});159
export { figure, html, dyn, css, style };