solarite 0.2.4 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,11 +1,12 @@
1
1
  import Template from "./Template.js";
2
2
  import Util from "./Util.js";
3
3
  import Globals from "./Globals.js";
4
+ import {assert} from "../util/Errors.js";
4
5
 
5
6
  /**
6
7
  * Convert strings to HTMLNodes.
7
- * Using r as a tag will always create a Template.
8
- * Using r() as a function() will always create a DOM element.
8
+ * Using h`...` as a tag will always create a Template.
9
+ * Using h() as a function() will always create a DOM element.
9
10
  *
10
11
  * Features beyond what standard js tagged template strings do:
11
12
  * 1. r`` sub-expressions
@@ -15,24 +16,25 @@ import Globals from "./Globals.js";
15
16
  * 5. TODO: list more
16
17
  *
17
18
  * Currently supported:
18
- * 1. r(el, options)`<b>${'Hi'}</b>` // Create template and render its nodes to el.
19
- * 2. r(el, template, ?options) // Render the Template created by #1 to element.
19
+ * 1. h(el, options)`<b>${'Hi'}</b>` // Create template and render its nodes to el.
20
+ * 2. h(el, template, ?options) // Render the Template created by #1 to element.
20
21
  *
21
- * 3. r`<b>Hello</b> ${'World'}!` // Create Template that can later be used to create nodes.
22
+ * 3. h`<b>Hello</b> ${'World'}!` // Create Template that can later be used to create nodes.
22
23
  *
23
- * 4. r('Hello'); // Create single text node.
24
- * 5. r('<b>Hello</b>'); // Create single HTMLElement
25
- * 6. r('<b>Hello</b><u>Goodbye</u>'); // Create document fragment because there's more than one node.
26
- * 7. r()`Hello<b>${'World'}!</b>` // Same as 4-6, but evaluates the string as a Solarite template, which
24
+ * 4. h('Hello'); // Create single text node.
25
+ * 5. h('<b>Hello</b>'); // Create single HTMLElement
26
+ * 6. h('<b>Hello</b><u>Goodbye</u>'); // Create document fragment because there's more than one node.
27
+ * 7. h()`Hello<b>${'World'}!</b>` // Same as 4-6, but evaluates the string as a Solarite template, which
27
28
  * // includes properly handling nested components and r`` sub-expressions.
28
- * 8. r(template) // Render Template created by #1.
29
- *
30
- * 9. r({render(){...}}) // Pass an object with a render method, and optionally other props/methods.
29
+ * 8. h(template) // Render Template created by #1.
31
30
  *
31
+ * 9. h({render(){...}}) // Pass an object with a render method, and optionally other props/methods.
32
+ * 10. h(string, object, ...) // JSX TODO
32
33
  * @param htmlStrings {?HTMLElement|string|string[]|function():Template|{render:function()}}
33
34
  * @param exprs {*[]|string|Template|Object}
34
35
  * @return {Node|HTMLElement|Template} */
