solarite 0.7.0 → 0.8.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.
- package/dist/Solarite-debug.js +5193 -4114
- package/dist/Solarite.js +4951 -3878
- package/dist/Solarite.min.js +2 -2
- package/dist/jsx-dev-runtime.js +3 -0
- package/dist/jsx-runtime.js +86 -0
- package/package.json +11 -10
- package/readme.md +8 -8
- package/src/MappedList.js +34 -0
- package/src/MultiValueMap.js +4 -4
- package/src/NodeGroup.js +205 -119
- package/src/Path.js +52 -53
- package/src/PathToAttribValue.js +182 -101
- package/src/PathToAttribs.js +10 -18
- package/src/PathToComponent.js +43 -19
- package/src/PathToEvent.js +18 -12
- package/src/PathToKey.js +0 -6
- package/src/PathToNodes.js +695 -155
- package/src/RootNodeGroup.js +23 -30
- package/src/Selector.js +207 -0
- package/src/Shell.js +276 -117
- package/src/Solarite.d.ts +89 -13
- package/src/Solarite.js +38 -25
- package/src/Template.js +20 -7
- package/src/Util.js +75 -60
- package/src/assert.js +1 -1
- package/src/assignAttributes.js +20 -19
- package/src/h.js +64 -37
- package/src/jsx.js +4 -7
- package/src/toEl.js +1 -1
- package/src/HtmlParser.js +0 -91
- package/src/PathToComment.js +0 -8
package/src/PathToAttribs.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import Path from "./Path.js";
|
|
2
2
|
import Util from "./Util.js";
|
|
3
|
-
import assert from "./assert.js";
|
|
4
3
|
import PathToAttribValue from "./PathToAttribValue.js";
|
|
5
4
|
import PathToEvent from "./PathToEvent.js";
|
|
6
5
|
import {JsxAttr, styleToCss} from "./jsx.js";
|
|
@@ -11,24 +10,21 @@ export default class PathToAttribs extends Path {
|
|
|
11
10
|
* @type {Set<string>} Used for type=AttribType.Multiple to remember the attributes that were added. */
|
|
12
11
|
attrNames;
|
|
13
12
|
|
|
14
|
-
/** @type {
|
|
15
|
-
|
|
13
|
+
/** @type {PathToEvent|PathToAttribValue|undefined} Cached sub-path for the JSX
|
|
14
|
+
* whole-attribute fast path; see applyJsxAttr(). Declared so the first assignment
|
|
15
|
+
* doesn't transition the hidden class. */
|
|
16
|
+
jsxSub;
|
|
17
|
+
|
|
18
|
+
/** @type {?string} The attribute name jsxSub was built for. */
|
|
19
|
+
jsxSubName;
|
|
16
20
|
|
|
17
21
|
constructor(nodeBefore, nodeMarker) {
|
|
18
|
-
|
|
19
|
-
|
|
22
|
+
// nodeBefore is discarded: an attribute path has no nodes of its own. The marker goes
|
|
23
|
+
// straight through the base constructor rather than being stored a second time after it.
|
|
24
|
+
super(null, nodeMarker);
|
|
20
25
|
this.attrNames = new Set();
|
|
21
26
|
}
|
|
22
27
|
|
|
23
|
-
/**
|
|
24
|
-
* @param exprs {Expr[][]} Only the first is used. */
|
|
25
|
-
apply(exprs) {
|
|
26
|
-
//#IFDEV
|
|
27
|
-
assert(Array.isArray(exprs));
|
|
28
|
-
//#ENDIF
|
|
29
|
-
this.applySingle(exprs[0]);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
28
|
/**
|
|
33
29
|
* @param expr {Expr} */
|
|
34
30
|
applySingle(expr) {
|
|
@@ -107,8 +103,4 @@ export default class PathToAttribs extends Path {
|
|
|
107
103
|
value = styleToCss(value);
|
|
108
104
|
sub.applySingle(value);
|
|
109
105
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
getExpressionCount() { return 1 }
|
|
113
|
-
getValue(exprs) { return exprs[0]; }
|
|
114
106
|
}
|
package/src/PathToComponent.js
CHANGED
|
@@ -4,6 +4,7 @@ import delve, {isDelvePath} from "./delve.js";
|
|
|
4
4
|
import assert from "./assert.js";
|
|
5
5
|
import PathToAttribValue from "./PathToAttribValue.js";
|
|
6
6
|
import PathToKey from "./PathToKey.js";
|
|
7
|
+
import PathToEvent from "./PathToEvent.js";
|
|
7
8
|
import Globals from "./Globals.js";
|
|
8
9
|
import {getObjectHash} from "./Template.js";
|
|
9
10
|
|
|
@@ -23,15 +24,15 @@ export default class PathToComponent extends Path {
|
|
|
23
24
|
* Call render() on the component pointed to by this Path.
|
|
24
25
|
* And instantiate it (from a -solarite-placeholder element) if it hasn't been done yet.
|
|
25
26
|
* @param exprs {Expr[][]} Expressions to evaluate for each attribute to pass to the constructor.
|
|
26
|
-
* This is different than other Path.
|
|
27
|
+
* This is different than other Path.applyAll() functions which only receive Expr[] and not Expr[][].
|
|
27
28
|
* Because here we're receiving an array of arrays of expressions, one for each dynamic attribute. */
|
|
28
|
-
|
|
29
|
-
//#
|
|
29
|
+
applyAll(exprs) {
|
|
30
|
+
//#IFDEBUG
|
|
30
31
|
assert(Array.isArray(exprs));
|
|
31
32
|
assert(!exprs.length || Array.isArray(exprs[0]));
|
|
32
33
|
//#ENDIF
|
|
33
34
|
|
|
34
|
-
//#
|
|
35
|
+
//#IFDEBUG
|
|
35
36
|
assert(exprs.length === this.attribPaths.length);
|
|
36
37
|
//#ENDIF
|
|
37
38
|
|
|
@@ -47,8 +48,15 @@ export default class PathToComponent extends Path {
|
|
|
47
48
|
for (let i=0, attribPath; attribPath = this.attribPaths[i]; i++) {
|
|
48
49
|
if (attribPath instanceof PathToKey) // The list key is never a component arg.
|
|
49
50
|
continue;
|
|
51
|
+
// Event attributes like onchange=${...} are bound with addEventListener when the
|
|
52
|
+
// PathToEvent itself is applied. They must not also become constructor fields:
|
|
53
|
+
// a component that assigns its fields onto itself would set the native on*
|
|
54
|
+
// property, making the handler fire a second time with only the (event) argument
|
|
55
|
+
// instead of Solarite's documented (event, element) signature.
|
|
56
|
+
if (attribPath instanceof PathToEvent)
|
|
57
|
+
continue;
|
|
50
58
|
if (attribPath instanceof PathToAttribValue) {
|
|
51
|
-
let name = Util.dashesToCamel(attribPath.
|
|
59
|
+
let name = Util.dashesToCamel(attribPath.attribName);
|
|
52
60
|
|
|
53
61
|
// Resolve two way bindimg path before we pass it to the component.
|
|
54
62
|
let value = attribPath.getValue(exprs[i]);
|
|
@@ -78,8 +86,27 @@ export default class PathToComponent extends Path {
|
|
|
78
86
|
// 2a. Instantiate component
|
|
79
87
|
let tagName = (isAttrib || el.tagName.slice(0, -21)).toLowerCase(); // Remove -SOLARITE-PLACEHOLDER
|
|
80
88
|
let Constructor = customElements.get(tagName);
|
|
81
|
-
|
|
82
|
-
|
|
89
|
+
|
|
90
|
+
// Not defined yet (e.g. the module is being lazily imported): keep the placeholder
|
|
91
|
+
// and instantiate when the definition lands, like a native custom-element upgrade.
|
|
92
|
+
// deferredExprs always holds the LATEST exprs so re-renders while undefined win.
|
|
93
|
+
if (!Constructor) {
|
|
94
|
+
this.deferredExprs = exprs;
|
|
95
|
+
if (!this.whenDefinedPending) {
|
|
96
|
+
this.whenDefinedPending = true;
|
|
97
|
+
console.warn(`Solarite: <${tagName}> is not defined yet; waiting for customElements.define().`);
|
|
98
|
+
customElements.whenDefined(tagName).then(() => {
|
|
99
|
+
this.whenDefinedPending = false;
|
|
100
|
+
let deferred = this.deferredExprs;
|
|
101
|
+
this.deferredExprs = null;
|
|
102
|
+
// Skip if a newer render already instantiated or replaced the placeholder.
|
|
103
|
+
if (deferred && this.nodeMarker === el && el.tagName.endsWith('-SOLARITE-PLACEHOLDER'))
|
|
104
|
+
this.applyAll(deferred);
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
Globals.currentSlotChildren = null;
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
83
110
|
|
|
84
111
|
Globals.currentSlotChildren = [...el.childNodes]; // TODO: Does this need to be a stack?
|
|
85
112
|
let newEl = new Constructor(attribs);
|
|
@@ -97,8 +124,10 @@ export default class PathToComponent extends Path {
|
|
|
97
124
|
for (let name in attribs) {
|
|
98
125
|
let val = attribs[name];
|
|
99
126
|
let valType = typeof val;
|
|
127
|
+
// Only true and false can reach here, so the undefined/null halves of the
|
|
128
|
+
// falsy test this used to spell out could never have decided anything.
|
|
100
129
|
if (valType === 'boolean') {
|
|
101
|
-
if (val
|
|
130
|
+
if (val)
|
|
102
131
|
newEl.setAttribute(name, '');
|
|
103
132
|
}
|
|
104
133
|
|
|
@@ -111,7 +140,7 @@ export default class PathToComponent extends Path {
|
|
|
111
140
|
// 2c. If an id pointed at the placeholder, update it to point to the new element.
|
|
112
141
|
let id = newEl.getAttribute('data-id') || newEl.getAttribute('id');
|
|
113
142
|
if (id)
|
|
114
|
-
delve(this.parentNg.
|
|
143
|
+
delve(this.parentNg.getRootEl(), id.split(/\./g), newEl);
|
|
115
144
|
|
|
116
145
|
// 2d. Update paths to use replaced element.
|
|
117
146
|
let ng = this.parentNg;
|
|
@@ -137,7 +166,7 @@ export default class PathToComponent extends Path {
|
|
|
137
166
|
for (let i=0, attribPath; attribPath = this.attribPaths[i]; i++) {
|
|
138
167
|
attribPath.parentNg = this.parentNg;
|
|
139
168
|
attribPath.nodeMarker = newEl;
|
|
140
|
-
attribPath.
|
|
169
|
+
attribPath.applyAll(exprs[i]);
|
|
141
170
|
}
|
|
142
171
|
|
|
143
172
|
// 2e. Swap it to the DOM.
|
|
@@ -156,21 +185,16 @@ export default class PathToComponent extends Path {
|
|
|
156
185
|
* @param pathOffset {int}
|
|
157
186
|
* @return {Path} */
|
|
158
187
|
clone(newRoot, pathOffset=0) {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
let result =
|
|
188
|
+
// A component path's nodeBefore is always null (the constructor discards it), so the
|
|
189
|
+
// base clone() resolves only the nodeMarker and hands back a new PathToComponent.
|
|
190
|
+
let result = super.clone(newRoot, pathOffset);
|
|
162
191
|
result.attribPaths = this.attribPaths.map(path => path.clone(newRoot, pathOffset));
|
|
163
|
-
|
|
164
|
-
//#IFDEV
|
|
165
|
-
result.verify();
|
|
166
|
-
//#ENDIF
|
|
167
|
-
|
|
168
192
|
return result;
|
|
169
193
|
}
|
|
170
194
|
|
|
171
195
|
getExpressionCount() { return 0 }
|
|
172
196
|
|
|
173
|
-
//#
|
|
197
|
+
//#IFDEBUG
|
|
174
198
|
verify() {
|
|
175
199
|
super.verify();
|
|
176
200
|
assert(this.nodeMarker.nodeType === Node.ELEMENT_NODE);
|
package/src/PathToEvent.js
CHANGED
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
import assert from "./assert.js";
|
|
2
|
-
import PathToAttribValue from "./PathToAttribValue.js";
|
|
2
|
+
import PathToAttribValue, {delegatedKeyFor} from "./PathToAttribValue.js";
|
|
3
3
|
|
|
4
4
|
// TODO: Merge this into PathToAttribValue?
|
|
5
5
|
export default class PathToEvent extends PathToAttribValue {
|
|
6
6
|
|
|
7
|
-
/** @type {string} The
|
|
7
|
+
/** @type {string} The attribName without the "on" prefix. */
|
|
8
8
|
eventName;
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
/** @type {symbol|undefined} Expando key nodes store this event's delegated handler under.
|
|
11
|
+
* Undefined for non-delegatable (non-bubbling) events; bindEvent() then binds directly. */
|
|
12
|
+
delegatedKey;
|
|
13
|
+
|
|
14
|
+
constructor(nodeBefore, nodeMarker, attribName=null, attrValue=null) {
|
|
15
|
+
super(null, nodeMarker, attribName, attrValue);
|
|
16
|
+
this.skipIfSame = true;
|
|
17
|
+
this.eventName = attribName ? attribName.slice(2) : null;
|
|
18
|
+
this.delegatedKey = this.eventName !== null ? delegatedKeyFor(this.eventName) : undefined;
|
|
13
19
|
}
|
|
14
20
|
|
|
15
21
|
/**
|
|
@@ -19,8 +25,8 @@ export default class PathToEvent extends PathToAttribValue {
|
|
|
19
25
|
* onclick=${[this, 'doSomething', 'meow']}
|
|
20
26
|
*
|
|
21
27
|
* @param exprs {Expr[]} Only the first is used.*/
|
|
22
|
-
|
|
23
|
-
//#
|
|
28
|
+
applyAll(exprs) {
|
|
29
|
+
//#IFDEBUG
|
|
24
30
|
assert(Array.isArray(exprs));
|
|
25
31
|
//#ENDIF
|
|
26
32
|
|
|
@@ -28,7 +34,7 @@ export default class PathToEvent extends PathToAttribValue {
|
|
|
28
34
|
// We have expressions within a string attribute value that's not a Solarite event. E.g.
|
|
29
35
|
// <div onclick="alert(${1});"
|
|
30
36
|
if (this.attrValue?.length > 1) {
|
|
31
|
-
super.
|
|
37
|
+
super.applyAll(exprs);
|
|
32
38
|
return;
|
|
33
39
|
}
|
|
34
40
|
|
|
@@ -40,16 +46,16 @@ export default class PathToEvent extends PathToAttribValue {
|
|
|
40
46
|
applySingle(expr) {
|
|
41
47
|
// Expressions within a string attribute value that's not a Solarite event.
|
|
42
48
|
if (this.attrValue?.length > 1)
|
|
43
|
-
return super.
|
|
49
|
+
return super.applyAll([expr]);
|
|
44
50
|
|
|
45
51
|
// Don't bind events to component placeholders.
|
|
46
52
|
// PathToComponent will do the binding later when it instantiates the component.
|
|
47
53
|
if (this.isComponentAttrib && this.nodeMarker.tagName.endsWith('-SOLARITE-PLACEHOLDER'))
|
|
48
54
|
return;
|
|
49
55
|
|
|
50
|
-
let root = this.parentNg.rootNg.
|
|
56
|
+
let root = this.parentNg.rootNg.rootEl
|
|
51
57
|
|
|
52
|
-
/*#
|
|
58
|
+
/*#IFDEBUG*/
|
|
53
59
|
assert(root?.nodeType === 1);
|
|
54
60
|
/*#ENDIF*/
|
|
55
61
|
|
|
@@ -67,7 +73,7 @@ export default class PathToEvent extends PathToAttribValue {
|
|
|
67
73
|
expr = null;
|
|
68
74
|
}
|
|
69
75
|
else
|
|
70
|
-
throw new Error(`
|
|
76
|
+
throw new Error(`Solarite: ${this.attribName}=\${...} is not a function.`);
|
|
71
77
|
|
|
72
78
|
this.bindEvent(node, root, eventName, eventName, func, expr);
|
|
73
79
|
}
|
package/src/PathToKey.js
CHANGED
|
@@ -7,12 +7,6 @@ import Path from "./Path.js";
|
|
|
7
7
|
* matches NodeGroups to new templates by this key. */
|
|
8
8
|
export default class PathToKey extends Path {
|
|
9
9
|
|
|
10
|
-
/**
|
|
11
|
-
* @param exprs {Expr[]} Only the first is used. */
|
|
12
|
-
apply(exprs) {
|
|
13
|
-
this.parentNg.key = exprs[0];
|
|
14
|
-
}
|
|
15
|
-
|
|
16
10
|
applySingle(expr) {
|
|
17
11
|
this.parentNg.key = expr;
|
|
18
12
|
}
|