j-templates 8.0.21 → 8.0.24
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/DOM/domNodeConfig.js +3 -0
- package/Node/component.d.ts +7 -1
- package/Node/component.js +8 -0
- package/Node/nodeConfig.d.ts +1 -0
- package/Node/vNode.d.ts +1 -1
- package/Node/vNode.js +59 -12
- package/Node/vNode.types.d.ts +2 -0
- package/Store/Diff/diffTree.js +13 -6
- package/package.json +1 -1
package/DOM/domNodeConfig.js
CHANGED
package/Node/component.d.ts
CHANGED
|
@@ -14,6 +14,11 @@ export declare class Component<D = void, T = void, E = {}> {
|
|
|
14
14
|
private scope;
|
|
15
15
|
private templates;
|
|
16
16
|
private componentEvents;
|
|
17
|
+
/**
|
|
18
|
+
* Return if the component's vNode is connected
|
|
19
|
+
* to a parent document
|
|
20
|
+
*/
|
|
21
|
+
get IsConnected(): boolean;
|
|
17
22
|
/**
|
|
18
23
|
* Returns the component's virtual node injector.
|
|
19
24
|
*/
|
|
@@ -51,6 +56,7 @@ export declare class Component<D = void, T = void, E = {}> {
|
|
|
51
56
|
* Override to perform post‑binding initialization.
|
|
52
57
|
*/
|
|
53
58
|
Bound(): void;
|
|
59
|
+
Connected(): void;
|
|
54
60
|
/**
|
|
55
61
|
* Fires a component event.
|
|
56
62
|
*
|
|
@@ -85,6 +91,6 @@ export declare namespace Component {
|
|
|
85
91
|
* @param vnode - The virtual node to be attached.
|
|
86
92
|
* @returns The result of the attachment operation.
|
|
87
93
|
*/
|
|
88
|
-
function Attach(node: any, vnode:
|
|
94
|
+
function Attach(node: any, vnode: vElementNode): vElementNode;
|
|
89
95
|
}
|
|
90
96
|
export {};
|
package/Node/component.js
CHANGED
|
@@ -9,6 +9,13 @@ import { ObservableScope } from "../Store";
|
|
|
9
9
|
* @template E - Event map type for component events.
|
|
10
10
|
*/
|
|
11
11
|
export class Component {
|
|
12
|
+
/**
|
|
13
|
+
* Return if the component's vNode is connected
|
|
14
|
+
* to a parent document
|
|
15
|
+
*/
|
|
16
|
+
get IsConnected() {
|
|
17
|
+
return this.vNode.connected;
|
|
18
|
+
}
|
|
12
19
|
/**
|
|
13
20
|
* Returns the component's virtual node injector.
|
|
14
21
|
*/
|
|
@@ -76,6 +83,7 @@ export class Component {
|
|
|
76
83
|
Bound() {
|
|
77
84
|
Bound.All(this);
|
|
78
85
|
}
|
|
86
|
+
Connected() { }
|
|
79
87
|
/**
|
|
80
88
|
* Fires a component event.
|
|
81
89
|
*
|
package/Node/nodeConfig.d.ts
CHANGED
package/Node/vNode.d.ts
CHANGED
|
@@ -6,5 +6,5 @@ export declare namespace vNode {
|
|
|
6
6
|
function Destroy(vnode: vNodeType): void;
|
|
7
7
|
function DestroyAll(vnodes: vNodeType[]): void;
|
|
8
8
|
function ToFunction<P = HTMLElement, E = HTMLElementEventMap>(type: string | typeof TEXT_NODE | typeof FRAGMENT_NODE, namespace?: string): <T>(config: vNodeConfig<P, E, T>, children?: vNodeType[] | vNodeChildrenFunction<T>) => vElementNode;
|
|
9
|
-
function Attach(node: any, vnode:
|
|
9
|
+
function Attach(node: any, vnode: vElementNode): vElementNode;
|
|
10
10
|
}
|
package/Node/vNode.js
CHANGED
|
@@ -13,6 +13,7 @@ export var vNode;
|
|
|
13
13
|
return {
|
|
14
14
|
definition,
|
|
15
15
|
type: definition.type,
|
|
16
|
+
connected: false,
|
|
16
17
|
injector: definition.componentFactory
|
|
17
18
|
? new Injector()
|
|
18
19
|
: (Injector.Current() ?? new Injector()),
|
|
@@ -71,8 +72,11 @@ export var vNode;
|
|
|
71
72
|
if (vnode.type === FRAGMENT_NODE)
|
|
72
73
|
throw new Error("Cannot attach a fragment directly. A fragment has no DOM node — " +
|
|
73
74
|
"wrap it in a real element (e.g. div) before attaching.");
|
|
75
|
+
// Must create a node + attach before Init so Connected can run correctly
|
|
76
|
+
// on a possible Component instance
|
|
77
|
+
const childNode = vnode.definition.node = CreateNode(vnode);
|
|
78
|
+
NodeConfig.addChild(node, childNode);
|
|
74
79
|
Init(vnode);
|
|
75
|
-
NodeConfig.addChild(node, vnode.node);
|
|
76
80
|
return vnode;
|
|
77
81
|
}
|
|
78
82
|
vNode.Attach = Attach;
|
|
@@ -80,19 +84,22 @@ export var vNode;
|
|
|
80
84
|
function ComponentChildren() {
|
|
81
85
|
return this.Template();
|
|
82
86
|
}
|
|
83
|
-
function
|
|
84
|
-
const { type, namespace, props, attrs, on, data, componentFactory, children, childrenArray, } = vnode.definition;
|
|
85
|
-
let node = null;
|
|
87
|
+
function CreateNode(vnode) {
|
|
86
88
|
switch (vnode.type) {
|
|
87
89
|
case FRAGMENT_NODE:
|
|
88
|
-
|
|
90
|
+
return null;
|
|
89
91
|
case TEXT_NODE:
|
|
90
|
-
|
|
91
|
-
break;
|
|
92
|
+
return vnode.definition.node ?? NodeConfig.createTextNode();
|
|
92
93
|
default:
|
|
93
|
-
|
|
94
|
-
break;
|
|
94
|
+
return vnode.definition.node ?? NodeConfig.createNode(vnode.definition.type, vnode.definition.namespace);
|
|
95
95
|
}
|
|
96
|
+
}
|
|
97
|
+
function InitNode(vnode, parentNode) {
|
|
98
|
+
const {
|
|
99
|
+
/* type,
|
|
100
|
+
namespace, */
|
|
101
|
+
props, attrs, on, data, componentFactory, children, childrenArray, } = vnode.definition;
|
|
102
|
+
const node = vnode.node = CreateNode(vnode);
|
|
96
103
|
vnode.definition = null;
|
|
97
104
|
vnode.parentNode = parentNode;
|
|
98
105
|
if (props) {
|
|
@@ -130,7 +137,7 @@ function InitNode(vnode, parentNode) {
|
|
|
130
137
|
}
|
|
131
138
|
if (componentFactory) {
|
|
132
139
|
vnode.component = Injector.Scope(vnode.injector, componentFactory, vnode);
|
|
133
|
-
vnode.component.Bound();
|
|
140
|
+
// vnode.component.Bound();
|
|
134
141
|
Children(vnode, ComponentChildren.bind(vnode.component));
|
|
135
142
|
}
|
|
136
143
|
else if (childrenArray) {
|
|
@@ -173,7 +180,7 @@ function CreateChildrenScope(vnode, children, data) {
|
|
|
173
180
|
return ObservableScope.Create(DynamicChildrenFunction.bind(null, vnode.injector, children, data));
|
|
174
181
|
}
|
|
175
182
|
function ToVNodeType(children) {
|
|
176
|
-
return typeof children === "string" ? { type: STRING_NODE, node: children } : children;
|
|
183
|
+
return typeof children === "string" ? { type: STRING_NODE, connected: false, node: children } : children;
|
|
177
184
|
}
|
|
178
185
|
function GetNextNodes(injector, children, data) {
|
|
179
186
|
return ToVNodeType(Injector.Scope(injector, children, data));
|
|
@@ -236,14 +243,18 @@ function UpdateChildren(vnode, init = false, skipInit = false) {
|
|
|
236
243
|
if (vnode.destroyed || children !== vnode.children)
|
|
237
244
|
return;
|
|
238
245
|
if (init) {
|
|
239
|
-
if (vnode.node !== null)
|
|
246
|
+
if (vnode.node !== null) {
|
|
240
247
|
NodeConfig.reconcileChildren(vnode.node, CleanupChildrenArray(vnode.children));
|
|
248
|
+
vnode.component?.Bound();
|
|
249
|
+
FireConnected(vnode, true);
|
|
250
|
+
}
|
|
241
251
|
}
|
|
242
252
|
else if (!async) {
|
|
243
253
|
let reconcileNode = vnode;
|
|
244
254
|
while (reconcileNode.node === null)
|
|
245
255
|
reconcileNode = reconcileNode.parentNode;
|
|
246
256
|
NodeConfig.reconcileChildren(reconcileNode.node, CleanupChildrenArray(reconcileNode.children));
|
|
257
|
+
FireConnected(vnode, true);
|
|
247
258
|
}
|
|
248
259
|
else {
|
|
249
260
|
NodeConfig.scheduleUpdate(function () {
|
|
@@ -253,11 +264,47 @@ function UpdateChildren(vnode, init = false, skipInit = false) {
|
|
|
253
264
|
while (reconcileNode.node === null)
|
|
254
265
|
reconcileNode = reconcileNode.parentNode;
|
|
255
266
|
NodeConfig.reconcileChildren(reconcileNode.node, CleanupChildrenArray(reconcileNode.children));
|
|
267
|
+
FireConnected(vnode, true);
|
|
256
268
|
});
|
|
257
269
|
}
|
|
258
270
|
});
|
|
259
271
|
});
|
|
260
272
|
}
|
|
273
|
+
function FireConnected(vnode, root = false) {
|
|
274
|
+
if ((!root && vnode.connected) ||
|
|
275
|
+
(vnode.type !== FRAGMENT_NODE && vnode.node === null))
|
|
276
|
+
return;
|
|
277
|
+
switch (vnode.type) {
|
|
278
|
+
case FRAGMENT_NODE:
|
|
279
|
+
break;
|
|
280
|
+
default: {
|
|
281
|
+
if (!vnode.connected) {
|
|
282
|
+
vnode.connected = NodeConfig.isConnected(vnode.node);
|
|
283
|
+
switch (vnode.type) {
|
|
284
|
+
case STRING_NODE:
|
|
285
|
+
case TEXT_NODE:
|
|
286
|
+
break;
|
|
287
|
+
default:
|
|
288
|
+
vnode.connected && vnode.component?.Connected();
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
break;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
if (vnode.type !== FRAGMENT_NODE && !vnode.connected)
|
|
295
|
+
return;
|
|
296
|
+
switch (vnode.type) {
|
|
297
|
+
case STRING_NODE:
|
|
298
|
+
case TEXT_NODE:
|
|
299
|
+
break;
|
|
300
|
+
default: {
|
|
301
|
+
const children = vnode.children;
|
|
302
|
+
const count = children?.length;
|
|
303
|
+
for (let x = 0; x < count; x++)
|
|
304
|
+
FireConnected(children[x]);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
261
308
|
function ScheduledAssignment(assign) {
|
|
262
309
|
let scheduled = false;
|
|
263
310
|
return function (scope) {
|
package/Node/vNode.types.d.ts
CHANGED
|
@@ -27,10 +27,12 @@ export type vNodeConfig<P = HTMLElement, E = HTMLElementEventMap, T = never> = {
|
|
|
27
27
|
};
|
|
28
28
|
export type vStringNode = {
|
|
29
29
|
type: typeof STRING_NODE;
|
|
30
|
+
connected: boolean;
|
|
30
31
|
node: string;
|
|
31
32
|
};
|
|
32
33
|
export type vElementNode = {
|
|
33
34
|
type: string | typeof TEXT_NODE | typeof FRAGMENT_NODE;
|
|
35
|
+
connected: boolean;
|
|
34
36
|
definition: vNodeDefinition<any, any, any>;
|
|
35
37
|
injector: Injector;
|
|
36
38
|
parentNode: vElementNode | null;
|
package/Store/Diff/diffTree.js
CHANGED
|
@@ -34,16 +34,16 @@ function FlattenValue(root, value, keyFunc) {
|
|
|
34
34
|
* @param path - Dot-separated path to the value (empty string returns source)
|
|
35
35
|
* @returns The value at the specified path
|
|
36
36
|
*/
|
|
37
|
-
function GetPathValue(source, path) {
|
|
37
|
+
function GetPathValue(source, path, keyFunc) {
|
|
38
38
|
if (path === "")
|
|
39
39
|
return [source, true];
|
|
40
40
|
const parts = path.split(".");
|
|
41
41
|
let curr = source;
|
|
42
42
|
let x = 0;
|
|
43
43
|
for (; curr && x < parts.length - 1; x++)
|
|
44
|
-
curr = curr[parts[x]];
|
|
44
|
+
curr = ResolveKeyed(source, curr[parts[x]], keyFunc);
|
|
45
45
|
if (curr && Object.hasOwn(curr, parts[x])) {
|
|
46
|
-
curr = curr[parts[x]];
|
|
46
|
+
curr = ResolveKeyed(source, curr[parts[x]], keyFunc);
|
|
47
47
|
x++;
|
|
48
48
|
}
|
|
49
49
|
else
|
|
@@ -224,7 +224,7 @@ function RunProjections(source, keyFunc, projections, memo) {
|
|
|
224
224
|
let y = 0;
|
|
225
225
|
for (; allPathsExist && y < reads.length && !reads[y].endsWith("*"); y++) {
|
|
226
226
|
const read = reads[y];
|
|
227
|
-
const [value, found] = GetPathValue(source, read);
|
|
227
|
+
const [value, found] = GetPathValue(source, read, keyFunc);
|
|
228
228
|
allPathsExist = found;
|
|
229
229
|
keys.push(read);
|
|
230
230
|
values.push((found && CreateSnapshot(source, keyFunc, value)) || value);
|
|
@@ -233,6 +233,7 @@ function RunProjections(source, keyFunc, projections, memo) {
|
|
|
233
233
|
const prefix = reads[y].slice(0, reads[y].length - 1);
|
|
234
234
|
const matchedKeys = Object.keys(source).filter(key => key.startsWith(prefix));
|
|
235
235
|
for (let z = 0; z < matchedKeys.length; z++) {
|
|
236
|
+
// No keyFunc passed because dot paths are not supported for wildcard paths
|
|
236
237
|
const [value, found] = GetPathValue(source, matchedKeys[z]);
|
|
237
238
|
allPathsExist = allPathsExist && found;
|
|
238
239
|
keys.push(matchedKeys[z]);
|
|
@@ -253,6 +254,12 @@ function RunProjections(source, keyFunc, projections, memo) {
|
|
|
253
254
|
}
|
|
254
255
|
return diffResult;
|
|
255
256
|
}
|
|
257
|
+
function ResolveKeyed(source, value, keyFunc) {
|
|
258
|
+
if (keyFunc === undefined || JsonType(value) === 'value')
|
|
259
|
+
return value;
|
|
260
|
+
const key = keyFunc(value);
|
|
261
|
+
return (key && source[key]) ?? value;
|
|
262
|
+
}
|
|
256
263
|
/**
|
|
257
264
|
* Resolves a path to its keyed root object if applicable.
|
|
258
265
|
* Searches up the path to find an object with a key from keyFunc.
|
|
@@ -402,7 +409,7 @@ export class DiffTree {
|
|
|
402
409
|
* @returns The value at the specified path
|
|
403
410
|
*/
|
|
404
411
|
GetPath(path) {
|
|
405
|
-
const [value] = GetPathValue(this.rootState, path);
|
|
412
|
+
const [value] = GetPathValue(this.rootState, path, this.keyFunc);
|
|
406
413
|
return value;
|
|
407
414
|
}
|
|
408
415
|
/**
|
|
@@ -411,7 +418,7 @@ export class DiffTree {
|
|
|
411
418
|
* @param path - Dot-separated path to the value
|
|
412
419
|
*/
|
|
413
420
|
Snapshot(path) {
|
|
414
|
-
const [value, found] = GetPathValue(this.rootState, path);
|
|
421
|
+
const [value, found] = GetPathValue(this.rootState, path, this.keyFunc);
|
|
415
422
|
if (!found)
|
|
416
423
|
throw `Unable to get snapshot for path ${path}`;
|
|
417
424
|
return CreateSnapshot(this.rootState, this.keyFunc, value);
|