@webtides/element-js 1.0.0-alpha.9 → 1.1.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.
Files changed (41) hide show
  1. package/CHANGELOG.md +369 -0
  2. package/README.md +3 -3
  3. package/docs/README.md +70 -89
  4. package/docs/concepts/classes.md +37 -66
  5. package/docs/concepts/hierarchy.md +29 -31
  6. package/docs/concepts/lifecycle.md +9 -23
  7. package/docs/concepts/reactivity.md +1 -3
  8. package/docs/features/computed-properties.md +13 -16
  9. package/docs/features/dependency-injection-context-protocol.md +75 -87
  10. package/docs/features/global-styles.md +1 -1
  11. package/docs/features/methods.md +14 -15
  12. package/docs/features/properties.md +21 -33
  13. package/docs/features/refs.md +23 -31
  14. package/docs/features/renderless.md +31 -37
  15. package/docs/features/store.md +63 -78
  16. package/docs/features/templates.md +4 -1
  17. package/docs/features/watch.md +28 -35
  18. package/docs/getting-started/upgrade-to-v1.md +16 -0
  19. package/docs/guide/guide.md +10 -22
  20. package/index.js +29 -2
  21. package/package.json +62 -62
  22. package/src/BaseElement.js +637 -632
  23. package/src/StyledElement.js +104 -104
  24. package/src/TemplateElement.js +53 -58
  25. package/src/dom-parts/AttributePart.js +116 -116
  26. package/src/dom-parts/ChildNodePart.js +288 -288
  27. package/src/dom-parts/NodePart.js +27 -27
  28. package/src/dom-parts/Part.js +6 -6
  29. package/src/dom-parts/TemplatePart.js +119 -119
  30. package/src/dom-parts/TemplateResult.js +274 -267
  31. package/src/dom-parts/directives.js +105 -48
  32. package/src/dom-parts/html.js +2 -2
  33. package/src/dom-parts/render.js +6 -1
  34. package/src/util/AttributeParser.js +47 -47
  35. package/src/util/DOMHelper.js +190 -191
  36. package/src/util/Directive.js +31 -24
  37. package/src/util/GlobalStylesStore.js +56 -49
  38. package/src/util/SerializeStateHelper.js +56 -56
  39. package/src/util/Store.js +174 -174
  40. package/src/util/defineElement.js +5 -5
  41. package/src/util/toString.js +5 -5
@@ -15,14 +15,14 @@ export const DOCUMENT_FRAGMENT_NODE = 11;
15
15
  * @return {number[]}
16
16
  */
17
17
  export const getNodePath = (node) => {
18
- const path = [];
19
- let parentNode = node.parentNode;
20
- while (parentNode) {
21
- path.push([...parentNode.childNodes].indexOf(node));
22
- node = parentNode;
23
- parentNode = node.parentNode;
24
- }
25
- return path;
18
+ const path = [];
19
+ let parentNode = node.parentNode;
20
+ while (parentNode) {
21
+ path.push([...parentNode.childNodes].indexOf(node));
22
+ node = parentNode;
23
+ parentNode = node.parentNode;
24
+ }
25
+ return path;
26
26
  };
27
27
 
28
28
  /**
@@ -31,14 +31,14 @@ export const getNodePath = (node) => {
31
31
  * @returns {Node[]}
32
32
  */
33
33
  export function getAllParentNodes(domNode) {
34
- let node = domNode.parentElement;
35
- let parents = [];
36
- while (node) {
37
- parents.unshift(node); // adds node to the beginning of parents
38
- node = node.parentElement;
39
- }
40
-
41
- return parents;
34
+ let node = domNode.parentElement;
35
+ let parents = [];
36
+ while (node) {
37
+ parents.unshift(node); // adds node to the beginning of parents
38
+ node = node.parentElement;
39
+ }
40
+
41
+ return parents;
42
42
  }
43
43
 
44
44
  /**
@@ -48,16 +48,16 @@ export function getAllParentNodes(domNode) {
48
48
  * @returns {ShadowRoot | HTMLElement}
49
49
  */
