@crediball/elements 0.8.0 → 0.9.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.
|
@@ -285,6 +285,25 @@ var STYLE = `
|
|
|
285
285
|
padding: 11px 22px;
|
|
286
286
|
border-radius: var(--crediball-radius, 9999px);
|
|
287
287
|
}
|
|
288
|
+
.promo-code { margin-top: 16px; }
|
|
289
|
+
.promo-code-toggle {
|
|
290
|
+
appearance: none; border: none; cursor: pointer; background: transparent; padding: 0;
|
|
291
|
+
font: inherit; font-size: 12px; color: var(--crediball-ink-muted, #7a7a7a); text-decoration: underline;
|
|
292
|
+
}
|
|
293
|
+
.promo-code-label { margin-bottom: 6px; font-size: 12px; font-weight: 600; color: var(--crediball-ink-muted, #7a7a7a); }
|
|
294
|
+
.promo-code-input {
|
|
295
|
+
width: 100%;
|
|
296
|
+
box-sizing: border-box;
|
|
297
|
+
appearance: none;
|
|
298
|
+
font: inherit;
|
|
299
|
+
font-size: 14px;
|
|
300
|
+
padding: 8px 12px;
|
|
301
|
+
border-radius: var(--crediball-radius, 9999px);
|
|
302
|
+
border: 1px solid var(--crediball-hairline, #e0e0e0);
|
|
303
|
+
background: var(--crediball-canvas, #ffffff);
|
|
304
|
+
color: var(--crediball-ink, #1d1d1f);
|
|
305
|
+
outline: none;
|
|
306
|
+
}
|
|
288
307
|
.autotopup { margin-top: 16px; }
|
|
289
308
|
.autotopup-toggle {
|
|
290
309
|
appearance: none; border: none; cursor: pointer; background: transparent; padding: 0;
|
|
@@ -328,10 +347,19 @@ var CrediballPaywallElement = class extends CrediballElement {
|
|
|
328
347
|
this.referral = null;
|
|
329
348
|
this.referralCopied = false;
|
|
330
349
|
this.autoTopupOpen = false;
|
|
350
|
+
this.promoCodeOpen = false;
|
|
331
351
|
this.shadow = this.attachShadow({ mode: "open" });
|
|
332
352
|
}
|
|
333
353
|
static get observedAttributes() {
|
|
334
|
-
return [
|
|
354
|
+
return [
|
|
355
|
+
...CrediballElement.observedAttributes,
|
|
356
|
+
"open",
|
|
357
|
+
"title",
|
|
358
|
+
"description",
|
|
359
|
+
"amounts",
|
|
360
|
+
"allow-promo-code",
|
|
361
|
+
"promo-code-label"
|
|
362
|
+
];
|
|
335
363
|
}
|
|
336
364
|
connectedCallback() {
|
|
337
365
|
super.connectedCallback();
|
|
@@ -405,6 +433,7 @@ var CrediballPaywallElement = class extends CrediballElement {
|
|
|
405
433
|
</button>`
|
|
406
434
|
).join("")}
|
|
407
435
|
</div>` : ""}
|
|
436
|
+
${this.renderPromoCode()}
|
|
408
437
|
<div class="list">
|
|
409
438
|
${packages.length ? packages.map(
|
|
410
439
|
(p) => `
|
|
@@ -454,8 +483,9 @@ var CrediballPaywallElement = class extends CrediballElement {
|
|
|
454
483
|
const kind = btn.dataset.kind;
|
|
455
484
|
if (kind === "package") {
|
|
456
485
|
const pkg = packages.find((p) => p.id === btn.dataset.id);
|
|
457
|
-
|
|
458
|
-
|
|
486
|
+
const code = this.currentPromoCode();
|
|
487
|
+
if (pkg && this.emit("crediball-select-package", { ...pkg, code })) {
|
|
488
|
+
void this.startCheckout({ packageId: pkg.id, code });
|
|
459
489
|
}
|
|
460
490
|
} else if (kind === "plan") {
|
|
461
491
|
const plan = plans.find((p) => p.id === btn.dataset.id);
|
|
@@ -464,12 +494,17 @@ var CrediballPaywallElement = class extends CrediballElement {
|
|
|
464
494
|
}
|
|
465
495
|
} else if (kind === "amount") {
|
|
466
496
|
const amount = Number(btn.dataset.amount);
|
|
467
|
-
|
|
468
|
-
|
|
497
|
+
const code = this.currentPromoCode();
|
|
498
|
+
if (this.emit("crediball-topup", { amount, code })) {
|
|
499
|
+
void this.startCheckout({ eurAmount: amount, code });
|
|
469
500
|
}
|
|
470
501
|
}
|
|
471
502
|
});
|
|
472
503
|
});
|
|
504
|
+
this.shadow.querySelector("[data-open-promo-code]")?.addEventListener("click", () => {
|
|
505
|
+
this.promoCodeOpen = true;
|
|
506
|
+
this.render();
|
|
507
|
+
});
|
|
473
508
|
if (showCustom) {
|
|
474
509
|
const input = this.shadow.querySelector(".custom-input");
|
|
475
510
|
const addBtn = this.shadow.querySelector(".custom-add");
|
|
@@ -499,8 +534,9 @@ var CrediballPaywallElement = class extends CrediballElement {
|
|
|
499
534
|
return showError(errEl, `Maximum is ${sym}${custom.maxEur}.`);
|
|
500
535
|
}
|
|
501
536
|
showError(errEl, null);
|
|
502
|
-
|
|
503
|
-
|
|
537
|
+
const code = this.currentPromoCode();
|
|
538
|
+
if (this.emit("crediball-topup", { amount, code })) {
|
|
539
|
+
void this.startCheckout({ eurAmount: amount, code });
|
|
504
540
|
}
|
|
505
541
|
};
|
|
506
542
|
addBtn?.addEventListener("click", submit);
|
|
@@ -532,6 +568,30 @@ var CrediballPaywallElement = class extends CrediballElement {
|
|
|
532
568
|
}
|
|
533
569
|
});
|
|
534
570
|
}
|
|
571
|
+
/** HTML for the "Have a promo code?" toggle-open field. Gated on the
|
|
572
|
+
* `allow-promo-code` attribute (off by default). Not validated client-side —
|
|
573
|
+
* an invalid code simply doesn't grant a bonus when the top-up is processed. */
|
|
574
|
+
renderPromoCode() {
|
|
575
|
+
if (!this.hasAttribute("allow-promo-code")) return "";
|
|
576
|
+
const label = this.getAttribute("promo-code-label") ?? "Have a promo code?";
|
|
577
|
+
if (!this.promoCodeOpen) {
|
|
578
|
+
return `<div class="promo-code">
|
|
579
|
+
<button class="promo-code-toggle" type="button" data-open-promo-code>${escapeHtml(label)}</button>
|
|
580
|
+
</div>`;
|
|
581
|
+
}
|
|
582
|
+
return `<div class="promo-code">
|
|
583
|
+
<div class="promo-code-label">${escapeHtml(label)}</div>
|
|
584
|
+
<input class="promo-code-input" placeholder="PROMOCODE" aria-label="Promo code" />
|
|
585
|
+
</div>`;
|
|
586
|
+
}
|
|
587
|
+
/** The current promo code input value (uppercased, trimmed), or undefined
|
|
588
|
+
* when the field isn't shown/filled in. Read at click time — never re-render
|
|
589
|
+
* on keystroke, same reasoning as the custom-amount field. */
|
|
590
|
+
currentPromoCode() {
|
|
591
|
+
const input = this.shadow.querySelector(".promo-code-input");
|
|
592
|
+
const value = input?.value.trim().toUpperCase();
|
|
593
|
+
return value || void 0;
|
|
594
|
+
}
|
|
535
595
|
/** HTML for the auto-topup opt-in row / status line / needs_auth-fix prompt. */
|
|
536
596
|
renderAutoTopup(packages, autoTopup, currency) {
|
|
537
597
|
if (!packages.length) return "";
|
|
@@ -699,6 +699,25 @@ var Crediball = (() => {
|
|
|
699
699
|
padding: 11px 22px;
|
|
700
700
|
border-radius: var(--crediball-radius, 9999px);
|
|
701
701
|
}
|
|
702
|
+
.promo-code { margin-top: 16px; }
|
|
703
|
+
.promo-code-toggle {
|
|
704
|
+
appearance: none; border: none; cursor: pointer; background: transparent; padding: 0;
|
|
705
|
+
font: inherit; font-size: 12px; color: var(--crediball-ink-muted, #7a7a7a); text-decoration: underline;
|
|
706
|
+
}
|
|
707
|
+
.promo-code-label { margin-bottom: 6px; font-size: 12px; font-weight: 600; color: var(--crediball-ink-muted, #7a7a7a); }
|
|
708
|
+
.promo-code-input {
|
|
709
|
+
width: 100%;
|
|
710
|
+
box-sizing: border-box;
|
|
711
|
+
appearance: none;
|
|
712
|
+
font: inherit;
|
|
713
|
+
font-size: 14px;
|
|
714
|
+
padding: 8px 12px;
|
|
715
|
+
border-radius: var(--crediball-radius, 9999px);
|
|
716
|
+
border: 1px solid var(--crediball-hairline, #e0e0e0);
|
|
717
|
+
background: var(--crediball-canvas, #ffffff);
|
|
718
|
+
color: var(--crediball-ink, #1d1d1f);
|
|
719
|
+
outline: none;
|
|
720
|
+
}
|
|
702
721
|
.autotopup { margin-top: 16px; }
|
|
703
722
|
.autotopup-toggle {
|
|
704
723
|
appearance: none; border: none; cursor: pointer; background: transparent; padding: 0;
|
|
@@ -742,10 +761,19 @@ var Crediball = (() => {
|
|
|
742
761
|
this.referral = null;
|
|
743
762
|
this.referralCopied = false;
|
|
744
763
|
this.autoTopupOpen = false;
|
|
764
|
+
this.promoCodeOpen = false;
|
|
745
765
|
this.shadow = this.attachShadow({ mode: "open" });
|
|
746
766
|
}
|
|
747
767
|
static get observedAttributes() {
|
|
748
|
-
return [
|
|
768
|
+
return [
|
|
769
|
+
...CrediballElement.observedAttributes,
|
|
770
|
+
"open",
|
|
771
|
+
"title",
|
|
772
|
+
"description",
|
|
773
|
+
"amounts",
|
|
774
|
+
"allow-promo-code",
|
|
775
|
+
"promo-code-label"
|
|
776
|
+
];
|
|
749
777
|
}
|
|
750
778
|
connectedCallback() {
|
|
751
779
|
super.connectedCallback();
|
|
@@ -819,6 +847,7 @@ var Crediball = (() => {
|
|
|
819
847
|
</button>`
|
|
820
848
|
).join("")}
|
|
821
849
|
</div>` : ""}
|
|
850
|
+
${this.renderPromoCode()}
|
|
822
851
|
<div class="list">
|
|
823
852
|
${packages.length ? packages.map(
|
|
824
853
|
(p) => `
|
|
@@ -868,8 +897,9 @@ var Crediball = (() => {
|
|
|
868
897
|
const kind = btn.dataset.kind;
|
|
869
898
|
if (kind === "package") {
|
|
870
899
|
const pkg = packages.find((p) => p.id === btn.dataset.id);
|
|
871
|
-
|
|
872
|
-
|
|
900
|
+
const code = this.currentPromoCode();
|
|
901
|
+
if (pkg && this.emit("crediball-select-package", { ...pkg, code })) {
|
|
902
|
+
void this.startCheckout({ packageId: pkg.id, code });
|
|
873
903
|
}
|
|
874
904
|
} else if (kind === "plan") {
|
|
875
905
|
const plan = plans.find((p) => p.id === btn.dataset.id);
|
|
@@ -878,12 +908,17 @@ var Crediball = (() => {
|
|
|
878
908
|
}
|
|
879
909
|
} else if (kind === "amount") {
|
|
880
910
|
const amount = Number(btn.dataset.amount);
|
|
881
|
-
|
|
882
|
-
|
|
911
|
+
const code = this.currentPromoCode();
|
|
912
|
+
if (this.emit("crediball-topup", { amount, code })) {
|
|
913
|
+
void this.startCheckout({ eurAmount: amount, code });
|
|
883
914
|
}
|
|
884
915
|
}
|
|
885
916
|
});
|
|
886
917
|
});
|
|
918
|
+
this.shadow.querySelector("[data-open-promo-code]")?.addEventListener("click", () => {
|
|
919
|
+
this.promoCodeOpen = true;
|
|
920
|
+
this.render();
|
|
921
|
+
});
|
|
887
922
|
if (showCustom) {
|
|
888
923
|
const input = this.shadow.querySelector(".custom-input");
|
|
889
924
|
const addBtn = this.shadow.querySelector(".custom-add");
|
|
@@ -913,8 +948,9 @@ var Crediball = (() => {
|
|
|
913
948
|
return showError(errEl, `Maximum is ${sym}${custom.maxEur}.`);
|
|
914
949
|
}
|
|
915
950
|
showError(errEl, null);
|
|
916
|
-
|
|
917
|
-
|
|
951
|
+
const code = this.currentPromoCode();
|
|
952
|
+
if (this.emit("crediball-topup", { amount, code })) {
|
|
953
|
+
void this.startCheckout({ eurAmount: amount, code });
|
|
918
954
|
}
|
|
919
955
|
};
|
|
920
956
|
addBtn?.addEventListener("click", submit);
|
|
@@ -946,6 +982,30 @@ var Crediball = (() => {
|
|
|
946
982
|
}
|
|
947
983
|
});
|
|
948
984
|
}
|
|
985
|
+
/** HTML for the "Have a promo code?" toggle-open field. Gated on the
|
|
986
|
+
* `allow-promo-code` attribute (off by default). Not validated client-side —
|
|
987
|
+
* an invalid code simply doesn't grant a bonus when the top-up is processed. */
|
|
988
|
+
renderPromoCode() {
|
|
989
|
+
if (!this.hasAttribute("allow-promo-code")) return "";
|
|
990
|
+
const label = this.getAttribute("promo-code-label") ?? "Have a promo code?";
|
|
991
|
+
if (!this.promoCodeOpen) {
|
|
992
|
+
return `<div class="promo-code">
|
|
993
|
+
<button class="promo-code-toggle" type="button" data-open-promo-code>${escapeHtml(label)}</button>
|
|
994
|
+
</div>`;
|
|
995
|
+
}
|
|
996
|
+
return `<div class="promo-code">
|
|
997
|
+
<div class="promo-code-label">${escapeHtml(label)}</div>
|
|
998
|
+
<input class="promo-code-input" placeholder="PROMOCODE" aria-label="Promo code" />
|
|
999
|
+
</div>`;
|
|
1000
|
+
}
|
|
1001
|
+
/** The current promo code input value (uppercased, trimmed), or undefined
|
|
1002
|
+
* when the field isn't shown/filled in. Read at click time — never re-render
|
|
1003
|
+
* on keystroke, same reasoning as the custom-amount field. */
|
|
1004
|
+
currentPromoCode() {
|
|
1005
|
+
const input = this.shadow.querySelector(".promo-code-input");
|
|
1006
|
+
const value = input?.value.trim().toUpperCase();
|
|
1007
|
+
return value || void 0;
|
|
1008
|
+
}
|
|
949
1009
|
/** HTML for the auto-topup opt-in row / status line / needs_auth-fix prompt. */
|
|
950
1010
|
renderAutoTopup(packages, autoTopup, currency) {
|
|
951
1011
|
if (!packages.length) return "";
|
package/dist/index.d.ts
CHANGED
|
@@ -113,6 +113,7 @@ declare class CrediballPaywallElement extends CrediballElement {
|
|
|
113
113
|
private referral;
|
|
114
114
|
private referralCopied;
|
|
115
115
|
private autoTopupOpen;
|
|
116
|
+
private promoCodeOpen;
|
|
116
117
|
static get observedAttributes(): string[];
|
|
117
118
|
constructor();
|
|
118
119
|
connectedCallback(): void;
|
|
@@ -122,6 +123,14 @@ declare class CrediballPaywallElement extends CrediballElement {
|
|
|
122
123
|
open(): void;
|
|
123
124
|
close(): void;
|
|
124
125
|
protected render(): void;
|
|
126
|
+
/** HTML for the "Have a promo code?" toggle-open field. Gated on the
|
|
127
|
+
* `allow-promo-code` attribute (off by default). Not validated client-side —
|
|
128
|
+
* an invalid code simply doesn't grant a bonus when the top-up is processed. */
|
|
129
|
+
private renderPromoCode;
|
|
130
|
+
/** The current promo code input value (uppercased, trimmed), or undefined
|
|
131
|
+
* when the field isn't shown/filled in. Read at click time — never re-render
|
|
132
|
+
* on keystroke, same reasoning as the custom-amount field. */
|
|
133
|
+
private currentPromoCode;
|
|
125
134
|
/** HTML for the auto-topup opt-in row / status line / needs_auth-fix prompt. */
|
|
126
135
|
private renderAutoTopup;
|
|
127
136
|
/** Copy the invite link to the clipboard, with a 2s "Copied!" confirmation. */
|
package/dist/index.js
CHANGED
package/dist/register.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crediball/elements",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Framework-agnostic web components for showing Crediball credits inside your AI app.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Filippo Rezzadore <filipporezzadore@gmail.com>",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"prepublishOnly": "npm run build"
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|
|
59
|
-
"@crediball/core": "^0.
|
|
59
|
+
"@crediball/core": "^0.8.0"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
62
|
"tsup": "^8.3.5",
|