@webtides/element-js 1.1.1 → 1.1.3
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/CHANGELOG.md
CHANGED
|
@@ -16,6 +16,21 @@ to [Semantic Versioning](http://semver.org/).
|
|
|
16
16
|
<!-- ### Removed -->
|
|
17
17
|
<!-- ### Fixed -->
|
|
18
18
|
|
|
19
|
+
|
|
20
|
+
## [1.1.3] - 2024-11-11
|
|
21
|
+
|
|
22
|
+
### FIXED
|
|
23
|
+
|
|
24
|
+
- Fixed unsafeHtml having more than one child ([#135](https://github.com/webtides/element-js/issues/135))
|
|
25
|
+
- Fixed early returns from templates and fixes static html changes not reflecting to DOM ([#132](https://github.com/webtides/element-js/issues/132))
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
## [1.1.2] - 2024-10-30
|
|
29
|
+
|
|
30
|
+
### FIXED
|
|
31
|
+
|
|
32
|
+
- Fixed rerendering of templates that were initially null, prevent runtime errors
|
|
33
|
+
|
|
19
34
|
## [1.1.1] - 2024-09-16
|
|
20
35
|
|
|
21
36
|
### FIXED
|
package/package.json
CHANGED
|
@@ -8,6 +8,9 @@ import { TemplatePart } from './TemplatePart.js';
|
|
|
8
8
|
* @return {ChildNode}
|
|
9
9
|
*/
|
|
10
10
|
const removeChildNodes = (node) => {
|
|
11
|
+
if (!Array.isArray(node) && !(node instanceof TemplatePart) && !node.hasChildNodes()) {
|
|
12
|
+
return node;
|
|
13
|
+
}
|
|
11
14
|
const range = globalThis.document?.createRange();
|
|
12
15
|
const firstChild = Array.isArray(node)
|
|
13
16
|
? node[0]
|
|
@@ -48,8 +51,25 @@ const removeNodesBetweenComments = (commentNode) => {
|
|
|
48
51
|
* @return {Node[]}
|
|
49
52
|
*/
|
|
50
53
|
const diffChildNodes = function (parentNode, domChildNodes, templateChildNodes, anchorNode) {
|
|
54
|
+
// We always want to have the two arrays (domChildNodes and templateChildNodes) to have the same length.
|
|
55
|
+
// We also don't ever want to replace/diff the last node of the domChildNodes as it will always be the
|
|
56
|
+
// closing comment marker (/template-part) aka the anchorNode. So we push it to the end by inserting undefined items.
|
|
57
|
+
if (templateChildNodes.length > domChildNodes.length) {
|
|
58
|
+
const nodesToFill = templateChildNodes.length - domChildNodes.length;
|
|
59
|
+
for (let i = 0; i < nodesToFill; i++) {
|
|
60
|
+
domChildNodes.splice(domChildNodes.length - 1, 0, undefined);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (domChildNodes.length > templateChildNodes.length) {
|
|
65
|
+
const nodesToFill = domChildNodes.length - templateChildNodes.length;
|
|
66
|
+
for (let i = 0; i < nodesToFill; i++) {
|
|
67
|
+
templateChildNodes.splice(templateChildNodes.length - 1, 0, undefined);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const length = domChildNodes.length;
|
|
51
72
|
// Diff each node in the child node lists
|
|
52
|
-
let length = Math.max(templateChildNodes.length, domChildNodes.length);
|
|
53
73
|
for (let index = 0; index < length; index++) {
|
|
54
74
|
let domChildNode = domChildNodes[index];
|
|
55
75
|
let templateChildNode = templateChildNodes[index];
|
|
@@ -58,6 +78,8 @@ const diffChildNodes = function (parentNode, domChildNodes, templateChildNodes,
|
|
|
58
78
|
if (!domChildNode) {
|
|
59
79
|
if (templateChildNode instanceof TemplatePart) {
|
|
60
80
|
anchorNode.before(...templateChildNode.childNodes);
|
|
81
|
+
} else if (Array.isArray(templateChildNode)) {
|
|
82
|
+
anchorNode.before(...templateChildNode);
|
|
61
83
|
} else if (typeof templateChildNode === 'object' && 'ELEMENT_NODE' in templateChildNode) {
|
|
62
84
|
parentNode.insertBefore(templateChildNode, anchorNode);
|
|
63
85
|
} else {
|
|
@@ -75,7 +97,12 @@ const diffChildNodes = function (parentNode, domChildNodes, templateChildNodes,
|
|
|
75
97
|
}
|
|
76
98
|
|
|
77
99
|
// If DOM node is equal to the template node, don't do anything
|
|
78
|
-
if (
|
|
100
|
+
if (
|
|
101
|
+
domChildNode === templateChildNode ||
|
|
102
|
+
(domChildNode.nodeType === COMMENT_NODE &&
|
|
103
|
+
templateChildNode.nodeType === COMMENT_NODE &&
|
|
104
|
+
domChildNode.isEqualNode(templateChildNode))
|
|
105
|
+
) {
|
|
79
106
|
continue;
|
|
80
107
|
}
|
|
81
108
|
|
|
@@ -91,7 +118,7 @@ const diffChildNodes = function (parentNode, domChildNodes, templateChildNodes,
|
|
|
91
118
|
* @return {(function(*): void)|*}
|
|
92
119
|
*/
|
|
93
120
|
export const processNodePart = (comment, initialValue) => {
|
|
94
|
-
let nodes = [];
|
|
121
|
+
let nodes = initialValue instanceof TemplatePart ? [...initialValue.childNodes] : [];
|
|
95
122
|
let oldValue = initialValue instanceof TemplatePart ? [...initialValue.childNodes] : initialValue;
|
|
96
123
|
// this is for string values to be inserted into the DOM. A cached TextNode will be used so that we don't have to constantly create new DOM nodes.
|
|
97
124
|
let cachedTextNode = undefined;
|
|
@@ -145,7 +172,7 @@ export const processNodePart = (comment, initialValue) => {
|
|
|
145
172
|
oldValue = newValue;
|
|
146
173
|
nodes = diffChildNodes(comment.parentNode, nodes, [newValue], comment);
|
|
147
174
|
} else if (newValue instanceof TemplatePart) {
|
|
148
|
-
oldValue = newValue;
|
|
175
|
+
oldValue = [...newValue.childNodes];
|
|
149
176
|
nodes = diffChildNodes(comment.parentNode, nodes, [...newValue.childNodes], comment);
|
|
150
177
|
} else {
|
|
151
178
|
console.warn('Could not process value', newValue);
|
|
@@ -2,7 +2,7 @@ import { COMMENT_NODE, convertStringToTemplate } from '../util/DOMHelper.js';
|
|
|
2
2
|
import { Part } from './Part.js';
|
|
3
3
|
import { TemplateResult } from './TemplateResult.js';
|
|
4
4
|
import { AttributePart } from './AttributePart.js';
|
|
5
|
-
import { ChildNodePart } from './ChildNodePart.js';
|
|
5
|
+
import { ChildNodePart, processNodePart } from './ChildNodePart.js';
|
|
6
6
|
import { NodePart } from './NodePart.js';
|
|
7
7
|
|
|
8
8
|
/** @type {Map<TemplateStringsArray, Part[]>} */
|
|
@@ -50,8 +50,11 @@ export class TemplatePart extends Part {
|
|
|
50
50
|
// if not SSRed, childNodes will only ever have two comment nodes, the start and the end marker
|
|
51
51
|
if (childNodes.length > 2) {
|
|
52
52
|
serverSideRendered = true;
|
|
53
|
-
this.childNodes = childNodes;
|
|
54
53
|
}
|
|
54
|
+
|
|
55
|
+
this.childNodes = childNodes;
|
|
56
|
+
this.startNode = startNode;
|
|
57
|
+
this.endNode = endNode;
|
|
55
58
|
}
|
|
56
59
|
|
|
57
60
|
this.parseValue(value);
|
|
@@ -61,6 +64,13 @@ export class TemplatePart extends Part {
|
|
|
61
64
|
// We need a childNodes list that is NOT live so that we don't loose elements when they get removed from the dom and we can (re)add them back in later.
|
|
62
65
|
this.childNodes = [...this.childNodes];
|
|
63
66
|
}
|
|
67
|
+
|
|
68
|
+
this.startNode = this.childNodes[0];
|
|
69
|
+
this.endNode = this.childNodes[this.childNodes.length - 1];
|
|
70
|
+
|
|
71
|
+
if (this.endNode) {
|
|
72
|
+
this.processor = processNodePart(this.endNode, this);
|
|
73
|
+
}
|
|
64
74
|
}
|
|
65
75
|
|
|
66
76
|
/**
|
|
@@ -69,6 +79,8 @@ export class TemplatePart extends Part {
|
|
|
69
79
|
update(value) {
|
|
70
80
|
this.parseValue(value);
|
|
71
81
|
this.updateParts(value.values);
|
|
82
|
+
this.childNodes = [...this.childNodes];
|
|
83
|
+
this.processor?.(this);
|
|
72
84
|
}
|
|
73
85
|
|
|
74
86
|
/**
|
|
@@ -85,15 +97,13 @@ export class TemplatePart extends Part {
|
|
|
85
97
|
*/
|
|
86
98
|
parseValue(templateResult) {
|
|
87
99
|
if (this.strings !== templateResult.strings) {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
fragmentsCache.set(templateResult.strings, fragment);
|
|
93
|
-
}
|
|
94
|
-
const importedFragment = globalThis.document?.importNode(fragment, true);
|
|
95
|
-
this.childNodes = importedFragment.childNodes;
|
|
100
|
+
let fragment = fragmentsCache.get(templateResult.strings);
|
|
101
|
+
if (!fragment) {
|
|
102
|
+
fragment = this.parseFragment(templateResult);
|
|
103
|
+
fragmentsCache.set(templateResult.strings, fragment);
|
|
96
104
|
}
|
|
105
|
+
const importedFragment = globalThis.document?.importNode(fragment, true);
|
|
106
|
+
this.childNodes = importedFragment.childNodes;
|
|
97
107
|
|
|
98
108
|
let parts = partsCache.get(templateResult.strings);
|
|
99
109
|
if (!parts) {
|
|
@@ -121,6 +131,7 @@ export class TemplatePart extends Part {
|
|
|
121
131
|
});
|
|
122
132
|
this.strings = templateResult.strings;
|
|
123
133
|
}
|
|
134
|
+
return this.childNodes;
|
|
124
135
|
}
|
|
125
136
|
|
|
126
137
|
/**
|
|
@@ -56,15 +56,15 @@ const choose = (value, cases, defaultCase) => {
|
|
|
56
56
|
/**
|
|
57
57
|
* Renders a given string as HTML instead of text or as unescaped string in SSR mode
|
|
58
58
|
* @param {string} string
|
|
59
|
-
* @returns {(function(): Node) | { __unsafeHTML: true, string: string }}
|
|
59
|
+
* @returns {(function(): [Node]) | { __unsafeHTML: true, string: string }}
|
|
60
60
|
*/
|
|
61
61
|
const unsafeHTML = (string) => {
|
|
62
62
|
if (!globalThis.DOMParser) {
|
|
63
63
|
return { __unsafeHTML: true, string };
|
|
64
64
|
}
|
|
65
|
-
const
|
|
66
|
-
const
|
|
67
|
-
return () =>
|
|
65
|
+
const body = convertStringToHTML(string);
|
|
66
|
+
const childNodes = [...body.childNodes];
|
|
67
|
+
return () => childNodes;
|
|
68
68
|
};
|
|
69
69
|
|
|
70
70
|
/**
|
package/src/dom-parts/render.js
CHANGED
|
@@ -4,7 +4,10 @@
|
|
|
4
4
|
* @param {Element} domNode
|
|
5
5
|
*/
|
|
6
6
|
const render = (template, domNode) => {
|
|
7
|
-
if (
|
|
7
|
+
if (!template) {
|
|
8
|
+
// empty template was returned
|
|
9
|
+
domNode.innerHTML = '';
|
|
10
|
+
} else if (typeof template === 'string') {
|
|
8
11
|
// just a plain string (or literal)
|
|
9
12
|
domNode.innerHTML = template;
|
|
10
13
|
} else {
|