react-country-list-picker 1.0.0

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 ADDED
@@ -0,0 +1,102 @@
1
+ # react-country-list-picker
2
+
3
+ A cross-platform country list / picker component **and** country dataset
4
+ (name, dial code, ISO code, flag emoji) that works in both **React (web)**
5
+ and **React Native** — with no extra config on your end.
6
+
7
+ It ships two versions of the `CountryList` component:
8
+
9
+ - `CountryList.js` — plain HTML/CSS, used automatically in web projects (CRA, Vite, Next.js, etc.)
10
+ - `CountryList.native.js` — built with React Native primitives (`View`, `FlatList`, ...)
11
+
12
+ React Native's Metro bundler automatically prefers `*.native.js` files, and
13
+ web bundlers (Webpack/Vite) just resolve the plain `.js` file — so you import
14
+ from the **same path** in both platforms and get the right component.
15
+
16
+ ## Install
17
+
18
+ ```bash
19
+ npm install react-country-list-picker
20
+ # or
21
+ yarn add react-country-list-picker
22
+ ```
23
+
24
+ React is a peer dependency (React Native is optional and only needed if
25
+ you're using it in an RN project — you already have it there).
26
+
27
+ ## Usage (identical code on web and React Native)
28
+
29
+ ```tsx
30
+ import { CountryList, Country } from 'react-country-list-picker';
31
+
32
+ function MyPicker() {
33
+ const handleSelect = (country: Country) => {
34
+ console.log(country.name, country.dial_code, country.code);
35
+ };
36
+
37
+ return <CountryList onSelect={handleSelect} />;
38
+ }
39
+ ```
40
+
41
+ ### Props
42
+
43
+ | Prop | Type | Default | Description |
44
+ |---------------------|-------------------------------|----------------------|-------------------------------------------|
45
+ | `onSelect` | `(country: Country) => void` | — | Called when a country row is tapped/clicked |
46
+ | `data` | `Country[]` | full built-in list | Pass a filtered/custom dataset |
47
+ | `searchable` | `boolean` | `true` | Show/hide the built-in search box |
48
+ | `searchPlaceholder` | `string` | `"Search country..."`| Placeholder for the search box |
49
+
50
+ ### Using just the raw data (no UI)
51
+
52
+ ```ts
53
+ import { countries } from 'react-country-list-picker';
54
+
55
+ console.log(countries[0]);
56
+ // { name: "Afghanistan", dial_code: "+93", code: "AF", Icon: "🇦🇫" }
57
+ ```
58
+
59
+ ## Project structure
60
+
61
+ ```
62
+ country-list-picker/
63
+ ├─ src/
64
+ │ ├─ data/countries.json # the dataset
65
+ │ ├─ CountryList.tsx # web version
66
+ │ ├─ CountryList.native.tsx # React Native version
67
+ │ ├─ types.ts
68
+ │ └─ index.ts # public entry point
69
+ ├─ dist/ # compiled output (generated, published to npm)
70
+ ├─ package.json
71
+ ├─ tsconfig.json
72
+ └─ README.md
73
+ ```
74
+
75
+ ## Build
76
+
77
+ ```bash
78
+ npm install
79
+ npm run build # compiles src/ -> dist/ with tsc
80
+ ```
81
+
82
+ ## Publish to npm
83
+
84
+ 1. Update `"name"` in `package.json` to something unique on the npm registry
85
+ (check availability at https://www.npmjs.com/).
86
+ 2. Bump `"version"` following semver.
87
+ 3. Log in and publish:
88
+
89
+ ```bash
90
+ npm login
91
+ npm publish --access public
92
+ ```
93
+
94
+ `prepublishOnly` automatically runs the build first, so `dist/` is always
95
+ fresh when you publish.
96
+
97
+ ## Notes / next steps you may want
98
+
99
+ - Add your own logo/name/license details in `package.json`.
100
+ - Add a `styles` prop or theme support if you want consumers to restyle rows.
101
+ - Add unit tests (e.g. with Jest + React Testing Library) before publishing v1.
102
+ - Set up CI (GitHub Actions) to run `npm run build` on every push.
@@ -0,0 +1,3 @@
1
+ import React from 'react';
2
+ import { CountryListProps } from './types';
3
+ export default function CountryList({ onSelect, data, searchable, searchPlaceholder, }: CountryListProps): React.JSX.Element;
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.default = CountryList;
7
+ const jsx_runtime_1 = require("react/jsx-runtime");
8
+ const react_1 = require("react");
9
+ const countries_json_1 = __importDefault(require("./data/countries.json"));
10
+ const styles = {
11
+ container: {
12
+ width: '100%',
13
+ maxWidth: 360,
14
+ border: '1px solid #e0e0e0',
15
+ borderRadius: 8,
16
+ fontFamily: 'sans-serif',
17
+ overflow: 'hidden',
18
+ },
19
+ search: {
20
+ width: '100%',
21
+ boxSizing: 'border-box',
22
+ padding: '10px 12px',
23
+ border: 'none',
24
+ borderBottom: '1px solid #e0e0e0',
25
+ outline: 'none',
26
+ fontSize: 14,
27
+ },
28
+ list: {
29
+ maxHeight: 320,
30
+ overflowY: 'auto',
31
+ },
32
+ row: {
33
+ display: 'flex',
34
+ alignItems: 'center',
35
+ gap: 10,
36
+ padding: '10px 12px',
37
+ cursor: 'pointer',
38
+ },
39
+ flag: {
40
+ fontSize: 20,
41
+ },
42
+ name: {
43
+ flex: 1,
44
+ fontSize: 14,
45
+ color: '#222',
46
+ },
47
+ dial: {
48
+ fontSize: 13,
49
+ color: '#888',
50
+ },
51
+ };
52
+ function CountryList({ onSelect, data, searchable = true, searchPlaceholder = 'Search country...', }) {
53
+ const [query, setQuery] = (0, react_1.useState)('');
54
+ const source = data !== null && data !== void 0 ? data : countries_json_1.default;
55
+ const filtered = (0, react_1.useMemo)(() => {
56
+ if (!query.trim())
57
+ return source;
58
+ const q = query.trim().toLowerCase();
59
+ return source.filter((c) => c.name.toLowerCase().includes(q) ||
60
+ c.dial_code.includes(q) ||
61
+ c.code.toLowerCase().includes(q));
62
+ }, [query, source]);
63
+ return ((0, jsx_runtime_1.jsxs)("div", { style: styles.container, children: [searchable && ((0, jsx_runtime_1.jsx)("input", { style: styles.search, placeholder: searchPlaceholder, value: query, onChange: (e) => setQuery(e.target.value) })), (0, jsx_runtime_1.jsx)("div", { style: styles.list, children: filtered.map((country) => ((0, jsx_runtime_1.jsxs)("div", { style: styles.row, onClick: () => onSelect(country), children: [(0, jsx_runtime_1.jsx)("span", { style: styles.flag, children: country.Icon }), (0, jsx_runtime_1.jsx)("span", { style: styles.name, children: country.name }), (0, jsx_runtime_1.jsx)("span", { style: styles.dial, children: country.dial_code })] }, country.code))) })] }));
64
+ }
@@ -0,0 +1,3 @@
1
+ import React from 'react';
2
+ import { CountryListProps } from './types';
3
+ export default function CountryList({ onSelect, data, searchable, searchPlaceholder, }: CountryListProps): React.JSX.Element;
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.default = CountryList;
7
+ const jsx_runtime_1 = require("react/jsx-runtime");
8
+ const react_1 = require("react");
9
+ const react_native_1 = require("react-native");
10
+ const countries_json_1 = __importDefault(require("./data/countries.json"));
11
+ function CountryList({ onSelect, data, searchable = true, searchPlaceholder = 'Search country...', }) {
12
+ const [query, setQuery] = (0, react_1.useState)('');
13
+ const source = data !== null && data !== void 0 ? data : countries_json_1.default;
14
+ const filtered = (0, react_1.useMemo)(() => {
15
+ if (!query.trim())
16
+ return source;
17
+ const q = query.trim().toLowerCase();
18
+ return source.filter((c) => c.name.toLowerCase().includes(q) ||
19
+ c.dial_code.includes(q) ||
20
+ c.code.toLowerCase().includes(q));
21
+ }, [query, source]);
22
+ return ((0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.container, children: [searchable && ((0, jsx_runtime_1.jsx)(react_native_1.TextInput, { style: styles.search, placeholder: searchPlaceholder, value: query, onChangeText: setQuery })), (0, jsx_runtime_1.jsx)(react_native_1.FlatList, { data: filtered, keyExtractor: (item) => item.code, renderItem: ({ item }) => ((0, jsx_runtime_1.jsxs)(react_native_1.TouchableOpacity, { style: styles.row, onPress: () => onSelect(item), children: [(0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.flag, children: item.Icon }), (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.name, children: item.name }), (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.dial, children: item.dial_code })] })) })] }));
23
+ }
24
+ const styles = react_native_1.StyleSheet.create({
25
+ container: {
26
+ width: '100%',
27
+ maxWidth: 360,
28
+ borderWidth: 1,
29
+ borderColor: '#e0e0e0',
30
+ borderRadius: 8,
31
+ overflow: 'hidden',
32
+ },
33
+ search: {
34
+ paddingHorizontal: 12,
35
+ paddingVertical: 10,
36
+ borderBottomWidth: 1,
37
+ borderBottomColor: '#e0e0e0',
38
+ fontSize: 14,
39
+ },
40
+ row: {
41
+ flexDirection: 'row',
42
+ alignItems: 'center',
43
+ paddingHorizontal: 12,
44
+ paddingVertical: 10,
45
+ gap: 10,
46
+ },
47
+ flag: {
48
+ fontSize: 20,
49
+ marginRight: 10,
50
+ },
51
+ name: {
52
+ flex: 1,
53
+ fontSize: 14,
54
+ color: '#222',
55
+ },
56
+ dial: {
57
+ fontSize: 13,
58
+ color: '#888',
59
+ },
60
+ });