thunderous 2.3.5 → 2.3.7

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/index.cjs +93 -118
  2. package/dist/index.js +93 -118
  3. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -50,8 +50,6 @@ var DEFAULT_RENDER_OPTIONS = {
50
50
 
51
51
  // src/signals.ts
52
52
  var subscriber = null;
53
- var updateQueue = /* @__PURE__ */ new Set();
54
- var isBatchingUpdates = false;
55
53
  var createSignal = (initVal, options) => {
56
54
  const subscribers = /* @__PURE__ */ new Set();
57
55
  let value = initVal;
@@ -82,36 +80,23 @@ var createSignal = (initVal, options) => {
82
80
  const oldValue = value;
83
81
  value = newValue;
84
82
  for (const fn of subscribers) {
85
- updateQueue.add(fn);
86
- }
87
- if (!isBatchingUpdates) {
88
- isBatchingUpdates = true;
89
- queueMicrotask(() => {
90
- while (updateQueue.size > 0) {
91
- const updates = Array.from(updateQueue);
92
- updateQueue.clear();
93
- for (const fn of updates) {
94
- try {
95
- fn();
96
- } catch (error) {
97
- console.error("Error in subscriber:", { error, oldValue, newValue, fn });
98
- }
99
- }
100
- }
101
- if (options?.debugMode === true || setterOptions?.debugMode === true) {
102
- let label = "anonymous signal";
103
- if (options?.label !== void 0) {
104
- label = `(${options.label})`;
105
- if (setterOptions?.label !== void 0) {
106
- label += ` ${setterOptions.label}`;
107
- }
108
- } else if (setterOptions?.label !== void 0) {
109
- label = setterOptions.label;
83
+ try {
84
+ fn();
85
+ } catch (error) {
86
+ console.error("Error in subscriber:", { error, oldValue, newValue, fn });
87
+ }
88
+ if (options?.debugMode === true || setterOptions?.debugMode === true) {
89
+ let label = "anonymous signal";
90
+ if (options?.label !== void 0) {
91
+ label = `(${options.label})`;
92
+ if (setterOptions?.label !== void 0) {
93
+ label += ` ${setterOptions.label}`;
110
94
  }
111
- console.log("Signal set:", { oldValue, newValue, subscribers, label });
95
+ } else if (setterOptions?.label !== void 0) {
96
+ label = setterOptions.label;
112
97
  }
113
- isBatchingUpdates = false;
114
- });
98
+ console.log("Signal set:", { oldValue, newValue, subscribers, label });
99
+ }
115
100
  }
116
101
  };
117
102
  return [getter, setter];
@@ -451,9 +436,7 @@ var evaluateBindings = (element, fragment) => {
451
436
  child.replaceWith(childFragment);
452
437
  }
453
438
  } else if (child instanceof Element) {
454
- const attrRemoveQueue = [];
455
- const attrSetQueue = [];
456
- for (const attr of child.attributes) {
439
+ for (const attr of [...child.attributes]) {
457
440
  const attrName = attr.name;
458
441
  if (SIGNAL_BINDING_REGEX.test(attr.value)) {
459
442
  const textList = attr.value.split(SIGNAL_BINDING_REGEX);
@@ -473,12 +456,12 @@ var evaluateBindings = (element, fragment) => {
473
456
  }
474
457
  }
475
458
  if (hasNull && newText === "null" || attrName.startsWith("prop:")) {
476
- attrRemoveQueue.push(attrName);
459
+ child.removeAttribute(attrName);
477
460
  } else {
478
- attrSetQueue.push([attrName, newText]);
461
+ child.setAttribute(attrName, newText);
479
462
  }
480
463
  if (attrName.startsWith("prop:")) {
481
- attrRemoveQueue.push(attrName);
464
+ child.removeAttribute(attrName);
482
465
  const propName = attrName.replace("prop:", "");
483
466
  const newValue = hasNull && newText === "null" ? null : newText;
484
467
  if (!(propName in child)) logPropertyWarning(propName, child);
@@ -505,27 +488,21 @@ var evaluateBindings = (element, fragment) => {
505
488
  }
506
489
  }
507
490
  if (uniqueKey !== "" && !attrName.startsWith("prop:")) {
508
- attrSetQueue.push([attrName, `this.__customCallbackFns.get('${uniqueKey}')(event)`]);
491
+ child.setAttribute(attrName, `this.__customCallbackFns.get('${uniqueKey}')(event)`);
509
492
  } else if (attrName.startsWith("prop:")) {
510
- attrRemoveQueue.push(attrName);
493
+ child.removeAttribute(attrName);
511
494
  const propName = attrName.replace("prop:", "");
512
495
  if (!(propName in child)) logPropertyWarning(propName, child);
513
496
  child[propName] = child.__customCallbackFns.get(uniqueKey);
514
497
  }
515
498
  });
516
499
  } else if (attrName.startsWith("prop:")) {
517
- attrRemoveQueue.push(attrName);
500
+ child.removeAttribute(attrName);
518
501
  const propName = attrName.replace("prop:", "");
519
502
  if (!(propName in child)) logPropertyWarning(propName, child);
520
503
  child[propName] = attr.value;
521
504
  }
522
505
  }
523
- for (const attrName of attrRemoveQueue) {
524
- child.removeAttribute(attrName);
525
- }
526
- for (const [name, value] of attrSetQueue) {
527
- child.setAttribute(name, value);
528
- }
529
506
  evaluateBindings(child, fragment);
530
507
  }
531
508
  }