35
- export default function r(htmlStrings=undefined, ...exprs) {
36
+ export default function h(htmlStrings=undefined, ...exprs) {
37
+
36
38
 
37
39
  // TODO: Make this a more flat if/else and call other functions for the logic.
38
40
  if (htmlStrings instanceof Node) {
@@ -84,6 +86,23 @@ export default function r(htmlStrings=undefined, ...exprs) {
84
86
  }
85
87
 
86
88
  else if (typeof htmlStrings === 'string' || htmlStrings instanceof String) {
89
+ // 10. JSX
90
+ if (typeof exprs[0] === 'object') {
91
+ let tag = htmlStrings;
92
+ let props = exprs[0] || {};
93
+ let children = exprs.slice(1);
94
+
95
+ let templateHtmlStrings = [];
96
+ let templateExprs = [];
97
+
98
+ // TODO How to know which children are static html and which are expression placeholders?
99
+ // Perhaps we have to treat every text child as a string?
100
+
101
+ assert(templateHtmlStrings.length === templateExprs.length+1);
102
+ return new Template(templateHtmlStrings, templateExprs);
103
+ }
104
+
105
+
87
106
  // If it starts with a string, trim both ends.
88
107
  // TODO: Also trim if it ends with whitespace?
89
108
  if (htmlStrings.match(/^\s^</))
@@ -107,7 +126,7 @@ export default function r(htmlStrings=undefined, ...exprs) {
107
126
  else if (htmlStrings === undefined) {
108
127
  return (htmlStrings, ...exprs) => {
109
128
  //Globals.rendered.add(parent)
110
- let template = r(htmlStrings, ...exprs);
129
+ let template = h(htmlStrings, ...exprs);
111
130
  return template.render();
112
131
  }
113
132
  }
@@ -119,13 +138,19 @@ export default function r(htmlStrings=undefined, ...exprs) {
119
138
 
120
139
 
121
140
  // 9. Create dynamic element with render() function.
141
+ // TODO: This path doesn't handle embeds like data-id="..."
122
142
  else if (typeof htmlStrings === 'object') {
123
143
  let obj = htmlStrings;
124
144
 
145
+ if (obj.constructor.name !== 'Object')
146
+ throw new Error(`Solarate Web Component class ${obj.constructor?.name} must extend HTMLElement.`);
147
+
148
+
125
149
  // Special rebound render path, called by normal path.
150
+ // Intercepts the main r`...` function call inside render().
126
151
  if (Globals.objToEl.has(obj)) {
127
152
  return function(...args) {
128
- let template = r(...args);
153
+ let template = h(...args);
129
154
  let el = template.render();
130
155
  Globals.objToEl.set(obj, el);
131
156
  }.bind(obj);
@@ -134,20 +159,33 @@ export default function r(htmlStrings=undefined, ...exprs) {
134
159
  // Normal path
135
160
  else {
136
161
  Globals.objToEl.set(obj, null);
137
- obj.render(); // Calls the Special rebound render path above, when the render function calls r(this)
162
+ obj[renderF](); // Calls the Special rebound render path above, when the render function calls r(this)
138
163
  let el = Globals.objToEl.get(obj);
139
164
  Globals.objToEl.delete(obj);
140
165
 
141
166
  for (let name in obj)
142
167
  if (typeof obj[name] === 'function')
143
- el[name] = obj[name].bind(el);
168
+ el[name] = obj[name].bind(el); // Make the "this" of functions be el.
169
+ // TODO: But this doesn't work for passing an object with functions as a constructor arg via an attribute:
170
+ // <my-element arg=${{myFunc() { return this }}}
144
171
  else
145
172
  el[name] = obj[name];
146
173
 
174
+ // Bind id's
175
+ // This doesn't work for id's referenced by attributes.
176
+ // for (let idEl of el.querySelectorAll('[id],[data-id]')) {
177
+ // Util.bindId(el, idEl);
178
+ // Util.bindId(obj, idEl);
179
+ // }
180
+ // TODO: Bind styles
181
+
147
182
  return el;
148
183
  }
149
184
  }
150
185
 
151
186
  else
152
187
  throw new Error('Unsupported arguments.')
153
- }
188
+ }
189
+
190
+ // Trick to prevent minifier from renaming this function.
191
+ let renderF = 'render';
@@ -10,7 +10,7 @@ export function getObjectId(obj) {
10
10
 
11
11
  let result = objectIds.get(obj);
12
12
  if (!result) { // convert to string, store in result, then add 1 to lastObjectId.
13
- result = (lastObjectId++); // We use a unique prefix to ensure it doesn't collide w/ strings not from getObjectId()
13
+ result = '~\f' + (lastObjectId++); // We use a unique, 2-byte prefix to ensure it doesn't collide w/ strings not from getObjectId()
14
14
  objectIds.set(obj, result)
15
15
  }
16
16
  return result;
@@ -29,14 +29,7 @@ function toJSON() {
29
29
 
30
30
  // Node.prototype.toJSON = toJSON;
31
31
  // Function.prototype.toJSON = toJSON;
32
- // Sometimes these get unassigned by Chrome and Brave 119, as well as Firefox, seemingly randomly!
33
- // The same tests sometimes pass, sometimes fail, even after browser and OS restarts.
34
- // So we check the assignments on every run of getObjectHash()
35
- if (Node.prototype.toJSON !== toJSON) {
36
- Node.prototype.toJSON = toJSON;
37
- if (Function.prototype.toJSON !== toJSON) // Will it only unmap one but not the other?
38
- Function.prototype.toJSON = toJSON;
39
- }
32
+
40
33
 
41
34
  /**
42
35
  * Get a string that uniquely maps to the values of the given object.
@@ -51,6 +44,16 @@ if (Node.prototype.toJSON !== toJSON) {
51
44
  * @param obj {*}
52
45
  * @returns {string} */
53
46
  export function getObjectHash(obj) {
47
+
48
+ // Sometimes these get unassigned by Chrome and Brave 119, as well as Firefox, seemingly randomly!
49
+ // The same tests sometimes pass, sometimes fail, even after browser and OS restarts.
50
+ // So we check the assignments on every run of getObjectHash()
51
+ if (Node.prototype.toJSON !== toJSON) {
52
+ Node.prototype.toJSON = toJSON;
53
+ if (Function.prototype.toJSON !== toJSON) // Will it only unmap one but not the other?
54
+ Function.prototype.toJSON = toJSON;
55
+ }
56
+
54
57
  let result;
55
58
  isHashing = true;
56
59
  try {