thunderous 2.3.5 → 2.3.6

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/index.cjs CHANGED
@@ -451,9 +451,7 @@ var evaluateBindings = (element, fragment) => {
451
451
  child.replaceWith(childFragment);
452
452
  }
453
453
  } else if (child instanceof Element) {
454
- const attrRemoveQueue = [];
455
- const attrSetQueue = [];
456
- for (const attr of child.attributes) {
454
+ for (const attr of [...child.attributes]) {
457
455
  const attrName = attr.name;
458
456
  if (SIGNAL_BINDING_REGEX.test(attr.value)) {
459
457
  const textList = attr.value.split(SIGNAL_BINDING_REGEX);
@@ -473,12 +471,12 @@ var evaluateBindings = (element, fragment) => {
473
471
  }
474
472
  }
475
473
  if (hasNull && newText === "null" || attrName.startsWith("prop:")) {
476
- attrRemoveQueue.push(attrName);
474
+ child.removeAttribute(attrName);
477
475
  } else {
478
- attrSetQueue.push([attrName, newText]);
476
+ child.setAttribute(attrName, newText);
479
477
  }
480
478
  if (attrName.startsWith("prop:")) {
481
- attrRemoveQueue.push(attrName);
479
+ child.removeAttribute(attrName);
482
480
  const propName = attrName.replace("prop:", "");
483
481
  const newValue = hasNull && newText === "null" ? null : newText;
484
482
  if (!(propName in child)) logPropertyWarning(propName, child);
@@ -505,27 +503,21 @@ var evaluateBindings = (element, fragment) => {
505
503
  }
506
504
  }
507
505
  if (uniqueKey !== "" && !attrName.startsWith("prop:")) {
508
- attrSetQueue.push([attrName, `this.__customCallbackFns.get('${uniqueKey}')(event)`]);
506
+ child.setAttribute(attrName, `this.__customCallbackFns.get('${uniqueKey}')(event)`);
509
507
  } else if (attrName.startsWith("prop:")) {
510
- attrRemoveQueue.push(attrName);
508
+ child.removeAttribute(attrName);
511
509
  const propName = attrName.replace("prop:", "");
512
510
  if (!(propName in child)) logPropertyWarning(propName, child);
513
511
  child[propName] = child.__customCallbackFns.get(uniqueKey);
514
512
  }
515
513
  });
516
514
  } else if (attrName.startsWith("prop:")) {
517
- attrRemoveQueue.push(attrName);
515
+ child.removeAttribute(attrName);
518
516
  const propName = attrName.replace("prop:", "");
519
517
  if (!(propName in child)) logPropertyWarning(propName, child);
520
518
  child[propName] = attr.value;
521
519
  }
522
520
  }
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
521
  evaluateBindings(child, fragment);
530
522
  }
531
523
  }
@@ -595,6 +587,7 @@ var css = (strings, ...values) => {
595
587
  };
596
588
 
597
589
  // src/custom-element.ts
590
+ var ANY_BINDING_REGEX = /(\{\{.+:.+\}\})/;
598
591
  var customElement = (render, options) => {
599
592
  const _options = { ...DEFAULT_RENDER_OPTIONS, ...options };
600
593
  const {
@@ -689,6 +682,46 @@ var customElement = (render, options) => {
689
682
  }
690
683
  }
691
684
  });
