@blocklet/payment-react 1.18.17 → 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 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
- - props:
327
- - `subscriptionId`: [required] The subscription ID
328
- - `onPaid`: [optional] Callback function called after successful payment
329
- - `mode`: [optional] Component mode, `default` or `custom` (default is `default`)
330
- - `dialogProps`: [optional] Dialog properties, default is `{ open: true }`
331
- - `children`: [optional] Custom rendering with payment data when `mode` is `custom`
332
- - Custom mode:
333
- - `children` will receive two parameters:
334
- - `handlePay`: Function to start the
335
-
336
- payment process
337
- - `data`: Payment data (includes `subscription`, `summary`, `invoices`, `subscriptionUrl`)
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: 18,
15
- lineHeight: 1.2,
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 support",
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.",
@@ -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 preset = settings?.amount?.preset || presets?.[0] || "0";
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: preset,
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
- setState({ selected: settings.amount.presets[0], custom: false });
35
- onChange({ priceId: item.price_id, amount: settings.amount.presets[0] });
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(Box, { display: "flex", flexDirection: "column", alignItems: "flex-start", gap: 1.5, children: [
76
- supportPreset && /* @__PURE__ */ jsxs(Grid, { container: true, spacing: 2, children: [
77
- settings.amount.presets && settings.amount.presets.length > 0 && settings.amount.presets.map((amount) => /* @__PURE__ */ jsx(Grid, { item: true, xs: 6, sm: 3, children: /* @__PURE__ */ jsx(
78
- Card,
79
- {
80
- variant: "outlined",
81
- sx: {
82
- minWidth: 115,
83
- textAlign: "center",
84
- transition: "all 0.3s",
85
- cursor: "pointer",
86
- "&:hover": {
87
- transform: "translateY(-4px)",
88
- boxShadow: 3
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
- direction: "row",
97
- sx: { py: 1.5, px: 1.5 },
98
- spacing: 0.5,
99
- alignItems: "center",
100
- justifyContent: "center",
101
- children: [
102
- /* @__PURE__ */ jsx(Avatar, { src: currency?.logo, sx: { width: 16, height: 16, mr: 0.5 }, alt: currency?.symbol }),
103
- /* @__PURE__ */ jsx(
104
- Typography,
105
- {
106
- component: "strong",
107
- lineHeight: 1,
108
- variant: "h3",
109
- sx: { fontVariantNumeric: "tabular-nums", fontWeight: 400 },
110
- children: amount
111
- }
112
- ),
113
- /* @__PURE__ */ jsx(Typography, { lineHeight: 1, fontSize: 14, color: "text.secondary", children: currency?.symbol })
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
- amount
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
- direction: "row",
139
- sx: { py: 1.5, px: 1.5 },
140
- spacing: 0.5,
141
- alignItems: "center",
142
- justifyContent: "center",
143
- children: /* @__PURE__ */ jsx(Typography, { variant: "h3", lineHeight: 1, sx: { fontWeight: 400 }, children: t("common.custom") })
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
- "custom"
148
- ) }, "custom")
149
- ] }),
150
- state.custom && /* @__PURE__ */ jsx(
151
- TextField,
152
- {
153
- type: "number",
154
- value: state.input,
155
- onChange: handleInput,
156
- margin: "none",
157
- fullWidth: true,
158
- error: !!state.error,
159
- helperText: state.error,
160
- inputRef: customInputRef,
161
- InputProps: {
162
- endAdornment: /* @__PURE__ */ jsxs(Stack, { direction: "row", spacing: 0.5, alignItems: "center", sx: { ml: 1 }, children: [
163
- /* @__PURE__ */ jsx(Avatar, { src: currency?.logo, sx: { width: 16, height: 16 }, alt: currency?.symbol }),
164
- /* @__PURE__ */ jsx(Typography, { children: currency?.symbol })
165
- ] }),
166
- autoComplete: "off"
167
- },
168
- sx: {
169
- mt: preset !== "0" ? 0 : 1
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
  }
@@ -21,8 +21,8 @@ function Livemode({
21
21
  color: "warning",
22
22
  sx: {
23
23
  ml: 2,
24
- height: 18,
25
- lineHeight: 1.2,
24
+ height: "18px",
25
+ lineHeight: "18px",
26
26
  textTransform: "uppercase",
27
27
  fontSize: "0.7rem",
28
28
  fontWeight: "bold",