From 9943628650506f19336334ddd123c186c06252fe Mon Sep 17 00:00:00 2001 From: Ben Keen Date: Sun, 1 Dec 2024 20:07:06 -0800 Subject: [PATCH] more doc; working on MUI integration --- DEVELOPER.md | 22 +- apps/docs/docs/Demos/BasicUsage.mdx | 2 +- .../docs/Demos/features/CountryBlacklist.mdx | 59 ++++ .../Demos/{ => features}/CountryWhitelist.mdx | 2 +- .../CustomDefaultOptionLabels.mdx | 2 +- .../DisableEmptyRegionField.mdx | 2 +- .../docs/Demos/features/DisableFields.mdx | 42 +++ .../Demos/{ => features}/NameIdClassAttrs.mdx | 2 +- .../Demos/{ => features}/NoDefaultOption.mdx | 2 +- .../docs/Demos/features/RegionBlacklist.mdx | 47 +++ .../docs/Demos/features/RegionWhitelist.mdx | 47 +++ .../Demos/{ => features}/ShortcodeLabels.mdx | 2 +- .../Demos/{ => features}/ShortcodeValues.mdx | 2 +- apps/docs/docs/Demos/features/index.mdx | 13 + apps/docs/docs/Demos/index.mdx | 11 + .../docs/Demos/integrations/MaterialUI.mdx | 14 + apps/docs/docusaurus.config.ts | 33 +- apps/docs/package.json | 3 + .../{examples => demos}/BasicUsage.tsx | 0 .../demos/features/CountryBlacklist.tsx | 41 +++ .../features}/CountryWhitelist.tsx | 0 .../features}/CustomDefaultOptionLabels.tsx | 0 .../features}/DisableEmptyRegionField.tsx | 0 .../demos/features/DisableFields.tsx | 25 ++ .../features}/NameIdClassAttrs.tsx | 0 .../features}/NoDefaultOption.tsx | 0 .../demos/features/RegionBlacklist.tsx | 28 ++ .../demos/features/RegionWhitelist.tsx | 28 ++ .../features}/ShortcodeLabels.tsx | 0 .../features}/ShortcodeValues.tsx | 0 .../demos/integrations/MaterialUI.tsx | 56 +++ .../src/CountryDropdown.tsx | 43 +-- .../src/RegionDropdown.tsx | 46 +-- .../src/{helpers.ts => helpers.tsx} | 21 ++ .../src/rcrs.types.ts | 8 +- pnpm-lock.yaml | 334 +++++++++++++++++- 36 files changed, 839 insertions(+), 98 deletions(-) create mode 100644 apps/docs/docs/Demos/features/CountryBlacklist.mdx rename apps/docs/docs/Demos/{ => features}/CountryWhitelist.mdx (92%) rename apps/docs/docs/Demos/{ => features}/CustomDefaultOptionLabels.mdx (92%) rename apps/docs/docs/Demos/{ => features}/DisableEmptyRegionField.mdx (89%) create mode 100644 apps/docs/docs/Demos/features/DisableFields.mdx rename apps/docs/docs/Demos/{ => features}/NameIdClassAttrs.mdx (93%) rename apps/docs/docs/Demos/{ => features}/NoDefaultOption.mdx (92%) create mode 100644 apps/docs/docs/Demos/features/RegionBlacklist.mdx create mode 100644 apps/docs/docs/Demos/features/RegionWhitelist.mdx rename apps/docs/docs/Demos/{ => features}/ShortcodeLabels.mdx (92%) rename apps/docs/docs/Demos/{ => features}/ShortcodeValues.mdx (93%) create mode 100644 apps/docs/docs/Demos/features/index.mdx create mode 100644 apps/docs/docs/Demos/index.mdx create mode 100644 apps/docs/docs/Demos/integrations/MaterialUI.mdx rename apps/docs/src/components/{examples => demos}/BasicUsage.tsx (100%) create mode 100644 apps/docs/src/components/demos/features/CountryBlacklist.tsx rename apps/docs/src/components/{examples => demos/features}/CountryWhitelist.tsx (100%) rename apps/docs/src/components/{examples => demos/features}/CustomDefaultOptionLabels.tsx (100%) rename apps/docs/src/components/{examples => demos/features}/DisableEmptyRegionField.tsx (100%) create mode 100644 apps/docs/src/components/demos/features/DisableFields.tsx rename apps/docs/src/components/{examples => demos/features}/NameIdClassAttrs.tsx (100%) rename apps/docs/src/components/{examples => demos/features}/NoDefaultOption.tsx (100%) create mode 100644 apps/docs/src/components/demos/features/RegionBlacklist.tsx create mode 100644 apps/docs/src/components/demos/features/RegionWhitelist.tsx rename apps/docs/src/components/{examples => demos/features}/ShortcodeLabels.tsx (100%) rename apps/docs/src/components/{examples => demos/features}/ShortcodeValues.tsx (100%) create mode 100644 apps/docs/src/components/demos/integrations/MaterialUI.tsx rename packages/react-country-region-selector/src/{helpers.ts => helpers.tsx} (88%) diff --git a/DEVELOPER.md b/DEVELOPER.md index 83139cd..4fd2064 100644 --- a/DEVELOPER.md +++ b/DEVELOPER.md @@ -1,23 +1,11 @@ ## Developer Notes -This was a very old script which I revamped in Dec 2024 to bring it into the 21st century. Version `4.0.0` now uses Typescript, -modern rollup + babel and jest for testing, plus uses this crazy new thing called "hooks" in React. +This was a very old script which I revamped in Dec 2024 to bring it into the 21st century. Version `4.0.0` now uses Turborepo, Typescript, +modern rollup + babel, jest for testing, modern React, Docusaurus for the doc, eslint and prettier. -The dev environment is functional but needs work. - -`yarn build` -> builds the script. -`yarn` - bootstraps the app. Also builds it. -`yarn test` - runs the tests. Note, if you're altering the code run `yarn && yarn test` (see "Ugly bits" below). - -Ugly bits: - -- The country-regions data set is very large, so to keep bundle sizes down, the rollup build for this script parses - the data and minifies it as much as it can. That's done in via a custom rollup plugin. It works fine, but makes local dev a - bit awkward, e.g. running the tests while tweaking the code. For that you have to run `yarn && yarn test` to first rebuild, - then run the tests - and the tests are ran on the `/dist` result. Awkward. This'd be nice to rethink. -- The `/example` folder is a mess. It's very data, requires a bit of custom setup to bootstrap both the main repo and that. Plus, - just like the tests it won't auto-reload when you change the source: you have to do a rebuild and even restart the server. -- needs linting + prettier set up. +`pnpm install` - bootstraps the monorepo +`npm run dev` - starts dev mode. +`npm run test` - runs the tests ### Notes for next 4.x release diff --git a/apps/docs/docs/Demos/BasicUsage.mdx b/apps/docs/docs/Demos/BasicUsage.mdx index fb509f0..2758962 100644 --- a/apps/docs/docs/Demos/BasicUsage.mdx +++ b/apps/docs/docs/Demos/BasicUsage.mdx @@ -3,7 +3,7 @@ title: Basic Usage sidebar_position: 1 --- -import BasicUsage from '@site/src/components/examples/BasicUsage'; +import BasicUsage from '@site/src/components/demos/BasicUsage'; Let's get started! This first demo shows the basic usage of the script, using minimal settings. As you can see, you're responsible for tracking state, but the tools handles rendering the appropriate countries and regions. diff --git a/apps/docs/docs/Demos/features/CountryBlacklist.mdx b/apps/docs/docs/Demos/features/CountryBlacklist.mdx new file mode 100644 index 0000000..c65d4ed --- /dev/null +++ b/apps/docs/docs/Demos/features/CountryBlacklist.mdx @@ -0,0 +1,59 @@ +--- +title: Country Blacklist +sidebar_position: 9 +--- + +import CountryBlacklist from '@site/src/components/demos/features/CountryBlacklist'; + +The `blacklist` prop omits those countries from the dropdown. This demo hides all countries starting with `A`. +The values are found in the `countryShortCode` value in the [source data package](https://github.com/country-regions/country-region-data/blob/master/data.json). + + + +--- + +```tsx +import React, { useState } from 'react'; +import { CountryDropdown, RegionDropdown } from 'react-country-region-selector'; + +const CountryBlacklist = () => { + const [country, setCountry] = useState(''); + const [region, setRegion] = useState(''); + + return ( + <> + setCountry(val)} + // highlight-start + blacklist={[ + 'AF', + 'AX', + 'AL', + 'DZ', + 'AS', + 'AD', + 'AO', + 'AI', + 'AQ', + 'AG', + 'AR', + 'AM', + 'AW', + 'AU', + 'AT', + 'AZ', + ]} + // highlight-end + /> + setRegion(val)} + /> + + ); +}; + +export default CountryBlacklist; +``` diff --git a/apps/docs/docs/Demos/CountryWhitelist.mdx b/apps/docs/docs/Demos/features/CountryWhitelist.mdx similarity index 92% rename from apps/docs/docs/Demos/CountryWhitelist.mdx rename to apps/docs/docs/Demos/features/CountryWhitelist.mdx index b44a7b0..82c3446 100644 --- a/apps/docs/docs/Demos/CountryWhitelist.mdx +++ b/apps/docs/docs/Demos/features/CountryWhitelist.mdx @@ -3,7 +3,7 @@ title: Country Whitelist sidebar_position: 8 --- -import CountryWhitelist from '@site/src/components/examples/CountryWhitelist'; +import CountryWhitelist from '@site/src/components/demos/features/CountryWhitelist'; The `whitelist` prop on the CountryDropdown component limits the listed countries to those that you specify. The values are found in the `countryShortCode` value in the [source data package](https://github.com/country-regions/country-region-data/blob/master/data.json). diff --git a/apps/docs/docs/Demos/CustomDefaultOptionLabels.mdx b/apps/docs/docs/Demos/features/CustomDefaultOptionLabels.mdx similarity index 92% rename from apps/docs/docs/Demos/CustomDefaultOptionLabels.mdx rename to apps/docs/docs/Demos/features/CustomDefaultOptionLabels.mdx index b228aa4..3fb1b93 100644 --- a/apps/docs/docs/Demos/CustomDefaultOptionLabels.mdx +++ b/apps/docs/docs/Demos/features/CustomDefaultOptionLabels.mdx @@ -3,7 +3,7 @@ title: Custom Default Option Labels sidebar_position: 4 --- -import CustomDefaultOptionLabels from '@site/src/components/examples/CustomDefaultOptionLabels'; +import CustomDefaultOptionLabels from '@site/src/components/demos/features/CustomDefaultOptionLabels'; In case you want to localize the strings or just change the defaults, take a look at the following props: diff --git a/apps/docs/docs/Demos/DisableEmptyRegionField.mdx b/apps/docs/docs/Demos/features/DisableEmptyRegionField.mdx similarity index 89% rename from apps/docs/docs/Demos/DisableEmptyRegionField.mdx rename to apps/docs/docs/Demos/features/DisableEmptyRegionField.mdx index 630a50a..0d14965 100644 --- a/apps/docs/docs/Demos/DisableEmptyRegionField.mdx +++ b/apps/docs/docs/Demos/features/DisableEmptyRegionField.mdx @@ -3,7 +3,7 @@ title: Disable Empty Region Field sidebar_position: 3 --- -import DisableEmptyRegionField from '@site/src/components/examples/DisableEmptyRegionField'; +import DisableEmptyRegionField from '@site/src/components/demos/features/DisableEmptyRegionField'; When the user hasn't selected a country, nothing's going to show up in the Region dropdown. If you want to disable it, use the `disableWhenEmpty` prop. diff --git a/apps/docs/docs/Demos/features/DisableFields.mdx b/apps/docs/docs/Demos/features/DisableFields.mdx new file mode 100644 index 0000000..e595e20 --- /dev/null +++ b/apps/docs/docs/Demos/features/DisableFields.mdx @@ -0,0 +1,42 @@ +--- +title: Disable Fields +sidebar_position: 12 +--- + +import DisableFields from '@site/src/components/demos/features/DisableFields'; + +The `disabled` prop can be used on either component. + + + +--- + +```tsx +import React, { useState } from 'react'; +import { CountryDropdown, RegionDropdown } from 'react-country-region-selector'; + +const DisableFields = () => { + const [country, setCountry] = useState(''); + const [region, setRegion] = useState(''); + + return ( + <> + setCountry(val)} + // highlight-next-line + disabled={true} + /> + setRegion(val)} + // highlight-next-line + disabled={true} + /> + + ); +}; + +export default DisableFields; +``` diff --git a/apps/docs/docs/Demos/NameIdClassAttrs.mdx b/apps/docs/docs/Demos/features/NameIdClassAttrs.mdx similarity index 93% rename from apps/docs/docs/Demos/NameIdClassAttrs.mdx rename to apps/docs/docs/Demos/features/NameIdClassAttrs.mdx index aa7004c..b24c0ab 100644 --- a/apps/docs/docs/Demos/NameIdClassAttrs.mdx +++ b/apps/docs/docs/Demos/features/NameIdClassAttrs.mdx @@ -3,7 +3,7 @@ title: Name, ID, Class attributes sidebar_position: 5 --- -import NameIdClassAttrs from '@site/src/components/examples/NameIdClassAttrs'; +import NameIdClassAttrs from '@site/src/components/demos/features/NameIdClassAttrs'; This demo shows how to set `name`, `id` and `class` attributes on each component. diff --git a/apps/docs/docs/Demos/NoDefaultOption.mdx b/apps/docs/docs/Demos/features/NoDefaultOption.mdx similarity index 92% rename from apps/docs/docs/Demos/NoDefaultOption.mdx rename to apps/docs/docs/Demos/features/NoDefaultOption.mdx index c7a38ad..650e97e 100644 --- a/apps/docs/docs/Demos/NoDefaultOption.mdx +++ b/apps/docs/docs/Demos/features/NoDefaultOption.mdx @@ -3,7 +3,7 @@ title: No Default Option sidebar_position: 2 --- -import NoDefaultOption from '@site/src/components/examples/NoDefaultOption'; +import NoDefaultOption from '@site/src/components/demos/features/NoDefaultOption'; The `showDefaultOption` boolean prop (default: `true`) can be set on both `CountryDropdown` and `RegionDropdown`. When set to `false` the default "Select Country" / "Select Region" option won't appear. diff --git a/apps/docs/docs/Demos/features/RegionBlacklist.mdx b/apps/docs/docs/Demos/features/RegionBlacklist.mdx new file mode 100644 index 0000000..7c3cdd2 --- /dev/null +++ b/apps/docs/docs/Demos/features/RegionBlacklist.mdx @@ -0,0 +1,47 @@ +--- +title: Region Blacklist +sidebar_position: 11 +--- + +import RegionBlacklist from '@site/src/components/demos/features/RegionBlacklist'; + +The `blacklist` prop on the RegionDropdown copmonent lets you hide specific regions from the list. This hides +Alberta and BC from Canada's regions and Washington and Texas from US. The +values are found in the `countryShortCode` value in the [source data package](https://github.com/country-regions/country-region-data/blob/master/data.json). + + + +--- + +```tsx +import React, { useState } from 'react'; +import { CountryDropdown, RegionDropdown } from 'react-country-region-selector'; + +const RegionBlacklist = () => { + const [country, setCountry] = useState(''); + const [region, setRegion] = useState(''); + + return ( + <> + setCountry(val)} + whitelist={['CA', 'US']} + /> + setRegion(val)} + // highlight-start + blacklist={{ + CA: ['AB', 'BC'], + US: ['WA', 'TX'], + }} + // highlight-end + /> + + ); +}; + +export default RegionBlacklist; +``` diff --git a/apps/docs/docs/Demos/features/RegionWhitelist.mdx b/apps/docs/docs/Demos/features/RegionWhitelist.mdx new file mode 100644 index 0000000..2326d34 --- /dev/null +++ b/apps/docs/docs/Demos/features/RegionWhitelist.mdx @@ -0,0 +1,47 @@ +--- +title: Region Whitelist +sidebar_position: 10 +--- + +import RegionWhitelist from '@site/src/components/demos/features/RegionWhitelist'; + +The `whitelist` prop on the `RegionDropdown` component limits the listed regions to those that you specify. The +values are found in the `countryShortCode` value in the [source data package](https://github.com/country-regions/country-region-data/blob/master/data.json). +Note the order will always be alphabetical. + + + +--- + +```tsx +import React, { useState } from 'react'; +import { CountryDropdown, RegionDropdown } from 'react-country-region-selector'; + +const RegionWhitelist = () => { + const [country, setCountry] = useState(''); + const [region, setRegion] = useState(''); + + return ( + <> + setCountry(val)} + whitelist={['CA', 'US']} + /> + setRegion(val)} + // highlight-start + whitelist={{ + CA: ['AB', 'BC'], + US: ['WA', 'TX'], + }} + // highlight-end + /> + + ); +}; + +export default RegionWhitelist; +``` diff --git a/apps/docs/docs/Demos/ShortcodeLabels.mdx b/apps/docs/docs/Demos/features/ShortcodeLabels.mdx similarity index 92% rename from apps/docs/docs/Demos/ShortcodeLabels.mdx rename to apps/docs/docs/Demos/features/ShortcodeLabels.mdx index 28fd33c..bfbb9e4 100644 --- a/apps/docs/docs/Demos/ShortcodeLabels.mdx +++ b/apps/docs/docs/Demos/features/ShortcodeLabels.mdx @@ -3,7 +3,7 @@ title: Shortcode Labels sidebar_position: 6 --- -import ShortcodeLabels from '@site/src/components/examples/ShortcodeLabels'; +import ShortcodeLabels from '@site/src/components/demos/features/ShortcodeLabels'; The `CountryDropdown` and `RegionDropdown` fields both let you customize the display values for the dropdowns. Either `full` (default) or `short`. You can mix and match. Note that this is purely for the _label_. diff --git a/apps/docs/docs/Demos/ShortcodeValues.mdx b/apps/docs/docs/Demos/features/ShortcodeValues.mdx similarity index 93% rename from apps/docs/docs/Demos/ShortcodeValues.mdx rename to apps/docs/docs/Demos/features/ShortcodeValues.mdx index b574caf..cd9b03f 100644 --- a/apps/docs/docs/Demos/ShortcodeValues.mdx +++ b/apps/docs/docs/Demos/features/ShortcodeValues.mdx @@ -3,7 +3,7 @@ title: Shortcode Values sidebar_position: 7 --- -import ShortcodeLabels from '@site/src/components/examples/ShortcodeLabels'; +import ShortcodeLabels from '@site/src/components/demos/features/ShortcodeLabels'; The `valueType` prop on both components alters the _value_ of each option to be a short code rather than the full country or region name, like with the visible label. Note that when you use `valueType` as `short` for the diff --git a/apps/docs/docs/Demos/features/index.mdx b/apps/docs/docs/Demos/features/index.mdx new file mode 100644 index 0000000..805d0e8 --- /dev/null +++ b/apps/docs/docs/Demos/features/index.mdx @@ -0,0 +1,13 @@ +--- +title: Features +--- + +This section demos all the various features of the script. You can read the descriptions in the sidebar in the left if +that works for you, but here's a simple mapping of prop to demo - in case that's more useful. + +| `CountryDropdown` | `RegionDropdown` | +| --------------------------- | --------------------------- | +| [`value`](../BasicUsage) | [`value`](../BasicUsage) | +| [`onChange`](../BasicUsage) | [`onChange`](../BasicUsage) | + +[TODO] diff --git a/apps/docs/docs/Demos/index.mdx b/apps/docs/docs/Demos/index.mdx new file mode 100644 index 0000000..a62c8e1 --- /dev/null +++ b/apps/docs/docs/Demos/index.mdx @@ -0,0 +1,11 @@ +--- +title: Demos +--- + +The following pages demo all the features of the script. + +- [Basic Usage](./BasicUsage): start here for a quick demonstration of how to use the components. +- [Features](./Features): this section contains demos of all the available features. +- [Integrations](./Integrations): by default, the components output by this package output plain HTML ` + {children} + +); + +const renderOption = ({ value, label }) => ( + {label} +); + +// https://mui.com/material-ui/react-select/ +const MaterialUISelect = () => { + const [country, setCountry] = React.useState(''); + const [region, setRegion] = React.useState(''); + + return ( + + + + Country + setCountry(val)} + renderSelect={renderSelect} + renderOption={renderOption} + /> + + + + + Region + setRegion(val)} + disableWhenEmpty={true} + renderSelect={renderSelect} + renderOption={renderOption} + /> + + + + ); +}; + +export default MaterialUISelect; diff --git a/packages/react-country-region-selector/src/CountryDropdown.tsx b/packages/react-country-region-selector/src/CountryDropdown.tsx index ad01826..442f7a4 100644 --- a/packages/react-country-region-selector/src/CountryDropdown.tsx +++ b/packages/react-country-region-selector/src/CountryDropdown.tsx @@ -1,6 +1,10 @@ import { FC, useMemo } from 'react'; import CountryRegionData from 'country-region-data/data.json'; -import { filterCountries } from './helpers'; +import { + filterCountries, + defaultRenderOption, + defaultRenderSelect, +} from './helpers'; import type { CountryDropdownProps } from './rcrs.types'; export const CountryDropdown: FC = ({ @@ -18,6 +22,8 @@ export const CountryDropdown: FC = ({ whitelist = [], blacklist = [], disabled = false, + renderSelect = defaultRenderSelect, + renderOption = defaultRenderOption, ...arbitraryProps }) => { const countries = useMemo(() => { @@ -28,25 +34,24 @@ export const CountryDropdown: FC = ({ blacklist ); - return countries.map(([countryName, countrySlug]) => ( - - )); + return countries.map(([countryName, countrySlug]) => + renderOption({ + value: valueType === 'short' ? countrySlug : countryName, + key: countrySlug, + label: labelType === 'short' ? countrySlug : countryName, + }) + ); }, [priorityOptions, whitelist, blacklist, valueType, labelType]); const defaultOption = useMemo(() => { if (!showDefaultOption) { return null; } - return ( - - ); + return renderOption({ + value: '', + key: 'default', + label: defaultOptionLabel, + }); }, [showDefaultOption, defaultOptionLabel]); const attrs: any = { @@ -64,10 +69,8 @@ export const CountryDropdown: FC = ({ attrs.className = className; } - return ( - - ); + return renderSelect({ + ...attrs, + children: [defaultOption, countries], + }); }; diff --git a/packages/react-country-region-selector/src/RegionDropdown.tsx b/packages/react-country-region-selector/src/RegionDropdown.tsx index b1123f5..60a487d 100644 --- a/packages/react-country-region-selector/src/RegionDropdown.tsx +++ b/packages/react-country-region-selector/src/RegionDropdown.tsx @@ -1,6 +1,11 @@ import { FC, useMemo } from 'react'; import CountryRegionData from '../node_modules/country-region-data/data.json'; -import { filterRegions, findDuplicates } from './helpers'; +import { + defaultRenderSelect, + defaultRenderOption, + filterRegions, + findDuplicates, +} from './helpers'; import * as C from './constants'; import type { RegionDropdownProps } from './rcrs.types'; @@ -23,6 +28,8 @@ export const RegionDropdown: FC = ({ customOptions = [], whitelist = {}, blacklist = {}, + renderSelect = defaultRenderSelect, + renderOption = defaultRenderOption, ...arbitraryProps }) => { const regions = useMemo(() => { @@ -66,11 +73,9 @@ export const RegionDropdown: FC = ({ }, [country, countryValueType, whitelist, blacklist]); const regionsJsx = useMemo(() => { - return regions.map(({ label, value }) => ( - - )); + return regions.map(({ label, value }) => + renderOption({ value, key: value, label }) + ); }, [regions]); const customRegionsJsx = useMemo(() => { @@ -90,11 +95,7 @@ export const RegionDropdown: FC = ({ return customOptions.map((option) => { if (option) { - return ( - - ); + return renderOption({ value: option, key: option, label: option }); } }); }, [regions, country, customOptions]); @@ -103,10 +104,18 @@ export const RegionDropdown: FC = ({ // a "default" option which shows const getDefaultOption = () => { if (!country) { - return ; + return renderOption({ + value: '', + key: 'default', + label: blankOptionLabel, + }); } if (showDefaultOption) { - return ; + return renderOption({ + value: '', + key: 'default', + label: defaultOptionLabel, + }); } return null; }; @@ -128,11 +137,8 @@ export const RegionDropdown: FC = ({ attrs.className = className; } - return ( - - ); + return renderSelect({ + ...attrs, + children: [getDefaultOption(), regionsJsx, customRegionsJsx], + }); }; diff --git a/packages/react-country-region-selector/src/helpers.ts b/packages/react-country-region-selector/src/helpers.tsx similarity index 88% rename from packages/react-country-region-selector/src/helpers.ts rename to packages/react-country-region-selector/src/helpers.tsx index a7c17e7..50a4e47 100644 --- a/packages/react-country-region-selector/src/helpers.ts +++ b/packages/react-country-region-selector/src/helpers.tsx @@ -89,3 +89,24 @@ export const findDuplicates = (regions, customOptions: string[]) => { .filter(({ value }) => customOptions.indexOf(value) !== -1) .map(({ label }) => label); }; + +export const defaultRenderSelect = (props: any) => { + const { children, ...rest } = props; + return ( + + ); +}; + +export const defaultRenderOption = ({ + label, + value, + key, +}: { + label: string; + value: string; + key: string; +}) => { + return ; +}; diff --git a/packages/react-country-region-selector/src/rcrs.types.ts b/packages/react-country-region-selector/src/rcrs.types.ts index 64f3aee..832d201 100644 --- a/packages/react-country-region-selector/src/rcrs.types.ts +++ b/packages/react-country-region-selector/src/rcrs.types.ts @@ -114,6 +114,9 @@ export interface CountryDropdownProps extends NativeDropdownProps { * Default value: [] */ readonly blacklist?: object; + + readonly renderSelect: any; + readonly renderOption: any; } export interface RegionDropdownProps extends NativeDropdownProps { @@ -257,6 +260,9 @@ export interface RegionDropdownProps extends NativeDropdownProps { readonly blacklist?: { [countryCode: string]: string[]; }; + + readonly renderSelect: any; + readonly renderOption: any; } -export type CountryRegionData = [string[]]; +export type CountryRegionDataMinified = [string[]]; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 95febeb..5570c3d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -19,7 +19,10 @@ importers: '@docusaurus/preset-classic': 3.6.3 '@docusaurus/tsconfig': 3.6.3 '@docusaurus/types': 3.6.3 + '@emotion/react': ^11.13.5 + '@emotion/styled': ^11.13.5 '@mdx-js/react': ^3.0.0 + '@mui/material': ^6.1.9 clsx: ^2.0.0 prism-react-renderer: ^2.3.0 react: ^18.0.0 @@ -29,7 +32,10 @@ importers: dependencies: '@docusaurus/core': 3.6.3_rjmn7ub6jwfoncbionbi4nckku '@docusaurus/preset-classic': 3.6.3_rjmn7ub6jwfoncbionbi4nckku + '@emotion/react': 11.13.5_react@18.3.1 + '@emotion/styled': 11.13.5_onjinwxmkozzmqs2h5h7n2si3a '@mdx-js/react': 3.1.0_react@18.3.1 + '@mui/material': 6.1.9_ugkx4ijbflwe3fpfsxfy6peupi clsx: 2.1.1 prism-react-renderer: 2.4.0_react@18.3.1 react: 18.3.1 @@ -3078,6 +3084,126 @@ packages: - webpack-cli dev: false + /@emotion/babel-plugin/11.13.5: + resolution: {integrity: sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==} + dependencies: + '@babel/helper-module-imports': 7.25.9 + '@babel/runtime': 7.26.0 + '@emotion/hash': 0.9.2 + '@emotion/memoize': 0.9.0 + '@emotion/serialize': 1.3.3 + babel-plugin-macros: 3.1.0 + convert-source-map: 1.9.0 + escape-string-regexp: 4.0.0 + find-root: 1.1.0 + source-map: 0.5.7 + stylis: 4.2.0 + transitivePeerDependencies: + - supports-color + dev: false + + /@emotion/cache/11.13.5: + resolution: {integrity: sha512-Z3xbtJ+UcK76eWkagZ1onvn/wAVb1GOMuR15s30Fm2wrMgC7jzpnO2JZXr4eujTTqoQFUrZIw/rT0c6Zzjca1g==} + dependencies: + '@emotion/memoize': 0.9.0 + '@emotion/sheet': 1.4.0 + '@emotion/utils': 1.4.2 + '@emotion/weak-memoize': 0.4.0 + stylis: 4.2.0 + dev: false + + /@emotion/hash/0.9.2: + resolution: {integrity: sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==} + dev: false + + /@emotion/is-prop-valid/1.3.1: + resolution: {integrity: sha512-/ACwoqx7XQi9knQs/G0qKvv5teDMhD7bXYns9N/wM8ah8iNb8jZ2uNO0YOgiq2o2poIvVtJS2YALasQuMSQ7Kw==} + dependencies: + '@emotion/memoize': 0.9.0 + dev: false + + /@emotion/memoize/0.9.0: + resolution: {integrity: sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==} + dev: false + + /@emotion/react/11.13.5_react@18.3.1: + resolution: {integrity: sha512-6zeCUxUH+EPF1s+YF/2hPVODeV/7V07YU5x+2tfuRL8MdW6rv5vb2+CBEGTGwBdux0OIERcOS+RzxeK80k2DsQ==} + peerDependencies: + '@types/react': '*' + react: '>=16.8.0' + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.26.0 + '@emotion/babel-plugin': 11.13.5 + '@emotion/cache': 11.13.5 + '@emotion/serialize': 1.3.3 + '@emotion/use-insertion-effect-with-fallbacks': 1.1.0_react@18.3.1 + '@emotion/utils': 1.4.2 + '@emotion/weak-memoize': 0.4.0 + hoist-non-react-statics: 3.3.2 + react: 18.3.1 + transitivePeerDependencies: + - supports-color + dev: false + + /@emotion/serialize/1.3.3: + resolution: {integrity: sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==} + dependencies: + '@emotion/hash': 0.9.2 + '@emotion/memoize': 0.9.0 + '@emotion/unitless': 0.10.0 + '@emotion/utils': 1.4.2 + csstype: 3.1.3 + dev: false + + /@emotion/sheet/1.4.0: + resolution: {integrity: sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==} + dev: false + + /@emotion/styled/11.13.5_onjinwxmkozzmqs2h5h7n2si3a: + resolution: {integrity: sha512-gnOQ+nGLPvDXgIx119JqGalys64lhMdnNQA9TMxhDA4K0Hq5+++OE20Zs5GxiCV9r814xQ2K5WmtofSpHVW6BQ==} + peerDependencies: + '@emotion/react': ^11.0.0-rc.0 + '@types/react': '*' + react: '>=16.8.0' + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.26.0 + '@emotion/babel-plugin': 11.13.5 + '@emotion/is-prop-valid': 1.3.1 + '@emotion/react': 11.13.5_react@18.3.1 + '@emotion/serialize': 1.3.3 + '@emotion/use-insertion-effect-with-fallbacks': 1.1.0_react@18.3.1 + '@emotion/utils': 1.4.2 + react: 18.3.1 + transitivePeerDependencies: + - supports-color + dev: false + + /@emotion/unitless/0.10.0: + resolution: {integrity: sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==} + dev: false + + /@emotion/use-insertion-effect-with-fallbacks/1.1.0_react@18.3.1: + resolution: {integrity: sha512-+wBOcIV5snwGgI2ya3u99D7/FJquOIniQT1IKyDsBmEgwvpxMNeS65Oib7OnE2d2aY+3BU4OiH+0Wchf8yk3Hw==} + peerDependencies: + react: '>=16.8.0' + dependencies: + react: 18.3.1 + dev: false + + /@emotion/utils/1.4.2: + resolution: {integrity: sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==} + dev: false + + /@emotion/weak-memoize/0.4.0: + resolution: {integrity: sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==} + dev: false + /@eslint-community/eslint-utils/4.4.1_eslint@8.57.1: resolution: {integrity: sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -3532,6 +3658,145 @@ packages: react: 18.3.1 dev: false + /@mui/core-downloads-tracker/6.1.9: + resolution: {integrity: sha512-TWqj7b1w5cmSz4H/uf+y2AHxAH4ldPR7D2bz0XVyn60GCAo/zRbRPx7cF8gTs/i7CiYeHzV6dtat0VpMwOtolw==} + dev: false + + /@mui/material/6.1.9_ugkx4ijbflwe3fpfsxfy6peupi: + resolution: {integrity: sha512-NwqIN0bdsgzSbZd5JFcC+2ez0XW/XNs8uiV2PDHrqQ4qf/FEasFJG1z6g8JbCN0YlTrHZekVb17X0Fv0qcYJfQ==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@emotion/react': ^11.5.0 + '@emotion/styled': ^11.3.0 + '@mui/material-pigment-css': ^6.1.9 + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + '@mui/material-pigment-css': + optional: true + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.26.0 + '@emotion/react': 11.13.5_react@18.3.1 + '@emotion/styled': 11.13.5_onjinwxmkozzmqs2h5h7n2si3a + '@mui/core-downloads-tracker': 6.1.9 + '@mui/system': 6.1.9_dotbb76culbwrzxs5wzo6apfry + '@mui/types': 7.2.19 + '@mui/utils': 6.1.9_react@18.3.1 + '@popperjs/core': 2.11.8 + '@types/react-transition-group': 4.4.11 + clsx: 2.1.1 + csstype: 3.1.3 + prop-types: 15.8.1 + react: 18.3.1 + react-dom: 18.3.1_react@18.3.1 + react-is: 18.3.1 + react-transition-group: 4.4.5_nnrd3gsncyragczmpvfhocinkq + dev: false + + /@mui/private-theming/6.1.9_react@18.3.1: + resolution: {integrity: sha512-7aum/O1RquBYhfwL/7egDyl9GqJgPM6hoJDFFBbhF6Sgv9yI9v4w3ArKUkuVvR0CtVj4NXRVMKEioh1bjUzvuA==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.26.0 + '@mui/utils': 6.1.9_react@18.3.1 + prop-types: 15.8.1 + react: 18.3.1 + dev: false + + /@mui/styled-engine/6.1.9_dotbb76culbwrzxs5wzo6apfry: + resolution: {integrity: sha512-xynSLlJRxHLzSfQaiDjkaTx8LiFb9ByVa7aOdwFnTxGWFMY1F+mkXwAUY4jDDE+MAxkWxlzzQE0wOohnsxhdQg==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@emotion/react': ^11.4.1 + '@emotion/styled': ^11.3.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + dependencies: + '@babel/runtime': 7.26.0 + '@emotion/cache': 11.13.5 + '@emotion/react': 11.13.5_react@18.3.1 + '@emotion/serialize': 1.3.3 + '@emotion/sheet': 1.4.0 + '@emotion/styled': 11.13.5_onjinwxmkozzmqs2h5h7n2si3a + csstype: 3.1.3 + prop-types: 15.8.1 + react: 18.3.1 + dev: false + + /@mui/system/6.1.9_dotbb76culbwrzxs5wzo6apfry: + resolution: {integrity: sha512-8x+RucnNp21gfFYsklCaZf0COXbv3+v0lrVuXONxvPEkESi2rwLlOi8UPJfcz6LxZOAX3v3oQ7qw18vnpgueRg==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@emotion/react': ^11.5.0 + '@emotion/styled': ^11.3.0 + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.26.0 + '@emotion/react': 11.13.5_react@18.3.1 + '@emotion/styled': 11.13.5_onjinwxmkozzmqs2h5h7n2si3a + '@mui/private-theming': 6.1.9_react@18.3.1 + '@mui/styled-engine': 6.1.9_dotbb76culbwrzxs5wzo6apfry + '@mui/types': 7.2.19 + '@mui/utils': 6.1.9_react@18.3.1 + clsx: 2.1.1 + csstype: 3.1.3 + prop-types: 15.8.1 + react: 18.3.1 + dev: false + + /@mui/types/7.2.19: + resolution: {integrity: sha512-6XpZEM/Q3epK9RN8ENoXuygnqUQxE+siN/6rGRi2iwJPgBUR25mphYQ9ZI87plGh58YoZ5pp40bFvKYOCDJ3tA==} + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + dev: false + + /@mui/utils/6.1.9_react@18.3.1: + resolution: {integrity: sha512-N7uzBp7p2or+xanXn3aH2OTINC6F/Ru/U8h6amhRZEev8bJhKN86rIDIoxZZ902tj+09LXtH83iLxFMjMHyqNA==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.26.0 + '@mui/types': 7.2.19 + '@types/prop-types': 15.7.13 + clsx: 2.1.1 + prop-types: 15.8.1 + react: 18.3.1 + react-is: 18.3.1 + dev: false + /@next/eslint-plugin-next/15.0.3: resolution: {integrity: sha512-3Ln/nHq2V+v8uIaxCR6YfYo7ceRgZNXfTd3yW1ukTaFbO+/I8jNakrjYWODvG9BuR2v5kgVtH/C8r0i11quOgw==} dependencies: @@ -3587,6 +3852,10 @@ packages: resolution: {integrity: sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==} dev: false + /@popperjs/core/2.11.8: + resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} + dev: false + /@rollup/plugin-babel/6.0.4_u54m75cmuqdvqkxqytvfxsrcyi: resolution: {integrity: sha512-YF7Y52kFdFT/xVSuVdjkV5ZdX/3YtmX0QulG+x0taQOtJdHYzVU61aSSkAgVJ7NOv6qPkIYiJSgSWWN/DM5sGw==} engines: {node: '>=14.0.0'} @@ -4405,6 +4674,12 @@ packages: '@types/history': 4.7.11 '@types/react': 18.3.12 + /@types/react-transition-group/4.4.11: + resolution: {integrity: sha512-RM05tAniPZ5DZPzzNFP+DmrcOdD0efDUxMy3145oljWSl3x9ZV5vhme98gTxFrj2lhXvmGNnUiuDyJgY9IKkNA==} + dependencies: + '@types/react': 18.3.12 + dev: false + /@types/react/18.3.1: resolution: {integrity: sha512-V0kuGBX3+prX+DQ/7r2qsv1NsdfnCLnTgnRJ1pYnxykBhGMz+qj+box5lq7XsO5mtZsBqpjwwTu/7wszPfMBcw==} dependencies: @@ -5146,6 +5421,15 @@ packages: '@types/babel__traverse': 7.20.6 dev: true + /babel-plugin-macros/3.1.0: + resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==} + engines: {node: '>=10', npm: '>=6'} + dependencies: + '@babel/runtime': 7.26.0 + cosmiconfig: 7.1.0 + resolve: 1.22.8 + dev: false + /babel-plugin-polyfill-corejs2/0.4.12_@babel+core@7.26.0: resolution: {integrity: sha512-CPWT6BwvhrTO2d8QVorhTCQw9Y43zOu7G9HigcfxvepOU6b8o3tcWad6oVgZIsZCTt42FFv97aA7ZJsbM4+8og==} peerDependencies: @@ -5689,6 +5973,10 @@ packages: engines: {node: '>= 0.6'} dev: false + /convert-source-map/1.9.0: + resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} + dev: false + /convert-source-map/2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} @@ -5751,6 +6039,17 @@ packages: yaml: 1.10.2 dev: false + /cosmiconfig/7.1.0: + resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} + engines: {node: '>=10'} + dependencies: + '@types/parse-json': 4.0.2 + import-fresh: 3.3.0 + parse-json: 5.2.0 + path-type: 4.0.0 + yaml: 1.10.2 + dev: false + /cosmiconfig/8.3.6_typescript@5.6.3: resolution: {integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==} engines: {node: '>=14'} @@ -6331,6 +6630,13 @@ packages: utila: 0.4.0 dev: false + /dom-helpers/5.2.1: + resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==} + dependencies: + '@babel/runtime': 7.26.0 + csstype: 3.1.3 + dev: false + /dom-serializer/1.4.1: resolution: {integrity: sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==} dependencies: @@ -7215,6 +7521,10 @@ packages: pkg-dir: 7.0.0 dev: false + /find-root/1.1.0: + resolution: {integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==} + dev: false + /find-up/3.0.0: resolution: {integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==} engines: {node: '>=6'} @@ -11496,7 +11806,6 @@ packages: /react-is/18.3.1: resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} - dev: true /react-json-view-lite/1.5.0_react@18.3.1: resolution: {integrity: sha512-nWqA1E4jKPklL2jvHWs6s+7Na0qNgw9HCP6xehdQJeg6nPBTFZgGwyko9Q0oj+jQWKTTVRS30u0toM5wiuL3iw==} @@ -11562,6 +11871,20 @@ packages: tiny-warning: 1.0.3 dev: false + /react-transition-group/4.4.5_nnrd3gsncyragczmpvfhocinkq: + resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==} + peerDependencies: + react: '>=16.6.0' + react-dom: '>=16.6.0' + dependencies: + '@babel/runtime': 7.26.0 + dom-helpers: 5.2.1 + loose-envify: 1.4.0 + prop-types: 15.8.1 + react: 18.3.1 + react-dom: 18.3.1_react@18.3.1 + dev: false + /react/18.3.1: resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} engines: {node: '>=0.10.0'} @@ -12343,6 +12666,11 @@ packages: buffer-from: 1.1.2 source-map: 0.6.1 + /source-map/0.5.7: + resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==} + engines: {node: '>=0.10.0'} + dev: false + /source-map/0.6.1: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} @@ -12594,6 +12922,10 @@ packages: postcss-selector-parser: 6.1.2 dev: false + /stylis/4.2.0: + resolution: {integrity: sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==} + dev: false + /supports-color/7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'}