@pollar/react 0.10.1-rc.2 → 0.10.1-rc.4

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/index.d.ts CHANGED
@@ -309,6 +309,13 @@ interface KycModalTemplateProps {
309
309
  declare function KycModalTemplate({ theme, accentColor, step, providers, selectedProvider, session, kycStatus, isLoading, onSelectProvider, onDoneVerifying, onClose, }: KycModalTemplateProps): react_jsx_runtime.JSX.Element;
310
310
 
311
311
  type RampStep = 'input' | 'loading_quote' | 'select_route' | 'contact' | 'status' | 'error';
312
+ /** A field a provider declares (via the quote) that the client must collect. */
313
+ interface RampFieldSpec {
314
+ key: string;
315
+ label: string;
316
+ type: 'text' | 'email' | 'tel';
317
+ bankType?: 'CLABE' | 'PIX' | 'PSE' | 'ACH';
318
+ }
312
319
  interface RampWidgetTemplateProps {
313
320
  theme: string;
314
321
  accentColor: string;
@@ -317,8 +324,8 @@ interface RampWidgetTemplateProps {
317
324
  amount: string;
318
325
  currency: string;
319
326
  country: string;
320
- email: string;
321
- fullName: string;
327
+ requiredFields: RampFieldSpec[];
328
+ fieldValues: Record<string, string>;
322
329
  countries: RampCountry[];
323
330
  countriesLoading: boolean;
324
331
  refreshing: boolean;
@@ -334,8 +341,7 @@ interface RampWidgetTemplateProps {
334
341
  errorMsg: string | null;
335
342
  onDirectionChange: (d: RampDirection) => void;
336
343
  onAmountChange: (v: string) => void;
337
- onEmailChange: (v: string) => void;
338
- onFullNameChange: (v: string) => void;
344
+ onFieldChange: (key: string, value: string) => void;
339
345
  onCountryChange: (v: string) => void;
340
346
  onFindRoute: () => void;
341
347
  onSelectQuote: (q: RampQuote) => void;
@@ -346,7 +352,7 @@ interface RampWidgetTemplateProps {
346
352
  onRefresh: () => void;
347
353
  onClose: () => void;
348
354
  }
349
- declare function RampWidgetTemplate({ theme, accentColor, step, direction, amount, currency, country, email, fullName, countries, countriesLoading, refreshing, quotes, isLoading, provider, txStatus, kycUrl, stellarTxHash, depositInstructions, canComplete, completing, errorMsg, onDirectionChange, onAmountChange, onEmailChange, onFullNameChange, onCountryChange, onFindRoute, onSelectQuote, onContactContinue, onOpenKyc, onCompleteWithdraw, onRetry, onRefresh, onClose, }: RampWidgetTemplateProps): react_jsx_runtime.JSX.Element;
355
+ declare function RampWidgetTemplate({ theme, accentColor, step, direction, amount, currency, country, requiredFields, fieldValues, countries, countriesLoading, refreshing, quotes, isLoading, provider, txStatus, kycUrl, stellarTxHash, depositInstructions, canComplete, completing, errorMsg, onDirectionChange, onAmountChange, onFieldChange, onCountryChange, onFindRoute, onSelectQuote, onContactContinue, onOpenKyc, onCompleteWithdraw, onRetry, onRefresh, onClose, }: RampWidgetTemplateProps): react_jsx_runtime.JSX.Element;
350
356
 
351
357
  interface TransactionModalTemplateProps {
352
358
  theme: string;
package/dist/index.js CHANGED
@@ -1035,11 +1035,62 @@ var PollarModalFooter = () => {
1035
1035
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-footer-name", children: "Pollar" }),
1036
1036
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "pollar-footer-version", children: [
1037
1037
  "v",
1038
- "0.10.1-rc.2"
1038
+ "0.10.1-rc.4"
1039
1039
  ] })
1040
1040
  ] })
1041
1041
  ] });
1042
1042
  };
