@webtides/element-js 1.1.4 → 1.1.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webtides/element-js",
3
- "version": "1.1.4",
3
+ "version": "1.1.6",
4
4
  "module": "index.js",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -37,8 +37,8 @@
37
37
  "devDependencies": {
38
38
  "@babel/eslint-parser": "^7.23.3",
39
39
  "@open-wc/testing": "^4.0.0",
40
- "@web/dev-server": "^0.4.1",
41
- "@web/test-runner": "^0.14.1",
40
+ "@web/dev-server": "^0.4.6",
41
+ "@web/test-runner": "^0.19.0",
42
42
  "babel-eslint": "^10.1.0",
43
43
  "eslint": "8.55.0",
44
44
  "eslint-config-prettier": "^9.1.0",
@@ -49,23 +49,15 @@ const processEventAttribute = (node, name) => {
49
49
  * @return {(function(*): void)|*}
50
50
  */
51
51
  const processAttribute = (node, name) => {
52
- let oldValue,
53
- orphan = true;
54
- const attributeNode = globalThis.document?.createAttributeNS(null, name);
52
+ let oldValue;
55
53
  return (newValue) => {
56
54
  const value = newValue?.valueOf();
57
55
  if (oldValue !== value) {
58
- if ((oldValue = value) == null) {
59
- if (!orphan) {
60
- node.removeAttributeNode(attributeNode);
61
- orphan = true;
62
- }
56
+ oldValue = value;
57
+ if (value == null) {
58
+ node.removeAttribute(name);
63
59
  } else {
64
- attributeNode.value = value;
65
- if (orphan) {
66
- node.setAttributeNodeNS(attributeNode);
67
- orphan = false;
68
- }
60
+ node.setAttribute(name, value);
69
61
  }
70
62
  }
71
63
  };
@@ -3,31 +3,6 @@ import { Part } from './Part.js';
3
3
  import { TemplateResult } from './TemplateResult.js';
4
4
  import { TemplatePart } from './TemplatePart.js';
5
5
 
6
- /**
7
- * @param {Node | TemplatePart | any[]} node
8
- * @return {ChildNode}
9
- */
10
- const removeChildNodes = (node) => {
11
- if (!Array.isArray(node) && !(node instanceof TemplatePart) && !node.hasChildNodes()) {
12
- return node;
13
- }
14
- const range = globalThis.document?.createRange();
15
- const firstChild = Array.isArray(node)
16
- ? node[0]
17
- : node instanceof TemplatePart
18
- ? node.childNodes[0]
19
- : node.firstChild;
20
- const lastChild = Array.isArray(node)
21
- ? node[node.length - 1]
22
- : node instanceof TemplatePart
23
- ? node.childNodes[node.childNodes.length - 1]
24
- : node.lastChild;
25
- range.setStartAfter(firstChild);
26
- range.setEndAfter(lastChild);
27
- range.deleteContents();
28
- return firstChild;
29
- };
30
-
31
6
  /**
32
7
  * @param {Element} commentNode
33
8
  */
@@ -51,31 +26,38 @@ const removeNodesBetweenComments = (commentNode) => {
51
26
  * @return {Node[]}
52
27
  */
53
28
  const diffChildNodes = function (parentNode, domChildNodes, templateChildNodes, anchorNode) {
54
- // We always want to have the two arrays (domChildNodes and templateChildNodes) to have the same length.
29
+ // TODO: when diffing arrays there is sometimes two lists of TemplateParts instead of lists of childNodes
30
+ const offset = domChildNodes.includes(anchorNode) ? 1 : 0;
31
+
32
+ // release first level references to prevent cache mutation
33
+ const clonedDomChildNodes = [...domChildNodes];
34
+ const clonedTemplateChildNodes = [...templateChildNodes];
35
+
36
+ // We always want to have the two arrays (domChildNodes and cloneTemplateChildNodes) to have the same length.
55
37
  // We also don't ever want to replace/diff the last node of the domChildNodes as it will always be the
56
38
  // 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;
39
+ if (clonedTemplateChildNodes.length > clonedDomChildNodes.length) {
40
+ const nodesToFill = clonedTemplateChildNodes.length - clonedDomChildNodes.length;
59
41
  for (let i = 0; i < nodesToFill; i++) {
60
- domChildNodes.splice(domChildNodes.length - 1, 0, undefined);
42
+ clonedDomChildNodes.splice(clonedDomChildNodes.length - offset, 0, undefined);
61
43
  }
62
44
  }
63
45
 
64
- if (domChildNodes.length > templateChildNodes.length) {
65
- const nodesToFill = domChildNodes.length - templateChildNodes.length;
46
+ if (clonedDomChildNodes.length > clonedTemplateChildNodes.length) {
47
+ const nodesToFill = clonedDomChildNodes.length - clonedTemplateChildNodes.length;
66
48
  for (let i = 0; i < nodesToFill; i++) {
67
- templateChildNodes.splice(templateChildNodes.length - 1, 0, undefined);
49
+ clonedTemplateChildNodes.splice(clonedTemplateChildNodes.length - offset, 0, undefined);
68
50
  }
69
51
  }
70
52
 
71
- const length = domChildNodes.length;
53
+ const length = clonedDomChildNodes.length;
72
54
  // Diff each node in the child node lists
73
55
  for (let index = 0; index < length; index++) {
74
- let domChildNode = domChildNodes[index];
75
- let templateChildNode = templateChildNodes[index];
56
+ let domChildNode = clonedDomChildNodes[index];
57
+ let templateChildNode = clonedTemplateChildNodes[index];
76
58
 
77
59
  // If the DOM node doesn't exist, append/copy the template node
78
- if (!domChildNode) {
60
+ if (domChildNode === undefined) {
79
61
  if (templateChildNode instanceof TemplatePart) {
80
62
  anchorNode.before(...templateChildNode.childNodes);
81
63
  } else if (Array.isArray(templateChildNode)) {
@@ -90,24 +72,47 @@ const diffChildNodes = function (parentNode, domChildNodes, templateChildNodes,
90
72
  continue;
91
73
  }
92
74
 
93
- // If the template node doesn't exist, remove the node in the DOM
94
- if (!templateChildNode) {
95
- parentNode.removeChild(removeChildNodes(domChildNode));
75
+ if (templateChildNode === undefined) {
76
+ // If the template node doesn't exist, remove the node in the DOM
77
+ if ('ELEMENT_NODE' in domChildNode) {
78
+ // remove dom node
79
+ parentNode.removeChild(domChildNode);
80
+ } else if (Array.isArray(domChildNode) || domChildNode instanceof TemplatePart) {
81
+ // remove childNodes
82
+ const childNodes = Array.isArray(domChildNode) ? domChildNode : [...domChildNode.childNodes];
83
+ for (const childNode of childNodes) {
84
+ childNode.remove();
85
+ }
86
+ } else {
87
+ console.log('This should not happen... what do we do about this node?!', domChildNode);
88
+ }
96
89
  continue;
97
90
  }
98
91
 
99
- // If DOM node is equal to the template node, don't do anything
92
+ // If DOM node is equal to the template node, don't do anything in the DOM
100
93
  if (
101
94
  domChildNode === templateChildNode ||
102
- (domChildNode.nodeType === COMMENT_NODE &&
103
- templateChildNode.nodeType === COMMENT_NODE &&
104
- domChildNode.isEqualNode(templateChildNode))
95
+ (domChildNode.nodeType && templateChildNode.nodeType && domChildNode.isEqualNode(templateChildNode))
105
96
  ) {
97
+ // but we need to assign the current DOM node into the templateChildNodes
98
+ // as they will be returned here at the end and become the domNodes for the next diffing.
99
+ const childIndex = templateChildNodes.indexOf(templateChildNode);
100
+ templateChildNodes[childIndex] = domChildNode;
106
101
  continue;
107
102
  }
108
103
 
109
104
  // Everything else is somehow different and can be replaced
110
- parentNode.replaceChild(templateChildNode, domChildNode);
105
+ if (parentNode.contains(domChildNode)) {
106
+ parentNode.replaceChild(templateChildNode, domChildNode);
107
+ } else {
108
+ console.warn(
109
+ 'This should not happen',
110
+ parentNode,
111
+ parentNode.contains(domChildNode),
112
+ templateChildNode,
113
+ domChildNode,
114
+ );
115
+ }
111
116
  }
112
117
  return templateChildNodes;
113
118
  };
@@ -119,7 +124,7 @@ const diffChildNodes = function (parentNode, domChildNodes, templateChildNodes,
119
124
  */
120
125
  export const processNodePart = (comment, initialValue) => {
121
126
  let nodes = initialValue instanceof TemplatePart ? [...initialValue.childNodes] : [];
122
- let oldValue = initialValue instanceof TemplatePart ? [...initialValue.childNodes] : initialValue;
127
+ let oldValue = initialValue instanceof TemplatePart ? initialValue.strings : initialValue;
123
128
  // 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.
124
129
  let cachedTextNode = undefined;
125
130
 
@@ -166,17 +171,16 @@ export const processNodePart = (comment, initialValue) => {
166
171
  oldValue = newValue;
167
172
  break;
168
173
  }
169
- // if the new value is a node or a fragment, and it's different from the live node, then it's diffed.
170
- if (oldValue !== newValue) {
171
- if ('ELEMENT_NODE' in newValue) {
172
- oldValue = newValue;
173
- nodes = diffChildNodes(comment.parentNode, nodes, [newValue], comment);
174
- } else if (newValue instanceof TemplatePart) {
175
- oldValue = [...newValue.childNodes];
176
- nodes = diffChildNodes(comment.parentNode, nodes, [...newValue.childNodes], comment);
177
- } else {
178
- console.warn('Could not process value', newValue);
179
- }
174
+
175
+ if (newValue instanceof TemplatePart && oldValue !== newValue.strings) {
176
+ // if the new value is a node or a fragment, and it's different from the live node, then it's diffed.
177
+ // static strings have changed in tpl part
178
+ oldValue = newValue.strings;
179
+ nodes = diffChildNodes(comment.parentNode, nodes, [...newValue.childNodes], comment);
180
+ } else if (oldValue !== newValue && 'ELEMENT_NODE' in newValue) {
181
+ // DOM Node changed, needs diffing
182
+ oldValue = newValue;
183
+ nodes = diffChildNodes(comment.parentNode, nodes, [newValue], comment);
180
184
  }
181
185
  break;
182
186
  case 'function':
@@ -275,7 +279,9 @@ export class ChildNodePart extends Part {
275
279
  this.updateParts(value);
276
280
  }
277
281
 
278
- this.processor?.(parsedValue);
282
+ if (!(value instanceof TemplateResult)) {
283
+ this.processor?.(parsedValue);
284
+ }
279
285
  }
280
286
 
281
287
  /**
@@ -324,6 +330,7 @@ export class ChildNodePart extends Part {
324
330
  let value = values[index];
325
331
 
326
332
  if (value instanceof TemplateResult) {
333
+ // TODO this is probably wrong. Should only be taken from parts cache if it has the same type (eg. template vs string, mixed types arrays)
327
334
  let templatePart = this.parts[index];
328
335
  if (!templatePart) {
329
336
  const templatePartCommentNodes = this.childNodes?.filter(
@@ -1,4 +1,4 @@
1
- import { COMMENT_NODE, getNodePath } from '../util/DOMHelper.js';
1
+ import { COMMENT_NODE, getNodePath, encodeHTML } from '../util/DOMHelper.js';
2
2
  import { encodeAttribute, isObjectLike } from '../util/AttributeParser.js';
3
3
  import { TemplatePart } from './TemplatePart.js';
4
4
 
@@ -8,18 +8,35 @@ const elements = /<([a-z]+[a-z0-9:._-]*)([^>]*?)(\/?)>/g;
8
8
  // TODO: v this will not match any values with escaped quotes like onClick='console.log("\'test")'
9
9
  const attributes = /([^\s]*)=((?:")[^"]*(?:")|(?:')[^']*(?:')|[^\s\/>]*)|([^\s\/>]*)/g;
10
10
  const partPositions = /[\x01\x02]/g;
11
+ const ssrPlaceholder = /{{dom-part\?(.*?)}}/g;
11
12
  // \x01 Node.ELEMENT_NODE
12
13
  // \x02 Node.ATTRIBUTE_NODE
13
14
  // \x03 COMMENT.ATTRIBUTE_TOKEN
14
15
  // \x04 Node.ATTRIBUTE_TOKEN
15
16
 
16
- const ssrPlaceholder = /{{dom-part\?(.*?)}}/g;
17
- function makeSSRPlaceholder(type, params) {
17
+ /**
18
+ * Creates a URL query string for meta information to pass along ssr part placeholders
19
+ * @param type
20
+ * @param params
21
+ * @return {string}
22
+ */
23
+ function makeSSRPlaceholderParams(type, params) {
18
24
  const urlSearchParams = new URLSearchParams({ type, ...params });
19
25
  const queryString = urlSearchParams.toString();
20
26
  return `{{dom-part?${queryString}}}`;
21
27
  }
22
28
 
29
+ /**
30
+ * Creates a URL query string for meta information to pass along part comments
31
+ * @param {string} type
32
+ * @param {{}} params
33
+ * @return {string}
34
+ */
35
+ function makePartParams(type, params) {
36
+ const urlSearchParams = new URLSearchParams({ type, ...params });
37
+ return urlSearchParams.toString();
38
+ }
39
+
23
40
  /**
24
41
  * Given a template, find part positions as both nodes and attributes and
25
42
  * return a string with placeholders as either comment nodes or named attributes.
@@ -41,9 +58,10 @@ export const createTemplateString = (templateStrings, ssr = false) => {
41
58
  if (directive && directive === '\x01') {
42
59
  elementTagWithAttributes = elementTagWithAttributes.replace(
43
60
  attribute,
44
- ssr ? makeSSRPlaceholder('directive') : '',
61
+ ssr ? makeSSRPlaceholderParams('directive') : '',
45
62
  );
46
- return `<!--\x02$-->`;
63
+ const params = makePartParams('directive');
64
+ return `<!--\x02?${params}-->`;
47
65
  }
48
66
 
49
67
  // remove quotes from attribute value to normalize the value
@@ -53,19 +71,22 @@ export const createTemplateString = (templateStrings, ssr = false) => {
53
71
  const partsCount = (attribute.match(/\x01/g) || []).length;
54
72
  const parts = [];
55
73
  for (let index = 0; index < partsCount; index++) {
56
- // TODO: maybe we could use url parameters here?! -> ?type=attribute&name=${name}&initialValue=${initialValue}
57
- parts.push(`<!--\x02:${name}=${value.replaceAll('\x01', '\x03')}-->`);
74
+ const params = makePartParams('attribute', {
75
+ name,
76
+ initialValue: value.replaceAll('\x01', '\x03'),
77
+ });
78
+ parts.push(`<!--\x02?${params}-->`);
58
79
  }
59
80
 
60
81
  if (parts.length > 0) {
61
82
  let replacement = '';
62
83
  if (ssr) {
63
84
  for (let index = 0; index < partsCount - 1; index++) {
64
- replacement = replacement + makeSSRPlaceholder('noop');
85
+ replacement = replacement + makeSSRPlaceholderParams('noop');
65
86
  }
66
87
  replacement =
67
88
  replacement +
68
- makeSSRPlaceholder('attribute', {
89
+ makeSSRPlaceholderParams('attribute', {
69
90
  name,
70
91
  interpolations: partsCount,
71
92
  initialValue: value.replaceAll('\x01', '\x03'),
@@ -110,19 +131,19 @@ export const createTemplateString = (templateStrings, ssr = false) => {
110
131
  const partsCount = (content.match(/\x01/g) || []).length;
111
132
  const parts = [];
112
133
  for (let index = 0; index < partsCount; index++) {
113
- // TODO: maybe we could use url parameters here?! -> ?type=raw-text-node&initialValue=${initialValue}
114
- parts.push(`<!--\x02/raw-text-node=${content.replaceAll('\x01', '\x03')}-->`);
134
+ const params = makePartParams('raw-text-node', {
135
+ initialValue: content.replaceAll('\x01', '\x03'),
136
+ });
137
+ parts.push(`<!--\x02?${params}-->`);
115
138
  }
116
- const parsedContent = content.replaceAll('\x01', ssr ? makeSSRPlaceholder('raw-text-node') : '');
139
+ const parsedContent = content.replaceAll('\x01', ssr ? makeSSRPlaceholderParams('raw-text-node') : '');
117
140
  return `${parts.join('')}<${tag}${attributes}>${parsedContent}</${tag}>`;
118
141
  });
119
142
  // replace interpolation placeholders with our indexed markers
120
143
  template = template.replace(partPositions, (partPosition) => {
121
144
  if (partPosition === '\x01') {
122
- if (ssr) {
123
- return `<!--dom-part-${partIndex}-->${makeSSRPlaceholder('node')}<!--/dom-part-${partIndex++}-->`;
124
- }
125
- return `<!--dom-part-${partIndex}--><!--/dom-part-${partIndex++}-->`;
145
+ const placeholder = ssr ? makeSSRPlaceholderParams('node') : '';
146
+ return `<!--dom-part-${partIndex}-->${placeholder}<!--/dom-part-${partIndex++}-->`;
126
147
  } else if (partPosition === '\x02') {
127
148
  return `dom-part-${partIndex++}`;
128
149
  }
@@ -149,7 +170,7 @@ const attribute = (name, value, quotes = '"') => {
149
170
  const getValue = (value) => {
150
171
  switch (typeof value) {
151
172
  case 'string':
152
- return encodeAttribute(value);
173
+ return encodeHTML(value);
153
174
  case 'boolean':
154
175
  case 'number':
155
176
  return String(value);
@@ -170,7 +191,7 @@ const getValue = (value) => {
170
191
  case 'function':
171
192
  return getValue(value());
172
193
  }
173
- return value == null ? '' : encodeAttribute(String(value));
194
+ return value == null ? '' : encodeHTML(String(value));
174
195
  };
175
196
 
176
197
  /** @type {Map<TemplateStringsArray, string>} */
@@ -196,14 +217,18 @@ export class TemplateResult {
196
217
  * @param {Element} domNode
197
218
  */
198
219
  renderInto(domNode) {
199
- let serverSideRendered = false;
220
+ const startNode = Array.from(domNode.childNodes)
221
+ .filter((node) => node.nodeType === COMMENT_NODE)
222
+ .find((node) => node.data === 'template-part');
223
+
224
+ let serverSideRendered = startNode !== undefined;
225
+
200
226
  let templatePart = templatePartsCache.get(domNode);
201
- if (!templatePart) {
202
- const startNode = Array.from(domNode.childNodes)
203
- .filter((node) => node.nodeType === COMMENT_NODE)
204
- .find((node) => node.data === 'template-part');
205
227
 
206
- serverSideRendered = startNode !== undefined;
228
+ if (!templatePart || !startNode) {
229
+ if (!startNode) {
230
+ domNode.replaceChildren();
231
+ }
207
232
 
208
233
  templatePart = new TemplatePart(startNode, this);
209
234
  templatePartsCache.set(domNode, templatePart);
@@ -378,29 +403,32 @@ export class TemplateResult {
378
403
  parts.push({ type: 'node', path: getNodePath(node) });
379
404
  continue;
380
405
  }
381
- if (/^dom-part-\d+:/.test(node.data)) {
382
- const [_, ...attribute] = node.data.split(':');
383
- const [name, ...initialValue] = attribute.join(':').split('=');
406
+ if (/^dom-part-\d+\?type=attribute&(.*?)$/.test(node.data)) {
407
+ // For html`<div id="${'foo'}" class="${'bar'}"></div>` we will get:
408
+ // <!--dom-part-0?type=attribute&name=id&initialValue=\x03--><!--dom-part-1?type=attribute&name=class&initialValue=\x03-->
409
+ const [_, paramsString] = node.data.split('?');
410
+ const searchParams = new URLSearchParams(paramsString);
384
411
  parts.push({
385
412
  type: 'attribute',
386
413
  path: getNodePath(node),
387
- name: name,
388
- initialValue: initialValue.join('='),
414
+ name: searchParams.get('name'),
415
+ initialValue: searchParams.get('initialValue'),
389
416
  });
390
417
  continue;
391
418
  }
392
- if (/^dom-part-\d+\/raw-text-node/.test(node.data)) {
419
+ if (/^dom-part-\d+\?type=raw-text-node&(.*?)$/.test(node.data)) {
393
420
  // For html`<textarea>${'foo'} bar</textarea>` we will get:
394
- // <!--dom-part-0/raw-text-node=\x03 bar-->
395
- const [_, ...initialValue] = node.data.split('=');
421
+ // <!--dom-part-0?type=raw-text-node&initialValue=\x03 bar-->
422
+ const [_, paramsString] = node.data.split('?');
423
+ const searchParams = new URLSearchParams(paramsString);
396
424
  parts.push({
397
425
  type: 'raw-text-node',
398
426
  path: getNodePath(node),
399
- initialValue: initialValue.join('='),
427
+ initialValue: searchParams.get('initialValue'),
400
428
  });
401
429
  continue;
402
430
  }
403
- if (/^dom-part-\d+\$/.test(node.data)) {
431
+ if (/^dom-part-\d+\?type=directive$/.test(node.data)) {
404
432
  parts.push({ type: 'directive', path: getNodePath(node) });
405
433
  }
406
434
  }
@@ -125,29 +125,23 @@ export function camelToDash(string) {
125
125
  }
126
126
 
127
127
  /**
128
- * Decodes an attribute.
128
+ * Decodes an attribute according to {@link https://html.spec.whatwg.org/multipage/syntax.html#attributes-2}
129
129
  * @param {string} attribute
130
130
  * @returns {string}
131
131
  */
132
132
  export function decodeAttribute(attribute) {
133
- return `${attribute}`
134
- .replace(/&lt;/g, '<')
135
- .replace(/&gt;/g, '>')
136
- .replace(/&quot;/g, '"')
137
- .replace(/&apos;/g, "'");
133
+ return `${attribute}`.replace(/&quot;/g, '"').replace(/&amp;/g, '&');
138
134
  }
139
135
 
140
136
  /**
141
- * Encodes an attribute.
137
+ * Encodes an attribute according to {@link https://html.spec.whatwg.org/multipage/syntax.html#attributes-2}
142
138
  * @param {string} attribute
143
139
  * @returns {string}
144
140
  */
145
141
  export function encodeAttribute(attribute) {
146
142
  return `${attribute}`
147
- .replace(/'/g, '&apos;')
143
+ .replace(/&/g, '&amp;')
148
144
  .replace(/"/g, '&quot;')
149
- .replace(/</g, '&lt;')
150
- .replace(/>/g, '&gt;')
151
145
  .replace(/\r\n/g, '\n')
152
146
  .replace(/[\r\n]/g, '\n');
153
147
  }
@@ -305,3 +305,33 @@ export const diffNodes = function (templateNode, domNode, shouldDiffChildNodes =
305
305
  }
306
306
  }
307
307
  };
308
+
309
+ /**
310
+ * Decodes a html string
311
+ * @param {string} html
312
+ * @returns {string}
313
+ */
314
+ export function decodeHTML(html) {
315
+ return `${html}`
316
+ .replace(/&amp;/g, '&')
317
+ .replace(/&lt;/g, '<')
318
+ .replace(/&gt;/g, '>')
319
+ .replace(/&quot;/g, '"')
320
+ .replace(/&apos;/g, "'");
321
+ }
322
+
323
+ /**
324
+ * Encodes a html string
325
+ * @param {string} html
326
+ * @returns {string}
327
+ */
328
+ export function encodeHTML(html) {
329
+ return `${html}`
330
+ .replace(/&/g, '&amp;')
331
+ .replace(/</g, '&lt;')
332
+ .replace(/>/g, '&gt;')
333
+ .replace(/"/g, '&quot;')
334
+ .replace(/'/g, '&apos;')
335
+ .replace(/\r\n/g, '\n')
336
+ .replace(/[\r\n]/g, '\n');
337
+ }
package/CHANGELOG.md DELETED
@@ -1,396 +0,0 @@
1
- # Change Log
2
-
3
- All notable changes to this project will be documented in this file.
4
-
5
- The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres
6
- to [Semantic Versioning](http://semver.org/).
7
-
8
- <!--
9
- PRs should document their user-visible changes (if any) in the
10
- Unreleased section, uncommenting the header as necessary.
11
- -->
12
-
13
- <!-- ## Unreleased -->
14
- <!-- ### Added -->
15
- <!-- ### Changed -->
16
- <!-- ### Removed -->
17
- <!-- ### Fixed -->
18
-
19
- ## [1.1.4] - 2024-11-12
20
-
21
- ### FIXED
22
-
23
- - Fixed rendering interpolations inside textarea nodes ([#133](https://github.com/webtides/element-js/issues/133))
24
-
25
- ## [1.1.3] - 2024-11-11
26
-
27
- ### FIXED
28
-
29
- - Fixed unsafeHtml having more than one child ([#135](https://github.com/webtides/element-js/issues/135))
30
- - Fixed early returns from templates and fixes static html changes not reflecting to DOM ([#132](https://github.com/webtides/element-js/issues/132))
31
-
32
-
33
- ## [1.1.2] - 2024-10-30
34
-
35
- ### FIXED
36
-
37
- - Fixed rerendering of templates that were initially null, prevent runtime errors
38
-
39
- ## [1.1.1] - 2024-09-16
40
-
41
- ### FIXED
42
-
43
- - Fixed base url and css rules order in adopted style sheets.
44
- - Fixed randomUUID not being available in unsecure hosts (like localhost) by adding a helper function
45
-
46
- ## [1.1.0] - 2024-06-24
47
-
48
- ### Added
49
-
50
- - Added `optionalAttribute` directive to conditionally render attributes (plus value)
51
-
52
- ### FIXED
53
-
54
- - Fixed `spreadAttributes` to not render "null|undefined|NaN" to the attributes value but removing the entire attribute
55
- when such values are passed.
56
-
57
- ## [1.0.0] - 2024-05-24
58
-
59
- ### Added
60
-
61
- - Added our own versions of directives and template helpers ([#67](https://github.com/webtides/element-js/pull/67))
62
- - Added JSDocs for everything in the codebase. This will especially be helpful when using constructor options.
63
- - Added state serialization. This improves hydration from SSR a
64
- lot. ([#106](https://github.com/webtides/element-js/pull/106))
65
- - added declarative shadow dom rendering for template elements
66
-
67
- ### Changed
68
-
69
- - _BREAKING_ Uses a new custom renderer for the `TemplateElement`. It replaces the old `lit-html` renderer. The API and
70
- usage should be exactly the same. The only thing to do/change is the use of the old `lit-html` directives. For
71
- detailed instructions see the upgrade guide. ([#67](https://github.com/webtides/element-js/pull/67))
72
- - _BREAKING_ `element-js` will now also trigger the `afterUpdate` hook right after the `connected` hook. This is to
73
- reduce the cases where you had to do the same things in `connected` and `afterUpdate`. You can now remove those
74
- duplicate calls from the `connected` hook. ([#60](https://github.com/webtides/element-js/pull/60))
75
- - _BREAKING_ Changed `package.json` type to `module`. This should hopefully not break anything. `element-js` is either
76
- used directly from the browser or through a bundler. In both cases the added type: module should not
77
- matter. ([#64](https://github.com/webtides/element-js/pull/64))
78
- - _BRAKING_ Changed the default options when using the `dispatch` helper function. `bubbles`, `cancelable`
79
- and `composed` will now be `true` by default.
80
- - _BRAKING_ Changed adopting global styles to include ALL global styles. Previously only one inline <style> with the ID
81
- of '#globalStyles' would be adopted. For more information see the docs.
82
-
83
- ### Removed
84
-
85
- - _BREAKING_ Removed the `lit-html` dependency. `element-js` is now officially dependency
86
- free! ([#67](https://github.com/webtides/element-js/pull/67))
87
- - _BREAKING_ Removed the deprecated `BaseElement` constructor option `childListUpdate`. Please use the "
88
- mutationObserverOptions" dictionary instead. See the docs for more
89
- info. ([#99](https://github.com/webtides/element-js/pull/99))
90
- - _BREAKING_ Removed the `hooks` map in the `BaseElement`. Using the hooks() map for lifecycle hooks is deprecated!
91
- Please overwrite the existing lifecycle hook functions. See the docs for more
92
- info. ([#99](https://github.com/webtides/element-js/pull/99))
93
- - _BREAKING_ Removed the `computed` map in the `BaseElement`. Using the computed() map for computed properties is
94
- deprecated! Please use regular JS getters and return the computed value. See the docs for more
95
- info. ([#99](https://github.com/webtides/element-js/pull/99))
96
- - _BREAKING_ Removed all occurrences of `ShadyCSS`
97
- in `StyledElement`. ([#99](https://github.com/webtides/element-js/pull/99))
98
- - _BRAKING_ Removed the `i18n` helper function. See CHANGELOG how to implement it yourself or use a third party library.
99
-
100
- ## [1.0.0-alpha.13] - 2024-05-08
101
-
102
- ### Fixed
103
-
104
- - catch security errors when `GlobalStylesStore` tries to read css rules from cross-origin stylesheets.
105
-
106
- ## [1.0.0-alpha.12] - 2024-03-03
107
-
108
- ### Added
109
-
110
- - added declarative shadow dom rendering for template elements
111
-
112
- ### Fixed
113
-
114
- - fixed issues with whitespace being returned untrimmed in a template result
115
-
116
- ## [1.0.0-alpha.11] - 2024-03-13
117
-
118
- ### Fixed
119
-
120
- - fixed missing return type `string` for the `template()` function in `TemplateElement`
121
- - fixed types for the `propertyOptions` option in `BaseElement`
122
-
123
- ## [1.0.0-alpha.10] - 2024-02-20
124
-
125
- ### Fixed
126
-
127
- - fixed unsafeHTML not rendering anything when toString was called
128
- - fixed using = sign in attribute positions with interpolations
129
- - fixed rendering null values in node part positions in SSR
130
- - fixed missing .js file extensions
131
-
132
- ## [1.0.0-alpha.9] - 2024-01-05
133
-
134
- ### Changed
135
-
136
- - moved DOM methods into connected callbacks to make it safe for the elements to be created in SSR environments
137
-
138
- ## [1.0.0-alpha.8] - 2024-01-04
139
-
140
- ### Fixed
141
-
142
- - fixed ChildNodePart not replacing dom nodes from unsafeHTML
143
-
144
- ## [1.0.0-alpha.7] - 2023-12-19
145
-
146
- ### Fixed
147
-
148
- - fixed wrong attribute parts for the same name on different elements
149
-
150
- ## [1.0.0-alpha.6] - 2023-12-18
151
-
152
- ### Fixed
153
-
154
- - fixed hyphens in attribute names being split in SSR mode
155
-
156
- ## [1.0.0-alpha.5] - 2023-12-07
157
-
158
- ### Added
159
-
160
- - Added the /docs directory to the files array in package.json
161
- - Added part instances as \_\_part to comment nodes
162
-
163
- ### Fixed
164
-
165
- - fixed escaped unsafeHTML in SSR mode
166
-
167
- ## [1.0.0-alpha.4] - 2023-12-01
168
-
169
- ### Fixed
170
-
171
- - falsy creation of directive parts for non-directive attributes
172
- - creating parts with wrong comment marker positions
173
-
174
- ## [1.0.0-alpha.3] - 2023-12-01
175
-
176
- ### Added
177
-
178
- - Added state serialization. This improves hydration from SSR a
179
- lot. ([#106](https://github.com/webtides/element-js/pull/106))
180
-
181
- ### Changed
182
-
183
- - _BRAKING_ Changed the default options when using the `dispatch` helper function. `bubbles`, `cancelable`
184
- and `composed` will now be `true` by default.
185
- - _BRAKING_ Changed adopting global styles to include ALL global styles. Previously only one inline <style> with the ID
186
- of '#globalStyles' would be adopted. For more information see the docs.
187
-
188
- ### Removed
189
-
190
- - _BRAKING_ Removed `i18n` helper function. See CHANGELOG how to implement it yourself or use a third party library.
191
-
192
- ## [1.0.0-alpha.2] - 2023-08-23
193
-
194
- ### Added
195
-
196
- - Added JSDocs for everything in the codebase. This will especially be helpful when using constructor options.
197
-
198
- ## [1.0.0-alpha.1] - 2023-08-15
199
-
200
- ### Added
201
-
202
- - Added our own versions of directives and template helpers ([#67](https://github.com/webtides/element-js/pull/67))
203
-
204
- ### Changed
205
-
206
- - _BRAKING_ Uses a new custom renderer for the `TemplateElement`. It replaces the old `lit-html` renderer. The API and
207
- usage should be exactly the same. The only thing to do/change is the use of the old `lit-html` directives. For
208
- detailed instructions see the upgrade guide. ([#67](https://github.com/webtides/element-js/pull/67))
209
- - _BREAKING_ `element-js` will now also trigger the `afterUpdate` hook right after the `connected` hook. This is to
210
- reduce the cases where you had to do the same things in `connected` and `afterUpdate`. You can now remove those
211
- duplicate calls from the `connected` hook. ([#60](https://github.com/webtides/element-js/pull/60))
212
- - _BREAKING_ Changed `package.json` type to `module`. This should hopefully not break anything. `element-js` is either
213
- used directly from the browser or through a bundler. In both cases the added type: module should not
214
- matter. ([#64](https://github.com/webtides/element-js/pull/64))
215
-
216
- ### Removed
217
-
218
- - _BREAKING_ Removed the `lit-html` dependency. `element-js` is now officially dependency
219
- free! ([#67](https://github.com/webtides/element-js/pull/67))
220
- - _BREAKING_ Removed the deprecated `BaseElement` constructor option `childListUpdate`. Please use the "
221
- mutationObserverOptions" dictionary instead. See the docs for more
222
- info. ([#99](https://github.com/webtides/element-js/pull/99))
223
- - _BREAKING_ Removed the `hooks` map in the `BaseElement`. Using the hooks() map for lifecycle hooks is deprecated!
224
- Please overwrite the existing lifecycle hook functions. See the docs for more
225
- info. ([#99](https://github.com/webtides/element-js/pull/99))
226
- - _BREAKING_ Removed the `computed` map in the `BaseElement`. Using the computed() map for computed properties is
227
- deprecated! Please use regular JS getters and return the computed value. See the docs for more
228
- info. ([#99](https://github.com/webtides/element-js/pull/99))
229
- - _BREAKING_ Removed all occurrences of `ShadyCSS`
230
- in `StyledElement`. ([#99](https://github.com/webtides/element-js/pull/99))
231
-
232
- ## [0.7.4] - 2023-11-24
233
-
234
- ### Added
235
-
236
- - storeception (reactive store properties in parent stores)
237
- - context injection in shadow elements requests travel though shadow roots
238
- - defer element connection (disables client side code until called manually)
239
-
240
- ### Fixed
241
-
242
- - late context injection if parent mounts after child (loading order)
243
- - stores updating elements that want to be updated
244
-
245
- ## [0.7.3] - 2023-03-07
246
-
247
- ### Fixed
248
-
249
- - last release was tagged wrong
250
-
251
- ## [0.7.2] - 2023-03-03
252
-
253
- ### Fixed
254
-
255
- - watcher callbacks for injected stores
256
-
257
- ## [0.7.1] - 2023-03-03
258
-
259
- ### Added
260
-
261
- - Elements are now able to watch Stores
262
-
263
- ### Fixed
264
-
265
- - Injection of falsy values
266
-
267
- ## [0.7.0] - 2023-02-13
268
-
269
- ### Added
270
-
271
- - Dependency Injection via Context Protocol 💉
272
- - Provide reactive Properties or entire Stores via: provideProperties() { return { name : value}}
273
- - Inject / Request them via injectProperties() { return { name : 'defaultValue'}}
274
-
275
- ### Changed
276
-
277
- - docs structure
278
-
279
- ### Fixed
280
-
281
- - Cannot re-render slotted light dom when using the vanilla renderer https://github.com/webtides/element-js/issues/76
282
- - adds Vanilla Renderer Tests
283
-
284
- ## [0.6.2] - 2022-11-18
285
-
286
- ### FIXED
287
-
288
- - Store always switching to singleProperty Mode if constructed without any arguments.
289
-
290
- ## [0.6.1] - 2022-11-18
291
-
292
- ### ADDED
293
-
294
- - Store watch() map to control internal store state scope
295
-
296
- ## [0.6.0] - 2022-11-17
297
-
298
- ### ADDED
299
-
300
- - StoreProperty for shared application state
301
- - Directives for Vanilla Renderer
302
-
303
- ## [0.5.0] - 2022-09-13
304
-
305
- ### Changed
306
-
307
- - improved the performance of the vanilla-renderer
308
-
309
- ### Removed
310
-
311
- - setting attributes as properties during dom diffing for the vanilla renderer. This is potentially a **breaking**
312
- change if you used attributes with the "." (dot) notation
313
-
314
- ## [0.4.3] - 2022-07-06
315
-
316
- ### ADDED
317
-
318
- - parse option to propertyOptions that can be either a boolean OR a function for custom parsing
319
- - enhance reflect option in propertyOptions that it can also be a function for custom reflection
320
-
321
- ## [0.4.2] - 2022-02-03
322
-
323
- ### Fixed
324
-
325
- - vanilla-renderer: fixes attribute comparsion
326
-
327
- ## [0.4.1] - 2022-02-01
328
-
329
- ### Fixed
330
-
331
- - refs="id[]" indexing keys not nodes _rolleyes_
332
-
333
- ### Added
334
-
335
- - refs="id[]" aka possibbility to refrence a list of nodes to the $refs map.
336
-
337
- ## [0.4.0] - 2022-01-28
338
-
339
- ### Added
340
-
341
- - a "vanilla" renderer with support for hydration and no dependencies
342
-
343
- ### Changed
344
-
345
- - uses new @web/test-runner isntead of karma (#27)
346
-
347
- ## [0.3.3] - 2021-09-22
348
-
349
- ### Added
350
-
351
- - optional complex event callback notation to support addEventListener options (i.E. for passive Event Listeners )
352
- - Example: {listener: () => {}, options: { passive: true } }
353
- - docs
354
- - tests
355
-
356
- ## [0.3.2] - 2021-03-09
357
-
358
- ### Added
359
-
360
- - bundled version of `element-js` to use from a CDN and be able to prototype in tools like CodePen etc.
361
-
362
- ## [0.3.1] - 2020-12-08
363
-
364
- ### Fixed
365
-
366
- - i18n fallback values
367
-
368
- ### Added
369
-
370
- - i18n tests
371
-
372
- ## [0.3.0] - 2020-07-27
373
-
374
- ### Added
375
-
376
- - first batch/draft of the documentation
377
- - property/attribute reflection via constructor options (see the [Documentation](/docs/README.md#propertyoptions) for
378
- more info)
379
- - mutationObserverOptions to enable observing subtree mutations (see
380
- the [Documentation](/docs/README.md#mutationobserveroptions) for more info)
381
-
382
- ### Removed
383
-
384
- - `childListUpdate` constructor option has been deprecated and will be removed before `1.0`
385
-
386
- ## [0.2.0] - 2020-07-01
387
-
388
- ### Changed
389
-
390
- - uses shady-render instead of render from `lit-html` for rendering templates to be compatible with the shady-css
391
- polyfill
392
- - refactored and simplified `StyledElement`s handling/loading of style sheets
393
-
394
- ## [0.1.0] - 2020-06-26
395
-
396
- - initial release