@webtides/element-js 0.7.3 → 1.0.0-alpha.2

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.
@@ -1,78 +0,0 @@
1
- import { camelToDash, decodeAttribute } from '../../../util/AttributeParser.js';
2
- import { Part } from './html.js';
3
-
4
- /**
5
- * Maps a list of classes to an element from an object.
6
- * The keys will become the classes when the value is truthy.
7
- * @param {Object.<string, string | boolean | number>} map
8
- * @returns {string} string of classes joined together
9
- */
10
- const classMap = (map) => {
11
- const classes = [];
12
- for (const [key, value] of Object.entries(map)) {
13
- if (value) classes.push(key);
14
- }
15
- return classes.join(' ');
16
- };
17
-
18
- /**
19
- * Maps a list of styles to an element from an object.
20
- * The keys will become the properties and the values will become the values.
21
- * @param {Object.<string, string | undefined | null>} map
22
- * @returns {string} string of styles joined together
23
- */
24
- const styleMap = (map) => {
25
- const styles = [];
26
- for (const [key, value] of Object.entries(map)) {
27
- if (value) styles.push(`${key}:${value};`);
28
- }
29
- return styles.join(' ');
30
- };
31
-
32
- /**
33
- * Renders one of two template parts based on a condition.
34
- * @param {boolean} condition
35
- * @param {TemplateLiteral | string} trueCase
36
- * @param {TemplateLiteral | string} [falseCase]
37
- * @returns {TemplateLiteral | string}
38
- */
39
- const when = (condition, trueCase, falseCase) => {
40
- return condition ? trueCase : falseCase;
41
- };
42
-
43
- /**
44
- * Chooses and evaluates a template part from a map of cases based on matching the given value to a case.
45
- * @param {string} value
46
- * @param {Object.<string, TemplateLiteral | string>} cases
47
- * @param {TemplateLiteral | string} [defaultCase]
48
- * @returns {TemplateLiteral | string}
49
- */
50
- const choose = (value, cases, defaultCase) => {
51
- return cases[value] || defaultCase;
52
- };
53
-
54
- /**
55
- * Renders a given string as HTML instead of text
56
- * @param {string} string
57
- * @returns {function(): string}
58
- */
59
- const unsafeHTML = (string) => {
60
- return () => `${decodeAttribute(string)}`;
61
- };
62
-
63
- /**
64
- * Renders multiple attributes as key="value" pairs from a map of attributes
65
- * @param {Object.<string, any>} attributes
66
- * @returns {function(): string}
67
- */
68
- const spreadAttributes = (attributes) => {
69
- return () => {
70
- return Object.keys(attributes)
71
- .map((key) => {
72
- return `${camelToDash(key)}='${new Part(attributes[key])}'`;
73
- })
74
- .join(' ');
75
- };
76
- };
77
-
78
- export { classMap, styleMap, when, choose, unsafeHTML, spreadAttributes };
@@ -1,65 +0,0 @@
1
- import { encodeAttribute } from '../../../util/AttributeParser.js';
2
- import { unsafeHTML, spreadAttributes } from './directives.js';
3
-
4
- class Part {
5
- constructor(value) {
6
- this.value = value;
7
- }
8
-
9
- toString() {
10
- if (typeof this.value !== 'string') {
11
- return encodeAttribute(JSON.stringify(this.value));
12
- }
13
- return encodeAttribute(this.value);
14
- }
15
- }
16
-
17
- class TemplatePart extends Part {
18
- $$templatePart = true;
19
-
20
- constructor(value) {
21
- super(value?.$$templatePart ? value.value : value);
22
- }
23
-
24
- toString() {
25
- if (this.value === null || this.value === undefined) {
26
- return '';
27
- } else if (this.value === '') {
28
- return '';
29
- } else if (Array.isArray(this.value)) {
30
- return this.value.map((row) => new TemplatePart(row).toString()).join('');
31
- } else if (this.value?.$$templateLiteral) {
32
- return this.value.toString();
33
- } else if (typeof this.value === 'function') {
34
- // A directive
35
- return this.value(this);
36
- }
37
- return super.toString();
38
- }
39
- }
40
-
41
- class TemplateLiteral {
42
- $$templateLiteral = true;
43
-
44
- constructor(strings, ...values) {
45
- let result = [];
46
- for (let i = 0; i < strings.length; i++) {
47
- result.push(strings[i]);
48
-
49
- if (i < values.length) {
50
- result.push(new TemplatePart(values[i]));
51
- }
52
- }
53
- this.result = result;
54
- }
55
-
56
- toString() {
57
- return `${this.result.join('')}`;
58
- }
59
- }
60
-
61
- const html = function (strings, ...values) {
62
- return new TemplateLiteral(strings, ...values);
63
- };
64
-
65
- export { Part, TemplatePart, TemplateLiteral, html, unsafeHTML, spreadAttributes };
@@ -1,149 +0,0 @@
1
- import { TemplateElement } from '../TemplateElement.js';
2
-
3
- /**
4
- * Parses the given template string and returns a real DOM element.
5
- * @param {string} template
6
- * @returns {HTMLElement}
7
- */
8
- const convertStringToHTML = (template) => {
9
- const parser = new DOMParser();
10
- const document = parser.parseFromString(template.toString(), 'text/html');
11
- return document.body;
12
- };
13
-
14
- /**
15
- * Diff the attributes for a live DOM element against a template DOM element
16
- * @param {Element} templateElement The new template
17
- * @param {Element} domElement The existing DOM node
18
- */
19
- const diffAttributes = function (templateElement, domElement) {
20
- // remove or update previous attributes
21
- for (/** @type {Attr} */ const attribute of Array.from(domElement.attributes)) {
22
- if (templateElement.hasAttribute(attribute.name)) {
23
- const oldValue = attribute.value;
24
- const newValue = templateElement.getAttribute(attribute.name) || '';
25
- if (oldValue !== newValue) {
26
- domElement.setAttribute(attribute.name, newValue);
27
- }
28
- } else {
29
- domElement.removeAttribute(attribute.name);
30
- }
31
- }
32
-
33
- // set newly added attributes
34
- for (/** @type {Attr} */ const attribute of Array.from(templateElement.attributes)) {
35
- if (!domElement.hasAttribute(attribute.name)) {
36
- domElement.setAttribute(attribute.name, attribute.value);
37
- }
38
- }
39
- };
40
-
41
- /**
42
- * Checks whether the given element or node is a TemplateElement or not
43
- * @param {Element|Node} element
44
- * @returns {boolean}
45
- */
46
- const isTemplateElement = (element) => {
47
- const tagName = element.tagName?.toLowerCase() ?? false;
48
- if (!tagName || !tagName.includes('-') || element.shadowRoot) {
49
- return false;
50
- }
51
- return element instanceof TemplateElement;
52
- };
53
-
54
- /**
55
- * Diff a live DOM node against a template DOM node
56
- * @param {Node} templateNode
57
- * @param {Node} domNode
58
- */
59
- const diffNodes = function (templateNode, domNode) {
60
- const templateChildNodes = [...templateNode.childNodes];
61
- const domChildNodes = [...domNode.childNodes];
62
-
63
- // Diff each node in the child node lists
64
- const length = Math.max(templateChildNodes.length, domChildNodes.length);
65
- for (let index = 0; index < length; index++) {
66
- const templateChildNode = templateChildNodes[index];
67
- const domChildNode = domChildNodes[index];
68
-
69
- // If the DOM node doesn't exist, append/copy the template node
70
- if (!domChildNode) {
71
- domNode.appendChild(templateChildNode);
72
- continue;
73
- }
74
-
75
- // If the template node doesn't exist, remove the node in the DOM
76
- if (!templateChildNode) {
77
- domChildNode.parentNode.removeChild(domChildNode);
78
- continue;
79
- }
80
-
81
- // If DOM node is equal to the template node, don't do anything
82
- if (domChildNode.isEqualNode(templateChildNode)) {
83
- continue;
84
- }
85
-
86
- // If node types are not the same, replace the DOM node with the template node
87
- if (templateChildNode.nodeType !== domChildNode.nodeType) {
88
- domChildNode.parentNode.replaceChild(templateChildNode, domChildNode);
89
- continue;
90
- }
91
-
92
- // If the element tag names are not the same, replace the DOM node with the template node
93
- if (templateChildNode.nodeType === 1 && templateChildNode.tagName !== domChildNode.tagName) {
94
- domChildNode.parentNode.replaceChild(templateChildNode, domChildNode);
95
- continue;
96
- }
97
-
98
- // If the node is an SVG element, don't even think about diffing it, just replace it
99
- if (templateChildNode.nodeType === 1 && templateChildNode.tagName === 'SVG') {
100
- domChildNode.parentNode.replaceChild(templateChildNode, domChildNode);
101
- continue;
102
- }
103
-
104
- // If the node is an Element node, diff the attributes
105
- if (templateChildNode.nodeType === 1) {
106
- diffAttributes(templateChildNode, domChildNode);
107
- }
108
-
109
- // If the node is a Text node or a Comment node, update it
110
- if (domChildNode.nodeType === 3 || domChildNode.nodeType === 8) {
111
- domChildNode.textContent = templateChildNode.textContent;
112
- continue;
113
- }
114
-
115
- // If the DOM node should be empty, remove all child nodes
116
- if (domChildNode.hasChildNodes() && !templateChildNode.hasChildNodes()) {
117
- domChildNode.replaceChildren();
118
- continue;
119
- }
120
-
121
- // If the DOM node is empty, but should have children, add child nodes from the template node
122
- if (!domChildNode.hasChildNodes() && templateChildNode.hasChildNodes()) {
123
- domChildNode.replaceChildren(...templateChildNode.childNodes);
124
- continue;
125
- }
126
-
127
- // If there are child nodes, diff them recursively
128
- if (templateChildNode.hasChildNodes()) {
129
- // https://stackoverflow.com/questions/46898739/why-domparser-doesnt-use-the-customelements-registered
130
- // we have to use the domChildNode here because the templateChildNode is not upgraded because it was not
131
- // imported into a document yet and therefore instanceOf will not work...
132
- if (!isTemplateElement(domChildNode)) {
133
- diffNodes(templateChildNode, domChildNode);
134
- }
135
- }
136
- }
137
- };
138
-
139
- /**
140
- * Render a template string into the given DOM node
141
- * @param {string} template
142
- * @param {Node} domNode
143
- */
144
- const render = (template, domNode) => {
145
- const templateNode = convertStringToHTML(template);
146
- diffNodes(templateNode, domNode);
147
- };
148
-
149
- export { render };