1043
+ function CopyButton({ value, label = "Copy", className }) {
1044
+ const [copied, setCopied] = react.useState(false);
1045
+ const timerRef = react.useRef(null);
1046
+ react.useEffect(() => {
1047
+ return () => {
1048
+ if (timerRef.current !== null) clearTimeout(timerRef.current);
1049
+ };
1050
+ }, []);
1051
+ function handleCopy(e) {
1052
+ e.stopPropagation();
1053
+ if (!value || !navigator.clipboard) return;
1054
+ void navigator.clipboard.writeText(value).then(() => {
1055
+ setCopied(true);
1056
+ if (timerRef.current !== null) clearTimeout(timerRef.current);
1057
+ timerRef.current = setTimeout(() => {
1058
+ setCopied(false);
1059
+ timerRef.current = null;
1060
+ }, 1500);
1061
+ });
1062
+ }
1063
+ return /* @__PURE__ */ jsxRuntime.jsx(
1064
+ "button",
1065
+ {
1066
+ type: "button",
1067
+ className: `pollar-copy-btn${className ? ` ${className}` : ""}`,
1068
+ onClick: handleCopy,
1069
+ "aria-label": copied ? "Copied" : label,
1070
+ children: copied ? /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "13", height: "13", viewBox: "0 0 14 14", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsxRuntime.jsx(
1071
+ "path",
1072
+ {
1073
+ d: "M3.5 7l2.5 2.5 4.5-5",
1074
+ stroke: "currentColor",
1075
+ strokeWidth: "1.5",
1076
+ strokeLinecap: "round",
1077
+ strokeLinejoin: "round"
1078
+ }
1079
+ ) }) : /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "13", height: "13", viewBox: "0 0 13 13", fill: "none", "aria-hidden": true, children: [
1080
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "4", y: "4", width: "8", height: "8", rx: "1.5", stroke: "currentColor", strokeWidth: "1.5" }),
1081
+ /* @__PURE__ */ jsxRuntime.jsx(
1082
+ "path",
1083
+ {
1084
+ d: "M3 9H2a1 1 0 01-1-1V2a1 1 0 011-1h6a1 1 0 011 1v1",
1085
+ stroke: "currentColor",
1086
+ strokeWidth: "1.5",
1087
+ strokeLinecap: "round"
1088
+ }
1089
+ )
1090
+ ] })
1091
+ }
1092
+ );
1093
+ }
1043
1094
  function ModalStatusBanner({ message, status, onCancel, onRetry }) {
1044
1095
  if (!message && status === "NONE") {
1045
1096
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-status" });
@@ -2506,8 +2557,8 @@ function RampWidgetTemplate({
2506
2557
  amount,
2507
2558
  currency,
2508
2559
  country,
2509
- email,
2510
- fullName,
2560
+ requiredFields,
2561
+ fieldValues,
2511
2562
  countries,
2512
2563
  countriesLoading,
2513
2564
  refreshing,
@@ -2523,8 +2574,7 @@ function RampWidgetTemplate({
2523
2574
  errorMsg,
2524
2575
  onDirectionChange,
2525
2576
  onAmountChange,
2526
- onEmailChange,
2527
- onFullNameChange,
2577
+ onFieldChange,
2528
2578
  onCountryChange,
2529
2579
  onFindRoute,
2530
2580
  onSelectQuote,
@@ -2675,33 +2725,19 @@ function RampWidgetTemplate({
2675
2725
  /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "pollar-btn-secondary", onClick: onClose, children: "Cancel" })
2676
2726
  ] }),
2677
2727
  step === "contact" && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2678
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-ramp-field", children: [
2679
- /* @__PURE__ */ jsxRuntime.jsx("label", { className: "pollar-ramp-label", children: "Full name" }),
2728
+ requiredFields.map((f) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-ramp-field", children: [
2729
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "pollar-ramp-label", children: f.label }),
2680
2730
  /* @__PURE__ */ jsxRuntime.jsx(
2681
2731
  "input",
2682
2732
  {
2683
- type: "text",
2733
+ type: f.type,
2684
2734
  className: "pollar-ramp-input",
2685
- placeholder: "Jane Doe",
2686
- value: fullName,
2687
- onChange: (e) => onFullNameChange(e.target.value)
2688
- }
2689
- )
2690
- ] }),
2691
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-ramp-field", children: [
2692
- /* @__PURE__ */ jsxRuntime.jsx("label", { className: "pollar-ramp-label", children: "Email" }),
2693
- /* @__PURE__ */ jsxRuntime.jsx(
2694
- "input",
2695
- {
2696
- type: "email",
2697
- className: "pollar-ramp-input",
2698
- placeholder: "jane@example.com",
2699
- value: email,
2700
- autoComplete: "email",
2701
- onChange: (e) => onEmailChange(e.target.value)
2735
+ value: fieldValues[f.key] ?? "",
2736
+ autoComplete: f.type === "email" ? "email" : "off",
2737
+ onChange: (e) => onFieldChange(f.key, e.target.value)
2702
2738
  }
2703
2739
  )
