@nexub/patterns 2.1.0 → 2.2.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.
@@ -0,0 +1,61 @@
1
+ 'use client';
2
+ import {
3
+ PageFrame,
4
+ usePageMessages
5
+ } from "./chunk-7QCJC2DV.js";
6
+
7
+ // src/components/SettingsPage/SettingsPage.tsx
8
+ import * as React from "react";
9
+ import { cn } from "@nexub/react";
10
+ import { ChevronRight } from "@nexub/icons";
11
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
12
+ function Row({ item }) {
13
+ const content = /* @__PURE__ */ jsxs(Fragment, { children: [
14
+ item.leading && /* @__PURE__ */ jsx("span", { className: "flex shrink-0 text-[var(--color-on-surface-variant)]", children: item.leading }),
15
+ /* @__PURE__ */ jsxs("span", { className: "flex min-w-0 flex-1 flex-col text-start", children: [
16
+ /* @__PURE__ */ jsx(
17
+ "span",
18
+ {
19
+ className: cn(
20
+ "text-body-md font-medium",
21
+ item.destructive ? "text-[var(--color-error)]" : "text-[var(--color-on-surface)]"
22
+ ),
23
+ children: item.title
24
+ }
25
+ ),
26
+ item.subtitle && /* @__PURE__ */ jsx("span", { className: "text-body-sm text-[var(--color-on-surface-muted)]", children: item.subtitle })
27
+ ] }),
28
+ item.value != null && /* @__PURE__ */ jsx("span", { className: "shrink-0 text-body-md text-[var(--color-on-surface-muted)]", children: item.value }),
29
+ (item.href || item.onSelect) && !item.destructive && /* @__PURE__ */ jsx(ChevronRight, { size: "sm", "aria-hidden": "true", className: "shrink-0 text-[var(--color-on-surface-muted)] rtl:-scale-x-100" })
30
+ ] });
31
+ const row = "flex min-h-[56px] w-full items-center gap-3 px-4 py-3";
32
+ if (item.href)
33
+ return /* @__PURE__ */ jsx("a", { href: item.href, onClick: item.onSelect && ((e) => {
34
+ e.preventDefault();
35
+ item.onSelect?.();
36
+ }), className: cn(row, "nx-state-layer"), children: content });
37
+ if (item.onSelect)
38
+ return /* @__PURE__ */ jsx("button", { type: "button", onClick: item.onSelect, className: cn(row, "nx-state-layer"), children: content });
39
+ return /* @__PURE__ */ jsxs("div", { className: row, children: [
40
+ content,
41
+ item.control && /* @__PURE__ */ jsx("span", { className: "shrink-0", children: item.control })
42
+ ] });
43
+ }
44
+ var SettingsPage = React.forwardRef(function SettingsPage2({ groups, header, title, messages, ...props }, ref) {
45
+ const m = usePageMessages(messages);
46
+ return /* @__PURE__ */ jsxs(PageFrame, { ref, title: title ?? m.settings, ...props, children: [
47
+ header,
48
+ groups.map((g, i) => {
49
+ const id = `settings-group-${i}`;
50
+ return /* @__PURE__ */ jsxs("section", { "aria-labelledby": g.header ? id : void 0, className: "flex flex-col gap-2", children: [
51
+ g.header && /* @__PURE__ */ jsx("h2", { id, className: "px-4 text-label-lg text-[var(--color-on-surface-variant)]", children: g.header }),
52
+ /* @__PURE__ */ jsx("ul", { className: "overflow-hidden rounded-shape-container border border-[var(--color-outline-variant)] bg-[var(--color-surface-elevated)]", children: g.items.map((it) => /* @__PURE__ */ jsx("li", { className: "border-t border-[var(--color-outline-variant)] first:border-t-0", children: /* @__PURE__ */ jsx(Row, { item: it }) }, it.title)) }),
53
+ g.footer && /* @__PURE__ */ jsx("p", { className: "px-4 text-body-sm text-[var(--color-on-surface-muted)]", children: g.footer })
54
+ ] }, g.header ?? i);
55
+ })
56
+ ] });
57
+ });
58
+
59
+ export {
60
+ SettingsPage
61
+ };
@@ -0,0 +1,71 @@
1
+ 'use client';
2
+ import {
3
+ StatCard
4
+ } from "./chunk-INKX2GYA.js";
5
+ import {
6
+ TransactionRow
7
+ } from "./chunk-OC4XY3US.js";
8
+ import {
9
+ PageFrame,
10
+ usePageMessages,
11
+ useTemplateLocale
12
+ } from "./chunk-7QCJC2DV.js";
13
+
14
+ // src/components/DashboardPage/DashboardPage.tsx
15
+ import * as React from "react";
16
+ import { Amount, Button, Card } from "@nexub/react";
17
+ import { jsx, jsxs } from "react/jsx-runtime";
18
+ function Panel({ title, action, children }) {
19
+ const id = React.useId();
20
+ return /* @__PURE__ */ jsxs("section", { "aria-labelledby": id, className: "flex flex-col gap-3", children: [
21
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
22
+ /* @__PURE__ */ jsx("h2", { id, className: "text-title-md text-[var(--color-on-surface)]", children: title }),
23
+ action
24
+ ] }),
25
+ children
26
+ ] });
27
+ }
28
+ var DashboardPage = React.forwardRef(function DashboardPage2({
29
+ title,
30
+ balance,
31
+ quickActions = [],
32
+ stats = [],
33
+ recent = [],
34
+ onSelectTransaction,
35
+ onSeeAllActivity,
36
+ insights = [],
37
+ messages,
38
+ ...props
39
+ }, ref) {
40
+ const m = usePageMessages(messages);
41
+ const locale = useTemplateLocale();
42
+ return /* @__PURE__ */ jsx(PageFrame, { ref, width: "lg", title, ...props, children: /* @__PURE__ */ jsxs("div", { className: "grid gap-6 lg:grid-cols-3", children: [
43
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-col gap-6 lg:col-span-2", children: [
44
+ /* @__PURE__ */ jsxs(Card, { className: "flex flex-col gap-5 p-6", children: [
45
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
46
+ /* @__PURE__ */ jsx("p", { className: "text-label-lg text-[var(--color-on-surface-variant)]", children: balance.label ?? m.totalBalance }),
47
+ /* @__PURE__ */ jsx(Amount, { value: balance.value, currency: balance.currency, locale, size: "display" })
48
+ ] }),
49
+ quickActions.length > 0 && /* @__PURE__ */ jsx("ul", { "aria-label": m.quickActions, className: "grid grid-cols-2 gap-2 sm:grid-cols-4", children: quickActions.map((a) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(Button, { variant: "secondary", fullWidth: true, onClick: a.onSelect, leadingIcon: a.icon, children: a.label }) }, a.id)) })
50
+ ] }),
51
+ stats.length > 0 && /* @__PURE__ */ jsx("div", { className: "grid gap-4 sm:grid-cols-2 xl:grid-cols-3", children: stats.map((s) => /* @__PURE__ */ jsx(StatCard, { ...s }, s.label)) }),
52
+ recent.length > 0 && /* @__PURE__ */ jsx(
53
+ Panel,
54
+ {
55
+ title: m.recentActivity,
56
+ action: onSeeAllActivity && /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onClick: onSeeAllActivity, children: m.seeAll }),
57
+ children: /* @__PURE__ */ jsx("ul", { className: "overflow-hidden rounded-shape-container border border-[var(--color-outline-variant)] bg-[var(--color-surface-elevated)]", children: recent.map((t) => /* @__PURE__ */ jsx(TransactionRow, { t, onSelect: onSelectTransaction, m, locale }, t.id)) })
58
+ }
59
+ )
60
+ ] }),
61
+ insights.length > 0 && /* @__PURE__ */ jsx(Panel, { title: m.insights, children: /* @__PURE__ */ jsx("ul", { className: "flex flex-col gap-3", children: insights.map((i) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(Card, { className: "flex flex-col gap-2 p-4", children: [
62
+ /* @__PURE__ */ jsx("h3", { className: "text-title-sm text-[var(--color-on-surface)]", children: i.title }),
63
+ /* @__PURE__ */ jsx("p", { className: "text-body-sm text-[var(--color-on-surface-muted)]", children: i.body }),
64
+ i.action && /* @__PURE__ */ jsx(Button, { variant: "link", size: "sm", className: "self-start px-0", onClick: i.action.onSelect, children: i.action.label })
65
+ ] }) }, i.id)) }) })
66
+ ] }) });
67
+ });
68
+
69
+ export {
70
+ DashboardPage
71
+ };
@@ -0,0 +1,83 @@
1
+ 'use client';
2
+ import {
3
+ Conversation
4
+ } from "./chunk-WRP73TXG.js";
5
+ import {
6
+ usePageMessages
7
+ } from "./chunk-7QCJC2DV.js";
8
+
9
+ // src/components/ChatPage/ChatPage.tsx
10
+ import * as React from "react";
11
+ import { MessageBubble, PromptInput, ThinkingIndicator, cn } from "@nexub/react";
12
+ import { jsx, jsxs } from "react/jsx-runtime";
13
+ var ChatPage = React.forwardRef(function ChatPage2({
14
+ messages: list,
15
+ onSend,
16
+ onStop,
17
+ thinking,
18
+ placeholder,
19
+ empty,
20
+ assistantAvatar,
21
+ title,
22
+ showTitle = false,
23
+ as: Tag = "main",
24
+ strings,
25
+ className,
26
+ ...props
27
+ }, ref) {
28
+ const m = usePageMessages(strings);
29
+ const [draft, setDraft] = React.useState("");
30
+ const streaming = list.at(-1)?.streaming ?? false;
31
+ const headingId = React.useId();
32
+ return /* @__PURE__ */ jsxs(
33
+ Tag,
34
+ {
35
+ ref,
36
+ "aria-labelledby": headingId,
37
+ className: cn("flex h-full min-h-0 w-full flex-col", className),
38
+ ...props,
39
+ children: [
40
+ /* @__PURE__ */ jsx("h1", { id: headingId, className: showTitle ? "px-4 pt-4 text-title-lg text-[var(--color-on-surface)]" : "sr-only", children: title ?? m.chat }),
41
+ /* @__PURE__ */ jsx(
42
+ Conversation,
43
+ {
44
+ className: "min-h-0 flex-1",
45
+ composer: /* @__PURE__ */ jsx(
46
+ PromptInput,
47
+ {
48
+ value: draft,
49
+ onValueChange: setDraft,
50
+ placeholder,
51
+ status: streaming ? "streaming" : thinking ? "submitting" : "idle",
52
+ onStop,
53
+ onSubmit: (text) => {
54
+ const t = text.trim();
55
+ if (!t) return;
56
+ onSend(t);
57
+ setDraft("");
58
+ }
59
+ }
60
+ ),
61
+ children: list.length === 0 && !thinking ? /* @__PURE__ */ jsx("div", { className: "flex flex-1 items-center justify-center py-16 text-center text-body-md text-[var(--color-on-surface-muted)]", children: empty ?? m.chatEmpty }) : /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
62
+ list.map((msg) => /* @__PURE__ */ jsx(
63
+ MessageBubble,
64
+ {
65
+ from: msg.role,
66
+ avatar: msg.role === "assistant" ? assistantAvatar : void 0,
67
+ "aria-busy": msg.streaming || void 0,
68
+ children: msg.text
69
+ },
70
+ msg.id
71
+ )),
72
+ thinking && /* @__PURE__ */ jsx(ThinkingIndicator, {})
73
+ ] })
74
+ }
75
+ )
76
+ ]
77
+ }
78
+ );
79
+ });
80
+
81
+ export {
82
+ ChatPage
83
+ };
@@ -0,0 +1,113 @@
1
+ 'use client';
2
+
3
+ // src/components/PageFrame/PageFrame.tsx
4
+ import * as React from "react";
5
+ import { Badge, cn, useLocale, useNexubMessages } from "@nexub/react";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
+ var defaultPageTemplateMessages = {
8
+ activity: "Activity",
9
+ noActivityTitle: "No activity yet",
10
+ noActivityDescription: "Your payments and transfers will show up here.",
11
+ loadMore: "Load more",
12
+ refresh: "Refresh",
13
+ pending: "Pending",
14
+ failed: "Failed",
15
+ completed: "Completed",
16
+ date: "Date",
17
+ details: "Details",
18
+ back: "Back",
19
+ settings: "Settings",
20
+ signIn: "Sign in",
21
+ email: "Email",
22
+ password: "Password",
23
+ forgotPassword: "Forgot password?",
24
+ createAccount: "Create an account",
25
+ howMuch: "How much?",
26
+ amountToSend: "Amount to send",
27
+ balance: (b) => `Balance ${b}`,
28
+ overBalance: (b) => `More than your balance (${b})`,
29
+ fee: "Fee",
30
+ rate: "Rate",
31
+ recipientGets: "Recipient gets",
32
+ arrives: "Arrives",
33
+ continue: "Continue",
34
+ checkDetails: "Check the details",
35
+ edit: "Edit",
36
+ done: "Done",
37
+ stepOf: (s, t) => `Step ${s} of ${t}`,
38
+ sent: "Sent",
39
+ chat: "Chat",
40
+ chatEmpty: "Ask anything to get started.",
41
+ restorePurchases: "Restore purchases",
42
+ choosePlan: "Choose a plan",
43
+ totalBalance: "Total balance",
44
+ quickActions: "Quick actions",
45
+ recentActivity: "Recent activity",
46
+ seeAll: "See all",
47
+ insights: "Insights"
48
+ };
49
+ function usePageMessages(overrides) {
50
+ const provided = useNexubMessages();
51
+ const out = { ...defaultPageTemplateMessages };
52
+ for (const key of Object.keys(out)) {
53
+ const v = overrides?.[key] ?? provided[key];
54
+ if (v !== void 0 && typeof v === typeof out[key]) out[key] = v;
55
+ }
56
+ return out;
57
+ }
58
+ function formatMoney(value, currency, locale, opts) {
59
+ return new Intl.NumberFormat(locale, { style: "currency", currency, ...opts }).format(value);
60
+ }
61
+ function groupByDay(transactions, locale) {
62
+ const day = new Intl.DateTimeFormat(locale, { dateStyle: "medium" });
63
+ const map = /* @__PURE__ */ new Map();
64
+ for (const t of [...transactions].sort((a, b) => +b.date - +a.date)) {
65
+ const key = day.format(t.date);
66
+ map.set(key, [...map.get(key) ?? [], t]);
67
+ }
68
+ return [...map].map(([title, data]) => ({ title, data }));
69
+ }
70
+ function useTemplateLocale() {
71
+ return useLocale().locale;
72
+ }
73
+ function StatusBadge({ status, m, dot }) {
74
+ if (!status) return null;
75
+ return /* @__PURE__ */ jsxs(Badge, { tone: status === "failed" ? "danger" : "warning", size: "sm", children: [
76
+ dot && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "me-1 inline-block h-1.5 w-1.5 rounded-full bg-current" }),
77
+ status === "failed" ? m.failed : m.pending
78
+ ] });
79
+ }
80
+ var widths = { sm: "max-w-[480px]", md: "max-w-3xl", lg: "max-w-6xl" };
81
+ var PageFrame = React.forwardRef(function PageFrame2({ as: Tag = "main", title, description, actions, eyebrow, width = "md", className, children, ...props }, ref) {
82
+ const headingId = React.useId();
83
+ return /* @__PURE__ */ jsxs(
84
+ Tag,
85
+ {
86
+ ref,
87
+ "aria-labelledby": title ? headingId : void 0,
88
+ className: cn("mx-auto flex w-full flex-col gap-6 px-4 py-6 sm:px-6 lg:py-10", widths[width], className),
89
+ ...props,
90
+ children: [
91
+ (title || actions || eyebrow) && /* @__PURE__ */ jsxs("header", { className: "flex flex-col gap-2", children: [
92
+ eyebrow,
93
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [
94
+ title && /* @__PURE__ */ jsx("h1", { id: headingId, className: "text-headline-sm text-[var(--color-on-surface)]", children: title }),
95
+ actions && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap items-center gap-2", children: actions })
96
+ ] }),
97
+ description && /* @__PURE__ */ jsx("p", { className: "text-body-md text-[var(--color-on-surface-muted)]", children: description })
98
+ ] }),
99
+ children
100
+ ]
101
+ }
102
+ );
103
+ });
104
+
105
+ export {
106
+ defaultPageTemplateMessages,
107
+ usePageMessages,
108
+ formatMoney,
109
+ groupByDay,
110
+ useTemplateLocale,
111
+ StatusBadge,
112
+ PageFrame
113
+ };
@@ -0,0 +1,42 @@
1
+ 'use client';
2
+ import {
3
+ InfoList
4
+ } from "./chunk-WPNT5JOU.js";
5
+ import {
6
+ PageFrame,
7
+ usePageMessages,
8
+ useTemplateLocale
9
+ } from "./chunk-7QCJC2DV.js";
10
+
11
+ // src/components/TransactionDetailPage/TransactionDetailPage.tsx
12
+ import * as React from "react";
13
+ import { Amount, Avatar, Badge, Button } from "@nexub/react";
14
+ import { ArrowLeft } from "@nexub/icons";
15
+ import { jsx, jsxs } from "react/jsx-runtime";
16
+ var TransactionDetailPage = React.forwardRef(
17
+ function TransactionDetailPage2({ transaction: t, rows = [], footer, onBack, messages, ...props }, ref) {
18
+ const m = usePageMessages(messages);
19
+ const locale = useTemplateLocale();
20
+ const headingId = React.useId();
21
+ const when = new Intl.DateTimeFormat(locale, { dateStyle: "full", timeStyle: "short" }).format(t.date);
22
+ return /* @__PURE__ */ jsxs(PageFrame, { ref, width: "sm", "aria-labelledby": headingId, ...props, children: [
23
+ onBack && /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", className: "self-start", onClick: onBack, leadingIcon: /* @__PURE__ */ jsx(ArrowLeft, { size: "sm", className: "rtl:-scale-x-100", "aria-hidden": "true" }), children: m.back }),
24
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-2 py-4 text-center", children: [
25
+ /* @__PURE__ */ jsx(Avatar, { name: t.title, src: t.image, size: "xl" }),
26
+ /* @__PURE__ */ jsx("h1", { id: headingId, className: "text-title-lg text-[var(--color-on-surface)]", children: t.title }),
27
+ /* @__PURE__ */ jsx(Amount, { value: t.amount, currency: t.currency, locale, size: "display", tone: "auto", signDisplay: "exceptZero" }),
28
+ /* @__PURE__ */ jsx(Badge, { tone: t.status === "failed" ? "danger" : t.status === "pending" ? "warning" : "success", children: t.status === "failed" ? m.failed : t.status === "pending" ? m.pending : m.completed })
29
+ ] }),
30
+ /* @__PURE__ */ jsx("div", { className: "rounded-shape-container border border-[var(--color-outline-variant)] bg-[var(--color-surface-elevated)] px-4", children: /* @__PURE__ */ jsxs(InfoList.Root, { children: [
31
+ /* @__PURE__ */ jsx(InfoList.Item, { label: m.date, value: when }),
32
+ t.subtitle && /* @__PURE__ */ jsx(InfoList.Item, { label: m.details, value: t.subtitle }),
33
+ rows.map((r) => /* @__PURE__ */ jsx(InfoList.Item, { label: r.label, value: r.value }, r.label))
34
+ ] }) }),
35
+ footer && /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-2 sm:flex-row sm:justify-center", children: footer })
36
+ ] });
37
+ }
38
+ );
39
+
40
+ export {
41
+ TransactionDetailPage
42
+ };
@@ -0,0 +1 @@
1
+ 'use client';
@@ -0,0 +1,134 @@
1
+ 'use client';
2
+ import {
3
+ InfoList
4
+ } from "./chunk-WPNT5JOU.js";
5
+ import {
6
+ PageFrame,
7
+ formatMoney,
8
+ usePageMessages,
9
+ useTemplateLocale
10
+ } from "./chunk-7QCJC2DV.js";
11
+
12
+ // src/components/CheckoutPage/CheckoutPage.tsx
13
+ import * as React from "react";
14
+ import { Alert, Amount, Button, FormField, MoneyInput, Progress } from "@nexub/react";
15
+ import { CheckCircle2, ShieldCheck } from "@nexub/icons";
16
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
17
+ var CheckoutPage = React.forwardRef(function CheckoutPage2({
18
+ from,
19
+ to,
20
+ balance,
21
+ quote,
22
+ reviewRows,
23
+ confirmLabel,
24
+ confirmHint,
25
+ onConfirm,
26
+ resultTitle,
27
+ resultDescription,
28
+ onDone,
29
+ messages,
30
+ ...props
31
+ }, ref) {
32
+ const m = usePageMessages(messages);
33
+ const locale = useTemplateLocale();
34
+ const [step, setStep] = React.useState("amount");
35
+ const [amount, setAmount] = React.useState(null);
36
+ const [busy, setBusy] = React.useState(false);
37
+ const [error, setError] = React.useState();
38
+ const headingRef = React.useRef(null);
39
+ const fmt = (n, c) => formatMoney(n, c, locale);
40
+ const over = amount != null && amount > balance;
41
+ const q = amount && !over ? quote(amount) : void 0;
42
+ const index = step === "amount" ? 1 : step === "review" ? 2 : 3;
43
+ const first = React.useRef(true);
44
+ React.useEffect(() => {
45
+ if (first.current) {
46
+ first.current = false;
47
+ return;
48
+ }
49
+ headingRef.current?.focus();
50
+ }, [step]);
51
+ const rows = amount && q ? reviewRows?.(amount, q) ?? [
52
+ { label: m.amountToSend, value: fmt(amount, from) },
53
+ { label: m.fee, value: fmt(q.fee, from) },
54
+ { label: m.rate, value: `1 ${from} = ${q.rate} ${to}` },
55
+ { label: m.recipientGets, value: fmt((amount - q.fee) * q.rate, to) },
56
+ { label: m.arrives, value: q.arrives }
57
+ ] : [];
58
+ const confirm = async () => {
59
+ if (!amount) return;
60
+ setBusy(true);
61
+ setError(void 0);
62
+ try {
63
+ await onConfirm(amount);
64
+ setStep("done");
65
+ } catch (e) {
66
+ setError(e instanceof Error ? e.message : String(e));
67
+ } finally {
68
+ setBusy(false);
69
+ }
70
+ };
71
+ const title = step === "amount" ? m.howMuch : step === "review" ? m.checkDetails : resultTitle ?? m.sent;
72
+ return /* @__PURE__ */ jsxs(PageFrame, { ref, width: "sm", ...props, children: [
73
+ step !== "done" && /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
74
+ /* @__PURE__ */ jsx(Progress, { value: index, max: 3, "aria-label": m.stepOf(index, 3) }),
75
+ /* @__PURE__ */ jsx("p", { className: "text-label-md text-[var(--color-on-surface-variant)]", children: m.stepOf(index, 3) })
76
+ ] }),
77
+ step === "done" ? /* @__PURE__ */ jsxs("div", { role: "status", className: "flex flex-col items-center gap-4 py-8 text-center", children: [
78
+ /* @__PURE__ */ jsx("span", { className: "grid h-20 w-20 place-items-center rounded-full bg-[var(--color-success-container)] text-[var(--color-success)]", children: /* @__PURE__ */ jsx(CheckCircle2, { size: 40, "aria-hidden": "true" }) }),
79
+ /* @__PURE__ */ jsx("h1", { ref: headingRef, tabIndex: -1, className: "text-headline-sm text-[var(--color-on-surface)] outline-none", children: title }),
80
+ amount != null && /* @__PURE__ */ jsx(Amount, { value: amount, currency: from, locale, size: "display" }),
81
+ resultDescription && /* @__PURE__ */ jsx("p", { className: "text-body-md text-[var(--color-on-surface-muted)]", children: resultDescription }),
82
+ /* @__PURE__ */ jsx(Button, { size: "lg", fullWidth: true, onClick: onDone, children: m.done })
83
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
84
+ /* @__PURE__ */ jsx("h1", { ref: headingRef, tabIndex: -1, className: "text-headline-sm text-[var(--color-on-surface)] outline-none", children: title }),
85
+ step === "amount" && /* @__PURE__ */ jsxs(
86
+ "form",
87
+ {
88
+ className: "flex flex-col gap-6",
89
+ onSubmit: (e) => {
90
+ e.preventDefault();
91
+ if (amount && !over) setStep("review");
92
+ },
93
+ children: [
94
+ /* @__PURE__ */ jsxs(FormField.Root, { invalid: over, children: [
95
+ /* @__PURE__ */ jsx(FormField.Label, { children: m.amountToSend }),
96
+ /* @__PURE__ */ jsx(FormField.Control, { children: /* @__PURE__ */ jsx(MoneyInput, { currency: from, locale, value: amount, onValueChange: setAmount }) }),
97
+ over ? /* @__PURE__ */ jsx(FormField.Error, { children: m.overBalance(fmt(balance, from)) }) : /* @__PURE__ */ jsx(FormField.Description, { children: m.balance(fmt(balance, from)) })
98
+ ] }),
99
+ q && amount && /* @__PURE__ */ jsx("div", { "aria-live": "polite", className: "rounded-shape-container border border-[var(--color-outline-variant)] bg-[var(--color-surface-elevated)] px-4", children: /* @__PURE__ */ jsxs(InfoList.Root, { children: [
100
+ /* @__PURE__ */ jsx(InfoList.Item, { label: m.fee, value: fmt(q.fee, from) }),
101
+ /* @__PURE__ */ jsx(InfoList.Item, { label: m.rate, value: `1 ${from} = ${q.rate} ${to}` }),
102
+ /* @__PURE__ */ jsx(InfoList.Item, { label: m.recipientGets, value: fmt((amount - q.fee) * q.rate, to) }),
103
+ /* @__PURE__ */ jsx(InfoList.Item, { label: m.arrives, value: q.arrives })
104
+ ] }) }),
105
+ /* @__PURE__ */ jsx(Button, { type: "submit", size: "lg", fullWidth: true, disabled: !amount || over, children: m.continue })
106
+ ]
107
+ }
108
+ ),
109
+ step === "review" && /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-6", children: [
110
+ error && /* @__PURE__ */ jsx(Alert, { tone: "danger", role: "alert", children: error }),
111
+ /* @__PURE__ */ jsx("div", { className: "rounded-shape-container border border-[var(--color-outline-variant)] bg-[var(--color-surface-elevated)] px-4", children: /* @__PURE__ */ jsx(InfoList.Root, { children: rows.map((r) => /* @__PURE__ */ jsx(InfoList.Item, { label: r.label, value: r.value }, r.label)) }) }),
112
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
113
+ /* @__PURE__ */ jsx(
114
+ Button,
115
+ {
116
+ size: "lg",
117
+ fullWidth: true,
118
+ loading: busy,
119
+ leadingIcon: confirmHint ? /* @__PURE__ */ jsx(ShieldCheck, { size: "sm", "aria-hidden": "true" }) : void 0,
120
+ onClick: confirm,
121
+ children: confirmLabel
122
+ }
123
+ ),
124
+ confirmHint && /* @__PURE__ */ jsx("p", { className: "text-center text-body-sm text-[var(--color-on-surface-muted)]", children: confirmHint }),
125
+ /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "lg", fullWidth: true, disabled: busy, onClick: () => setStep("amount"), children: m.edit })
126
+ ] })
127
+ ] })
128
+ ] })
129
+ ] });
130
+ });
131
+
132
+ export {
133
+ CheckoutPage
134
+ };
@@ -0,0 +1,42 @@
1
+ 'use client';
2
+ import {
3
+ PageFrame,
4
+ usePageMessages
5
+ } from "./chunk-7QCJC2DV.js";
6
+
7
+ // src/components/PaywallPage/PaywallPage.tsx
8
+ import * as React from "react";
9
+ import { Badge, Button, RadioCard, RadioCardGroup } from "@nexub/react";
10
+ import { Check } from "@nexub/icons";
11
+ import { jsx, jsxs } from "react/jsx-runtime";
12
+ var PaywallPage = React.forwardRef(function PaywallPage2({ title, description, plans, defaultPlan, onSubscribe, onRestore, terms, loading, messages, ...props }, ref) {
13
+ const m = usePageMessages(messages);
14
+ const [plan, setPlan] = React.useState(defaultPlan ?? plans[0]?.id);
15
+ return /* @__PURE__ */ jsxs(PageFrame, { ref, width: "sm", title, description, ...props, children: [
16
+ /* @__PURE__ */ jsx(RadioCardGroup, { value: plan, onValueChange: setPlan, "aria-label": m.choosePlan, columns: 1, children: plans.map((p) => /* @__PURE__ */ jsx(
17
+ RadioCard,
18
+ {
19
+ value: p.id,
20
+ title: /* @__PURE__ */ jsxs("span", { className: "flex flex-wrap items-center gap-2", children: [
21
+ p.name,
22
+ p.badge && /* @__PURE__ */ jsx(Badge, { tone: "info", size: "sm", children: p.badge })
23
+ ] }),
24
+ description: p.price,
25
+ children: /* @__PURE__ */ jsx("ul", { className: "mt-2 flex flex-col gap-1.5", children: p.features.map((f) => /* @__PURE__ */ jsxs("li", { className: "flex items-start gap-2 text-body-sm text-[var(--color-on-surface-variant)]", children: [
26
+ /* @__PURE__ */ jsx(Check, { size: "sm", "aria-hidden": "true", className: "mt-0.5 shrink-0 text-[var(--color-success)]" }),
27
+ f
28
+ ] }, f)) })
29
+ },
30
+ p.id
31
+ )) }),
32
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
33
+ /* @__PURE__ */ jsx(Button, { size: "lg", fullWidth: true, loading, disabled: !plan, onClick: () => plan && onSubscribe(plan), children: m.continue }),
34
+ onRestore && /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "lg", fullWidth: true, onClick: onRestore, children: m.restorePurchases }),
35
+ terms && /* @__PURE__ */ jsx("p", { className: "text-center text-label-sm text-[var(--color-on-surface-muted)]", children: terms })
36
+ ] })
37
+ ] });
38
+ });
39
+
40
+ export {
41
+ PaywallPage
42
+ };
@@ -0,0 +1,87 @@
1
+ 'use client';
2
+ import {
3
+ PageFrame,
4
+ StatusBadge,
5
+ groupByDay,
6
+ usePageMessages,
7
+ useTemplateLocale
8
+ } from "./chunk-7QCJC2DV.js";
9
+
10
+ // src/components/ActivityPage/ActivityPage.tsx
11
+ import * as React from "react";
12
+ import { Amount, Avatar, Button, EmptyState, cn } from "@nexub/react";
13
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
14
+ function TransactionRow({
15
+ t,
16
+ onSelect,
17
+ actions,
18
+ m,
19
+ locale
20
+ }) {
21
+ const body = /* @__PURE__ */ jsxs(Fragment, { children: [
22
+ /* @__PURE__ */ jsx(Avatar, { name: t.title, src: t.image }),
23
+ /* @__PURE__ */ jsxs("span", { className: "flex min-w-0 flex-1 flex-col text-start", children: [
24
+ /* @__PURE__ */ jsx("span", { className: "truncate text-body-md font-medium text-[var(--color-on-surface)]", children: t.title }),
25
+ t.subtitle && /* @__PURE__ */ jsx("span", { className: "truncate text-body-sm text-[var(--color-on-surface-muted)]", children: t.subtitle })
26
+ ] }),
27
+ /* @__PURE__ */ jsxs("span", { className: "flex shrink-0 flex-col items-end gap-1", children: [
28
+ /* @__PURE__ */ jsx(Amount, { value: t.amount, currency: t.currency, locale, tone: "auto", signDisplay: "exceptZero" }),
29
+ /* @__PURE__ */ jsx(StatusBadge, { status: t.status, m })
30
+ ] })
31
+ ] });
32
+ const row = "flex w-full items-center gap-3 px-4 py-3";
33
+ return /* @__PURE__ */ jsxs("li", { className: "flex items-center border-t border-[var(--color-outline-variant)] first:border-t-0", children: [
34
+ onSelect ? /* @__PURE__ */ jsx("button", { type: "button", onClick: () => onSelect(t), className: cn(row, "nx-state-layer min-h-[56px]"), children: body }) : /* @__PURE__ */ jsx("div", { className: row, children: body }),
35
+ actions && /* @__PURE__ */ jsx("div", { className: "shrink-0 pe-2", children: actions })
36
+ ] });
37
+ }
38
+ var ActivityPage = React.forwardRef(function ActivityPage2({
39
+ transactions,
40
+ onSelect,
41
+ onRefresh,
42
+ refreshing,
43
+ onLoadMore,
44
+ hasMore,
45
+ loadingMore,
46
+ rowActions,
47
+ toolbar,
48
+ empty,
49
+ title,
50
+ actions,
51
+ messages,
52
+ ...props
53
+ }, ref) {
54
+ const m = usePageMessages(messages);
55
+ const locale = useTemplateLocale();
56
+ const sections = React.useMemo(() => groupByDay(transactions, locale), [transactions, locale]);
57
+ return /* @__PURE__ */ jsxs(
58
+ PageFrame,
59
+ {
60
+ ref,
61
+ title: title ?? m.activity,
62
+ actions: (actions || onRefresh) && /* @__PURE__ */ jsxs(Fragment, { children: [
63
+ actions,
64
+ onRefresh && /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onClick: onRefresh, loading: refreshing, children: m.refresh })
65
+ ] }),
66
+ ...props,
67
+ children: [
68
+ toolbar,
69
+ sections.length === 0 ? empty ?? /* @__PURE__ */ jsxs(EmptyState.Root, { children: [
70
+ /* @__PURE__ */ jsx(EmptyState.Title, { children: m.noActivityTitle }),
71
+ /* @__PURE__ */ jsx(EmptyState.Description, { children: m.noActivityDescription })
72
+ ] }) : /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-6", "aria-busy": refreshing || void 0, children: [
73
+ sections.map((s) => /* @__PURE__ */ jsxs("section", { "aria-label": s.title, className: "flex flex-col gap-2", children: [
74
+ /* @__PURE__ */ jsx("h2", { className: "px-1 text-title-sm text-[var(--color-on-surface-variant)]", children: s.title }),
75
+ /* @__PURE__ */ jsx("ul", { className: "overflow-hidden rounded-shape-container border border-[var(--color-outline-variant)] bg-[var(--color-surface-elevated)]", children: s.data.map((t) => /* @__PURE__ */ jsx(TransactionRow, { t, onSelect, actions: rowActions?.(t), m, locale }, t.id)) })
76
+ ] }, s.title)),
77
+ onLoadMore && hasMore && /* @__PURE__ */ jsx(Button, { variant: "secondary", className: "self-center", onClick: onLoadMore, loading: loadingMore, children: m.loadMore })
78
+ ] })
79
+ ]
80
+ }
81
+ );
82
+ });
83
+
84
+ export {
85
+ TransactionRow,
86
+ ActivityPage
87
+ };
@@ -0,0 +1 @@
1
+ 'use client';