@monetizekit/react 0.1.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/README.md +63 -0
- package/dist/index.cjs +571 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +234 -0
- package/dist/index.d.ts +234 -0
- package/dist/index.js +552 -0
- package/dist/index.js.map +1 -0
- package/package.json +68 -0
package/README.md
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# @monetizekit/react
|
|
2
|
+
|
|
3
|
+
Themeable React components and hooks for [MonetizeKit](https://monetizekit.app) —
|
|
4
|
+
the canonical UI library that MonetizeKit's own marketing site and dashboard
|
|
5
|
+
dogfood, and that customers embed in their SaaS apps.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pnpm add @monetizekit/react
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick start
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
import { MonetizeKitProvider, PricingTable } from "@monetizekit/react";
|
|
17
|
+
|
|
18
|
+
export function Pricing() {
|
|
19
|
+
return (
|
|
20
|
+
<MonetizeKitProvider
|
|
21
|
+
publishableKey="pk_live_xxx"
|
|
22
|
+
baseUrl="https://app.monetizekit.app"
|
|
23
|
+
appearance="memphis"
|
|
24
|
+
>
|
|
25
|
+
<PricingTable highlightPlan="Pro" />
|
|
26
|
+
</MonetizeKitProvider>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The `publishableKey` is a browser-safe `pk_*` key restricted to read-only public
|
|
32
|
+
catalog reads and to the origins you allowlist (CORS) in MonetizeKit.
|
|
33
|
+
|
|
34
|
+
## Components
|
|
35
|
+
|
|
36
|
+
- `PricingTable` — live plans incl. graduated/volume metered pricing + Contact Sales
|
|
37
|
+
- `Paywall` — gate content behind an entitlement with an upgrade prompt
|
|
38
|
+
- `EntitlementGate` — conditionally render on a live entitlement check
|
|
39
|
+
- `UsageBanner` — current usage vs allotment with overage hint
|
|
40
|
+
- `CustomerPortal` — plan, usage meters, and credit balance
|
|
41
|
+
|
|
42
|
+
## Hooks
|
|
43
|
+
|
|
44
|
+
`useEntitlement(feature)`, `useUsage(meterId)`, `useCredits()`.
|
|
45
|
+
|
|
46
|
+
## Theming
|
|
47
|
+
|
|
48
|
+
Pass an `appearance` to the provider: a preset name (`light`, `dark`, `memphis`,
|
|
49
|
+
`dashboard`) or `{ preset, tokens }` to override design tokens. Components render
|
|
50
|
+
via `--mk-*` CSS custom properties so they adapt to the host design system.
|
|
51
|
+
|
|
52
|
+
## Development
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
pnpm install
|
|
56
|
+
pnpm test # vitest + RTL
|
|
57
|
+
pnpm build # tsup (ESM + CJS + d.ts)
|
|
58
|
+
pnpm storybook # local component gallery
|
|
59
|
+
pnpm build-storybook
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
The Storybook is deployed as the living component gallery at
|
|
63
|
+
`ui.monetizekit.{dev,delivery,app}`.
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,571 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var react = require('react');
|
|
4
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
5
|
+
|
|
6
|
+
// src/provider.tsx
|
|
7
|
+
|
|
8
|
+
// src/lib/client.ts
|
|
9
|
+
var MonetizeKitClient = class {
|
|
10
|
+
constructor(config) {
|
|
11
|
+
this.config = config;
|
|
12
|
+
}
|
|
13
|
+
async get(path) {
|
|
14
|
+
const headers = {
|
|
15
|
+
Authorization: `Bearer ${this.config.publishableKey}`
|
|
16
|
+
};
|
|
17
|
+
if (this.config.customerToken) {
|
|
18
|
+
headers["X-Customer-Token"] = this.config.customerToken;
|
|
19
|
+
}
|
|
20
|
+
const res = await fetch(`${this.config.baseUrl}${path}`, { headers });
|
|
21
|
+
if (!res.ok) {
|
|
22
|
+
throw new Error(`MonetizeKit API ${res.status} for ${path}`);
|
|
23
|
+
}
|
|
24
|
+
return await res.json();
|
|
25
|
+
}
|
|
26
|
+
listPlans() {
|
|
27
|
+
return this.get("/api/v1/plans?page=1&pageSize=100");
|
|
28
|
+
}
|
|
29
|
+
getEntitlement(customerId, featureKey) {
|
|
30
|
+
return this.get(`/api/v1/entitlements/${customerId}/${encodeURIComponent(featureKey)}`);
|
|
31
|
+
}
|
|
32
|
+
getUsage(customerId, meterId) {
|
|
33
|
+
return this.get(`/api/v1/usage/${customerId}/${encodeURIComponent(meterId)}`);
|
|
34
|
+
}
|
|
35
|
+
getCredits(customerId) {
|
|
36
|
+
return this.get(`/api/v1/credits/${customerId}`);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// src/theme/tokens.ts
|
|
41
|
+
var THEME_PRESETS = {
|
|
42
|
+
light: {
|
|
43
|
+
colorBackground: "#ffffff",
|
|
44
|
+
colorForeground: "#0a0a0a",
|
|
45
|
+
colorMuted: "#71717a",
|
|
46
|
+
colorPrimary: "#4f46e5",
|
|
47
|
+
colorPrimaryForeground: "#ffffff",
|
|
48
|
+
colorAccent: "#10b981",
|
|
49
|
+
colorBorder: "#e4e4e7",
|
|
50
|
+
radius: "0.5rem",
|
|
51
|
+
fontFamily: "system-ui, -apple-system, sans-serif"
|
|
52
|
+
},
|
|
53
|
+
dark: {
|
|
54
|
+
colorBackground: "#0a0a0a",
|
|
55
|
+
colorForeground: "#fafafa",
|
|
56
|
+
colorMuted: "#a1a1aa",
|
|
57
|
+
colorPrimary: "#6366f1",
|
|
58
|
+
colorPrimaryForeground: "#0a0a0a",
|
|
59
|
+
colorAccent: "#10b981",
|
|
60
|
+
colorBorder: "#27272a",
|
|
61
|
+
radius: "0.5rem",
|
|
62
|
+
fontFamily: "system-ui, -apple-system, sans-serif"
|
|
63
|
+
},
|
|
64
|
+
memphis: {
|
|
65
|
+
colorBackground: "#FFFEF2",
|
|
66
|
+
colorForeground: "#1a1a1a",
|
|
67
|
+
colorMuted: "#5b5b52",
|
|
68
|
+
colorPrimary: "#FF6B35",
|
|
69
|
+
colorPrimaryForeground: "#1a1a1a",
|
|
70
|
+
colorAccent: "#00D9FF",
|
|
71
|
+
colorBorder: "#1a1a1a",
|
|
72
|
+
radius: "0",
|
|
73
|
+
fontFamily: "system-ui, -apple-system, sans-serif"
|
|
74
|
+
},
|
|
75
|
+
dashboard: {
|
|
76
|
+
colorBackground: "oklch(1 0 0)",
|
|
77
|
+
colorForeground: "oklch(0.145 0 0)",
|
|
78
|
+
colorMuted: "oklch(0.556 0 0)",
|
|
79
|
+
colorPrimary: "oklch(0.205 0 0)",
|
|
80
|
+
colorPrimaryForeground: "oklch(0.985 0 0)",
|
|
81
|
+
colorAccent: "oklch(0.97 0 0)",
|
|
82
|
+
colorBorder: "oklch(0.922 0 0)",
|
|
83
|
+
radius: "0.625rem",
|
|
84
|
+
fontFamily: "Geist, system-ui, sans-serif"
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
function resolveTokens(appearance = "light") {
|
|
88
|
+
if (typeof appearance === "string") {
|
|
89
|
+
return THEME_PRESETS[appearance];
|
|
90
|
+
}
|
|
91
|
+
const base = THEME_PRESETS[appearance.preset ?? "light"];
|
|
92
|
+
return { ...base, ...appearance.tokens };
|
|
93
|
+
}
|
|
94
|
+
var TOKEN_TO_CSS_VAR = {
|
|
95
|
+
colorBackground: "--mk-bg",
|
|
96
|
+
colorForeground: "--mk-fg",
|
|
97
|
+
colorMuted: "--mk-muted",
|
|
98
|
+
colorPrimary: "--mk-primary",
|
|
99
|
+
colorPrimaryForeground: "--mk-primary-fg",
|
|
100
|
+
colorAccent: "--mk-accent",
|
|
101
|
+
colorBorder: "--mk-border",
|
|
102
|
+
radius: "--mk-radius",
|
|
103
|
+
fontFamily: "--mk-font"
|
|
104
|
+
};
|
|
105
|
+
function tokensToStyle(tokens) {
|
|
106
|
+
const style = {};
|
|
107
|
+
Object.keys(tokens).forEach((key) => {
|
|
108
|
+
style[TOKEN_TO_CSS_VAR[key]] = String(tokens[key]);
|
|
109
|
+
});
|
|
110
|
+
return style;
|
|
111
|
+
}
|
|
112
|
+
var MonetizeKitContext = react.createContext(null);
|
|
113
|
+
function MonetizeKitProvider({
|
|
114
|
+
publishableKey,
|
|
115
|
+
baseUrl,
|
|
116
|
+
customerToken,
|
|
117
|
+
customerId,
|
|
118
|
+
appearance = "light",
|
|
119
|
+
children
|
|
120
|
+
}) {
|
|
121
|
+
const value = react.useMemo(() => {
|
|
122
|
+
return {
|
|
123
|
+
client: new MonetizeKitClient({ publishableKey, baseUrl, customerToken }),
|
|
124
|
+
customerId,
|
|
125
|
+
appearance,
|
|
126
|
+
tokens: resolveTokens(appearance)
|
|
127
|
+
};
|
|
128
|
+
}, [publishableKey, baseUrl, customerToken, customerId, appearance]);
|
|
129
|
+
return /* @__PURE__ */ jsxRuntime.jsx(MonetizeKitContext.Provider, { value, children });
|
|
130
|
+
}
|
|
131
|
+
function useMonetizeKit() {
|
|
132
|
+
const ctx = react.useContext(MonetizeKitContext);
|
|
133
|
+
if (!ctx) {
|
|
134
|
+
throw new Error("useMonetizeKit must be used within a <MonetizeKitProvider>.");
|
|
135
|
+
}
|
|
136
|
+
return ctx;
|
|
137
|
+
}
|
|
138
|
+
function useAsync(run, deps) {
|
|
139
|
+
const [state, setState] = react.useState({
|
|
140
|
+
data: null,
|
|
141
|
+
loading: true,
|
|
142
|
+
error: null
|
|
143
|
+
});
|
|
144
|
+
react.useEffect(() => {
|
|
145
|
+
let active = true;
|
|
146
|
+
const promise = run();
|
|
147
|
+
if (!promise) {
|
|
148
|
+
setState({ data: null, loading: false, error: null });
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
setState((prev) => ({ ...prev, loading: true, error: null }));
|
|
152
|
+
promise.then((data) => {
|
|
153
|
+
if (active) setState({ data, loading: false, error: null });
|
|
154
|
+
}).catch((error) => {
|
|
155
|
+
if (active) {
|
|
156
|
+
setState({
|
|
157
|
+
data: null,
|
|
158
|
+
loading: false,
|
|
159
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
return () => {
|
|
164
|
+
active = false;
|
|
165
|
+
};
|
|
166
|
+
}, deps);
|
|
167
|
+
return state;
|
|
168
|
+
}
|
|
169
|
+
function useEntitlement(featureKey) {
|
|
170
|
+
const { client, customerId } = useMonetizeKit();
|
|
171
|
+
const state = useAsync(
|
|
172
|
+
() => customerId ? client.getEntitlement(customerId, featureKey) : null,
|
|
173
|
+
[client, customerId, featureKey]
|
|
174
|
+
);
|
|
175
|
+
return {
|
|
176
|
+
value: state.data?.value ?? null,
|
|
177
|
+
allowed: state.data?.allowed ?? false,
|
|
178
|
+
loading: state.loading,
|
|
179
|
+
error: state.error
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
function useUsage(meterId) {
|
|
183
|
+
const { client, customerId } = useMonetizeKit();
|
|
184
|
+
const state = useAsync(
|
|
185
|
+
() => customerId ? client.getUsage(customerId, meterId) : null,
|
|
186
|
+
[client, customerId, meterId]
|
|
187
|
+
);
|
|
188
|
+
return {
|
|
189
|
+
current: state.data?.current ?? 0,
|
|
190
|
+
limit: state.data?.limit ?? null,
|
|
191
|
+
loading: state.loading,
|
|
192
|
+
error: state.error
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
function useCredits() {
|
|
196
|
+
const { client, customerId } = useMonetizeKit();
|
|
197
|
+
const state = useAsync(
|
|
198
|
+
() => customerId ? client.getCredits(customerId) : null,
|
|
199
|
+
[client, customerId]
|
|
200
|
+
);
|
|
201
|
+
return {
|
|
202
|
+
balance: state.data?.balance ?? 0,
|
|
203
|
+
currency: state.data?.currency,
|
|
204
|
+
loading: state.loading,
|
|
205
|
+
error: state.error
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// src/lib/format.ts
|
|
210
|
+
function formatMoney(amount, currency = "USD", locale) {
|
|
211
|
+
return new Intl.NumberFormat(locale, {
|
|
212
|
+
style: "currency",
|
|
213
|
+
currency,
|
|
214
|
+
minimumFractionDigits: Number.isInteger(amount) ? 0 : 2,
|
|
215
|
+
maximumFractionDigits: amount < 1 ? 3 : 2
|
|
216
|
+
}).format(amount);
|
|
217
|
+
}
|
|
218
|
+
function formatUnits(value, locale) {
|
|
219
|
+
return new Intl.NumberFormat(locale, {
|
|
220
|
+
notation: value >= 1e4 ? "compact" : "standard",
|
|
221
|
+
maximumFractionDigits: 1
|
|
222
|
+
}).format(value);
|
|
223
|
+
}
|
|
224
|
+
var INTERVAL_SUFFIX = {
|
|
225
|
+
monthly: "/mo",
|
|
226
|
+
annually: "/yr",
|
|
227
|
+
one_time: ""
|
|
228
|
+
};
|
|
229
|
+
function describePlanPrice(plan, locale) {
|
|
230
|
+
const pricing = plan.pricing ?? [];
|
|
231
|
+
const contactSales = (plan.tags ?? []).includes("contact_sales") || pricing.length === 0;
|
|
232
|
+
if (contactSales) {
|
|
233
|
+
return { headline: null, contactSales: true };
|
|
234
|
+
}
|
|
235
|
+
const base = pricing.find((t) => t.type === "flat");
|
|
236
|
+
const hasUsage = pricing.some((t) => t.type === "usage");
|
|
237
|
+
const currency = base?.currency ?? pricing[0]?.currency ?? "USD";
|
|
238
|
+
const interval = base?.interval ?? pricing[0]?.interval ?? "monthly";
|
|
239
|
+
const baseAmount = base?.amount ?? 0;
|
|
240
|
+
const headline = `${formatMoney(baseAmount, currency, locale)}${INTERVAL_SUFFIX[interval]}`;
|
|
241
|
+
return {
|
|
242
|
+
headline,
|
|
243
|
+
caption: hasUsage ? "+ usage" : void 0,
|
|
244
|
+
contactSales: false
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
function describeUsageTerm(term, locale) {
|
|
248
|
+
const parts = [];
|
|
249
|
+
if (term.includedUnits && term.includedUnits > 0) {
|
|
250
|
+
parts.push(`${formatUnits(term.includedUnits, locale)} ${term.meterDisplayName ?? "units"} included`);
|
|
251
|
+
}
|
|
252
|
+
const tiers = term.tieredPricing ?? [];
|
|
253
|
+
if (tiers.length > 0) {
|
|
254
|
+
const first = tiers[0];
|
|
255
|
+
parts.push(
|
|
256
|
+
`then ${formatMoney(first.unitPrice, term.currency, locale)}/unit${term.tierMode === "volume" ? " (volume)" : ""}`
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
return parts.join(", ");
|
|
260
|
+
}
|
|
261
|
+
var wrapperStyle = {
|
|
262
|
+
display: "grid",
|
|
263
|
+
gridTemplateColumns: "repeat(auto-fit, minmax(240px, 1fr))",
|
|
264
|
+
gap: "1.5rem",
|
|
265
|
+
background: "var(--mk-bg)",
|
|
266
|
+
color: "var(--mk-fg)",
|
|
267
|
+
fontFamily: "var(--mk-font)"
|
|
268
|
+
};
|
|
269
|
+
var cardBase = {
|
|
270
|
+
border: "1px solid var(--mk-border)",
|
|
271
|
+
borderRadius: "var(--mk-radius)",
|
|
272
|
+
padding: "1.5rem",
|
|
273
|
+
display: "flex",
|
|
274
|
+
flexDirection: "column",
|
|
275
|
+
gap: "0.75rem"
|
|
276
|
+
};
|
|
277
|
+
function PricingTable({
|
|
278
|
+
plans: plansProp,
|
|
279
|
+
highlightPlan,
|
|
280
|
+
locale,
|
|
281
|
+
onSelectPlan,
|
|
282
|
+
onContactSales
|
|
283
|
+
}) {
|
|
284
|
+
const { client, tokens } = useMonetizeKit();
|
|
285
|
+
const [plans, setPlans] = react.useState(plansProp ?? null);
|
|
286
|
+
const [error, setError] = react.useState(null);
|
|
287
|
+
react.useEffect(() => {
|
|
288
|
+
if (plansProp) {
|
|
289
|
+
setPlans(plansProp);
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
let active = true;
|
|
293
|
+
client.listPlans().then((res) => {
|
|
294
|
+
if (active) setPlans(res.data ?? []);
|
|
295
|
+
}).catch((e) => {
|
|
296
|
+
if (active) setError(e instanceof Error ? e : new Error(String(e)));
|
|
297
|
+
});
|
|
298
|
+
return () => {
|
|
299
|
+
active = false;
|
|
300
|
+
};
|
|
301
|
+
}, [client, plansProp]);
|
|
302
|
+
if (error) {
|
|
303
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { role: "alert", style: { color: "var(--mk-muted)" }, children: "Unable to load pricing." });
|
|
304
|
+
}
|
|
305
|
+
if (!plans) {
|
|
306
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { "aria-busy": "true", style: { color: "var(--mk-muted)" }, children: "Loading pricing\u2026" });
|
|
307
|
+
}
|
|
308
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { ...tokensToStyle(tokens), ...wrapperStyle }, "data-mk-component": "pricing-table", children: plans.map((plan) => {
|
|
309
|
+
const price = describePlanPrice(plan, locale);
|
|
310
|
+
const highlighted = highlightPlan != null && plan.name.toLowerCase() === highlightPlan.toLowerCase();
|
|
311
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
312
|
+
"div",
|
|
313
|
+
{
|
|
314
|
+
style: {
|
|
315
|
+
...cardBase,
|
|
316
|
+
borderColor: highlighted ? "var(--mk-primary)" : "var(--mk-border)",
|
|
317
|
+
borderWidth: highlighted ? 2 : 1
|
|
318
|
+
},
|
|
319
|
+
"data-mk-plan": plan.name,
|
|
320
|
+
"data-mk-highlighted": highlighted ? "true" : void 0,
|
|
321
|
+
children: [
|
|
322
|
+
highlighted ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
323
|
+
"span",
|
|
324
|
+
{
|
|
325
|
+
style: {
|
|
326
|
+
alignSelf: "flex-start",
|
|
327
|
+
background: "var(--mk-primary)",
|
|
328
|
+
color: "var(--mk-primary-fg)",
|
|
329
|
+
borderRadius: "var(--mk-radius)",
|
|
330
|
+
padding: "0.125rem 0.5rem",
|
|
331
|
+
fontSize: "0.75rem",
|
|
332
|
+
fontWeight: 600
|
|
333
|
+
},
|
|
334
|
+
children: "Most Popular"
|
|
335
|
+
}
|
|
336
|
+
) : null,
|
|
337
|
+
/* @__PURE__ */ jsxRuntime.jsx("h3", { style: { margin: 0, fontSize: "1.25rem", fontWeight: 700 }, children: plan.name }),
|
|
338
|
+
plan.description ? /* @__PURE__ */ jsxRuntime.jsx("p", { style: { margin: 0, color: "var(--mk-muted)", fontSize: "0.875rem" }, children: plan.description }) : null,
|
|
339
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "baseline", gap: "0.375rem" }, children: [
|
|
340
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: "2rem", fontWeight: 700 }, children: price.contactSales ? "Custom" : price.headline }),
|
|
341
|
+
price.caption ? /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "var(--mk-muted)", fontSize: "0.875rem" }, children: price.caption }) : null
|
|
342
|
+
] }),
|
|
343
|
+
plan.entitlements && plan.entitlements.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("ul", { style: { margin: 0, paddingLeft: "1rem", color: "var(--mk-fg)", fontSize: "0.875rem" }, children: plan.entitlements.slice(0, 6).map((e) => /* @__PURE__ */ jsxRuntime.jsxs("li", { children: [
|
|
344
|
+
e.featureDisplayName,
|
|
345
|
+
e.type !== "boolean" ? `: ${String(e.value)}` : ""
|
|
346
|
+
] }, e.featureKey)) }) : null,
|
|
347
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
348
|
+
"button",
|
|
349
|
+
{
|
|
350
|
+
type: "button",
|
|
351
|
+
onClick: () => price.contactSales ? onContactSales?.(plan.id) : onSelectPlan?.(plan.id),
|
|
352
|
+
style: {
|
|
353
|
+
marginTop: "auto",
|
|
354
|
+
background: "var(--mk-primary)",
|
|
355
|
+
color: "var(--mk-primary-fg)",
|
|
356
|
+
border: "none",
|
|
357
|
+
borderRadius: "var(--mk-radius)",
|
|
358
|
+
padding: "0.625rem 1rem",
|
|
359
|
+
fontWeight: 600,
|
|
360
|
+
cursor: "pointer"
|
|
361
|
+
},
|
|
362
|
+
children: price.contactSales ? "Contact Sales" : "Get started"
|
|
363
|
+
}
|
|
364
|
+
)
|
|
365
|
+
]
|
|
366
|
+
},
|
|
367
|
+
plan.id
|
|
368
|
+
);
|
|
369
|
+
}) });
|
|
370
|
+
}
|
|
371
|
+
var overlayStyle = {
|
|
372
|
+
border: "1px solid var(--mk-border)",
|
|
373
|
+
borderRadius: "var(--mk-radius)",
|
|
374
|
+
padding: "2rem",
|
|
375
|
+
textAlign: "center",
|
|
376
|
+
background: "var(--mk-bg)",
|
|
377
|
+
color: "var(--mk-fg)",
|
|
378
|
+
fontFamily: "var(--mk-font)",
|
|
379
|
+
display: "flex",
|
|
380
|
+
flexDirection: "column",
|
|
381
|
+
gap: "0.75rem",
|
|
382
|
+
alignItems: "center"
|
|
383
|
+
};
|
|
384
|
+
function Paywall({
|
|
385
|
+
feature,
|
|
386
|
+
children,
|
|
387
|
+
title = "Upgrade to unlock this feature",
|
|
388
|
+
description = "This feature isn't included in your current plan.",
|
|
389
|
+
ctaLabel = "Upgrade",
|
|
390
|
+
onUpgrade
|
|
391
|
+
}) {
|
|
392
|
+
const { allowed, loading } = useEntitlement(feature);
|
|
393
|
+
const { tokens } = useMonetizeKit();
|
|
394
|
+
if (loading) {
|
|
395
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { "aria-busy": "true", style: { color: "var(--mk-muted)" }, children: "Checking access\u2026" });
|
|
396
|
+
}
|
|
397
|
+
if (allowed) {
|
|
398
|
+
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
|
|
399
|
+
}
|
|
400
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { ...tokensToStyle(tokens), ...overlayStyle }, "data-mk-component": "paywall", children: [
|
|
401
|
+
/* @__PURE__ */ jsxRuntime.jsx("h3", { style: { margin: 0, fontSize: "1.125rem", fontWeight: 700 }, children: title }),
|
|
402
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { style: { margin: 0, color: "var(--mk-muted)", fontSize: "0.875rem" }, children: description }),
|
|
403
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
404
|
+
"button",
|
|
405
|
+
{
|
|
406
|
+
type: "button",
|
|
407
|
+
onClick: onUpgrade,
|
|
408
|
+
style: {
|
|
409
|
+
background: "var(--mk-primary)",
|
|
410
|
+
color: "var(--mk-primary-fg)",
|
|
411
|
+
border: "none",
|
|
412
|
+
borderRadius: "var(--mk-radius)",
|
|
413
|
+
padding: "0.625rem 1.25rem",
|
|
414
|
+
fontWeight: 600,
|
|
415
|
+
cursor: "pointer"
|
|
416
|
+
},
|
|
417
|
+
children: ctaLabel
|
|
418
|
+
}
|
|
419
|
+
)
|
|
420
|
+
] });
|
|
421
|
+
}
|
|
422
|
+
var bannerStyle = {
|
|
423
|
+
border: "1px solid var(--mk-border)",
|
|
424
|
+
borderRadius: "var(--mk-radius)",
|
|
425
|
+
padding: "0.875rem 1rem",
|
|
426
|
+
background: "var(--mk-bg)",
|
|
427
|
+
color: "var(--mk-fg)",
|
|
428
|
+
fontFamily: "var(--mk-font)",
|
|
429
|
+
display: "flex",
|
|
430
|
+
flexDirection: "column",
|
|
431
|
+
gap: "0.5rem"
|
|
432
|
+
};
|
|
433
|
+
function UsageBanner({
|
|
434
|
+
meterId,
|
|
435
|
+
label = "Usage",
|
|
436
|
+
locale,
|
|
437
|
+
warnAt = 0.8
|
|
438
|
+
}) {
|
|
439
|
+
const { current, limit, loading } = useUsage(meterId);
|
|
440
|
+
const { tokens } = useMonetizeKit();
|
|
441
|
+
if (loading) {
|
|
442
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { "aria-busy": "true", style: { color: "var(--mk-muted)" }, children: "Loading usage\u2026" });
|
|
443
|
+
}
|
|
444
|
+
const hasLimit = typeof limit === "number" && limit > 0;
|
|
445
|
+
const fraction = hasLimit ? Math.min(1, current / limit) : 0;
|
|
446
|
+
const over = hasLimit && current > limit;
|
|
447
|
+
const warn = hasLimit && fraction >= warnAt;
|
|
448
|
+
const barColor = over || warn ? "var(--mk-primary)" : "var(--mk-accent)";
|
|
449
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { ...tokensToStyle(tokens), ...bannerStyle }, "data-mk-component": "usage-banner", children: [
|
|
450
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", justifyContent: "space-between", fontSize: "0.875rem" }, children: [
|
|
451
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontWeight: 600 }, children: label }),
|
|
452
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { style: { color: "var(--mk-muted)" }, children: [
|
|
453
|
+
formatUnits(current, locale),
|
|
454
|
+
hasLimit ? ` / ${formatUnits(limit, locale)}` : ""
|
|
455
|
+
] })
|
|
456
|
+
] }),
|
|
457
|
+
hasLimit ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
458
|
+
"div",
|
|
459
|
+
{
|
|
460
|
+
style: {
|
|
461
|
+
height: 6,
|
|
462
|
+
borderRadius: 999,
|
|
463
|
+
background: "var(--mk-border)",
|
|
464
|
+
overflow: "hidden"
|
|
465
|
+
},
|
|
466
|
+
role: "progressbar",
|
|
467
|
+
"aria-valuenow": Math.round(fraction * 100),
|
|
468
|
+
"aria-valuemin": 0,
|
|
469
|
+
"aria-valuemax": 100,
|
|
470
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: { width: `${fraction * 100}%`, height: "100%", background: barColor } })
|
|
471
|
+
}
|
|
472
|
+
) : null,
|
|
473
|
+
over ? /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "var(--mk-primary)", fontSize: "0.75rem" }, children: "Over included allotment \u2014 overage billed per usage pricing." }) : null
|
|
474
|
+
] });
|
|
475
|
+
}
|
|
476
|
+
var containerStyle = {
|
|
477
|
+
border: "1px solid var(--mk-border)",
|
|
478
|
+
borderRadius: "var(--mk-radius)",
|
|
479
|
+
padding: "1.25rem",
|
|
480
|
+
background: "var(--mk-bg)",
|
|
481
|
+
color: "var(--mk-fg)",
|
|
482
|
+
fontFamily: "var(--mk-font)",
|
|
483
|
+
display: "flex",
|
|
484
|
+
flexDirection: "column",
|
|
485
|
+
gap: "1rem",
|
|
486
|
+
maxWidth: 480
|
|
487
|
+
};
|
|
488
|
+
function CustomerPortal({
|
|
489
|
+
planName = "Current plan",
|
|
490
|
+
meterIds = [],
|
|
491
|
+
showCredits = true,
|
|
492
|
+
locale,
|
|
493
|
+
currency = "USD",
|
|
494
|
+
onManageBilling
|
|
495
|
+
}) {
|
|
496
|
+
const { tokens } = useMonetizeKit();
|
|
497
|
+
const credits = useCredits();
|
|
498
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { ...tokensToStyle(tokens), ...containerStyle }, "data-mk-component": "customer-portal", children: [
|
|
499
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center" }, children: [
|
|
500
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
501
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: "0.75rem", color: "var(--mk-muted)" }, children: "Plan" }),
|
|
502
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: "1.125rem", fontWeight: 700 }, children: planName })
|
|
503
|
+
] }),
|
|
504
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
505
|
+
"button",
|
|
506
|
+
{
|
|
507
|
+
type: "button",
|
|
508
|
+
onClick: onManageBilling,
|
|
509
|
+
style: {
|
|
510
|
+
background: "var(--mk-primary)",
|
|
511
|
+
color: "var(--mk-primary-fg)",
|
|
512
|
+
border: "none",
|
|
513
|
+
borderRadius: "var(--mk-radius)",
|
|
514
|
+
padding: "0.5rem 0.875rem",
|
|
515
|
+
fontWeight: 600,
|
|
516
|
+
cursor: "pointer"
|
|
517
|
+
},
|
|
518
|
+
children: "Manage billing"
|
|
519
|
+
}
|
|
520
|
+
)
|
|
521
|
+
] }),
|
|
522
|
+
meterIds.map((meterId) => /* @__PURE__ */ jsxRuntime.jsx(UsageBanner, { meterId, label: meterId, locale }, meterId)),
|
|
523
|
+
showCredits ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
524
|
+
"div",
|
|
525
|
+
{
|
|
526
|
+
style: {
|
|
527
|
+
border: "1px solid var(--mk-border)",
|
|
528
|
+
borderRadius: "var(--mk-radius)",
|
|
529
|
+
padding: "0.875rem 1rem",
|
|
530
|
+
display: "flex",
|
|
531
|
+
justifyContent: "space-between"
|
|
532
|
+
},
|
|
533
|
+
children: [
|
|
534
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontWeight: 600, fontSize: "0.875rem" }, children: "Credits" }),
|
|
535
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "var(--mk-muted)" }, children: credits.loading ? "\u2026" : formatMoney(credits.balance, credits.currency ?? currency, locale) })
|
|
536
|
+
]
|
|
537
|
+
}
|
|
538
|
+
) : null
|
|
539
|
+
] });
|
|
540
|
+
}
|
|
541
|
+
function EntitlementGate({
|
|
542
|
+
feature,
|
|
543
|
+
children,
|
|
544
|
+
fallback = null,
|
|
545
|
+
loadingFallback = null
|
|
546
|
+
}) {
|
|
547
|
+
const { allowed, loading } = useEntitlement(feature);
|
|
548
|
+
if (loading) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: loadingFallback });
|
|
549
|
+
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: allowed ? children : fallback });
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
exports.CustomerPortal = CustomerPortal;
|
|
553
|
+
exports.EntitlementGate = EntitlementGate;
|
|
554
|
+
exports.MonetizeKitClient = MonetizeKitClient;
|
|
555
|
+
exports.MonetizeKitProvider = MonetizeKitProvider;
|
|
556
|
+
exports.Paywall = Paywall;
|
|
557
|
+
exports.PricingTable = PricingTable;
|
|
558
|
+
exports.THEME_PRESETS = THEME_PRESETS;
|
|
559
|
+
exports.UsageBanner = UsageBanner;
|
|
560
|
+
exports.describePlanPrice = describePlanPrice;
|
|
561
|
+
exports.describeUsageTerm = describeUsageTerm;
|
|
562
|
+
exports.formatMoney = formatMoney;
|
|
563
|
+
exports.formatUnits = formatUnits;
|
|
564
|
+
exports.resolveTokens = resolveTokens;
|
|
565
|
+
exports.tokensToStyle = tokensToStyle;
|
|
566
|
+
exports.useCredits = useCredits;
|
|
567
|
+
exports.useEntitlement = useEntitlement;
|
|
568
|
+
exports.useMonetizeKit = useMonetizeKit;
|
|
569
|
+
exports.useUsage = useUsage;
|
|
570
|
+
//# sourceMappingURL=index.cjs.map
|
|
571
|
+
//# sourceMappingURL=index.cjs.map
|