@crediball/elements 0.5.0 → 0.6.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.
@@ -185,7 +185,7 @@ var CrediballLowCreditWarningElement = class extends CrediballElement {
185
185
  };
186
186
 
187
187
  // src/paywall.ts
188
- import { formatMoney } from "@crediball/core";
188
+ import { formatMoney, fetchReferral } from "@crediball/core";
189
189
  var CURRENCY_SYMBOLS = { EUR: "\u20AC", USD: "$", GBP: "\xA3" };
190
190
  function currencySymbolFor(currency) {
191
191
  return CURRENCY_SYMBOLS[currency.toUpperCase()] ?? currency;
@@ -283,15 +283,59 @@ var STYLE = `
283
283
  padding: 11px 22px;
284
284
  border-radius: var(--crediball-radius, 9999px);
285
285
  }
286
+ .referral { margin-top: 20px; padding-top: 16px; border-top: 1px solid var(--crediball-hairline, #e0e0e0); }
287
+ .referral .eyebrow { margin-bottom: 8px; }
288
+ .referral-row { display: flex; gap: 8px; align-items: center; }
289
+ .referral-row .link {
290
+ flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
291
+ font-size: 13px; color: var(--crediball-ink, #1d1d1f);
292
+ border: 1px solid var(--crediball-hairline, #e0e0e0);
293
+ border-radius: var(--crediball-radius, 9999px); padding: 8px 14px;
294
+ }
295
+ button.referral-copy {
296
+ flex: none; appearance: none; cursor: pointer; background: transparent;
297
+ color: var(--crediball-ink, #1d1d1f); border: 1px solid var(--crediball-hairline, #e0e0e0);
298
+ font: inherit; font-size: 14px; padding: 8px 16px; border-radius: var(--crediball-radius, 9999px);
299
+ white-space: nowrap;
300
+ }
286
301
  `;
287
302
  var CrediballPaywallElement = class extends CrediballElement {
288
- static get observedAttributes() {
289
- return [...CrediballElement.observedAttributes, "open", "title", "description", "amounts"];
290
- }
291
303
  constructor() {
292
304
  super();
305
+ this.referral = null;
306
+ this.referralCopied = false;
293
307
  this.shadow = this.attachShadow({ mode: "open" });
294
308
  }
309
+ static get observedAttributes() {
310
+ return [...CrediballElement.observedAttributes, "open", "title", "description", "amounts"];
311
+ }
312
+ connectedCallback() {
313
+ super.connectedCallback();
314
+ void this.loadReferral();
315
+ }
316
+ attributeChangedCallback(name) {
317
+ super.attributeChangedCallback(name);
318
+ if (this.isConnected && (name === "publishable-key" || name === "user-id" || name === "api-url")) {
319
+ void this.loadReferral();
320
+ }
321
+ }
322
+ /** Fetch the user's invite link for the "Refer a friend" row (best-effort). */
323
+ async loadReferral() {
324
+ const publishableKey = this.getAttribute("publishable-key");
325
+ const userId = this.getAttribute("user-id");
326
+ const apiUrl = this.getAttribute("api-url") ?? void 0;
327
+ if (!publishableKey || !userId) {
328
+ this.referral = null;
329
+ return;
330
+ }
331
+ try {
332
+ const data = await fetchReferral({ publishableKey, userId, apiUrl });
333
+ this.referral = data.enabled ? data : null;
334
+ } catch {
335
+ this.referral = null;
336
+ }
337
+ if (this.hasAttribute("open")) this.render();
338
+ }
295
339
  open() {
296
340
  this.setAttribute("open", "");
297
341
  }
@@ -357,6 +401,13 @@ var CrediballPaywallElement = class extends CrediballElement {
357
401
  <button class="custom-add" type="button">${escapeHtml(addButtonText)}</button>
358
402
  </div>` : ""}
359
403
  <p class="error" hidden></p>
404
+ ${this.referral?.link ? `<div class="referral">
405
+ <div class="eyebrow">${this.referral.rewardCredits && this.referral.rewardCredits > 0 ? `Refer a friend \xB7 earn ${formatCredits(this.referral.rewardCredits)} credits` : "Refer a friend"}</div>
406
+ <div class="referral-row">
407
+ <div class="link" title="${escapeHtml(this.referral.link)}">${escapeHtml(this.referral.link)}</div>
408
+ <button class="referral-copy" type="button">${this.referralCopied ? "Copied!" : "Copy"}</button>
409
+ </div>
410
+ </div>` : ""}
360
411
  <button class="close" part="close" type="button">Not now</button>
361
412
  </div>
362
413
  </div>
@@ -365,6 +416,7 @@ var CrediballPaywallElement = class extends CrediballElement {
365
416
  if (e.target === e.currentTarget) this.close();
366
417
  });
367
418
  this.shadow.querySelector(".close")?.addEventListener("click", () => this.close());
419
+ this.shadow.querySelector(".referral-copy")?.addEventListener("click", () => void this.copyReferral());
368
420
  this.shadow.querySelectorAll("button.option").forEach((el) => {
369
421
  const btn = el;
370
422
  btn.addEventListener("click", () => {
@@ -414,6 +466,18 @@ var CrediballPaywallElement = class extends CrediballElement {
414
466
  });
415
467
  }
416
468
  }
469
+ /** Copy the invite link to the clipboard, with a 2s "Copied!" confirmation. */
470
+ async copyReferral() {
471
+ const link = this.referral?.link;
472
+ if (!link || typeof navigator === "undefined" || !navigator.clipboard) return;
473
+ await navigator.clipboard.writeText(link);
474
+ this.referralCopied = true;
475
+ this.render();
476
+ setTimeout(() => {
477
+ this.referralCopied = false;
478
+ if (this.hasAttribute("open")) this.render();
479
+ }, 2e3);
480
+ }
417
481
  /** Dispatch a cancelable option event; returns true when the host did NOT preventDefault. */
418
482
  emit(name, detail) {
419
483
  const ev = new CustomEvent(name, { detail, bubbles: true, composed: true, cancelable: true });
@@ -458,7 +522,7 @@ function showError(el, message) {
458
522
 
459
523
  // src/referral-card.ts
460
524
  import {
461
- fetchReferral,
525
+ fetchReferral as fetchReferral2,
462
526
  captureReferral,
463
527
  completeStoredReferral
464
528
  } from "@crediball/core";
@@ -563,7 +627,7 @@ var CrediballReferralCardElement = class extends CrediballElement {
563
627
  return;
564
628
  }
565
629
  try {
566
- this.data = await fetchReferral({ publishableKey, userId, apiUrl });
630
+ this.data = await fetchReferral2({ publishableKey, userId, apiUrl });
567
631
  } catch {
568
632
  this.data = null;
569
633
  }
@@ -694,15 +694,59 @@ var Crediball = (() => {
694
694
  padding: 11px 22px;
695
695
  border-radius: var(--crediball-radius, 9999px);
696
696
  }
697
+ .referral { margin-top: 20px; padding-top: 16px; border-top: 1px solid var(--crediball-hairline, #e0e0e0); }
698
+ .referral .eyebrow { margin-bottom: 8px; }
699
+ .referral-row { display: flex; gap: 8px; align-items: center; }
700
+ .referral-row .link {
701
+ flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
702
+ font-size: 13px; color: var(--crediball-ink, #1d1d1f);
703
+ border: 1px solid var(--crediball-hairline, #e0e0e0);
704
+ border-radius: var(--crediball-radius, 9999px); padding: 8px 14px;
705
+ }
706
+ button.referral-copy {
707
+ flex: none; appearance: none; cursor: pointer; background: transparent;
708
+ color: var(--crediball-ink, #1d1d1f); border: 1px solid var(--crediball-hairline, #e0e0e0);
709
+ font: inherit; font-size: 14px; padding: 8px 16px; border-radius: var(--crediball-radius, 9999px);
710
+ white-space: nowrap;
711
+ }
697
712
  `;
698
713
  var CrediballPaywallElement = class extends CrediballElement {
699
- static get observedAttributes() {
700
- return [...CrediballElement.observedAttributes, "open", "title", "description", "amounts"];
701
- }
702
714
  constructor() {
703
715
  super();
716
+ this.referral = null;
717
+ this.referralCopied = false;
704
718
  this.shadow = this.attachShadow({ mode: "open" });
705
719
  }
720
+ static get observedAttributes() {
721
+ return [...CrediballElement.observedAttributes, "open", "title", "description", "amounts"];
722
+ }
723
+ connectedCallback() {
724
+ super.connectedCallback();
725
+ void this.loadReferral();
726
+ }
727
+ attributeChangedCallback(name) {
728
+ super.attributeChangedCallback(name);
729
+ if (this.isConnected && (name === "publishable-key" || name === "user-id" || name === "api-url")) {
730
+ void this.loadReferral();
731
+ }
732
+ }
733
+ /** Fetch the user's invite link for the "Refer a friend" row (best-effort). */
734
+ async loadReferral() {
735
+ const publishableKey = this.getAttribute("publishable-key");
736
+ const userId = this.getAttribute("user-id");
737
+ const apiUrl = this.getAttribute("api-url") ?? void 0;
738
+ if (!publishableKey || !userId) {
739
+ this.referral = null;
740
+ return;
741
+ }
742
+ try {
743
+ const data = await fetchReferral({ publishableKey, userId, apiUrl });
744
+ this.referral = data.enabled ? data : null;
745
+ } catch {
746
+ this.referral = null;
747
+ }
748
+ if (this.hasAttribute("open")) this.render();
749
+ }
706
750
  open() {
707
751
  this.setAttribute("open", "");
708
752
  }
@@ -768,6 +812,13 @@ var Crediball = (() => {
768
812
  <button class="custom-add" type="button">${escapeHtml(addButtonText)}</button>
769
813
  </div>` : ""}
770
814
  <p class="error" hidden></p>
815
+ ${this.referral?.link ? `<div class="referral">
816
+ <div class="eyebrow">${this.referral.rewardCredits && this.referral.rewardCredits > 0 ? `Refer a friend \xB7 earn ${formatCredits(this.referral.rewardCredits)} credits` : "Refer a friend"}</div>
817
+ <div class="referral-row">
818
+ <div class="link" title="${escapeHtml(this.referral.link)}">${escapeHtml(this.referral.link)}</div>
819
+ <button class="referral-copy" type="button">${this.referralCopied ? "Copied!" : "Copy"}</button>
820
+ </div>
821
+ </div>` : ""}
771
822
  <button class="close" part="close" type="button">Not now</button>
772
823
  </div>
773
824
  </div>
@@ -776,6 +827,7 @@ var Crediball = (() => {
776
827
  if (e.target === e.currentTarget) this.close();
777
828
  });
778
829
  this.shadow.querySelector(".close")?.addEventListener("click", () => this.close());
830
+ this.shadow.querySelector(".referral-copy")?.addEventListener("click", () => void this.copyReferral());
779
831
  this.shadow.querySelectorAll("button.option").forEach((el) => {
780
832
  const btn = el;
781
833
  btn.addEventListener("click", () => {
@@ -825,6 +877,18 @@ var Crediball = (() => {
825
877
  });
826
878
  }
827
879
  }
880
+ /** Copy the invite link to the clipboard, with a 2s "Copied!" confirmation. */
881
+ async copyReferral() {
882
+ const link = this.referral?.link;
883
+ if (!link || typeof navigator === "undefined" || !navigator.clipboard) return;
884
+ await navigator.clipboard.writeText(link);
885
+ this.referralCopied = true;
886
+ this.render();
887
+ setTimeout(() => {
888
+ this.referralCopied = false;
889
+ if (this.hasAttribute("open")) this.render();
890
+ }, 2e3);
891
+ }
828
892
  /** Dispatch a cancelable option event; returns true when the host did NOT preventDefault. */
829
893
  emit(name, detail) {
830
894
  const ev = new CustomEvent(name, { detail, bubbles: true, composed: true, cancelable: true });
package/dist/index.d.ts CHANGED
@@ -110,11 +110,19 @@ declare class CrediballLowCreditWarningElement extends CrediballElement {
110
110
  */
111
111
  declare class CrediballPaywallElement extends CrediballElement {
112
112
  private shadow;
113
+ private referral;
114
+ private referralCopied;
113
115
  static get observedAttributes(): string[];
114
116
  constructor();
117
+ connectedCallback(): void;
118
+ attributeChangedCallback(name: string): void;
119
+ /** Fetch the user's invite link for the "Refer a friend" row (best-effort). */
120
+ private loadReferral;
115
121
  open(): void;
116
122
  close(): void;
117
123
  protected render(): void;
124
+ /** Copy the invite link to the clipboard, with a 2s "Copied!" confirmation. */
125
+ private copyReferral;
118
126
  /** Dispatch a cancelable option event; returns true when the host did NOT preventDefault. */
119
127
  private emit;
120
128
  private returnPath;
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  CrediballReferralCardElement,
7
7
  CrediballTopUpButtonElement,
8
8
  CrediballUsageElement
9
- } from "./chunk-6CNCZGCF.js";
9
+ } from "./chunk-MWB56UEW.js";
10
10
  export {
11
11
  CrediballBalanceElement,
12
12
  CrediballElement,
package/dist/register.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  CrediballReferralCardElement,
6
6
  CrediballTopUpButtonElement,
7
7
  CrediballUsageElement
8
- } from "./chunk-6CNCZGCF.js";
8
+ } from "./chunk-MWB56UEW.js";
9
9
 
10
10
  // src/register.ts
11
11
  function define(name, ctor) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crediball/elements",
3
- "version": "0.5.0",
3
+ "version": "0.6.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>",