@crediball/react 0.11.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.js +1 -1
- package/dist/PaywallModal.d.ts +20 -10
- package/dist/PaywallModal.js +16 -4
- package/package.json +2 -2
|
@@ -264,7 +264,7 @@ export function CrediballProvider({ children, publishableKey, userId, apiUrl, po
|
|
|
264
264
|
// layout box. Host props/CSS are overridden by design (dashboard wins).
|
|
265
265
|
const themeVars = applyRemoteTheme ? themeToCssVars(uiTheme) : {};
|
|
266
266
|
const hasThemeVars = Object.keys(themeVars).length > 0;
|
|
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, 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 })] }));
|
|
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 })] }));
|
|
268
268
|
const configCtx = useMemo(() => ({ publishableKey, userId, apiUrl }), [publishableKey, userId, apiUrl]);
|
|
269
269
|
return (_jsx(CrediballContext.Provider, { value: ctx, children: _jsx(CrediballConfigContext.Provider, { value: configCtx, children: hasThemeVars ? (_jsx("div", { style: { display: "contents", ...themeVars }, children: inner })) : (inner) }) }));
|
|
270
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 {
|
|
@@ -67,17 +67,19 @@ export interface PaywallModalProps extends CrediballThemeOverrides {
|
|
|
67
67
|
onCancelSubscription?: (subscriptionId: string) => void | Promise<void>;
|
|
68
68
|
/**
|
|
69
69
|
* The end user's current auto-topup rule (from `meter.listPackages({ userId })`
|
|
70
|
-
* or `meter.getAutoTopupStatus(userId)`). When absent/null
|
|
71
|
-
*
|
|
72
|
-
*
|
|
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
73
|
*/
|
|
74
74
|
autoTopup?: AutoTopupInfo | null;
|
|
75
75
|
/**
|
|
76
|
-
* Called when the user opts into auto top-up
|
|
77
|
-
* non-empty
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
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`.
|
|
81
83
|
*/
|
|
82
84
|
onEnableAutoTopup?: (input: {
|
|
83
85
|
packageId: string;
|
|
@@ -103,6 +105,14 @@ export interface PaywallModalProps extends CrediballThemeOverrides {
|
|
|
103
105
|
customMin?: number;
|
|
104
106
|
/** Optional maximum custom amount. */
|
|
105
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;
|
|
106
116
|
/** Currency symbol shown beside the custom field (default "€"). */
|
|
107
117
|
currencySymbol?: string;
|
|
108
118
|
/**
|
|
@@ -143,4 +153,4 @@ export interface PaywallModalProps extends CrediballThemeOverrides {
|
|
|
143
153
|
* continue. The host controls when it appears; shows a small "Powered by
|
|
144
154
|
* Crediball" line by default (set `hideBranding` to remove it).
|
|
145
155
|
*/
|
|
146
|
-
export declare function PaywallModal({ open, packages, onSelectPackage, subscriptionPlans, onSelectPlan, activeSubscription, onCancelSubscription, autoTopup, onEnableAutoTopup, amounts, amountPrefix, title, description, onAdd, onClose, allowCustom, addButtonText, customMin, customMax, currencySymbol, balance, balanceRate, currency, usage, usageLabel, hideBranding, referralLink, referralReward, referralLabel, 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, 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, currencySymbol = "€", balance, balanceRate, currency = "EUR", usage, usageLabel = "Usage", hideBranding = false, referralLink, referralReward, referralLabel = "Refer a friend · earn {credits} credits", 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
|
|
@@ -114,6 +120,12 @@ export function PaywallModal({ open, packages, onSelectPackage, subscriptionPlan
|
|
|
114
120
|
borderRadius: theme.radiusPill,
|
|
115
121
|
};
|
|
116
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;
|
|
117
129
|
return (_jsx("div", { ref: overlayRef, role: "dialog", "aria-modal": "true", style: {
|
|
118
130
|
position: "fixed",
|
|
119
131
|
inset: 0,
|
|
@@ -174,7 +186,7 @@ export function PaywallModal({ open, packages, onSelectPackage, subscriptionPlan
|
|
|
174
186
|
display: "flex",
|
|
175
187
|
justifyContent: "space-between",
|
|
176
188
|
alignItems: "center",
|
|
177
|
-
}, 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
|
|
178
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)))
|
|
179
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: {
|
|
180
192
|
flex: 1,
|
|
@@ -189,9 +201,9 @@ export function PaywallModal({ open, packages, onSelectPackage, subscriptionPlan
|
|
|
189
201
|
color: theme.ink,
|
|
190
202
|
background: theme.canvas,
|
|
191
203
|
outline: "none",
|
|
192
|
-
} }), _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,
|
|
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"
|
|
193
205
|
? "Auto top-up needs you to re-confirm your payment method."
|
|
194
|
-
: "Auto top-up's last charge failed." }), _jsx("button", { onClick: () => onEnableAutoTopup({
|
|
206
|
+
: "Auto top-up's last charge failed." }), _jsx("button", { onClick: () => onEnableAutoTopup?.({
|
|
195
207
|
packageId: autoTopup.packageId,
|
|
196
208
|
thresholdCredits: autoTopup.thresholdCredits,
|
|
197
209
|
}), style: {
|
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",
|