@blocklet/payment-react 1.15.34 → 1.15.36

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.
@@ -57,11 +57,12 @@ export type DonateProps = Pick<CheckoutProps, 'onPaid' | 'onError'> & {
57
57
  settings: DonationSettings;
58
58
  livemode?: boolean;
59
59
  timeout?: number;
60
- mode?: 'inline' | 'default';
60
+ mode?: 'inline' | 'default' | 'custom';
61
61
  inlineOptions?: {
62
62
  button?: ButtonType;
63
63
  };
64
64
  theme?: 'default' | 'inherit' | PaymentThemeOptions;
65
+ children?: (openDialog: () => void, donateTotalAmount: string, supporters: DonateHistory) => React.ReactNode;
65
66
  };
66
67
 
67
68
  const donationCache: { [key: string]: Promise<TPaymentLink> } = {};
@@ -315,6 +316,7 @@ function CheckoutDonateInner({
315
316
  mode,
316
317
  inlineOptions = {},
317
318
  theme,
319
+ children,
318
320
  }: DonateProps) {
319
321
  // eslint-disable-line
320
322
  const { state, setState, donation, supporters } = useDonation(settings, livemode, mode);
@@ -350,105 +352,135 @@ function CheckoutDonateInner({
350
352
  setPopoverOpen(false);
351
353
  };
352
354
 
353
- return (
355
+ const startDonate = () => {
356
+ setState({ open: true });
357
+ };
358
+
359
+ const inlineRender = (
354
360
  <>
355
- {mode === 'inline' ? (
356
- <>
357
- <Button
358
- size={(settings.appearance?.button?.size || 'medium') as any}
359
- color={(settings.appearance?.button?.color || 'primary') as any}
360
- variant={(settings.appearance?.button?.variant || 'contained') as any}
361
- {...settings.appearance?.button}
362
- onClick={handlePopoverOpen}>
363
- <Stack direction="row" alignItems="center" spacing={0.5}>
364
- {settings.appearance.button.icon}
365
- {typeof settings.appearance.button.text === 'string' ? (
366
- <Typography sx={{ whiteSpace: 'nowrap' }}>{settings.appearance.button.text}</Typography>
367
- ) : (
368
- settings.appearance.button.text
369
- )}
370
- </Stack>
371
- </Button>
372
- <Popover
373
- id="mouse-over-popper"
374
- open={popoverOpen}
375
- anchorEl={anchorEl}
376
- onClose={handlePopoverClose}
377
- anchorOrigin={{
378
- vertical: 'top',
379
- horizontal: 'center',
380
- }}
381
- transformOrigin={{
382
- vertical: 'bottom',
383
- horizontal: 'center',
384
- }}>
385
- <Box
386
- sx={{
387
- minWidth: 320,
388
- padding: '20px',
389
- }}>
390
- {supporters.loading && (
391
- <div
392
- style={{
393
- position: 'absolute',
394
- top: 0,
395
- left: 0,
396
- right: 0,
397
- bottom: 0,
398
- display: 'flex',
399
- justifyContent: 'center',
400
- alignItems: 'center',
401
- backgroundColor: 'rgba(255, 255, 255, 0.7)',
402
- }}>
403
- <CircularProgress />
404
- </div>
405
- )}
406
- <Box display="flex" alignItems="center" flexDirection="column" gap={2}>
407
- <Button {...inlineOptions.button} onClick={() => setState({ open: true })}>
408
- <Stack direction="row" alignItems="center" spacing={0.5}>
409
- {inlineOptions?.button?.icon}
410
- {typeof inlineOptions?.button?.text === 'string' ? (
411
- <Typography sx={{ whiteSpace: 'nowrap' }}>{inlineOptions?.button?.text}</Typography>
412
- ) : (
413
- inlineOptions?.button?.text
414
- )}
415
- </Stack>
416
- </Button>
417
- <SupporterSimple {...(supporters.data as DonateHistory)} />
418
- </Box>
419
- </Box>
420
- </Popover>
421
- </>
422
- ) : (
423
- <Box
424
- sx={{ width: '100%', minWidth: 300, maxWidth: 720 }}
425
- display="flex"
426
- flexDirection="column"
427
- alignItems="center"
428
- gap={{ xs: 1, sm: 2 }}>
429
- <Button
430
- size={(settings.appearance?.button?.size || 'medium') as any}
431
- color={(settings.appearance?.button?.color || 'primary') as any}
432
- variant={(settings.appearance?.button?.variant || 'contained') as any}
433
- {...settings.appearance?.button}
434
- onClick={() => setState({ open: true })}>
435
- <Stack direction="row" alignItems="center" spacing={0.5}>
436
- {settings.appearance.button.icon}
437
- {typeof settings.appearance.button.text === 'string' ? (
438
- <Typography>{settings.appearance.button.text}</Typography>
439
- ) : (
440
- settings.appearance.button.text
441
- )}
442
- </Stack>
443
- </Button>
444
- {supporters.data && settings.appearance.history.variant === 'avatar' && (
445
- <SupporterAvatar {...(supporters.data as DonateHistory)} />
361
+ <Button
362
+ size={(settings.appearance?.button?.size || 'medium') as any}
363
+ color={(settings.appearance?.button?.color || 'primary') as any}
364
+ variant={(settings.appearance?.button?.variant || 'contained') as any}
365
+ {...settings.appearance?.button}
366
+ onClick={handlePopoverOpen}>
367
+ <Stack direction="row" alignItems="center" spacing={0.5}>
368
+ {settings.appearance.button.icon}
369
+ {typeof settings.appearance.button.text === 'string' ? (
370
+ <Typography sx={{ whiteSpace: 'nowrap' }}>{settings.appearance.button.text}</Typography>
371
+ ) : (
372
+ settings.appearance.button.text
446
373
  )}
447
- {supporters.data && settings.appearance.history.variant === 'table' && (
448
- <SupporterTable {...(supporters.data as DonateHistory)} />
374
+ </Stack>
375
+ </Button>
376
+ <Popover
377
+ id="mouse-over-popper"
378
+ open={popoverOpen}
379
+ anchorEl={anchorEl}
380
+ onClose={handlePopoverClose}
381
+ anchorOrigin={{
382
+ vertical: 'top',
383
+ horizontal: 'center',
384
+ }}
385
+ transformOrigin={{
386
+ vertical: 'bottom',
387
+ horizontal: 'center',
388
+ }}>
389
+ <Box
390
+ sx={{
391
+ minWidth: 320,
392
+ padding: '20px',
393
+ }}>
394
+ {supporters.loading && (
395
+ <div
396
+ style={{
397
+ position: 'absolute',
398
+ top: 0,
399
+ left: 0,
400
+ right: 0,
401
+ bottom: 0,
402
+ display: 'flex',
403
+ justifyContent: 'center',
404
+ alignItems: 'center',
405
+ backgroundColor: 'rgba(255, 255, 255, 0.7)',
406
+ }}>
407
+ <CircularProgress />
408
+ </div>
449
409
  )}
410
+ <Box display="flex" alignItems="center" flexDirection="column" gap={2}>
411
+ <Button {...inlineOptions.button} onClick={() => startDonate()}>
412
+ <Stack direction="row" alignItems="center" spacing={0.5}>
413
+ {inlineOptions?.button?.icon}
414
+ {typeof inlineOptions?.button?.text === 'string' ? (
415
+ <Typography sx={{ whiteSpace: 'nowrap' }}>{inlineOptions?.button?.text}</Typography>
416
+ ) : (
417
+ inlineOptions?.button?.text
418
+ )}
419
+ </Stack>
420
+ </Button>
421
+ <SupporterSimple {...(supporters.data as DonateHistory)} />
422
+ </Box>
450
423
  </Box>
424
+ </Popover>
425
+ </>
426
+ );
427
+ const defaultRender = (
428
+ <Box
429
+ sx={{ width: '100%', minWidth: 300, maxWidth: 720 }}
430
+ display="flex"
431
+ flexDirection="column"
432
+ alignItems="center"
433
+ gap={{ xs: 1, sm: 2 }}>
434
+ <Button
435
+ size={(settings.appearance?.button?.size || 'medium') as any}
436
+ color={(settings.appearance?.button?.color || 'primary') as any}
437
+ variant={(settings.appearance?.button?.variant || 'contained') as any}
438
+ {...settings.appearance?.button}
439
+ onClick={() => startDonate()}>
440
+ <Stack direction="row" alignItems="center" spacing={0.5}>
441
+ {settings.appearance.button.icon}
442
+ {typeof settings.appearance.button.text === 'string' ? (
443
+ <Typography>{settings.appearance.button.text}</Typography>
444
+ ) : (
445
+ settings.appearance.button.text
446
+ )}
447
+ </Stack>
448
+ </Button>
449
+ {supporters.data && settings.appearance.history.variant === 'avatar' && (
450
+ <SupporterAvatar {...(supporters.data as DonateHistory)} />
451
451
  )}
452
+ {supporters.data && settings.appearance.history.variant === 'table' && (
453
+ <SupporterTable {...(supporters.data as DonateHistory)} />
454
+ )}
455
+ </Box>
456
+ );
457
+ const renderInnerView = () => {
458
+ if (mode === 'inline') {
459
+ return inlineRender;
460
+ }
461
+ if (mode === 'custom') {
462
+ return children && typeof children === 'function' ? (
463
+ <>
464
+ {children(
465
+ startDonate,
466
+ `${formatAmount(
467
+ (supporters.data as DonateHistory)?.totalAmount || '0',
468
+ (supporters.data as DonateHistory)?.currency?.decimal
469
+ )} ${(supporters.data as DonateHistory)?.currency?.symbol}`,
470
+ (supporters.data as DonateHistory) || {}
471
+ )}
472
+ </>
473
+ ) : (
474
+ <Typography>
475
+ Please provide a valid render function <pre>{'(openDonate, donateTotalAmount, supporters) => ReactNode'}</pre>
476
+ </Typography>
477
+ );
478
+ }
479
+ return defaultRender;
480
+ };
481
+ return (
482
+ <>
483
+ {renderInnerView()}
452
484
  {donation.data && (
453
485
  <Dialog
454
486
  open={state.open}
@@ -33,7 +33,6 @@ import {
33
33
  getPriceUintAmountByCurrency,
34
34
  isMobileSafari,
35
35
  } from '../libs/util';
36
- import Amount from '../payment/amount';
37
36
  import { useMobile } from '../hooks/mobile';
38
37
  import TruncatedText from './truncated-text';
39
38
 
@@ -203,10 +202,10 @@ export default function PricingTable({ table, alignItems, interval, mode, onSele
203
202
  max-width: 360px !important;
204
203
  }
205
204
  .price-table-wrap:has(> div:nth-child(2)) {
206
- max-width: 720px !important;
205
+ max-width: 780px !important;
207
206
  }
208
207
  .price-table-wrap:has(> div:nth-child(3)) {
209
- max-width: 1080px !important;
208
+ max-width: 1200px !important;
210
209
  }
211
210
  }
212
211
  `;
@@ -327,6 +326,7 @@ export default function PricingTable({ table, alignItems, interval, mode, onSele
327
326
  if (mode === 'select') {
328
327
  action = x.is_selected ? t('payment.checkout.selected') : t('payment.checkout.select');
329
328
  }
329
+ const [amount, unit] = formatPriceAmount(x.price, currency, x.product.unit_label).split('/');
330
330
  return (
331
331
  <Stack
332
332
  key={x?.price_id}
@@ -351,14 +351,13 @@ export default function PricingTable({ table, alignItems, interval, mode, onSele
351
351
  },
352
352
  width: {
353
353
  xs: '100%',
354
- md: '320px',
354
+ md: '360px',
355
355
  },
356
356
  maxWidth: '360px',
357
357
  minWidth: '300px',
358
358
 
359
359
  padding: '20px',
360
360
  position: 'relative',
361
- height: 'fit-content',
362
361
  }}>
363
362
  <Box textAlign="center">
364
363
  <Stack
@@ -369,7 +368,7 @@ export default function PricingTable({ table, alignItems, interval, mode, onSele
369
368
  sx={{ gap: '12px' }}>
370
369
  <Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
371
370
  <Typography
372
- color="text.primary"
371
+ color="text.secondary"
373
372
  fontWeight={600}
374
373
  sx={{
375
374
  fontSize: '18px !important',
@@ -380,7 +379,7 @@ export default function PricingTable({ table, alignItems, interval, mode, onSele
380
379
  {x.is_highlight && (
381
380
  <Chip
382
381
  label={x.highlight_text}
383
- color="default"
382
+ color="primary"
384
383
  size="small"
385
384
  sx={{
386
385
  position: 'absolute',
@@ -390,10 +389,29 @@ export default function PricingTable({ table, alignItems, interval, mode, onSele
390
389
  />
391
390
  )}
392
391
  </Box>
393
- <Amount
394
- amount={formatPriceAmount(x.price, currency, x.product.unit_label)}
395
- sx={{ my: 0, marginTop: '0px !important', fontSize: '48px', fontWeight: 'bold' }}
396
- />
392
+ <Typography
393
+ component="div"
394
+ sx={{
395
+ my: 0,
396
+ fontWeight: '700',
397
+ fontSize: '32px',
398
+ letterSpacing: '-0.03rem',
399
+ fontVariantNumeric: 'tabular-nums',
400
+ display: 'flex',
401
+ alignItems: 'baseline',
402
+ gap: '4px',
403
+ flexWrap: 'wrap',
404
+ }}>
405
+ {amount}
406
+ {unit ? (
407
+ <Typography
408
+ sx={{ fontSize: '16px', fontWeight: '400', color: 'text.secondary', textAlign: 'left' }}>
409
+ / {unit}
410
+ </Typography>
411
+ ) : (
412
+ ''
413
+ )}
414
+ </Typography>
397
415
  <Typography
398
416
  color="text.secondary"
399
417
  sx={{
@@ -473,7 +491,7 @@ function Subscribe({ x, action, onSelect, currencyId }: any) {
473
491
  fullWidth
474
492
  size="medium"
475
493
  variant="contained"
476
- color={x.is_highlight || x.is_selected ? 'info' : 'primary'}
494
+ color="primary"
477
495
  sx={{
478
496
  fontSize: '16px',
479
497
  padding: '10px 20px',
@@ -0,0 +1,16 @@
1
+ import { useEffect } from 'react';
2
+
3
+ export function usePreventWheel() {
4
+ useEffect(() => {
5
+ const handleWheel = (e: any) => {
6
+ // @ts-ignore
7
+ if (document.activeElement?.type === 'number') {
8
+ e.preventDefault();
9
+ }
10
+ };
11
+ window.addEventListener('wheel', handleWheel, { passive: false });
12
+ return () => {
13
+ window.removeEventListener('wheel', handleWheel);
14
+ };
15
+ }, []);
16
+ }
package/src/index.ts CHANGED
@@ -38,6 +38,7 @@ export * from './contexts/payment';
38
38
  export * from './hooks/subscription';
39
39
  export * from './hooks/mobile';
40
40
  export * from './hooks/table';
41
+ export * from './hooks/scroll';
41
42
 
42
43
  export { translations, createTranslator } from './locales';
43
44
 
package/src/libs/util.ts CHANGED
@@ -1121,14 +1121,17 @@ export function getInvoiceDescriptionAndReason(invoice: TInvoiceExpanded, locale
1121
1121
  slash_stake: t('payment.invoice.reason.slashStake', locale),
1122
1122
  stake: t('payment.invoice.reason.stake', locale),
1123
1123
  return_stake: t('payment.invoice.reason.returnStake', locale),
1124
+ recharge: t('payment.invoice.reason.recharge', locale),
1124
1125
  };
1126
+ let invoiceType = t('payment.invoice.reason.payment', locale);
1127
+ if (reason.includes('stake') || reason.includes('recharge')) {
1128
+ invoiceType = reasonMap[reason as keyof typeof reasonMap];
1129
+ }
1125
1130
  if (description?.startsWith('Subscription ') || description?.startsWith('Slash stake')) {
1126
1131
  return {
1127
1132
  description: reasonMap[reason as keyof typeof reasonMap],
1128
1133
  reason: reasonMap[reason as keyof typeof reasonMap],
1129
- type: reason.includes('stake')
1130
- ? reasonMap[reason as keyof typeof reasonMap]
1131
- : t('payment.invoice.reason.payment', locale),
1134
+ type: invoiceType,
1132
1135
  };
1133
1136
  }
1134
1137
  const descMap = {
@@ -1136,12 +1139,12 @@ export function getInvoiceDescriptionAndReason(invoice: TInvoiceExpanded, locale
1136
1139
  'Stake for subscription payment change': t('payment.invoice.reason.stakeForChangePayment', locale),
1137
1140
  'Stake for subscription': t('payment.invoice.reason.staking', locale),
1138
1141
  'Return Subscription staking': t('payment.invoice.reason.returnStake', locale),
1142
+ 'Recharge for subscription': t('payment.invoice.reason.rechargeForSubscription', locale),
1139
1143
  };
1144
+
1140
1145
  return {
1141
1146
  description: descMap[description as keyof typeof descMap] || description,
1142
1147
  reason: reasonMap[reason as keyof typeof reasonMap] || reason,
1143
- type: reason.includes('stake')
1144
- ? reasonMap[reason as keyof typeof reasonMap]
1145
- : t('payment.invoice.reason.payment', locale),
1148
+ type: invoiceType,
1146
1149
  };
1147
1150
  }
@@ -326,6 +326,8 @@ export default flat({
326
326
  returnStake: 'Return stake',
327
327
  stakeForChangePlan: 'Subscription plan update',
328
328
  stakeForChangePayment: 'Subscription payment method update',
329
+ recharge: 'Recharge',
330
+ rechargeForSubscription: 'Subscription recharge',
329
331
  },
330
332
  },
331
333
  },
@@ -316,6 +316,8 @@ export default flat({
316
316
  returnStake: '退押金',
317
317
  stakeForChangePlan: '订阅套餐更新',
318
318
  stakeForChangePayment: '订阅支付方式更新',
319
+ recharge: '充值',
320
+ rechargeForSubscription: '订阅充值',
319
321
  },
320
322
  },
321
323
  },
@@ -7,6 +7,7 @@ import { useEffect } from 'react';
7
7
  import Switch from '../components/switch-button';
8
8
  import { formatAmountPrecisionLimit } from '../libs/util';
9
9
  import { usePaymentContext } from '../contexts/payment';
10
+ import { usePreventWheel } from '../hooks/scroll';
10
11
 
11
12
  export default function ProductDonation({
12
13
  item,
@@ -21,6 +22,7 @@ export default function ProductDonation({
21
22
  }) {
22
23
  const { t, locale } = useLocaleContext();
23
24
  const { setPayable } = usePaymentContext();
25
+ usePreventWheel();
24
26
  const preset = settings.amount.preset || settings.amount.presets?.[0] || '0';
25
27
  const [state, setState] = useSetState({
26
28
  selected: preset,