685
+ #getPropSignal = ((prop, { allowUndefined = false } = {}) => {
686
+ if (!(prop in this.#propSignals)) this.#propSignals[prop] = createSignal();
687
+ const [_getter, _setter] = this.#propSignals[prop];
688
+ let setFromProp = false;
689
+ const setter = (newValue) => {
690
+ if (!setFromProp) this[prop] = newValue;
691
+ _setter(newValue);
692
+ };
693
+ const getter = () => {
694
+ const value = _getter();
695
+ if (value === void 0 && !allowUndefined) {
696
+ const error = new Error(
697
+ `Error accessing property: "${prop}"
698
+ You must set an initial value before calling a property signal's getter.
699
+ `
700
+ );
701
+ console.error(error);
702
+ throw error;
703
+ }
704
+ return value;
705
+ };
706
+ getter.getter = true;
707
+ const descriptor = Object.getOwnPropertyDescriptor(this, prop);
708
+ if (descriptor === void 0) {
709
+ Object.defineProperty(this, prop, {
710
+ get: getter,
711
+ set: (newValue) => {
712
+ setFromProp = true;
713
+ _setter(newValue);
714
+ setFromProp = false;
715
+ }
716
+ });
717
+ }
718
+ const publicSignal = [getter, setter];
719
+ publicSignal.init = (value) => {
720
+ _setter(value);
721
+ return [getter, setter];
722
+ };
723
+ return publicSignal;
724
+ }).bind(this);
692
725
  #render() {
693
726
  const root = this.#shadowRoot ?? this;
694
727
  renderState.currentShadowRoot = this.#shadowRoot;
@@ -732,43 +765,7 @@ var customElement = (render, options) => {
732
765
  }
733
766
  ),
734
767
  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
