@blocklet/payment-react 1.18.18 → 1.18.20
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 +3 -2
- package/es/locales/zh.js +2 -1
- package/es/payment/donation-form.js +51 -14
- 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 +3 -2
- package/lib/locales/zh.js +2 -1
- package/lib/payment/donation-form.js +42 -7
- 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 +3 -2
- package/src/locales/zh.tsx +2 -1
- package/src/payment/donation-form.tsx +44 -11
- package/src/payment/form/index.tsx +20 -0
- package/src/payment/product-donation.tsx +180 -72
|
@@ -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
|
}
|
|
@@ -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,85 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.useTabNavigation = void 0;
|
|
7
|
+
var _react = require("react");
|
|
8
|
+
const useTabNavigation = (items, onSelect, options) => {
|
|
9
|
+
const {
|
|
10
|
+
valueType = "value",
|
|
11
|
+
includeCustom = false,
|
|
12
|
+
currentValue,
|
|
13
|
+
isCustomSelected = false,
|
|
14
|
+
compareValue = (item, value) => item === value,
|
|
15
|
+
enabled = true,
|
|
16
|
+
selector = ".tab-navigable-card button",
|
|
17
|
+
containerRef
|
|
18
|
+
} = options || {};
|
|
19
|
+
const hasTabbed = (0, _react.useRef)(false);
|
|
20
|
+
const findNavigableElements = (0, _react.useCallback)(() => {
|
|
21
|
+
if (containerRef?.current) {
|
|
22
|
+
return containerRef.current.querySelectorAll(selector);
|
|
23
|
+
}
|
|
24
|
+
return document.querySelectorAll(selector);
|
|
25
|
+
}, [containerRef, selector]);
|
|
26
|
+
const determineCurrentIndex = (0, _react.useCallback)(() => {
|
|
27
|
+
const allOptions = includeCustom ? [...items, "custom"] : items;
|
|
28
|
+
if (allOptions.length === 0) return -1;
|
|
29
|
+
if (!hasTabbed.current) {
|
|
30
|
+
if (isCustomSelected && includeCustom) {
|
|
31
|
+
return items.length;
|
|
32
|
+
}
|
|
33
|
+
if (currentValue !== void 0) {
|
|
34
|
+
if (valueType === "index" && typeof currentValue === "number") {
|
|
35
|
+
return currentValue >= 0 && currentValue < items.length ? currentValue : -1;
|
|
36
|
+
}
|
|
37
|
+
return items.findIndex(item => compareValue(item, currentValue));
|
|
38
|
+
}
|
|
39
|
+
} else {
|
|
40
|
+
const focusedElement = document.activeElement;
|
|
41
|
+
const navigableElements = findNavigableElements();
|
|
42
|
+
for (let i = 0; i < navigableElements.length; i++) {
|
|
43
|
+
if (navigableElements[i] === focusedElement) {
|
|
44
|
+
return i;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return -1;
|
|
49
|
+
}, [items, includeCustom, isCustomSelected, currentValue, valueType, compareValue, findNavigableElements]);
|
|
50
|
+
const getNextIndex = (0, _react.useCallback)((currentIndex, isShiftKey) => {
|
|
51
|
+
const totalOptions = includeCustom ? items.length + 1 : items.length;
|
|
52
|
+
if (currentIndex === -1) {
|
|
53
|
+
return 0;
|
|
54
|
+
}
|
|
55
|
+
if (isShiftKey) {
|
|
56
|
+
return currentIndex === 0 ? totalOptions - 1 : currentIndex - 1;
|
|
57
|
+
}
|
|
58
|
+
return currentIndex === totalOptions - 1 ? 0 : currentIndex + 1;
|
|
59
|
+
}, [items, includeCustom]);
|
|
60
|
+
const handleKeyDown = (0, _react.useCallback)(e => {
|
|
61
|
+
if (!enabled || e.key !== "Tab") 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
|
+
}, [items, onSelect, enabled, determineCurrentIndex, getNextIndex, findNavigableElements]);
|
|
76
|
+
const resetTabNavigation = (0, _react.useCallback)(() => {
|
|
77
|
+
hasTabbed.current = false;
|
|
78
|
+
}, []);
|
|
79
|
+
return {
|
|
80
|
+
handleKeyDown,
|
|
81
|
+
resetTabNavigation,
|
|
82
|
+
isTabNavigationActive: hasTabbed.current
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
exports.useTabNavigation = useTabNavigation;
|
package/lib/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/lib/index.js
CHANGED
|
@@ -418,6 +418,18 @@ Object.keys(_scroll).forEach(function (key) {
|
|
|
418
418
|
}
|
|
419
419
|
});
|
|
420
420
|
});
|
|
421
|
+
var _keyboard = require("./hooks/keyboard");
|
|
422
|
+
Object.keys(_keyboard).forEach(function (key) {
|
|
423
|
+
if (key === "default" || key === "__esModule") return;
|
|
424
|
+
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
|
|
425
|
+
if (key in exports && exports[key] === _keyboard[key]) return;
|
|
426
|
+
Object.defineProperty(exports, key, {
|
|
427
|
+
enumerable: true,
|
|
428
|
+
get: function () {
|
|
429
|
+
return _keyboard[key];
|
|
430
|
+
}
|
|
431
|
+
});
|
|
432
|
+
});
|
|
421
433
|
var _locales = require("./locales");
|
|
422
434
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
423
435
|
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
package/lib/locales/en.js
CHANGED
|
@@ -138,6 +138,7 @@ module.exports = (0, _flat.default)({
|
|
|
138
138
|
empty: "No supporters yet",
|
|
139
139
|
gaveTips: "{count} people gave tips",
|
|
140
140
|
tipAmount: "Tip Amount",
|
|
141
|
+
tabHint: "to switch amount",
|
|
141
142
|
benefits: {
|
|
142
143
|
one: "{name} will receive all tips",
|
|
143
144
|
multiple: "Tips will be distributed to {count} beneficiaries",
|
|
@@ -151,7 +152,7 @@ module.exports = (0, _flat.default)({
|
|
|
151
152
|
later: "Configure Later",
|
|
152
153
|
configTip: "Configure donation settings in Payment Kit"
|
|
153
154
|
},
|
|
154
|
-
cardPay: "{action} with card",
|
|
155
|
+
cardPay: "{action} with bank card",
|
|
155
156
|
empty: "No thing to pay",
|
|
156
157
|
per: "per",
|
|
157
158
|
pay: "Pay {payee}",
|
|
@@ -168,7 +169,7 @@ module.exports = (0, _flat.default)({
|
|
|
168
169
|
payment: "Thanks for your purchase",
|
|
169
170
|
subscription: "Thanks for your subscribing",
|
|
170
171
|
setup: "Thanks for your subscribing",
|
|
171
|
-
donate: "Thanks for your
|
|
172
|
+
donate: "Thanks for your tip",
|
|
172
173
|
tip: "A payment to {payee} has been completed. You can view the details of this payment in your account."
|
|
173
174
|
},
|
|
174
175
|
confirm: "Confirming allows {payee} to charge or reduce your staking. You can cancel or revoke staking anytime.",
|
package/lib/locales/zh.js
CHANGED
|
@@ -138,6 +138,7 @@ module.exports = (0, _flat.default)({
|
|
|
138
138
|
empty: "\u2764\uFE0F \u652F\u6301\u4E00\u4E0B",
|
|
139
139
|
gaveTips: "\u5DF2\u6709 {count} \u4EBA\u6253\u8D4F",
|
|
140
140
|
tipAmount: "\u6253\u8D4F\u91D1\u989D",
|
|
141
|
+
tabHint: "\u5FEB\u901F\u5207\u6362\u91D1\u989D",
|
|
141
142
|
benefits: {
|
|
142
143
|
one: "{name} \u5C06\u83B7\u5F97\u5168\u90E8\u6253\u8D4F",
|
|
143
144
|
multiple: "\u6253\u8D4F\u5C06\u6309\u6BD4\u4F8B\u5206\u914D\u7ED9 {count} \u4F4D\u53D7\u76CA\u4EBA",
|
|
@@ -151,7 +152,7 @@ module.exports = (0, _flat.default)({
|
|
|
151
152
|
later: "\u7A0D\u540E\u914D\u7F6E",
|
|
152
153
|
configTip: "\u524D\u5F80 Payment Kit \u914D\u7F6E\u6253\u8D4F\u9009\u9879"
|
|
153
154
|
},
|
|
154
|
-
cardPay: "\u4F7F\u7528\u5361
|
|
155
|
+
cardPay: "\u4F7F\u7528\u94F6\u884C\u5361{action}",
|
|
155
156
|
empty: "\u6CA1\u6709\u53EF\u652F\u4ED8\u7684\u9879\u76EE",
|
|
156
157
|
per: "\u6BCF",
|
|
157
158
|
pay: "\u4ED8\u6B3E\u7ED9 {payee}",
|
|
@@ -62,6 +62,9 @@ function PaymentInner({
|
|
|
62
62
|
settings,
|
|
63
63
|
session
|
|
64
64
|
} = (0, _payment.usePaymentContext)();
|
|
65
|
+
const {
|
|
66
|
+
isMobile
|
|
67
|
+
} = (0, _mobile.useMobile)();
|
|
65
68
|
const [state, setState] = (0, _ahooks.useSetState)({
|
|
66
69
|
checkoutSession,
|
|
67
70
|
submitting: false,
|
|
@@ -234,16 +237,48 @@ function PaymentInner({
|
|
|
234
237
|
sx: {
|
|
235
238
|
display: benefitsState.open ? "none" : "block"
|
|
236
239
|
},
|
|
237
|
-
children: [/* @__PURE__ */(0, _jsxRuntime.
|
|
238
|
-
|
|
240
|
+
children: [/* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
|
|
241
|
+
direction: "row",
|
|
242
|
+
justifyContent: "space-between",
|
|
243
|
+
alignItems: "center",
|
|
239
244
|
sx: {
|
|
240
|
-
color: "text.primary",
|
|
241
|
-
fontSize: "18px",
|
|
242
|
-
fontWeight: "500",
|
|
243
|
-
lineHeight: "24px",
|
|
244
245
|
mb: 2
|
|
245
246
|
},
|
|
246
|
-
children:
|
|
247
|
+
children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
|
|
248
|
+
title: t("payment.checkout.orderSummary"),
|
|
249
|
+
sx: {
|
|
250
|
+
color: "text.primary",
|
|
251
|
+
fontSize: "18px",
|
|
252
|
+
fontWeight: "500",
|
|
253
|
+
lineHeight: "24px"
|
|
254
|
+
},
|
|
255
|
+
children: t("payment.checkout.donation.tipAmount")
|
|
256
|
+
}), !isMobile && donationSettings?.amount?.presets && donationSettings.amount.presets.length > 0 && /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Typography, {
|
|
257
|
+
sx: {
|
|
258
|
+
color: "text.secondary",
|
|
259
|
+
fontSize: "13px",
|
|
260
|
+
display: "flex",
|
|
261
|
+
alignItems: "center",
|
|
262
|
+
gap: 0.5,
|
|
263
|
+
opacity: 0.8
|
|
264
|
+
},
|
|
265
|
+
children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Box, {
|
|
266
|
+
component: "span",
|
|
267
|
+
sx: {
|
|
268
|
+
border: "1px solid",
|
|
269
|
+
borderColor: "divider",
|
|
270
|
+
borderRadius: 0.75,
|
|
271
|
+
px: 0.75,
|
|
272
|
+
py: 0.25,
|
|
273
|
+
fontSize: "12px",
|
|
274
|
+
lineHeight: 1,
|
|
275
|
+
color: "text.secondary",
|
|
276
|
+
fontWeight: "400",
|
|
277
|
+
bgcolor: "transparent"
|
|
278
|
+
},
|
|
279
|
+
children: "Tab"
|
|
280
|
+
}), t("payment.checkout.donation.tabHint")]
|
|
281
|
+
})]
|
|
247
282
|
}), items.map(x => /* @__PURE__ */(0, _jsxRuntime.jsx)(_productDonation.default, {
|
|
248
283
|
item: x,
|
|
249
284
|
settings: donationSettings,
|