so://phie

figure

The vanilla alternative for writing JSX-based React applications
git clone git://soophie.de/figure

blob: c2eed6aae06744169309a06793308a191fe15fcf (3404 bytes)[doc][blame][raw]

1const parser = new DOMParser();

2let counter = 1000;

3

4export 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 }

18

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 }

71

72 return tlx;

73}

74

75export 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}

94

95function 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}

109

110function 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}