so://phie

figure

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

blob: 338e6616ea9e4304a4c44af1fbc1135ae40ecc2e (4478 bytes)[doc][blame][raw]

1const parser = new DOMParser();

2let count = 1000;

3let create = null;

4

5const figure = ({createElement}) => {

6 create = createElement;

7}

8

9const 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}

19

20const 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}

33

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 populated

37 * HTML string returned.

38 *

39 * @param {string[]} strings - The template literal strings

40 * @param {any[]} props - The template literal properties

41 * @return {any[]} The joined HTML string and the properties mapped to there references.

42 */

43const compose = (strings, props) => {

44 if(strings === undefined) {

45 // handles dyn function without body

46 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}

60

61/**

62 * Injects the properties into the corresponding reference locations of the string.

63 *

64 * @param {string} string - The string containing references

65 * @param {any[]} refs - The properties mapped to there references

66 * @return {any[]} The string populate with the passed properties

67 */

68const 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}

87

88const dyn = (element, props) => {

89 return (strings, ...childProps) => {

90 return create(element, props, ...parse(strings, ...childProps));

91 };

92}

93

94const 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 event = match[1];

116 attributes['value'] = 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}

153

154import {defCss, defStyle} from './mods.js';

155

156const css = defCss({html, compose, feed});

157const style = defStyle({html, compose, feed});

158

159export { figure, html, dyn, css, style };