@htmlplus/element 3.3.0 → 3.3.1

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/dist/client.js CHANGED
@@ -27,11 +27,19 @@ const dispatch = (target, type, eventInitDict) => {
27
27
  */
28
28
  const on = (target, type, handler, options) => {
29
29
  const element = host(target);
30
- if (type != 'outside') {
30
+ if (type !== 'outside') {
31
31
  return element.addEventListener(type, handler, options);
32
32
  }
33
33
  const callback = (event) => {
34
- !event.composedPath().some((item) => item == element) && handler(event);
34
+ const has = event.composedPath().some((item) => item === element);
35
+ if (has)
36
+ return;
37
+ if (typeof handler === 'function') {
38
+ handler(event);
39
+ }
40
+ else {
41
+ handler.handleEvent(event);
42
+ }
35
43
  };
36
44
  type = 'ontouchstart' in window.document.documentElement ? 'touchstart' : 'click';
37
45
  on(document, type, callback, options);
@@ -48,11 +56,11 @@ const on = (target, type, handler, options) => {
48
56
  */
49
57
  const off = (target, type, handler, options) => {
50
58
  const element = host(target);
51
- if (type != 'outside') {
52
- return element.removeEventListener(type, handler, options);
59
+ if (type !== 'outside') {
60
+ return void element.removeEventListener(type, handler, options);
53
61
  }
54
62
  const index = outsides.findIndex((outside) => {
55
- return outside.element == element && outside.handler == handler && outside.options == options;
63
+ return (outside.element === element && outside.handler === handler && outside.options === options);
56
64
  });
57
65
  const outside = outsides[index];
58
66
  if (!outside)
@@ -71,12 +79,10 @@ const toEvent = (input) => {
71
79
 
72
80
  const updateAttribute = (target, key, value) => {
73
81
  const element = host(target);
74
- if ([undefined, null, false].includes(value)) {
75
- element.removeAttribute(key);
76
- }
77
- else {
78
- element.setAttribute(key, value === true ? '' : value);
82
+ if (value === undefined || value === null || value === false) {
83
+ return void element.removeAttribute(key);
79
84
  }
85
+ element.setAttribute(key, value === true ? '' : String(value));
80
86
  };
81
87
 
82
88
  const symbol = Symbol();
@@ -132,9 +138,10 @@ const classes = (input, smart) => {
132
138
  break;
133
139
  }
134
140
  case 'object': {
135
- const keys = Object.keys(input);
141
+ const obj = input;
142
+ const keys = Object.keys(obj);
136
143
  for (const key of keys) {
137
- const value = input[key];
144
+ const value = obj[key];
138
145
  const name = kebabCase(key);
139
146
  const type = typeOf(value);
140
147
  if (!smart) {
@@ -167,7 +174,7 @@ const merge = (target, ...sources) => {
167
174
  for (const source of sources) {
168
175
  if (!source)
169
176
  continue;
170
- if (typeOf(source) != 'object') {
177
+ if (typeOf(source) !== 'object') {
171
178
  target = source;
172
179
  continue;
173
180
  }
@@ -263,13 +270,15 @@ const isCSSColor = (input) => {
263
270
  /**
264
271
  * Indicates whether the direction of the element is `Right-To-Left` or not.
265
272
  */
266
- const isRTL = (target) => direction(target) == 'rtl';
273
+ const isRTL = (target) => {
274
+ return direction(target) === 'rtl';
275
+ };
267
276
 
268
277
  /**
269
278
  * Indicates whether the current code is running on a server.
270
279
  */
271
280
  const isServer = () => {
272
- return !(typeof window != 'undefined' && window.document);
281
+ return !(typeof window !== 'undefined' && window.document);
273
282
  };
274
283
 
275
284
  const shadowRoot = (target) => {
@@ -291,7 +300,8 @@ function queryAll(target, selectors) {
291
300
  }
292
301
 
293
302
  const task = (options) => {
294
- let running, promise;
303
+ let running;
304
+ let promise;
295
305
  const run = () => {
296
306
  if (options.canStart && !options.canStart())
297
307
  return Promise.resolve(false);
@@ -311,8 +321,10 @@ const task = (options) => {
311
321
  if (!running)
312
322
  return promise;
313
323
  try {
314
- if (options.canRun && !options.canRun())
315
- return (running = false);
324
+ if (options.canRun && !options.canRun()) {
325
+ running = false;
326
+ return running;
327
+ }
316
328
  options.handler();
317
329
  running = false;
318
330
  return true;
@@ -325,23 +337,25 @@ const task = (options) => {
325
337
  return run;
326
338
  };
327
339
 
340
+ // biome-ignore-all lint: TODO
341
+
328
342
  class MapSet extends Map {
329
- set(key, value) {
330
- super.set(key, value);
331
- return value;
332
- }
343
+ set(key, value) {
344
+ super.set(key, value);
345
+ return value;
346
+ }
333
347
  }
334
348
 
335
349
  class WeakMapSet extends WeakMap {
336
- set(key, value) {
337
- super.set(key, value);
338
- return value;
339
- }
350
+ set(key, value) {
351
+ super.set(key, value);
352
+ return value;
353
+ }
340
354
  }
341
355
 
342
356
  /*! (c) Andrea Giammarchi - ISC */
343
357
  const empty =
344
- /^(?:area|base|br|col|embed|hr|img|input|keygen|link|menuitem|meta|param|source|track|wbr)$/i;
358
+ /^(?:area|base|br|col|embed|hr|img|input|keygen|link|menuitem|meta|param|source|track|wbr)$/i;
345
359
  const elements = /<([a-z]+[a-z0-9:._-]*)([^>]*?)(\/?)>/g;
346
360
  const attributes$1 = /([^\s\\>"'=]+)\s*=\s*(['"]?)\x01/g;
347
361
  const holes = /[\x01\x02]/g;
@@ -358,155 +372,155 @@ const holes = /[\x01\x02]/g;
358
372
  * @returns {string} X/HTML with prefixed comments or attributes
359
373
  */
360
374
  var instrument = (template, prefix, svg) => {
361
- let i = 0;
362
- return template
363
- .join('\x01')
364
- .trim()
365
- .replace(elements, (_, name, attrs, selfClosing) => {
366
- let ml = name + attrs.replace(attributes$1, '\x02=$2$1').trimEnd();
367
- if (selfClosing.length) ml += svg || empty.test(name) ? ' /' : '></' + name;
368
- return '<' + ml + '>';
369
- })
370
- .replace(holes, (hole) => (hole === '\x01' ? '<!--' + prefix + i++ + '-->' : prefix + i++));
375
+ let i = 0;
376
+ return template
377
+ .join('\x01')
378
+ .trim()
379
+ .replace(elements, (_, name, attrs, selfClosing) => {
380
+ let ml = name + attrs.replace(attributes$1, '\x02=$2$1').trimEnd();
381
+ if (selfClosing.length) ml += svg || empty.test(name) ? ' /' : '></' + name;
382
+ return '<' + ml + '>';
383
+ })
384
+ .replace(holes, (hole) => (hole === '\x01' ? '<!--' + prefix + i++ + '-->' : prefix + i++));
371
385
  };
372
386
 
373
387
  const ELEMENT_NODE = 1;
374
388
  const nodeType = 111;
375
389
 
376
390
  const remove = ({ firstChild, lastChild }) => {
377
- const range = document.createRange();
378
- range.setStartAfter(firstChild);
379
- range.setEndAfter(lastChild);
380
- range.deleteContents();
381
- return firstChild;
391
+ const range = document.createRange();
392
+ range.setStartAfter(firstChild);
393
+ range.setEndAfter(lastChild);
394
+ range.deleteContents();
395
+ return firstChild;
382
396
  };
383
397
 
384
398
  const diffable = (node, operation) =>
385
- node.nodeType === nodeType
386
- ? 1 / operation < 0
387
- ? operation
388
- ? remove(node)
389
- : node.lastChild
390
- : operation
391
- ? node.valueOf()
392
- : node.firstChild
393
- : node;
399
+ node.nodeType === nodeType
400
+ ? 1 / operation < 0
401
+ ? operation
402
+ ? remove(node)
403
+ : node.lastChild
404
+ : operation
405
+ ? node.valueOf()
406
+ : node.firstChild
407
+ : node;
394
408
  const persistent = (fragment) => {
395
- const { firstChild, lastChild } = fragment;
396
- if (firstChild === lastChild) return lastChild || fragment;
397
- const { childNodes } = fragment;
398
- const nodes = [...childNodes];
399
- return {
400
- ELEMENT_NODE,
401
- nodeType,
402
- firstChild,
403
- lastChild,
404
- valueOf() {
405
- if (childNodes.length !== nodes.length) fragment.append(...nodes);
406
- return fragment;
407
- }
408
- };
409
+ const { firstChild, lastChild } = fragment;
410
+ if (firstChild === lastChild) return lastChild || fragment;
411
+ const { childNodes } = fragment;
412
+ const nodes = [...childNodes];
413
+ return {
414
+ ELEMENT_NODE,
415
+ nodeType,
416
+ firstChild,
417
+ lastChild,
418
+ valueOf() {
419
+ if (childNodes.length !== nodes.length) fragment.append(...nodes);
420
+ return fragment;
421
+ }
422
+ };
409
423
  };
410
424
 
411
425
  const { isArray: isArray$1 } = Array;
412
426
 
413
427
  const aria = (node) => (values) => {
414
- for (const key in values) {
415
- const name = key === 'role' ? key : `aria-${key}`;
416
- const value = values[key];
417
- if (value == null) node.removeAttribute(name);
418
- else node.setAttribute(name, value);
419
- }
428
+ for (const key in values) {
429
+ const name = key === 'role' ? key : `aria-${key}`;
430
+ const value = values[key];
431
+ if (value == null) node.removeAttribute(name);
432
+ else node.setAttribute(name, value);
433
+ }
420
434
  };
421
435
 
422
436
  const attribute = (node, name) => {
423
- let oldValue,
424
- orphan = true;
425
- const attributeNode = document.createAttributeNS(null, name);
426
- return (newValue) => {
427
- if (oldValue !== newValue) {
428
- oldValue = newValue;
429
- if (oldValue == null) {
430
- if (!orphan) {
431
- node.removeAttributeNode(attributeNode);
432
- orphan = true;
433
- }
434
- } else {
435
- const value = newValue;
436
- if (value == null) {
437
- if (!orphan) node.removeAttributeNode(attributeNode);
438
- orphan = true;
439
- } else {
440
- attributeNode.value = value;
441
- if (orphan) {
442
- node.setAttributeNodeNS(attributeNode);
443
- orphan = false;
444
- }
445
- }
446
- }
447
- }
448
- };
437
+ let oldValue,
438
+ orphan = true;
439
+ const attributeNode = document.createAttributeNS(null, name);
440
+ return (newValue) => {
441
+ if (oldValue !== newValue) {
442
+ oldValue = newValue;
443
+ if (oldValue == null) {
444
+ if (!orphan) {
445
+ node.removeAttributeNode(attributeNode);
446
+ orphan = true;
447
+ }
448
+ } else {
449
+ const value = newValue;
450
+ if (value == null) {
451
+ if (!orphan) node.removeAttributeNode(attributeNode);
452
+ orphan = true;
453
+ } else {
454
+ attributeNode.value = value;
455
+ if (orphan) {
456
+ node.setAttributeNodeNS(attributeNode);
457
+ orphan = false;
458
+ }
459
+ }
460
+ }
461
+ }
462
+ };
449
463
  };
450
464
 
451
465
  const boolean = (node, key, oldValue) => (newValue) => {
452
- if (oldValue !== !!newValue) {
453
- // when IE won't be around anymore ...
454
- // node.toggleAttribute(key, oldValue = !!newValue);
455
- if ((oldValue = !!newValue)) node.setAttribute(key, '');
456
- else node.removeAttribute(key);
457
- }
466
+ if (oldValue !== !!newValue) {
467
+ // when IE won't be around anymore ...
468
+ // node.toggleAttribute(key, oldValue = !!newValue);
469
+ if ((oldValue = !!newValue)) node.setAttribute(key, '');
470
+ else node.removeAttribute(key);
471
+ }
458
472
  };
459
473
 
460
474
  const data =
461
- ({ dataset }) =>
462
- (values) => {
463
- for (const key in values) {
464
- const value = values[key];
465
- if (value == null) delete dataset[key];
466
- else dataset[key] = value;
467
- }
468
- };
475
+ ({ dataset }) =>
476
+ (values) => {
477
+ for (const key in values) {
478
+ const value = values[key];
479
+ if (value == null) delete dataset[key];
480
+ else dataset[key] = value;
481
+ }
482
+ };
469
483
 
470
484
  const event = (node, name) => {
471
- let oldValue,
472
- lower,
473
- type = name.slice(2);
474
- if (!(name in node) && (lower = name.toLowerCase()) in node) type = lower.slice(2);
475
- return (newValue) => {
476
- const info = isArray$1(newValue) ? newValue : [newValue, false];
477
- if (oldValue !== info[0]) {
478
- if (oldValue) node.removeEventListener(type, oldValue, info[1]);
479
- if ((oldValue = info[0])) node.addEventListener(type, oldValue, info[1]);
480
- }
481
- };
485
+ let oldValue,
486
+ lower,
487
+ type = name.slice(2);
488
+ if (!(name in node) && (lower = name.toLowerCase()) in node) type = lower.slice(2);
489
+ return (newValue) => {
490
+ const info = isArray$1(newValue) ? newValue : [newValue, false];
491
+ if (oldValue !== info[0]) {
492
+ if (oldValue) node.removeEventListener(type, oldValue, info[1]);
493
+ if ((oldValue = info[0])) node.addEventListener(type, oldValue, info[1]);
494
+ }
495
+ };
482
496
  };
483
497
 
484
498
  const ref = (node) => {
485
- let oldValue;
486
- return (value) => {
487
- if (oldValue !== value) {
488
- oldValue = value;
489
- if (typeof value === 'function') value(node);
490
- else value.current = node;
491
- }
492
- };
499
+ let oldValue;
500
+ return (value) => {
501
+ if (oldValue !== value) {
502
+ oldValue = value;
503
+ if (typeof value === 'function') value(node);
504
+ else value.current = node;
505
+ }
506
+ };
493
507
  };
494
508
 
495
509
  const setter = (node, key) =>
496
- key === 'dataset'
497
- ? data(node)
498
- : (value) => {
499
- node[key] = value;
500
- };
510
+ key === 'dataset'
511
+ ? data(node)
512
+ : (value) => {
513
+ node[key] = value;
514
+ };
501
515
 
502
516
  const text = (node) => {
503
- let oldValue;
504
- return (newValue) => {
505
- if (oldValue != newValue) {
506
- oldValue = newValue;
507
- node.textContent = newValue == null ? '' : newValue;
508
- }
509
- };
517
+ let oldValue;
518
+ return (newValue) => {
519
+ if (oldValue != newValue) {
520
+ oldValue = newValue;
521
+ node.textContent = newValue == null ? '' : newValue;
522
+ }
523
+ };
510
524
  };
511
525
 
512
526
  /**
@@ -537,148 +551,148 @@ const text = (node) => {
537
551
  * @returns {Node[]} The same list of future children.
538
552
  */
539
553
  var udomdiff = (parentNode, a, b, get, before) => {
540
- const bLength = b.length;
541
- let aEnd = a.length;
542
- let bEnd = bLength;
543
- let aStart = 0;
544
- let bStart = 0;
545
- let map = null;
546
- while (aStart < aEnd || bStart < bEnd) {
547
- // append head, tail, or nodes in between: fast path
548
- if (aEnd === aStart) {
549
- // we could be in a situation where the rest of nodes that
550
- // need to be added are not at the end, and in such case
551
- // the node to `insertBefore`, if the index is more than 0
552
- // must be retrieved, otherwise it's gonna be the first item.
553
- const node =
554
- bEnd < bLength
555
- ? bStart
556
- ? get(b[bStart - 1], -0).nextSibling
557
- : get(b[bEnd - bStart], 0)
558
- : before;
559
- while (bStart < bEnd) parentNode.insertBefore(get(b[bStart++], 1), node);
560
- }
561
- // remove head or tail: fast path
562
- else if (bEnd === bStart) {
563
- while (aStart < aEnd) {
564
- // remove the node only if it's unknown or not live
565
- if (!map || !map.has(a[aStart])) parentNode.removeChild(get(a[aStart], -1));
566
- aStart++;
567
- }
568
- }
569
- // same node: fast path
570
- else if (a[aStart] === b[bStart]) {
571
- aStart++;
572
- bStart++;
573
- }
574
- // same tail: fast path
575
- else if (a[aEnd - 1] === b[bEnd - 1]) {
576
- aEnd--;
577
- bEnd--;
578
- }
579
- // The once here single last swap "fast path" has been removed in v1.1.0
580
- // https://github.com/WebReflection/udomdiff/blob/single-final-swap/esm/index.js#L69-L85
581
- // reverse swap: also fast path
582
- else if (a[aStart] === b[bEnd - 1] && b[bStart] === a[aEnd - 1]) {
583
- // this is a "shrink" operation that could happen in these cases:
584
- // [1, 2, 3, 4, 5]
585
- // [1, 4, 3, 2, 5]
586
- // or asymmetric too
587
- // [1, 2, 3, 4, 5]
588
- // [1, 2, 3, 5, 6, 4]
589
- const node = get(a[--aEnd], -1).nextSibling;
590
- parentNode.insertBefore(get(b[bStart++], 1), get(a[aStart++], -1).nextSibling);
591
- parentNode.insertBefore(get(b[--bEnd], 1), node);
592
- // mark the future index as identical (yeah, it's dirty, but cheap 👍)
593
- // The main reason to do this, is that when a[aEnd] will be reached,
594
- // the loop will likely be on the fast path, as identical to b[bEnd].
595
- // In the best case scenario, the next loop will skip the tail,
596
- // but in the worst one, this node will be considered as already
597
- // processed, bailing out pretty quickly from the map index check
598
- a[aEnd] = b[bEnd];
599
- }
600
- // map based fallback, "slow" path
601
- else {
602
- // the map requires an O(bEnd - bStart) operation once
603
- // to store all future nodes indexes for later purposes.
604
- // In the worst case scenario, this is a full O(N) cost,
605
- // and such scenario happens at least when all nodes are different,
606
- // but also if both first and last items of the lists are different
607
- if (!map) {
608
- map = new Map();
609
- let i = bStart;
610
- while (i < bEnd) map.set(b[i], i++);
611
- }
612
- // if it's a future node, hence it needs some handling
613
- if (map.has(a[aStart])) {
614
- // grab the index of such node, 'cause it might have been processed
615
- const index = map.get(a[aStart]);
616
- // if it's not already processed, look on demand for the next LCS
617
- if (bStart < index && index < bEnd) {
618
- let i = aStart;
619
- // counts the amount of nodes that are the same in the future
620
- let sequence = 1;
621
- while (++i < aEnd && i < bEnd && map.get(a[i]) === index + sequence) sequence++;
622
- // effort decision here: if the sequence is longer than replaces
623
- // needed to reach such sequence, which would brings again this loop
624
- // to the fast path, prepend the difference before a sequence,
625
- // and move only the future list index forward, so that aStart
626
- // and bStart will be aligned again, hence on the fast path.
627
- // An example considering aStart and bStart are both 0:
628
- // a: [1, 2, 3, 4]
629
- // b: [7, 1, 2, 3, 6]
630
- // this would place 7 before 1 and, from that time on, 1, 2, and 3
631
- // will be processed at zero cost
632
- if (sequence > index - bStart) {
633
- const node = get(a[aStart], 0);
634
- while (bStart < index) parentNode.insertBefore(get(b[bStart++], 1), node);
635
- }
636
- // if the effort wasn't good enough, fallback to a replace,
637
- // moving both source and target indexes forward, hoping that some
638
- // similar node will be found later on, to go back to the fast path
639
- else {
640
- parentNode.replaceChild(get(b[bStart++], 1), get(a[aStart++], -1));
641
- }
642
- }
643
- // otherwise move the source forward, 'cause there's nothing to do
644
- else aStart++;
645
- }
646
- // this node has no meaning in the future list, so it's more than safe
647
- // to remove it, and check the next live node out instead, meaning
648
- // that only the live list index should be forwarded
649
- else parentNode.removeChild(get(a[aStart++], -1));
650
- }
651
- }
652
- return b;
554
+ const bLength = b.length;
555
+ let aEnd = a.length;
556
+ let bEnd = bLength;
557
+ let aStart = 0;
558
+ let bStart = 0;
559
+ let map = null;
560
+ while (aStart < aEnd || bStart < bEnd) {
561
+ // append head, tail, or nodes in between: fast path
562
+ if (aEnd === aStart) {
563
+ // we could be in a situation where the rest of nodes that
564
+ // need to be added are not at the end, and in such case
565
+ // the node to `insertBefore`, if the index is more than 0
566
+ // must be retrieved, otherwise it's gonna be the first item.
567
+ const node =
568
+ bEnd < bLength
569
+ ? bStart
570
+ ? get(b[bStart - 1], -0).nextSibling
571
+ : get(b[bEnd - bStart], 0)
572
+ : before;
573
+ while (bStart < bEnd) parentNode.insertBefore(get(b[bStart++], 1), node);
574
+ }
575
+ // remove head or tail: fast path
576
+ else if (bEnd === bStart) {
577
+ while (aStart < aEnd) {
578
+ // remove the node only if it's unknown or not live
579
+ if (!map || !map.has(a[aStart])) parentNode.removeChild(get(a[aStart], -1));
580
+ aStart++;
581
+ }
582
+ }
583
+ // same node: fast path
584
+ else if (a[aStart] === b[bStart]) {
585
+ aStart++;
586
+ bStart++;
587
+ }
588
+ // same tail: fast path
589
+ else if (a[aEnd - 1] === b[bEnd - 1]) {
590
+ aEnd--;
591
+ bEnd--;
592
+ }
593
+ // The once here single last swap "fast path" has been removed in v1.1.0
594
+ // https://github.com/WebReflection/udomdiff/blob/single-final-swap/esm/index.js#L69-L85
595
+ // reverse swap: also fast path
596
+ else if (a[aStart] === b[bEnd - 1] && b[bStart] === a[aEnd - 1]) {
597
+ // this is a "shrink" operation that could happen in these cases:
598
+ // [1, 2, 3, 4, 5]
599
+ // [1, 4, 3, 2, 5]
600
+ // or asymmetric too
601
+ // [1, 2, 3, 4, 5]
602
+ // [1, 2, 3, 5, 6, 4]
603
+ const node = get(a[--aEnd], -1).nextSibling;
604
+ parentNode.insertBefore(get(b[bStart++], 1), get(a[aStart++], -1).nextSibling);
605
+ parentNode.insertBefore(get(b[--bEnd], 1), node);
606
+ // mark the future index as identical (yeah, it's dirty, but cheap 👍)
607
+ // The main reason to do this, is that when a[aEnd] will be reached,
608
+ // the loop will likely be on the fast path, as identical to b[bEnd].
609
+ // In the best case scenario, the next loop will skip the tail,
610
+ // but in the worst one, this node will be considered as already
611
+ // processed, bailing out pretty quickly from the map index check
612
+ a[aEnd] = b[bEnd];
613
+ }
614
+ // map based fallback, "slow" path
615
+ else {
616
+ // the map requires an O(bEnd - bStart) operation once
617
+ // to store all future nodes indexes for later purposes.
618
+ // In the worst case scenario, this is a full O(N) cost,
619
+ // and such scenario happens at least when all nodes are different,
620
+ // but also if both first and last items of the lists are different
621
+ if (!map) {
622
+ map = new Map();
623
+ let i = bStart;
624
+ while (i < bEnd) map.set(b[i], i++);
625
+ }
626
+ // if it's a future node, hence it needs some handling
627
+ if (map.has(a[aStart])) {
628
+ // grab the index of such node, 'cause it might have been processed
629
+ const index = map.get(a[aStart]);
630
+ // if it's not already processed, look on demand for the next LCS
631
+ if (bStart < index && index < bEnd) {
632
+ let i = aStart;
633
+ // counts the amount of nodes that are the same in the future
634
+ let sequence = 1;
635
+ while (++i < aEnd && i < bEnd && map.get(a[i]) === index + sequence) sequence++;
636
+ // effort decision here: if the sequence is longer than replaces
637
+ // needed to reach such sequence, which would brings again this loop
638
+ // to the fast path, prepend the difference before a sequence,
639
+ // and move only the future list index forward, so that aStart
640
+ // and bStart will be aligned again, hence on the fast path.
641
+ // An example considering aStart and bStart are both 0:
642
+ // a: [1, 2, 3, 4]
643
+ // b: [7, 1, 2, 3, 6]
644
+ // this would place 7 before 1 and, from that time on, 1, 2, and 3
645
+ // will be processed at zero cost
646
+ if (sequence > index - bStart) {
647
+ const node = get(a[aStart], 0);
648
+ while (bStart < index) parentNode.insertBefore(get(b[bStart++], 1), node);
649
+ }
650
+ // if the effort wasn't good enough, fallback to a replace,
651
+ // moving both source and target indexes forward, hoping that some
652
+ // similar node will be found later on, to go back to the fast path
653
+ else {
654
+ parentNode.replaceChild(get(b[bStart++], 1), get(a[aStart++], -1));
655
+ }
656
+ }
657
+ // otherwise move the source forward, 'cause there's nothing to do
658
+ else aStart++;
659
+ }
660
+ // this node has no meaning in the future list, so it's more than safe
661
+ // to remove it, and check the next live node out instead, meaning
662
+ // that only the live list index should be forwarded
663
+ else parentNode.removeChild(get(a[aStart++], -1));
664
+ }
665
+ }
666
+ return b;
653
667
  };
654
668
 
655
669
  const { isArray, prototype } = Array;
656
670
  const { indexOf } = prototype;
657
671
 
658
672
  const {
659
- createDocumentFragment,
660
- createElement,
661
- createElementNS,
662
- createTextNode,
663
- createTreeWalker,
664
- importNode
673
+ createDocumentFragment,
674
+ createElement,
675
+ createElementNS,
676
+ createTextNode,
677
+ createTreeWalker,
678
+ importNode
665
679
  } = new Proxy(typeof window == 'undefined' ? {} : window.document, {
666
- get: (target, method) => (target[method] || function () {}).bind(target)
680
+ get: (target, method) => (target[method] || function () {}).bind(target)
667
681
  });
668
682
 
669
683
  const createHTML = (html) => {
670
- const template = createElement('template');
671
- template.innerHTML = html;
672
- return template.content;
684
+ const template = createElement('template');
685
+ template.innerHTML = html;
686
+ return template.content;
673
687
  };
674
688
 
675
689
  let xml;
676
690
  const createSVG = (svg) => {
677
- if (!xml) xml = createElementNS('http://www.w3.org/2000/svg', 'svg');
678
- xml.innerHTML = svg;
679
- const content = createDocumentFragment();
680
- content.append(...xml.childNodes);
681
- return content;
691
+ if (!xml) xml = createElementNS('http://www.w3.org/2000/svg', 'svg');
692
+ xml.innerHTML = svg;
693
+ const content = createDocumentFragment();
694
+ content.append(...xml.childNodes);
695
+ return content;
682
696
  };
683
697
 
684
698
  const createContent = (text, svg) => (svg ? createSVG(text) : createHTML(text));
@@ -688,91 +702,91 @@ const reducePath = ({ childNodes }, i) => childNodes[i];
688
702
 
689
703
  // this helper avoid code bloat around handleAnything() callback
690
704
  const diff = (comment, oldNodes, newNodes) =>
691
- udomdiff(
692
- comment.parentNode,
693
- // TODO: there is a possible edge case where a node has been
694
- // removed manually, or it was a keyed one, attached
695
- // to a shared reference between renders.
696
- // In this case udomdiff might fail at removing such node
697
- // as its parent won't be the expected one.
698
- // The best way to avoid this issue is to filter oldNodes
699
- // in search of those not live, or not in the current parent
700
- // anymore, but this would require both a change to uwire,
701
- // exposing a parentNode from the firstChild, as example,
702
- // but also a filter per each diff that should exclude nodes
703
- // that are not in there, penalizing performance quite a lot.
704
- // As this has been also a potential issue with domdiff,
705
- // and both lighterhtml and hyperHTML might fail with this
706
- // very specific edge case, I might as well document this possible
707
- // "diffing shenanigan" and call it a day.
708
- oldNodes,
709
- newNodes,
710
- diffable,
711
- comment
712
- );
705
+ udomdiff(
706
+ comment.parentNode,
707
+ // TODO: there is a possible edge case where a node has been
708
+ // removed manually, or it was a keyed one, attached
709
+ // to a shared reference between renders.
710
+ // In this case udomdiff might fail at removing such node
711
+ // as its parent won't be the expected one.
712
+ // The best way to avoid this issue is to filter oldNodes
713
+ // in search of those not live, or not in the current parent
714
+ // anymore, but this would require both a change to uwire,
715
+ // exposing a parentNode from the firstChild, as example,
716
+ // but also a filter per each diff that should exclude nodes
717
+ // that are not in there, penalizing performance quite a lot.
718
+ // As this has been also a potential issue with domdiff,
719
+ // and both lighterhtml and hyperHTML might fail with this
720
+ // very specific edge case, I might as well document this possible
721
+ // "diffing shenanigan" and call it a day.
722
+ oldNodes,
723
+ newNodes,
724
+ diffable,
725
+ comment
726
+ );
713
727
 
714
728
  // if an interpolation represents a comment, the whole
715
729
  // diffing will be related to such comment.
716
730
  // This helper is in charge of understanding how the new
717
731
  // content for such interpolation/hole should be updated
718
732
  const handleAnything = (comment) => {
719
- let oldValue,
720
- text,
721
- nodes = [];
722
- const anyContent = (newValue) => {
723
- switch (typeof newValue) {
724
- // primitives are handled as text content
725
- case 'string':
726
- case 'number':
727
- case 'boolean':
728
- if (oldValue !== newValue) {
729
- oldValue = newValue;
730
- if (!text) text = createTextNode('');
731
- text.data = newValue;
732
- nodes = diff(comment, nodes, [text]);
733
- }
734
- break;
735
- // null, and undefined are used to cleanup previous content
736
- case 'object':
737
- case 'undefined':
738
- if (newValue == null) {
739
- if (oldValue != newValue) {
740
- oldValue = newValue;
741
- nodes = diff(comment, nodes, []);
742
- }
743
- break;
744
- }
745
- // arrays and nodes have a special treatment
746
- if (isArray(newValue)) {
747
- oldValue = newValue;
748
- // arrays can be used to cleanup, if empty
749
- if (newValue.length === 0) nodes = diff(comment, nodes, []);
750
- // or diffed, if these contains nodes or "wires"
751
- else if (typeof newValue[0] === 'object') nodes = diff(comment, nodes, newValue);
752
- // in all other cases the content is stringified as is
753
- else anyContent(String(newValue));
754
- break;
755
- }
756
- // if the new value is a DOM node, or a wire, and it's
757
- // different from the one already live, then it's diffed.
758
- // if the node is a fragment, it's appended once via its childNodes
759
- // There is no `else` here, meaning if the content
760
- // is not expected one, nothing happens, as easy as that.
761
- if (oldValue !== newValue && 'ELEMENT_NODE' in newValue) {
762
- oldValue = newValue;
763
- nodes = diff(
764
- comment,
765
- nodes,
766
- newValue.nodeType === 11 ? [...newValue.childNodes] : [newValue]
767
- );
768
- }
769
- break;
770
- case 'function':
771
- anyContent(newValue(comment));
772
- break;
773
- }
774
- };
775
- return anyContent;
733
+ let oldValue,
734
+ text,
735
+ nodes = [];
736
+ const anyContent = (newValue) => {
737
+ switch (typeof newValue) {
738
+ // primitives are handled as text content
739
+ case 'string':
740
+ case 'number':
741
+ case 'boolean':
742
+ if (oldValue !== newValue) {
743
+ oldValue = newValue;
744
+ if (!text) text = createTextNode('');
745
+ text.data = newValue;
746
+ nodes = diff(comment, nodes, [text]);
747
+ }
748
+ break;
749
+ // null, and undefined are used to cleanup previous content
750
+ case 'object':
751
+ case 'undefined':
752
+ if (newValue == null) {
753
+ if (oldValue != newValue) {
754
+ oldValue = newValue;
755
+ nodes = diff(comment, nodes, []);
756
+ }
757
+ break;
758
+ }
759
+ // arrays and nodes have a special treatment
760
+ if (isArray(newValue)) {
761
+ oldValue = newValue;
762
+ // arrays can be used to cleanup, if empty
763
+ if (newValue.length === 0) nodes = diff(comment, nodes, []);
764
+ // or diffed, if these contains nodes or "wires"
765
+ else if (typeof newValue[0] === 'object') nodes = diff(comment, nodes, newValue);
766
+ // in all other cases the content is stringified as is
767
+ else anyContent(String(newValue));
768
+ break;
769
+ }
770
+ // if the new value is a DOM node, or a wire, and it's
771
+ // different from the one already live, then it's diffed.
772
+ // if the node is a fragment, it's appended once via its childNodes
773
+ // There is no `else` here, meaning if the content
774
+ // is not expected one, nothing happens, as easy as that.
775
+ if (oldValue !== newValue && 'ELEMENT_NODE' in newValue) {
776
+ oldValue = newValue;
777
+ nodes = diff(
778
+ comment,
779
+ nodes,
780
+ newValue.nodeType === 11 ? [...newValue.childNodes] : [newValue]
781
+ );
782
+ }
783
+ break;
784
+ case 'function':
785
+ anyContent(newValue(comment));
786
+ break;
787
+ }
788
+ };
789
+ return anyContent;
776
790
  };
777
791
 
778
792
  // attributes can be:
@@ -786,25 +800,25 @@ const handleAnything = (comment) => {
786
800
  // * onevent=${...} to automatically handle event listeners
787
801
  // * generic=${...} to handle an attribute just like an attribute
788
802
  const handleAttribute = (node, name /*, svg*/) => {
789
- switch (name[0]) {
790
- case '?':
791
- return boolean(node, name.slice(1), false);
792
- case '.':
793
- return setter(node, name.slice(1));
794
- case '@':
795
- return event(node, 'on' + name.slice(1));
796
- case 'o':
797
- if (name[1] === 'n') return event(node, name);
798
- }
799
-
800
- switch (name) {
801
- case 'ref':
802
- return ref(node);
803
- case 'aria':
804
- return aria(node);
805
- }
806
-
807
- return attribute(node, name /*, svg*/);
803
+ switch (name[0]) {
804
+ case '?':
805
+ return boolean(node, name.slice(1), false);
806
+ case '.':
807
+ return setter(node, name.slice(1));
808
+ case '@':
809
+ return event(node, 'on' + name.slice(1));
810
+ case 'o':
811
+ if (name[1] === 'n') return event(node, name);
812
+ }
813
+
814
+ switch (name) {
815
+ case 'ref':
816
+ return ref(node);
817
+ case 'aria':
818
+ return aria(node);
819
+ }
820
+
821
+ return attribute(node, name /*, svg*/);
808
822
  };
809
823
 
810
824
  // each mapped update carries the update type and its path
@@ -812,27 +826,27 @@ const handleAttribute = (node, name /*, svg*/) => {
812
826
  // the path is how to retrieve the related node to update.
813
827
  // In the attribute case, the attribute name is also carried along.
814
828
  function handlers(options) {
815
- const { type, path } = options;
816
- const node = path.reduceRight(reducePath, this);
817
- return type === 'node'
818
- ? handleAnything(node)
819
- : type === 'attr'
820
- ? handleAttribute(node, options.name /*, options.svg*/)
821
- : text(node);
829
+ const { type, path } = options;
830
+ const node = path.reduceRight(reducePath, this);
831
+ return type === 'node'
832
+ ? handleAnything(node)
833
+ : type === 'attr'
834
+ ? handleAttribute(node, options.name /*, options.svg*/)
835
+ : text(node);
822
836
  }
823
837
 
824
838
  // from a fragment container, create an array of indexes
825
839
  // related to its child nodes, so that it's possible
826
840
  // to retrieve later on exact node via reducePath
827
841
  const createPath = (node) => {
828
- const path = [];
829
- let { parentNode } = node;
830
- while (parentNode) {
831
- path.push(indexOf.call(parentNode.childNodes, node));
832
- node = parentNode;
833
- ({ parentNode } = node);
834
- }
835
- return path;
842
+ const path = [];
843
+ let { parentNode } = node;
844
+ while (parentNode) {
845
+ path.push(indexOf.call(parentNode.childNodes, node));
846
+ node = parentNode;
847
+ ({ parentNode } = node);
848
+ }
849
+ return path;
836
850
  };
837
851
 
838
852
  // the prefix is used to identify either comments, attributes, or nodes
@@ -854,100 +868,100 @@ const cache$1 = new WeakMapSet();
854
868
  const textOnly = /^(?:textarea|script|style|title|plaintext|xmp)$/;
855
869
 
856
870
  const createCache = () => ({
857
- stack: [], // each template gets a stack for each interpolation "hole"
858
-
859
- entry: null, // each entry contains details, such as:
860
- // * the template that is representing
861
- // * the type of node it represents (html or svg)
862
- // * the content fragment with all nodes
863
- // * the list of updates per each node (template holes)
864
- // * the "wired" node or fragment that will get updates
865
- // if the template or type are different from the previous one
866
- // the entry gets re-created each time
867
-
868
- wire: null // each rendered node represent some wired content and
869
- // this reference to the latest one. If different, the node
870
- // will be cleaned up and the new "wire" will be appended
871
+ stack: [], // each template gets a stack for each interpolation "hole"
872
+
873
+ entry: null, // each entry contains details, such as:
874
+ // * the template that is representing
875
+ // * the type of node it represents (html or svg)
876
+ // * the content fragment with all nodes
877
+ // * the list of updates per each node (template holes)
878
+ // * the "wired" node or fragment that will get updates
879
+ // if the template or type are different from the previous one
880
+ // the entry gets re-created each time
881
+
882
+ wire: null // each rendered node represent some wired content and
883
+ // this reference to the latest one. If different, the node
884
+ // will be cleaned up and the new "wire" will be appended
871
885
  });
872
886
 
873
887
  // the entry stored in the rendered node cache, and per each "hole"
874
888
  const createEntry = (type, template) => {
875
- const { content, updates } = mapUpdates(type, template);
876
- return { type, template, content, updates, wire: null };
889
+ const { content, updates } = mapUpdates(type, template);
890
+ return { type, template, content, updates, wire: null };
877
891
  };
878
892
 
879
893
  // a template is instrumented to be able to retrieve where updates are needed.
880
894
  // Each unique template becomes a fragment, cloned once per each other
881
895
  // operation based on the same template, i.e. data => html`<p>${data}</p>`
882
896
  const mapTemplate = (type, template) => {
883
- const svg = type === 'svg';
884
- const text = instrument(template, prefix, svg);
885
- const content = createContent(text, svg);
886
- // once instrumented and reproduced as fragment, it's crawled
887
- // to find out where each update is in the fragment tree
888
- const tw = createTreeWalker(content, 1 | 128);
889
- const nodes = [];
890
- const length = template.length - 1;
891
- let i = 0;
892
- // updates are searched via unique names, linearly increased across the tree
893
- // <div isµ0="attr" isµ1="other"><!--isµ2--><style><!--isµ3--</style></div>
894
- let search = `${prefix}${i}`;
895
- while (i < length) {
896
- const node = tw.nextNode();
897
- // if not all updates are bound but there's nothing else to crawl
898
- // it means that there is something wrong with the template.
899
- if (!node) throw `bad template: ${text}`;
900
- // if the current node is a comment, and it contains isµX
901
- // it means the update should take care of any content
902
- if (node.nodeType === 8) {
903
- // The only comments to be considered are those
904
- // which content is exactly the same as the searched one.
905
- if (node.data === search) {
906
- nodes.push({ type: 'node', path: createPath(node) });
907
- search = `${prefix}${++i}`;
908
- }
909
- } else {
910
- // if the node is not a comment, loop through all its attributes
911
- // named isµX and relate attribute updates to this node and the
912
- // attribute name, retrieved through node.getAttribute("isµX")
913
- // the isµX attribute will be removed as irrelevant for the layout
914
- // let svg = -1;
915
- while (node.hasAttribute(search)) {
916
- nodes.push({
917
- type: 'attr',
918
- path: createPath(node),
919
- name: node.getAttribute(search)
920
- });
921
- node.removeAttribute(search);
922
- search = `${prefix}${++i}`;
923
- }
924
- // if the node was a style, textarea, or others, check its content
925
- // and if it is <!--isµX--> then update tex-only this node
926
- if (textOnly.test(node.localName) && node.textContent.trim() === `<!--${search}-->`) {
927
- node.textContent = '';
928
- nodes.push({ type: 'text', path: createPath(node) });
929
- search = `${prefix}${++i}`;
930
- }
931
- }
932
- }
933
- // once all nodes to update, or their attributes, are known, the content
934
- // will be cloned in the future to represent the template, and all updates
935
- // related to such content retrieved right away without needing to re-crawl
936
- // the exact same template, and its content, more than once.
937
- return { content, nodes };
897
+ const svg = type === 'svg';
898
+ const text = instrument(template, prefix, svg);
899
+ const content = createContent(text, svg);
900
+ // once instrumented and reproduced as fragment, it's crawled
901
+ // to find out where each update is in the fragment tree
902
+ const tw = createTreeWalker(content, 1 | 128);
903
+ const nodes = [];
904
+ const length = template.length - 1;
905
+ let i = 0;
906
+ // updates are searched via unique names, linearly increased across the tree
907
+ // <div isµ0="attr" isµ1="other"><!--isµ2--><style><!--isµ3--</style></div>
908
+ let search = `${prefix}${i}`;
909
+ while (i < length) {
910
+ const node = tw.nextNode();
911
+ // if not all updates are bound but there's nothing else to crawl
912
+ // it means that there is something wrong with the template.
913
+ if (!node) throw `bad template: ${text}`;
914
+ // if the current node is a comment, and it contains isµX
915
+ // it means the update should take care of any content
916
+ if (node.nodeType === 8) {
917
+ // The only comments to be considered are those
918
+ // which content is exactly the same as the searched one.
919
+ if (node.data === search) {
920
+ nodes.push({ type: 'node', path: createPath(node) });
921
+ search = `${prefix}${++i}`;
922
+ }
923
+ } else {
924
+ // if the node is not a comment, loop through all its attributes
925
+ // named isµX and relate attribute updates to this node and the
926
+ // attribute name, retrieved through node.getAttribute("isµX")
927
+ // the isµX attribute will be removed as irrelevant for the layout
928
+ // let svg = -1;
929
+ while (node.hasAttribute(search)) {
930
+ nodes.push({
931
+ type: 'attr',
932
+ path: createPath(node),
933
+ name: node.getAttribute(search)
934
+ });
935
+ node.removeAttribute(search);
936
+ search = `${prefix}${++i}`;
937
+ }
938
+ // if the node was a style, textarea, or others, check its content
939
+ // and if it is <!--isµX--> then update tex-only this node
940
+ if (textOnly.test(node.localName) && node.textContent.trim() === `<!--${search}-->`) {
941
+ node.textContent = '';
942
+ nodes.push({ type: 'text', path: createPath(node) });
943
+ search = `${prefix}${++i}`;
944
+ }
945
+ }
946
+ }
947
+ // once all nodes to update, or their attributes, are known, the content
948
+ // will be cloned in the future to represent the template, and all updates
949
+ // related to such content retrieved right away without needing to re-crawl
950
+ // the exact same template, and its content, more than once.
951
+ return { content, nodes };
938
952
  };
939
953
 
940
954
  // if a template is unknown, perform the previous mapping, otherwise grab
941
955
  // its details such as the fragment with all nodes, and updates info.
942
956
  const mapUpdates = (type, template) => {
943
- const { content, nodes } =
944
- cache$1.get(template) || cache$1.set(template, mapTemplate(type, template));
945
- // clone deeply the fragment
946
- const fragment = importNode(content, true);
947
- // and relate an update handler per each node that needs one
948
- const updates = nodes.map(handlers, fragment);
949
- // return the fragment and all updates to use within its nodes
950
- return { content: fragment, updates };
957
+ const { content, nodes } =
958
+ cache$1.get(template) || cache$1.set(template, mapTemplate(type, template));
959
+ // clone deeply the fragment
960
+ const fragment = importNode(content, true);
961
+ // and relate an update handler per each node that needs one
962
+ const updates = nodes.map(handlers, fragment);
963
+ // return the fragment and all updates to use within its nodes
964
+ return { content: fragment, updates };
951
965
  };
952
966
 
953
967
  // as html and svg can be nested calls, but no parent node is known
@@ -955,48 +969,48 @@ const mapUpdates = (type, template) => {
955
969
  // discover what to do with each interpolation, which will result
956
970
  // into an update operation.
957
971
  const unroll = (info, { type, template, values }) => {
958
- // interpolations can contain holes and arrays, so these need
959
- // to be recursively discovered
960
- const length = unrollValues(info, values);
961
- let { entry } = info;
962
- // if the cache entry is either null or different from the template
963
- // and the type this unroll should resolve, create a new entry
964
- // assigning a new content fragment and the list of updates.
965
- if (!entry || entry.template !== template || entry.type !== type)
966
- info.entry = entry = createEntry(type, template);
967
- const { content, updates, wire } = entry;
968
- // even if the fragment and its nodes is not live yet,
969
- // it is already possible to update via interpolations values.
970
- for (let i = 0; i < length; i++) updates[i](values[i]);
971
- // if the entry was new, or representing a different template or type,
972
- // create a new persistent entity to use during diffing.
973
- // This is simply a DOM node, when the template has a single container,
974
- // as in `<p></p>`, or a "wire" in `<p></p><p></p>` and similar cases.
975
- return wire || (entry.wire = persistent(content));
972
+ // interpolations can contain holes and arrays, so these need
973
+ // to be recursively discovered
974
+ const length = unrollValues(info, values);
975
+ let { entry } = info;
976
+ // if the cache entry is either null or different from the template
977
+ // and the type this unroll should resolve, create a new entry
978
+ // assigning a new content fragment and the list of updates.
979
+ if (!entry || entry.template !== template || entry.type !== type)
980
+ info.entry = entry = createEntry(type, template);
981
+ const { content, updates, wire } = entry;
982
+ // even if the fragment and its nodes is not live yet,
983
+ // it is already possible to update via interpolations values.
984
+ for (let i = 0; i < length; i++) updates[i](values[i]);
985
+ // if the entry was new, or representing a different template or type,
986
+ // create a new persistent entity to use during diffing.
987
+ // This is simply a DOM node, when the template has a single container,
988
+ // as in `<p></p>`, or a "wire" in `<p></p><p></p>` and similar cases.
989
+ return wire || (entry.wire = persistent(content));
976
990
  };
977
991
 
978
992
  // the stack retains, per each interpolation value, the cache
979
993
  // related to each interpolation value, or null, if the render
980
994
  // was conditional and the value is not special (Array or Hole)
981
995
  const unrollValues = ({ stack }, values) => {
982
- const { length } = values;
983
- for (let i = 0; i < length; i++) {
984
- const hole = values[i];
985
- // each Hole gets unrolled and re-assigned as value
986
- // so that domdiff will deal with a node/wire, not with a hole
987
- if (hole instanceof Hole) values[i] = unroll(stack[i] || (stack[i] = createCache()), hole);
988
- // arrays are recursively resolved so that each entry will contain
989
- // also a DOM node or a wire, hence it can be diffed if/when needed
990
- else if (isArray(hole)) unrollValues(stack[i] || (stack[i] = createCache()), hole);
991
- // if the value is nothing special, the stack doesn't need to retain data
992
- // this is useful also to cleanup previously retained data, if the value
993
- // was a Hole, or an Array, but not anymore, i.e.:
994
- // const update = content => html`<div>${content}</div>`;
995
- // update(listOfItems); update(null); update(html`hole`)
996
- else stack[i] = null;
997
- }
998
- if (length < stack.length) stack.splice(length);
999
- return length;
996
+ const { length } = values;
997
+ for (let i = 0; i < length; i++) {
998
+ const hole = values[i];
999
+ // each Hole gets unrolled and re-assigned as value
1000
+ // so that domdiff will deal with a node/wire, not with a hole
1001
+ if (hole instanceof Hole) values[i] = unroll(stack[i] || (stack[i] = createCache()), hole);
1002
+ // arrays are recursively resolved so that each entry will contain
1003
+ // also a DOM node or a wire, hence it can be diffed if/when needed
1004
+ else if (isArray(hole)) unrollValues(stack[i] || (stack[i] = createCache()), hole);
1005
+ // if the value is nothing special, the stack doesn't need to retain data
1006
+ // this is useful also to cleanup previously retained data, if the value
1007
+ // was a Hole, or an Array, but not anymore, i.e.:
1008
+ // const update = content => html`<div>${content}</div>`;
1009
+ // update(listOfItems); update(null); update(html`hole`)
1010
+ else stack[i] = null;
1011
+ }
1012
+ if (length < stack.length) stack.splice(length);
1013
+ return length;
1000
1014
  };
1001
1015
 
1002
1016
  /**
@@ -1007,44 +1021,44 @@ const unrollValues = ({ stack }, values) => {
1007
1021
  * @param {Array} values Zero, one, or more interpolated values to render.
1008
1022
  */
1009
1023
  class Hole {
1010
- constructor(type, template, values) {
1011
- this.type = type;
1012
- this.template = template;
1013
- this.values = values;
1014
- }
1024
+ constructor(type, template, values) {
1025
+ this.type = type;
1026
+ this.template = template;
1027
+ this.values = values;
1028
+ }
1015
1029
  }
1016
1030
 
1017
1031
  // both `html` and `svg` template literal tags are polluted
1018
1032
  // with a `for(ref[, id])` and a `node` tag too
1019
1033
  const tag = (type) => {
1020
- // both `html` and `svg` tags have their own cache
1021
- const keyed = new WeakMapSet();
1022
- // keyed operations always re-use the same cache and unroll
1023
- // the template and its interpolations right away
1024
- const fixed =
1025
- (cache) =>
1026
- (template, ...values) =>
1027
- unroll(cache, { type, template, values });
1028
- return Object.assign(
1029
- // non keyed operations are recognized as instance of Hole
1030
- // during the "unroll", recursively resolved and updated
1031
- (template, ...values) => new Hole(type, template, values),
1032
- {
1033
- // keyed operations need a reference object, usually the parent node
1034
- // which is showing keyed results, and optionally a unique id per each
1035
- // related node, handy with JSON results and mutable list of objects
1036
- // that usually carry a unique identifier
1037
- for(ref, id) {
1038
- const memo = keyed.get(ref) || keyed.set(ref, new MapSet());
1039
- return memo.get(id) || memo.set(id, fixed(createCache()));
1040
- },
1041
- // it is possible to create one-off content out of the box via node tag
1042
- // this might return the single created node, or a fragment with all
1043
- // nodes present at the root level and, of course, their child nodes
1044
- node: (template, ...values) =>
1045
- unroll(createCache(), new Hole(type, template, values)).valueOf()
1046
- }
1047
- );
1034
+ // both `html` and `svg` tags have their own cache
1035
+ const keyed = new WeakMapSet();
1036
+ // keyed operations always re-use the same cache and unroll
1037
+ // the template and its interpolations right away
1038
+ const fixed =
1039
+ (cache) =>
1040
+ (template, ...values) =>
1041
+ unroll(cache, { type, template, values });
1042
+ return Object.assign(
1043
+ // non keyed operations are recognized as instance of Hole
1044
+ // during the "unroll", recursively resolved and updated
1045
+ (template, ...values) => new Hole(type, template, values),
1046
+ {
1047
+ // keyed operations need a reference object, usually the parent node
1048
+ // which is showing keyed results, and optionally a unique id per each
1049
+ // related node, handy with JSON results and mutable list of objects
1050
+ // that usually carry a unique identifier
1051
+ for(ref, id) {
1052
+ const memo = keyed.get(ref) || keyed.set(ref, new MapSet());
1053
+ return memo.get(id) || memo.set(id, fixed(createCache()));
1054
+ },
1055
+ // it is possible to create one-off content out of the box via node tag
1056
+ // this might return the single created node, or a fragment with all
1057
+ // nodes present at the root level and, of course, their child nodes
1058
+ node: (template, ...values) =>
1059
+ unroll(createCache(), new Hole(type, template, values)).valueOf()
1060
+ }
1061
+ );
1048
1062
  };
1049
1063
 
1050
1064
  // each rendered node gets its own cache
@@ -1056,18 +1070,18 @@ const cache = new WeakMapSet();
1056
1070
  // and the new new content is appended, and if such content is a Hole
1057
1071
  // then it's "unrolled" to resolve all its inner nodes.
1058
1072
  const render = (where, what) => {
1059
- const hole = typeof what === 'function' ? what() : what;
1060
- const info = cache.get(where) || cache.set(where, createCache());
1061
- const wire = hole instanceof Hole ? unroll(info, hole) : hole;
1062
- if (wire !== info.wire) {
1063
- info.wire = wire;
1064
- // valueOf() simply returns the node itself, but in case it was a "wire"
1065
- // it will eventually re-append all nodes to its fragment so that such
1066
- // fragment can be re-appended many times in a meaningful way
1067
- // (wires are basically persistent fragments facades with special behavior)
1068
- where.replaceChildren(wire.valueOf());
1069
- }
1070
- return where;
1073
+ const hole = typeof what === 'function' ? what() : what;
1074
+ const info = cache.get(where) || cache.set(where, createCache());
1075
+ const wire = hole instanceof Hole ? unroll(info, hole) : hole;
1076
+ if (wire !== info.wire) {
1077
+ info.wire = wire;
1078
+ // valueOf() simply returns the node itself, but in case it was a "wire"
1079
+ // it will eventually re-append all nodes to its fragment so that such
1080
+ // fragment can be re-appended many times in a meaningful way
1081
+ // (wires are basically persistent fragments facades with special behavior)
1082
+ where.replaceChildren(wire.valueOf());
1083
+ }
1084
+ return where;
1071
1085
  };
1072
1086
 
1073
1087
  const html$1 = tag('html');
@@ -1081,8 +1095,10 @@ tag('svg');
1081
1095
  * @param callback Invoked when the rendering phase is completed.
1082
1096
  */
1083
1097
  const requestUpdate = (target, name, previous, callback) => {
1098
+ // Ensure API_STACKS exists on target
1099
+ target[API_STACKS] ||= new Map();
1084
1100
  // Creates/Gets a stacks.
1085
- const stacks = (target[API_STACKS] ||= new Map());
1101
+ const stacks = target[API_STACKS];
1086
1102
  // Creates/Updates a stack.
1087
1103
  const stack = stacks.get(name) || { callbacks: [], previous };
1088
1104
  // Adds the callback to the stack, if exists.
@@ -1105,7 +1121,7 @@ const requestUpdate = (target, name, previous, callback) => {
1105
1121
  // Invokes requests' callback.
1106
1122
  stacks.forEach((state) => {
1107
1123
  state.callbacks.forEach((callback, index, callbacks) => {
1108
- callback(callbacks.length - 1 != index);
1124
+ callback(callbacks.length - 1 !== index);
1109
1125
  });
1110
1126
  });
1111
1127
  // TODO
@@ -1123,7 +1139,7 @@ const requestUpdate = (target, name, previous, callback) => {
1123
1139
  if (!hasVariable && localSheet)
1124
1140
  return;
1125
1141
  const parsed = raw
1126
- .replace(regex1, (match, key) => {
1142
+ .replace(regex1, (_match, key) => {
1127
1143
  let value = target;
1128
1144
  for (const section of key.split('-')) {
1129
1145
  value = value?.[section];
@@ -1134,7 +1150,7 @@ const requestUpdate = (target, name, previous, callback) => {
1134
1150
  if (!localSheet) {
1135
1151
  localSheet = new CSSStyleSheet();
1136
1152
  target[API_STYLE] = localSheet;
1137
- shadowRoot(target).adoptedStyleSheets.push(localSheet);
1153
+ shadowRoot(target)?.adoptedStyleSheets.push(localSheet);
1138
1154
  }
1139
1155
  const localStyle = parsed.replace(regex3, '');
1140
1156
  localSheet.replaceSync(localStyle);
@@ -1149,7 +1165,7 @@ const requestUpdate = (target, name, previous, callback) => {
1149
1165
  ?.match(regex3)
1150
1166
  ?.join('')
1151
1167
  ?.replaceAll('global', '')
1152
- ?.replaceAll(':host', getTag(target));
1168
+ ?.replaceAll(':host', getTag(target) || '');
1153
1169
  globalSheet.replaceSync(globalStyle);
1154
1170
  })();
1155
1171
  // Calls the lifecycle's callback after the rendering phase.
@@ -1173,9 +1189,15 @@ const slots = (target) => {
1173
1189
  const slots = {};
1174
1190
  const children = Array.from(element.childNodes);
1175
1191
  for (const child of children) {
1176
- if (child.nodeName == '#comment')
1192
+ if (child.nodeType === Node.COMMENT_NODE)
1177
1193
  continue;
1178
- const name = child['slot'] || (child.nodeValue?.trim() && 'default') || ('slot' in child && 'default');
1194
+ let name;
1195
+ if (child instanceof HTMLElement) {
1196
+ name = child.slot || 'default';
1197
+ }
1198
+ else if (child.nodeType === Node.TEXT_NODE && child.nodeValue?.trim()) {
1199
+ name = 'default';
1200
+ }
1179
1201
  if (!name)
1180
1202
  continue;
1181
1203
  slots[name] = true;
@@ -1198,10 +1220,10 @@ const toCSSColor = (input) => {
1198
1220
  };
1199
1221
 
1200
1222
  const toCSSUnit = (input) => {
1201
- if (input == null || input === '') {
1223
+ if (input === undefined || input === null || input === '') {
1202
1224
  return;
1203
1225
  }
1204
- if (typeof input === 'number' || !isNaN(Number(input))) {
1226
+ if (typeof input === 'number' || (typeof input === 'string' && !Number.isNaN(Number(input)))) {
1205
1227
  return `${input}px`;
1206
1228
  }
1207
1229
  if (/^\d+(\.\d+)?(px|pt|cm|mm|in|em|rem|%|vw|vh)$/.test(input)) {
@@ -1210,7 +1232,7 @@ const toCSSUnit = (input) => {
1210
1232
  };
1211
1233
 
1212
1234
  function toDecorator(util, ...args) {
1213
- return function (target, key) {
1235
+ return (target, key) => {
1214
1236
  defineProperty(target, key, {
1215
1237
  get() {
1216
1238
  return util(this, ...args);
@@ -1223,7 +1245,8 @@ const toProperty = (input, type) => {
1223
1245
  if (type === undefined)
1224
1246
  return input;
1225
1247
  const string = `${input}`;
1226
- if (TYPE_BOOLEAN & type || type === Boolean) {
1248
+ const typeNumber = typeof type === 'number' ? type : 0;
1249
+ if (TYPE_BOOLEAN & typeNumber || type === Boolean) {
1227
1250
  if (string === '')
1228
1251
  return true;
1229
1252
  if (string === 'true')
@@ -1231,55 +1254,51 @@ const toProperty = (input, type) => {
1231
1254
  if (string === 'false')
1232
1255
  return false;
1233
1256
  }
1234
- if (TYPE_NUMBER & type || type === Number) {
1235
- if (string != '' && !isNaN(input)) {
1236
- return parseFloat(input);
1257
+ if (TYPE_NUMBER & typeNumber || type === Number) {
1258
+ if (string !== '' && !Number.isNaN(Number(input))) {
1259
+ return parseFloat(string);
1237
1260
  }
1238
1261
  }
1239
- if (TYPE_NULL & type || type === null) {
1262
+ if (TYPE_NULL & typeNumber || type === null) {
1240
1263
  if (string === 'null') {
1241
1264
  return null;
1242
1265
  }
1243
1266
  }
1244
- if (TYPE_DATE & type || type === Date) {
1245
- const value = new Date(input);
1246
- if (value.toString() != 'Invalid Date') {
1267
+ if (TYPE_DATE & typeNumber || type === Date) {
1268
+ const value = new Date(string);
1269
+ if (!Number.isNaN(value.getTime())) {
1247
1270
  return value;
1248
1271
  }
1249
1272
  }
1250
- if (TYPE_ARRAY & type || type === Array) {
1273
+ if (TYPE_ARRAY & typeNumber || type === Array) {
1251
1274
  try {
1252
- const value = JSON.parse(input);
1253
- if (typeOf(value) == 'array') {
1275
+ const value = JSON.parse(string);
1276
+ if (typeOf(value) === 'array')
1254
1277
  return value;
1255
- }
1256
1278
  }
1257
1279
  catch { }
1258
1280
  }
1259
- if (TYPE_OBJECT & type || type === Object) {
1281
+ if (TYPE_OBJECT & typeNumber || type === Object) {
1260
1282
  try {
1261
- const value = JSON.parse(input);
1262
- if (typeOf(value) == 'object') {
1283
+ const value = JSON.parse(string);
1284
+ if (typeOf(value) === 'object') {
1263
1285
  return value;
1264
1286
  }
1265
1287
  }
1266
1288
  catch { }
1267
1289
  }
1268
- if (TYPE_UNDEFINED & type || type === undefined) {
1290
+ if (TYPE_UNDEFINED & typeNumber) {
1269
1291
  if (string === 'undefined') {
1270
1292
  return undefined;
1271
1293
  }
1272
1294
  }
1273
- if (TYPE_STRING & type || type === String) {
1274
- return input;
1295
+ if (TYPE_STRING & typeNumber || type === String) {
1296
+ return string;
1275
1297
  }
1276
- // TODO
1277
- // if (CONSTANTS.TYPE_BIGINT & type || type === BigInt) { }
1278
- // if (CONSTANTS.TYPE_ENUM & type || type === TODO) { }
1279
- // if (CONSTANTS.TYPE_FUNCTION & type || type === Function) { }
1298
+ // TODO: BigInt, Enum, Function
1280
1299
  try {
1281
1300
  // TODO
1282
- return JSON.parse(input);
1301
+ return JSON.parse(string);
1283
1302
  }
1284
1303
  catch {
1285
1304
  return input;
@@ -1291,13 +1310,15 @@ const toProperty = (input, type) => {
1291
1310
  */
1292
1311
  const toUnit = (input, unit = 'px') => {
1293
1312
  if (input === null || input === undefined || input === '')
1294
- return input;
1295
- if (isNaN(+input))
1296
1313
  return String(input);
1297
- return Number(input) + unit;
1314
+ if (Number.isNaN(Number(input)))
1315
+ return String(input);
1316
+ return `${Number(input)}${unit}`;
1298
1317
  };
1299
1318
 
1300
- const wrapMethod = (mode, target, key, handler) => {
1319
+ const wrapMethod = (mode, target, key,
1320
+ // biome-ignore lint: TODO
1321
+ handler) => {
1301
1322
  // Gets the original function
1302
1323
  const original = target[key];
1303
1324
  // Validate target property
@@ -1307,13 +1328,13 @@ const wrapMethod = (mode, target, key, handler) => {
1307
1328
  // Creates new function
1308
1329
  function wrapped(...args) {
1309
1330
  // Calls the handler before the original
1310
- if (mode == 'before') {
1331
+ if (mode === 'before') {
1311
1332
  handler.apply(this, args);
1312
1333
  }
1313
1334
  // Calls the original
1314
1335
  const result = original?.apply(this, args);
1315
1336
  // Calls the handler after the original
1316
- if (mode == 'after') {
1337
+ if (mode === 'after') {
1317
1338
  handler.apply(this, args);
1318
1339
  }
1319
1340
  // Returns the result
@@ -1328,7 +1349,7 @@ const wrapMethod = (mode, target, key, handler) => {
1328
1349
  * making it easier to reference `this` within the method.
1329
1350
  */
1330
1351
  function Bind() {
1331
- return function (target, key, descriptor) {
1352
+ return (_target, key, descriptor) => {
1332
1353
  const original = descriptor.value;
1333
1354
  return {
1334
1355
  configurable: true,
@@ -1345,8 +1366,9 @@ function Bind() {
1345
1366
  };
1346
1367
  }
1347
1368
 
1369
+ // biome-ignore-all lint: TODO
1348
1370
  function Provider(namespace) {
1349
- return function (target, key) {
1371
+ return (target, key) => {
1350
1372
  const symbol = Symbol();
1351
1373
  const [MAIN, SUB] = namespace.split('.');
1352
1374
  const prefix = `${KEY}:${MAIN}`;
@@ -1393,12 +1415,14 @@ function Provider(namespace) {
1393
1415
  cleanups(this).set(type, cleanup);
1394
1416
  });
1395
1417
  wrapMethod('after', target, LIFECYCLE_DISCONNECTED, function () {
1396
- cleanups(this).forEach((cleanup) => cleanup());
1418
+ cleanups(this).forEach((cleanup) => {
1419
+ cleanup();
1420
+ });
1397
1421
  });
1398
1422
  };
1399
1423
  }
1400
1424
  function Consumer(namespace) {
1401
- return function (target, key) {
1425
+ return (target, key) => {
1402
1426
  const symbol = Symbol();
1403
1427
  const [MAIN, SUB] = namespace.split('.');
1404
1428
  const prefix = `${KEY}:${MAIN}`;
@@ -1414,7 +1438,7 @@ function Consumer(namespace) {
1414
1438
  if (SUB && this[SUB])
1415
1439
  return;
1416
1440
  // TODO
1417
- let connected;
1441
+ let connected = false;
1418
1442
  const options = {
1419
1443
  bubbles: true
1420
1444
  };
@@ -1460,7 +1484,9 @@ function Consumer(namespace) {
1460
1484
  dispatch(window, `${type}:presence`);
1461
1485
  });
1462
1486
  wrapMethod('after', target, LIFECYCLE_DISCONNECTED, function () {
1463
- cleanups(this).forEach((cleanup) => cleanup());
1487
+ cleanups(this).forEach((cleanup) => {
1488
+ cleanup();
1489
+ });
1464
1490
  });
1465
1491
  };
1466
1492
  }
@@ -1473,7 +1499,7 @@ function Consumer(namespace) {
1473
1499
  * @param {number} delay - The debounce delay in milliseconds.
1474
1500
  */
1475
1501
  function Debounce(delay = 0) {
1476
- return function (target, key, descriptor) {
1502
+ return (target, key, descriptor) => {
1477
1503
  const KEY = Symbol();
1478
1504
  const original = descriptor.value;
1479
1505
  function clear() {
@@ -1508,27 +1534,36 @@ function Direction() {
1508
1534
  * and its name, in kebab-case, serves as the element name.
1509
1535
  */
1510
1536
  function Element() {
1511
- return function (constructor) {
1537
+ // biome-ignore lint: TODO
1538
+ return (constructor) => {
1512
1539
  if (isServer())
1513
1540
  return;
1514
1541
  const tag = getTag(constructor);
1542
+ if (!tag)
1543
+ return;
1515
1544
  if (customElements.get(tag))
1516
1545
  return;
1517
1546
  customElements.define(tag, proxy(constructor));
1518
1547
  };
1519
1548
  }
1549
+ // biome-ignore lint: TODO
1520
1550
  const proxy = (constructor) => {
1521
1551
  return class Plus extends HTMLElement {
1522
1552
  #instance;
1553
+ // biome-ignore lint: TODO
1523
1554
  static formAssociated = constructor['formAssociated'];
1555
+ // biome-ignore lint: TODO
1524
1556
  static observedAttributes = constructor['observedAttributes'];
1525
1557
  constructor() {
1526
1558
  super();
1527
1559
  this.attachShadow({
1528
1560
  mode: 'open',
1561
+ // biome-ignore lint: TODO
1529
1562
  delegatesFocus: constructor['delegatesFocus'],
1563
+ // biome-ignore lint: TODO
1530
1564
  slotAssignment: constructor['slotAssignment']
1531
1565
  });
1566
+ // biome-ignore lint: TODO
1532
1567
  this.#instance = new constructor();
1533
1568
  this.#instance[API_HOST] = () => this;
1534
1569
  call(this.#instance, LIFECYCLE_CONSTRUCTED);
@@ -1537,15 +1572,15 @@ const proxy = (constructor) => {
1537
1572
  call(this.#instance, LIFECYCLE_ADOPTED);
1538
1573
  }
1539
1574
  attributeChangedCallback(key, prev, next) {
1540
- if (prev != next) {
1541
- this.#instance['RAW:' + key] = next;
1575
+ if (prev !== next) {
1576
+ this.#instance[`RAW:${key}`] = next;
1542
1577
  }
1543
1578
  }
1544
1579
  connectedCallback() {
1545
1580
  // TODO: experimental for global config
1546
1581
  (() => {
1547
- const namespace = getNamespace(this.#instance);
1548
- const tag = getTag(this.#instance);
1582
+ const namespace = getNamespace(this.#instance) || '';
1583
+ const tag = getTag(this.#instance) || '';
1549
1584
  const properties = getConfig(namespace).elements?.[tag]?.properties;
1550
1585
  if (!properties)
1551
1586
  return;
@@ -1559,11 +1594,11 @@ const proxy = (constructor) => {
1559
1594
  if (!props)
1560
1595
  return;
1561
1596
  for (const [key, value] of Object.entries(props)) {
1562
- if (this[key] != undefined)
1597
+ if (this[key] !== undefined)
1563
1598
  continue;
1564
- if (key == 'children')
1599
+ if (key === 'children')
1565
1600
  continue;
1566
- if (typeof value != 'object')
1601
+ if (typeof value !== 'object')
1567
1602
  continue;
1568
1603
  this[key] = value;
1569
1604
  }
@@ -1587,7 +1622,7 @@ const proxy = (constructor) => {
1587
1622
  * @param options An object that configures options for the event dispatcher.
1588
1623
  */
1589
1624
  function Event(options = {}) {
1590
- return function (target, key) {
1625
+ return (target, key) => {
1591
1626
  target[key] = function (detail) {
1592
1627
  const element = host(this);
1593
1628
  const framework = getFramework(this);
@@ -1599,6 +1634,7 @@ function Event(options = {}) {
1599
1634
  options.bubbles = true;
1600
1635
  type = pascalCase(type);
1601
1636
  try {
1637
+ // biome-ignore lint: TODO
1602
1638
  window['Blazor'].registerCustomEventType(type, {
1603
1639
  createEventArgs: (event) => ({
1604
1640
  detail: event.detail
@@ -1620,7 +1656,7 @@ function Event(options = {}) {
1620
1656
  break;
1621
1657
  }
1622
1658
  let event;
1623
- const resolver = getConfig(getNamespace(target)).event?.resolver;
1659
+ const resolver = getConfig(getNamespace(target) || '').event?.resolver;
1624
1660
  event ||= resolver?.({ detail, element, framework, options, type });
1625
1661
  event && element.dispatchEvent(event);
1626
1662
  event ||= dispatch(this, type, { ...options, detail });
@@ -1653,7 +1689,7 @@ function IsRTL() {
1653
1689
  * for the event listener.
1654
1690
  */
1655
1691
  function Listen(type, options) {
1656
- return function (target, key, descriptor) {
1692
+ return (target, key, descriptor) => {
1657
1693
  const element = (instance) => {
1658
1694
  switch (options?.target) {
1659
1695
  case 'body':
@@ -1683,7 +1719,7 @@ function Listen(type, options) {
1683
1719
  * and invoke it as needed, both internally and externally.
1684
1720
  */
1685
1721
  function Method() {
1686
- return function (target, key, descriptor) {
1722
+ return (target, key, _descriptor) => {
1687
1723
  wrapMethod('before', target, LIFECYCLE_CONSTRUCTED, function () {
1688
1724
  host(this)[key] = this[key].bind(this);
1689
1725
  });
@@ -1720,17 +1756,18 @@ const getMedias = (breakpoints) => {
1720
1756
  }, {});
1721
1757
  };
1722
1758
  const matchContainer = (element, container) => {
1723
- const $element = element;
1724
1759
  const getData = () => {
1725
- if ($element[CONTAINER_DATA])
1726
- return $element[CONTAINER_DATA];
1760
+ if (element[CONTAINER_DATA])
1761
+ return element[CONTAINER_DATA];
1727
1762
  const listeners = new Set();
1728
1763
  const observer = new ResizeObserver(() => {
1729
- listeners.forEach((listener) => listener());
1764
+ listeners.forEach((listener) => {
1765
+ listener();
1766
+ });
1730
1767
  });
1731
1768
  observer.observe(element);
1732
- $element[CONTAINER_DATA] = { listeners, observer };
1733
- return $element[CONTAINER_DATA];
1769
+ element[CONTAINER_DATA] = { listeners, observer };
1770
+ return element[CONTAINER_DATA];
1734
1771
  };
1735
1772
  const getMatches = () => {
1736
1773
  const width = element.offsetWidth;
@@ -1738,16 +1775,16 @@ const matchContainer = (element, container) => {
1738
1775
  (container.max === undefined || width <= container.max);
1739
1776
  return matches;
1740
1777
  };
1741
- const addEventListener = (type, listener) => {
1778
+ const addEventListener = (_type, listener) => {
1742
1779
  getData().listeners.add(listener);
1743
1780
  };
1744
- const removeEventListener = (type, listener) => {
1781
+ const removeEventListener = (_type, listener) => {
1745
1782
  const data = getData();
1746
1783
  data.listeners.delete(listener);
1747
1784
  if (data.listeners.size !== 0)
1748
1785
  return;
1749
1786
  data.observer.disconnect();
1750
- delete $element[CONTAINER_DATA];
1787
+ delete element[CONTAINER_DATA];
1751
1788
  };
1752
1789
  return {
1753
1790
  get matches() {
@@ -1758,26 +1795,27 @@ const matchContainer = (element, container) => {
1758
1795
  };
1759
1796
  };
1760
1797
  function Overrides() {
1761
- return function (target, key) {
1798
+ return (target, key) => {
1762
1799
  const DISPOSERS = Symbol();
1763
- const breakpoints = getConfig(getNamespace(target)).breakpoints || {};
1800
+ const breakpoints = getConfig(getNamespace(target) || '').breakpoints || {};
1764
1801
  const containers = getContainers(breakpoints);
1765
1802
  const medias = getMedias(breakpoints);
1766
1803
  wrapMethod('after', target, LIFECYCLE_UPDATE, function (states) {
1767
1804
  if (!states.has(key))
1768
1805
  return;
1769
- const disposers = (this[DISPOSERS] ??= new Map());
1806
+ this[DISPOSERS] ??= new Map();
1807
+ const disposers = this[DISPOSERS];
1770
1808
  const overrides = this[key] || {};
1771
1809
  const activeKeys = new Set(disposers.keys());
1772
1810
  const overrideKeys = Object.keys(overrides);
1773
1811
  const containerKeys = overrideKeys.filter((breakpoint) => breakpoint in containers);
1774
1812
  const mediaKeys = overrideKeys.filter((breakpoint) => breakpoint in medias);
1775
- let timeout;
1813
+ let timeout = -1;
1776
1814
  let next = {};
1777
1815
  const apply = (key) => {
1778
1816
  clearTimeout(timeout);
1779
1817
  Object.assign(next, overrides[key]);
1780
- timeout = setTimeout(() => {
1818
+ timeout = window.setTimeout(() => {
1781
1819
  Object.assign(host(this), overrides[key]);
1782
1820
  next = {};
1783
1821
  }, 0);
@@ -1837,13 +1875,16 @@ function Overrides() {
1837
1875
  }
1838
1876
  for (const activeKey of activeKeys) {
1839
1877
  const disposer = disposers.get(activeKey);
1840
- disposer();
1878
+ disposer?.();
1841
1879
  disposers.delete(activeKey);
1842
1880
  }
1843
1881
  });
1844
1882
  wrapMethod('after', target, LIFECYCLE_DISCONNECTED, function () {
1845
- const disposers = (this[DISPOSERS] ??= new Map());
1846
- disposers.forEach((disposer) => disposer());
1883
+ this[DISPOSERS] ??= new Map();
1884
+ const disposers = this[DISPOSERS];
1885
+ disposers.forEach((disposer) => {
1886
+ disposer();
1887
+ });
1847
1888
  disposers.clear();
1848
1889
  });
1849
1890
  };
@@ -1854,7 +1895,7 @@ function Overrides() {
1854
1895
  * and updates the element when the property is set.
1855
1896
  */
1856
1897
  function Property(options) {
1857
- return function (target, key, descriptor) {
1898
+ return (target, key, descriptor) => {
1858
1899
  // Unique symbol for property storage to avoid naming conflicts
1859
1900
  const KEY = Symbol();
1860
1901
  // Unique symbol for the lock flag to prevent infinite loops during updates
@@ -1863,8 +1904,12 @@ function Property(options) {
1863
1904
  const attribute = options?.attribute || kebabCase(key);
1864
1905
  // Store the original setter (if it exists) to preserve its behavior
1865
1906
  const originalSetter = descriptor?.set;
1907
+ // Ensure the element class has an observedAttributes array
1908
+ // biome-ignore lint: TODO
1909
+ target.constructor['observedAttributes'] ||= [];
1866
1910
  // Register the attribute in the observedAttributes array for the element
1867
- (target.constructor['observedAttributes'] ||= []).push(attribute);
1911
+ // biome-ignore lint: TODO
1912
+ target.constructor['observedAttributes'].push(attribute);
1868
1913
  // Getter function to retrieve the property value
1869
1914
  function get() {
1870
1915
  return this[KEY];
@@ -1914,7 +1959,7 @@ function Property(options) {
1914
1959
  * Define a raw property setter to handle updates that trigger from the `attributeChangedCallback`,
1915
1960
  * To intercept and process raw attribute values before they are assigned to the property
1916
1961
  */
1917
- defineProperty(target, 'RAW:' + attribute, {
1962
+ defineProperty(target, `RAW:${attribute}`, {
1918
1963
  set(value) {
1919
1964
  if (!this[LOCKED]) {
1920
1965
  // Convert the raw value and set it to the corresponding property
@@ -2001,7 +2046,7 @@ function Slots() {
2001
2046
  * element to re-render upon the desired property changes.
2002
2047
  */
2003
2048
  function State() {
2004
- return function (target, key) {
2049
+ return (target, key) => {
2005
2050
  const KEY = Symbol();
2006
2051
  const name = String(key);
2007
2052
  defineProperty(target, key, {
@@ -2023,7 +2068,7 @@ function State() {
2023
2068
 
2024
2069
  // TODO: check the logic
2025
2070
  function Style() {
2026
- return function (target, key) {
2071
+ return (target, key) => {
2027
2072
  const LAST = Symbol();
2028
2073
  const SHEET = Symbol();
2029
2074
  wrapMethod('before', target, LIFECYCLE_UPDATED, function () {
@@ -2044,6 +2089,7 @@ function Style() {
2044
2089
  value = value.call(this);
2045
2090
  }
2046
2091
  if (value instanceof Promise) {
2092
+ // biome-ignore lint: TODO
2047
2093
  value.then(update((this[LAST] = value))).catch((error) => {
2048
2094
  throw new Error('TODO', { cause: error });
2049
2095
  });
@@ -2055,7 +2101,7 @@ function Style() {
2055
2101
  };
2056
2102
  }
2057
2103
  const toCssString = (input, parent) => {
2058
- if (typeof input == 'string') {
2104
+ if (typeof input === 'string') {
2059
2105
  return input.trim();
2060
2106
  }
2061
2107
  if (Array.isArray(input)) {
@@ -2064,7 +2110,9 @@ const toCssString = (input, parent) => {
2064
2110
  .filter(Boolean)
2065
2111
  .join('\n');
2066
2112
  }
2067
- if (typeof input != 'object')
2113
+ if (input === null)
2114
+ return '';
2115
+ if (typeof input !== 'object')
2068
2116
  return '';
2069
2117
  let result = '';
2070
2118
  for (const key of Object.keys(input)) {
@@ -2093,7 +2141,7 @@ const toCssString = (input, parent) => {
2093
2141
  * @param immediate Triggers the callback immediately after initialization.
2094
2142
  */
2095
2143
  function Watch(keys, immediate) {
2096
- return function (target, key) {
2144
+ return (target, key) => {
2097
2145
  // Gets all keys
2098
2146
  const all = [keys].flat().filter((item) => item);
2099
2147
  // Registers a lifecycle to detect changes.