2704
- ] }),
2740
+ ] }, f.key)),
2705
2741
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-modal-actions", children: [
2706
2742
  /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "pollar-btn-secondary", onClick: onRetry, children: "Back" }),
2707
2743
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -2709,7 +2745,7 @@ function RampWidgetTemplate({
2709
2745
  {
2710
2746
  type: "button",
2711
2747
  className: "pollar-btn-primary",
2712
- disabled: !fullName.trim() || !email.trim() || isLoading,
2748
+ disabled: requiredFields.some((f) => !(fieldValues[f.key] ?? "").trim()) || isLoading,
2713
2749
  onClick: onContactContinue,
2714
2750
  children: isLoading ? "Starting\u2026" : "Continue"
2715
2751
  }
@@ -2755,6 +2791,9 @@ function RampWidgetTemplate({
2755
2791
  ] });
2756
2792
  }
2757
2793
  var TERMINAL = ["completed", "failed"];
2794
+ function requiredFieldsOf(quote) {
2795
+ return quote.requiredFields ?? [];
2796
+ }
2758
2797
  function RampWidget({ onClose }) {
2759
2798
  const { getClient, signTx, wallet, styles } = usePollar();
2760
2799
  const walletAddress = wallet?.address ?? "";
@@ -2765,8 +2804,8 @@ function RampWidget({ onClose }) {
2765
2804
  const [amount, setAmount] = react.useState("");
2766
2805
  const [currency, setCurrency] = react.useState("");
2767
2806
  const [country, setCountry] = react.useState("");
2768
- const [email, setEmail] = react.useState("");
2769
- const [fullName, setFullName] = react.useState("");
2807
+ const [fieldValues, setFieldValues] = react.useState({});
2808
+ const setFieldValue = (key, value) => setFieldValues((v) => ({ ...v, [key]: value }));
2770
2809
  const [countries, setCountries] = react.useState([]);
2771
2810
  const [countriesLoading, setCountriesLoading] = react.useState(true);
2772
2811
  const [refreshing, setRefreshing] = react.useState(false);
@@ -2913,8 +2952,9 @@ function RampWidget({ onClose }) {
2913
2952
  function handleSelectQuote(quote) {
2914
2953
  setSelectedQuote(quote);
2915
2954
  setErrorMsg(null);
2916
- const requiresContact = quote.requiresContact ?? false;
2917
- if (requiresContact && (!email.trim() || !fullName.trim())) {
2955
+ const fields = requiredFieldsOf(quote);
2956
+ const missing = fields.some((f) => !(fieldValues[f.key] ?? "").trim());
2957
+ if (fields.length > 0 && missing) {
2918
2958
  setStep("contact");
2919
2959
  return;
2920
2960
  }
@@ -2927,15 +2967,14 @@ function RampWidget({ onClose }) {
2927
2967
  setIsLoading(true);
2928
2968
  setErrorMsg(null);
2929
2969
  try {
2930
- const base = {
2931
- quoteId: quote.quoteId,
2932
- amount: Number(amount),
2933
- currency,
2934
- country,
2935
- ...email.trim() ? { email: email.trim() } : {},
2936
- ...fullName.trim() ? { fullName: fullName.trim() } : {},
2937
- ...walletAddress ? { walletAddress } : {}
2938
- };
2970
+ const base = { quoteId: quote.quoteId, amount: Number(amount), currency, country };
2971
+ if (walletAddress) base.walletAddress = walletAddress;
2972
+ for (const f of requiredFieldsOf(quote)) {
2973
+ const val = (fieldValues[f.key] ?? "").trim();
2974
+ if (!val) continue;
2975
+ if (f.bankType) base.bankDetails = { type: f.bankType, value: val };
2976
+ else base[f.key] = val;
2977
+ }
2939
2978
  const result = direction === "onramp" ? await client.createOnRamp(base) : await client.createOffRamp(base);
2940
2979
  await applyResult(result);
2941
2980
  } catch (e) {
@@ -2980,8 +3019,8 @@ function RampWidget({ onClose }) {
2980
3019
  amount,
2981
3020
  currency,
2982
3021
  country,
2983
- email,
2984
- fullName,
3022
+ requiredFields: selectedQuote ? requiredFieldsOf(selectedQuote) : [],
3023
+ fieldValues,
2985
3024
  countries,
2986
3025
  countriesLoading,
2987
3026
  refreshing,
@@ -2997,8 +3036,7 @@ function RampWidget({ onClose }) {
2997
3036
  errorMsg,
2998
3037
  onDirectionChange: setDirection,
2999
3038
  onAmountChange: setAmount,
3000
- onEmailChange: setEmail,
3001
- onFullNameChange: setFullName,
3039
+ onFieldChange: setFieldValue,
3002
3040
  onCountryChange: handleCountryChange,
3003
3041
  onFindRoute: handleFindRoute,
3004
3042
  onSelectQuote: handleSelectQuote,
@@ -4650,6 +4688,7 @@ function TxHistoryModalTemplate({
4650
4688
  txHistory.step === "loaded" && records.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-modal-empty", children: "No transactions yet." }),
4651
4689
  records.map((record) => {
4652
4690
  const explorerUrl = `https://stellar.expert/explorer/${record.network === "testnet" ? "testnet" : "public"}/tx/${record.hash}`;
4691
+ const destination = typeof record.details?.destination === "string" ? record.details.destination : void 0;
4653
4692
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-hist-item", children: [
4654
4693
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-hist-item-summary", children: record.summary }),
4655
4694
  /* @__PURE__ */ jsxRuntime.jsx(StatusBadge, { status: record.status }),
@@ -4668,6 +4707,10 @@ function TxHistoryModalTemplate({
4668
4707
  "\xB7 ",
4669
4708
  formatDate(record.createdAt)
4670
4709
  ] }),
4710
+ destination && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
4711
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "\xB7" }),
4712
+ /* @__PURE__ */ jsxRuntime.jsx(CopyButton, { value: destination, label: "Copy wallet address", className: "pollar-copy-btn-sm" })
4713
+ ] }),
4671
4714
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: "\xB7" }),
4672
4715
  /* @__PURE__ */ jsxRuntime.jsxs(
4673
4716
  "a",
@@ -4756,7 +4799,7 @@ function TxHistoryModal({ onClose }) {
4756
4799
  }
4757
4800
  function formatBalance2(balance) {
4758
4801
  const n = parseFloat(balance);
4759
- return isNaN(n) ? balance : n.toLocaleString(void 0, { maximumFractionDigits: 7 });
4802
+ return isNaN(n) ? balance : n.toLocaleString(void 0, { minimumFractionDigits: 7, maximumFractionDigits: 7 });
4760
4803
  }
4761
4804
  function cropAddress2(address) {
4762
4805
  if (address.length <= 16) return address;
@@ -4765,7 +4808,13 @@ function cropAddress2(address) {
4765
4808
  function BalanceItem({ record }) {
4766
4809
  const balanceDiffers = record.balance !== record.available;
4767
4810
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-bal-item", children: [
4768
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-bal-asset", children: record.code }),
4811
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-bal-asset-info", children: [
4812
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-bal-asset", children: record.code }),
4813
+ record.issuer && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "pollar-bal-issuer", children: [
4814
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-bal-issuer-addr", children: cropAddress2(record.issuer) }),
4815
+ /* @__PURE__ */ jsxRuntime.jsx(CopyButton, { value: record.issuer, label: "Copy issuer address", className: "pollar-copy-btn-sm" })
4816
+ ] })
4817
+ ] }),
4769
4818
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-bal-amounts", children: [
4770
4819
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-bal-amount", children: formatBalance2(record.balance) }),
4771
4820
  balanceDiffers && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "pollar-bal-available", children: [
@@ -4828,7 +4877,10 @@ function WalletBalanceModalTemplate({
4828
4877
  /* @__PURE__ */ jsxRuntime.jsx("button", { className: "pollar-modal-close", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M2 2l12 12M14 2L2 14", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) }) })
4829
4878
  ] })
4830
4879
  ] }),
4831
- walletAddress && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-bal-address", children: cropAddress2(walletAddress) }),
4880
+ walletAddress && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-bal-address-row", children: [
4881
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-bal-address", children: cropAddress2(walletAddress) }),
4882
+ /* @__PURE__ */ jsxRuntime.jsx(CopyButton, { value: walletAddress, label: "Copy wallet address" })
4883
+ ] }),
4832
4884
  isLoading && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-modal-empty", children: "Loading\u2026" }),
4833
4885
  walletBalance.step === "error" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-modal-error", children: walletBalance.message }),
4834
4886
  data && !data.exists && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-modal-empty", children: [