solarite 0.4.0 → 0.5.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,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,36 @@ 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
117
  node.setAttribute(attr.name, parts.join(''));
104
118
  }
105
119
  }
106
120
  }
121
+
122
+ // Web components
123
+ if (isComponent) {
124
+ let path = new PathToComponent(null, node);
125
+ path.attribPaths = componentAttribPaths;
126
+ this.paths.splice(this.paths.length - componentAttribPaths.length, 0, path); // Insert before its componentAttribPaths
127
+
128
+ if (hasIs) {
129
+ node.setAttribute('_is', node.getAttribute('is'));
130
+ node.removeAttribute('is');
131
+ }
132
+ }
107
133
  }
134
+
108
135
  // Replace comment placeholders
109
136
  else if (node.nodeType === 8 && node.nodeValue === '!✨!') {
110
137
 
@@ -114,7 +141,7 @@ export default class Shell {
114
141
  // Get or create nodeBefore.
115
142
  let nodeBefore = node.previousSibling; // Can be the same as another Path's nodeMarker.
116
143
  if (!nodeBefore) {
117
- nodeBefore = Globals.doc.createComment('ExprPath:'+this.paths.length);
144
+ nodeBefore = Globals.doc.createComment('Path:'+this.paths.length);
118
145
  node.parentNode.insertBefore(nodeBefore, node)
119
146
  }
120
147
  /*#IFDEV*/assert(nodeBefore);/*#ENDIF*/
@@ -130,11 +157,11 @@ export default class Shell {
130
157
  // Re-use existing comment placeholder.
131
158
  else {
132
159
  nodeMarker = node;
133
- nodeMarker.textContent = 'ExprPathEnd:'+ this.paths.length;
160
+ nodeMarker.textContent = 'PathEnd:'+ this.paths.length;
134
161
  }
135
162
  /*#IFDEV*/assert(nodeMarker);/*#ENDIF*/
136
163
 
137
- let path = new ExprPath(nodeBefore, nodeMarker, ExprPathType.Content);
164
+ let path = new PathToNodes(nodeBefore, nodeMarker);
138
165
  this.paths.push(path);
139
166
  placeholdersUsed ++;
140
167
  }
@@ -148,18 +175,17 @@ export default class Shell {
148
175
  // Here we look for expressions in comments.
149
176
  // We don't actually update them dynamically, but we still add paths for them.
150
177
  // That way the expression count still matches.
151
- else if (node.nodeType === Node.COMMENT_NODE) {
178
+ else if (node.nodeType === 8) { // Node.COMMENT_NODE
152
179
  let parts = node.textContent.split(/[\ue000-\uf8ff]/g);
153
180
  for (let i=0; i<parts.length-1; i++) {
154
- let path = new ExprPath(node.previousSibling, node)
155
- path.type = ExprPathType.Comment;
181
+ let path = new Path(node.previousSibling, node)
156
182
  this.paths.push(path);
157
183
  placeholdersUsed ++;
158
184
  }
159
185
  }
160
186
 
161
187
  // 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)) {
188
+ else if (node.nodeType === 3 && ['SCRIPT', 'STYLE'].includes(node.parentNode?.nodeName)) { // Node.TEXT_NODE
163
189
  let parts = node.textContent.split(commentPlaceholder);
164
190
  if (parts.length > 1) {
165
191
 
@@ -172,7 +198,7 @@ export default class Shell {
172
198
  }
173
199
 
174
200
  for (let i=0, node; node=placeholders[i]; i++) {
175
- let path = new ExprPath(node.previousSibling, node, ExprPathType.Content);
201
+ let path = new PathToNodes(node.previousSibling, node);
176
202
  this.paths.push(path);
177
203
  placeholdersUsed ++;
178
204
 
@@ -191,51 +217,31 @@ export default class Shell {
191
217
  if (placeholdersUsed !== html.length-1)
192
218
  throw new Error(`Could not parse expressions in template. Check for duplicate attributes or malformed html: ${html.join('${...}')}`);
193
219
 
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
220
  for (let path of this.paths) {
202
221
  if (path.nodeBefore)
203
222
  path.nodeBeforeIndex = Array.prototype.indexOf.call(path.nodeBefore.parentNode.childNodes, path.nodeBefore)
204
- path.nodeMarkerPath = getNodePath(path.nodeMarker)
205
223
 
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
- }
224
+ // Must be calculated after we remove the toRemove nodes:
225
+ path.nodeMarkerPath = Path.get(path.nodeMarker)
226
+
227
+
211
228
  }
212
229
 
213
230
  this.findEmbeds();
214
231
 
232
+
215
233
  /*#IFDEV*/this.verify();/*#ENDIF*/
216
234
  }
217
235
 
218
236
  /**
219
237
  * 1. Add a Unicode placeholder char for where expressions go within attributes.
220
238
  * 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.
239
+ * 3. Append -solarite-placeholder to the tag names of custom components so that we can instantiate them later
240
+ * when we can manually call their constructors with the proper attribute and children arguments from evaluated expressions.
222
241
  * @param htmlChunks {string[]}
223
- * @returns {string} */
242
+ * @returns {string} Html with the placeholders in place. */
224
243
  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
- }
244
+ let result = [];
239
245
 
240
246
  let htmlParser = new HtmlParser(); // Reset the context.
241
247
  for (let i = 0; i < htmlChunks.length; i++) {
@@ -243,10 +249,20 @@ export default class Shell {
243
249
 
244
250
  // Append -solarite-placholder to web component tags, so we can pass args to them when they're instantiated.
245
251
  let lastIndex = 0;
246
- let context = htmlParser.parse(lastHtml, (html, index, oldContext, newContext) => {
252
+ let context = htmlParser.parse(lastHtml, (html, index, prevContext/*, nextContext*/) => { // This function is called every time the html context changes.
247
253
  if (lastIndex !== index) {
248
254
  let token = html.slice(lastIndex, index);
249
- addToken(token, oldContext);
255
+
256
+ if (prevContext === HtmlParser.Tag) {
257
+ // Find Web Component tags and append -solarite-placeholder to their tag names
258
+ // This way we can gather their constructor arguments and their children before we call their constructor.
259
+ // Later, PathToComponent.apply() will replace them with the real components.
260
+ // Ctrl+F "solarite-placeholder" in project to find all code that manages subcomponents.
261
+ const isWebComponentTagName = /^<\/?[a-z][a-z0-9]*-[a-z0-9-]+/i; // a dash in the middle
262
+ token = token.replace(isWebComponentTagName, match => match + '-SOLARITE-PLACEHOLDER'); // caps to match other instances of this string, for better compression.
263
+ }
264
+
265
+ result.push(token);
250
266
  }
251
267
  lastIndex = index;
252
268
  });
@@ -254,13 +270,13 @@ export default class Shell {
254
270
  // Insert placeholders
255
271
  if (i < htmlChunks.length - 1) {
256
272
  if (context === HtmlParser.Text)
257
- tokens.push(commentPlaceholder) // Comment Placeholder. because we can't put text in between <tr> tags for example.
273
+ result.push(commentPlaceholder) // Comment Placeholder. because we can't put text in between <tr> tags for example.
258
274
  else
259
- tokens.push(String.fromCharCode(attribPlaceholder + i));
275
+ result.push(String.fromCharCode(attribPlaceholder + i));
260
276
  }
261
277
  }
262
278
 
263
- return tokens.join('');
279
+ return result.join('');
264
280
  }
265
281
 
266
282
  /**
@@ -272,10 +288,10 @@ export default class Shell {
272
288
  * this.ids
273
289
  * this.staticComponents */
274
290
  findEmbeds() {
275
- this.scripts = Array.prototype.map.call(this.fragment.querySelectorAll('scripts'), el => getNodePath(el))
291
+ this.scripts = Array.prototype.map.call(this.fragment.querySelectorAll('scripts'), el => Path.get(el))
276
292
 
277
- // TODO: only find styles that have ExprPaths in them?
278
- this.styles = Array.prototype.map.call(this.fragment.querySelectorAll('style'), el => getNodePath(el))
293
+ // TODO: only find styles that have Paths in them?
294
+ this.styles = Array.prototype.map.call(this.fragment.querySelectorAll('style'), el => Path.get(el))
279
295
 
280
296
  let idEls = this.fragment.querySelectorAll('[id],[data-id]');
281
297
 
@@ -286,17 +302,7 @@ export default class Shell {
286
302
  throw new Error(`<${el.tagName.toLowerCase()} id="${id}"> can't override existing HTMLElement id property.`)
287
303
  }
288
304
 
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
- }
305
+ this.ids = Array.prototype.map.call(idEls, el => Path.get(el))
300
306
  }
301
307
 
302
308
  /**
package/src/Solarite.d.ts CHANGED
@@ -4,63 +4,103 @@
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
12
22
 
23
+ export default h;
24
+ export {h};
25
+ export {h as r};
13
26
 
14
- export const ArgType: {
15
- Bool: string;
16
- Int: string;
17
- Float: string;
18
- String: string;
19
- Json: string;
20
- Eval: string;
27
+ /**
28
+ * Solarite provides more features if your web component extends Solarite instead of HTMLElement. */
29
+ export class Solarite extends HTMLElement {
30
+ constructor(attribs?: Record<string, any> | null);
31
+ render(attribs?: Record<string, any>, changed?:boolean): void;
32
+ renderFirstTime(): void;
33
+ connectedCallback(): void;
34
+ static define(tagName?: string | null): void;
35
+ static getAttribs(el: HTMLElement): Record<string, any>;
21
36
  }
22
37
 
23
- export function getArg(el:HTMLElement, attributeName:string, defaultValue?:any,
24
- type?:typeof ArgType[keyof typeof ArgType] | Function | any[], fallback?:any): any;
25
38
 
26
- export interface RenderOptions {
27
- styles?: boolean;
28
- scripts?: boolean;
29
- ids?: boolean;
30
- render?: boolean;
39
+ /**
40
+ * Convert a template, string, or object into a DOM Node or Element. */
41
+ export function toEl(arg: string | Template | {render: () => void}): Node | HTMLElement | DocumentFragment;
42
+
43
+ /**
44
+ * @deprecated
45
+ * Retrieve and cast an attribute value from an HTMLElement. */
46
+ export function getArg(el: HTMLElement, attributeName: string, defaultValue?: any,
47
+ type?: typeof ArgType[keyof typeof ArgType] | Function | any[]): any;
48
+
49
+ /**
50
+ * @deprecated
51
+ * Update attributes on an element from an object. */
52
+ export function setArgs(el: HTMLElement, args: object): void;
53
+
54
+ /** @deprecated */
55
+ export const ArgType: {
56
+ Bool: string;
57
+ Int: string;
58
+ Float: string;
59
+ String: string;
60
+ Json: string;
61
+ Eval: string;
31
62
  }
32
63
 
33
64
  export class Template {
34
- exprs: (Template|string|Function)[];
65
+ exprs: any[];
35
66
  html: string[];
36
67
  constructor(htmlStrings: string[], exprs: any[]);
37
- render(el?: HTMLElement, options?: RenderOptions): DocumentFragment | HTMLElement | null;
68
+ render(el?: HTMLElement | null, options?: RenderOptions): HTMLElement | DocumentFragment;
38
69
  getExactKey(): string;
39
70
  getCloseKey(): string;
71
+ static fromJsx(tag: string, props: Record<string, any> | null, children: any[]): Template;
40
72
  }
41
73
 
42
74
  export function delve(obj: object, path: string[], createVal?: any): any;
43
75
 
44
-
45
-
46
- // Experimental:
47
- //--------------
48
-
49
- // Globals object
76
+ /**
77
+ * Internal utilities and state. */
50
78
  export const Globals: {
51
- //componentArgsHash: WeakMap<any, any>;
52
79
  connected: WeakSet<HTMLElement>;
53
- currentExprPath: any;
80
+ currentPath: any;
81
+ currentSlotChildren: any[] | null;
54
82
  div: HTMLDivElement;
83
+ doc: Document;
55
84
  elementClasses: {[key: string]: typeof Node};
56
85
  htmlProps: {[key: string]: boolean};
57
86
  nodeEvents: WeakMap<Node, {[eventName: string]: [Function, Function, any[]]}>;
58
- nodeGroups: WeakMap<HTMLElement, any>;
87
+ rootNodeGroups: WeakMap<HTMLElement, any>;
59
88
  objToEl: WeakMap<any, any>;
60
89
  rendered: WeakSet<HTMLElement>;
61
- rendering: WeakSet<HTMLElement>;
62
90
  shells: WeakMap<string[], any>;
63
91
  reset: Function;
64
- count: number;
92
+ };
93
+
94
+ export const SolariteUtil: {
95
+ arraySame(a: any[], b: any[]): boolean;
96
+ attribsToObject(el: HTMLElement, ignore?: string | null): Record<string, any>;
97
+ bindId(root: any, el: HTMLElement): void;
98
+ bindStyles(style: HTMLStyleElement, root: HTMLElement): void;
99
+ camelToDashes(str: string): string;
100
+ dashesToCamel(str: string): string;
101
+ defineClass(Class: typeof HTMLElement, tagName?: string | null): void;
102
+ isIterable(obj: any): boolean;
103
+ trimEmptyNodes(nodes: NodeList | Node[]): Node[];
104
+ [key: string]: any;
65
105
  };
66
106