solarite 0.4.0 → 0.5.1

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,153 +1,8 @@
1
- import Globals from './Globals.js';
2
1
  import NodeGroup from './NodeGroup.js';
3
2
 
4
-
5
3
  export default class RootNodeGroup extends NodeGroup {
6
4
 
7
- /**
8
- * Root node at the top of the hierarchy.
9
- * @type {HTMLElement} */
10
- root;
11
-
12
- /**
13
- * When we call renerWatched() we re-render these expressions, then clear this to a new Map()
14
- * @type {Map<ExprPath, ValueOp|WholeArrayOp|ArraySpliceOp[]>} */
15
- exprsToRender = new Map();
16
-
17
- /**
18
- * @param template {Template}
19
- * @param el {?HTMLElement} Optional, pre-existing htmlElement tat will be the root.
20
- * @param options {?object} */
21
- constructor(template, el, options) {
22
- super(template);
23
-
24
- this.options = options;
25
-
26
- let [fragment, shell] = this.populateFromTemplate(template);
27
-
28
- let startingPathDepth = 0;
29
-
30
-
31
- if (fragment instanceof Text) {
32
-
33
- if (el) {
34
- this.startNode = el;
35
- this.endNode = el;
36
- if (fragment.nodeValue.length)
37
- el.append(fragment);
38
- this.root = el;
39
- }
40
- else
41
- throw new Error('Cannot create a standalone text node');
42
- Globals.nodeGroups.set(this.root, this);
43
- }
44
-
45
-
46
- else {
47
-
48
- // If adding NodeGroup to an element.
49
- if (el) {
50
- this.root = el;
51
-
52
- // Save slot children
53
- let slotChildren;
54
- if (el.childNodes.length) {
55
- slotChildren = Globals.doc.createDocumentFragment();
56
- slotChildren.append(...el.childNodes);
57
- }
58
-
59
- // If el should replace the root node of the fragment.
60
- if (isReplaceEl(fragment, el)) {
61
- el.append(...fragment.children[0].childNodes);
62
-
63
- // Copy attributes
64
- for (let attrib of fragment.children[0].attributes)
65
- if (!el.hasAttribute(attrib.name) && attrib.name !== 'solarite-placeholder')
66
- el.setAttribute(attrib.name, attrib.value);
67
-
68
- // Go one level deeper into all of shell's paths.
69
- startingPathDepth = 1;
70
- }
71
-
72
- else {
73
- let isEmpty = fragment.childNodes.length === 1 && fragment.childNodes[0].nodeType === 3 && fragment.childNodes[0].textContent === '';
74
- if (!isEmpty)
75
- el.append(...fragment.childNodes);
76
- }
77
-
78
- // Setup children
79
- if (slotChildren) {
80
-
81
- // Named slots
82
- for (let slot of el.querySelectorAll('slot[name]')) {
83
- let name = slot.getAttribute('name')
84
- if (name) {
85
- let slotChildren2 = slotChildren.querySelectorAll(`[slot='${name}']`);
86
- slot.append(...slotChildren2);
87
- }
88
- }
89
-
90
- // Unnamed slots
91
- let unamedSlot = el.querySelector('slot:not([name])')
92
- if (unamedSlot)
93
- unamedSlot.append(slotChildren);
94
-
95
- // No slots
96
- else
97
- el.append(slotChildren);
98
- }
99
-
100
- this.startNode = el;
101
- this.endNode = el;
102
- }
103
-
104
- // Instantiate as a standalone element.
105
- else {
106
- let singleEl = getSingleEl(fragment);
107
- this.root = singleEl || fragment; // We return the whole fragment when calling h() with a collection of nodes.
108
-
109
- if (singleEl)
110
- startingPathDepth = 1;
111
- }
112
- Globals.nodeGroups.set(this.root, this);
113
- this.updatePaths(this.root, shell.paths, startingPathDepth);
114
-
115
- // Static web components can sometimes have children created via expressions.
116
- // But calling applyExprs() will mess up the shell's path to them.
117
- // So we find them first, then call activateStaticComponents() after their children have been created.
118
- this.staticComponents = this.findStaticComponents(this.root, shell, startingPathDepth);
119
-
120
- this.activateEmbeds(this.root, shell, startingPathDepth);
121
-
122
- // Apply exprs
123
- this.applyExprs(template.exprs);
124
-
125
- this.instantiateStaticComponents(this.staticComponents);
126
- }
127
-
128
-
129
- }
130
- }
131
-
132
- function getSingleEl(fragment) {
133
- let nonempty = [];
134
- for (let n of fragment.childNodes) {
135
- if (n.nodeType === 1 || n.nodeType === 3 && n.textContent.trim().length) {
136
- if (nonempty.length)
137
- return null;
138
- nonempty.push(n);
139
- }
140
- }
141
- return nonempty[0];
142
- }
5
+ // Used only by watch.js
6
+ exprsToRender;
143
7
 
