recur-tw 0.0.3-beta.5 → 0.0.4-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +631 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +95 -2
- package/dist/index.d.ts +95 -2
- package/dist/index.js +631 -12
- package/dist/index.js.map +1 -1
- package/dist/recur.umd.js +398 -21
- package/dist/recur.umd.js.map +1 -1
- package/package.json +12 -12
package/dist/index.cjs
CHANGED
|
@@ -606,6 +606,523 @@ var init_skeleton_loader = __esm({
|
|
|
606
606
|
}
|
|
607
607
|
});
|
|
608
608
|
|
|
609
|
+
// src/components/base/payment-form-skeleton.ts
|
|
610
|
+
var payment_form_skeleton_exports = {};
|
|
611
|
+
__export(payment_form_skeleton_exports, {
|
|
612
|
+
RecurPaymentFormSkeleton: () => RecurPaymentFormSkeleton
|
|
613
|
+
});
|
|
614
|
+
var RecurPaymentFormSkeleton;
|
|
615
|
+
var init_payment_form_skeleton = __esm({
|
|
616
|
+
"src/components/base/payment-form-skeleton.ts"() {
|
|
617
|
+
RecurPaymentFormSkeleton = class extends HTMLElement {
|
|
618
|
+
constructor() {
|
|
619
|
+
super();
|
|
620
|
+
this.attachShadow({ mode: "open" });
|
|
621
|
+
}
|
|
622
|
+
connectedCallback() {
|
|
623
|
+
this.render();
|
|
624
|
+
}
|
|
625
|
+
render() {
|
|
626
|
+
this.shadowRoot.innerHTML = `
|
|
627
|
+
<style>
|
|
628
|
+
:host {
|
|
629
|
+
display: block;
|
|
630
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
.payment-form-wrapper {
|
|
634
|
+
max-width: 500px;
|
|
635
|
+
margin: 0 auto;
|
|
636
|
+
padding: 24px;
|
|
637
|
+
background: white;
|
|
638
|
+
border-radius: 12px;
|
|
639
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
.form-header {
|
|
643
|
+
margin-bottom: 24px;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
.form-section {
|
|
647
|
+
margin-bottom: 24px;
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
.section-title {
|
|
651
|
+
margin: 0 0 12px 0;
|
|
652
|
+
font-size: 16px;
|
|
653
|
+
font-weight: 600;
|
|
654
|
+
color: #2d3748;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
.label {
|
|
658
|
+
font-size: 14px;
|
|
659
|
+
color: #4a5568;
|
|
660
|
+
margin-bottom: 4px;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
/* Skeleton base styles */
|
|
664
|
+
.skeleton {
|
|
665
|
+
background: linear-gradient(
|
|
666
|
+
90deg,
|
|
667
|
+
#f0f0f0 0%,
|
|
668
|
+
#f8f8f8 50%,
|
|
669
|
+
#f0f0f0 100%
|
|
670
|
+
);
|
|
671
|
+
background-size: 200% 100%;
|
|
672
|
+
animation: skeleton-loading 1.5s ease-in-out infinite;
|
|
673
|
+
border-radius: 4px;
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
@keyframes skeleton-loading {
|
|
677
|
+
0% {
|
|
678
|
+
background-position: 200% 0;
|
|
679
|
+
}
|
|
680
|
+
100% {
|
|
681
|
+
background-position: -200% 0;
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
.skeleton-title {
|
|
686
|
+
height: 28px;
|
|
687
|
+
width: 200px;
|
|
688
|
+
margin-bottom: 8px;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
.skeleton-subtitle {
|
|
692
|
+
height: 18px;
|
|
693
|
+
width: 320px;
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
.skeleton-order-summary {
|
|
697
|
+
padding: 16px;
|
|
698
|
+
background: #f7fafc;
|
|
699
|
+
border-radius: 6px;
|
|
700
|
+
margin-bottom: 24px;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
.skeleton-order-row {
|
|
704
|
+
display: flex;
|
|
705
|
+
justify-content: space-between;
|
|
706
|
+
align-items: center;
|
|
707
|
+
margin-bottom: 12px;
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
.skeleton-order-row:last-child {
|
|
711
|
+
margin-bottom: 0;
|
|
712
|
+
padding-top: 12px;
|
|
713
|
+
border-top: 1px solid #e2e8f0;
|
|
714
|
+
font-weight: 600;
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
.skeleton-order-value {
|
|
718
|
+
height: 20px;
|
|
719
|
+
width: 120px;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
.input-group {
|
|
723
|
+
margin-bottom: 16px;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
.input-group:last-child {
|
|
727
|
+
margin-bottom: 0;
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
.skeleton-input {
|
|
731
|
+
height: 44px;
|
|
732
|
+
width: 100%;
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
.card-fields-grid {
|
|
736
|
+
display: grid;
|
|
737
|
+
grid-template-columns: 1fr 1fr;
|
|
738
|
+
gap: 12px;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
.skeleton-button {
|
|
742
|
+
height: 44px;
|
|
743
|
+
width: 100%;
|
|
744
|
+
border-radius: 6px;
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
.security-note {
|
|
748
|
+
display: flex;
|
|
749
|
+
align-items: center;
|
|
750
|
+
gap: 8px;
|
|
751
|
+
padding: 12px;
|
|
752
|
+
background: #f7fafc;
|
|
753
|
+
border-radius: 6px;
|
|
754
|
+
margin-top: 16px;
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
.security-icon {
|
|
758
|
+
color: #48bb78;
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
.security-text {
|
|
762
|
+
font-size: 13px;
|
|
763
|
+
color: #4a5568;
|
|
764
|
+
margin: 0;
|
|
765
|
+
}
|
|
766
|
+
</style>
|
|
767
|
+
|
|
768
|
+
<div class="payment-form-wrapper">
|
|
769
|
+
<!-- Header Skeleton -->
|
|
770
|
+
<div class="form-header">
|
|
771
|
+
<div class="skeleton skeleton-title"></div>
|
|
772
|
+
<div class="skeleton skeleton-subtitle"></div>
|
|
773
|
+
</div>
|
|
774
|
+
|
|
775
|
+
<!-- Order Summary - Static labels, skeleton values -->
|
|
776
|
+
<div class="skeleton-order-summary">
|
|
777
|
+
<h3 class="section-title">\u8A02\u55AE\u6458\u8981</h3>
|
|
778
|
+
<div class="skeleton-order-row">
|
|
779
|
+
<span class="label">\u65B9\u6848</span>
|
|
780
|
+
<div class="skeleton skeleton-order-value"></div>
|
|
781
|
+
</div>
|
|
782
|
+
<div class="skeleton-order-row">
|
|
783
|
+
<span class="label">\u91D1\u984D</span>
|
|
784
|
+
<div class="skeleton skeleton-order-value"></div>
|
|
785
|
+
</div>
|
|
786
|
+
<div class="skeleton-order-row">
|
|
787
|
+
<span class="label">\u9031\u671F</span>
|
|
788
|
+
<div class="skeleton skeleton-order-value"></div>
|
|
789
|
+
</div>
|
|
790
|
+
<div class="skeleton-order-row">
|
|
791
|
+
<span class="label">\u7E3D\u8A08</span>
|
|
792
|
+
<div class="skeleton skeleton-order-value"></div>
|
|
793
|
+
</div>
|
|
794
|
+
</div>
|
|
795
|
+
|
|
796
|
+
<!-- Customer Info Section - Static labels, skeleton inputs -->
|
|
797
|
+
<div class="form-section">
|
|
798
|
+
<h3 class="section-title">\u5BA2\u6236\u8CC7\u8A0A</h3>
|
|
799
|
+
<div class="input-group">
|
|
800
|
+
<div class="label">\u59D3\u540D</div>
|
|
801
|
+
<div class="skeleton skeleton-input"></div>
|
|
802
|
+
</div>
|
|
803
|
+
<div class="input-group">
|
|
804
|
+
<div class="label">Email</div>
|
|
805
|
+
<div class="skeleton skeleton-input"></div>
|
|
806
|
+
</div>
|
|
807
|
+
<div class="input-group">
|
|
808
|
+
<div class="label">\u96FB\u8A71\uFF08\u9078\u586B\uFF09</div>
|
|
809
|
+
<div class="skeleton skeleton-input"></div>
|
|
810
|
+
</div>
|
|
811
|
+
</div>
|
|
812
|
+
|
|
813
|
+
<!-- Card Fields Section - Static labels, skeleton inputs -->
|
|
814
|
+
<div class="form-section">
|
|
815
|
+
<h3 class="section-title">\u4FE1\u7528\u5361\u8CC7\u8A0A</h3>
|
|
816
|
+
<div class="input-group">
|
|
817
|
+
<div class="label">\u4FE1\u7528\u5361\u865F</div>
|
|
818
|
+
<div class="skeleton skeleton-input"></div>
|
|
819
|
+
</div>
|
|
820
|
+
<div class="card-fields-grid">
|
|
821
|
+
<div class="input-group">
|
|
822
|
+
<div class="label">\u6709\u6548\u671F\u9650</div>
|
|
823
|
+
<div class="skeleton skeleton-input"></div>
|
|
824
|
+
</div>
|
|
825
|
+
<div class="input-group">
|
|
826
|
+
<div class="label">\u5B89\u5168\u78BC</div>
|
|
827
|
+
<div class="skeleton skeleton-input"></div>
|
|
828
|
+
</div>
|
|
829
|
+
</div>
|
|
830
|
+
</div>
|
|
831
|
+
|
|
832
|
+
<!-- Submit Button Skeleton -->
|
|
833
|
+
<div class="skeleton skeleton-button"></div>
|
|
834
|
+
|
|
835
|
+
<!-- Security Note -->
|
|
836
|
+
<div class="security-note">
|
|
837
|
+
<svg class="security-icon" width="16" height="16" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
838
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
|
|
839
|
+
</svg>
|
|
840
|
+
<p class="security-text">\u60A8\u7684\u4ED8\u6B3E\u8CC7\u8A0A\u7D93\u904E\u52A0\u5BC6\u4FDD\u8B77</p>
|
|
841
|
+
</div>
|
|
842
|
+
</div>
|
|
843
|
+
`;
|
|
844
|
+
}
|
|
845
|
+
};
|
|
846
|
+
if (typeof window !== "undefined" && !customElements.get("recur-payment-form-skeleton")) {
|
|
847
|
+
customElements.define("recur-payment-form-skeleton", RecurPaymentFormSkeleton);
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
});
|
|
851
|
+
|
|
852
|
+
// src/components/base/toast.ts
|
|
853
|
+
var toast_exports = {};
|
|
854
|
+
__export(toast_exports, {
|
|
855
|
+
RecurToast: () => RecurToast,
|
|
856
|
+
RecurToastContainer: () => RecurToastContainer
|
|
857
|
+
});
|
|
858
|
+
var RecurToast, _RecurToastContainer, RecurToastContainer;
|
|
859
|
+
var init_toast = __esm({
|
|
860
|
+
"src/components/base/toast.ts"() {
|
|
861
|
+
RecurToast = class extends HTMLElement {
|
|
862
|
+
static get observedAttributes() {
|
|
863
|
+
return ["message", "type", "duration"];
|
|
864
|
+
}
|
|
865
|
+
constructor() {
|
|
866
|
+
super();
|
|
867
|
+
this.attachShadow({ mode: "open" });
|
|
868
|
+
}
|
|
869
|
+
connectedCallback() {
|
|
870
|
+
this.render();
|
|
871
|
+
this.setupAutoDismiss();
|
|
872
|
+
}
|
|
873
|
+
get message() {
|
|
874
|
+
return this.getAttribute("message") || "Notification";
|
|
875
|
+
}
|
|
876
|
+
get type() {
|
|
877
|
+
const typeAttr = this.getAttribute("type");
|
|
878
|
+
if (typeAttr === "success" || typeAttr === "error") return typeAttr;
|
|
879
|
+
return "info";
|
|
880
|
+
}
|
|
881
|
+
get duration() {
|
|
882
|
+
const durationAttr = this.getAttribute("duration");
|
|
883
|
+
return durationAttr ? parseInt(durationAttr, 10) : 5e3;
|
|
884
|
+
}
|
|
885
|
+
setupAutoDismiss() {
|
|
886
|
+
const duration = this.duration;
|
|
887
|
+
if (duration > 0) {
|
|
888
|
+
setTimeout(() => this.dismiss(), duration);
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
dismiss() {
|
|
892
|
+
this.style.animation = "recur-toast-slide-out 0.3s ease-in-out";
|
|
893
|
+
setTimeout(() => this.remove(), 300);
|
|
894
|
+
}
|
|
895
|
+
getTypeIcon() {
|
|
896
|
+
switch (this.type) {
|
|
897
|
+
case "success":
|
|
898
|
+
return `<svg width="20" height="20" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
899
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
|
|
900
|
+
</svg>`;
|
|
901
|
+
case "error":
|
|
902
|
+
return `<svg width="20" height="20" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
903
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
|
904
|
+
</svg>`;
|
|
905
|
+
case "info":
|
|
906
|
+
default:
|
|
907
|
+
return `<svg width="20" height="20" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
908
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
|
909
|
+
</svg>`;
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
getTypeColor() {
|
|
913
|
+
switch (this.type) {
|
|
914
|
+
case "success":
|
|
915
|
+
return "#10b981";
|
|
916
|
+
// green-500
|
|
917
|
+
case "error":
|
|
918
|
+
return "#ef4444";
|
|
919
|
+
// red-500
|
|
920
|
+
case "info":
|
|
921
|
+
default:
|
|
922
|
+
return "#3b82f6";
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
render() {
|
|
926
|
+
const color = this.getTypeColor();
|
|
927
|
+
this.shadowRoot.innerHTML = `
|
|
928
|
+
<style>
|
|
929
|
+
:host {
|
|
930
|
+
display: block;
|
|
931
|
+
margin-bottom: 12px;
|
|
932
|
+
animation: recur-toast-slide-in 0.3s ease-out;
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
@keyframes recur-toast-slide-in {
|
|
936
|
+
from {
|
|
937
|
+
transform: translateX(100%);
|
|
938
|
+
opacity: 0;
|
|
939
|
+
}
|
|
940
|
+
to {
|
|
941
|
+
transform: translateX(0);
|
|
942
|
+
opacity: 1;
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
@keyframes recur-toast-slide-out {
|
|
947
|
+
from {
|
|
948
|
+
transform: translateX(0);
|
|
949
|
+
opacity: 1;
|
|
950
|
+
}
|
|
951
|
+
to {
|
|
952
|
+
transform: translateX(100%);
|
|
953
|
+
opacity: 0;
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
.toast {
|
|
958
|
+
display: flex;
|
|
959
|
+
align-items: center;
|
|
960
|
+
gap: 12px;
|
|
961
|
+
padding: 12px 16px;
|
|
962
|
+
background: white;
|
|
963
|
+
border-left: 4px solid ${color};
|
|
964
|
+
border-radius: 8px;
|
|
965
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
966
|
+
min-width: 280px;
|
|
967
|
+
max-width: 420px;
|
|
968
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
969
|
+
cursor: pointer;
|
|
970
|
+
transition: transform 0.2s ease;
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
.toast:hover {
|
|
974
|
+
transform: translateY(-2px);
|
|
975
|
+
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2);
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
.toast-icon {
|
|
979
|
+
flex-shrink: 0;
|
|
980
|
+
color: ${color};
|
|
981
|
+
display: flex;
|
|
982
|
+
align-items: center;
|
|
983
|
+
justify-content: center;
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
.toast-message {
|
|
987
|
+
flex: 1;
|
|
988
|
+
color: #374151;
|
|
989
|
+
font-size: 14px;
|
|
990
|
+
line-height: 1.5;
|
|
991
|
+
margin: 0;
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
.toast-close {
|
|
995
|
+
flex-shrink: 0;
|
|
996
|
+
color: #9ca3af;
|
|
997
|
+
cursor: pointer;
|
|
998
|
+
padding: 4px;
|
|
999
|
+
display: flex;
|
|
1000
|
+
align-items: center;
|
|
1001
|
+
justify-content: center;
|
|
1002
|
+
border-radius: 4px;
|
|
1003
|
+
transition: all 0.2s;
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
.toast-close:hover {
|
|
1007
|
+
color: #6b7280;
|
|
1008
|
+
background: #f3f4f6;
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
/* Dark mode support */
|
|
1012
|
+
:host([theme="dark"]) .toast {
|
|
1013
|
+
background: #1f2937;
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
:host([theme="dark"]) .toast-message {
|
|
1017
|
+
color: #e5e7eb;
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
:host([theme="dark"]) .toast-close {
|
|
1021
|
+
color: #6b7280;
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
:host([theme="dark"]) .toast-close:hover {
|
|
1025
|
+
color: #9ca3af;
|
|
1026
|
+
background: #374151;
|
|
1027
|
+
}
|
|
1028
|
+
</style>
|
|
1029
|
+
|
|
1030
|
+
<div class="toast" role="alert">
|
|
1031
|
+
<div class="toast-icon">
|
|
1032
|
+
${this.getTypeIcon()}
|
|
1033
|
+
</div>
|
|
1034
|
+
<p class="toast-message">${this.message}</p>
|
|
1035
|
+
<button class="toast-close" aria-label="Close">
|
|
1036
|
+
<svg width="16" height="16" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
1037
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
|
1038
|
+
</svg>
|
|
1039
|
+
</button>
|
|
1040
|
+
</div>
|
|
1041
|
+
`;
|
|
1042
|
+
const closeBtn = this.shadowRoot.querySelector(".toast-close");
|
|
1043
|
+
closeBtn?.addEventListener("click", (e) => {
|
|
1044
|
+
e.stopPropagation();
|
|
1045
|
+
this.dismiss();
|
|
1046
|
+
});
|
|
1047
|
+
const toast = this.shadowRoot.querySelector(".toast");
|
|
1048
|
+
toast?.addEventListener("click", () => this.dismiss());
|
|
1049
|
+
}
|
|
1050
|
+
/**
|
|
1051
|
+
* Static helper method to show toast
|
|
1052
|
+
*/
|
|
1053
|
+
static show(message, type = "info", duration = 5e3) {
|
|
1054
|
+
const container = RecurToastContainer.getInstance();
|
|
1055
|
+
const toast = document.createElement("recur-toast");
|
|
1056
|
+
toast.setAttribute("message", message);
|
|
1057
|
+
toast.setAttribute("type", type);
|
|
1058
|
+
toast.setAttribute("duration", duration.toString());
|
|
1059
|
+
container.appendChild(toast);
|
|
1060
|
+
return toast;
|
|
1061
|
+
}
|
|
1062
|
+
};
|
|
1063
|
+
_RecurToastContainer = class _RecurToastContainer extends HTMLElement {
|
|
1064
|
+
constructor() {
|
|
1065
|
+
super();
|
|
1066
|
+
this.attachShadow({ mode: "open" });
|
|
1067
|
+
}
|
|
1068
|
+
connectedCallback() {
|
|
1069
|
+
this.render();
|
|
1070
|
+
}
|
|
1071
|
+
render() {
|
|
1072
|
+
this.shadowRoot.innerHTML = `
|
|
1073
|
+
<style>
|
|
1074
|
+
:host {
|
|
1075
|
+
position: fixed;
|
|
1076
|
+
top: 24px;
|
|
1077
|
+
right: 24px;
|
|
1078
|
+
z-index: 9999;
|
|
1079
|
+
display: flex;
|
|
1080
|
+
flex-direction: column;
|
|
1081
|
+
pointer-events: none;
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
::slotted(*) {
|
|
1085
|
+
pointer-events: auto;
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
/* Mobile responsive */
|
|
1089
|
+
@media (max-width: 640px) {
|
|
1090
|
+
:host {
|
|
1091
|
+
top: 16px;
|
|
1092
|
+
right: 16px;
|
|
1093
|
+
left: 16px;
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
</style>
|
|
1097
|
+
|
|
1098
|
+
<slot></slot>
|
|
1099
|
+
`;
|
|
1100
|
+
}
|
|
1101
|
+
/**
|
|
1102
|
+
* Get or create singleton instance
|
|
1103
|
+
*/
|
|
1104
|
+
static getInstance() {
|
|
1105
|
+
if (!_RecurToastContainer.instance) {
|
|
1106
|
+
_RecurToastContainer.instance = document.querySelector("recur-toast-container");
|
|
1107
|
+
if (!_RecurToastContainer.instance) {
|
|
1108
|
+
_RecurToastContainer.instance = document.createElement("recur-toast-container");
|
|
1109
|
+
document.body.appendChild(_RecurToastContainer.instance);
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
return _RecurToastContainer.instance;
|
|
1113
|
+
}
|
|
1114
|
+
};
|
|
1115
|
+
__publicField(_RecurToastContainer, "instance", null);
|
|
1116
|
+
RecurToastContainer = _RecurToastContainer;
|
|
1117
|
+
if (typeof window !== "undefined" && !customElements.get("recur-toast")) {
|
|
1118
|
+
customElements.define("recur-toast", RecurToast);
|
|
1119
|
+
}
|
|
1120
|
+
if (typeof window !== "undefined" && !customElements.get("recur-toast-container")) {
|
|
1121
|
+
customElements.define("recur-toast-container", RecurToastContainer);
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
});
|
|
1125
|
+
|
|
609
1126
|
// src/components/payment-form.ts
|
|
610
1127
|
var payment_form_exports = {};
|
|
611
1128
|
__export(payment_form_exports, {
|
|
@@ -619,6 +1136,8 @@ var init_payment_form = __esm({
|
|
|
619
1136
|
super();
|
|
620
1137
|
__publicField(this, "containerId");
|
|
621
1138
|
__publicField(this, "customStyles");
|
|
1139
|
+
__publicField(this, "_isInitializing", false);
|
|
1140
|
+
__publicField(this, "_initializationAborted", false);
|
|
622
1141
|
this.containerId = this.getAttribute("container-id") || `recur-${Date.now()}`;
|
|
623
1142
|
this.customStyles = this.getAttribute("custom-styles") || "";
|
|
624
1143
|
this.attachShadow({ mode: "open" });
|
|
@@ -626,6 +1145,16 @@ var init_payment_form = __esm({
|
|
|
626
1145
|
connectedCallback() {
|
|
627
1146
|
this.render();
|
|
628
1147
|
}
|
|
1148
|
+
disconnectedCallback() {
|
|
1149
|
+
console.log("[PaymentForm] Component disconnected, cleaning up...");
|
|
1150
|
+
this._initializationAborted = true;
|
|
1151
|
+
this._paymentSession = null;
|
|
1152
|
+
const submitBtn = document.getElementById(`${this.containerId}-submit-btn`);
|
|
1153
|
+
if (submitBtn) {
|
|
1154
|
+
const newBtn = submitBtn.cloneNode(true);
|
|
1155
|
+
submitBtn.parentNode?.replaceChild(newBtn, submitBtn);
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
629
1158
|
// 監聽 attribute 變化
|
|
630
1159
|
static get observedAttributes() {
|
|
631
1160
|
return ["custom-styles", "customer-name", "customer-email", "plan-name", "amount", "billing-period"];
|
|
@@ -1120,11 +1649,35 @@ var init_payment_form = __esm({
|
|
|
1120
1649
|
* 這個方法會在外部調用,傳入 SDK token
|
|
1121
1650
|
*/
|
|
1122
1651
|
async initializePayment(sdkToken, environment) {
|
|
1652
|
+
if (this._initializationAborted) {
|
|
1653
|
+
console.log("[PaymentForm] Initialization aborted before start");
|
|
1654
|
+
return null;
|
|
1655
|
+
}
|
|
1656
|
+
this._isInitializing = true;
|
|
1123
1657
|
await this.updateComplete();
|
|
1658
|
+
if (this._initializationAborted) {
|
|
1659
|
+
console.log("[PaymentForm] Initialization aborted after updateComplete");
|
|
1660
|
+
this._isInitializing = false;
|
|
1661
|
+
return null;
|
|
1662
|
+
}
|
|
1124
1663
|
if (!window.UniPayment) {
|
|
1664
|
+
this._isInitializing = false;
|
|
1125
1665
|
throw new Error("PAYUNi SDK not loaded");
|
|
1126
1666
|
}
|
|
1127
1667
|
try {
|
|
1668
|
+
const cardNoEl = document.getElementById(`${this.containerId}-card-no`);
|
|
1669
|
+
const cardExpEl = document.getElementById(`${this.containerId}-card-exp`);
|
|
1670
|
+
const cardCvcEl = document.getElementById(`${this.containerId}-card-cvc`);
|
|
1671
|
+
if (!cardNoEl || !cardExpEl || !cardCvcEl) {
|
|
1672
|
+
console.log("[PaymentForm] DOM elements not found, likely disconnected");
|
|
1673
|
+
this._isInitializing = false;
|
|
1674
|
+
return null;
|
|
1675
|
+
}
|
|
1676
|
+
if (this._initializationAborted) {
|
|
1677
|
+
console.log("[PaymentForm] Initialization aborted before createSession");
|
|
1678
|
+
this._isInitializing = false;
|
|
1679
|
+
return null;
|
|
1680
|
+
}
|
|
1128
1681
|
const paymentSession = window.UniPayment.createSession(sdkToken, {
|
|
1129
1682
|
env: environment === "SANDBOX" ? "S" : "P",
|
|
1130
1683
|
elements: {
|
|
@@ -1134,8 +1687,32 @@ var init_payment_form = __esm({
|
|
|
1134
1687
|
CardCvc: `${this.containerId}-card-cvc`
|
|
1135
1688
|
}
|
|
1136
1689
|
});
|
|
1137
|
-
|
|
1690
|
+
if (this._initializationAborted) {
|
|
1691
|
+
console.log("[PaymentForm] Initialization aborted before paymentSession.start()");
|
|
1692
|
+
this._isInitializing = false;
|
|
1693
|
+
return null;
|
|
1694
|
+
}
|
|
1695
|
+
try {
|
|
1696
|
+
await paymentSession.start();
|
|
1697
|
+
} catch (startError) {
|
|
1698
|
+
const is1008Error = startError?.message?.includes("1008") || startError?.message?.includes("timeout") || startError?.code === 1008;
|
|
1699
|
+
if (is1008Error && this._initializationAborted) {
|
|
1700
|
+
console.log("[PaymentForm] Caught 1008 timeout error after component disconnect - ignoring");
|
|
1701
|
+
this._isInitializing = false;
|
|
1702
|
+
return null;
|
|
1703
|
+
}
|
|
1704
|
+
throw startError;
|
|
1705
|
+
}
|
|
1706
|
+
if (this._initializationAborted) {
|
|
1707
|
+
console.log("[PaymentForm] Initialization aborted after paymentSession.start()");
|
|
1708
|
+
this._isInitializing = false;
|
|
1709
|
+
return null;
|
|
1710
|
+
}
|
|
1138
1711
|
paymentSession.onUpdate?.((data) => {
|
|
1712
|
+
if (this._initializationAborted) {
|
|
1713
|
+
console.log("[PaymentForm] Ignoring onUpdate event - component disconnected");
|
|
1714
|
+
return;
|
|
1715
|
+
}
|
|
1139
1716
|
console.log("[PaymentForm] PAYUNi update:", data);
|
|
1140
1717
|
const allFieldsComplete = data.status && data.status.CardNo === true && data.status.CardExp === true && data.status.CardCvc === true;
|
|
1141
1718
|
const submitBtn = document.getElementById(`${this.containerId}-submit-btn`);
|
|
@@ -1144,10 +1721,17 @@ var init_payment_form = __esm({
|
|
|
1144
1721
|
console.log("[PaymentForm] Submit button disabled:", !allFieldsComplete);
|
|
1145
1722
|
}
|
|
1146
1723
|
});
|
|
1724
|
+
if (this._initializationAborted) {
|
|
1725
|
+
console.log("[PaymentForm] Initialization aborted before saving session");
|
|
1726
|
+
this._isInitializing = false;
|
|
1727
|
+
return null;
|
|
1728
|
+
}
|
|
1147
1729
|
this._paymentSession = paymentSession;
|
|
1148
1730
|
this.setupFormSubmission();
|
|
1731
|
+
this._isInitializing = false;
|
|
1149
1732
|
return paymentSession;
|
|
1150
1733
|
} catch (error) {
|
|
1734
|
+
this._isInitializing = false;
|
|
1151
1735
|
console.error("[PaymentForm] Failed to initialize payment:", error);
|
|
1152
1736
|
throw error;
|
|
1153
1737
|
}
|
|
@@ -1171,17 +1755,18 @@ var init_payment_form = __esm({
|
|
|
1171
1755
|
name = nameInput.value;
|
|
1172
1756
|
phone = phoneInput?.value;
|
|
1173
1757
|
if (!email || !name) {
|
|
1174
|
-
|
|
1758
|
+
this.showError("\u8ACB\u586B\u5BEB\u59D3\u540D\u548C\u96FB\u5B50\u90F5\u4EF6");
|
|
1175
1759
|
return;
|
|
1176
1760
|
}
|
|
1177
1761
|
} else {
|
|
1178
1762
|
email = this.getAttribute("customer-email") || void 0;
|
|
1179
1763
|
name = this.getAttribute("customer-name") || void 0;
|
|
1180
1764
|
if (!email || !name) {
|
|
1181
|
-
|
|
1765
|
+
this.showError("\u5BA2\u6236\u8CC7\u8A0A\u4E0D\u5B8C\u6574");
|
|
1182
1766
|
return;
|
|
1183
1767
|
}
|
|
1184
1768
|
}
|
|
1769
|
+
this.clearError();
|
|
1185
1770
|
this.setButtonLoading(true);
|
|
1186
1771
|
this.dispatchEvent(new CustomEvent("submit", {
|
|
1187
1772
|
detail: {
|
|
@@ -1221,6 +1806,33 @@ var init_payment_form = __esm({
|
|
|
1221
1806
|
resetButton() {
|
|
1222
1807
|
this.setButtonLoading(false);
|
|
1223
1808
|
}
|
|
1809
|
+
/**
|
|
1810
|
+
* Show error message using RecurErrorDisplay component
|
|
1811
|
+
*/
|
|
1812
|
+
showError(message) {
|
|
1813
|
+
let errorContainer = this.querySelector(".recur-sdk__error-container");
|
|
1814
|
+
if (!errorContainer) {
|
|
1815
|
+
errorContainer = document.createElement("div");
|
|
1816
|
+
errorContainer.className = "recur-sdk__error-container";
|
|
1817
|
+
errorContainer.style.cssText = "margin-bottom: 16px;";
|
|
1818
|
+
this.insertBefore(errorContainer, this.firstChild);
|
|
1819
|
+
}
|
|
1820
|
+
const errorDisplay = document.createElement("recur-error-display");
|
|
1821
|
+
errorDisplay.setAttribute("error", message);
|
|
1822
|
+
errorDisplay.setAttribute("dismissible", "true");
|
|
1823
|
+
errorContainer.innerHTML = "";
|
|
1824
|
+
errorContainer.appendChild(errorDisplay);
|
|
1825
|
+
errorDisplay.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
1826
|
+
}
|
|
1827
|
+
/**
|
|
1828
|
+
* Clear any displayed errors
|
|
1829
|
+
*/
|
|
1830
|
+
clearError() {
|
|
1831
|
+
const errorContainer = this.querySelector(".recur-sdk__error-container");
|
|
1832
|
+
if (errorContainer) {
|
|
1833
|
+
errorContainer.innerHTML = "";
|
|
1834
|
+
}
|
|
1835
|
+
}
|
|
1224
1836
|
/**
|
|
1225
1837
|
* 等待組件完全連接到 DOM
|
|
1226
1838
|
*/
|
|
@@ -1284,6 +1896,8 @@ async function registerComponents() {
|
|
|
1284
1896
|
Promise.resolve().then(() => (init_success_message(), success_message_exports)),
|
|
1285
1897
|
Promise.resolve().then(() => (init_error_display(), error_display_exports)),
|
|
1286
1898
|
Promise.resolve().then(() => (init_skeleton_loader(), skeleton_loader_exports)),
|
|
1899
|
+
Promise.resolve().then(() => (init_payment_form_skeleton(), payment_form_skeleton_exports)),
|
|
1900
|
+
Promise.resolve().then(() => (init_toast(), toast_exports)),
|
|
1287
1901
|
Promise.resolve().then(() => (init_payment_form(), payment_form_exports))
|
|
1288
1902
|
]);
|
|
1289
1903
|
const components = [
|
|
@@ -1291,6 +1905,9 @@ async function registerComponents() {
|
|
|
1291
1905
|
"recur-success-message",
|
|
1292
1906
|
"recur-error-display",
|
|
1293
1907
|
"recur-skeleton-loader",
|
|
1908
|
+
"recur-payment-form-skeleton",
|
|
1909
|
+
"recur-toast",
|
|
1910
|
+
"recur-toast-container",
|
|
1294
1911
|
"recur-payment-form"
|
|
1295
1912
|
];
|
|
1296
1913
|
const unregistered = components.filter((name) => !customElements.get(name));
|
|
@@ -1444,10 +2061,9 @@ function RecurProvider({ children, config: initialConfig = {} }) {
|
|
|
1444
2061
|
loadingContainer = document.createElement("div");
|
|
1445
2062
|
loadingContainer.id = "recur-modal-loading";
|
|
1446
2063
|
loadingContainer.className = "recur-sdk__loading-container";
|
|
1447
|
-
loadingContainer.style.cssText = "
|
|
1448
|
-
const
|
|
1449
|
-
|
|
1450
|
-
loadingContainer.appendChild(spinner);
|
|
2064
|
+
loadingContainer.style.cssText = "padding: 0;";
|
|
2065
|
+
const skeleton = document.createElement("recur-payment-form-skeleton");
|
|
2066
|
+
loadingContainer.appendChild(skeleton);
|
|
1451
2067
|
modalContent.appendChild(closeButton);
|
|
1452
2068
|
modalContent.appendChild(title);
|
|
1453
2069
|
modalContent.appendChild(loadingContainer);
|
|
@@ -1476,10 +2092,9 @@ function RecurProvider({ children, config: initialConfig = {} }) {
|
|
|
1476
2092
|
loadingContainer = document.createElement("div");
|
|
1477
2093
|
loadingContainer.id = "recur-embedded-loading";
|
|
1478
2094
|
loadingContainer.className = "recur-sdk__loading-container";
|
|
1479
|
-
loadingContainer.style.cssText = "
|
|
1480
|
-
const
|
|
1481
|
-
|
|
1482
|
-
loadingContainer.appendChild(embeddedSpinner);
|
|
2095
|
+
loadingContainer.style.cssText = "padding: 0;";
|
|
2096
|
+
const embeddedSkeleton = document.createElement("recur-payment-form-skeleton");
|
|
2097
|
+
loadingContainer.appendChild(embeddedSkeleton);
|
|
1483
2098
|
embeddedContainer.appendChild(loadingContainer);
|
|
1484
2099
|
} else {
|
|
1485
2100
|
console.log("[Recur SDK] Container not found after polling, will continue without early loading state");
|
|
@@ -1607,10 +2222,14 @@ function RecurProvider({ children, config: initialConfig = {} }) {
|
|
|
1607
2222
|
container.appendChild(paymentForm);
|
|
1608
2223
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
1609
2224
|
console.log("[Recur SDK] Initializing PAYUNi SDK via Web Component...");
|
|
1610
|
-
await paymentForm.initializePayment(
|
|
2225
|
+
const initResult = await paymentForm.initializePayment(
|
|
1611
2226
|
tokenResult.sdkToken,
|
|
1612
2227
|
tokenResult.environment
|
|
1613
2228
|
);
|
|
2229
|
+
if (initResult === null) {
|
|
2230
|
+
console.log("[Recur SDK] Initialization aborted - component was unmounted");
|
|
2231
|
+
return;
|
|
2232
|
+
}
|
|
1614
2233
|
console.log("[Recur SDK] PAYUNi SDK initialized successfully");
|
|
1615
2234
|
console.log("[Recur SDK] Step 7: Setting up submit handler...");
|
|
1616
2235
|
paymentForm.addEventListener("submit", (async (event) => {
|