@blocklet/payment-react 1.18.18 → 1.18.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -13
- package/es/components/livemode.js +2 -2
- package/es/hooks/keyboard.d.ts +44 -0
- package/es/hooks/keyboard.js +86 -0
- package/es/index.d.ts +1 -0
- package/es/index.js +1 -0
- package/es/locales/en.js +1 -1
- package/es/payment/form/index.js +11 -0
- package/es/payment/product-donation.js +223 -115
- package/lib/components/livemode.js +2 -2
- package/lib/hooks/keyboard.d.ts +44 -0
- package/lib/hooks/keyboard.js +85 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +12 -0
- package/lib/locales/en.js +1 -1
- package/lib/payment/form/index.js +11 -0
- package/lib/payment/product-donation.js +148 -40
- package/package.json +6 -6
- package/src/components/livemode.tsx +2 -2
- package/src/hooks/keyboard.ts +159 -0
- package/src/index.ts +1 -0
- package/src/locales/en.tsx +1 -1
- package/src/payment/form/index.tsx +20 -0
- package/src/payment/product-donation.tsx +180 -72
package/README.md
CHANGED
|
@@ -323,18 +323,20 @@ function DonationPage() {
|
|
|
323
323
|
|
|
324
324
|
- `OverdueInvoicePayment` component
|
|
325
325
|
- Display overdue invoices for a subscription, and support batch payment
|
|
326
|
-
-
|
|
327
|
-
- `subscriptionId`: [
|
|
328
|
-
- `
|
|
329
|
-
- `
|
|
330
|
-
- `
|
|
331
|
-
- `
|
|
332
|
-
|
|
333
|
-
- `
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
326
|
+
- Props:
|
|
327
|
+
- `subscriptionId`: [Optional] The subscription ID
|
|
328
|
+
- `customerId`: [Optional] The customer ID or DID
|
|
329
|
+
- `onPaid`: [Optional] Callback function called after successful payment, receives `(id, currencyId, type)`
|
|
330
|
+
- `mode`: [Optional] Component mode, `default` or `custom` (default is `default`)
|
|
331
|
+
- `dialogProps`: [Optional] Dialog properties, default is `{ open: true }`
|
|
332
|
+
- `detailLinkOptions`: [Optional] Detail link options, format: `{ enabled, onClick, title }`
|
|
333
|
+
- `successToast`: [Optional] Whether to show success toast, default is `true`
|
|
334
|
+
- `children`: [Optional] Custom rendering function, used only when `mode="custom"`
|
|
335
|
+
|
|
336
|
+
- Custom Mode:
|
|
337
|
+
- `children` function receives two parameters:
|
|
338
|
+
- `handlePay`: Function to start the payment process
|
|
339
|
+
- `data`: Payment data (includes `subscription`, `summary`, `invoices`, `subscriptionCount`, `detailUrl`)
|
|
338
340
|
```tsx
|
|
339
341
|
import {
|
|
340
342
|
PaymentProvider,
|
|
@@ -346,7 +348,7 @@ import {
|
|
|
346
348
|
function SubscriptionPage({ subscriptionId }) {
|
|
347
349
|
return (
|
|
348
350
|
<PaymentProvider session={session}>
|
|
349
|
-
{/* Handle overdue payments */}
|
|
351
|
+
{/* Handle subscription overdue payments */}
|
|
350
352
|
<OverdueInvoicePayment
|
|
351
353
|
subscriptionId={subscriptionId}
|
|
352
354
|
onPaid={() => {
|
|
@@ -354,6 +356,14 @@ function SubscriptionPage({ subscriptionId }) {
|
|
|
354
356
|
refetchSubscription();
|
|
355
357
|
}}
|
|
356
358
|
/>
|
|
359
|
+
{/* Handle customer overdue payment */}
|
|
360
|
+
<OverdueInvoicePayment
|
|
361
|
+
customerId={session.user.did}
|
|
362
|
+
onPaid={() => {
|
|
363
|
+
// Refresh customer status
|
|
364
|
+
refetch();
|
|
365
|
+
}}
|
|
366
|
+
/>
|
|
357
367
|
|
|
358
368
|
{/* Custom Overdue Invoice Payment */}
|
|
359
369
|
<OverdueInvoicePayment
|
|
@@ -11,8 +11,8 @@ export default function Livemode({ color, backgroundColor, sx }) {
|
|
|
11
11
|
color: "warning",
|
|
12
12
|
sx: {
|
|
13
13
|
ml: 2,
|
|
14
|
-
height:
|
|
15
|
-
lineHeight:
|
|
14
|
+
height: "18px",
|
|
15
|
+
lineHeight: "18px",
|
|
16
16
|
textTransform: "uppercase",
|
|
17
17
|
fontSize: "0.7rem",
|
|
18
18
|
fontWeight: "bold",
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { KeyboardEvent, RefObject } from 'react';
|
|
2
|
+
type TabNavigationOptions<T> = {
|
|
3
|
+
/** whether to include custom option as the last item */
|
|
4
|
+
includeCustom?: boolean;
|
|
5
|
+
/** the value or index of the current selected item */
|
|
6
|
+
currentValue?: T | number;
|
|
7
|
+
/** whether the current selected item is custom */
|
|
8
|
+
isCustomSelected?: boolean;
|
|
9
|
+
/** a function to compare values, used to determine the current selected item */
|
|
10
|
+
compareValue?: (item: T, value: any) => boolean;
|
|
11
|
+
/** whether to allow Tab key navigation */
|
|
12
|
+
enabled?: boolean;
|
|
13
|
+
/** a selector to find navigable elements */
|
|
14
|
+
selector?: string;
|
|
15
|
+
/** an element container reference, limiting the query DOM range to improve performance */
|
|
16
|
+
containerRef?: RefObject<HTMLElement>;
|
|
17
|
+
/** the type of the current value, can be 'index' or 'value' */
|
|
18
|
+
valueType?: 'index' | 'value';
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Tab key navigation hook - implement Tab key circular navigation between a set of options
|
|
22
|
+
*
|
|
23
|
+
* @param items an array of options, can be a simple type (string, number) array or an object array
|
|
24
|
+
* @param onSelect callback when an item is selected
|
|
25
|
+
* @param options configuration options
|
|
26
|
+
* @returns an object containing the event handler and control functions
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* // simple string array
|
|
30
|
+
* const { handleKeyDown } = useTabNavigation(['10', '20', '50'], handleSelect);
|
|
31
|
+
*
|
|
32
|
+
* // object array
|
|
33
|
+
* const { handleKeyDown } = useTabNavigation(
|
|
34
|
+
* [{id: 1, name: 'A'}, {id: 2, name: 'B'}],
|
|
35
|
+
* handleSelect,
|
|
36
|
+
* { compareValue: (item, value) => item.id === value.id }
|
|
37
|
+
* );
|
|
38
|
+
*/
|
|
39
|
+
export declare const useTabNavigation: <T>(items: T[], onSelect: (item: T | "custom", index: number) => void, options?: TabNavigationOptions<T>) => {
|
|
40
|
+
handleKeyDown: (e: KeyboardEvent) => void;
|
|
41
|
+
resetTabNavigation: () => void;
|
|
42
|
+
isTabNavigationActive: boolean;
|
|
43
|
+
};
|
|
44
|
+
export {};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { useCallback, useRef } from "react";
|
|
2
|
+
export const useTabNavigation = (items, onSelect, options) => {
|
|
3
|
+
const {
|
|
4
|
+
valueType = "value",
|
|
5
|
+
includeCustom = false,
|
|
6
|
+
currentValue,
|
|
7
|
+
isCustomSelected = false,
|
|
8
|
+
compareValue = (item, value) => item === value,
|
|
9
|
+
enabled = true,
|
|
10
|
+
selector = ".tab-navigable-card button",
|
|
11
|
+
containerRef
|
|
12
|
+
} = options || {};
|
|
13
|
+
const hasTabbed = useRef(false);
|
|
14
|
+
const findNavigableElements = useCallback(() => {
|
|
15
|
+
if (containerRef?.current) {
|
|
16
|
+
return containerRef.current.querySelectorAll(selector);
|
|
17
|
+
}
|
|
18
|
+
return document.querySelectorAll(selector);
|
|
19
|
+
}, [containerRef, selector]);
|
|
20
|
+
const determineCurrentIndex = useCallback(() => {
|
|
21
|
+
const allOptions = includeCustom ? [...items, "custom"] : items;
|
|
22
|
+
if (allOptions.length === 0)
|
|
23
|
+
return -1;
|
|
24
|
+
if (!hasTabbed.current) {
|
|
25
|
+
if (isCustomSelected && includeCustom) {
|
|
26
|
+
return items.length;
|
|
27
|
+
}
|
|
28
|
+
if (currentValue !== void 0) {
|
|
29
|
+
if (valueType === "index" && typeof currentValue === "number") {
|
|
30
|
+
return currentValue >= 0 && currentValue < items.length ? currentValue : -1;
|
|
31
|
+
}
|
|
32
|
+
return items.findIndex((item) => compareValue(item, currentValue));
|
|
33
|
+
}
|
|
34
|
+
} else {
|
|
35
|
+
const focusedElement = document.activeElement;
|
|
36
|
+
const navigableElements = findNavigableElements();
|
|
37
|
+
for (let i = 0; i < navigableElements.length; i++) {
|
|
38
|
+
if (navigableElements[i] === focusedElement) {
|
|
39
|
+
return i;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return -1;
|
|
44
|
+
}, [items, includeCustom, isCustomSelected, currentValue, valueType, compareValue, findNavigableElements]);
|
|
45
|
+
const getNextIndex = useCallback(
|
|
46
|
+
(currentIndex, isShiftKey) => {
|
|
47
|
+
const totalOptions = includeCustom ? items.length + 1 : items.length;
|
|
48
|
+
if (currentIndex === -1) {
|
|
49
|
+
return 0;
|
|
50
|
+
}
|
|
51
|
+
if (isShiftKey) {
|
|
52
|
+
return currentIndex === 0 ? totalOptions - 1 : currentIndex - 1;
|
|
53
|
+
}
|
|
54
|
+
return currentIndex === totalOptions - 1 ? 0 : currentIndex + 1;
|
|
55
|
+
},
|
|
56
|
+
[items, includeCustom]
|
|
57
|
+
);
|
|
58
|
+
const handleKeyDown = useCallback(
|
|
59
|
+
(e) => {
|
|
60
|
+
if (!enabled || e.key !== "Tab")
|
|
61
|
+
return;
|
|
62
|
+
e.preventDefault();
|
|
63
|
+
e.stopPropagation();
|
|
64
|
+
const currentIndex = determineCurrentIndex();
|
|
65
|
+
const nextIndex = getNextIndex(currentIndex, e.shiftKey);
|
|
66
|
+
hasTabbed.current = true;
|
|
67
|
+
const selectedItem = nextIndex === items.length ? "custom" : items[nextIndex];
|
|
68
|
+
onSelect(selectedItem, nextIndex);
|
|
69
|
+
setTimeout(() => {
|
|
70
|
+
const elements = findNavigableElements();
|
|
71
|
+
if (elements[nextIndex]) {
|
|
72
|
+
elements[nextIndex].focus();
|
|
73
|
+
}
|
|
74
|
+
}, 0);
|
|
75
|
+
},
|
|
76
|
+
[items, onSelect, enabled, determineCurrentIndex, getNextIndex, findNavigableElements]
|
|
77
|
+
);
|
|
78
|
+
const resetTabNavigation = useCallback(() => {
|
|
79
|
+
hasTabbed.current = false;
|
|
80
|
+
}, []);
|
|
81
|
+
return {
|
|
82
|
+
handleKeyDown,
|
|
83
|
+
resetTabNavigation,
|
|
84
|
+
isTabNavigationActive: hasTabbed.current
|
|
85
|
+
};
|
|
86
|
+
};
|
package/es/index.d.ts
CHANGED
|
@@ -42,5 +42,6 @@ export * from './hooks/subscription';
|
|
|
42
42
|
export * from './hooks/mobile';
|
|
43
43
|
export * from './hooks/table';
|
|
44
44
|
export * from './hooks/scroll';
|
|
45
|
+
export * from './hooks/keyboard';
|
|
45
46
|
export { translations, createTranslator } from './locales';
|
|
46
47
|
export { createLazyComponent, 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, Link, OverdueInvoicePayment, PaymentBeneficiaries, LoadingButton, DonateDetails, };
|
package/es/index.js
CHANGED
|
@@ -42,6 +42,7 @@ export * from "./hooks/subscription.js";
|
|
|
42
42
|
export * from "./hooks/mobile.js";
|
|
43
43
|
export * from "./hooks/table.js";
|
|
44
44
|
export * from "./hooks/scroll.js";
|
|
45
|
+
export * from "./hooks/keyboard.js";
|
|
45
46
|
export { translations, createTranslator } from "./locales/index.js";
|
|
46
47
|
export {
|
|
47
48
|
createLazyComponent,
|
package/es/locales/en.js
CHANGED
|
@@ -161,7 +161,7 @@ export default flat({
|
|
|
161
161
|
payment: "Thanks for your purchase",
|
|
162
162
|
subscription: "Thanks for your subscribing",
|
|
163
163
|
setup: "Thanks for your subscribing",
|
|
164
|
-
donate: "Thanks for your
|
|
164
|
+
donate: "Thanks for your tip",
|
|
165
165
|
tip: "A payment to {payee} has been completed. You can view the details of this payment in your account."
|
|
166
166
|
},
|
|
167
167
|
confirm: "Confirming allows {payee} to charge or reduce your staking. You can cancel or revoke staking anytime.",
|
package/es/payment/form/index.js
CHANGED
|
@@ -342,6 +342,17 @@ export default function PaymentForm({
|
|
|
342
342
|
const onStripeCancel = () => {
|
|
343
343
|
setState({ stripePaying: false });
|
|
344
344
|
};
|
|
345
|
+
useEffect(() => {
|
|
346
|
+
const handleKeyDown = (e) => {
|
|
347
|
+
if (e.key === "Enter" && !state.submitting && !state.paying && !state.stripePaying && quantityInventoryStatus && payable) {
|
|
348
|
+
onAction();
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
window.addEventListener("keydown", handleKeyDown);
|
|
352
|
+
return () => {
|
|
353
|
+
window.removeEventListener("keydown", handleKeyDown);
|
|
354
|
+
};
|
|
355
|
+
}, [state.submitting, state.paying, state.stripePaying, quantityInventoryStatus, payable]);
|
|
345
356
|
if (onlyShowBtn) {
|
|
346
357
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
347
358
|
/* @__PURE__ */ jsx(Box, { className: "cko-payment-submit-btn", children: /* @__PURE__ */ jsxs(
|
|
@@ -6,6 +6,12 @@ import { useEffect, useRef } from "react";
|
|
|
6
6
|
import { formatAmountPrecisionLimit } from "../libs/util.js";
|
|
7
7
|
import { usePaymentContext } from "../contexts/payment.js";
|
|
8
8
|
import { usePreventWheel } from "../hooks/scroll.js";
|
|
9
|
+
import { useTabNavigation } from "../hooks/keyboard.js";
|
|
10
|
+
const DONATION_PRESET_KEY_BASE = "payment-donation-preset";
|
|
11
|
+
const DONATION_CUSTOM_AMOUNT_KEY_BASE = "payment-donation-custom-amount";
|
|
12
|
+
const formatAmount = (amount) => {
|
|
13
|
+
return String(amount);
|
|
14
|
+
};
|
|
9
15
|
export default function ProductDonation({
|
|
10
16
|
item,
|
|
11
17
|
settings,
|
|
@@ -13,40 +19,120 @@ export default function ProductDonation({
|
|
|
13
19
|
currency
|
|
14
20
|
}) {
|
|
15
21
|
const { t, locale } = useLocaleContext();
|
|
16
|
-
const { setPayable } = usePaymentContext();
|
|
22
|
+
const { setPayable, session } = usePaymentContext();
|
|
17
23
|
usePreventWheel();
|
|
18
|
-
const presets = settings?.amount?.presets || [];
|
|
19
|
-
const
|
|
24
|
+
const presets = (settings?.amount?.presets || []).map(formatAmount);
|
|
25
|
+
const getUserStorageKey = (base) => {
|
|
26
|
+
const userDid = session?.user?.did;
|
|
27
|
+
return userDid ? `${base}:${userDid}` : base;
|
|
28
|
+
};
|
|
29
|
+
const getSavedCustomAmount = () => {
|
|
30
|
+
try {
|
|
31
|
+
return localStorage.getItem(getUserStorageKey(DONATION_CUSTOM_AMOUNT_KEY_BASE)) || "";
|
|
32
|
+
} catch (e) {
|
|
33
|
+
console.warn("Failed to access localStorage", e);
|
|
34
|
+
return "";
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
const getDefaultPreset = () => {
|
|
38
|
+
if (settings?.amount?.preset) {
|
|
39
|
+
return formatAmount(settings.amount.preset);
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
42
|
+
const savedPreset = localStorage.getItem(getUserStorageKey(DONATION_PRESET_KEY_BASE));
|
|
43
|
+
if (savedPreset) {
|
|
44
|
+
if (presets.includes(formatAmount(savedPreset))) {
|
|
45
|
+
return formatAmount(savedPreset);
|
|
46
|
+
}
|
|
47
|
+
if (savedPreset === "custom" && supportCustom) {
|
|
48
|
+
return "custom";
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
} catch (e) {
|
|
52
|
+
console.warn("Failed to access localStorage", e);
|
|
53
|
+
}
|
|
54
|
+
if (presets.length > 0) {
|
|
55
|
+
const middleIndex = Math.floor(presets.length / 2);
|
|
56
|
+
return presets[middleIndex] || presets[0];
|
|
57
|
+
}
|
|
58
|
+
return "0";
|
|
59
|
+
};
|
|
20
60
|
const supportPreset = presets.length > 0;
|
|
21
61
|
const supportCustom = !!settings?.amount?.custom;
|
|
62
|
+
const defaultPreset = getDefaultPreset();
|
|
63
|
+
const defaultCustomAmount = defaultPreset === "custom" ? getSavedCustomAmount() : "";
|
|
22
64
|
const [state, setState] = useSetState({
|
|
23
|
-
selected:
|
|
24
|
-
input:
|
|
25
|
-
custom: !supportPreset,
|
|
65
|
+
selected: defaultPreset === "custom" ? "" : defaultPreset,
|
|
66
|
+
input: defaultCustomAmount,
|
|
67
|
+
custom: !supportPreset || defaultPreset === "custom",
|
|
26
68
|
error: ""
|
|
27
69
|
});
|
|
28
70
|
const customInputRef = useRef(null);
|
|
71
|
+
const containerRef = useRef(null);
|
|
72
|
+
const handleSelect = (amount) => {
|
|
73
|
+
setPayable(true);
|
|
74
|
+
setState({ selected: formatAmount(amount), custom: false, error: "" });
|
|
75
|
+
onChange({ priceId: item.price_id, amount: formatAmount(amount) });
|
|
76
|
+
localStorage.setItem(getUserStorageKey(DONATION_PRESET_KEY_BASE), formatAmount(amount));
|
|
77
|
+
};
|
|
78
|
+
const handleCustomSelect = () => {
|
|
79
|
+
setState({ custom: true, selected: "", error: "" });
|
|
80
|
+
const savedCustomAmount = getSavedCustomAmount();
|
|
81
|
+
if (savedCustomAmount) {
|
|
82
|
+
setState({ input: savedCustomAmount });
|
|
83
|
+
onChange({ priceId: item.price_id, amount: savedCustomAmount });
|
|
84
|
+
setPayable(true);
|
|
85
|
+
} else if (!state.input) {
|
|
86
|
+
setPayable(false);
|
|
87
|
+
}
|
|
88
|
+
localStorage.setItem(getUserStorageKey(DONATION_PRESET_KEY_BASE), "custom");
|
|
89
|
+
};
|
|
90
|
+
const handleTabSelect = (selectedItem) => {
|
|
91
|
+
if (selectedItem === "custom") {
|
|
92
|
+
handleCustomSelect();
|
|
93
|
+
} else {
|
|
94
|
+
handleSelect(selectedItem);
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
const { handleKeyDown } = useTabNavigation(presets, handleTabSelect, {
|
|
98
|
+
includeCustom: supportCustom,
|
|
99
|
+
currentValue: state.custom ? void 0 : state.selected,
|
|
100
|
+
isCustomSelected: state.custom,
|
|
101
|
+
enabled: true,
|
|
102
|
+
selector: ".tab-navigable-card button",
|
|
103
|
+
containerRef
|
|
104
|
+
});
|
|
29
105
|
useEffect(() => {
|
|
30
106
|
if (settings.amount.preset) {
|
|
31
107
|
setState({ selected: settings.amount.preset, custom: false });
|
|
32
108
|
onChange({ priceId: item.price_id, amount: settings.amount.preset });
|
|
33
109
|
} else if (settings.amount.presets && settings.amount.presets.length > 0) {
|
|
34
|
-
|
|
35
|
-
|
|
110
|
+
const isCustom = defaultPreset === "custom";
|
|
111
|
+
setState({
|
|
112
|
+
selected: isCustom ? "" : defaultPreset,
|
|
113
|
+
custom: isCustom,
|
|
114
|
+
input: isCustom ? getSavedCustomAmount() : ""
|
|
115
|
+
});
|
|
116
|
+
if (!isCustom) {
|
|
117
|
+
onChange({ priceId: item.price_id, amount: defaultPreset });
|
|
118
|
+
} else if (defaultCustomAmount) {
|
|
119
|
+
onChange({ priceId: item.price_id, amount: defaultCustomAmount });
|
|
120
|
+
setPayable(true);
|
|
121
|
+
} else {
|
|
122
|
+
setPayable(false);
|
|
123
|
+
}
|
|
36
124
|
}
|
|
37
125
|
}, [settings.amount.preset, settings.amount.presets]);
|
|
38
126
|
useEffect(() => {
|
|
127
|
+
if (containerRef.current) {
|
|
128
|
+
containerRef.current.focus();
|
|
129
|
+
}
|
|
39
130
|
if (state.custom) {
|
|
40
131
|
setTimeout(() => {
|
|
41
132
|
customInputRef.current?.focus();
|
|
42
133
|
}, 0);
|
|
43
134
|
}
|
|
44
135
|
}, [state.custom]);
|
|
45
|
-
const handleSelect = (amount) => {
|
|
46
|
-
setPayable(true);
|
|
47
|
-
setState({ selected: amount, custom: false, error: "" });
|
|
48
|
-
onChange({ priceId: item.price_id, amount });
|
|
49
|
-
};
|
|
50
136
|
const handleInput = (event) => {
|
|
51
137
|
const { value } = event.target;
|
|
52
138
|
const min = parseFloat(settings.amount.minimum || "0");
|
|
@@ -64,111 +150,133 @@ export default function ProductDonation({
|
|
|
64
150
|
}
|
|
65
151
|
setPayable(true);
|
|
66
152
|
setState({ error: "", input: value });
|
|
67
|
-
onChange({ priceId: item.price_id, amount: value });
|
|
68
|
-
|
|
69
|
-
const handleCustomSelect = () => {
|
|
70
|
-
setState({ custom: true, error: "" });
|
|
71
|
-
if (!state.input) {
|
|
72
|
-
setPayable(false);
|
|
73
|
-
}
|
|
153
|
+
onChange({ priceId: item.price_id, amount: formatAmount(value) });
|
|
154
|
+
localStorage.setItem(getUserStorageKey(DONATION_CUSTOM_AMOUNT_KEY_BASE), formatAmount(value));
|
|
74
155
|
};
|
|
75
|
-
return /* @__PURE__ */ jsxs(
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
height: "42px",
|
|
91
|
-
...state.selected === amount && !state.custom ? { borderColor: "primary.main", borderWidth: 1 } : {}
|
|
92
|
-
},
|
|
93
|
-
children: /* @__PURE__ */ jsx(CardActionArea, { onClick: () => handleSelect(amount), children: /* @__PURE__ */ jsxs(
|
|
94
|
-
Stack,
|
|
156
|
+
return /* @__PURE__ */ jsxs(
|
|
157
|
+
Box,
|
|
158
|
+
{
|
|
159
|
+
ref: containerRef,
|
|
160
|
+
display: "flex",
|
|
161
|
+
flexDirection: "column",
|
|
162
|
+
alignItems: "flex-start",
|
|
163
|
+
gap: 1.5,
|
|
164
|
+
onKeyDown: handleKeyDown,
|
|
165
|
+
tabIndex: 0,
|
|
166
|
+
sx: { outline: "none" },
|
|
167
|
+
children: [
|
|
168
|
+
supportPreset && /* @__PURE__ */ jsxs(Grid, { container: true, spacing: 2, children: [
|
|
169
|
+
presets.map((amount) => /* @__PURE__ */ jsx(Grid, { item: true, xs: 6, sm: 3, children: /* @__PURE__ */ jsx(
|
|
170
|
+
Card,
|
|
95
171
|
{
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
172
|
+
variant: "outlined",
|
|
173
|
+
className: "tab-navigable-card",
|
|
174
|
+
sx: {
|
|
175
|
+
minWidth: 115,
|
|
176
|
+
textAlign: "center",
|
|
177
|
+
transition: "all 0.3s",
|
|
178
|
+
cursor: "pointer",
|
|
179
|
+
"&:hover": {
|
|
180
|
+
transform: "translateY(-4px)",
|
|
181
|
+
boxShadow: 3
|
|
182
|
+
},
|
|
183
|
+
".MuiCardActionArea-focusHighlight": {
|
|
184
|
+
backgroundColor: "transparent"
|
|
185
|
+
},
|
|
186
|
+
height: "42px",
|
|
187
|
+
...formatAmount(state.selected) === formatAmount(amount) && !state.custom ? { borderColor: "primary.main", borderWidth: 1 } : {}
|
|
188
|
+
},
|
|
189
|
+
children: /* @__PURE__ */ jsx(
|
|
190
|
+
CardActionArea,
|
|
191
|
+
{
|
|
192
|
+
onClick: () => handleSelect(amount),
|
|
193
|
+
tabIndex: 0,
|
|
194
|
+
"aria-selected": formatAmount(state.selected) === formatAmount(amount) && !state.custom,
|
|
195
|
+
children: /* @__PURE__ */ jsxs(
|
|
196
|
+
Stack,
|
|
197
|
+
{
|
|
198
|
+
direction: "row",
|
|
199
|
+
sx: { py: 1.5, px: 1.5 },
|
|
200
|
+
spacing: 0.5,
|
|
201
|
+
alignItems: "center",
|
|
202
|
+
justifyContent: "center",
|
|
203
|
+
children: [
|
|
204
|
+
/* @__PURE__ */ jsx(Avatar, { src: currency?.logo, sx: { width: 16, height: 16, mr: 0.5 }, alt: currency?.symbol }),
|
|
205
|
+
/* @__PURE__ */ jsx(
|
|
206
|
+
Typography,
|
|
207
|
+
{
|
|
208
|
+
component: "strong",
|
|
209
|
+
lineHeight: 1,
|
|
210
|
+
variant: "h3",
|
|
211
|
+
sx: { fontVariantNumeric: "tabular-nums", fontWeight: 400 },
|
|
212
|
+
children: amount
|
|
213
|
+
}
|
|
214
|
+
),
|
|
215
|
+
/* @__PURE__ */ jsx(Typography, { lineHeight: 1, fontSize: 14, color: "text.secondary", children: currency?.symbol })
|
|
216
|
+
]
|
|
217
|
+
}
|
|
218
|
+
)
|
|
219
|
+
}
|
|
220
|
+
)
|
|
115
221
|
}
|
|
116
|
-
) })
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
) }, amount)),
|
|
120
|
-
supportCustom && /* @__PURE__ */ jsx(Grid, { item: true, xs: 6, sm: 3, children: /* @__PURE__ */ jsx(
|
|
121
|
-
Card,
|
|
122
|
-
{
|
|
123
|
-
variant: "outlined",
|
|
124
|
-
sx: {
|
|
125
|
-
textAlign: "center",
|
|
126
|
-
transition: "all 0.3s",
|
|
127
|
-
cursor: "pointer",
|
|
128
|
-
"&:hover": {
|
|
129
|
-
transform: "translateY(-4px)",
|
|
130
|
-
boxShadow: 3
|
|
131
|
-
},
|
|
132
|
-
height: "42px",
|
|
133
|
-
...state.custom ? { borderColor: "primary.main", borderWidth: 1 } : {}
|
|
134
|
-
},
|
|
135
|
-
children: /* @__PURE__ */ jsx(CardActionArea, { onClick: () => handleCustomSelect(), children: /* @__PURE__ */ jsx(
|
|
136
|
-
Stack,
|
|
222
|
+
) }, amount)),
|
|
223
|
+
supportCustom && /* @__PURE__ */ jsx(Grid, { item: true, xs: 6, sm: 3, children: /* @__PURE__ */ jsx(
|
|
224
|
+
Card,
|
|
137
225
|
{
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
226
|
+
variant: "outlined",
|
|
227
|
+
className: "tab-navigable-card",
|
|
228
|
+
sx: {
|
|
229
|
+
textAlign: "center",
|
|
230
|
+
transition: "all 0.3s",
|
|
231
|
+
cursor: "pointer",
|
|
232
|
+
"&:hover": {
|
|
233
|
+
transform: "translateY(-4px)",
|
|
234
|
+
boxShadow: 3
|
|
235
|
+
},
|
|
236
|
+
".MuiCardActionArea-focusHighlight": {
|
|
237
|
+
backgroundColor: "transparent"
|
|
238
|
+
},
|
|
239
|
+
height: "42px",
|
|
240
|
+
...state.custom ? { borderColor: "primary.main", borderWidth: 1 } : {}
|
|
241
|
+
},
|
|
242
|
+
children: /* @__PURE__ */ jsx(CardActionArea, { onClick: handleCustomSelect, tabIndex: 0, "aria-selected": state.custom, children: /* @__PURE__ */ jsx(
|
|
243
|
+
Stack,
|
|
244
|
+
{
|
|
245
|
+
direction: "row",
|
|
246
|
+
sx: { py: 1.5, px: 1.5 },
|
|
247
|
+
spacing: 0.5,
|
|
248
|
+
alignItems: "center",
|
|
249
|
+
justifyContent: "center",
|
|
250
|
+
children: /* @__PURE__ */ jsx(Typography, { variant: "h3", lineHeight: 1, sx: { fontWeight: 400 }, children: t("common.custom") })
|
|
251
|
+
}
|
|
252
|
+
) })
|
|
144
253
|
}
|
|
145
|
-
) })
|
|
146
|
-
},
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
] });
|
|
254
|
+
) }, "custom")
|
|
255
|
+
] }),
|
|
256
|
+
state.custom && /* @__PURE__ */ jsx(
|
|
257
|
+
TextField,
|
|
258
|
+
{
|
|
259
|
+
type: "number",
|
|
260
|
+
value: state.input,
|
|
261
|
+
onChange: handleInput,
|
|
262
|
+
margin: "none",
|
|
263
|
+
fullWidth: true,
|
|
264
|
+
error: !!state.error,
|
|
265
|
+
helperText: state.error,
|
|
266
|
+
inputRef: customInputRef,
|
|
267
|
+
InputProps: {
|
|
268
|
+
endAdornment: /* @__PURE__ */ jsxs(Stack, { direction: "row", spacing: 0.5, alignItems: "center", sx: { ml: 1 }, children: [
|
|
269
|
+
/* @__PURE__ */ jsx(Avatar, { src: currency?.logo, sx: { width: 16, height: 16 }, alt: currency?.symbol }),
|
|
270
|
+
/* @__PURE__ */ jsx(Typography, { children: currency?.symbol })
|
|
271
|
+
] }),
|
|
272
|
+
autoComplete: "off"
|
|
273
|
+
},
|
|
274
|
+
sx: {
|
|
275
|
+
mt: defaultPreset !== "0" ? 0 : 1
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
)
|
|
279
|
+
]
|
|
280
|
+
}
|
|
281
|
+
);
|
|
174
282
|
}
|