50
50
  export function getShadowParentOrBody(element) {
51
- if (element instanceof ShadowRoot) {
52
- return element;
53
- }
54
-
55
- while (element.parentNode && (element = element.parentNode)) {
56
- if (element instanceof ShadowRoot) {
57
- return element;
58
- }
59
- }
60
- return document.body;
51
+ if (element instanceof ShadowRoot) {
52
+ return element;
53
+ }
54
+
55
+ while (element.parentNode && (element = element.parentNode)) {
56
+ if (element instanceof ShadowRoot) {
57
+ return element;
58
+ }
59
+ }
60
+ return document.body;
61
61
  }
62
62
 
63
63
  // TODO: add function for getClosestParentOfNodeType('custom-element')
@@ -68,11 +68,11 @@ export function getShadowParentOrBody(element) {
68
68
  * @returns {Node}
69
69
  */
70
70
  export function getClosestParentCustomElementNode(domNode) {
71
- const customElementParents = getAllParentNodes(domNode).filter((node) => {
72
- return node.localName && node.localName.indexOf('-') !== -1;
73
- });
71
+ const customElementParents = getAllParentNodes(domNode).filter((node) => {
72
+ return node.localName && node.localName.indexOf('-') !== -1;
73
+ });
74
74
 
75
- return customElementParents.pop();
75
+ return customElementParents.pop();
76
76
  }
77
77
 
78
78
  /**
@@ -82,17 +82,17 @@ export function getClosestParentCustomElementNode(domNode) {
82
82
  * @return {Node[]}
83
83
  */
84
84
  export function getAllElementChildren(domNode, instanceOfFilter = HTMLElement) {
85
- const nodeIterator = document.createNodeIterator(domNode, NodeFilter.SHOW_ELEMENT, (node) =>
86
- node.nodeName.includes('-') && node instanceof instanceOfFilter
87
- ? NodeFilter.FILTER_ACCEPT
88
- : NodeFilter.FILTER_REJECT,
89
- );
90
- const customElements = [];
91
- let currentNode;
92
- while ((currentNode = nodeIterator.nextNode())) {
93
- customElements.push(currentNode);
94
- }
95
- return customElements;
85
+ const nodeIterator = document.createNodeIterator(domNode, NodeFilter.SHOW_ELEMENT, (node) =>
86
+ node.nodeName.includes('-') && node instanceof instanceOfFilter
87
+ ? NodeFilter.FILTER_ACCEPT
88
+ : NodeFilter.FILTER_REJECT,
89
+ );
90
+ const customElements = [];
91
+ let currentNode;
92
+ while ((currentNode = nodeIterator.nextNode())) {
93
+ customElements.push(currentNode);
94
+ }
95
+ return customElements;
96
96
  }
97
97
 
98
98
  /**
@@ -102,9 +102,9 @@ export function getAllElementChildren(domNode, instanceOfFilter = HTMLElement) {
102
102
  * @returns {boolean}
103
103
  */
104
104
  export function isOfSameNodeType(nodeA, nodeB) {
105
- if (!nodeA && !nodeA.localName) return false;
106
- if (!nodeB && !nodeB.localName) return false;
107
- return nodeA.localName === nodeB.localName;
105
+ if (!nodeA && !nodeA.localName) return false;
106
+ if (!nodeB && !nodeB.localName) return false;
107
+ return nodeA.localName === nodeB.localName;
108
108
  }
109
109
 
110
110
  /**
@@ -112,7 +112,7 @@ export function isOfSameNodeType(nodeA, nodeB) {
112
112
  * @returns {boolean}
113
113
  */
114
114
  export const supportsAdoptingStyleSheets = () => {
115
- return 'adoptedStyleSheets' in Document.prototype && 'replace' in CSSStyleSheet.prototype;
115
+ return 'adoptedStyleSheets' in Document.prototype && 'replace' in CSSStyleSheet.prototype;
116
116
  };
117
117
 
118
118
  /**
@@ -121,17 +121,17 @@ export const supportsAdoptingStyleSheets = () => {
121
121
  * @returns {boolean}
122
122
  */
123
123
  export const hasChildNodes = (node) => {
124
- const childNodesLength = node.childNodes.length;
124
+ const childNodesLength = node.childNodes.length;
125
125
 
126
- if (childNodesLength === 0) return false;
126
+ if (childNodesLength === 0) return false;
127
127
 
128
- if (childNodesLength === 1 && node.firstChild.nodeType === 3 && node.firstChild.textContent.trim() === '')
129
- return false;
128
+ if (childNodesLength === 1 && node.firstChild.nodeType === 3 && node.firstChild.textContent.trim() === '')
129
+ return false;
130
130
 
131
- // children will not contain text nodes...
132
- if (childNodesLength > 0) return true;
131
+ // children will not contain text nodes...
132
+ if (childNodesLength > 0) return true;
133
133
 
134
- // TODO: check if more than 1 child nodes, but all childnodes are whitespace text ndoes?!
134
+ // TODO: check if more than 1 child nodes, but all childnodes are whitespace text ndoes?!
135
135
  };
136
136
 
137
137
  /**
@@ -140,9 +140,9 @@ export const hasChildNodes = (node) => {
140
140
  * @returns {HTMLElement}
141
141
  */
142
142
  export const convertStringToHTML = (template) => {
143
- const parser = new DOMParser();
144
- const document = parser.parseFromString(template.toString(), 'text/html');
145
- return document.body;
143
+ const parser = new DOMParser();
144
+ const document = parser.parseFromString(template.toString(), 'text/html');
145
+ return document.body;
146
146
  };
147
147
 
148
148
  /**
@@ -152,10 +152,9 @@ export const convertStringToHTML = (template) => {
152
152
  * @return {DocumentFragment}
153
153
  */
154
154
  export const convertStringToTemplate = (string) => {
155
- const template = globalThis.document?.createElement('template');
156
- // TODO: ensure that SSRed and CSRed templates have the same amount of whitespace text nodes
157
- template.innerHTML = string.trimStart().trimEnd();
158
- return template.content;
155
+ const template = globalThis.document?.createElement('template');
156
+ template.innerHTML = string;
157
+ return template.content;
159
158
  };
160
159
 
161
160
  /**
@@ -164,25 +163,25 @@ export const convertStringToTemplate = (string) => {
164
163
  * @param {Element} domElement - The existing DOM node
165
164
  */
166
165
  export const diffAttributes = function (templateElement, domElement) {
167
- // remove or update previous attributes
168
- for (/** @type {Attr} */ const attribute of Array.from(domElement.attributes)) {
169
- if (templateElement.hasAttribute(attribute.name)) {
170
- const oldValue = attribute.value;
171
- const newValue = templateElement.getAttribute(attribute.name) || '';
172
- if (oldValue !== newValue) {
173
- domElement.setAttribute(attribute.name, newValue);
174
- }
175
- } else {
176
- domElement.removeAttribute(attribute.name);
177
- }
178
- }
179
-
180
- // set newly added attributes
181
- for (/** @type {Attr} */ const attribute of Array.from(templateElement.attributes)) {
182
- if (!domElement.hasAttribute(attribute.name)) {
183
- domElement.setAttribute(attribute.name, attribute.value);
184
- }
185
- }
166
+ // remove or update previous attributes
167
+ for (/** @type {Attr} */ const attribute of Array.from(domElement.attributes)) {
168
+ if (templateElement.hasAttribute(attribute.name)) {
169
+ const oldValue = attribute.value;
170
+ const newValue = templateElement.getAttribute(attribute.name) || '';
171
+ if (oldValue !== newValue) {
172
+ domElement.setAttribute(attribute.name, newValue);
173
+ }
174
+ } else {
175
+ domElement.removeAttribute(attribute.name);
176
+ }
177
+ }
178
+
179
+ // set newly added attributes
180
+ for (/** @type {Attr} */ const attribute of Array.from(templateElement.attributes)) {
181
+ if (!domElement.hasAttribute(attribute.name)) {
182
+ domElement.setAttribute(attribute.name, attribute.value);
183
+ }
184
+ }
186
185
  };
187
186
 
188
187
  /**
@@ -192,117 +191,117 @@ export const diffAttributes = function (templateElement, domElement) {
192
191
  * @param {(Node) => Boolean} shouldDiffChildNodes
193
192
  */
194
193
  export const diffNodes = function (templateNode, domNode, shouldDiffChildNodes = (templateChildNode) => true) {
195
- // If the DOM node should be empty, remove all child nodes
196
- if (hasChildNodes(domNode) && !hasChildNodes(templateNode)) {
197
- domNode.replaceChildren();
198
- return;
199
- }
200
-
201
- // If the DOM node is empty, but should have children, add child nodes from the template node
202
- if (!hasChildNodes(domNode) && hasChildNodes(templateNode)) {
203
- domNode.replaceChildren(...templateNode.childNodes);
204
- return;
205
- }
206
-
207
- const templateChildNodes = [...templateNode.childNodes];
208
- const domChildNodes = [...domNode.childNodes];
209
-
210
- // Diff each node in the child node lists
211
- let length = Math.max(templateChildNodes.length, domChildNodes.length);
212
- for (let index = 0; index < length; index++) {
213
- const templateChildNode = templateChildNodes[index];
214
- const domChildNode = domChildNodes[index];
215
-
216
- // If the DOM node doesn't exist, append/copy the template node
217
- if (!domChildNode) {
218
- domNode.appendChild(templateChildNode);
219
- continue;
220
- }
221
-
222
- // If the template node doesn't exist, remove the node in the DOM
223
- if (!templateChildNode) {
224
- domNode.removeChild(domChildNode);
225
- continue;
226
- }
227
-
228
- // If DOM node is equal to the template node, don't do anything
229
- if (domChildNode.isEqualNode(templateChildNode)) {
230
- continue;
231
- }
232
-
233
- // the following two checks don't have to be correct, but it could bring as back to the fast path
234
- // in the worst case we still have to compare and swap all the elements
235
- // but in the best case we find the one element that should actually be inserted or removed
236
- // and from there on, every node that comes after should be equal again
237
-
238
- // fast path for removing DOM nodes - we delete the node now instead of at the end
239
- if (domNode.childNodes.length > templateNode.childNodes.length) {
240
- domChildNodes.splice(index, 1);
241
- domNode.removeChild(domChildNode);
242
- // because domChildNodes will get shorter by splicing it, everything moves up by one
243
- // so the (actual) next element will have the same index now as we currently have
244
- // therefore we have to adjust our counters
245
- length--;
246
- index--;
247
-
248
- // we might have corrected everything and the parent nodes could be equal again
249
- // if so, we can skip the rest of the child node checks
250
- if (domNode.childNodes.length === templateNode.childNodes.length && domNode.isEqualNode(templateNode)) {
251
- return;
252
- }
253
-
254
- continue;
255
- }
256
-
257
- // fast path for adding DOM nodes - we insert the node now instead of at the end
258
- // but NOT if we already are at the end of the list of child nodes
259
- if (index !== domNode.childNodes.length - 1 && domNode.childNodes.length < templateNode.childNodes.length) {
260
- domChildNodes.splice(index, 0, templateChildNode);
261
- domNode.insertBefore(templateChildNode, domChildNode);
262
-
263
- // we might have corrected everything and the parent nodes could be equal again
264
- // if so, we can skip the rest of the child node checks
265
- if (domNode.childNodes.length === templateNode.childNodes.length && domNode.isEqualNode(templateNode)) {
266
- return;
267
- }
268
-
269
- continue;
270
- }
271
-
272
- // If node types are not the same, replace the DOM node with the template node
273
- if (templateChildNode.nodeType !== domChildNode.nodeType) {
274
- domNode.replaceChild(templateChildNode, domChildNode);
275
- continue;
276
- }
277
-
278
- // If the node is a Text node or a Comment node, update it
279
- if (domChildNode.nodeType === TEXT_NODE || domChildNode.nodeType === COMMENT_NODE) {
280
- domChildNode.textContent = templateChildNode.textContent;
281
- continue;
282
- }
283
-
284
- // If the element tag names are not the same, replace the DOM node with the template node
285
- if (templateChildNode.nodeType === ELEMENT_NODE && templateChildNode.tagName !== domChildNode.tagName) {
286
- domNode.replaceChild(templateChildNode, domChildNode);
287
- continue;
288
- }
289
-
290
- // If the node is an SVG element, don't even think about diffing it, just replace it
291
- if (templateChildNode.nodeType === ELEMENT_NODE && templateChildNode.tagName === 'SVG') {
292
- domNode.replaceChild(templateChildNode, domChildNode);
293
- continue;
294
- }
295
-
296
- // If the node is an Element node, diff the attributes
297
- if (templateChildNode.nodeType === ELEMENT_NODE) {
298
- diffAttributes(templateChildNode, domChildNode);
299
- }
300
-
301
- // If there are child nodes, diff them recursively
302
- if (templateChildNode.hasChildNodes()) {
303
- if (shouldDiffChildNodes(templateChildNode)) {
304
- diffNodes(templateChildNode, domChildNode);
305
- }
306
- }
307
- }
194
+ // If the DOM node should be empty, remove all child nodes
195
+ if (hasChildNodes(domNode) && !hasChildNodes(templateNode)) {
196
+ domNode.replaceChildren();
197
+ return;
198
+ }
199
+
200
+ // If the DOM node is empty, but should have children, add child nodes from the template node
201
+ if (!hasChildNodes(domNode) && hasChildNodes(templateNode)) {
202
+ domNode.replaceChildren(...templateNode.childNodes);
203
+ return;
204
+ }
205
+
206
+ const templateChildNodes = [...templateNode.childNodes];
207
+ const domChildNodes = [...domNode.childNodes];
208
+
209
+ // Diff each node in the child node lists
210
+ let length = Math.max(templateChildNodes.length, domChildNodes.length);
211
+ for (let index = 0; index < length; index++) {
212
+ const templateChildNode = templateChildNodes[index];
213
+ const domChildNode = domChildNodes[index];
214
+
215
+ // If the DOM node doesn't exist, append/copy the template node
216
+ if (!domChildNode) {
217
+ domNode.appendChild(templateChildNode);
218
+ continue;
219
+ }
220
+
221
+ // If the template node doesn't exist, remove the node in the DOM
222
+ if (!templateChildNode) {
223
+ domNode.removeChild(domChildNode);
224
+ continue;
225
+ }
226
+
227
+ // If DOM node is equal to the template node, don't do anything
228
+ if (domChildNode.isEqualNode(templateChildNode)) {
229
+ continue;
230
+ }
231
+
232
+ // the following two checks don't have to be correct, but it could bring as back to the fast path
233
+ // in the worst case we still have to compare and swap all the elements
234
+ // but in the best case we find the one element that should actually be inserted or removed
235
+ // and from there on, every node that comes after should be equal again
236
+
237
+ // fast path for removing DOM nodes - we delete the node now instead of at the end
238
+ if (domNode.childNodes.length > templateNode.childNodes.length) {
239
+ domChildNodes.splice(index, 1);
240
+ domNode.removeChild(domChildNode);
241
+ // because domChildNodes will get shorter by splicing it, everything moves up by one
242
+ // so the (actual) next element will have the same index now as we currently have
243
+ // therefore we have to adjust our counters
244
+ length--;
245
+ index--;
246
+
247
+ // we might have corrected everything and the parent nodes could be equal again
248
+ // if so, we can skip the rest of the child node checks
249
+ if (domNode.childNodes.length === templateNode.childNodes.length && domNode.isEqualNode(templateNode)) {
250
+ return;
251
+ }
252
+
253
+ continue;
254
+ }
255
+
256
+ // fast path for adding DOM nodes - we insert the node now instead of at the end
257
+ // but NOT if we already are at the end of the list of child nodes
258
+ if (index !== domNode.childNodes.length - 1 && domNode.childNodes.length < templateNode.childNodes.length) {
259
+ domChildNodes.splice(index, 0, templateChildNode);
260
+ domNode.insertBefore(templateChildNode, domChildNode);
261
+
262
+ // we might have corrected everything and the parent nodes could be equal again
263
+ // if so, we can skip the rest of the child node checks
264
+ if (domNode.childNodes.length === templateNode.childNodes.length && domNode.isEqualNode(templateNode)) {
265
+ return;
266
+ }
267
+
268
+ continue;
269
+ }
270
+
271
+ // If node types are not the same, replace the DOM node with the template node
272
+ if (templateChildNode.nodeType !== domChildNode.nodeType) {
273
+ domNode.replaceChild(templateChildNode, domChildNode);
274
+ continue;
275
+ }
276
+
277
+ // If the node is a Text node or a Comment node, update it
278
+ if (domChildNode.nodeType === TEXT_NODE || domChildNode.nodeType === COMMENT_NODE) {
279
+ domChildNode.textContent = templateChildNode.textContent;
280
+ continue;
281
+ }
282
+
283
+ // If the element tag names are not the same, replace the DOM node with the template node
284
+ if (templateChildNode.nodeType === ELEMENT_NODE && templateChildNode.tagName !== domChildNode.tagName) {
285
+ domNode.replaceChild(templateChildNode, domChildNode);
286
+ continue;
287
+ }
288
+
289
+ // If the node is an SVG element, don't even think about diffing it, just replace it
290
+ if (templateChildNode.nodeType === ELEMENT_NODE && templateChildNode.tagName === 'SVG') {
291
+ domNode.replaceChild(templateChildNode, domChildNode);
292
+ continue;
293
+ }
294
+
295
+ // If the node is an Element node, diff the attributes
296
+ if (templateChildNode.nodeType === ELEMENT_NODE) {
297
+ diffAttributes(templateChildNode, domChildNode);
298
+ }
299
+
300
+ // If there are child nodes, diff them recursively
301
+ if (templateChildNode.hasChildNodes()) {
302
+ if (shouldDiffChildNodes(templateChildNode)) {
303
+ diffNodes(templateChildNode, domChildNode);
304
+ }
305
+ }
306
+ }
308
307
  };
@@ -1,35 +1,42 @@
1
1
  export class Directive {
2
- /** @type {Element} */
3
- node = undefined;
2
+ /** @type {Element} */
3
+ node = undefined;
4
4
 
5
- /**
6
- * @param {Node} node
7
- */
8
- constructor(node) {
9
- this.node = node;
10
- }
5
+ /**
6
+ * @param {Node} node
7
+ */
8
+ constructor(node) {
9
+ this.node = node;
10
+ }
11
11
 
12
- /**
13
- * @abstract
14
- */
15
- update() {}
12
+ /**
13
+ * @abstract
14
+ */
15
+ update() {}
16
16
 
17
- // TODO: this is somewhat custom... maybe we could set the initial values in the constructor and then simply override the default toString() method?!
18
- /**
19
- * Implement the stringify method and return a string for rendering the directive in SSR mode
20
- * @param {...any} values
21
- * @returns {string}
22
- */
23
- stringify(...values) {
24
- return '';
25
- }
17
+ // TODO: this is somewhat custom... maybe we could set the initial values in the constructor and then simply override the default toString() method?!
18
+ /**
19
+ * Implement the stringify method and return a string for rendering the directive in SSR mode
20
+ * @param {...any} values
21
+ * @returns {string}
22
+ */
23
+ stringify(...values) {
24
+ return '';
25
+ }
26
26
  }
27
27
 
28
28
  /**
29
29
  * @param {Class<Directive>} directiveClass
30
30
  */
31
31
  export const defineDirective = (directiveClass) => {
32
- return (...values) => {
33
- return { directiveClass, values };
34
- };
32
+ return (...values) => {
33
+ return {
34
+ directiveClass,
35
+ values,
36
+ toString: () => {
37
+ const directive = new directiveClass();
38
+ return directive.stringify(...values);
39
+ },
40
+ };
41
+ };
35
42
  };
@@ -4,63 +4,70 @@ import { Store } from './Store.js';
4
4
  * @property {CSSStyleSheet[]} cssStyleSheets
5
5
  */
6
6
  class GlobalStylesStore extends Store {
7
- /** @type {Map<Node, CSSStyleSheet>} */
8
- globalStyleSheetsCache = new WeakMap();
7
+ /** @type {Map<Node, CSSStyleSheet>} */
8
+ globalStyleSheetsCache = new WeakMap();
9
9
 
10
- constructor() {
11
- super();
12
- if (globalThis.elementJsConfig?.observeGlobalStyles) {
13
- const mutationObserver = new MutationObserver((mutationRecord) => {
14
- if (!mutationRecord[0]) return;
15
- const filteredNodes = Array.from(mutationRecord[0].addedNodes).filter(
16
- (node) => node.tagName === 'STYLE' || node.tagName === 'LINK',
17
- );
18
- if (filteredNodes && filteredNodes[0] && filteredNodes[0].tagName === 'LINK') {
19
- filteredNodes[0].onload = () => {
20
- this.requestUpdate();
21
- };
22
- } else if (filteredNodes && filteredNodes[0] && filteredNodes[0].tagName === 'STYLE') {
23
- this.requestUpdate();
24
- }
25
- });
26
- mutationObserver.observe(globalThis.document, { subtree: true, childList: true });
27
- }
28
- }
10
+ constructor() {
11
+ super();
12
+ if (globalThis.elementJsConfig?.observeGlobalStyles) {
13
+ const mutationObserver = new MutationObserver((mutationRecord) => {
14
+ if (!mutationRecord[0]) return;
15
+ const filteredNodes = Array.from(mutationRecord[0].addedNodes).filter(
16
+ (node) => node.tagName === 'STYLE' || node.tagName === 'LINK',
17
+ );
18
+ if (filteredNodes && filteredNodes[0] && filteredNodes[0].tagName === 'LINK') {
19
+ filteredNodes[0].onload = () => {
20
+ this.requestUpdate();
21
+ };
22
+ } else if (filteredNodes && filteredNodes[0] && filteredNodes[0].tagName === 'STYLE') {
23
+ this.requestUpdate();
24
+ }
25
+ });
26
+ mutationObserver.observe(globalThis.document, { subtree: true, childList: true });
27
+ }
28
+ }
29
29
 
30
- getGlobalStyleSheets(selector) {
31
- /** @type {CSSStyleSheet[]}*/
32
- const cssStyleSheets = [];
30
+ getGlobalStyleSheets(selector) {
31
+ /** @type {CSSStyleSheet[]}*/
32
+ const cssStyleSheets = [];
33
33
 
34
- if (selector === false) return cssStyleSheets;
34
+ if (selector === false) return cssStyleSheets;
35
35
 
36
- if (typeof selector === 'string') {
37
- selector = [selector];
38
- }
36
+ if (typeof selector === 'string') {
37
+ selector = [selector];
38
+ }
39
39
 
40
- if (selector === true || selector.includes('document')) {
41
- cssStyleSheets.push(...globalThis.document?.adoptedStyleSheets);
42
- }
40
+ if (selector === true || selector.includes('document')) {
41
+ cssStyleSheets.push(...globalThis.document?.adoptedStyleSheets);
42
+ }
43
43
 
44
- Array.from(globalThis.document?.styleSheets).map((styleSheet) => {
45
- if (Array.isArray(selector) && !selector.some((cssSelector) => styleSheet.ownerNode.matches(cssSelector))) {
46
- return;
47
- }
44
+ Array.from(globalThis.document?.styleSheets).map((styleSheet) => {
45
+ if (Array.isArray(selector) && !selector.some((cssSelector) => styleSheet.ownerNode.matches(cssSelector))) {
46
+ return;
47
+ }
48
48
 
49
- // TODO: this will always be null as we never set anything to the cache...
50
- let cssStyleSheet = this.globalStyleSheetsCache.get(styleSheet.ownerNode);
51
- if (!cssStyleSheet) {
52
- cssStyleSheet = new CSSStyleSheet();
53
- if (styleSheet.ownerNode.tagName === 'STYLE') {
54
- cssStyleSheet.replaceSync(styleSheet.ownerNode.textContent);
55
- } else if (styleSheet.ownerNode.tagName === 'LINK') {
56
- Array.from(styleSheet.cssRules).map((rule) => cssStyleSheet.insertRule(rule.cssText));
57
- }
58
- }
59
- cssStyleSheets.push(cssStyleSheet);
60
- });
49
+ // TODO: this will always be null as we never set anything to the cache...
50
+ let cssStyleSheet = this.globalStyleSheetsCache.get(styleSheet.ownerNode);
51
+ if (!cssStyleSheet) {
52
+ cssStyleSheet = new CSSStyleSheet();
53
+ if (styleSheet.ownerNode.tagName === 'STYLE') {
54
+ cssStyleSheet.replaceSync(styleSheet.ownerNode.textContent);
55
+ } else if (styleSheet.ownerNode.tagName === 'LINK') {
56
+ try {
57
+ Array.from(styleSheet?.cssRules ?? []).map((rule) => cssStyleSheet.insertRule(rule.cssText));
58
+ } catch (e) {
59
+ console.error(
60
+ 'GlobalStylesStore: cannot read cssRules. Maybe add crossorigin="anonymous" to your style link?',
61
+ e,
62
+ );
63
+ }
64
+ }
65
+ }
66
+ cssStyleSheets.push(cssStyleSheet);
67
+ });
61
68
 
62
- return cssStyleSheets;
63
- }
69
+ return cssStyleSheets;
70
+ }
64
71
  }
65
72
 
66
73
  export const globalStylesStore = new GlobalStylesStore();