so://phie

figure

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

blob: a568d56a1274fff0fe05af5512abf5dcfa333f9f (3717 bytes)[doc][blame][raw]

1const parser = new DOMParser();

2let counter = 1000;

3let tlx = null;

4

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

16

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 }

72

73 tlx = parse;

74 return tlx;

75}

76

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

85

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

105

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

124

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