144
- /**
145
- * Does the fragment have one child that's an element matching the tagname of el?
146
- * @param fragment {DocumentFragment}
147
- * @param el {HTMLElement}
148
- * @returns {boolean} */
149
- function isReplaceEl(fragment, el) {
150
- return fragment.children.length===1
151
- && el.tagName.includes('-') // TODO: Check for solarite-placeholder attribute instead?
152
- && fragment.children[0].tagName.replace('-SOLARITE-PLACEHOLDER', '') === el.tagName;
153
8
  }
package/src/Shell.js CHANGED
@@ -1,8 +1,13 @@
1
- import {assert} from "./assert.js";
2
- import ExprPath, {ExprPathType, getNodePath} from "./ExprPath.js";
1
+ import assert from "./assert.js";
2
+ import Path from "./Path.js";
3
3
  import Util from "./Util.js";
4
4
  import Globals from "./Globals.js";
5
5
  import HtmlParser from "./HtmlParser.js";
6
+ import PathToEvent from "./PathToEvent.js";
7
+ import PathToAttribValue from "./PathToAttribValue.js";
8
+ import PathToAttribs from "./PathToAttribs.js";
9
+ import PathToNodes from "./PathToNodes.js";
10
+ import PathToComponent from "./PathToComponent.js";
6
11
 
7
12
  /**
8
13
  * A Shell is created from a tagged template expression instantiated as Nodes,
@@ -17,10 +22,10 @@ export default class Shell {
17
22
  * @type {DocumentFragment|Text} DOM parent of the shell's nodes. */
18
23
  fragment;
19
24
 
20
- /** @type {ExprPath[]} Paths to where expressions should go. */
25
+ /** @type {Path[]} Paths to where expressions should go. */
21
26
  paths = [];
22
27
 
23
- // Elements with events. Not yet used.
28
+ // Elements with events. Is there a reason to use this? We already mark event Exprs in Shell.js.
24
29
  // events = [];
25
30
 
26
31
  /** @type {int[][]} Array of paths */
