mi-element 0.9.3 → 0.9.4

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 (3) hide show
  1. package/dist/html.js +32 -20
  2. package/package.json +1 -1
  3. package/src/html.js +27 -9
package/dist/html.js CHANGED
@@ -33,9 +33,9 @@ const unsafeHtml = str => new UnsafeHtml(str), escMap = {
33
33
  '>': '>',
34
34
  '"': '"',
35
35
  "'": '''
36
- }, esc = string => string.replace(/[&<>"']/g, tag => escMap[tag]), escHtml = string => string instanceof UnsafeHtml ? string : unsafeHtml(esc('' + string)), escValue = any => {
36
+ }, esc = string => string.replace(/[&<>"']/g, tag => escMap[tag]), escHtml = string => string instanceof UnsafeHtml ? string : unsafeHtml(esc('' + string)), OBJECT = 'object', FUNCTION = 'function', escValue = any => {
37
37
  if (any instanceof UnsafeHtml) return any;
38
- if ([ 'object', 'function' ].includes(typeof any)) {
38
+ if ([ OBJECT, FUNCTION ].includes(typeof any)) {
39
39
  const key = globalRenderCache.set(any);
40
40
  return unsafeHtml(key);
41
41
  }
@@ -52,26 +52,38 @@ function render(node, template, handlers = {}) {
52
52
  return refs;
53
53
  }
54
54
 
55
+ const REF = 'ref', REF_Q = '[ref]';
56
+
55
57
  function renderAttrs(node, handlers = {}, refs = {}) {
56
- if (node.nodeType === Node.ELEMENT_NODE) for (let attr of node.attributes) {
57
- const startsWith = attr.name[0], name = attr.name.slice(1);
58
- let rm = 0;
59
- if ('?' === startsWith) toJson(attr.value) ? node.setAttribute(name, '') : node.removeAttribute(name),
60
- rm = 1; else if ('...' === attr.name) {
61
- const obj = globalRenderCache.get(attr.value);
62
- if (obj && 'object' == typeof obj) for (const [k, v] of Object.entries(obj)) node[k] = v;
63
- rm = 1;
64
- } else if ('.' === startsWith) node[name] = globalRenderCache.get(attr.value) ?? attr.value,
65
- rm = 1; else if ('@' === startsWith) {
66
- const handlerName = attr.value, fn = globalRenderCache.get(handlerName);
67
- fn ? node.addEventListener(name, e => fn(e)) : 'function' == typeof handlers[handlerName] && node.addEventListener(name, e => handlers[handlerName](e)),
68
- rm = 1;
69
- } else 'ref' === attr.name && (refs[attr.value] = node, rm = 1);
70
- rm && requestAnimationFrame(() => {
71
- node.removeAttribute(attr.name);
72
- });
58
+ if (node.nodeType === Node.ELEMENT_NODE) {
59
+ const rmFns = [];
60
+ for (let attr of node.attributes) {
61
+ const startsWith = attr.name[0], name = attr.name.slice(1);
62
+ let rm = 0;
63
+ if ('?' === startsWith) toJson(attr.value) ? node.setAttribute(name, '') : node.removeAttribute(name),
64
+ rm = 1; else if ('...' === attr.name) {
65
+ const obj = globalRenderCache.get(attr.value);
66
+ if (obj && typeof obj === OBJECT) for (const [k, v] of Object.entries(obj)) node[k] = v;
67
+ rm = 1;
68
+ } else if ('.' === startsWith) node[name] = globalRenderCache.get(attr.value) ?? attr.value,
69
+ rm = 1; else if ('@' === startsWith) {
70
+ const handlerName = attr.value, fn = globalRenderCache.get(handlerName);
71
+ fn ? node.addEventListener(name, e => fn(e)) : typeof handlers[handlerName] === FUNCTION && node.addEventListener(name, e => handlers[handlerName](e)),
72
+ rm = 1;
73
+ } else attr.name === REF && (refs[attr.value] = node, rm = 1);
74
+ rm && rmFns.push([ node, attr.name ]);
75
+ }
76
+ rmFns.forEach(([node, name]) => node.removeAttribute(name));
77
+ }
78
+ if (customElements.get(node.localName)) {
79
+ const q = node.querySelectorAll(REF_Q);
80
+ for (let el of q) {
81
+ const refName = el.getAttribute(REF);
82
+ refName && !refs[refName] && (refs[refName] = el);
83
+ }
84
+ return refs;
73
85
  }
74
- if (!node.children?.length || customElements.get(node.localName)) return refs;
86
+ if (!node.children?.length) return refs;
75
87
  for (let child of Array.from(node.children)) renderAttrs(child, handlers, refs);
76
88
  return refs;
77
89
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mi-element",
3
- "version": "0.9.3",
3
+ "version": "0.9.4",
4
4
  "description": "Build lightweight reactive micro web-components",
5
5
  "keywords": [],
6
6
  "homepage": "https://github.com/commenthol/mi-element/tree/main/packages/mi-element#readme",
package/src/html.js CHANGED
@@ -77,6 +77,9 @@ export const escHtml = (string) =>
77
77
  // @ts-expect-error
78
78
  string instanceof UnsafeHtml ? string : unsafeHtml(esc('' + string))
79
79
 
80
+ const OBJECT = 'object'
81
+ const FUNCTION = 'function'
82
+
80
83
  /**
81
84
  * escape any value for HTML context; objects and functions are stored in the render cache
82
85
  * @param {any} any
@@ -87,7 +90,7 @@ const escValue = (any) => {
87
90
  // @ts-expect-error
88
91
  return any
89
92
  }
90
- if (['object', 'function'].includes(typeof any)) {
93
+ if ([OBJECT, FUNCTION].includes(typeof any)) {
91
94
  const key = globalRenderCache.set(any)
92
95
  return unsafeHtml(key)
93
96
  }
@@ -136,6 +139,9 @@ export function render(node, template, handlers = {}) {
136
139
  return refs
137
140
  }
138
141
 
142
+ const REF = 'ref'
143
+ const REF_Q = '[ref]'
144
+
139
145
  /**
140
146
  * Post-processing of rendered nodes to handle special attributes:
141
147
  *
@@ -156,6 +162,7 @@ export function render(node, template, handlers = {}) {
156
162
  */
157
163
  export function renderAttrs(node, handlers = {}, refs = {}) {
158
164
  if (node.nodeType === Node.ELEMENT_NODE) {
165
+ const rmFns = []
159
166
  for (let attr of node.attributes) {
160
167
  const startsWith = attr.name[0]
161
168
  const name = attr.name.slice(1)
@@ -171,7 +178,7 @@ export function renderAttrs(node, handlers = {}, refs = {}) {
171
178
  } else if (attr.name === '...') {
172
179
  // spread attribute
173
180
  const obj = globalRenderCache.get(attr.value)
174
- if (obj && typeof obj === 'object') {
181
+ if (obj && typeof obj === OBJECT) {
175
182
  for (const [k, v] of Object.entries(obj)) {
176
183
  node[k] = v
177
184
  }
@@ -187,25 +194,36 @@ export function renderAttrs(node, handlers = {}, refs = {}) {
187
194
  const fn = globalRenderCache.get(handlerName)
188
195
  if (fn) {
189
196
  node.addEventListener(name, (e) => fn(e))
190
- } else if (typeof handlers[handlerName] === 'function') {
197
+ } else if (typeof handlers[handlerName] === FUNCTION) {
191
198
  node.addEventListener(name, (e) => handlers[handlerName](e))
192
199
  }
193
200
  rm = 1
194
- } else if (attr.name === 'ref') {
201
+ } else if (attr.name === REF) {
195
202
  // element reference - remove as well to prevent collection by other processors
196
203
  const refName = attr.value
197
204
  refs[refName] = node
198
205
  rm = 1
199
206
  }
200
207
  if (rm) {
201
- requestAnimationFrame(() => {
202
- node.removeAttribute(attr.name)
203
- })
208
+ rmFns.push([node, attr.name])
204
209
  }
205
210
  }
211
+ // @ts-expect-error
212
+ rmFns.forEach(([node, name]) => node.removeAttribute(name))
213
+ }
214
+ // early abort if custom element but resolve slotted refs
215
+ if (customElements.get(node.localName)) {
216
+ const q = node.querySelectorAll(REF_Q)
217
+ for (let el of q) {
218
+ const refName = el.getAttribute(REF)
219
+ if (refName && !refs[refName]) {
220
+ refs[refName] = el
221
+ }
222
+ }
223
+ return refs
206
224
  }
207
- // early abort if no children or custom element
208
- if (!node.children?.length || customElements.get(node.localName)) {
225
+ // early abort if no children
226
+ if (!node.children?.length) {
209
227
  return refs
210
228
  }
211
229
  for (let child of Array.from(node.children)) {