@crediball/elements 0.10.0 → 0.11.0
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/{chunk-YP5JNDTD.js → chunk-FOMDJR2B.js} +58 -17
- package/dist/crediball-elements.iife.js +58 -17
- package/dist/index.d.ts +3 -2
- package/dist/index.js +1 -1
- package/dist/register.js +1 -1
- package/package.json +1 -1
|
@@ -217,13 +217,24 @@ var STYLE = `
|
|
|
217
217
|
.card {
|
|
218
218
|
width: 100%;
|
|
219
219
|
max-width: 420px;
|
|
220
|
+
/* Cap to the overlay's own (viewport-correct, since it's fixed/inset:0)
|
|
221
|
+
content box and scroll internally past that \u2014 otherwise a host with
|
|
222
|
+
many optional sections enabled (balance, subscribe, promo code,
|
|
223
|
+
packages, custom amount, auto top-up, referral) can produce a card
|
|
224
|
+
taller than the mobile viewport, which the centered overlay then
|
|
225
|
+
clips top and bottom with no way to reach the rest. */
|
|
226
|
+
max-height: 100%;
|
|
227
|
+
overflow-y: auto;
|
|
228
|
+
-webkit-overflow-scrolling: touch;
|
|
229
|
+
overscroll-behavior: contain;
|
|
230
|
+
box-sizing: border-box;
|
|
220
231
|
padding: 32px;
|
|
221
232
|
border-radius: var(--crediball-radius-card, 18px);
|
|
222
233
|
background: var(--crediball-canvas, #ffffff);
|
|
223
234
|
color: var(--crediball-ink, #1d1d1f);
|
|
224
235
|
}
|
|
225
236
|
.balance-row { margin-bottom: 24px; padding-bottom: 20px; border-bottom: 1px solid var(--crediball-hairline, #e0e0e0); }
|
|
226
|
-
.eyebrow { font-size: 11px; font-weight: 600; letter-spacing: 0.06em;
|
|
237
|
+
.eyebrow { font-size: 11px; font-weight: 600; letter-spacing: 0.06em; color: var(--crediball-ink-muted, #7a7a7a); margin-bottom: 4px; }
|
|
227
238
|
.balance-value { font-size: 22px; font-weight: 600; }
|
|
228
239
|
h3 { margin: 0; font-size: 24px; font-weight: 600; letter-spacing: -0.01em; }
|
|
229
240
|
p.desc { margin: 8px 0 0; font-size: 14px; color: var(--crediball-ink-muted, #7a7a7a); }
|
|
@@ -236,14 +247,17 @@ var STYLE = `
|
|
|
236
247
|
display: flex;
|
|
237
248
|
justify-content: space-between;
|
|
238
249
|
align-items: center;
|
|
250
|
+
gap: 16px;
|
|
239
251
|
background: var(--crediball-accent, #0066cc);
|
|
240
252
|
color: var(--crediball-on-accent, #ffffff);
|
|
241
253
|
font: inherit;
|
|
242
|
-
font-size: 17px;
|
|
243
254
|
padding: 11px 22px;
|
|
244
255
|
border-radius: var(--crediball-radius, 9999px);
|
|
245
256
|
}
|
|
246
|
-
|
|
257
|
+
.option-left { display: flex; flex-direction: column; align-items: flex-start; flex: 1; min-width: 0; }
|
|
258
|
+
.option-name { font-size: 17px; font-weight: 600; line-height: 1.2; }
|
|
259
|
+
.option-credits { font-size: 13px; opacity: 0.85; line-height: 1.2; }
|
|
260
|
+
.option-price { font-size: 17px; font-weight: 600; white-space: nowrap; flex-shrink: 0; }
|
|
247
261
|
.custom-row { display: flex; gap: 8px; align-items: center; margin-top: 12px; }
|
|
248
262
|
.custom-row .sym { font-size: 14px; color: var(--crediball-ink-muted, #7a7a7a); }
|
|
249
263
|
.custom-row input {
|
|
@@ -304,6 +318,8 @@ var STYLE = `
|
|
|
304
318
|
color: var(--crediball-ink, #1d1d1f);
|
|
305
319
|
outline: none;
|
|
306
320
|
}
|
|
321
|
+
.promo-code-input.invalid { border-color: var(--crediball-accent, #0066cc); }
|
|
322
|
+
.promo-code-error { margin: 6px 0 0; font-size: 12px; color: var(--crediball-accent, #0066cc); }
|
|
307
323
|
.autotopup { margin-top: 16px; }
|
|
308
324
|
.autotopup-toggle {
|
|
309
325
|
appearance: none; border: none; cursor: pointer; background: transparent; padding: 0;
|
|
@@ -326,7 +342,7 @@ var STYLE = `
|
|
|
326
342
|
}
|
|
327
343
|
.autotopup-hint { margin: 6px 0 0; font-size: 11px; color: var(--crediball-ink-muted, #7a7a7a); }
|
|
328
344
|
.referral { margin-top: 16px; padding-top: 16px; border-top: 1px solid var(--crediball-hairline, #e0e0e0); }
|
|
329
|
-
.referral-label { margin-bottom: 8px; font-size: 12px; font-weight: 600; color: var(--crediball-ink-muted, #7a7a7a); }
|
|
345
|
+
.referral-label { margin-bottom: 8px; font-size: 12px; font-weight: 600; color: var(--crediball-ink-muted, #7a7a7a); white-space: pre-line; }
|
|
330
346
|
.referral-row { display: flex; gap: 8px; align-items: center; }
|
|
331
347
|
.referral-row .link {
|
|
332
348
|
flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
@@ -414,6 +430,10 @@ var CrediballPaywallElement = class extends CrediballElement {
|
|
|
414
430
|
const plans = pkgs?.activeSubscription ? [] : pkgs?.subscriptionPlans ?? [];
|
|
415
431
|
const custom = pkgs?.customTopup;
|
|
416
432
|
const showCustom = Boolean(custom?.enabled);
|
|
433
|
+
const referralReward = this.referral?.rewardCredits ?? 0;
|
|
434
|
+
const referredReward = this.referral?.referredRewardCredits ?? 0;
|
|
435
|
+
const defaultReferralLabel = referredReward > 0 ? "Refer a friend.\nYou earn {credits} credits and your friend {referredCredits} credits" : "Refer a friend \xB7 earn {credits} credits";
|
|
436
|
+
const referralLabel = (this.getAttribute("referral-label") ?? defaultReferralLabel).replace("{credits}", formatCredits(referralReward)).replace("{referredCredits}", formatCredits(referredReward));
|
|
417
437
|
this.shadow.innerHTML = `
|
|
418
438
|
<style>${STYLE}</style>
|
|
419
439
|
<div class="overlay" part="overlay">
|
|
@@ -428,18 +448,23 @@ var CrediballPaywallElement = class extends CrediballElement {
|
|
|
428
448
|
${plans.map(
|
|
429
449
|
(p) => `
|
|
430
450
|
<button class="option" data-kind="plan" data-id="${escapeHtml(p.id)}">
|
|
431
|
-
<
|
|
432
|
-
|
|
451
|
+
<div class="option-left">
|
|
452
|
+
<div class="option-name">${escapeHtml(p.name)}</div>
|
|
453
|
+
<div class="option-credits">${formatCredits(p.credits)} credits</div>
|
|
454
|
+
</div>
|
|
455
|
+
<div class="option-price">${formatMoney(p.price, currency)}/${PERIOD_SUFFIX[p.period] ?? p.period}</div>
|
|
433
456
|
</button>`
|
|
434
457
|
).join("")}
|
|
435
458
|
</div>` : ""}
|
|
436
|
-
${this.renderPromoCode(Boolean(pkgs?.hasActiveCheckoutPromotion))}
|
|
437
459
|
<div class="list">
|
|
438
460
|
${packages.length ? packages.map(
|
|
439
461
|
(p) => `
|
|
440
462
|
<button class="option" data-kind="package" data-id="${escapeHtml(p.id)}">
|
|
441
|
-
<
|
|
442
|
-
|
|
463
|
+
<div class="option-left">
|
|
464
|
+
<div class="option-name">${escapeHtml(p.name)}</div>
|
|
465
|
+
<div class="option-credits">${formatCredits(p.credits)} credits</div>
|
|
466
|
+
</div>
|
|
467
|
+
<div class="option-price">${formatMoney(p.price, currency)}</div>
|
|
443
468
|
</button>`
|
|
444
469
|
).join("") : showCustom ? "" : legacyAmounts.map(
|
|
445
470
|
(a) => `
|
|
@@ -454,16 +479,12 @@ var CrediballPaywallElement = class extends CrediballElement {
|
|
|
454
479
|
<button class="custom-add" type="button">${escapeHtml(addButtonText)}</button>
|
|
455
480
|
</div>
|
|
456
481
|
<p class="custom-preview" hidden></p>` : ""}
|
|
482
|
+
${this.renderPromoCode(Boolean(pkgs?.hasActiveCheckoutPromotion))}
|
|
457
483
|
${this.renderAutoTopup(packages, pkgs?.autoTopup, currency)}
|
|
458
484
|
<p class="error" hidden></p>
|
|
459
485
|
<button class="close" part="close" type="button">Not now</button>
|
|
460
486
|
${this.referral?.link ? `<div class="referral">
|
|
461
|
-
<div class="referral-label">${escapeHtml(
|
|
462
|
-
(this.getAttribute("referral-label") ?? "Refer a friend \xB7 earn {credits} credits").replace(
|
|
463
|
-
"{credits}",
|
|
464
|
-
formatCredits(this.referral.rewardCredits ?? 0)
|
|
465
|
-
)
|
|
466
|
-
)}</div>
|
|
487
|
+
<div class="referral-label">${escapeHtml(referralLabel)}</div>
|
|
467
488
|
<div class="referral-row">
|
|
468
489
|
<div class="link" title="${escapeHtml(this.referral.link)}">${escapeHtml(this.referral.link)}</div>
|
|
469
490
|
<button class="referral-copy" type="button">${this.referralCopied ? "Copied!" : "Copy"}</button>
|
|
@@ -505,6 +526,24 @@ var CrediballPaywallElement = class extends CrediballElement {
|
|
|
505
526
|
this.promoCodeOpen = true;
|
|
506
527
|
this.render();
|
|
507
528
|
});
|
|
529
|
+
const promoInput = this.shadow.querySelector(".promo-code-input");
|
|
530
|
+
const promoErrorEl = this.shadow.querySelector(".promo-code-error");
|
|
531
|
+
if (promoInput && promoErrorEl) {
|
|
532
|
+
let promoTouched = false;
|
|
533
|
+
const checkPromoFormat = () => {
|
|
534
|
+
const value = promoInput.value.trim();
|
|
535
|
+
const valid = !value || /^[A-Za-z0-9_-]+$/.test(value);
|
|
536
|
+
const showError2 = promoTouched && !valid;
|
|
537
|
+
promoInput.classList.toggle("invalid", showError2);
|
|
538
|
+
promoErrorEl.hidden = !showError2;
|
|
539
|
+
if (showError2) promoErrorEl.textContent = "Promo codes only contain letters, numbers, - and _.";
|
|
540
|
+
};
|
|
541
|
+
promoInput.addEventListener("blur", () => {
|
|
542
|
+
promoTouched = true;
|
|
543
|
+
checkPromoFormat();
|
|
544
|
+
});
|
|
545
|
+
promoInput.addEventListener("input", checkPromoFormat);
|
|
546
|
+
}
|
|
508
547
|
if (showCustom) {
|
|
509
548
|
const input = this.shadow.querySelector(".custom-input");
|
|
510
549
|
const addBtn = this.shadow.querySelector(".custom-add");
|
|
@@ -572,8 +611,9 @@ var CrediballPaywallElement = class extends CrediballElement {
|
|
|
572
611
|
* once the app has an active Checkout promotion (same as the custom-amount
|
|
573
612
|
* field auto-following the dashboard's custom-topup config) — the
|
|
574
613
|
* `allow-promo-code` attribute is only needed to force it on before you've
|
|
575
|
-
* set one up.
|
|
576
|
-
*
|
|
614
|
+
* set one up. Format-only client check (flags an obvious typo/paste
|
|
615
|
+
* mistake on blur, see wireUpPromoCode()) — whether the code is actually
|
|
616
|
+
* active/redeemable is only known server-side when a purchase is made. */
|
|
577
617
|
renderPromoCode(hasActiveCheckoutPromotion) {
|
|
578
618
|
if (!this.hasAttribute("allow-promo-code") && !hasActiveCheckoutPromotion) return "";
|
|
579
619
|
const label = this.getAttribute("promo-code-label") ?? "Have a promo code?";
|
|
@@ -585,6 +625,7 @@ var CrediballPaywallElement = class extends CrediballElement {
|
|
|
585
625
|
return `<div class="promo-code">
|
|
586
626
|
<div class="promo-code-label">${escapeHtml(label)}</div>
|
|
587
627
|
<input class="promo-code-input" placeholder="PROMOCODE" aria-label="Promo code" />
|
|
628
|
+
<p class="promo-code-error" hidden></p>
|
|
588
629
|
</div>`;
|
|
589
630
|
}
|
|
590
631
|
/** The current promo code input value (uppercased, trimmed), or undefined
|
|
@@ -631,13 +631,24 @@ var Crediball = (() => {
|
|
|
631
631
|
.card {
|
|
632
632
|
width: 100%;
|
|
633
633
|
max-width: 420px;
|
|
634
|
+
/* Cap to the overlay's own (viewport-correct, since it's fixed/inset:0)
|
|
635
|
+
content box and scroll internally past that \u2014 otherwise a host with
|
|
636
|
+
many optional sections enabled (balance, subscribe, promo code,
|
|
637
|
+
packages, custom amount, auto top-up, referral) can produce a card
|
|
638
|
+
taller than the mobile viewport, which the centered overlay then
|
|
639
|
+
clips top and bottom with no way to reach the rest. */
|
|
640
|
+
max-height: 100%;
|
|
641
|
+
overflow-y: auto;
|
|
642
|
+
-webkit-overflow-scrolling: touch;
|
|
643
|
+
overscroll-behavior: contain;
|
|
644
|
+
box-sizing: border-box;
|
|
634
645
|
padding: 32px;
|
|
635
646
|
border-radius: var(--crediball-radius-card, 18px);
|
|
636
647
|
background: var(--crediball-canvas, #ffffff);
|
|
637
648
|
color: var(--crediball-ink, #1d1d1f);
|
|
638
649
|
}
|
|
639
650
|
.balance-row { margin-bottom: 24px; padding-bottom: 20px; border-bottom: 1px solid var(--crediball-hairline, #e0e0e0); }
|
|
640
|
-
.eyebrow { font-size: 11px; font-weight: 600; letter-spacing: 0.06em;
|
|
651
|
+
.eyebrow { font-size: 11px; font-weight: 600; letter-spacing: 0.06em; color: var(--crediball-ink-muted, #7a7a7a); margin-bottom: 4px; }
|
|
641
652
|
.balance-value { font-size: 22px; font-weight: 600; }
|
|
642
653
|
h3 { margin: 0; font-size: 24px; font-weight: 600; letter-spacing: -0.01em; }
|
|
643
654
|
p.desc { margin: 8px 0 0; font-size: 14px; color: var(--crediball-ink-muted, #7a7a7a); }
|
|
@@ -650,14 +661,17 @@ var Crediball = (() => {
|
|
|
650
661
|
display: flex;
|
|
651
662
|
justify-content: space-between;
|
|
652
663
|
align-items: center;
|
|
664
|
+
gap: 16px;
|
|
653
665
|
background: var(--crediball-accent, #0066cc);
|
|
654
666
|
color: var(--crediball-on-accent, #ffffff);
|
|
655
667
|
font: inherit;
|
|
656
|
-
font-size: 17px;
|
|
657
668
|
padding: 11px 22px;
|
|
658
669
|
border-radius: var(--crediball-radius, 9999px);
|
|
659
670
|
}
|
|
660
|
-
|
|
671
|
+
.option-left { display: flex; flex-direction: column; align-items: flex-start; flex: 1; min-width: 0; }
|
|
672
|
+
.option-name { font-size: 17px; font-weight: 600; line-height: 1.2; }
|
|
673
|
+
.option-credits { font-size: 13px; opacity: 0.85; line-height: 1.2; }
|
|
674
|
+
.option-price { font-size: 17px; font-weight: 600; white-space: nowrap; flex-shrink: 0; }
|
|
661
675
|
.custom-row { display: flex; gap: 8px; align-items: center; margin-top: 12px; }
|
|
662
676
|
.custom-row .sym { font-size: 14px; color: var(--crediball-ink-muted, #7a7a7a); }
|
|
663
677
|
.custom-row input {
|
|
@@ -718,6 +732,8 @@ var Crediball = (() => {
|
|
|
718
732
|
color: var(--crediball-ink, #1d1d1f);
|
|
719
733
|
outline: none;
|
|
720
734
|
}
|
|
735
|
+
.promo-code-input.invalid { border-color: var(--crediball-accent, #0066cc); }
|
|
736
|
+
.promo-code-error { margin: 6px 0 0; font-size: 12px; color: var(--crediball-accent, #0066cc); }
|
|
721
737
|
.autotopup { margin-top: 16px; }
|
|
722
738
|
.autotopup-toggle {
|
|
723
739
|
appearance: none; border: none; cursor: pointer; background: transparent; padding: 0;
|
|
@@ -740,7 +756,7 @@ var Crediball = (() => {
|
|
|
740
756
|
}
|
|
741
757
|
.autotopup-hint { margin: 6px 0 0; font-size: 11px; color: var(--crediball-ink-muted, #7a7a7a); }
|
|
742
758
|
.referral { margin-top: 16px; padding-top: 16px; border-top: 1px solid var(--crediball-hairline, #e0e0e0); }
|
|
743
|
-
.referral-label { margin-bottom: 8px; font-size: 12px; font-weight: 600; color: var(--crediball-ink-muted, #7a7a7a); }
|
|
759
|
+
.referral-label { margin-bottom: 8px; font-size: 12px; font-weight: 600; color: var(--crediball-ink-muted, #7a7a7a); white-space: pre-line; }
|
|
744
760
|
.referral-row { display: flex; gap: 8px; align-items: center; }
|
|
745
761
|
.referral-row .link {
|
|
746
762
|
flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
@@ -828,6 +844,10 @@ var Crediball = (() => {
|
|
|
828
844
|
const plans = pkgs?.activeSubscription ? [] : pkgs?.subscriptionPlans ?? [];
|
|
829
845
|
const custom = pkgs?.customTopup;
|
|
830
846
|
const showCustom = Boolean(custom?.enabled);
|
|
847
|
+
const referralReward = this.referral?.rewardCredits ?? 0;
|
|
848
|
+
const referredReward = this.referral?.referredRewardCredits ?? 0;
|
|
849
|
+
const defaultReferralLabel = referredReward > 0 ? "Refer a friend.\nYou earn {credits} credits and your friend {referredCredits} credits" : "Refer a friend \xB7 earn {credits} credits";
|
|
850
|
+
const referralLabel = (this.getAttribute("referral-label") ?? defaultReferralLabel).replace("{credits}", formatCredits(referralReward)).replace("{referredCredits}", formatCredits(referredReward));
|
|
831
851
|
this.shadow.innerHTML = `
|
|
832
852
|
<style>${STYLE}</style>
|
|
833
853
|
<div class="overlay" part="overlay">
|
|
@@ -842,18 +862,23 @@ var Crediball = (() => {
|
|
|
842
862
|
${plans.map(
|
|
843
863
|
(p) => `
|
|
844
864
|
<button class="option" data-kind="plan" data-id="${escapeHtml(p.id)}">
|
|
845
|
-
<
|
|
846
|
-
|
|
865
|
+
<div class="option-left">
|
|
866
|
+
<div class="option-name">${escapeHtml(p.name)}</div>
|
|
867
|
+
<div class="option-credits">${formatCredits(p.credits)} credits</div>
|
|
868
|
+
</div>
|
|
869
|
+
<div class="option-price">${formatMoney(p.price, currency)}/${PERIOD_SUFFIX[p.period] ?? p.period}</div>
|
|
847
870
|
</button>`
|
|
848
871
|
).join("")}
|
|
849
872
|
</div>` : ""}
|
|
850
|
-
${this.renderPromoCode(Boolean(pkgs?.hasActiveCheckoutPromotion))}
|
|
851
873
|
<div class="list">
|
|
852
874
|
${packages.length ? packages.map(
|
|
853
875
|
(p) => `
|
|
854
876
|
<button class="option" data-kind="package" data-id="${escapeHtml(p.id)}">
|
|
855
|
-
<
|
|
856
|
-
|
|
877
|
+
<div class="option-left">
|
|
878
|
+
<div class="option-name">${escapeHtml(p.name)}</div>
|
|
879
|
+
<div class="option-credits">${formatCredits(p.credits)} credits</div>
|
|
880
|
+
</div>
|
|
881
|
+
<div class="option-price">${formatMoney(p.price, currency)}</div>
|
|
857
882
|
</button>`
|
|
858
883
|
).join("") : showCustom ? "" : legacyAmounts.map(
|
|
859
884
|
(a) => `
|
|
@@ -868,16 +893,12 @@ var Crediball = (() => {
|
|
|
868
893
|
<button class="custom-add" type="button">${escapeHtml(addButtonText)}</button>
|
|
869
894
|
</div>
|
|
870
895
|
<p class="custom-preview" hidden></p>` : ""}
|
|
896
|
+
${this.renderPromoCode(Boolean(pkgs?.hasActiveCheckoutPromotion))}
|
|
871
897
|
${this.renderAutoTopup(packages, pkgs?.autoTopup, currency)}
|
|
872
898
|
<p class="error" hidden></p>
|
|
873
899
|
<button class="close" part="close" type="button">Not now</button>
|
|
874
900
|
${this.referral?.link ? `<div class="referral">
|
|
875
|
-
<div class="referral-label">${escapeHtml(
|
|
876
|
-
(this.getAttribute("referral-label") ?? "Refer a friend \xB7 earn {credits} credits").replace(
|
|
877
|
-
"{credits}",
|
|
878
|
-
formatCredits(this.referral.rewardCredits ?? 0)
|
|
879
|
-
)
|
|
880
|
-
)}</div>
|
|
901
|
+
<div class="referral-label">${escapeHtml(referralLabel)}</div>
|
|
881
902
|
<div class="referral-row">
|
|
882
903
|
<div class="link" title="${escapeHtml(this.referral.link)}">${escapeHtml(this.referral.link)}</div>
|
|
883
904
|
<button class="referral-copy" type="button">${this.referralCopied ? "Copied!" : "Copy"}</button>
|
|
@@ -919,6 +940,24 @@ var Crediball = (() => {
|
|
|
919
940
|
this.promoCodeOpen = true;
|
|
920
941
|
this.render();
|
|
921
942
|
});
|
|
943
|
+
const promoInput = this.shadow.querySelector(".promo-code-input");
|
|
944
|
+
const promoErrorEl = this.shadow.querySelector(".promo-code-error");
|
|
945
|
+
if (promoInput && promoErrorEl) {
|
|
946
|
+
let promoTouched = false;
|
|
947
|
+
const checkPromoFormat = () => {
|
|
948
|
+
const value = promoInput.value.trim();
|
|
949
|
+
const valid = !value || /^[A-Za-z0-9_-]+$/.test(value);
|
|
950
|
+
const showError2 = promoTouched && !valid;
|
|
951
|
+
promoInput.classList.toggle("invalid", showError2);
|
|
952
|
+
promoErrorEl.hidden = !showError2;
|
|
953
|
+
if (showError2) promoErrorEl.textContent = "Promo codes only contain letters, numbers, - and _.";
|
|
954
|
+
};
|
|
955
|
+
promoInput.addEventListener("blur", () => {
|
|
956
|
+
promoTouched = true;
|
|
957
|
+
checkPromoFormat();
|
|
958
|
+
});
|
|
959
|
+
promoInput.addEventListener("input", checkPromoFormat);
|
|
960
|
+
}
|
|
922
961
|
if (showCustom) {
|
|
923
962
|
const input = this.shadow.querySelector(".custom-input");
|
|
924
963
|
const addBtn = this.shadow.querySelector(".custom-add");
|
|
@@ -986,8 +1025,9 @@ var Crediball = (() => {
|
|
|
986
1025
|
* once the app has an active Checkout promotion (same as the custom-amount
|
|
987
1026
|
* field auto-following the dashboard's custom-topup config) — the
|
|
988
1027
|
* `allow-promo-code` attribute is only needed to force it on before you've
|
|
989
|
-
* set one up.
|
|
990
|
-
*
|
|
1028
|
+
* set one up. Format-only client check (flags an obvious typo/paste
|
|
1029
|
+
* mistake on blur, see wireUpPromoCode()) — whether the code is actually
|
|
1030
|
+
* active/redeemable is only known server-side when a purchase is made. */
|
|
991
1031
|
renderPromoCode(hasActiveCheckoutPromotion) {
|
|
992
1032
|
if (!this.hasAttribute("allow-promo-code") && !hasActiveCheckoutPromotion) return "";
|
|
993
1033
|
const label = this.getAttribute("promo-code-label") ?? "Have a promo code?";
|
|
@@ -999,6 +1039,7 @@ var Crediball = (() => {
|
|
|
999
1039
|
return `<div class="promo-code">
|
|
1000
1040
|
<div class="promo-code-label">${escapeHtml(label)}</div>
|
|
1001
1041
|
<input class="promo-code-input" placeholder="PROMOCODE" aria-label="Promo code" />
|
|
1042
|
+
<p class="promo-code-error" hidden></p>
|
|
1002
1043
|
</div>`;
|
|
1003
1044
|
}
|
|
1004
1045
|
/** The current promo code input value (uppercased, trimmed), or undefined
|
package/dist/index.d.ts
CHANGED
|
@@ -127,8 +127,9 @@ declare class CrediballPaywallElement extends CrediballElement {
|
|
|
127
127
|
* once the app has an active Checkout promotion (same as the custom-amount
|
|
128
128
|
* field auto-following the dashboard's custom-topup config) — the
|
|
129
129
|
* `allow-promo-code` attribute is only needed to force it on before you've
|
|
130
|
-
* set one up.
|
|
131
|
-
*
|
|
130
|
+
* set one up. Format-only client check (flags an obvious typo/paste
|
|
131
|
+
* mistake on blur, see wireUpPromoCode()) — whether the code is actually
|
|
132
|
+
* active/redeemable is only known server-side when a purchase is made. */
|
|
132
133
|
private renderPromoCode;
|
|
133
134
|
/** The current promo code input value (uppercased, trimmed), or undefined
|
|
134
135
|
* when the field isn't shown/filled in. Read at click time — never re-render
|
package/dist/index.js
CHANGED
package/dist/register.js
CHANGED
package/package.json
CHANGED