@blocklet/payment-react 1.20.5 → 1.20.6

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.
Files changed (55) hide show
  1. package/.aigne/doc-smith/config.yaml +2 -2
  2. package/.aigne/doc-smith/upload-cache.yaml +381 -0
  3. package/docs/components-business-auto-topup.md +128 -179
  4. package/docs/components-business-overdue-invoice-payment.md +108 -143
  5. package/docs/components-business-resume-subscription.md +117 -104
  6. package/docs/components-business.md +12 -36
  7. package/docs/components-checkout-checkout-donate.md +209 -149
  8. package/docs/components-checkout-checkout-form.md +115 -136
  9. package/docs/components-checkout-checkout-table.md +92 -172
  10. package/docs/components-checkout.md +43 -109
  11. package/docs/components-history-credit-grants-list.md +45 -70
  12. package/docs/components-history-credit-transactions-list.md +57 -67
  13. package/docs/components-history-invoice-list.md +58 -52
  14. package/docs/components-history-payment-list.md +19 -40
  15. package/docs/components-history.md +42 -67
  16. package/docs/components-ui-form-elements-address-form.md +37 -65
  17. package/docs/components-ui-form-elements-country-select.md +80 -59
  18. package/docs/components-ui-form-elements-currency-selector.md +57 -73
  19. package/docs/components-ui-form-elements-phone-input.md +90 -112
  20. package/docs/components-ui-form-elements.md +46 -80
  21. package/docs/components-ui-payment-summary.md +71 -119
  22. package/docs/components-ui-pricing-table.md +117 -204
  23. package/docs/components-ui.md +59 -32
  24. package/docs/components.md +89 -62
  25. package/docs/getting-started.md +36 -63
  26. package/docs/guides-theming.md +122 -84
  27. package/docs/guides-utilities.md +107 -145
  28. package/docs/guides.md +7 -84
  29. package/docs/hooks-use-mobile.md +50 -36
  30. package/docs/hooks-use-subscription.md +72 -89
  31. package/docs/hooks.md +12 -82
  32. package/docs/overview.md +45 -52
  33. package/docs/providers-donate-provider.md +73 -95
  34. package/docs/providers-payment-provider.md +115 -169
  35. package/docs/providers.md +27 -86
  36. package/es/locales/en.js +7 -0
  37. package/es/locales/zh.js +8 -1
  38. package/es/payment/index.js +3 -0
  39. package/es/payment/progress-item.d.ts +12 -0
  40. package/es/payment/progress-item.js +78 -0
  41. package/es/payment/success.d.ts +4 -1
  42. package/es/payment/success.js +55 -3
  43. package/lib/locales/en.js +7 -0
  44. package/lib/locales/zh.js +8 -1
  45. package/lib/payment/index.js +3 -0
  46. package/lib/payment/progress-item.d.ts +12 -0
  47. package/lib/payment/progress-item.js +107 -0
  48. package/lib/payment/success.d.ts +4 -1
  49. package/lib/payment/success.js +59 -3
  50. package/package.json +3 -3
  51. package/src/locales/en.tsx +7 -0
  52. package/src/locales/zh.tsx +8 -1
  53. package/src/payment/index.tsx +6 -0
  54. package/src/payment/progress-item.tsx +107 -0
  55. package/src/payment/success.tsx +88 -3
@@ -1,105 +1,126 @@
1
1
  # CountrySelect
2
2
 
3
- The `CountrySelect` component is a dropdown for selecting a country, featuring an integrated search filter, country flags, and a responsive design that adapts to desktop and mobile views. It is built to work seamlessly within forms managed by `react-hook-form` and must be wrapped in a `FormProvider` to function correctly.
3
+ The `CountrySelect` component provides a user-friendly dropdown for selecting a country. It features a searchable list, displays country flags, and offers a responsive design that adapts to both desktop and mobile viewports. It is designed to be used within a form managed by `react-hook-form`.
4
4
 
5
5
  ## Props
6
6
 