@@ -595,6 +572,7 @@ var css = (strings, ...values) => {
595
572
  };
596
573
 
597
574
  // src/custom-element.ts
575
+ var ANY_BINDING_REGEX = /(\{\{.+:.+\}\})/;
598
576
  var customElement = (render, options) => {
599
577
  const _options = { ...DEFAULT_RENDER_OPTIONS, ...options };
600
578
  const {
@@ -689,6 +667,46 @@ var customElement = (render, options) => {
689
667
  }
690
668
  }
691
669
  });
670
+ #getPropSignal = ((prop, { allowUndefined = false } = {}) => {
671
+ if (!(prop in this.#propSignals)) this.#propSignals[prop] = createSignal();
672
+ const [_getter, _setter] = this.#propSignals[prop];
673
+ let setFromProp = false;
674
+ const setter = (newValue) => {
675
+ if (!setFromProp) this[prop] = newValue;
676
+ _setter(newValue);
677
+ };
678
+ const getter = () => {
679
+ const value = _getter();
680
+ if (value === void 0 && !allowUndefined) {
681
+ const error = new Error(
682
+ `Error accessing property: "${prop}"
683
+ You must set an initial value before calling a property signal's getter.
684
+ `
685
+ );
686
+ console.error(error);
687
+ throw error;
688
+ }
689
+ return value;
690
+ };
691
+ getter.getter = true;
692
+ const descriptor = Object.getOwnPropertyDescriptor(this, prop);
693
+ if (descriptor === void 0) {
694
+ Object.defineProperty(this, prop, {
695
+ get: getter,
696
+ set: (newValue) => {
697
+ setFromProp = true;
698
+ _setter(newValue);
699
+ setFromProp = false;
700
+ }
701
+ });
702
+ }
703
+ const publicSignal = [getter, setter];
704
+ publicSignal.init = (value) => {
705
+ _setter(value);
706
+ return [getter, setter];
707
+ };
708
+ return publicSignal;
709
+ }).bind(this);
692
710
  #render() {
693
711
  const root = this.#shadowRoot ?? this;
694
712
  renderState.currentShadowRoot = this.#shadowRoot;
@@ -732,43 +750,7 @@ var customElement = (render, options) => {
732
750
  }
733
751
  ),
