@blocklet/payment-react 1.14.29 → 1.14.31

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.
Files changed (43) hide show
  1. package/es/checkout/table.js +3 -2
  2. package/es/components/pricing-table.js +2 -1
  3. package/es/components/truncated-text.d.ts +15 -0
  4. package/es/components/truncated-text.js +27 -0
  5. package/es/history/invoice/list.js +14 -4
  6. package/es/index.d.ts +2 -1
  7. package/es/index.js +3 -1
  8. package/es/libs/util.d.ts +2 -0
  9. package/es/libs/util.js +33 -0
  10. package/es/locales/en.js +3 -1
  11. package/es/locales/zh.js +3 -1
  12. package/es/payment/form/index.js +89 -60
  13. package/es/payment/index.js +81 -15
  14. package/es/payment/summary.js +2 -18
  15. package/es/theme/index.js +5 -0
  16. package/lib/checkout/table.js +8 -2
  17. package/lib/components/pricing-table.js +6 -1
  18. package/lib/components/truncated-text.d.ts +15 -0
  19. package/lib/components/truncated-text.js +55 -0
  20. package/lib/history/invoice/list.js +8 -3
  21. package/lib/index.d.ts +2 -1
  22. package/lib/index.js +8 -0
  23. package/lib/libs/util.d.ts +2 -0
  24. package/lib/libs/util.js +35 -0
  25. package/lib/locales/en.js +3 -1
  26. package/lib/locales/zh.js +3 -1
  27. package/lib/payment/form/index.js +37 -11
  28. package/lib/payment/index.js +59 -4
  29. package/lib/payment/summary.js +2 -6
  30. package/lib/theme/index.js +5 -0
  31. package/package.json +8 -8
  32. package/src/checkout/table.tsx +3 -2
  33. package/src/components/pricing-table.tsx +2 -1
  34. package/src/components/truncated-text.tsx +41 -0
  35. package/src/history/invoice/list.tsx +9 -4
  36. package/src/index.ts +2 -0
  37. package/src/libs/util.ts +41 -0
  38. package/src/locales/en.tsx +3 -0
  39. package/src/locales/zh.tsx +2 -0
  40. package/src/payment/form/index.tsx +43 -15
  41. package/src/payment/index.tsx +70 -5
  42. package/src/payment/summary.tsx +2 -8
  43. package/src/theme/index.tsx +5 -0
@@ -11,6 +11,7 @@ import api from "../libs/api.js";
11
11
  import { mergeExtraParams } from "../libs/util.js";
12
12
  import CheckoutForm from "./form.js";
13
13
  import { PaymentThemeProvider } from "../theme/index.js";