7
- The component accepts the following props:
8
-
9
- | Prop | Type | Required | Default | Description |
10
- | :--- | :--- | :--- | :--- | :--- |
11
- | `value` | `CountryIso2` | Yes | | The ISO2 code of the currently selected country (e.g., 'us'). |
12
- | `onChange` | `(value: CountryIso2) => void` | Yes | | Callback function invoked when a country is selected. |
13
- | `name` | `string` | Yes | | The name of the input field, used for integration with `react-hook-form`. |
14
- | `sx` | `SxProps` | No | `{}` | Custom styles applied to the root element using the MUI `sx` prop. |
15
- | `showDialCode` | `boolean` | No | `false` | If `true`, the country's dial code is displayed next to its name in the list. |
7
+ | Prop | Type | Description | Required | Default |
8
+ |--------------|----------------------------------|---------------------------------------------------------------------------------------------|----------|---------|
9
+ | `value` | `CountryIso2` | The ISO2 code of the selected country (e.g., 'us'). | Yes | |
10
+ | `onChange` | `(value: CountryIso2) => void` | A callback function that is invoked when a country is selected. | Yes | |
11
+ | `name` | `string` | The name attribute for the input, used for integration with `react-hook-form`. | Yes | |
12
+ | `sx` | `SxProps` | Custom styles to be applied to the root element. | No | `{}` |
13
+ | `showDialCode` | `boolean` | If `true`, the country's dial code will be displayed alongside its name in the list. | No | `false` |
16
14
 
17
15
  ## Basic Usage
18
16
 
19
- Here is a basic example of how to use the `CountrySelect` component. It must be wrapped in a `FormProvider` from `react-hook-form` because it internally uses `useFormContext` to manage form state.
17
+ To use the `CountrySelect` component, you must wrap it in a `FormProvider` from `react-hook-form`. The component's state should be managed through the form context.
20
18
 
21
- ```jsx
22
- import { useState } from 'react';
19
+ ```tsx Basic CountrySelect Example icon=logos:react
23
20
  import { FormProvider, useForm } from 'react-hook-form';
24
- import { CountrySelect } from '@blocklet/payment-react';
25
- import { Box } from '@mui/material';
21
+ import { Box, Button, Typography } from '@mui/material';
22
+ import CountrySelect from '@blocklet/payment-react/src/components/country-select'; // Adjust path as needed
26
23
  import type { CountryIso2 } from 'react-international-phone';
27
24
 