734
752
  propSignals: new Proxy({}, {
735
- get: (_, prop) => {
736
- if (!(prop in this.#propSignals)) this.#propSignals[prop] = createSignal();
737
- const [_getter, _setter] = this.#propSignals[prop];
738
- let setFromProp = false;
739
- const setter = (newValue) => {
740
- if (!setFromProp) this[prop] = newValue;
741
- _setter(newValue);
742
- };
743
- const getter = () => {
744
- const value = _getter();
745
- if (value === void 0) {
746
- const error = new Error(
747
- `Error accessing property: "${prop}"
748
- You must set an initial value before calling a property signal's getter.
749
- `
750
- );
751
- console.error(error);
752
- throw error;
753
- }
754
- return value;
755
- };
756
- getter.getter = true;
757
- Object.defineProperty(this, prop, {
758
- get: getter,
759
- set: (newValue) => {
760
- setFromProp = true;
761
- _setter(newValue);
762
- setFromProp = false;
763
- }
764
- });
765
- const publicSignal = [getter, setter];
766
- publicSignal.init = (value) => {
767
- _setter(value);
768
- return [getter, setter];
769
- };
770
- return publicSignal;
771
- },
753
+ get: (_, prop) => this.#getPropSignal(prop),
772
754
  set: () => {
773
755
  console.error("Signals must be assigned via setters.");
774
756
  return false;
@@ -825,6 +807,27 @@ You must set an initial value before calling a property signal's getter.
825
807
  constructor() {
826
808
  try {
827
809
  super();
810
+ if (!Object.prototype.hasOwnProperty.call(this, "__customCallbackFns")) {
811
+ this.__customCallbackFns = /* @__PURE__ */ new Map();
812
+ }
813
+ for (const attr of this.attributes) {
814
+ this.#attrSignals[attr.name] = createSignal(attr.value);
815
+ }
816
+ for (const [attrName, attr] of this.#attributesAsPropertiesMap) {
817
+ if (!(attrName in this.#attrSignals)) this.#attrSignals[attrName] = createSignal(null);
818
+ const propName = attr.prop;
819
+ const [getter] = this.#getPropSignal(propName, { allowUndefined: true });
820
+ createEffect(() => {
821
+ const value = getter();
822
+ if (value === void 0) return;
823
+ if (value !== null) {
824
+ this.setAttribute(attrName, String(value));
825
+ } else {
826
+ this.removeAttribute(attrName);
827
+ }
828
+ });
829
+ }
830
+ this.#render();
828
831
  } catch (error) {
829
832
  const _error = new Error(
830
833
  "Error instantiating element:\nThis usually occurs if you have errors in the function body of your component. Check prior logs for possible causes.\n",
@@ -833,41 +836,6 @@ You must set an initial value before calling a property signal's getter.
833
836
  console.error(_error);
834
837
  throw _error;
835
838
  }
836
- if (!Object.prototype.hasOwnProperty.call(this, "__customCallbackFns")) {
837
- this.__customCallbackFns = /* @__PURE__ */ new Map();
838
- }
839
- for (const [attrName, attr] of this.#attributesAsPropertiesMap) {
840
- this.#attrSignals[attrName] = createSignal(null);
841
- Object.defineProperty(this, attr.prop, {
842
- get: () => {
843
- if (!(attrName in this.#attrSignals)) this.#attrSignals[attrName] = createSignal(null);
844
- const [getter] = this.#attrSignals[attrName];
845
- const raw = getter();
846
- const rawOnly = raw !== null && attr.value === null;
847
- const value = rawOnly ? attr.coerce(raw) : attr.value;
848
- return value === null ? null : value;
849
- },
850
- set: (newValue) => {
851
- const oldValue = attr.value;
852
- attr.value = newValue;
853
- if (!(attrName in this.#attrSignals)) this.#attrSignals[attrName] = createSignal(null);
854
- const [, attrSetter] = this.#attrSignals[attrName];
855
- const [, propSetter] = this.#propSignals[attrName];
856
- const attrValue = newValue === null ? null : String(newValue);
857
- if (String(oldValue) === attrValue) return;
858
- attrSetter(attrValue);
859
- propSetter(newValue);
860
- if (attrValue === null) this.removeAttribute(attrName);
861
- else this.setAttribute(attrName, attrValue);
862
- },
863
- configurable: true,
864
- enumerable: true
865
- });
866
- }
867
- for (const attr of this.attributes) {
868
- this.#attrSignals[attr.name] = createSignal(attr.value);
869
- }
870
- this.#render();
871
839
  }
872
840
  connectedCallback() {
873
841
  if (this.#observer !== null) {
@@ -885,12 +853,19 @@ You must set an initial value before calling a property signal's getter.
885
853
  fn();
886
854
  }
887
855
  }
856
+ #attributesBusy = false;
888
857
  attributeChangedCallback(name, oldValue, newValue) {
858
+ if (this.#attributesBusy || ANY_BINDING_REGEX.test(newValue ?? "")) return;
889
859
  const [, attrSetter] = this.#attrSignals[name] ?? [];
890
860
  attrSetter?.(newValue);
891
861
  const prop = this.#attributesAsPropertiesMap.get(name);
892
862
  if (prop !== void 0) {
893
- this[prop.prop] = newValue === null ? null : prop.coerce(newValue);
863
+ const propName = prop.prop;
864
+ this.#attributesBusy = true;
865
+ const [, propSetter] = this.#getPropSignal(propName);
866
+ const propValue = newValue === null ? null : prop.coerce(newValue);
867
+ propSetter(propValue);
868
+ this.#attributesBusy = false;
894
869
  }
895
870
  for (const fn of this.#attributeChangedFns) {
896
871
  fn(name, oldValue, newValue);
package/dist/index.js CHANGED
@@ -15,8 +15,6 @@ var DEFAULT_RENDER_OPTIONS = {
15
15
 
16
16
  // src/signals.ts
17
17
  var subscriber = null;
18
- var updateQueue = /* @__PURE__ */ new Set();
19
- var isBatchingUpdates = false;
20
18
  var createSignal = (initVal, options) => {
21
19
  const subscribers = /* @__PURE__ */ new Set();
22
20
  let value = initVal;
@@ -47,36 +45,23 @@ var createSignal = (initVal, options) => {
47
45
  const oldValue = value;
48
46
  value = newValue;
49
47
  for (const fn of subscribers) {
50
- updateQueue.add(fn);
51
- }
52
- if (!isBatchingUpdates) {
53
- isBatchingUpdates = true;
54
- queueMicrotask(() => {
55
- while (updateQueue.size > 0) {
56
- const updates = Array.from(updateQueue);
57
- updateQueue.clear();
58
- for (const fn of updates) {
59
- try {
60
- fn();
61
- } catch (error) {
62
- console.error("Error in subscriber:", { error, oldValue, newValue, fn });
63
- }
64
- }
65
- }
66
- if (options?.debugMode === true || setterOptions?.debugMode === true) {
67
- let label = "anonymous signal";
68
- if (options?.label !== void 0) {
69
- label = `(${options.label})`;
70
- if (setterOptions?.label !== void 0) {
71
- label += ` ${setterOptions.label}`;
72
- }
73
- } else if (setterOptions?.label !== void 0) {
74
- label = setterOptions.label;
48
+ try {
49
+ fn();
50
+ } catch (error) {
51
+ console.error("Error in subscriber:", { error, oldValue, newValue, fn });
52
+ }
53
+ if (options?.debugMode === true || setterOptions?.debugMode === true) {
54
+ let label = "anonymous signal";
55
+ if (options?.label !== void 0) {
56
+ label = `(${options.label})`;
57
+ if (setterOptions?.label !== void 0) {
58
+ label += ` ${setterOptions.label}`;
75
59
  }
76
- console.log("Signal set:", { oldValue, newValue, subscribers, label });
60
+ } else if (setterOptions?.label !== void 0) {
61
+ label = setterOptions.label;
77
62
  }
78
- isBatchingUpdates = false;
79
- });
63
+ console.log("Signal set:", { oldValue, newValue, subscribers, label });
64
+ }
80
65
  }
81
66
  };
82
67
  return [getter, setter];
@@ -416,9 +401,7 @@ var evaluateBindings = (element, fragment) => {
416
401
  child.replaceWith(childFragment);
417
402
  }
418
403
  } else if (child instanceof Element) {
419
- const attrRemoveQueue = [];
420
- const attrSetQueue = [];
421
- for (const attr of child.attributes) {
404
+ for (const attr of [...child.attributes]) {
422
405
  const attrName = attr.name;
423
406
  if (SIGNAL_BINDING_REGEX.test(attr.value)) {
424
407
  const textList = attr.value.split(SIGNAL_BINDING_REGEX);
@@ -438,12 +421,12 @@ var evaluateBindings = (element, fragment) => {
438
421
  }
439
422
  }
440
423
  if (hasNull && newText === "null" || attrName.startsWith("prop:")) {
441
- attrRemoveQueue.push(attrName);
424
+ child.removeAttribute(attrName);
442
425
  } else {
443
- attrSetQueue.push([attrName, newText]);
426
+ child.setAttribute(attrName, newText);
444
427
  }
445
428
  if (attrName.startsWith("prop:")) {
446
- attrRemoveQueue.push(attrName);
429
+ child.removeAttribute(attrName);
447
430
  const propName = attrName.replace("prop:", "");
448
431
  const newValue = hasNull && newText === "null" ? null : newText;
449
432
  if (!(propName in child)) logPropertyWarning(propName, child);
@@ -470,27 +453,21 @@ var evaluateBindings = (element, fragment) => {
470
453
  }
471
454
  }
472
455
  if (uniqueKey !== "" && !attrName.startsWith("prop:")) {
473
- attrSetQueue.push([attrName, `this.__customCallbackFns.get('${uniqueKey}')(event)`]);
456
+ child.setAttribute(attrName, `this.__customCallbackFns.get('${uniqueKey}')(event)`);
474
457
  } else if (attrName.startsWith("prop:")) {
475
- attrRemoveQueue.push(attrName);
458
+ child.removeAttribute(attrName);
476
459
  const propName = attrName.replace("prop:", "");
477
460
  if (!(propName in child)) logPropertyWarning(propName, child);
478
461
  child[propName] = child.__customCallbackFns.get(uniqueKey);
479
462
  }
480
463
  });
481
464
  } else if (attrName.startsWith("prop:")) {
482
- attrRemoveQueue.push(attrName);
465
+ child.removeAttribute(attrName);
483
466
  const propName = attrName.replace("prop:", "");
484
467
  if (!(propName in child)) logPropertyWarning(propName, child);
485
468
  child[propName] = attr.value;
486
469
  }
487
470
  }
488
- for (const attrName of attrRemoveQueue) {
489
- child.removeAttribute(attrName);
490
- }
491
- for (const [name, value] of attrSetQueue) {
492
- child.setAttribute(name, value);
493
- }
494
471
  evaluateBindings(child, fragment);
495
472
  }
496
473
  }
@@ -560,6 +537,7 @@ var css = (strings, ...values) => {
560
537
  };
561
538
 
562
539
  // src/custom-element.ts
540
+ var ANY_BINDING_REGEX = /(\{\{.+:.+\}\})/;
563
541
  var customElement = (render, options) => {
564
542
  const _options = { ...DEFAULT_RENDER_OPTIONS, ...options };
565
543
  const {
@@ -654,6 +632,46 @@ var customElement = (render, options) => {
654
632
  }
655
633
  }
656
634
  });
635
+ #getPropSignal = ((prop, { allowUndefined = false } = {}) => {
636
+ if (!(prop in this.#propSignals)) this.#propSignals[prop] = createSignal();
637
+ const [_getter, _setter] = this.#propSignals[prop];
638
+ let setFromProp = false;
639
+ const setter = (newValue) => {
640
+ if (!setFromProp) this[prop] = newValue;
641
+ _setter(newValue);
642
+ };
643
+ const getter = () => {
644
+ const value = _getter();
645
+ if (value === void 0 && !allowUndefined) {
646
+ const error = new Error(
647
+ `Error accessing property: "${prop}"
648
+ You must set an initial value before calling a property signal's getter.
649
+ `
650
+ );
651
+ console.error(error);
652
+ throw error;
653
+ }
654
+ return value;
655
+ };
656
+ getter.getter = true;
657
+ const descriptor = Object.getOwnPropertyDescriptor(this, prop);
658
+ if (descriptor === void 0) {
659
+ Object.defineProperty(this, prop, {
660
+ get: getter,
661
+ set: (newValue) => {
662
+ setFromProp = true;
663
+ _setter(newValue);
664
+ setFromProp = false;
665
+ }
666
+ });
667
+ }
668
+ const publicSignal = [getter, setter];
669
+ publicSignal.init = (value) => {
670
+ _setter(value);
671
+ return [getter, setter];
672
+ };
673
+ return publicSignal;
674
+ }).bind(this);
657
675
  #render() {
658
676
  const root = this.#shadowRoot ?? this;
659
677
  renderState.currentShadowRoot = this.#shadowRoot;
@@ -697,43 +715,7 @@ var customElement = (render, options) => {
697
715
  }
698
716
  ),
699
717
  propSignals: new Proxy({}, {
700
- get: (_, prop) => {
701
- if (!(prop in this.#propSignals)) this.#propSignals[prop] = createSignal();
702
- const [_getter, _setter] = this.#propSignals[prop];
703
- let setFromProp = false;
704
- const setter = (newValue) => {
705
- if (!setFromProp) this[prop] = newValue;
706
- _setter(newValue);
707
- };
708
- const getter = () => {
709
- const value = _getter();
710
- if (value === void 0) {
711
- const error = new Error(
712
- `Error accessing property: "${prop}"
713
- You must set an initial value before calling a property signal's getter.
714
- `
715
- );
716
- console.error(error);
717
- throw error;
718
- }
719
- return value;
720
- };
721
- getter.getter = true;
722
- Object.defineProperty(this, prop, {
723
- get: getter,
724
- set: (newValue) => {
725
- setFromProp = true;
726
- _setter(newValue);
727
- setFromProp = false;
728
- }
729
- });
730
- const publicSignal = [getter, setter];
731
- publicSignal.init = (value) => {
732
- _setter(value);
733
- return [getter, setter];
734
- };
735
- return publicSignal;
736
- },
718
+ get: (_, prop) => this.#getPropSignal(prop),
737
719
  set: () => {
738
720
  console.error("Signals must be assigned via setters.");
739
721
  return false;
@@ -790,6 +772,27 @@ You must set an initial value before calling a property signal's getter.
790
772
  constructor() {
791
773
  try {
792
774
  super();
775
+ if (!Object.prototype.hasOwnProperty.call(this, "__customCallbackFns")) {
776
+ this.__customCallbackFns = /* @__PURE__ */ new Map();
777
+ }
778
+ for (const attr of this.attributes) {
779
+ this.#attrSignals[attr.name] = createSignal(attr.value);
780
+ }
781
+ for (const [attrName, attr] of this.#attributesAsPropertiesMap) {
782
+ if (!(attrName in this.#attrSignals)) this.#attrSignals[attrName] = createSignal(null);
783
+ const propName = attr.prop;
784
+ const [getter] = this.#getPropSignal(propName, { allowUndefined: true });
785
+ createEffect(() => {
786
+ const value = getter();
787
+ if (value === void 0) return;
788
+ if (value !== null) {
789
+ this.setAttribute(attrName, String(value));
790
+ } else {
791
+ this.removeAttribute(attrName);
792
+ }
793
+ });
794
+ }
795
+ this.#render();
793
796
  } catch (error) {
794
797
  const _error = new Error(
795
798
  "Error instantiating element:\nThis usually occurs if you have errors in the function body of your component. Check prior logs for possible causes.\n",
@@ -798,41 +801,6 @@ You must set an initial value before calling a property signal's getter.
798
801
  console.error(_error);
799
802
  throw _error;
800
803
  }
801
- if (!Object.prototype.hasOwnProperty.call(this, "__customCallbackFns")) {
802
- this.__customCallbackFns = /* @__PURE__ */ new Map();
803
- }
804
- for (const [attrName, attr] of this.#attributesAsPropertiesMap) {
805
- this.#attrSignals[attrName] = createSignal(null);
806
- Object.defineProperty(this, attr.prop, {
807
- get: () => {
808
- if (!(attrName in this.#attrSignals)) this.#attrSignals[attrName] = createSignal(null);
809
- const [getter] = this.#attrSignals[attrName];
810
- const raw = getter();
811
- const rawOnly = raw !== null && attr.value === null;
812
- const value = rawOnly ? attr.coerce(raw) : attr.value;
813
- return value === null ? null : value;
814
- },
815
- set: (newValue) => {
816
- const oldValue = attr.value;
817
- attr.value = newValue;
818
- if (!(attrName in this.#attrSignals)) this.#attrSignals[attrName] = createSignal(null);
819
- const [, attrSetter] = this.#attrSignals[attrName];
820
- const [, propSetter] = this.#propSignals[attrName];
821
- const attrValue = newValue === null ? null : String(newValue);
822
- if (String(oldValue) === attrValue) return;
823
- attrSetter(attrValue);
824
- propSetter(newValue);
825
- if (attrValue === null) this.removeAttribute(attrName);
826
- else this.setAttribute(attrName, attrValue);
827
- },
828
- configurable: true,
829
- enumerable: true
830
- });
831
- }
832
- for (const attr of this.attributes) {
833
- this.#attrSignals[attr.name] = createSignal(attr.value);
834
- }
835
- this.#render();
836
804
  }
837
805
  connectedCallback() {
838
806
  if (this.#observer !== null) {
@@ -850,12 +818,19 @@ You must set an initial value before calling a property signal's getter.
850
818
  fn();
851
819
  }
852
820
  }
821
+ #attributesBusy = false;
853
822
  attributeChangedCallback(name, oldValue, newValue) {
823
+ if (this.#attributesBusy || ANY_BINDING_REGEX.test(newValue ?? "")) return;
854
824
  const [, attrSetter] = this.#attrSignals[name] ?? [];
855
825
  attrSetter?.(newValue);
856
826
  const prop = this.#attributesAsPropertiesMap.get(name);
857
827
  if (prop !== void 0) {
858
- this[prop.prop] = newValue === null ? null : prop.coerce(newValue);
828
+ const propName = prop.prop;
829
+ this.#attributesBusy = true;
830
+ const [, propSetter] = this.#getPropSignal(propName);
831
+ const propValue = newValue === null ? null : prop.coerce(newValue);
832
+ propSetter(propValue);
833
+ this.#attributesBusy = false;
859
834
  }
860
835
  for (const fn of this.#attributeChangedFns) {
861
836
  fn(name, oldValue, newValue);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thunderous",
3
- "version": "2.3.5",
3
+ "version": "2.3.7",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",