- },
768
+ get: (_, prop) => this.#getPropSignal(prop),
772
769
  set: () => {
773
770
  console.error("Signals must be assigned via setters.");
774
771
  return false;
@@ -825,6 +822,27 @@ You must set an initial value before calling a property signal's getter.
825
822
  constructor() {
826
823
  try {
827
824
  super();
825
+ if (!Object.prototype.hasOwnProperty.call(this, "__customCallbackFns")) {
826
+ this.__customCallbackFns = /* @__PURE__ */ new Map();
827
+ }
828
+ for (const attr of this.attributes) {
829
+ this.#attrSignals[attr.name] = createSignal(attr.value);
830
+ }
831
+ for (const [attrName, attr] of this.#attributesAsPropertiesMap) {
832
+ if (!(attrName in this.#attrSignals)) this.#attrSignals[attrName] = createSignal(null);
833
+ const propName = attr.prop;
834
+ const [getter] = this.#getPropSignal(propName, { allowUndefined: true });
835
+ createEffect(() => {
836
+ const value = getter();
837
+ if (value === void 0) return;
838
+ if (value !== null) {
839
+ this.setAttribute(attrName, String(value));
840
+ } else {
841
+ this.removeAttribute(attrName);
842
+ }
843
+ });
844
+ }
845
+ this.#render();
828
846
  } catch (error) {
829
847
  const _error = new Error(
830
848
  "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 +851,6 @@ You must set an initial value before calling a property signal's getter.
833
851
  console.error(_error);
834
852
  throw _error;
835
853
  }
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
854
  }
872
855
  connectedCallback() {
873
856
  if (this.#observer !== null) {
@@ -885,12 +868,19 @@ You must set an initial value before calling a property signal's getter.
885
868
  fn();
886
869
  }
887
870
  }
871
+ #attributesBusy = false;
888
872
  attributeChangedCallback(name, oldValue, newValue) {
873
+ if (this.#attributesBusy || ANY_BINDING_REGEX.test(newValue ?? "")) return;
889
874
  const [, attrSetter] = this.#attrSignals[name] ?? [];
890
875
  attrSetter?.(newValue);
891
876
  const prop = this.#attributesAsPropertiesMap.get(name);
892
877
  if (prop !== void 0) {
893
- this[prop.prop] = newValue === null ? null : prop.coerce(newValue);
878
+ const propName = prop.prop;
879
+ this.#attributesBusy = true;
880
+ const [, propSetter] = this.#getPropSignal(propName);
881
+ const propValue = newValue === null ? null : prop.coerce(newValue);
882
+ propSetter(propValue);
883
+ this.#attributesBusy = false;
894
884
  }
895
885
  for (const fn of this.#attributeChangedFns) {
896
886
  fn(name, oldValue, newValue);
package/dist/index.js CHANGED
@@ -416,9 +416,7 @@ var evaluateBindings = (element, fragment) => {
416
416
  child.replaceWith(childFragment);
417
417
  }
418
418
  } else if (child instanceof Element) {
419
- const attrRemoveQueue = [];
420
- const attrSetQueue = [];
421
- for (const attr of child.attributes) {
419
+ for (const attr of [...child.attributes]) {
422
420
  const attrName = attr.name;
423
421
  if (SIGNAL_BINDING_REGEX.test(attr.value)) {
424
422
  const textList = attr.value.split(SIGNAL_BINDING_REGEX);
@@ -438,12 +436,12 @@ var evaluateBindings = (element, fragment) => {
438
436
  }
439
437
  }
440
438
  if (hasNull && newText === "null" || attrName.startsWith("prop:")) {
441
- attrRemoveQueue.push(attrName);
439
+ child.removeAttribute(attrName);
442
440
  } else {
443
- attrSetQueue.push([attrName, newText]);
441
+ child.setAttribute(attrName, newText);
444
442
  }
445
443
  if (attrName.startsWith("prop:")) {
446
- attrRemoveQueue.push(attrName);
444
+ child.removeAttribute(attrName);
447
445
  const propName = attrName.replace("prop:", "");
448
446
  const newValue = hasNull && newText === "null" ? null : newText;
449
447
  if (!(propName in child)) logPropertyWarning(propName, child);
@@ -470,27 +468,21 @@ var evaluateBindings = (element, fragment) => {
470
468
  }
471
469
  }
472
470
  if (uniqueKey !== "" && !attrName.startsWith("prop:")) {
473
- attrSetQueue.push([attrName, `this.__customCallbackFns.get('${uniqueKey}')(event)`]);
471
+ child.setAttribute(attrName, `this.__customCallbackFns.get('${uniqueKey}')(event)`);
474
472
  } else if (attrName.startsWith("prop:")) {
475
- attrRemoveQueue.push(attrName);
473
+ child.removeAttribute(attrName);
476
474
  const propName = attrName.replace("prop:", "");
477
475
  if (!(propName in child)) logPropertyWarning(propName, child);
478
476
  child[propName] = child.__customCallbackFns.get(uniqueKey);
479
477
  }
480
478
  });
481
479
  } else if (attrName.startsWith("prop:")) {
482
- attrRemoveQueue.push(attrName);
480
+ child.removeAttribute(attrName);
483
481
  const propName = attrName.replace("prop:", "");
484
482
  if (!(propName in child)) logPropertyWarning(propName, child);
485
483
  child[propName] = attr.value;
486
484
  }
487
485
  }
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
486
  evaluateBindings(child, fragment);
495
487
  }
496
488
  }
@@ -560,6 +552,7 @@ var css = (strings, ...values) => {
560
552
  };
561
553
 
562
554
  // src/custom-element.ts
555
+ var ANY_BINDING_REGEX = /(\{\{.+:.+\}\})/;
563
556
  var customElement = (render, options) => {
564
557
  const _options = { ...DEFAULT_RENDER_OPTIONS, ...options };
565
558
  const {
@@ -654,6 +647,46 @@ var customElement = (render, options) => {
654
647
  }
655
648
  }
656
649
  });
650
+ #getPropSignal = ((prop, { allowUndefined = false } = {}) => {
651
+ if (!(prop in this.#propSignals)) this.#propSignals[prop] = createSignal();
652
+ const [_getter, _setter] = this.#propSignals[prop];
653
+ let setFromProp = false;
654
+ const setter = (newValue) => {
655
+ if (!setFromProp) this[prop] = newValue;
656
+ _setter(newValue);
657
+ };
658
+ const getter = () => {
659
+ const value = _getter();
660
+ if (value === void 0 && !allowUndefined) {
661
+ const error = new Error(
662
+ `Error accessing property: "${prop}"
663
+ You must set an initial value before calling a property signal's getter.
664
+ `
665
+ );
666
+ console.error(error);
667
+ throw error;
668
+ }
669
+ return value;
670
+ };
671
+ getter.getter = true;
672
+ const descriptor = Object.getOwnPropertyDescriptor(this, prop);
673
+ if (descriptor === void 0) {
674
+ Object.defineProperty(this, prop, {
675
+ get: getter,
676
+ set: (newValue) => {
677
+ setFromProp = true;
678
+ _setter(newValue);
679
+ setFromProp = false;
680
+ }
681
+ });
682
+ }
683
+ const publicSignal = [getter, setter];
684
+ publicSignal.init = (value) => {
685
+ _setter(value);
686
+ return [getter, setter];
687
+ };
688
+ return publicSignal;
689
+ }).bind(this);
657
690
  #render() {
658
691
  const root = this.#shadowRoot ?? this;
659
692
  renderState.currentShadowRoot = this.#shadowRoot;
@@ -697,43 +730,7 @@ var customElement = (render, options) => {
697
730
  }
698
731
  ),
699
732
  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
- },
733
+ get: (_, prop) => this.#getPropSignal(prop),
737
734
  set: () => {
738
735
  console.error("Signals must be assigned via setters.");
739
736
  return false;
@@ -790,6 +787,27 @@ You must set an initial value before calling a property signal's getter.
790
787
  constructor() {
791
788
  try {
792
789
  super();
790
+ if (!Object.prototype.hasOwnProperty.call(this, "__customCallbackFns")) {
791
+ this.__customCallbackFns = /* @__PURE__ */ new Map();
792
+ }
793
+ for (const attr of this.attributes) {
794
+ this.#attrSignals[attr.name] = createSignal(attr.value);
795
+ }
796
+ for (const [attrName, attr] of this.#attributesAsPropertiesMap) {
797
+ if (!(attrName in this.#attrSignals)) this.#attrSignals[attrName] = createSignal(null);
798
+ const propName = attr.prop;
799
+ const [getter] = this.#getPropSignal(propName, { allowUndefined: true });
800
+ createEffect(() => {
801
+ const value = getter();
802
+ if (value === void 0) return;
803
+ if (value !== null) {
804
+ this.setAttribute(attrName, String(value));
805
+ } else {
806
+ this.removeAttribute(attrName);
807
+ }
808
+ });
809
+ }
810
+ this.#render();
793
811
  } catch (error) {
794
812
  const _error = new Error(
795
813
  "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 +816,6 @@ You must set an initial value before calling a property signal's getter.
798
816
  console.error(_error);
799
817
  throw _error;
800
818
  }
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
819
  }
837
820
  connectedCallback() {
838
821
  if (this.#observer !== null) {
@@ -850,12 +833,19 @@ You must set an initial value before calling a property signal's getter.
850
833
  fn();
851
834
  }
852
835
  }
836
+ #attributesBusy = false;
853
837
  attributeChangedCallback(name, oldValue, newValue) {
838
+ if (this.#attributesBusy || ANY_BINDING_REGEX.test(newValue ?? "")) return;
854
839
  const [, attrSetter] = this.#attrSignals[name] ?? [];
855
840
  attrSetter?.(newValue);
856
841
  const prop = this.#attributesAsPropertiesMap.get(name);
857
842
  if (prop !== void 0) {
858
- this[prop.prop] = newValue === null ? null : prop.coerce(newValue);
843
+ const propName = prop.prop;
844
+ this.#attributesBusy = true;
845
+ const [, propSetter] = this.#getPropSignal(propName);
846
+ const propValue = newValue === null ? null : prop.coerce(newValue);
847
+ propSetter(propValue);
848
+ this.#attributesBusy = false;
859
849
  }
860
850
  for (const fn of this.#attributeChangedFns) {
861
851
  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.6",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",