@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 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