@blocklet/payment-react 1.18.25 → 1.18.27
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/es/checkout/donate.js +11 -1
- package/es/components/country-select.js +243 -21
- package/es/components/over-due-invoice-payment.d.ts +3 -1
- package/es/components/over-due-invoice-payment.js +6 -4
- package/es/contexts/payment.d.ts +2 -1
- package/es/contexts/payment.js +8 -1
- package/es/index.d.ts +1 -0
- package/es/index.js +1 -0
- package/es/libs/api.js +4 -0
- package/es/libs/currency.d.ts +3 -0
- package/es/libs/currency.js +22 -0
- package/es/libs/phone-validator.js +2 -0
- package/es/libs/validator.d.ts +1 -0
- package/es/libs/validator.js +70 -0
- package/es/payment/form/address.js +17 -3
- package/es/payment/form/index.js +10 -1
- package/es/payment/form/phone.js +12 -1
- package/es/payment/form/stripe/form.js +14 -5
- package/es/payment/index.js +33 -11
- package/es/payment/product-donation.js +110 -12
- package/es/types/shims.d.ts +2 -0
- package/lib/checkout/donate.js +11 -1
- package/lib/components/country-select.js +243 -39
- package/lib/components/over-due-invoice-payment.d.ts +3 -1
- package/lib/components/over-due-invoice-payment.js +7 -4
- package/lib/contexts/payment.d.ts +2 -1
- package/lib/contexts/payment.js +9 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.js +12 -0
- package/lib/libs/api.js +4 -0
- package/lib/libs/currency.d.ts +3 -0
- package/lib/libs/currency.js +31 -0
- package/lib/libs/phone-validator.js +1 -0
- package/lib/libs/validator.d.ts +1 -0
- package/lib/libs/validator.js +20 -0
- package/lib/payment/form/address.js +15 -2
- package/lib/payment/form/index.js +12 -1
- package/lib/payment/form/phone.js +13 -1
- package/lib/payment/form/stripe/form.js +21 -5
- package/lib/payment/index.js +34 -10
- package/lib/payment/product-donation.js +106 -15
- package/lib/types/shims.d.ts +2 -0
- package/package.json +8 -8
- package/src/checkout/donate.tsx +11 -1
- package/src/components/country-select.tsx +265 -20
- package/src/components/over-due-invoice-payment.tsx +6 -2
- package/src/contexts/payment.tsx +11 -1
- package/src/index.ts +1 -0
- package/src/libs/api.ts +4 -1
- package/src/libs/currency.ts +25 -0
- package/src/libs/phone-validator.ts +1 -0
- package/src/libs/validator.ts +70 -0
- package/src/payment/form/address.tsx +17 -4
- package/src/payment/form/index.tsx +11 -1
- package/src/payment/form/phone.tsx +15 -1
- package/src/payment/form/stripe/form.tsx +20 -9
- package/src/payment/index.tsx +45 -14
- package/src/payment/product-donation.tsx +129 -10
- package/src/types/shims.d.ts +2 -0
package/es/payment/form/phone.js
CHANGED
|
@@ -4,12 +4,14 @@ import omit from "lodash/omit";
|
|
|
4
4
|
import { useEffect, useRef, useCallback } from "react";
|
|
5
5
|
import { useFormContext, useWatch } from "react-hook-form";
|
|
6
6
|
import { defaultCountries, usePhoneInput } from "react-international-phone";
|
|
7
|
+
import { useMount } from "ahooks";
|
|
7
8
|
import FormInput from "../../components/input.js";
|
|
8
9
|
import { isValidCountry } from "../../libs/util.js";
|
|
9
10
|
import CountrySelect from "../../components/country-select.js";
|
|
11
|
+
import { getPhoneUtil } from "../../libs/phone-validator.js";
|
|
10
12
|
export default function PhoneInput({ ...props }) {
|
|
11
13
|
const countryFieldName = props.countryFieldName || "billing_address.country";
|
|
12
|
-
const { control, getValues, setValue } = useFormContext();
|
|
14
|
+
const { control, getValues, setValue, trigger } = useFormContext();
|
|
13
15
|
const values = getValues();
|
|
14
16
|
const isUpdatingRef = useRef(false);
|
|
15
17
|
const safeUpdate = useCallback((callback) => {
|
|
@@ -43,6 +45,11 @@ export default function PhoneInput({ ...props }) {
|
|
|
43
45
|
setCountry(userCountry);
|
|
44
46
|
});
|
|
45
47
|
}, [userCountry, country, setCountry, safeUpdate]);
|
|
48
|
+
useMount(() => {
|
|
49
|
+
getPhoneUtil().catch((err) => {
|
|
50
|
+
console.error("Failed to preload phone validator:", err);
|
|
51
|
+
});
|
|
52
|
+
});
|
|
46
53
|
const onCountryChange = useCallback(
|
|
47
54
|
(v) => {
|
|
48
55
|
safeUpdate(() => {
|
|
@@ -51,6 +58,9 @@ export default function PhoneInput({ ...props }) {
|
|
|
51
58
|
},
|
|
52
59
|
[setCountry, safeUpdate]
|
|
53
60
|
);
|
|
61
|
+
const handleBlur = useCallback(() => {
|
|
62
|
+
trigger(props.name);
|
|
63
|
+
}, [props.name]);
|
|
54
64
|
return (
|
|
55
65
|
// @ts-ignore
|
|
56
66
|
/* @__PURE__ */ jsx(
|
|
@@ -60,6 +70,7 @@ export default function PhoneInput({ ...props }) {
|
|
|
60
70
|
onChange: handlePhoneValueChange,
|
|
61
71
|
type: "tel",
|
|
62
72
|
inputRef,
|
|
73
|
+
onBlur: handleBlur,
|
|
63
74
|
InputProps: {
|
|
64
75
|
startAdornment: /* @__PURE__ */ jsx(InputAdornment, { position: "start", style: { marginRight: "2px", marginLeft: "-8px" }, children: /* @__PURE__ */ jsx(
|
|
65
76
|
CountrySelect,
|
|
@@ -32,7 +32,8 @@ function StripeCheckoutForm({
|
|
|
32
32
|
confirming: false,
|
|
33
33
|
loaded: false,
|
|
34
34
|
showBillingForm: false,
|
|
35
|
-
isTransitioning: false
|
|
35
|
+
isTransitioning: false,
|
|
36
|
+
paymentMethod: "card"
|
|
36
37
|
});
|
|
37
38
|
const handlePaymentMethodChange = (event) => {
|
|
38
39
|
const method = event.value?.type;
|
|
@@ -43,12 +44,14 @@ function StripeCheckoutForm({
|
|
|
43
44
|
setTimeout(() => {
|
|
44
45
|
setState({
|
|
45
46
|
isTransitioning: false,
|
|
47
|
+
paymentMethod: method,
|
|
46
48
|
showBillingForm: true
|
|
47
49
|
});
|
|
48
50
|
}, 300);
|
|
49
51
|
} else {
|
|
50
52
|
setState({
|
|
51
53
|
showBillingForm: false,
|
|
54
|
+
paymentMethod: method,
|
|
52
55
|
isTransitioning: false
|
|
53
56
|
});
|
|
54
57
|
}
|
|
@@ -86,13 +89,14 @@ function StripeCheckoutForm({
|
|
|
86
89
|
return;
|
|
87
90
|
}
|
|
88
91
|
try {
|
|
89
|
-
setState({ confirming: true });
|
|
92
|
+
setState({ confirming: true, message: "" });
|
|
90
93
|
const method = intentType === "payment_intent" ? "confirmPayment" : "confirmSetup";
|
|
91
94
|
const { error: submitError } = await elements.submit();
|
|
92
95
|
if (submitError) {
|
|
96
|
+
setState({ confirming: false });
|
|
93
97
|
return;
|
|
94
98
|
}
|
|
95
|
-
const { error } = await stripe[method]({
|
|
99
|
+
const { error, paymentIntent, setupIntent } = await stripe[method]({
|
|
96
100
|
elements,
|
|
97
101
|
redirect: "if_required",
|
|
98
102
|
confirmParams: {
|
|
@@ -117,6 +121,11 @@ function StripeCheckoutForm({
|
|
|
117
121
|
} : {}
|
|
118
122
|
}
|
|
119
123
|
});
|
|
124
|
+
const intent = paymentIntent || setupIntent;
|
|
125
|
+
if (intent?.status === "canceled" || intent?.status === "requires_payment_method") {
|
|
126
|
+
setState({ confirming: false });
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
120
129
|
setState({ confirming: false });
|
|
121
130
|
if (error) {
|
|
122
131
|
if (error.type === "validation_error") {
|
|
@@ -131,11 +140,11 @@ function StripeCheckoutForm({
|
|
|
131
140
|
setState({ confirming: false, message: err.message });
|
|
132
141
|
}
|
|
133
142
|
},
|
|
134
|
-
[customer, intentType, stripe]
|
|
143
|
+
[customer, intentType, stripe, state.showBillingForm, returnUrl]
|
|
135
144
|
// eslint-disable-line
|
|
136
145
|
);
|
|
137
146
|
return /* @__PURE__ */ jsxs(Content, { onSubmit: handleSubmit, children: [
|
|
138
|
-
/* @__PURE__ */ jsx(
|
|
147
|
+
(!state.paymentMethod || state.paymentMethod === "card") && /* @__PURE__ */ jsx(
|
|
139
148
|
LinkAuthenticationElement,
|
|
140
149
|
{
|
|
141
150
|
options: {
|
package/es/payment/index.js
CHANGED
|
@@ -7,7 +7,7 @@ import { Box, Fade, Stack } from "@mui/material";
|
|
|
7
7
|
import { styled } from "@mui/system";
|
|
8
8
|
import { fromTokenToUnit } from "@ocap/util";
|
|
9
9
|
import { useSetState } from "ahooks";
|
|
10
|
-
import { useEffect, useState } from "react";
|
|
10
|
+
import { useEffect, useState, useMemo } from "react";
|
|
11
11
|
import { FormProvider, useForm, useWatch } from "react-hook-form";
|
|
12
12
|
import trim from "lodash/trim";
|
|
13
13
|
import { usePaymentContext } from "../contexts/payment.js";
|
|
@@ -22,13 +22,14 @@ import {
|
|
|
22
22
|
} from "../libs/util.js";
|
|
23
23
|
import PaymentError from "./error.js";
|
|
24
24
|
import CheckoutFooter from "./footer.js";
|
|
25
|
-
import PaymentForm from "./form/index.js";
|
|
25
|
+
import PaymentForm, { hasDidWallet } from "./form/index.js";
|
|
26
26
|
import OverviewSkeleton from "./skeleton/overview.js";
|
|
27
27
|
import PaymentSkeleton from "./skeleton/payment.js";
|
|
28
28
|
import PaymentSuccess from "./success.js";
|
|
29
29
|
import PaymentSummary from "./summary.js";
|
|
30
30
|
import { useMobile } from "../hooks/mobile.js";
|
|
31
31
|
import { formatPhone } from "../libs/phone-validator.js";
|
|
32
|
+
import { getCurrencyPreference } from "../libs/currency.js";
|
|
32
33
|
PaymentInner.defaultProps = {
|
|
33
34
|
completed: false,
|
|
34
35
|
showCheckoutSummary: true
|
|
@@ -52,7 +53,36 @@ function PaymentInner({
|
|
|
52
53
|
const { isMobile } = useMobile();
|
|
53
54
|
const [state, setState] = useSetState({ checkoutSession });
|
|
54
55
|
const query = getQueryParams(window.location.href);
|
|
55
|
-
const
|
|
56
|
+
const availableCurrencyIds = useMemo(() => {
|
|
57
|
+
const currencyIds = /* @__PURE__ */ new Set();
|
|
58
|
+
paymentMethods.forEach((method2) => {
|
|
59
|
+
method2.payment_currencies.forEach((currency2) => {
|
|
60
|
+
if (currency2.active) {
|
|
61
|
+
currencyIds.add(currency2.id);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
return Array.from(currencyIds);
|
|
66
|
+
}, [paymentMethods]);
|
|
67
|
+
const defaultCurrencyId = useMemo(() => {
|
|
68
|
+
if (query.currencyId && availableCurrencyIds.includes(query.currencyId)) {
|
|
69
|
+
return query.currencyId;
|
|
70
|
+
}
|
|
71
|
+
if (session?.user && !hasDidWallet(session.user)) {
|
|
72
|
+
const stripeCurrencyId = paymentMethods.find((m) => m.type === "stripe")?.payment_currencies.find((c) => c.active)?.id;
|
|
73
|
+
if (stripeCurrencyId) {
|
|
74
|
+
return stripeCurrencyId;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
const savedPreference = getCurrencyPreference(session?.user?.did, availableCurrencyIds);
|
|
78
|
+
if (savedPreference) {
|
|
79
|
+
return savedPreference;
|
|
80
|
+
}
|
|
81
|
+
if (state.checkoutSession.currency_id && availableCurrencyIds.includes(state.checkoutSession.currency_id)) {
|
|
82
|
+
return state.checkoutSession.currency_id;
|
|
83
|
+
}
|
|
84
|
+
return availableCurrencyIds?.[0];
|
|
85
|
+
}, [query.currencyId, availableCurrencyIds, session?.user, state.checkoutSession.currency_id, paymentMethods]);
|
|
56
86
|
const defaultMethodId = paymentMethods.find((m) => m.payment_currencies.some((c) => c.id === defaultCurrencyId))?.id;
|
|
57
87
|
const hideSummaryCard = mode.endsWith("-minimal") || !showCheckoutSummary;
|
|
58
88
|
const methods = useForm({
|
|
@@ -97,14 +127,6 @@ function PaymentInner({
|
|
|
97
127
|
document.body.removeEventListener("focusout", focusoutHandler);
|
|
98
128
|
};
|
|
99
129
|
}, []);
|
|
100
|
-
useEffect(() => {
|
|
101
|
-
if (!methods || query.currencyId) {
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
if (state.checkoutSession.currency_id !== defaultCurrencyId) {
|
|
105
|
-
methods.setValue("payment_currency", state.checkoutSession.currency_id);
|
|
106
|
-
}
|
|
107
|
-
}, [state.checkoutSession, defaultCurrencyId, query.currencyId]);
|
|
108
130
|
const currencyId = useWatch({ control: methods.control, name: "payment_currency", defaultValue: defaultCurrencyId });
|
|
109
131
|
const currency = findCurrency(paymentMethods, currencyId) || settings.baseCurrency;
|
|
110
132
|
const method = paymentMethods.find((x) => x.id === currency.payment_method_id);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useLocaleContext } from "@arcblock/ux/lib/Locale/context";
|
|
3
|
-
import { Avatar, Box, Card, CardActionArea, Grid, Stack, TextField, Typography } from "@mui/material";
|
|
3
|
+
import { Avatar, Box, Card, CardActionArea, Grid, Stack, TextField, Typography, IconButton } from "@mui/material";
|
|
4
|
+
import AutoAwesomeIcon from "@mui/icons-material/AutoAwesome";
|
|
4
5
|
import { useSetState } from "ahooks";
|
|
5
6
|
import { useEffect, useRef } from "react";
|
|
6
7
|
import { formatAmountPrecisionLimit } from "../libs/util.js";
|
|
@@ -62,7 +63,8 @@ export default function ProductDonation({
|
|
|
62
63
|
selected: defaultPreset === "custom" ? "" : defaultPreset,
|
|
63
64
|
input: defaultCustomAmount,
|
|
64
65
|
custom: !supportPreset || defaultPreset === "custom",
|
|
65
|
-
error: ""
|
|
66
|
+
error: "",
|
|
67
|
+
animating: false
|
|
66
68
|
});
|
|
67
69
|
const customInputRef = useRef(null);
|
|
68
70
|
const containerRef = useRef(null);
|
|
@@ -73,15 +75,73 @@ export default function ProductDonation({
|
|
|
73
75
|
localStorage.setItem(getUserStorageKey(DONATION_PRESET_KEY_BASE), formatAmount(amount));
|
|
74
76
|
};
|
|
75
77
|
const handleCustomSelect = () => {
|
|
76
|
-
setState({
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
78
|
+
setState({
|
|
79
|
+
custom: true,
|
|
80
|
+
selected: "",
|
|
81
|
+
animating: true
|
|
82
|
+
});
|
|
83
|
+
const hasPresets = presets.length > 0;
|
|
84
|
+
let sortedPresets = [];
|
|
85
|
+
if (hasPresets) {
|
|
86
|
+
sortedPresets = [...presets].map((p) => parseFloat(p)).sort((a, b) => a - b);
|
|
87
|
+
}
|
|
88
|
+
const minPreset = hasPresets ? sortedPresets[0] : 1;
|
|
89
|
+
const middleIndex = Math.floor(sortedPresets.length / 2);
|
|
90
|
+
const maxPreset = hasPresets ? sortedPresets[middleIndex] : 10;
|
|
91
|
+
const detectPrecision = () => {
|
|
92
|
+
let maxPrecision = 2;
|
|
93
|
+
if (!hasPresets)
|
|
94
|
+
return 0;
|
|
95
|
+
const allIntegers = presets.every((preset) => {
|
|
96
|
+
const num = parseFloat(preset);
|
|
97
|
+
return num === Math.floor(num);
|
|
98
|
+
});
|
|
99
|
+
if (allIntegers)
|
|
100
|
+
return 0;
|
|
101
|
+
presets.forEach((preset) => {
|
|
102
|
+
const decimalPart = preset.toString().split(".")[1];
|
|
103
|
+
if (decimalPart) {
|
|
104
|
+
maxPrecision = Math.max(maxPrecision, decimalPart.length);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
return maxPrecision;
|
|
108
|
+
};
|
|
109
|
+
const precision = detectPrecision();
|
|
110
|
+
let randomAmount;
|
|
111
|
+
if (precision === 0) {
|
|
112
|
+
randomAmount = (Math.round(Math.random() * (maxPreset - minPreset) + minPreset) || 1).toString();
|
|
113
|
+
} else {
|
|
114
|
+
randomAmount = (Math.random() * (maxPreset - minPreset) + minPreset).toFixed(precision);
|
|
84
115
|
}
|
|
116
|
+
const startValue = state.input ? parseFloat(state.input) : 0;
|
|
117
|
+
const targetValue = parseFloat(randomAmount);
|
|
118
|
+
const difference = targetValue - startValue;
|
|
119
|
+
const startTime = Date.now();
|
|
120
|
+
const duration = 800;
|
|
121
|
+
const updateCounter = () => {
|
|
122
|
+
const currentTime = Date.now();
|
|
123
|
+
const elapsed = currentTime - startTime;
|
|
124
|
+
if (elapsed < duration) {
|
|
125
|
+
const progress = elapsed / duration;
|
|
126
|
+
const intermediateValue = startValue + difference * progress;
|
|
127
|
+
const currentValue = precision === 0 ? Math.floor(intermediateValue).toString() : intermediateValue.toFixed(precision);
|
|
128
|
+
setState({ input: currentValue });
|
|
129
|
+
requestAnimationFrame(updateCounter);
|
|
130
|
+
} else {
|
|
131
|
+
setState({
|
|
132
|
+
input: randomAmount,
|
|
133
|
+
animating: false,
|
|
134
|
+
error: ""
|
|
135
|
+
});
|
|
136
|
+
onChange({ priceId: item.price_id, amount: formatAmount(randomAmount) });
|
|
137
|
+
setPayable(true);
|
|
138
|
+
localStorage.setItem(getUserStorageKey(DONATION_CUSTOM_AMOUNT_KEY_BASE), formatAmount(randomAmount));
|
|
139
|
+
setTimeout(() => {
|
|
140
|
+
customInputRef.current?.focus();
|
|
141
|
+
}, 200);
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
requestAnimationFrame(updateCounter);
|
|
85
145
|
localStorage.setItem(getUserStorageKey(DONATION_PRESET_KEY_BASE), "custom");
|
|
86
146
|
};
|
|
87
147
|
const handleTabSelect = (selectedItem) => {
|
|
@@ -261,13 +321,51 @@ export default function ProductDonation({
|
|
|
261
321
|
inputRef: customInputRef,
|
|
262
322
|
InputProps: {
|
|
263
323
|
endAdornment: /* @__PURE__ */ jsxs(Stack, { direction: "row", spacing: 0.5, alignItems: "center", sx: { ml: 1 }, children: [
|
|
324
|
+
/* @__PURE__ */ jsx(
|
|
325
|
+
IconButton,
|
|
326
|
+
{
|
|
327
|
+
size: "small",
|
|
328
|
+
onClick: handleCustomSelect,
|
|
329
|
+
disabled: state.animating,
|
|
330
|
+
sx: {
|
|
331
|
+
mr: 0.5,
|
|
332
|
+
opacity: state.animating ? 0.5 : 1,
|
|
333
|
+
transition: "all 0.2s ease",
|
|
334
|
+
"&:hover": {
|
|
335
|
+
transform: "scale(1.2)",
|
|
336
|
+
transition: "transform 0.3s ease"
|
|
337
|
+
}
|
|
338
|
+
},
|
|
339
|
+
"aria-label": t("common.random"),
|
|
340
|
+
children: /* @__PURE__ */ jsx(AutoAwesomeIcon, { fontSize: "small" })
|
|
341
|
+
}
|
|
342
|
+
),
|
|
264
343
|
/* @__PURE__ */ jsx(Avatar, { src: currency?.logo, sx: { width: 16, height: 16 }, alt: currency?.symbol }),
|
|
265
344
|
/* @__PURE__ */ jsx(Typography, { children: currency?.symbol })
|
|
266
345
|
] }),
|
|
267
|
-
autoComplete: "off"
|
|
346
|
+
autoComplete: "off",
|
|
347
|
+
sx: {
|
|
348
|
+
"& input": {
|
|
349
|
+
transition: "all 0.25s ease"
|
|
350
|
+
}
|
|
351
|
+
}
|
|
268
352
|
},
|
|
269
353
|
sx: {
|
|
270
|
-
mt: defaultPreset !== "0" ? 0 : 1
|
|
354
|
+
mt: defaultPreset !== "0" ? 0 : 1,
|
|
355
|
+
"& .MuiInputBase-root": {
|
|
356
|
+
transition: "all 0.3s ease"
|
|
357
|
+
},
|
|
358
|
+
"& input[type=number]": {
|
|
359
|
+
MozAppearance: "textfield"
|
|
360
|
+
},
|
|
361
|
+
"& input[type=number]::-webkit-outer-spin-button": {
|
|
362
|
+
WebkitAppearance: "none",
|
|
363
|
+
margin: 0
|
|
364
|
+
},
|
|
365
|
+
"& input[type=number]::-webkit-inner-spin-button": {
|
|
366
|
+
WebkitAppearance: "none",
|
|
367
|
+
margin: 0
|
|
368
|
+
}
|
|
271
369
|
}
|
|
272
370
|
}
|
|
273
371
|
)
|
package/es/types/shims.d.ts
CHANGED
package/lib/checkout/donate.js
CHANGED
|
@@ -63,6 +63,7 @@ function DonateDetails({
|
|
|
63
63
|
locale
|
|
64
64
|
} = (0, _context.useLocaleContext)();
|
|
65
65
|
return /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Stack, {
|
|
66
|
+
className: "cko-donate-details",
|
|
66
67
|
sx: {
|
|
67
68
|
width: "100%",
|
|
68
69
|
minWidth: "256px",
|
|
@@ -227,8 +228,17 @@ function SupporterAvatar({
|
|
|
227
228
|
onClose: () => setOpen(false),
|
|
228
229
|
sx: {
|
|
229
230
|
".MuiDialogContent-root": {
|
|
230
|
-
width:
|
|
231
|
+
width: {
|
|
232
|
+
xs: "100%",
|
|
233
|
+
md: "450px"
|
|
234
|
+
},
|
|
231
235
|
padding: "8px"
|
|
236
|
+
},
|
|
237
|
+
".cko-donate-details": {
|
|
238
|
+
maxHeight: {
|
|
239
|
+
xs: "100%",
|
|
240
|
+
md: "300px"
|
|
241
|
+
}
|
|
232
242
|
}
|
|
233
243
|
},
|
|
234
244
|
title: `${customersNum} supporter${customersNum > 1 ? "s" : ""}`,
|