@crediball/react 0.10.0 → 0.13.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/CrediballProvider.d.ts +13 -1
- package/dist/CrediballProvider.js +14 -2
- package/dist/PaywallModal.d.ts +43 -2
- package/dist/PaywallModal.js +99 -20
- package/package.json +2 -2
|
@@ -54,6 +54,18 @@ export interface CrediballProviderProps {
|
|
|
54
54
|
* `meter.cancelSubscription({ userId, subscriptionId })` on your backend.
|
|
55
55
|
*/
|
|
56
56
|
onCancelSubscription?: (subscriptionId: string) => void | Promise<void>;
|
|
57
|
+
/**
|
|
58
|
+
* Optional override for the auto-topup opt-in. If omitted, the paywall starts a
|
|
59
|
+
* Crediball-hosted Stripe Checkout in setup mode itself (connect payouts in the
|
|
60
|
+
* dashboard) — no money changes hands there, it only saves a payment method.
|
|
61
|
+
* Provide this only to run your own flow (e.g.
|
|
62
|
+
* `meter.setupAutoTopupCheckout({ userId, packageId, thresholdCredits, ... })`
|
|
63
|
+
* server-side).
|
|
64
|
+
*/
|
|
65
|
+
onEnableAutoTopup?: (input: {
|
|
66
|
+
packageId: string;
|
|
67
|
+
thresholdCredits: number;
|
|
68
|
+
}) => void | Promise<void>;
|
|
57
69
|
/**
|
|
58
70
|
* Auto-detect insufficient-credit responses from any fetch() call and open the
|
|
59
71
|
* paywall automatically — no per-call wiring. Defaults to true. Set false to only
|
|
@@ -172,7 +184,7 @@ interface CrediballContextValue {
|
|
|
172
184
|
* SSE/streaming routes (text/event-stream, ndjson) are also handled automatically —
|
|
173
185
|
* the watcher scans each chunk for credit errors, so no manual wiring is needed.
|
|
174
186
|
*/
|
|
175
|
-
export declare function CrediballProvider({ children, publishableKey, userId, apiUrl, pollIntervalMs, lowCreditThreshold, amounts, onTopup, onSelectPackage, onSelectPlan, onCancelSubscription, watchFetch, allowCustom, customMin, customMax, currencySymbol: currencySymbolProp, title, description, accentColor, applyRemoteTheme, captureReferrals, }: CrediballProviderProps): import("react").JSX.Element;
|
|
187
|
+
export declare function CrediballProvider({ children, publishableKey, userId, apiUrl, pollIntervalMs, lowCreditThreshold, amounts, onTopup, onSelectPackage, onSelectPlan, onCancelSubscription, onEnableAutoTopup, watchFetch, allowCustom, customMin, customMax, currencySymbol: currencySymbolProp, title, description, accentColor, applyRemoteTheme, captureReferrals, }: CrediballProviderProps): import("react").JSX.Element;
|
|
176
188
|
/**
|
|
177
189
|
* Access the paywall + live data from anywhere inside <CrediballProvider>.
|
|
178
190
|
*
|
|
@@ -63,7 +63,7 @@ function symbolForCurrency(iso) {
|
|
|
63
63
|
* SSE/streaming routes (text/event-stream, ndjson) are also handled automatically —
|
|
64
64
|
* the watcher scans each chunk for credit errors, so no manual wiring is needed.
|
|
65
65
|
*/
|
|
66
|
-
export function CrediballProvider({ children, publishableKey, userId, apiUrl, pollIntervalMs, lowCreditThreshold, amounts = [5, 10, 20], onTopup, onSelectPackage, onSelectPlan, onCancelSubscription, watchFetch = true, allowCustom, customMin, customMax, currencySymbol: currencySymbolProp, title, description, accentColor, applyRemoteTheme = true, captureReferrals = true, }) {
|
|
66
|
+
export function CrediballProvider({ children, publishableKey, userId, apiUrl, pollIntervalMs, lowCreditThreshold, amounts = [5, 10, 20], onTopup, onSelectPackage, onSelectPlan, onCancelSubscription, onEnableAutoTopup, watchFetch = true, allowCustom, customMin, customMax, currencySymbol: currencySymbolProp, title, description, accentColor, applyRemoteTheme = true, captureReferrals = true, }) {
|
|
67
67
|
const [open, setOpen] = useState(false);
|
|
68
68
|
// Referral auto-capture: pick up a `?ref=CODE` visit into localStorage (and
|
|
69
69
|
// count the click) as soon as the provider mounts — the visitor usually has
|
|
@@ -240,6 +240,18 @@ export function CrediballProvider({ children, publishableKey, userId, apiUrl, po
|
|
|
240
240
|
await onCancelSubscription?.(subscriptionId);
|
|
241
241
|
await client?.refresh();
|
|
242
242
|
}
|
|
243
|
+
function handleEnableAutoTopup(input) {
|
|
244
|
+
return runPaywallAction(onEnableAutoTopup, input, async () => {
|
|
245
|
+
if (!client)
|
|
246
|
+
return;
|
|
247
|
+
const { url } = await client.createAutoTopupCheckout({
|
|
248
|
+
...input,
|
|
249
|
+
returnPath: currentReturnPath(),
|
|
250
|
+
});
|
|
251
|
+
if (typeof window !== "undefined")
|
|
252
|
+
window.location.assign(url);
|
|
253
|
+
}, "auto-topup setup");
|
|
254
|
+
}
|
|
243
255
|
// Auto-derive the custom-amount field from the dashboard's custom-topup config,
|
|
244
256
|
// so a developer who enabled it doesn't also have to pass allowCustom/min/max.
|
|
245
257
|
// Explicit props still win (allowCustom={false} force-hides it).
|
|
@@ -252,7 +264,7 @@ export function CrediballProvider({ children, publishableKey, userId, apiUrl, po
|
|
|
252
264
|
// layout box. Host props/CSS are overridden by design (dashboard wins).
|
|
253
265
|
const themeVars = applyRemoteTheme ? themeToCssVars(uiTheme) : {};
|
|
254
266
|
const hasThemeVars = Object.keys(themeVars).length > 0;
|
|
255
|
-
const inner = (_jsxs(_Fragment, { children: [children, _jsx(PaywallModal, { open: open, packages: pkgs?.packages, onSelectPackage: handleSelectPackage, subscriptionPlans: pkgs?.subscriptionPlans, onSelectPlan: handleSelectPlan, activeSubscription: pkgs?.activeSubscription, onCancelSubscription: onCancelSubscription ? handleCancelSubscription : undefined, amounts: amounts, amountPrefix: `Add ${currencySymbol}`, onAdd: handleAdd, onClose: hidePaywall, allowCustom: effectiveAllowCustom, customMin: effectiveCustomMin, customMax: effectiveCustomMax, currencySymbol: currencySymbol, balance: snapshot.balance ?? undefined, balanceRate: pkgs?.customTopup.enabled ? pkgs.customTopup.rate : undefined, currency: pkgs?.currency, usage: snapshot.usage?.usedCredits ?? undefined, title: title ?? uiContent?.paywallTitle, description: description ?? uiContent?.paywallDescription, addButtonText: uiContent?.paywallButtonText, referralLink: referral?.link ?? undefined, referralReward: referral?.rewardCredits, accentColor: accentColor })] }));
|
|
267
|
+
const inner = (_jsxs(_Fragment, { children: [children, _jsx(PaywallModal, { open: open, packages: pkgs?.packages, onSelectPackage: handleSelectPackage, subscriptionPlans: pkgs?.subscriptionPlans, onSelectPlan: handleSelectPlan, activeSubscription: pkgs?.activeSubscription, onCancelSubscription: onCancelSubscription ? handleCancelSubscription : undefined, autoTopup: pkgs?.autoTopup, onEnableAutoTopup: handleEnableAutoTopup, amounts: amounts, amountPrefix: `Add ${currencySymbol}`, onAdd: handleAdd, onClose: hidePaywall, allowCustom: effectiveAllowCustom, customMin: effectiveCustomMin, customMax: effectiveCustomMax, customTopupRate: pkgs?.customTopup.enabled ? pkgs.customTopup.rate : undefined, currencySymbol: currencySymbol, balance: snapshot.balance ?? undefined, balanceRate: pkgs?.customTopup.enabled ? pkgs.customTopup.rate : undefined, currency: pkgs?.currency, usage: snapshot.usage?.usedCredits ?? undefined, title: title ?? uiContent?.paywallTitle, description: description ?? uiContent?.paywallDescription, addButtonText: uiContent?.paywallButtonText, referralLink: referral?.link ?? undefined, referralReward: referral?.rewardCredits, referralLabel: uiContent?.referralLabel, accentColor: accentColor })] }));
|
|
256
268
|
const configCtx = useMemo(() => ({ publishableKey, userId, apiUrl }), [publishableKey, userId, apiUrl]);
|
|
257
269
|
return (_jsx(CrediballContext.Provider, { value: ctx, children: _jsx(CrediballConfigContext.Provider, { value: configCtx, children: hasThemeVars ? (_jsx("div", { style: { display: "contents", ...themeVars }, children: inner })) : (inner) }) }));
|
|
258
270
|
}
|
package/dist/PaywallModal.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ export interface SubscriptionPlanOption {
|
|
|
13
13
|
name: string;
|
|
14
14
|
price: number;
|
|
15
15
|
credits: number;
|
|
16
|
-
period: "monthly" | "yearly";
|
|
16
|
+
period: "weekly" | "monthly" | "yearly";
|
|
17
17
|
}
|
|
18
18
|
/** The end user's current active subscription (returned by listPackages() with userId). */
|
|
19
19
|
export interface ActiveSubscriptionInfo {
|
|
@@ -22,6 +22,13 @@ export interface ActiveSubscriptionInfo {
|
|
|
22
22
|
planPeriod: string;
|
|
23
23
|
currentPeriodEnd: string;
|
|
24
24
|
}
|
|
25
|
+
/** The end user's current auto-topup rule (returned by listPackages() with userId). */
|
|
26
|
+
export interface AutoTopupInfo {
|
|
27
|
+
enabled: boolean;
|
|
28
|
+
packageId: string;
|
|
29
|
+
thresholdCredits: number;
|
|
30
|
+
status: "pending" | "active" | "needs_auth" | "failed";
|
|
31
|
+
}
|
|
25
32
|
export interface PaywallModalProps extends CrediballThemeOverrides {
|
|
26
33
|
open: boolean;
|
|
27
34
|
/**
|
|
@@ -58,6 +65,26 @@ export interface PaywallModalProps extends CrediballThemeOverrides {
|
|
|
58
65
|
* Call `meter.cancelSubscription({ userId, subscriptionId })` in your backend.
|
|
59
66
|
*/
|
|
60
67
|
onCancelSubscription?: (subscriptionId: string) => void | Promise<void>;
|
|
68
|
+
/**
|
|
69
|
+
* The end user's current auto-topup rule (from `meter.listPackages({ userId })`
|
|
70
|
+
* or `meter.getAutoTopupStatus(userId)`). When absent/null, an opt-in row is
|
|
71
|
+
* shown below the packages so the user can turn on "keep me topped up
|
|
72
|
+
* automatically".
|
|
73
|
+
*/
|
|
74
|
+
autoTopup?: AutoTopupInfo | null;
|
|
75
|
+
/**
|
|
76
|
+
* Called when the user opts into auto top-up. Shown whenever `packages` is
|
|
77
|
+
* non-empty — same as `onSelectPackage`, this is optional to invoke (a no-op
|
|
78
|
+
* if you don't wire it, e.g. in a preview with no backend). Call
|
|
79
|
+
* `meter.setupAutoTopupCheckout({ userId, packageId, thresholdCredits,
|
|
80
|
+
* successUrl, cancelUrl })` in your backend — it saves a payment method, no
|
|
81
|
+
* money changes hands in this step — then redirect the browser to the
|
|
82
|
+
* returned `url`.
|
|
83
|
+
*/
|
|
84
|
+
onEnableAutoTopup?: (input: {
|
|
85
|
+
packageId: string;
|
|
86
|
+
thresholdCredits: number;
|
|
87
|
+
}) => void;
|
|
61
88
|
/**
|
|
62
89
|
* Legacy: flat list of euro amounts. Prefer `packages` + `onSelectPackage`.
|
|
63
90
|
* When both are provided, `packages` takes precedence.
|
|
@@ -78,6 +105,14 @@ export interface PaywallModalProps extends CrediballThemeOverrides {
|
|
|
78
105
|
customMin?: number;
|
|
79
106
|
/** Optional maximum custom amount. */
|
|
80
107
|
customMax?: number;
|
|
108
|
+
/**
|
|
109
|
+
* Credits granted per 1 currency unit for the custom-amount field (your
|
|
110
|
+
* dashboard's custom top-up rate). When provided, a live "≈ N credits"
|
|
111
|
+
* line appears under the field as the user types, computed the same way
|
|
112
|
+
* the actual charge is (Math.round(amount * rate)) — so it never promises
|
|
113
|
+
* a different number than what gets granted.
|
|
114
|
+
*/
|
|
115
|
+
customTopupRate?: number;
|
|
81
116
|
/** Currency symbol shown beside the custom field (default "€"). */
|
|
82
117
|
currencySymbol?: string;
|
|
83
118
|
/**
|
|
@@ -105,6 +140,12 @@ export interface PaywallModalProps extends CrediballThemeOverrides {
|
|
|
105
140
|
referralLink?: string | null;
|
|
106
141
|
/** Credits earned per successful referral, shown in the refer-a-friend blurb. */
|
|
107
142
|
referralReward?: number;
|
|
143
|
+
/**
|
|
144
|
+
* Label above the referral link. `{credits}` is replaced with `referralReward`.
|
|
145
|
+
* Default "Refer a friend · earn {credits} credits". Shown as written (not
|
|
146
|
+
* uppercased), so lowercase/sentence case is preserved.
|
|
147
|
+
*/
|
|
148
|
+
referralLabel?: string;
|
|
108
149
|
style?: CSSProperties;
|
|
109
150
|
}
|
|
110
151
|
/**
|
|
@@ -112,4 +153,4 @@ export interface PaywallModalProps extends CrediballThemeOverrides {
|
|
|
112
153
|
* continue. The host controls when it appears; shows a small "Powered by
|
|
113
154
|
* Crediball" line by default (set `hideBranding` to remove it).
|
|
114
155
|
*/
|
|
115
|
-
export declare function PaywallModal({ open, packages, onSelectPackage, subscriptionPlans, onSelectPlan, activeSubscription, onCancelSubscription, amounts, amountPrefix, title, description, onAdd, onClose, allowCustom, addButtonText, customMin, customMax, currencySymbol, balance, balanceRate, currency, usage, usageLabel, hideBranding, referralLink, referralReward, accentColor, style, }: PaywallModalProps): import("react").JSX.Element | null;
|
|
156
|
+
export declare function PaywallModal({ open, packages, onSelectPackage, subscriptionPlans, onSelectPlan, activeSubscription, onCancelSubscription, autoTopup, onEnableAutoTopup, amounts, amountPrefix, title, description, onAdd, onClose, allowCustom, addButtonText, customMin, customMax, customTopupRate, currencySymbol, balance, balanceRate, currency, usage, usageLabel, hideBranding, referralLink, referralReward, referralLabel, accentColor, style, }: PaywallModalProps): import("react").JSX.Element | null;
|
package/dist/PaywallModal.js
CHANGED
|
@@ -3,6 +3,12 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { useEffect, useRef, useState } from "react";
|
|
4
4
|
import { theme, accentStyle } from "./theme";
|
|
5
5
|
import { formatCredits, formatMoney } from "./format";
|
|
6
|
+
/** Shorthand suffix for a billing period, used in plan price labels (e.g. "€9.99/mo"). */
|
|
7
|
+
const PERIOD_SUFFIX = {
|
|
8
|
+
weekly: "wk",
|
|
9
|
+
monthly: "mo",
|
|
10
|
+
yearly: "yr",
|
|
11
|
+
};
|
|
6
12
|
/** Shared uppercase eyebrow-label style used above the subscribe/buy-credits sections. */
|
|
7
13
|
function sectionLabelStyle(marginBottom) {
|
|
8
14
|
return {
|
|
@@ -19,7 +25,7 @@ function sectionLabelStyle(marginBottom) {
|
|
|
19
25
|
* continue. The host controls when it appears; shows a small "Powered by
|
|
20
26
|
* Crediball" line by default (set `hideBranding` to remove it).
|
|
21
27
|
*/
|
|
22
|
-
export function PaywallModal({ open, packages, onSelectPackage, subscriptionPlans, onSelectPlan, activeSubscription, onCancelSubscription, amounts = [5, 10], amountPrefix = "Add €", title = "You need more credits to continue.", description = "Top up to keep using this feature.", onAdd, onClose, allowCustom = false, addButtonText = "Add", customMin = 1, customMax, currencySymbol = "€", balance, balanceRate, currency = "EUR", usage, usageLabel = "Usage", hideBranding = false, referralLink, referralReward, accentColor, style, }) {
|
|
28
|
+
export function PaywallModal({ open, packages, onSelectPackage, subscriptionPlans, onSelectPlan, activeSubscription, onCancelSubscription, autoTopup, onEnableAutoTopup, amounts = [5, 10], amountPrefix = "Add €", title = "You need more credits to continue.", description = "Top up to keep using this feature.", onAdd, onClose, allowCustom = false, addButtonText = "Add", customMin = 1, customMax, customTopupRate, currencySymbol = "€", balance, balanceRate, currency = "EUR", usage, usageLabel = "Usage", hideBranding = false, referralLink, referralReward, referralLabel = "Refer a friend · earn {credits} credits", accentColor, style, }) {
|
|
23
29
|
// Stack the top-up buttons one-per-line when the dialog itself is narrow.
|
|
24
30
|
// Measured off the dialog's own box (via ResizeObserver) rather than the
|
|
25
31
|
// window — in normal use the two are the same width, but this also makes
|
|
@@ -32,6 +38,9 @@ export function PaywallModal({ open, packages, onSelectPackage, subscriptionPlan
|
|
|
32
38
|
const [cancelPending, setCancelPending] = useState(false);
|
|
33
39
|
const [cancelDone, setCancelDone] = useState(false);
|
|
34
40
|
const [referralCopied, setReferralCopied] = useState(false);
|
|
41
|
+
const [autoTopupOpen, setAutoTopupOpen] = useState(false);
|
|
42
|
+
const [autoTopupPackageId, setAutoTopupPackageId] = useState("");
|
|
43
|
+
const [autoTopupThreshold, setAutoTopupThreshold] = useState("");
|
|
35
44
|
useEffect(() => {
|
|
36
45
|
const el = overlayRef.current;
|
|
37
46
|
if (!el || typeof ResizeObserver === "undefined")
|
|
@@ -43,6 +52,15 @@ export function PaywallModal({ open, packages, onSelectPackage, subscriptionPlan
|
|
|
43
52
|
observer.observe(el);
|
|
44
53
|
return () => observer.disconnect();
|
|
45
54
|
}, []);
|
|
55
|
+
// Default the auto-topup picker to the cheapest package + its own credit
|
|
56
|
+
// amount as the suggested threshold, once packages are available.
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
if (!packages || packages.length === 0 || autoTopupPackageId)
|
|
59
|
+
return;
|
|
60
|
+
const cheapest = packages.reduce((min, p) => (p.price < min.price ? p : min), packages[0]);
|
|
61
|
+
setAutoTopupPackageId(cheapest.id);
|
|
62
|
+
setAutoTopupThreshold(String(cheapest.credits));
|
|
63
|
+
}, [packages, autoTopupPackageId]);
|
|
46
64
|
if (!open)
|
|
47
65
|
return null;
|
|
48
66
|
async function handleCancelSubscription() {
|
|
@@ -81,6 +99,12 @@ export function PaywallModal({ open, packages, onSelectPackage, subscriptionPlan
|
|
|
81
99
|
setCustomError(null);
|
|
82
100
|
onAdd?.(amt);
|
|
83
101
|
}
|
|
102
|
+
function submitAutoTopup() {
|
|
103
|
+
const threshold = Number(autoTopupThreshold);
|
|
104
|
+
if (!autoTopupPackageId || !Number.isFinite(threshold) || threshold < 0)
|
|
105
|
+
return;
|
|
106
|
+
onEnableAutoTopup?.({ packageId: autoTopupPackageId, thresholdCredits: threshold });
|
|
107
|
+
}
|
|
84
108
|
const buttonStyle = {
|
|
85
109
|
flex: 1,
|
|
86
110
|
appearance: "none",
|
|
@@ -95,6 +119,13 @@ export function PaywallModal({ open, packages, onSelectPackage, subscriptionPlan
|
|
|
95
119
|
padding: "8px 16px",
|
|
96
120
|
borderRadius: theme.radiusPill,
|
|
97
121
|
};
|
|
122
|
+
const resolvedReferralLabel = referralLabel.replace("{credits}", formatCredits(referralReward ?? 0));
|
|
123
|
+
// Same rounding the actual charge uses (see /api/public/topup/checkout),
|
|
124
|
+
// so this preview never promises a different number than what's granted.
|
|
125
|
+
const customAmountParsed = Number(customValue);
|
|
126
|
+
const customCreditsPreview = customTopupRate && customTopupRate > 0 && Number.isFinite(customAmountParsed) && customAmountParsed > 0
|
|
127
|
+
? Math.round(customAmountParsed * customTopupRate)
|
|
128
|
+
: null;
|
|
98
129
|
return (_jsx("div", { ref: overlayRef, role: "dialog", "aria-modal": "true", style: {
|
|
99
130
|
position: "fixed",
|
|
100
131
|
inset: 0,
|
|
@@ -155,7 +186,7 @@ export function PaywallModal({ open, packages, onSelectPackage, subscriptionPlan
|
|
|
155
186
|
display: "flex",
|
|
156
187
|
justifyContent: "space-between",
|
|
157
188
|
alignItems: "center",
|
|
158
|
-
}, children: [_jsx("span", { children: plan.name }), _jsxs("span", { style: { opacity: 0.85, fontSize: 14 }, children: [formatMoney(plan.price, currency), "/", plan.period
|
|
189
|
+
}, children: [_jsx("span", { children: plan.name }), _jsxs("span", { style: { opacity: 0.85, fontSize: 14 }, children: [formatMoney(plan.price, currency), "/", PERIOD_SUFFIX[plan.period], " \u00B7 ", formatCredits(plan.credits), " cr"] })] }, plan.id))) })] })), _jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 10, marginTop: 24 }, children: [(!activeSubscription && subscriptionPlans && subscriptionPlans.length > 0) && (_jsx("div", { style: sectionLabelStyle(-2), children: "Or buy credits" })), activeSubscription && packages && packages.length > 0 && (_jsx("div", { style: sectionLabelStyle(-2), children: "Add extra credits" })), packages && packages.length > 0
|
|
159
190
|
? packages.map((pkg) => (_jsxs("button", { onClick: () => onSelectPackage?.(pkg), style: { ...buttonStyle, flex: "none", display: "flex", justifyContent: "space-between", alignItems: "center" }, children: [_jsx("span", { children: pkg.name }), _jsxs("span", { style: { opacity: 0.85, fontSize: 14 }, children: [formatMoney(pkg.price, currency), " \u00B7 ", formatCredits(pkg.credits), " cr"] })] }, pkg.id)))
|
|
160
191
|
: (!activeSubscription && (_jsx("div", { style: { display: "flex", flexDirection: narrow ? "column" : "row", gap: 12 }, children: amounts.map((amount) => (_jsxs("button", { onClick: () => onAdd?.(amount), style: buttonStyle, children: [amountPrefix, amount] }, amount))) })))] }), allowCustom ? (_jsxs("div", { style: { marginTop: 12 }, children: [_jsxs("div", { style: { display: "flex", gap: 8, alignItems: "center" }, children: [_jsx("span", { style: { fontSize: 14, color: theme.inkMuted }, children: currencySymbol }), _jsx("input", { value: customValue, onChange: (e) => setCustomValue(e.target.value), inputMode: "decimal", placeholder: String(customMin), style: {
|
|
161
192
|
flex: 1,
|
|
@@ -170,13 +201,73 @@ export function PaywallModal({ open, packages, onSelectPackage, subscriptionPlan
|
|
|
170
201
|
color: theme.ink,
|
|
171
202
|
background: theme.canvas,
|
|
172
203
|
outline: "none",
|
|
173
|
-
} }), _jsx("button", { onClick: submitCustom, style: { ...buttonStyle, flex: "none" }, children: addButtonText })] }), customError ? (_jsx("p", { style: { margin: "8px 0 0", fontSize: 13, color: theme.accent }, children: customError })) : null] })) : null,
|
|
174
|
-
|
|
204
|
+
} }), _jsx("button", { onClick: submitCustom, style: { ...buttonStyle, flex: "none" }, children: addButtonText })] }), customCreditsPreview != null ? (_jsxs("p", { style: { margin: "6px 0 0", fontSize: 12, color: theme.inkMuted }, children: ["\u2248 ", formatCredits(customCreditsPreview), " credits"] })) : null, customError ? (_jsx("p", { style: { margin: "8px 0 0", fontSize: 13, color: theme.accent }, children: customError })) : null] })) : null, packages && packages.length > 0 && (_jsx("div", { style: { marginTop: 16 }, children: autoTopup?.enabled && autoTopup.status === "active" ? (_jsxs("p", { style: { margin: 0, fontSize: 12, color: theme.inkMuted }, children: ["Auto top-up is on \u2014 buying", " ", packages.find((p) => p.id === autoTopup.packageId)?.name ?? "your package", " whenever your balance drops below ", formatCredits(autoTopup.thresholdCredits), " credits."] })) : autoTopup?.enabled && (autoTopup.status === "needs_auth" || autoTopup.status === "failed") ? (_jsxs("div", { children: [_jsx("p", { style: { margin: 0, fontSize: 12, color: theme.accent }, children: autoTopup.status === "needs_auth"
|
|
205
|
+
? "Auto top-up needs you to re-confirm your payment method."
|
|
206
|
+
: "Auto top-up's last charge failed." }), _jsx("button", { onClick: () => onEnableAutoTopup?.({
|
|
207
|
+
packageId: autoTopup.packageId,
|
|
208
|
+
thresholdCredits: autoTopup.thresholdCredits,
|
|
209
|
+
}), style: {
|
|
210
|
+
marginTop: 6,
|
|
211
|
+
appearance: "none",
|
|
212
|
+
border: "none",
|
|
213
|
+
background: "transparent",
|
|
214
|
+
cursor: "pointer",
|
|
215
|
+
padding: 0,
|
|
216
|
+
fontSize: 12,
|
|
217
|
+
color: theme.ink,
|
|
218
|
+
fontFamily: theme.font,
|
|
219
|
+
textDecoration: "underline",
|
|
220
|
+
}, children: "Fix auto top-up" })] })) : autoTopupOpen ? (_jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [_jsxs("div", { style: { display: "flex", gap: 8, alignItems: "center" }, children: [_jsx("select", { value: autoTopupPackageId, onChange: (e) => setAutoTopupPackageId(e.target.value), style: {
|
|
221
|
+
flex: 1,
|
|
222
|
+
minWidth: 0,
|
|
223
|
+
fontFamily: theme.font,
|
|
224
|
+
fontSize: 13,
|
|
225
|
+
padding: "6px 10px",
|
|
226
|
+
borderRadius: theme.radiusPill,
|
|
227
|
+
border: `1px solid ${theme.hairline}`,
|
|
228
|
+
color: theme.ink,
|
|
229
|
+
background: theme.canvas,
|
|
230
|
+
}, children: packages.map((pkg) => (_jsxs("option", { value: pkg.id, children: [pkg.name, " \u00B7 ", formatMoney(pkg.price, currency)] }, pkg.id))) }), _jsx("input", { value: autoTopupThreshold, onChange: (e) => setAutoTopupThreshold(e.target.value), inputMode: "numeric", "aria-label": "Threshold credits", style: {
|
|
231
|
+
width: 72,
|
|
232
|
+
fontFamily: theme.font,
|
|
233
|
+
fontSize: 13,
|
|
234
|
+
padding: "6px 10px",
|
|
235
|
+
borderRadius: theme.radiusPill,
|
|
236
|
+
border: `1px solid ${theme.hairline}`,
|
|
237
|
+
color: theme.ink,
|
|
238
|
+
background: theme.canvas,
|
|
239
|
+
} }), _jsx("button", { onClick: submitAutoTopup, style: { ...buttonStyle, flex: "none", padding: "6px 14px", fontSize: 13 }, children: "Turn on" })] }), _jsx("p", { style: { margin: 0, fontSize: 11, color: theme.inkMuted }, children: "Buy that package automatically whenever your balance drops below the credit amount above." })] })) : (_jsx("button", { onClick: () => setAutoTopupOpen(true), style: {
|
|
240
|
+
appearance: "none",
|
|
241
|
+
border: "none",
|
|
242
|
+
background: "transparent",
|
|
243
|
+
cursor: "pointer",
|
|
244
|
+
padding: 0,
|
|
245
|
+
fontSize: 12,
|
|
246
|
+
color: theme.inkMuted,
|
|
247
|
+
fontFamily: theme.font,
|
|
248
|
+
textDecoration: "underline",
|
|
249
|
+
}, children: "Keep me topped up automatically" })) })), onClose ? (_jsx("button", { onClick: onClose, style: {
|
|
250
|
+
width: "100%",
|
|
251
|
+
marginTop: 12,
|
|
252
|
+
appearance: "none",
|
|
253
|
+
border: "none",
|
|
254
|
+
cursor: "pointer",
|
|
255
|
+
background: "transparent",
|
|
256
|
+
color: theme.inkMuted,
|
|
257
|
+
fontFamily: theme.font,
|
|
258
|
+
fontSize: 12,
|
|
259
|
+
padding: "8px 16px",
|
|
260
|
+
borderRadius: theme.radiusPill,
|
|
261
|
+
}, children: "Not now" })) : null, referralLink ? (_jsxs("div", { style: {
|
|
262
|
+
marginTop: 16,
|
|
175
263
|
paddingTop: 16,
|
|
176
264
|
borderTop: `1px solid ${theme.hairline}`,
|
|
177
|
-
}, children: [_jsx("div", { style:
|
|
178
|
-
|
|
179
|
-
:
|
|
265
|
+
}, children: [_jsx("div", { style: {
|
|
266
|
+
fontSize: 12,
|
|
267
|
+
fontWeight: 600,
|
|
268
|
+
color: theme.inkMuted,
|
|
269
|
+
marginBottom: 8,
|
|
270
|
+
}, children: resolvedReferralLabel }), _jsxs("div", { style: { display: "flex", gap: 8, alignItems: "center" }, children: [_jsx("div", { title: referralLink, style: {
|
|
180
271
|
flex: 1,
|
|
181
272
|
minWidth: 0,
|
|
182
273
|
overflow: "hidden",
|
|
@@ -200,17 +291,5 @@ export function PaywallModal({ open, packages, onSelectPackage, subscriptionPlan
|
|
|
200
291
|
padding: "8px 16px",
|
|
201
292
|
borderRadius: theme.radiusPill,
|
|
202
293
|
whiteSpace: "nowrap",
|
|
203
|
-
}, children: referralCopied ? "Copied!" : "Copy" })] })] })) : null,
|
|
204
|
-
width: "100%",
|
|
205
|
-
marginTop: 12,
|
|
206
|
-
appearance: "none",
|
|
207
|
-
border: "none",
|
|
208
|
-
cursor: "pointer",
|
|
209
|
-
background: "transparent",
|
|
210
|
-
color: theme.inkMuted,
|
|
211
|
-
fontFamily: theme.font,
|
|
212
|
-
fontSize: 12,
|
|
213
|
-
padding: "8px 16px",
|
|
214
|
-
borderRadius: theme.radiusPill,
|
|
215
|
-
}, children: "Not now" })) : null, !hideBranding && (_jsx("div", { style: { display: "flex", justifyContent: "flex-end", marginTop: 10 }, children: _jsx("span", { style: { fontSize: 10, color: theme.inkMuted }, children: "Powered by Crediball." }) }))] }) }));
|
|
294
|
+
}, children: referralCopied ? "Copied!" : "Copy" })] })] })) : null, !hideBranding && (_jsx("div", { style: { display: "flex", justifyContent: "flex-end", marginTop: 10 }, children: _jsx("span", { style: { fontSize: 10, color: theme.inkMuted }, children: "Powered by Crediball." }) }))] }) }));
|
|
216
295
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crediball/react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "Drop-in React components for showing Crediball credits inside your AI app.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Filippo Rezzadore <filipporezzadore@gmail.com>",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"react": ">=18"
|
|
52
52
|
},
|
|
53
53
|
"dependencies": {
|
|
54
|
-
"@crediball/core": "^0.
|
|
54
|
+
"@crediball/core": "^0.7.0"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
57
|
"@types/react": "^18.3.11",
|