14
+ import TruncatedText from "../components/truncated-text.js";
14
15
  const fetchData = async (id) => {
15
16
  const { data } = await api.get(`/api/pricing-tables/${id}`);
16
17
  return data;
@@ -85,7 +86,7 @@ function CheckoutTableInner({ id, mode, onPaid, onError, onChange, extraParams,
85
86
  textAlign: "center"
86
87
  },
87
88
  children: [
88
- !data.livemode && /* @__PURE__ */ jsx(Livemode, { sx: { display: "flex", marginBottom: "8px", width: "fit-content" } }),
89
+ !data.livemode && /* @__PURE__ */ jsx(Livemode, { sx: { display: "flex", marginBottom: "8px", width: "fit-content", ml: 0 } }),
89
90
  /* @__PURE__ */ jsx(
90
91
  Typography,
91
92
  {
@@ -95,7 +96,7 @@ function CheckoutTableInner({ id, mode, onPaid, onError, onChange, extraParams,
95
96
  lineHeight: "32px",
96
97
  fontSize: "24px"
97
98
  },
98
- children: data.name
99
+ children: /* @__PURE__ */ jsx(TruncatedText, { text: data.name, maxLength: 60, useWidth: true })
99
100
  }
100
101
  )
101
102
  ]
@@ -32,6 +32,7 @@ import {
32
32
  } from "../libs/util.js";
33
33
  import Amount from "../payment/amount.js";
34
34
  import { useMobile } from "../hooks/mobile.js";
35
+ import TruncatedText from "./truncated-text.js";
35
36
  const sortOrder = {
36
37
  year: 1,
37
38
  month: 2,
@@ -294,7 +295,7 @@ export default function PricingTable({ table, alignItems, interval, mode, onSele
294
295
  fontSize: "18px !important",
295
296
  fontWeight: "600"
296
297
  },
297
- children: x.product.name
298
+ children: /* @__PURE__ */ jsx(TruncatedText, { text: x.product.name, maxLength: 26, useWidth: true })
298
299
  }
299
300
  ),
300
301
  x.is_highlight && /* @__PURE__ */ jsx(
@@ -0,0 +1,15 @@
1
+ /// <reference types="react" />
2
+ interface TruncatedTextProps {
3
+ text?: string;
4
+ maxLength?: number;
5
+ useWidth?: boolean;
6
+ }
7
+ declare function TruncatedText({ text, maxLength, useWidth }: TruncatedTextProps): import("react").JSX.Element | null;
8
+ declare namespace TruncatedText {
9
+ var defaultProps: {
10
+ useWidth: boolean;
11
+ text: string;
12
+ maxLength: number;
13
+ };
14
+ }
15
+ export default TruncatedText;
@@ -0,0 +1,27 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { Tooltip, tooltipClasses } from "@mui/material";
3
+ import { styled } from "@mui/system";
4
+ import { truncateText } from "../libs/util.js";
5
+ const CustomTooltip = styled(({ className, ...props }) => /* @__PURE__ */ jsx(Tooltip, { ...props, classes: { popper: className } }))({
6
+ [`& .${tooltipClasses.tooltip}`]: {
7
+ fontSize: 11,
8
+ maxHeight: 120,
9
+ maxWidth: 500,
10
+ overflowY: "auto"
11
+ }
12
+ });
13
+ export default function TruncatedText({ text = "", maxLength = 100, useWidth = false }) {
14
+ if (!text) {
15
+ return null;
16
+ }
17
+ const truncatedText = truncateText(text, maxLength, useWidth);
18
+ if (!truncatedText.endsWith("...")) {
19
+ return /* @__PURE__ */ jsx("span", { children: truncatedText });
20
+ }
21
+ return /* @__PURE__ */ jsx(CustomTooltip, { title: text, placement: "bottom", enterTouchDelay: 0, children: /* @__PURE__ */ jsx("span", { title: text, children: truncatedText }) });
22
+ }
23
+ TruncatedText.defaultProps = {
24
+ useWidth: false,
25
+ text: "",
26
+ maxLength: 100
27
+ };
@@ -96,7 +96,7 @@ const InvoiceTable = React.memo((props) => {
96
96
  {
97
97
  label: t("common.amount"),
98
98
  name: "total",
99
- width: 60,
99
+ width: 80,
100
100
  align: "right",
101
101
  options: {
102
102
  customBodyRenderLite: (_, index) => {
@@ -146,9 +146,8 @@ const InvoiceTable = React.memo((props) => {
146
146
  },
147
147
  {
148
148
  label: t("common.status"),
149
- name: "created_at",
149
+ name: "status",
150
150
  options: {
151
- sort: true,
152
151
  customBodyRenderLite: (val, index) => {
153
152
  const invoice = data?.list[index];
154
153
  const link = getInvoiceLink(invoice, action);
@@ -300,7 +299,17 @@ const InvoiceList = React.memo((props) => {
300
299
  ] }) }),
301
300
  /* @__PURE__ */ jsx(Box, { flex: 1, textAlign: "right", children: /* @__PURE__ */ jsx(Typography, { children: formatToDate(invoice.created_at, locale, "HH:mm:ss") }) }),
302
301
  !action && /* @__PURE__ */ jsx(Hidden, { mdDown: true, children: /* @__PURE__ */ jsx(Box, { flex: 2, className: "invoice-description", textAlign: "right", children: /* @__PURE__ */ jsx(Typography, { children: invoice.description || invoice.id }) }) }),
303
- /* @__PURE__ */ jsx(Box, { flex: 1, textAlign: "right", children: action ? link.connect ? /* @__PURE__ */ jsx(Button, { variant: "contained", color: "primary", size: "small", onClick: () => onPay(invoice.id), children: t("payment.customer.invoice.pay") }) : /* @__PURE__ */ jsx(
302
+ /* @__PURE__ */ jsx(Box, { flex: 1, textAlign: "right", children: action ? link.connect ? /* @__PURE__ */ jsx(
303
+ Button,
304
+ {
305
+ variant: "contained",
306
+ color: "primary",
307
+ size: "small",
308
+ onClick: () => onPay(invoice.id),
309
+ sx: { whiteSpace: "nowrap" },
310
+ children: t("payment.customer.invoice.pay")
311
+ }
312
+ ) : /* @__PURE__ */ jsx(
304
313
  Button,
305
314
  {
306
315
  component: "a",
@@ -308,6 +317,7 @@ const InvoiceList = React.memo((props) => {
308
317
  size: "small",
309
318
  href: link.url,
310
319
  target: link.external ? "_blank" : target,
320
+ sx: { whiteSpace: "nowrap" },
311
321
  rel: "noreferrer",
312
322
  children: t("payment.customer.invoice.pay")
313
323
  }
package/es/index.d.ts CHANGED
@@ -25,6 +25,7 @@ import ProductSkeleton from './payment/product-skeleton';
25
25
  import PaymentSummary from './payment/summary';
26
26
  import PricingItem from './components/pricing-item';
27
27
  import CountrySelect from './components/country-select';
28
+ import TruncatedText from './components/truncated-text';
28
29
  export { PaymentThemeProvider } from './theme';
29
30
  export * from './libs/util';
30
31
  export * from './libs/connect';
@@ -33,4 +34,4 @@ export * from './hooks/subscription';
33
34
  export * from './hooks/mobile';
34
35
  export * from './hooks/table';
35
36
  export { translations, createTranslator } from './locales';
36
- export { api, dayjs, FormInput, PhoneInput, AddressForm, StripeForm, Status, Livemode, Switch, ConfirmDialog, CheckoutForm, CheckoutTable, CheckoutDonate, CurrencySelector, Payment, PaymentSummary, PricingTable, ProductSkeleton, Amount, CustomerInvoiceList, CustomerPaymentList, TxLink, TxGas, SafeGuard, PricingItem, CountrySelect, Table, };
37
+ export { api, dayjs, FormInput, PhoneInput, AddressForm, StripeForm, Status, Livemode, Switch, ConfirmDialog, CheckoutForm, CheckoutTable, CheckoutDonate, CurrencySelector, Payment, PaymentSummary, PricingTable, ProductSkeleton, Amount, CustomerInvoiceList, CustomerPaymentList, TxLink, TxGas, SafeGuard, PricingItem, CountrySelect, Table, TruncatedText, };
package/es/index.js CHANGED
@@ -25,6 +25,7 @@ import ProductSkeleton from "./payment/product-skeleton.js";
25
25
  import PaymentSummary from "./payment/summary.js";
26
26
  import PricingItem from "./components/pricing-item.js";
27
27
  import CountrySelect from "./components/country-select.js";
28
+ import TruncatedText from "./components/truncated-text.js";
28
29
  export { PaymentThemeProvider } from "./theme/index.js";
29
30
  export * from "./libs/util.js";
30
31
  export * from "./libs/connect.js";
@@ -60,5 +61,6 @@ export {
60
61
  SafeGuard,
61
62
  PricingItem,
62
63
  CountrySelect,
63
- Table
64
+ Table,
65
+ TruncatedText
64
66
  };
package/es/libs/util.d.ts CHANGED
@@ -105,3 +105,5 @@ export declare function formatQuantityInventory(price: TPrice, quantity: string
105
105
  export declare function formatSubscriptionStatus(status: string): string;
106
106
  export declare function formatAmountPrecisionLimit(amount: string, locale?: string, precision?: number): string;
107
107
  export declare function getWordBreakStyle(value: any): 'break-word' | 'break-all';
108
+ export declare function isMobileSafari(): boolean;
109
+ export declare function truncateText(text: string, maxLength: number, useWidth?: boolean): string;
package/es/libs/util.js CHANGED
@@ -2,6 +2,7 @@ import { BN, fromUnitToToken } from "@ocap/util";
2
2
  import omit from "lodash/omit";
3
3
  import trimEnd from "lodash/trimEnd";
4
4
  import numbro from "numbro";
5
+ import stringWidth from "string-width";
5
6
  import { defaultCountries } from "react-international-phone";
6
7
  import { joinURL } from "ufo";
7
8
  import { t } from "../locales/index.js";
@@ -805,3 +806,35 @@ export function getWordBreakStyle(value) {
805
806
  }
806
807
  return "break-all";
807
808
  }
809
+ export function isMobileSafari() {
810
+ const ua = navigator.userAgent.toLowerCase();
811
+ const isSafari = ua.indexOf("safari") > -1 && ua.indexOf("chrome") === -1;
812
+ const isMobile = ua.indexOf("mobile") > -1 || /iphone|ipad|ipod/.test(ua);
813
+ const isIOS = /iphone|ipad|ipod/.test(ua);
814
+ return isSafari && isMobile && isIOS;
815
+ }
816
+ export function truncateText(text, maxLength, useWidth = false) {
817
+ if (!text || !maxLength) {
818
+ return text;
819
+ }
820
+ if (!useWidth) {
821
+ if (text.length <= maxLength) {
822
+ return text;
823
+ }
824
+ return `${text.substring(0, maxLength)}...`;
825
+ }
826
+ let width = 0;
827
+ let truncated = "";
828
+ for (let i = 0; i < text.length; i++) {
829
+ const charWidth = stringWidth(text.charAt(i));
830
+ if (width + charWidth > maxLength) {
831
+ break;
832
+ }
833
+ truncated += text.charAt(i);
834
+ width += charWidth;
835
+ }
836
+ if (truncated === text) {
837
+ return truncated;
838
+ }
839
+ return `${truncated}...`;
840
+ }
package/es/locales/en.js CHANGED
@@ -202,6 +202,7 @@ export default flat({
202
202
  title: "Cancel your subscription",
203
203
  comment: "Any additional feedback?",
204
204
  description: "Your subscription will be canceled, but it is still available until the end of your current billing period on {date}",
205
+ trialDescription: "Free trial subscriptions will be canceled immediately and no longer available, confirm to continue",
205
206
  feedback: {
206
207
  tip: "We would love your feedback, it will help us improve our service",
207
208
  too_expensive: "The service is too expensive",
@@ -303,7 +304,8 @@ export default flat({
303
304
  threshold: "Metered usage billing",
304
305
  cancel: "Subscription cancel",
305
306
  manual: "Manual invoice",
306
- upcoming: "Upcoming invoice"
307
+ upcoming: "Upcoming invoice",
308
+ slashStake: "Slash stake"
307
309
  }
308
310
  }
309
311
  },
package/es/locales/zh.js CHANGED
@@ -202,6 +202,7 @@ export default flat({
202
202
  title: "\u53D6\u6D88\u60A8\u7684\u8BA2\u9605",
203
203
  comment: "\u4F60\u8FD8\u6709\u5176\u4ED6\u53CD\u9988\u4E48\uFF1F",
204
204
  description: "\u60A8\u7684\u8BA2\u9605\u5C06\u88AB\u53D6\u6D88\uFF0C\u4F46\u4ECD\u7136\u53EF\u7528\u76F4\u5230\u60A8\u5F53\u524D\u8BA1\u8D39\u5468\u671F\u7ED3\u675F\u4E8E{date}",
205
+ trialDescription: "\u514D\u8D39\u8BD5\u7528\u7684\u8BA2\u9605\u5C06\u88AB\u7ACB\u5373\u53D6\u6D88\uFF0C\u4E0D\u518D\u53EF\u7528\uFF0C\u786E\u8BA4\u662F\u5426\u7EE7\u7EED",
205
206
  feedback: {
206
207
  tip: "\u6211\u4EEC\u5E0C\u671B\u542C\u5230\u60A8\u7684\u53CD\u9988\uFF0C\u8FD9\u5C06\u5E2E\u52A9\u6211\u4EEC\u6539\u8FDB\u6211\u4EEC\u7684\u670D\u52A1",
207
208
  too_expensive: "\u670D\u52A1\u8D39\u7528\u592A\u9AD8",
@@ -303,7 +304,8 @@ export default flat({
303
304
  threshold: "\u7528\u91CF\u8D26\u5355",
304
305
  cancel: "\u8BA2\u9605\u53D6\u6D88",
305
306
  manual: "\u4EBA\u5DE5\u8D26\u5355",
306
- upcoming: "\u672A\u6765\u8D26\u5355"
307
+ upcoming: "\u672A\u6765\u8D26\u5355",
308
+ slashStake: "\u7F5A\u6CA1\u8D28\u62BC"
307
309
  }
308
310
  }
309
311
  },
@@ -3,15 +3,16 @@ import "react-international-phone/style.css";
3
3
  import { useLocaleContext } from "@arcblock/ux/lib/Locale/context";
4
4
  import Toast from "@arcblock/ux/lib/Toast";
5
5
  import { LoadingButton } from "@mui/lab";
6
- import { Divider, Fade, FormLabel, Stack, Typography } from "@mui/material";
6
+ import { Box, Divider, Fade, FormLabel, Stack, Typography } from "@mui/material";
7
7
  import { useMemoizedFn, useSetState } from "ahooks";
8
8
  import { PhoneNumberUtil } from "google-libphonenumber";
9
9
  import pWaitFor from "p-wait-for";
10
- import { useEffect, useMemo, useState } from "react";
10
+ import { useEffect, useMemo, useRef, useState } from "react";
11
11
  import { Controller, useFormContext, useWatch } from "react-hook-form";
12
12
  import { joinURL } from "ufo";
13
13
  import { dispatch } from "use-bus";
14
14
  import isEmail from "validator/es/lib/isEmail";
15
+ import { isEmpty } from "lodash";
15
16
  import ConfirmDialog from "../../components/confirm.js";
16
17
  import FormInput from "../../components/input.js";
17
18
  import { usePaymentContext } from "../../contexts/payment.js";
@@ -29,6 +30,7 @@ import AddressForm from "./address.js";
29
30
  import CurrencySelector from "./currency.js";
30
31
  import PhoneInput from "./phone.js";
31
32
  import StripeCheckout from "./stripe.js";
33
+ import { useMobile } from "../../hooks/mobile.js";
32
34
  const phoneUtil = PhoneNumberUtil.getInstance();
33
35
  const waitForCheckoutComplete = async (sessionId) => {
34
36
  let result;
@@ -62,9 +64,17 @@ export default function PaymentForm({
62
64
  action
63
65
  }) {
64
66
  const { t } = useLocaleContext();
67
+ const { isMobile } = useMobile();
65
68
  const { session, connect, payable } = usePaymentContext();
66
69
  const subscription = useSubscription("events");
67
- const { control, getValues, setValue, handleSubmit } = useFormContext();
70
+ const {
71
+ control,
72
+ getValues,
73
+ setValue,
74
+ handleSubmit,
75
+ formState: { errors }
76
+ } = useFormContext();
77
+ const errorRef = useRef(null);
68
78
  const quantityInventoryStatus = useMemo(() => {
69
79
  let status = true;
70
80
  for (const item of checkoutSession.line_items) {
@@ -163,6 +173,11 @@ export default function PaymentForm({
163
173
  setState({ paying: false });
164
174
  }
165
175
  };
176
+ useEffect(() => {
177
+ if (errorRef.current && !isEmpty(errors) && isMobile) {
178
+ errorRef.current.scrollIntoView({ behavior: "smooth" });
179
+ }
180
+ }, [errors, isMobile]);
166
181
  const onUserLoggedIn = async () => {
167
182
  const { data: profile } = await api.get("/api/customers/me?fallback=1");
168
183
  if (profile) {
@@ -267,6 +282,9 @@ export default function PaymentForm({
267
282
  setState({ submitting: false });
268
283
  };
269
284
  const onAction = () => {
285
+ if (errorRef.current && !isEmpty(errors) && isMobile) {
286
+ errorRef.current.scrollIntoView({ behavior: "smooth" });
287
+ }
270
288
  if (session?.user) {
271
289
  if (hasDidWallet(session.user)) {
272
290
  handleSubmit(onFormSubmit, onFormError)();
@@ -343,68 +361,79 @@ export default function PaymentForm({
343
361
  )
344
362
  ] }) }),
345
363
  /* @__PURE__ */ jsx(Stack, { direction: "row", sx: { mb: 1 }, alignItems: "center", justifyContent: "space-between" }),
346
- /* @__PURE__ */ jsxs(Stack, { direction: "column", className: "cko-payment-form", spacing: 0, sx: { flex: 1, overflow: "auto" }, children: [
347
- /* @__PURE__ */ jsx(FormLabel, { className: "base-label", children: t("payment.checkout.customer.name") }),
348
- /* @__PURE__ */ jsx(
349
- FormInput,
350
- {
351
- name: "customer_name",
352
- variant: "outlined",
353
- errorPosition: "right",
354
- rules: {
355
- required: t("payment.checkout.required")
356
- }
357
- }
358
- ),
359
- /* @__PURE__ */ jsx(FormLabel, { className: "base-label", children: t("payment.checkout.customer.email") }),
360
- /* @__PURE__ */ jsx(
361
- FormInput,
362
- {
363
- name: "customer_email",
364
- variant: "outlined",
365
- errorPosition: "right",
366
- rules: {
367
- required: t("payment.checkout.required"),
368
- validate: (x) => isEmail(x) ? true : t("payment.checkout.invalid")
369
- }
370
- }
371
- ),
372
- checkoutSession.phone_number_collection?.enabled && /* @__PURE__ */ jsxs(Fragment, { children: [
373
- /* @__PURE__ */ jsx(FormLabel, { className: "base-label", children: t("payment.checkout.customer.phone") }),
374
- /* @__PURE__ */ jsx(
375
- PhoneInput,
376
- {
377
- name: "customer_phone",
378
- variant: "outlined",
379
- errorPosition: "right",
380
- placeholder: "Phone number",
381
- rules: {
382
- required: t("payment.checkout.required"),
383
- validate: (x) => {
384
- try {
385
- const parsed = phoneUtil.parseAndKeepRawInput(x);
386
- return phoneUtil.isValidNumber(parsed) ? true : t("payment.checkout.invalid");
387
- } catch {
388
- return t("payment.checkout.invalid");
364
+ /* @__PURE__ */ jsxs(
365
+ Stack,
366
+ {
367
+ direction: "column",
368
+ className: "cko-payment-form",
369
+ id: "cko-payment-form",
370
+ spacing: 0,
371
+ ref: !isEmpty(errors) ? errorRef : void 0,
372
+ sx: { flex: 1, overflow: "auto" },
373
+ children: [
374
+ /* @__PURE__ */ jsx(FormLabel, { className: "base-label", children: t("payment.checkout.customer.name") }),
375
+ /* @__PURE__ */ jsx(
376
+ FormInput,
377
+ {
378
+ name: "customer_name",
379
+ variant: "outlined",
380
+ errorPosition: "right",
381
+ rules: {
382
+ required: t("payment.checkout.required")
383
+ }
384
+ }
385
+ ),
386
+ /* @__PURE__ */ jsx(FormLabel, { className: "base-label", children: t("payment.checkout.customer.email") }),
387
+ /* @__PURE__ */ jsx(
388
+ FormInput,
389
+ {
390
+ name: "customer_email",
391
+ variant: "outlined",
392
+ errorPosition: "right",
393
+ rules: {
394
+ required: t("payment.checkout.required"),
395
+ validate: (x) => isEmail(x) ? true : t("payment.checkout.invalid")
396
+ }
397
+ }
398
+ ),
399
+ checkoutSession.phone_number_collection?.enabled && /* @__PURE__ */ jsxs(Fragment, { children: [
400
+ /* @__PURE__ */ jsx(FormLabel, { className: "base-label", children: t("payment.checkout.customer.phone") }),
401
+ /* @__PURE__ */ jsx(
402
+ PhoneInput,
403
+ {
404
+ name: "customer_phone",
405
+ variant: "outlined",
406
+ errorPosition: "right",
407
+ placeholder: "Phone number",
408
+ rules: {
409
+ required: t("payment.checkout.required"),
410
+ validate: (x) => {
411
+ try {
412
+ const parsed = phoneUtil.parseAndKeepRawInput(x);
413
+ return phoneUtil.isValidNumber(parsed) ? true : t("payment.checkout.invalid");
414
+ } catch {
415
+ return t("payment.checkout.invalid");
416
+ }
417
+ }
389
418
  }
390
419
  }
420
+ )
421
+ ] }),
422
+ /* @__PURE__ */ jsx(
423
+ AddressForm,
424
+ {
425
+ mode: checkoutSession.billing_address_collection,
426
+ stripe: method?.type === "stripe",
427
+ sx: { marginTop: "0 !important" }
391
428
  }
392
- }
393
- )
394
- ] }),
395
- /* @__PURE__ */ jsx(
396
- AddressForm,
397
- {
398
- mode: checkoutSession.billing_address_collection,
399
- stripe: method?.type === "stripe",
400
- sx: { marginTop: "0 !important" }
401
- }
402
- )
403
- ] })
429
+ )
430
+ ]
431
+ }
432
+ )
404
433
  ] }) }),
405
434
  /* @__PURE__ */ jsx(Divider, { sx: { mt: 2.5, mb: 2.5 } }),
406
435
  /* @__PURE__ */ jsx(Fade, { in: true, children: /* @__PURE__ */ jsxs(Stack, { className: "cko-payment-submit", children: [
407
- /* @__PURE__ */ jsx(
436
+ /* @__PURE__ */ jsx(Box, { className: "cko-payment-submit-btn", children: /* @__PURE__ */ jsx(
408
437
  LoadingButton,
409
438
  {
410
439
  variant: "contained",
@@ -417,7 +446,7 @@ export default function PaymentForm({
417
446
  loading: state.submitting || state.paying,
418
447
  children: state.submitting || state.paying ? t("payment.checkout.processing") : buttonText
419
448
  }
420
- ),
449
+ ) }),
421
450
  ["subscription", "setup"].includes(checkoutSession.mode) && /* @__PURE__ */ jsx(Typography, { sx: { mt: 2.5, color: "text.lighter", fontSize: "0.9rem", lineHeight: "1.1rem" }, children: t("payment.checkout.confirm", { payee }) })
422
451
  ] }) }),
423
452
  state.customerLimited && /* @__PURE__ */ jsx(
@@ -11,7 +11,7 @@ import { useEffect, useState } from "react";
11
11
  import { FormProvider, useForm, useWatch } from "react-hook-form";
12
12
  import { usePaymentContext } from "../contexts/payment.js";
13
13
  import api from "../libs/api.js";
14
- import { findCurrency, formatError, getStatementDescriptor, isValidCountry } from "../libs/util.js";
14
+ import { findCurrency, formatError, getStatementDescriptor, isMobileSafari, isValidCountry } from "../libs/util.js";
15
15
  import PaymentError from "./error.js";
16
16
  import CheckoutFooter from "./footer.js";
17
17
  import PaymentForm from "./form/index.js";
@@ -66,6 +66,25 @@ function PaymentInner({
66
66
  )
67
67
  }
68
68
  });
69
+ useEffect(() => {
70
+ if (!isMobileSafari()) {
71
+ return () => {
72
+ };
73
+ }
74
+ let scrollTop = 0;
75
+ const focusinHandler = () => {
76
+ scrollTop = window.scrollY;
77
+ };
78
+ const focusoutHandler = () => {
79
+ window.scrollTo(0, scrollTop);
80
+ };
81
+ document.body.addEventListener("focusin", focusinHandler);
82
+ document.body.addEventListener("focusout", focusoutHandler);
83
+ return () => {
84
+ document.body.removeEventListener("focusin", focusinHandler);
85
+ document.body.removeEventListener("focusout", focusoutHandler);
86
+ };
87
+ }, []);
69
88
  const currencyId = useWatch({ control: methods.control, name: "payment_currency", defaultValue: defaultCurrencyId });
70
89
  const currency = findCurrency(paymentMethods, currencyId) || settings.baseCurrency;
71
90
  const method = paymentMethods.find((x) => x.id === currency.payment_method_id);
@@ -221,7 +240,9 @@ export default function Payment({
221
240
  const { t } = useLocaleContext();
222
241
  const { refresh, livemode, setLivemode } = usePaymentContext();
223
242
  const [delay, setDelay] = useState(false);
243
+ const { isMobile } = useMobile();
224
244
  const hideSummaryCard = mode.endsWith("-minimal") || !showCheckoutSummary;
245
+ const isMobileSafariEnv = isMobileSafari();
225
246
  useEffect(() => {
226
247
  setTimeout(() => {
227
248
  setDelay(true);
@@ -291,7 +312,10 @@ export default function Payment({
291
312
  {
292
313
  display: "flex",
293
314
  flexDirection: "column",
294
- sx: { height: mode === "standalone" ? "100vh" : "auto", overflow: "hidden" },
315
+ sx: {
316
+ height: mode === "standalone" ? "100vh" : "auto",
317
+ overflow: isMobileSafariEnv ? "visible" : "hidden"
318
+ },
295
319
  children: [
296
320
  mode === "standalone" ? /* @__PURE__ */ jsx(
297
321
  Header,
@@ -306,17 +330,58 @@ export default function Payment({
306
330
  sx: { borderBottom: "1px solid var(--stroke-border-base, #EFF1F5)" }
307
331
  }
308
332
  ) : null,
309
- /* @__PURE__ */ jsxs(Root, { mode, sx: { flex: 1 }, children: [
310
- goBack && /* @__PURE__ */ jsx(
311
- ArrowBackOutlined,
312
- {
313
- sx: { mr: 0.5, color: "text.secondary", alignSelf: "flex-start", margin: "16px 0", cursor: "pointer" },
314
- onClick: goBack,
315
- fontSize: "medium"
316
- }
317
- ),
318
- /* @__PURE__ */ jsx(Stack, { className: "cko-container", sx: { gap: { sm: mode === "standalone" ? 0 : mode === "inline" ? 4 : 8 } }, children: renderContent() })
319
- ] })
333
+ /* @__PURE__ */ jsxs(
334
+ Root,
335
+ {
336
+ mode,
337
+ sx: {
338
+ flex: 1,
339
+ overflow: {
340
+ xs: isMobileSafariEnv ? "visible" : "auto",
341
+ md: "hidden"
342
+ },
343
+ ...isMobile && mode === "standalone" ? {
344
+ ".cko-payment-submit-btn": {
345
+ position: "fixed",
346
+ bottom: 20,
347
+ left: 0,
348
+ right: 0,
349
+ zIndex: 999,
350
+ background: "#fff",
351
+ padding: "10px",
352
+ textAlign: "center",
353
+ button: {
354
+ color: "#fff",
355
+ maxWidth: 542
356
+ }
357
+ },
358
+ ".cko-footer": {
359
+ position: "fixed",
360
+ bottom: 0,
361
+ left: 0,
362
+ right: 0,
363
+ zIndex: 999,
364
+ background: "#fff",
365
+ marginBottom: 0
366
+ },
367
+ ".cko-payment": {
368
+ paddingBottom: "100px"
369
+ }
370
+ } : {}
371
+ },
372
+ children: [
373
+ goBack && /* @__PURE__ */ jsx(
374
+ ArrowBackOutlined,
375
+ {
376
+ sx: { mr: 0.5, color: "text.secondary", alignSelf: "flex-start", margin: "16px 0", cursor: "pointer" },
377
+ onClick: goBack,
378
+ fontSize: "medium"
379
+ }
380
+ ),
381
+ /* @__PURE__ */ jsx(Stack, { className: "cko-container", sx: { gap: { sm: mode === "standalone" ? 0 : mode === "inline" ? 4 : 8 } }, children: renderContent() })
382
+ ]
383
+ }
384
+ )
320
385
  ]
321
386
  }
322
387
  );
@@ -461,7 +526,6 @@ export const Root = styled(Box)`
461
526
  }
462
527
 
463
528
  @media (max-width: ${({ theme }) => theme.breakpoints.values.md}px) {
464
- background: ${(props) => props.mode === "standalone" ? "var(--backgrounds-bg-subtle, #F9FAFB)" : "transparent"};
465
529
  padding-top: 0;
466
530
  overflow: auto;
467
531
  &:before {
@@ -470,7 +534,8 @@ export const Root = styled(Box)`
470
534
  .cko-container {
471
535
  flex-direction: column;
472
536
  align-items: center;
473
- gap: 32px;
537
+ justify-content: flex-start;
538
+ gap: 0;
474
539
  overflow: visible;
475
540
  min-width: 200px;
476
541
  }
@@ -483,6 +548,7 @@ export const Root = styled(Box)`
483
548
  width: 100%;
484
549
  height: fit-content;
485
550
  flex: none;
551
+ border-top: 1px solid var(--stroke-border-base, #eff1f5);
486
552
  &:before {
487
553
  display: none;
488
554
  }
@@ -277,30 +277,14 @@ export default function PaymentSummary({
277
277
  /* @__PURE__ */ jsxs(Stack, { direction: "row", justifyContent: "space-between", alignItems: "center", spacing: 1, children: [
278
278
  /* @__PURE__ */ jsxs(Stack, { direction: "row", alignItems: "center", spacing: 0.5, children: [
279
279
  /* @__PURE__ */ jsx(Typography, { sx: { color: "text.secondary" }, children: t("payment.checkout.paymentRequired") }),
280
- /* @__PURE__ */ jsx(
281
- Tooltip,
282
- {
283
- title: /* @__PURE__ */ jsx(Typography, { children: t("payment.checkout.stakingConfirm") }),
284
- placement: "top",
285
- sx: { maxWidth: "150px" },
286
- children: /* @__PURE__ */ jsx(HelpOutline, { fontSize: "small", sx: { color: "text.lighter" } })
287
- }
288
- )
280
+ /* @__PURE__ */ jsx(Tooltip, { title: t("payment.checkout.stakingConfirm"), placement: "top", sx: { maxWidth: "150px" }, children: /* @__PURE__ */ jsx(HelpOutline, { fontSize: "small", sx: { color: "text.lighter" } }) })
289
281
  ] }),
290
282
  /* @__PURE__ */ jsx(Typography, { children: headlines.amount })
291
283
  ] }),
292
284
  /* @__PURE__ */ jsxs(Stack, { direction: "row", justifyContent: "space-between", alignItems: "center", spacing: 1, children: [
293
285
  /* @__PURE__ */ jsxs(Stack, { direction: "row", alignItems: "center", spacing: 0.5, children: [
294
286
  /* @__PURE__ */ jsx(Typography, { sx: { color: "text.secondary" }, children: t("payment.checkout.staking.title") }),
295
- /* @__PURE__ */ jsx(
296
- Tooltip,
297
- {
298
- title: /* @__PURE__ */ jsx(Typography, { children: t("payment.checkout.staking.tooltip") }),
299
- placement: "top",
300
- sx: { maxWidth: "150px" },
301
- children: /* @__PURE__ */ jsx(HelpOutline, { fontSize: "small", sx: { color: "text.lighter" } })
302
- }
303
- )
287
+ /* @__PURE__ */ jsx(Tooltip, { title: t("payment.checkout.staking.tooltip"), placement: "top", sx: { maxWidth: "150px" }, children: /* @__PURE__ */ jsx(HelpOutline, { fontSize: "small", sx: { color: "text.lighter" } }) })
304
288
  ] }),
305
289
  /* @__PURE__ */ jsxs(Typography, { children: [
306
290
  formatAmount(staking, currency.decimal),