@@ -32,14 +37,6 @@ export default class Shell {
32
37
  /** @type {int[][]} Array of paths */
33
38
  styles = [];
34
39
 
35
- /** @type {int[][]} Array of paths. Used by activateEmbeds() to quickly find components. */
36
- staticComponents = [];
37
-
38
- /** @type {{path:int[], attribs:Record<string, string>}[]} */
39
- //componentAttribs = [];
40
-
41
-
42
-
43
40
  /**
44
41
  * Create the nodes but without filling in the expressions.
45
42
  * This is useful because the expression-less nodes created by a template can be cached.
@@ -52,6 +49,7 @@ export default class Shell {
52
49
  this._html = html.join('');
53
50
  //#ENDIF
54
51
 
52
+ // If no html tags or entities, just create a text node.
55
53
  if (html.length === 1 && !html[0].match(/[<&]/)) {
56
54
  this.fragment = Globals.doc.createTextNode(html[0]);
57
55
  return;
@@ -59,11 +57,11 @@ export default class Shell {
59
57
 
60
58
 
61
59
  // 1. Add placeholders
62
- let joinedHtml = Shell.addPlaceholders(html);
60
+ let htmlWithPlaceholders = Shell.addPlaceholders(html);
63
61
 
64
62
  let template = Globals.doc.createElement('template'); // Using a single global template won't keep the nodes as children of the DocumentFragment.
65
- if (joinedHtml)
66
- template.innerHTML = joinedHtml;
63
+ if (htmlWithPlaceholders)
64
+ template.innerHTML = htmlWithPlaceholders;
67
65
  else // Create one text node, so shell isn't empty and NodeGroups created from it have something to point the startNode and endNode at.
68
66
  template.content.append(Globals.doc.createTextNode(''))
69
67
  this.fragment = template.content;
@@ -75,20 +73,28 @@ export default class Shell {
75
73
  const walker = Globals.doc.createTreeWalker(this.fragment, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT);
76
74
  while (node = walker.nextNode()) {
77
75
 
78
- // Remove previous after each iteration, so paths will still be calculated correctly.
76
+ // Remove previous elements after each iteration, so paths will still be calculated correctly.
79
77
  toRemove.map(el => el.remove());
80
78
  toRemove = [];
81
79
 
82
80
  // Replace attributes
83
81
  if (node.nodeType === 1) {
84
- for (let attr of [...node.attributes]) { // Copy the attributes array b/c we remove attributes as we go.
82
+ const hasIs = node.hasAttribute('is');
83
+ const isComponent = (hasIs || node.tagName.includes('-'));
84
+ const componentAttribPaths = [];
85
+
86
+ for (let attr of [...node.attributes]) { // Copy the attributes array b/c we remove attributes with placeholders as we go.
85
87
 
86
88
  // Whole attribute
87
89
  let matches = attr.name.match(/^[\ue000-\uf8ff]$/)
88
90
  if (matches) {
89
- this.paths.push(new ExprPath(null, node, ExprPathType.AttribMultiple));
91
+ let path = new PathToAttribs(null, node);
92
+ this.paths.push(path);
93
+ if (isComponent)
94
+ componentAttribPaths.push(path);
95
+
90
96
  placeholdersUsed ++;
91
- node.removeAttribute(matches[0]);
97
+ node.removeAttribute(matches[0]); // TODO: Is this necessary?
92
98
  }
93
99
 
94
100
  // Just the attribute value.
@@ -96,15 +102,41 @@ export default class Shell {
96
102
  let parts = attr.value.split(/[\ue000-\uf8ff]/g);
97
103
  if (parts.length > 1) {
98
104
  let nonEmptyParts = (parts.length === 2 && !parts[0].length && !parts[1].length) ? null : parts;
99
- let type = Util.isEvent(attr.name) ? ExprPathType.Event : ExprPathType.AttribValue;
100
105
 
101
- this.paths.push(new ExprPath(null, node, type, attr.name, nonEmptyParts));
106
+ let path = Util.isEvent(attr.name)
107
+ ? new PathToEvent(null, node, attr.name, nonEmptyParts)
108
+ : new PathToAttribValue(null, node, attr.name, nonEmptyParts);
109
+ path.isHtmlProperty = Util.isHtmlProp(node, attr.name);
110
+ this.paths.push(path);
111
+ if (isComponent) {
112
+ path.isComponentAttrib = true;
113
+ componentAttribPaths.push(path);
114
+ }
115
+
102
116
  placeholdersUsed += parts.length - 1;
103
- node.setAttribute(attr.name, parts.join(''));
117
+ try {
118
+ node.setAttribute(attr.name, parts.join(''));
119
+ }
120
+ catch (e) {
121
+ throw new Error(`Error setting attribute "${attr.name}" on node <${node.tagName}>: ${e.message}`);
122
+ }
104
123
  }
105
124
  }
106
125
  }
126
+
127
+ // Web components
128
+ if (isComponent) {
129
+ let path = new PathToComponent(null, node);
130
+ path.attribPaths = componentAttribPaths;
131
+ this.paths.splice(this.paths.length - componentAttribPaths.length, 0, path); // Insert before its componentAttribPaths
132
+
133
+ if (hasIs) {
134
+ node.setAttribute('_is', node.getAttribute('is'));
135
+ node.removeAttribute('is');
136
+ }
137
+ }
107
138
  }
139
+
108
140
  // Replace comment placeholders
109
141
  else if (node.nodeType === 8 && node.nodeValue === '!✨!') {
110
142
 
@@ -114,7 +146,7 @@ export default class Shell {
114
146
  // Get or create nodeBefore.
115
147
  let nodeBefore = node.previousSibling; // Can be the same as another Path's nodeMarker.
116
148
  if (!nodeBefore) {
117
- nodeBefore = Globals.doc.createComment('ExprPath:'+this.paths.length);
149
+ nodeBefore = Globals.doc.createComment('Path:'+this.paths.length);
118
150
  node.parentNode.insertBefore(nodeBefore, node)
119
151
  }
120
152
  /*#IFDEV*/assert(nodeBefore);/*#ENDIF*/
@@ -130,11 +162,11 @@ export default class Shell {
130
162
  // Re-use existing comment placeholder.
131
163
  else {
132
164
  nodeMarker = node;
133
- nodeMarker.textContent = 'ExprPathEnd:'+ this.paths.length;
165
+ nodeMarker.textContent = 'PathEnd:'+ this.paths.length;
134
166
  }
135
167
  /*#IFDEV*/assert(nodeMarker);/*#ENDIF*/
136
168
 
137
- let path = new ExprPath(nodeBefore, nodeMarker, ExprPathType.Content);
169
+ let path = new PathToNodes(nodeBefore, nodeMarker);
138
170
  this.paths.push(path);
139
171
  placeholdersUsed ++;
140
172
  }
@@ -148,18 +180,17 @@ export default class Shell {
148
180
  // Here we look for expressions in comments.
149
181
  // We don't actually update them dynamically, but we still add paths for them.
150
182
  // That way the expression count still matches.
151
- else if (node.nodeType === Node.COMMENT_NODE) {
183
+ else if (node.nodeType === 8) { // Node.COMMENT_NODE
152
184
  let parts = node.textContent.split(/[\ue000-\uf8ff]/g);
153
185
  for (let i=0; i<parts.length-1; i++) {
154
- let path = new ExprPath(node.previousSibling, node)
155
- path.type = ExprPathType.Comment;
186
+ let path = new Path(node.previousSibling, node)
156
187
  this.paths.push(path);
157
188
  placeholdersUsed ++;
158
189
  }
159
190
  }
160
191
 
161
192
  // Replace comment placeholders inside script and style tags, which have become text nodes.
162
- else if (node.nodeType === Node.TEXT_NODE && ['SCRIPT', 'STYLE'].includes(node.parentNode?.nodeName)) {
193
+ else if (node.nodeType === 3 && ['SCRIPT', 'STYLE'].includes(node.parentNode?.nodeName)) { // Node.TEXT_NODE
163
194
  let parts = node.textContent.split(commentPlaceholder);
164
195
  if (parts.length > 1) {
165
196
 
@@ -172,7 +203,7 @@ export default class Shell {
172
203
  }
173
204
 
174
205
  for (let i=0, node; node=placeholders[i]; i++) {
175
- let path = new ExprPath(node.previousSibling, node, ExprPathType.Content);
206
+ let path = new PathToNodes(node.previousSibling, node);
176
207
  this.paths.push(path);
177
208
  placeholdersUsed ++;
178
209
 
@@ -191,51 +222,31 @@ export default class Shell {
191
222
  if (placeholdersUsed !== html.length-1)
192
223
  throw new Error(`Could not parse expressions in template. Check for duplicate attributes or malformed html: ${html.join('${...}')}`);
193
224
 
194
- // Handle solarite-placeholder's.
195
-
196
- // 3. Rename "is" attributes so the Web Components don't instantiate until we have the values of their PathExpr arguments.
197
- // that happens in NodeGroup.applyComponentExprs()
198
- for (let el of this.fragment.querySelectorAll('[is]'))
199
- el.setAttribute('_is', el.getAttribute('is'));
200
-
201
225
  for (let path of this.paths) {
202
226
  if (path.nodeBefore)
203
227
  path.nodeBeforeIndex = Array.prototype.indexOf.call(path.nodeBefore.parentNode.childNodes, path.nodeBefore)
204
- path.nodeMarkerPath = getNodePath(path.nodeMarker)
205
228
 
206
- // Cache so we don't have to calculate this later inside NodeGroup.applyExprs()
207
- if ((path.type === ExprPathType.AttribValue || path.type === ExprPathType.Event) && path.nodeMarker.nodeType === 1 &&
208
- (path.nodeMarker.tagName.includes('-') || path.nodeMarker.hasAttribute('is'))) {
209
- path.isComponent = true;
210
- }
229
+ // Must be calculated after we remove the toRemove nodes:
230
+ path.nodeMarkerPath = Path.get(path.nodeMarker)
231
+
232
+
211
233
  }
212
234
 
213
235
  this.findEmbeds();
214
236
 
237
+
215
238
  /*#IFDEV*/this.verify();/*#ENDIF*/
216
239
  }
217
240
 
218
241
  /**
219
242
  * 1. Add a Unicode placeholder char for where expressions go within attributes.
220
243
  * 2. Add a comment placeholder for where expressions are children of other nodes.
221
- * 3. Append -solarite-placeholder to the tag names of custom components so that we can wait to instantiate them later.
244
+ * 3. Append -solarite-placeholder to the tag names of custom components so that we can instantiate them later
245
+ * when we can manually call their constructors with the proper attribute and children arguments from evaluated expressions.
222
246
  * @param htmlChunks {string[]}
223
- * @returns {string} */
247
+ * @returns {string} Html with the placeholders in place. */
224
248
  static addPlaceholders(htmlChunks) {
225
- let tokens = [];
226
-
227
- function addToken(token, context) {
228
-
229
- if (context === HtmlParser.Tag) {
230
- // Find Solarite Components tags and append -solarite-placeholder to their tag names
231
- // and give them a solarite-placeholder attribute so we can easily find them later.
232
- // This way we can gather their constructor arguments and their children before we call their constructor.
233
- // Later, NodeGroup.instantiateComponent() will replace them with the real components.
234
- // Ctrl+F "solarite-placeholder" in project to find all code that manages subcomponents.
235
- token = token.replace(/^<\/?[a-z][a-z0-9]*-[a-z0-9-]+/i, match => match + '-solarite-placeholder solarite-placeholder');
236
- }
237
- tokens.push(token)
238
- }
249
+ let result = [];
239
250
 
240
251
  let htmlParser = new HtmlParser(); // Reset the context.
241
252
  for (let i = 0; i < htmlChunks.length; i++) {
@@ -243,10 +254,20 @@ export default class Shell {
243
254
 
244
255
  // Append -solarite-placholder to web component tags, so we can pass args to them when they're instantiated.
245
256
  let lastIndex = 0;
246
- let context = htmlParser.parse(lastHtml, (html, index, oldContext, newContext) => {
257
+ let context = htmlParser.parse(lastHtml, (html, index, prevContext/*, nextContext*/) => { // This function is called every time the html context changes.
247
258
  if (lastIndex !== index) {
248
259
  let token = html.slice(lastIndex, index);
249
- addToken(token, oldContext);
260
+
261
+ if (prevContext === HtmlParser.Tag) {
262
+ // Find Web Component tags and append -solarite-placeholder to their tag names
263
+ // This way we can gather their constructor arguments and their children before we call their constructor.
264
+ // Later, PathToComponent.apply() will replace them with the real components.
265
+ // Ctrl+F "solarite-placeholder" in project to find all code that manages subcomponents.
266
+ const isWebComponentTagName = /^<\/?[a-z][a-z0-9]*-[a-z0-9-]+/i; // a dash in the middle
267
+ token = token.replace(isWebComponentTagName, match => match + '-SOLARITE-PLACEHOLDER'); // caps to match other instances of this string, for better compression.
268
+ }
269
+
270
+ result.push(token);
250
271
  }
251
272
  lastIndex = index;
252
273
  });
@@ -254,13 +275,13 @@ export default class Shell {
254
275
  // Insert placeholders
255
276
  if (i < htmlChunks.length - 1) {
256
277
  if (context === HtmlParser.Text)
257
- tokens.push(commentPlaceholder) // Comment Placeholder. because we can't put text in between <tr> tags for example.
278
+ result.push(commentPlaceholder) // Comment Placeholder. because we can't put text in between <tr> tags for example.
258
279
  else
259
- tokens.push(String.fromCharCode(attribPlaceholder + i));
280
+ result.push(String.fromCharCode(attribPlaceholder + i));
260
281
  }
261
282
  }
262
283
 
263
- return tokens.join('');
284
+ return result.join('');
264
285
  }
265
286
 
266
287
  /**
@@ -272,10 +293,10 @@ export default class Shell {
272
293
  * this.ids
273
294
  * this.staticComponents */
274
295
  findEmbeds() {
275
- this.scripts = Array.prototype.map.call(this.fragment.querySelectorAll('scripts'), el => getNodePath(el))
296
+ this.scripts = Array.prototype.map.call(this.fragment.querySelectorAll('scripts'), el => Path.get(el))
276
297
 
277
- // TODO: only find styles that have ExprPaths in them?
278
- this.styles = Array.prototype.map.call(this.fragment.querySelectorAll('style'), el => getNodePath(el))
298
+ // TODO: only find styles that have Paths in them?
299
+ this.styles = Array.prototype.map.call(this.fragment.querySelectorAll('style'), el => Path.get(el))
279
300
 
280
301
  let idEls = this.fragment.querySelectorAll('[id],[data-id]');
281
302
 
@@ -286,17 +307,7 @@ export default class Shell {
286
307
  throw new Error(`<${el.tagName.toLowerCase()} id="${id}"> can't override existing HTMLElement id property.`)
287
308
  }
288
309
 
289
- this.ids = Array.prototype.map.call(idEls, el => getNodePath(el))
290
-
291
- for (let el of this.fragment.querySelectorAll('*')) {
292
- if (el.tagName.includes('-') || el.hasAttribute('_is'))
293
-
294
- // Dynamic components are components that have attributes with expression values.
295
- // They are created from applyExprs()
296
- // But static components are created in a separate path inside the NodeGroup constructor.
297
- if (!this.paths.find(path => path.nodeMarker === el))
298
- this.staticComponents.push(getNodePath(el));
299
- }
310
+ this.ids = Array.prototype.map.call(idEls, el => Path.get(el))
300
311
  }
301
312
 
302
313
  /**
package/src/Solarite.d.ts CHANGED
@@ -4,63 +4,110 @@
4
4
  * https://vorticode.github.io/solarite/
5
5
  */
6
6
 
7
- export default function h(htmlStrings?: HTMLElement | string | string[] | TemplateStringsArray | Function | {render: Function}, ...exprs: any[]): Node | HTMLElement | Template | Function;
8
- export function toEl(htmlOrTemplate: string|Template|{render:()=>void}) : Node|HTMLElement|DocumentFragment;
7
+ export interface RenderOptions {
8
+ styles?: boolean;
9
+ scripts?: boolean;
10
+ ids?: boolean;
11
+ render?: boolean;
12
+ }
9
13
 
10
- // Deprecated:
11
- export function t(html: string): Template;
14
+ /**
15
+ * Tagged template literal or function for creating Templates and rendering to the DOM. */
16
+ declare function h(htmlStrings: TemplateStringsArray, ...exprs: any[]): Template;
17
+ declare function h(htmlStrings: string | string[], ...exprs: any[]): Template;
18
+ declare function h(el: HTMLElement | DocumentFragment, options?: RenderOptions): (htmlStrings: TemplateStringsArray, ...exprs: any[]) => HTMLElement | DocumentFragment;
19
+ declare function h(el: HTMLElement | DocumentFragment, template: Template, options?: RenderOptions): void;
20
+ declare function h(tag: string, props: object, ...children: any[]): Template; // JSX
21
+ declare function h(obj: {render: Function}): (htmlStrings: TemplateStringsArray, ...exprs: any[]) => void; // Rebound render
22
+ declare function h(): (htmlStrings: TemplateStringsArray, ...exprs: any[]) => Node|DocumentFragment;
12
23
 
24
+ export default h;
25
+ export {h};
26
+ export {h as r}; // deprecated
13
27
 
14
- export const ArgType: {
15
- Bool: string;
16
- Int: string;
17
- Float: string;
18
- String: string;
19
- Json: string;
20
- Eval: string;
28
+ /**
29
+ * Solarite provides more features if your web component extends Solarite instead of HTMLElement. */
30
+ export class Solarite extends HTMLElement {
31
+ constructor(attribs?: Record<string, any> | null);
32
+ render(attribs?: Record<string, any>, changed?:boolean): void;
33
+ renderFirstTime(): void;
34
+ connectedCallback(): void;
35
+ static define(tagName?: string | null): void;
36
+ static getAttribs(el: HTMLElement): Record<string, any>;
21
37
  }
22
38
 
23
- export function getArg(el:HTMLElement, attributeName:string, defaultValue?:any,
24
- type?:typeof ArgType[keyof typeof ArgType] | Function | any[], fallback?:any): any;
25
39
 
26
- export interface RenderOptions {
27
- styles?: boolean;
28
- scripts?: boolean;
29
- ids?: boolean;
30
- render?: boolean;
40
+ /**
41
+ * Convert a template, string, or object into a DOM Node or Element. */
42
+ export function toEl(arg: string | Template | {render: () => void}): Node | HTMLElement | DocumentFragment;
43
+
44
+ /**
45
+ * Assign fields from `src` to `dest` if they exist in `dest` and don't exist in `ignore`.
46
+ * When a value in `src` is a string and the existing value in `dest` is a boolean, number, or Date,
47
+ * it will be converted to that type. */
48
+ export function assignFields(dest: object, src: object|null, ignore?: string[]): void;
49
+
50
+ /**
51
+ * @deprecated
52
+ * Retrieve and cast an attribute value from an HTMLElement. */
53
+ export function getArg(el: HTMLElement, attributeName: string, defaultValue?: any,
54
+ type?: typeof ArgType[keyof typeof ArgType] | Function | any[]): any;
55
+
56
+ /**
57
+ * @deprecated
58
+ * Update attributes on an element from an object. */
59
+ export function setArgs(el: HTMLElement, args: object): void;
60
+
61
+ /** @deprecated */
62
+ export const ArgType: {
63
+ Bool: string;
64
+ Int: string;
65
+ Float: string;
66
+ String: string;
67
+ Json: string;
68
+ Eval: string;
31
69
  }
32
70
 
33
71
  export class Template {
34
- exprs: (Template|string|Function)[];
72
+ exprs: any[];
35
73
  html: string[];
36
74
  constructor(htmlStrings: string[], exprs: any[]);
37
- render(el?: HTMLElement, options?: RenderOptions): DocumentFragment | HTMLElement | null;
75
+ render(el?: HTMLElement | null, options?: RenderOptions): HTMLElement | DocumentFragment;
38
76
  getExactKey(): string;
39
77
  getCloseKey(): string;
78
+ static fromJsx(tag: string, props: Record<string, any> | null, children: any[]): Template;
40
79
  }
41
80
 
42
81
  export function delve(obj: object, path: string[], createVal?: any): any;
43
82
 
44
-
45
-
46
- // Experimental:
47
- //--------------
48
-
49
- // Globals object
83
+ /**
84
+ * Internal utilities and state. */
50
85
  export const Globals: {
51
- //componentArgsHash: WeakMap<any, any>;
52
86
  connected: WeakSet<HTMLElement>;
53
- currentExprPath: any;
87
+ currentPath: any;
88
+ currentSlotChildren: any[] | null;
54
89
  div: HTMLDivElement;
90
+ doc: Document;
55
91
  elementClasses: {[key: string]: typeof Node};
56
92
  htmlProps: {[key: string]: boolean};
57
93
  nodeEvents: WeakMap<Node, {[eventName: string]: [Function, Function, any[]]}>;
58
- nodeGroups: WeakMap<HTMLElement, any>;
94
+ rootNodeGroups: WeakMap<HTMLElement, any>;
59
95
  objToEl: WeakMap<any, any>;
60
96
  rendered: WeakSet<HTMLElement>;
61
- rendering: WeakSet<HTMLElement>;
62
97
  shells: WeakMap<string[], any>;
63
98
  reset: Function;
64
- count: number;
99
+ };
100
+
101
+ export const SolariteUtil: {
102
+ arraySame(a: any[], b: any[]): boolean;
103
+ attribsToObject(el: HTMLElement, ignore?: string | null): Record<string, any>;
104
+ bindId(root: any, el: HTMLElement): void;
105
+ bindStyles(style: HTMLStyleElement, root: HTMLElement): void;
106
+ camelToDashes(str: string): string;
107
+ dashesToCamel(str: string): string;
108
+ defineClass(Class: typeof HTMLElement, tagName?: string | null): void;
109
+ isIterable(obj: any): boolean;
110
+ trimEmptyNodes(nodes: NodeList | Node[]): Node[];
111
+ [key: string]: any;
65
112
  };
66
113