28
- function BasicCountrySelect() {
29
- const [selectedCountry, setSelectedCountry] = useState<CountryIso2>('us');
30
- const methods = useForm({
25
+ export default function BasicCountrySelect() {
26
+ const methods = useForm<{ country: CountryIso2 }>({
31
27
  defaultValues: {
32
28
  country: 'us',
33
29
  },
34
30
  });
35
31
 
36
- const handleCountryChange = (country: CountryIso2) => {
37
- setSelectedCountry(country);
38
- console.log('Selected Country:', country);
32
+ const { handleSubmit, watch, setValue } = methods;
33
+ const countryValue = watch('country'); // Watch for changes to update the controlled component
34
+
35
+ const onSubmit = (data: { country: CountryIso2 }) => {
36
+ alert(`Form submitted with country: ${data.country}`);
39
37
  };
40
38
 
41
39
  return (
42
40
  <FormProvider {...methods}>
43
- <Box sx={{ maxWidth: 300 }}>
44
- <CountrySelect
45
- name="country"
46
- value={selectedCountry}
47
- onChange={handleCountryChange}
48
- />
49
- </Box>
41
+ <form onSubmit={handleSubmit(onSubmit)}>
42
+ <Box sx={{ display: 'flex', flexDirection: 'column', gap: 2, maxWidth: 400 }}>
43
+ <Typography variant="h6">Select Your Country</Typography>
44
+ <CountrySelect
45
+ name="country"
46
+ value={countryValue}
47
+ onChange={(newCountry) => {
48
+ setValue('country', newCountry, { shouldValidate: true });
49
+ }}
50
+ />
51
+ <Button type="submit" variant="contained">
52
+ Submit
53
+ </Button>
54
+ <Typography>
55
+ Current form value: {countryValue}
56
+ </Typography>
57
+ </Box>
58
+ </form>
50
59
  </FormProvider>
51
60
  );
52
61
  }
53
-
54
- export default BasicCountrySelect;
55
62
  ```
56
63
 
57
64
  ## Features
58
65
 
59
- ### Search and Filtering
60
- The dropdown includes a search field at the top of the list, allowing users to filter countries by name, two-letter ISO code, or dial code (e.g., `+1`). This makes it easy to find a specific country in the extensive list.
66
+ ### Search and Filter
67
+ The component includes a search bar at the top of the dropdown list, allowing users to quickly find a country by its name, ISO2 code, or dial code. For example, searching for "+1" will show the United States and Canada.
61
68
 
62
- ### Responsive Design
63
- The component automatically adapts its UI based on the viewport. On desktop devices, it renders as a traditional dropdown menu. On mobile devices, it presents a full-width bottom-sheet dialog, providing a more native and touch-friendly user experience.
69
+ ### Responsive UI
70
+ On desktop devices, `CountrySelect` renders as a standard dropdown menu. On mobile, it transforms into a full-width dialog that slides up from the bottom of the screen, providing an optimized user experience on smaller devices.
64
71
 
65
72
  ### Keyboard Accessibility
66
- `CountrySelect` is fully navigable using the keyboard, enhancing accessibility:
67
- - **ArrowDown / ArrowUp / Tab**: Navigate through the list of countries.
68
- - **Enter**: Select the currently focused country and close the dropdown.
69
- - **Escape**: Close the dropdown without making a selection.
73
+ `CountrySelect` supports full keyboard navigation. Users can use the `ArrowUp` and `ArrowDown` keys to navigate the list, `Enter` to select a country, and `Escape` to close the dropdown. `Tab` and `Shift+Tab` also cycle through the list items.
70
74
 
71
- ## Customization
75
+ ### Form Integration
76
+ Designed to work seamlessly with `react-hook-form`. It automatically updates the form state when a selection is made, requiring it to be wrapped within a `FormProvider`.
72
77
 
73
- ### Displaying the Dial Code
78
+ ## Advanced Usage
74
79
 
75
- To display the phone dial code next to each country, set the `showDialCode` prop to `true`. This is particularly useful when pairing `CountrySelect` with the [`PhoneInput`](./components-ui-form-elements-phone-input.md) component to build a complete international phone number input.
80
+ ### Displaying Dial Codes
76
81
 
77
- ```jsx
78
- import { useState } from 'react';
82
+ You can display the telephone dial code for each country in the list by setting the `showDialCode` prop to `true`.
83
+
84
+ ```tsx CountrySelect with Dial Code icon=logos:react
79
85
  import { FormProvider, useForm } from 'react-hook-form';
80
- import { CountrySelect } from '@blocklet/payment-react';
81
- import { Box } from '@mui/material';
86
+ import { Box, Button } from '@mui/material';
87
+ import CountrySelect from '@blocklet/payment-react/src/components/country-select'; // Adjust path as needed
82
88
  import type { CountryIso2 } from 'react-international-phone';
83
89
 
84
- function CountrySelectWithDialCode() {
85
- const [country, setCountry] = useState<CountryIso2>('us');
86
- const methods = useForm({ defaultValues: { country: 'us' } });
90
+ export default function CountrySelectWithDialCode() {
91
+ const methods = useForm<{ country: CountryIso2 }>({
92
+ defaultValues: {
93
+ country: 'gb',
94
+ },
95
+ });
96
+
97
+ const { handleSubmit, watch, setValue } = methods;
98
+ const countryValue = watch('country');
99
+
100
+ const onSubmit = (data: { country: CountryIso2 }) => {
101
+ alert(`Form submitted with country: ${data.country}`);
102
+ };
87
103
 
88
104
  return (
89
105
  <FormProvider {...methods}>
90
- <Box sx={{ maxWidth: 300 }}>
91
- <CountrySelect
92
- name="country"
93
- value={country}
94
- onChange={setCountry}
95
- showDialCode={true}
96
- />
97
- </Box>
106
+ <form onSubmit={handleSubmit(onSubmit)}>
107
+ <Box sx={{ display: 'flex', flexDirection: 'column', gap: 2, maxWidth: 400 }}>
108
+ <CountrySelect
109
+ name="country"
110
+ value={countryValue}
111
+ onChange={(newCountry) => setValue('country', newCountry)}
112
+ showDialCode={true}
113
+ />
114
+ <Button type="submit" variant="contained">
115
+ Submit
116
+ </Button>
117
+ </Box>
118
+ </form>
98
119
  </FormProvider>
99
120
  );
100
121
  }
101
-
102
- export default CountrySelectWithDialCode;
103
122
  ```
104
123
 
105
- By combining these features, the `CountrySelect` component offers a robust and user-friendly solution for country input fields in any form.
124
+ ## Next Steps
125
+
126
+ This component is a building block for more complex form elements. See how it's integrated into the [PhoneInput](./components-ui-form-elements-phone-input.md) component to create a complete international phone number field.
@@ -1,124 +1,108 @@
1
1
  # CurrencySelector
2
2
 
3
- The `CurrencySelector` component renders an interactive list of available payment currencies. Each currency is displayed as a distinct card, showing its logo, symbol, and the associated payment network, allowing users to easily choose their preferred option.
4
-
5
- This component is a form element designed to be integrated into broader payment flows, such as within a `CheckoutForm` or a custom payment UI.
3
+ The `CurrencySelector` component provides a user-friendly interface for selecting a payment currency from a list of available options. It displays each currency with its logo, symbol, and associated payment method, making it easy for users to make a choice. This component is typically used within a larger payment or checkout form.
6
4
 
7
5
  ## Props
8
6
 
9
- The `CurrencySelector` component accepts the following props:
10
-
11
- | Prop | Type | Description | Required |
12
- | :----------- | :----------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------- | :------- |
13
- | `value` | `string` | The `id` of the currently selected currency. This prop controls which currency card is highlighted with a checked radio button. | Yes |
14
- | `currencies` | `TPaymentCurrency[]` | An array of currency objects to display as selectable options. See the `TPaymentCurrency` interface below for the required object structure. | Yes |
15
- | `onChange` | `(currencyId: string, methodId?: string) => void` | A callback function that fires when the user selects a currency. It receives the `id` of the chosen currency and its associated method. | Yes |
16
-
17
- ### `TPaymentCurrency` Interface
18
-
19
- The `currencies` prop requires an array of objects, where each object represents a selectable currency and must conform to the following structure:
20
-
21
- ```typescript
22
- interface TPaymentCurrency {
23
- id: string; // Unique identifier for the currency option
24
- logo: string; // URL to the currency or payment method logo
25
- name: string; // The display name of the currency
26
- symbol: string; // The currency symbol or ticker (e.g., "USDT", "BTC")
27
- method: {
28
- id: string; // ID of the associated payment method or network
29
- name: string; // Display name of the associated payment method or network (e.g., "TRON Network")
7
+ | Prop | Type | Description | Required |
8
+ | :----------- | :------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------- | :------- |
9
+ | `value` | `string` | The ID of the currently selected currency. This should be managed by the parent component's state. | Yes |
10
+ | `currencies` | `TPaymentCurrency[]` | An array of available currency objects to display as selectable options. | Yes |
11
+ | `onChange` | `(currencyId: string, methodId?: string) => void` | Callback function that is executed when a user selects a currency. It receives the new currency ID and the optional associated payment method ID. | Yes |
12
+
13
+ ### TPaymentCurrency Type
14
+
15
+ The `currencies` prop expects an array of objects with the following structure:
16
+
17
+ ```typescript TPaymentCurrency icon=mdi:code-json
18
+ export type TPaymentCurrency = {
19
+ id: string; // Unique identifier for the currency option
20
+ logo: string; // URL for the currency/method logo
21
+ name: string; // Display name for the currency
22
+ symbol: string; // Currency symbol (e.g., '$', '€')
23
+ method: { // Associated payment method
24
+ id: string;
25
+ name: string;
30
26
  };
31
- }
27
+ };
32
28
  ```
33
29
 
34
- ## Example Usage
30
+ ## Usage Example
35
31
 
36
- Below is an example of how to implement the `CurrencySelector`. It uses the `useState` hook to manage the user's selection and updates the UI accordingly.
32
+ Here is a basic example of how to implement the `CurrencySelector` component. You need to manage the selected currency's state in the parent component and provide a list of available currencies.
37
33
 
38
- Note that while `CurrencySelector` is a UI component, it's designed to function within a payment context. Therefore, it must be wrapped in `PaymentProvider`. For details on how to set up your session context, please refer to the [PaymentProvider documentation](./providers-payment-provider.md).
34
+ Note that `@blocklet/payment-react` components are designed to work within a `PaymentProvider`, which supplies the necessary theme and context. Ensure your component is wrapped accordingly.
39
35
 
40
- ```tsx
36
+ ```jsx MyCurrencySelectorComponent.tsx icon=logos:react
41
37
  import React, { useState } from 'react';
42
- import { CurrencySelector, PaymentProvider } from '@blocklet/payment-react';
43
- import { useSessionContext } from '../hooks/session'; // Assuming a local session context hook
38
+ import { PaymentProvider } from '@blocklet/payment-react';
39
+ import { CurrencySelector } from '@blocklet/payment-react/components';
44
40
  import type { TPaymentCurrency } from '@blocklet/payment-types';
45
- import { Box, Typography, CircularProgress } from '@mui/material';
41
+ import { useSessionContext } from '@/hooks/session-context'; // Adjust this import path to your project structure
46
42
 
47
- // Mock data representing available currencies
43
+ // Mock data for demonstration purposes
48
44
  const mockCurrencies: TPaymentCurrency[] = [
49
45
  {
50
- id: 'usdt_tron',
51
- logo: 'https://cdn.jsdelivr.net/gh/atomiclabs/cryptocurrency-icons@1a63530be6e374711a8554f31b17e4cb92c25661/svg/color/usdt.svg',
52
- name: 'Tether',
53
- symbol: 'USDT',
54
- method: {
55
- id: 'tron_network',
56
- name: 'TRON Network',
57
- },
58
- },
59
- {
60
- id: 'btc_native',
61
- logo: 'https://cdn.jsdelivr.net/gh/atomiclabs/cryptocurrency-icons@1a63530be6e374711a8554f31b17e4cb92c25661/svg/color/btc.svg',
62
- name: 'Bitcoin',
63
- symbol: 'BTC',
46
+ id: 'usd_xxxxxx',
47
+ logo: 'https://path-to-your/usd-logo.png',
48
+ name: 'US Dollar',
49
+ symbol: 'USD',
64
50
  method: {
65
- id: 'bitcoin_network',
66
- name: 'Bitcoin Network',
51
+ id: 'stripe_yyyyy',
52
+ name: 'Stripe',
67
53
  },
68
54
  },
69
55
  {
70
- id: 'eth_native',
71
- logo: 'https://cdn.jsdelivr.net/gh/atomiclabs/cryptocurrency-icons@1a63530be6e374711a8554f31b17e4cb92c25661/svg/color/eth.svg',
56
+ id: 'eth_zzzzzz',
57
+ logo: 'https://path-to-your/eth-logo.png',
72
58
  name: 'Ethereum',
73
59
  symbol: 'ETH',
74
60
  method: {
75
- id: 'ethereum_network',
76
- name: 'Ethereum Network',
61
+ id: 'crypto_aaaaa',
62
+ name: 'Crypto Payment',
77
63
  },
78
64
  },
79
65
  ];
80
66
 
81
- function CurrencySelectorExample() {
82
- const [selectedCurrencyId, setSelectedCurrencyId] = useState<string>('usdt_tron');
67
+ function MyCurrencySelectorComponent() {
68
+ const [selectedCurrency, setSelectedCurrency] = useState<string>(mockCurrencies[0].id);
83
69
 
84
70
  const handleCurrencyChange = (currencyId: string, methodId?: string) => {
85
- console.log(`Selected Currency ID: ${currencyId}, Method ID: ${methodId}`);
86
- setSelectedCurrencyId(currencyId);
71
+ console.log(`Selected currency: ${currencyId}, Method: ${methodId}`);
72
+ setSelectedCurrency(currencyId);
87
73
  };
88
74
 
89
75
  return (
90
- <Box sx={{ padding: 2, border: '1px solid #ddd', borderRadius: '4px' }}>
91
- <Typography variant="h6" gutterBottom>
92
- Select Your Payment Currency
93
- </Typography>
76
+ <div>
77
+ <h3>Select a Currency</h3>
94
78
  <CurrencySelector
95
- value={selectedCurrencyId}
79
+ value={selectedCurrency}
96
80
  currencies={mockCurrencies}
97
81
  onChange={handleCurrencyChange}
98
82
  />
99
- <Typography sx={{ marginTop: '20px' }}>
100
- Current Selection: <strong>{selectedCurrencyId}</strong>
101
- </Typography>
102
- </Box>
83
+ </div>
103
84
  );
104
85
  }
105
86
 
106
- // Most components from this library must be wrapped in PaymentProvider.
107
- export default function MyPaymentPage() {
87
+ // Your App's entry point
88
+ export default function App() {
108
89
  const { session, connectApi } = useSessionContext();
109
90
 
110
- // PaymentProvider requires session and connectApi.
111
- // Ensure these are available before rendering.
112
91
  if (!session || !connectApi) {
113
- return <CircularProgress />;
92
+ return <div>Loading...</div>;
114
93
  }
115
94
 
116
95
  return (
117
96
  <PaymentProvider session={session} connectApi={connectApi}>
118
- <CurrencySelectorExample />
97
+ <MyCurrencySelectorComponent />
119
98
  </PaymentProvider>
120
99
  );
121
100
  }
122
101
  ```
123
102
 
124
- In this example, `selectedCurrencyId` holds the state for the chosen currency. When a user clicks a currency card, the `handleCurrencyChange` function is triggered, which updates the state. The `CurrencySelector` re-renders to visually reflect the new selection.
103
+ In this example:
104
+ 1. We define a `mockCurrencies` array that follows the `TPaymentCurrency` structure.
105
+ 2. The `MyCurrencySelectorComponent` uses the `useState` hook to keep track of the `selectedCurrency`.
106
+ 3. The `handleCurrencyChange` function updates the state when a new currency is selected and logs the new values.
107
+ 4. The `CurrencySelector` is a controlled component, with its `value` and `onChange` props managed by the parent's state.
108
+ 5. The entire component is wrapped in `PaymentProvider` to ensure proper rendering and access to the Material-UI theme used for styling.
@@ -1,160 +1,138 @@
1
1
  # PhoneInput
2
2
 
3
- The `PhoneInput` component provides a user-friendly field for entering international phone numbers. It integrates seamlessly with `react-hook-form` and includes a built-in, searchable country selector. It is designed to work in tandem with a country selection field, ensuring data consistency across your form.
3
+ The `PhoneInput` component provides a user-friendly, international phone number input field. It integrates seamlessly with `react-hook-form` and includes a searchable country selector with flags and dial codes, automatic number formatting, and powerful validation powered by `google-libphonenumber`.
4
4
 
5
- ### Features
5
+ This component is designed to work in tandem with other form elements, such as the [`CountrySelect`](./components-ui-form-elements-country-select.md) or [`AddressForm`](./components-ui-form-elements-address-form.md), by synchronizing its selected country with a shared form field.
6
6
 
7
- - **React Hook Form Integration**: Built to work directly with `useFormContext` for easy state management and validation.
8
- - **Interactive Country Selector**: Includes the `CountrySelect` component as a prefix, offering country flags, names, and a search field.
9
- - **Two-Way Country Synchronization**: Automatically syncs the selected country with another form field (e.g., a billing address country field).
10
- - **Asynchronous Validation**: Leverages `google-libphonenumber` for robust validation without blocking the main thread on initial load.
11
- - **Responsive Design**: The country selector adapts to the viewport, using a dropdown on desktop and a dialog on mobile devices.
7
+ ## How It Works
8
+
9
+ The component leverages the `react-international-phone` library for its core functionality and wraps it to provide deep integration with `react-hook-form` and Material-UI. When a user selects a country or types a number, the component updates the corresponding fields in the form state. It also automatically updates its country selection when another component modifies the linked country field in the form.
10
+
11
+ ```d2
12
+ direction: down
13
+
14
+ User: {
15
+ shape: c4-person
16
+ }
17
+
18
+ Form-State: {
19
+ label: "React Hook Form State"
20
+ shape: cylinder
21
+ }
22
+
23
+ PhoneInput-Component: {
24
+ label: "PhoneInput Component"
25
+
26
+ CountrySelect: {
27
+ label: "CountrySelect"
28
+ }
29
+
30
+ Phone-Field: {
31
+ label: "Phone Text Field"
32
+ }
33
+ }
34
+
35
+ User -> PhoneInput-Component.CountrySelect: "1. Selects a country"
36
+ PhoneInput-Component.CountrySelect -> Form-State: "2. Updates 'countryFieldName' value"
37
+
38
+ User -> PhoneInput-Component.Phone-Field: "3. Enters phone number"
39
+ PhoneInput-Component.Phone-Field -> Form-State: "4. Updates 'name' value (phone number)"
40
+
41
+ Form-State -> PhoneInput-Component: "5. Synchronizes state"
42
+ ```
12
43
 
13
44
  ## Props
14
45
 
15
- The `PhoneInput` component accepts all standard props from Material-UI's `TextField` in addition to its own specific props.
46
+ The `PhoneInput` component accepts all standard Material-UI `TextField` props, in addition to the following specific props:
16
47
 
17
- | Prop | Type | Description | Default |
18
- | --- | --- | --- | --- |
19
- | `name` | `string` | **Required.** The name of the field to be registered with `react-hook-form`. | - |
20
- | `countryFieldName` | `string` | The name of the form field that stores the selected country's ISO 3166-1 alpha-2 code. The component synchronizes with this field. | `'billing_address.country'` |
21
- | `...rest` | `TextFieldProps` | Any other props accepted by the Material-UI `TextField` component, such as `label`, `placeholder`, `required`, `helperText`, and `error`. | - |
48
+ | Prop | Type | Required | Default | Description |
49
+ |---|---|---|---|---|
50
+ | `name` | `string` | Yes | - | The name of the field to register with `react-hook-form` for the phone number. |
51
+ | `countryFieldName` | `string` | No | `'billing_address.country'` | The name of the form field that stores the selected country's ISO2 code. This allows the phone input's country to be synchronized with other parts of your form, such as an address component. |
22
52
 
23
53
  ## Usage
24
54
 
25
- The `PhoneInput` must be wrapped in a `FormProvider` from `react-hook-form`. While it is often used within payment forms that require `PaymentProvider`, the component itself does not need it. For details on setting up the payment context, please refer to the [PaymentProvider documentation](./providers-payment-provider.md).
26
-
27
- ### Basic Example
55
+ To use the `PhoneInput` component, you must wrap it in a `FormProvider` from `react-hook-form`. The following example demonstrates a basic implementation with validation.
56
+
57
+ First, you need the asynchronous validation function provided by the library.
58
+
59
+ ```javascript phone-validator.js icon=logos:javascript
60
+ // src/libs/phone-validator.js
61
+ import { getPhoneUtil } from '@blocklet/payment-react/libs/phone-validator';
62
+
63
+ export const validatePhoneNumber = async (phoneNumber) => {
64
+ if (!phoneNumber) return true;
65
+ try {
66
+ const util = await getPhoneUtil();
67
+ const parsed = util.parseAndKeepRawInput(phoneNumber);
68
+ return util.isValidNumber(parsed);
69
+ } catch (err) {
70
+ console.error('Phone validation error:', err);
71
+ // Fallback to a simple regex if lib fails to load
72
+ const pattern = /^[+]?[(]?[0-9]{3}[)]?[-\s.]?[0-9]{3}[-\s.]?[0-9]{4,6}$/im;
73
+ return pattern.test(phoneNumber) || 'Invalid phone number';
74
+ }
75
+ };
76
+ ```
28
77
 
29
- This example demonstrates a complete form with a `PhoneInput` and a separate `CountrySelect` for the billing address. The two are synchronized via the `countryFieldName` prop.
78
+ Now, you can use this validator in your form component.
30
79
 
31
- ```tsx
80
+ ```jsx MyPaymentForm.tsx icon=logos:react
32
81
  import { FormProvider, useForm } from 'react-hook-form';
33
- import { Button, Stack } from '@mui/material';
34
- import PhoneInput from '@blocklet/payment-react/components/ui/form-elements/phone-input';
35
- import CountrySelect from '@blocklet/payment-react/components/ui/form-elements/country-select';
36
- import { validatePhoneNumber } from '@blocklet/payment-react/libs/phone-validator';
82
+ import { Button, Box } from '@mui/material';
83
+ import { PhoneInput } from '@blocklet/payment-react';
84
+ import { validatePhoneNumber } from '../libs/phone-validator'; // Adjust path as needed
37
85
 
38
- export default function PhoneForm() {
86
+ export default function MyPaymentForm() {
39
87
  const methods = useForm({
40
- mode: 'onBlur', // Validate on blur
88
+ mode: 'onBlur',
41
89
  defaultValues: {
42
90
  phone: '',
43
91
  'billing_address.country': 'us', // Default country
44
92
  },
45
93
  });
46
94
 
47
- const { handleSubmit, formState: { errors }, watch, setValue, register } = methods;
48
-
49
95
  const onSubmit = (data) => {
50
96
  alert(JSON.stringify(data, null, 2));
51
97
  };
52
98
 
53
99
  return (
54
100
  <FormProvider {...methods}>
55
- <form onSubmit={handleSubmit(onSubmit)}>
56
- <Stack spacing={2}>
57
- <CountrySelect
58
- name="billing_address.country"
59
- value={watch('billing_address.country')}
60
- onChange={(country) => setValue('billing_address.country', country, { shouldValidate: true })}
61
- />
101
+ <form onSubmit={methods.handleSubmit(onSubmit)}>
102
+ <Box display="flex" flexDirection="column" gap={2}>
62
103
  <PhoneInput
63
104
  label="Phone Number"
64
105
  name="phone"
65
- required
66
- {...register('phone', {
67
- required: 'Phone number is required.',
106
+ // Link to the country field in the form state
107
+ countryFieldName="billing_address.country"
108
+ // Add validation rules
109
+ rules={{
68
110
  validate: async (value) => {
69
111
  const isValid = await validatePhoneNumber(value);
70
- return isValid || 'Please enter a valid phone number';
112
+ return isValid || 'Please enter a valid phone number.';
71
113
  },
72
- })}
73
- error={!!errors.phone}
74
- helperText={errors.phone?.message?.toString()}
114
+ }}
115
+ fullWidth
75
116
  />
76
117
  <Button type="submit" variant="contained">
77
118
  Submit
78
119
  </Button>
79
- </Stack>
120
+ </Box>
80
121
  </form>
81
122
  </FormProvider>
82
123
  );
83
124
  }
84
125
  ```
85
126
 
86
- ## Country Synchronization
87
-
88
- The key feature of `PhoneInput` is its ability to synchronize with an external country field. This is particularly useful in forms like an [AddressForm](./components-ui-form-elements-address-form.md) where the phone number's country should match the billing address country.
89
-
90
- - **Default Behavior**: By default, it listens to and updates a form field named `billing_address.country`.
91
- - **Customization**: You can specify a different field name by passing the `countryFieldName` prop.
127
+ ### Explanation
92
128
 
93
- This two-way synchronization works as follows:
94
- 1. When the user selects a country in an external `CountrySelect` component, the flag and dial code inside the `PhoneInput` update automatically.
95
- 2. When the user changes the country from the dropdown within the `PhoneInput`, the value of the `countryFieldName` field is also updated, ensuring the entire form state remains consistent.
129
+ 1. **`FormProvider`**: The entire form, including the `PhoneInput`, is wrapped in a `FormProvider` to provide the necessary form context.
130
+ 2. **`name="phone"`**: This registers the input field under the name `phone` in the form data.
131
+ 3. **`countryFieldName="billing_address.country"`**: This tells `PhoneInput` to both read from and write to the `billing_address.country` field in the form's state. This is how it stays synchronized. The default value for this field is set to `'us'` in `useForm`.
132
+ 4. **`rules`**: We pass an asynchronous `validate` function to the `rules` prop. `react-hook-form` will await this function to resolve during validation. The `validatePhoneNumber` utility is called to perform the check.
96
133
 
97
- The following diagram illustrates this interaction:
134
+ ## Integration with AddressForm
98
135
 
99
- ```d2
100
- direction: down
101
-
102
- User: "User"
103
-
104
- UI: {
105
- shape: package
106
- "PhoneInput": {
107
- "Internal CountrySelect": "Country selector inside phone input"
108
- }
109
- "External CountrySelect": "Separate country selector for address"
110
- }
111
-
112
- FormState: "react-hook-form State" {
113
- shape: cylinder
114
- phone: "Phone number value"
115
- "billing_address.country": "Country ISO code"
116
- }
117
-
118
- User -> UI."External CountrySelect": "Selects 'Canada'"
119
- UI."External CountrySelect" -> FormState."billing_address.country": "Updates state to 'ca'"
120
- FormState."billing_address.country" -> UI."PhoneInput"."Internal CountrySelect": "Syncs flag and dial code to Canada"
121
-
122
- User -> UI."PhoneInput"."Internal CountrySelect": "Selects 'UK'"
123
- UI."PhoneInput"."Internal CountrySelect" -> FormState."billing_address.country": "Updates state to 'gb'"
124
- FormState."billing_address.country" -> UI."External CountrySelect": "Syncs selection to UK"
125
- ```
126
-
127
- ## Validation
128
-
129
- Phone number validation is handled by the `validatePhoneNumber` utility. This function asynchronously loads the `google-libphonenumber` library to perform validation without impacting your application's initial load time. If the library fails to load, it falls back to a basic regex pattern for validation.
130
-
131
- To apply validation, use the `validate` function in the rules object when registering the field with `react-hook-form`, as shown in the example above.
132
-
133
- ## Helper Utilities
134
-
135
- ### `formatPhone`
136
-
137
- The library also exports a `formatPhone` utility function that can be used to standardize a phone number into the E.164 international format (e.g., `+12125552368`). This is useful for normalizing data before sending it to your backend.
138
-
139
- **Parameters**
140
-
141
- | Parameter | Type | Description | Default |
142
- | --- | --- | --- | --- |
143
- | `phoneNumber` | `string` | The original phone number string. | - |
144
- | `defaultCountry` | `string` | The default country code (ISO 3166-1 alpha-2) to use if the number is ambiguous. | `'US'` |
145
-
146
- **Example**
147
-
148
- ```javascript
149
- import { formatPhone } from '@blocklet/payment-react/libs/phone-validator';
150
-
151
- const rawNumber = '(541) 754-3010';
152
- const formattedNumber = formatPhone(rawNumber, 'US');
153
- // formattedNumber will be '+15417543010'
154
-
155
- const rawUkNumber = '07700 900077';
156
- const formattedUkNumber = formatPhone(rawUkNumber, 'GB');
157
- // formattedUkNumber will be '+447700900077'
158
- ```
136
+ The true power of `PhoneInput` is revealed when used alongside an `AddressForm`. Since both components can be linked to the same country field (`billing_address.country` by default), changing the country in the `AddressForm` will automatically update the flag and dial code in the `PhoneInput`.
159
137
 
160
- This utility is particularly helpful for normalizing phone numbers before submitting them to a backend service.
138
+ For a complete example of this integration, please see the documentation for [`AddressForm`](./components-ui-form-elements-address-form.md).