geo-sl 1.0.1 → 1.1.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 +155 -33
- package/dist/gn.cjs +1 -1
- package/dist/gn.d.cts +33 -1
- package/dist/gn.d.ts +33 -1
- package/dist/gn.js +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +26 -3
- package/dist/index.d.ts +26 -3
- package/dist/index.js +1 -1
- package/dist/types.cjs +1 -1
- package/dist/types.d.cts +25 -1
- package/dist/types.d.ts +25 -1
- package/dist/types.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,11 +12,11 @@
|
|
|
12
12
|
|
|
13
13
|
## 🗺️ 100% Comprehensive Island-wide Coverage
|
|
14
14
|
|
|
15
|
-
`geo-sl`
|
|
15
|
+
`geo-sl` covers the entire territory of Sri Lanka — not just major cities and urban centers. It provides complete, authoritative, and verified geographic data from provincial capitals all the way down to individual rural villages and remote Grama Niladhari divisions:
|
|
16
16
|
|
|
17
17
|
| Administrative / Geographic Level | Total Count | Scope & Details | Supported Languages |
|
|
18
18
|
|---|---|---|---|
|
|
19
|
-
| **Provinces** | **9** | All 9 provinces (WP, CP, SP, NP, EP,
|
|
19
|
+
| **Provinces** | **9** | All 9 provinces (WP, CP, SP, NP, EP, NWP, NCP, UP, SGP) | English, සිංහල, தமிழ் |
|
|
20
20
|
| **Districts** | **25** | All 25 administrative districts across the island | English, සිංහල, தமிழ் |
|
|
21
21
|
| **Divisional Secretariats (DSD)** | **340** | 100% of Divisional Secretariat Divisions (MOHA) | English, සිංහල, தமிழ் |
|
|
22
22
|
| **Grama Niladhari (GN) Divisions** | **14,020** | Every single village, ward, and local community | English, සිංහල, தமிழ் |
|
|
@@ -92,7 +92,7 @@ getPostalCode('මහනුවර'); // => "20000"
|
|
|
92
92
|
// 5. Search with Relevance Ranking
|
|
93
93
|
search('colombo'); // => [ { name_en: 'Colombo 1', ... }, { name_en: 'Colombo 2', ... } ]
|
|
94
94
|
search('nawala'); // => [ { name_en: 'Nawala', ... }, ... ]
|
|
95
|
-
search('කොළඹ'); // Sinhala search
|
|
95
|
+
search('කොළඹ'); // => [ { name_en: 'Colombo 1', ... }, ... ] (Sinhala search)
|
|
96
96
|
```
|
|
97
97
|
|
|
98
98
|
---
|
|
@@ -121,18 +121,22 @@ import { BANKS, getBanks, getBankByCode, getBranches } from 'geo-sl/banks';
|
|
|
121
121
|
import { GN_DIVISIONS, getGNDivisions, searchGN } from 'geo-sl/gn';
|
|
122
122
|
|
|
123
123
|
// Validators & Parsers (~2.3 KB)
|
|
124
|
-
import { validateNIC, parseNIC, validatePhone, parsePhone, validatePostalCode } from 'geo-sl/validators';
|
|
124
|
+
import { validateNIC, parseNIC, convertOldNICToNew, validatePhone, parsePhone, formatPhone, validatePostalCode } from 'geo-sl/validators';
|
|
125
125
|
```
|
|
126
126
|
|
|
127
127
|
---
|
|
128
128
|
|
|
129
|
-
## 🎨 Interactive React / Next.js Cascading Form
|
|
129
|
+
## 🎨 Interactive React / Next.js Cascading Form Examples
|
|
130
130
|
|
|
131
|
-
|
|
131
|
+
`geo-sl` makes building multi-level dependent dropdowns straightforward for both e-commerce checkouts and official KYC/government forms.
|
|
132
|
+
|
|
133
|
+
### 1. Delivery & Checkout Address Form (`Province ➔ District ➔ City / Postal Code`)
|
|
134
|
+
|
|
135
|
+
For shipping and delivery addresses, use `getCascadingData()` or `toSelectOptions()`:
|
|
132
136
|
|
|
133
137
|
```tsx
|
|
134
138
|
import React, { useState } from 'react';
|
|
135
|
-
import { getCascadingData } from 'geo-sl';
|
|
139
|
+
import { getCascadingData, toSelectOptions } from 'geo-sl';
|
|
136
140
|
|
|
137
141
|
const addressData = getCascadingData({ lang: 'en' });
|
|
138
142
|
|
|
@@ -144,22 +148,32 @@ export function SriLankaAddressForm() {
|
|
|
144
148
|
const currentProvince = addressData.find((p) => p.code === selectedProvince);
|
|
145
149
|
const currentDistrict = currentProvince?.districts.find((d) => d.code === selectedDistrict);
|
|
146
150
|
|
|
151
|
+
// Convert to standard { label, value } options for React-Select / Shadcn / HTML select
|
|
152
|
+
const provinceOptions = toSelectOptions(addressData, 'name', 'code');
|
|
153
|
+
const districtOptions = toSelectOptions(currentProvince?.districts || [], 'name', 'code');
|
|
154
|
+
const cityOptions = toSelectOptions(
|
|
155
|
+
currentDistrict?.cities || [],
|
|
156
|
+
(c) => `${c.name} (${c.postal_code})`,
|
|
157
|
+
'name'
|
|
158
|
+
);
|
|
159
|
+
|
|
147
160
|
return (
|
|
148
161
|
<div className="space-y-4">
|
|
149
162
|
{/* Province */}
|
|
150
163
|
<select
|
|
151
164
|
value={selectedProvince}
|
|
152
165
|
onChange={(e) => {
|
|
153
|
-
|
|
154
|
-
|
|
166
|
+
const code = e.target.value;
|
|
167
|
+
setSelectedProvince(code as any);
|
|
168
|
+
const prov = addressData.find((p) => p.code === code);
|
|
155
169
|
if (prov && prov.districts[0]) {
|
|
156
170
|
setSelectedDistrict(prov.districts[0].code);
|
|
157
171
|
setSelectedCity(prov.districts[0].cities[0]?.name || '');
|
|
158
172
|
}
|
|
159
173
|
}}
|
|
160
174
|
>
|
|
161
|
-
{
|
|
162
|
-
<option key={
|
|
175
|
+
{provinceOptions.map((opt) => (
|
|
176
|
+
<option key={opt.value} value={opt.value}>{opt.label}</option>
|
|
163
177
|
))}
|
|
164
178
|
</select>
|
|
165
179
|
|
|
@@ -167,15 +181,16 @@ export function SriLankaAddressForm() {
|
|
|
167
181
|
<select
|
|
168
182
|
value={selectedDistrict}
|
|
169
183
|
onChange={(e) => {
|
|
170
|
-
|
|
171
|
-
|
|
184
|
+
const code = e.target.value;
|
|
185
|
+
setSelectedDistrict(code as any);
|
|
186
|
+
const dist = currentProvince?.districts.find((d) => d.code === code);
|
|
172
187
|
if (dist && dist.cities[0]) {
|
|
173
188
|
setSelectedCity(dist.cities[0].name);
|
|
174
189
|
}
|
|
175
190
|
}}
|
|
176
191
|
>
|
|
177
|
-
{
|
|
178
|
-
<option key={
|
|
192
|
+
{districtOptions.map((opt) => (
|
|
193
|
+
<option key={opt.value} value={opt.value}>{opt.label}</option>
|
|
179
194
|
))}
|
|
180
195
|
</select>
|
|
181
196
|
|
|
@@ -184,10 +199,8 @@ export function SriLankaAddressForm() {
|
|
|
184
199
|
value={selectedCity}
|
|
185
200
|
onChange={(e) => setSelectedCity(e.target.value)}
|
|
186
201
|
>
|
|
187
|
-
{
|
|
188
|
-
<option key={
|
|
189
|
-
{c.name} ({c.postal_code})
|
|
190
|
-
</option>
|
|
202
|
+
{cityOptions.map((opt) => (
|
|
203
|
+
<option key={opt.value} value={opt.value}>{opt.label}</option>
|
|
191
204
|
))}
|
|
192
205
|
</select>
|
|
193
206
|
</div>
|
|
@@ -195,6 +208,94 @@ export function SriLankaAddressForm() {
|
|
|
195
208
|
}
|
|
196
209
|
```
|
|
197
210
|
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
### 2. Administrative & Village Form (`Province ➔ District ➔ DS Division ➔ Village / GN`)
|
|
214
|
+
|
|
215
|
+
For banking KYC, voter registration, legal documentation, or government administrative forms, query on-demand down to the **14,020 official villages (Grama Niladhari divisions)**:
|
|
216
|
+
|
|
217
|
+
```tsx
|
|
218
|
+
import React, { useState, useEffect } from 'react';
|
|
219
|
+
import { getProvinces, getDistrictsByProvince, getDivisionsByDistrict, toSelectOptions } from 'geo-sl';
|
|
220
|
+
import { getVillagesByDivision, searchVillages, type Village } from 'geo-sl/gn';
|
|
221
|
+
|
|
222
|
+
export function SriLankaAdministrativeForm() {
|
|
223
|
+
const [provinceCode, setProvinceCode] = useState('WP');
|
|
224
|
+
const [district, setDistrict] = useState('Colombo');
|
|
225
|
+
const [division, setDivision] = useState('Colombo');
|
|
226
|
+
const [villages, setVillages] = useState<readonly Village[]>([]);
|
|
227
|
+
const [selectedVillage, setSelectedVillage] = useState('');
|
|
228
|
+
|
|
229
|
+
// Load villages dynamically whenever the DS division changes
|
|
230
|
+
useEffect(() => {
|
|
231
|
+
const list = getVillagesByDivision(division);
|
|
232
|
+
setVillages(list);
|
|
233
|
+
if (list.length > 0) setSelectedVillage(list[0].code);
|
|
234
|
+
}, [division]);
|
|
235
|
+
|
|
236
|
+
const provinces = toSelectOptions(getProvinces(), 'name', 'code');
|
|
237
|
+
const districts = toSelectOptions(getDistrictsByProvince(provinceCode), 'name', 'name_en');
|
|
238
|
+
const divisions = toSelectOptions(getDivisionsByDistrict(district), 'name', 'name_en');
|
|
239
|
+
const villageOptions = toSelectOptions(
|
|
240
|
+
villages,
|
|
241
|
+
(v) => `${v.name} (${v.code})`,
|
|
242
|
+
'code'
|
|
243
|
+
);
|
|
244
|
+
|
|
245
|
+
return (
|
|
246
|
+
<form className="space-y-4">
|
|
247
|
+
{/* 1. Province */}
|
|
248
|
+
<select
|
|
249
|
+
value={provinceCode}
|
|
250
|
+
onChange={(e) => {
|
|
251
|
+
setProvinceCode(e.target.value);
|
|
252
|
+
const firstDist = getDistrictsByProvince(e.target.value)[0];
|
|
253
|
+
if (firstDist) {
|
|
254
|
+
setDistrict(firstDist.name_en);
|
|
255
|
+
const firstDiv = getDivisionsByDistrict(firstDist.name_en)[0];
|
|
256
|
+
if (firstDiv) setDivision(firstDiv.name_en);
|
|
257
|
+
}
|
|
258
|
+
}}
|
|
259
|
+
>
|
|
260
|
+
{provinces.map((p) => <option key={p.value} value={p.value}>{p.label}</option>)}
|
|
261
|
+
</select>
|
|
262
|
+
|
|
263
|
+
{/* 2. District */}
|
|
264
|
+
<select
|
|
265
|
+
value={district}
|
|
266
|
+
onChange={(e) => {
|
|
267
|
+
setDistrict(e.target.value);
|
|
268
|
+
const firstDiv = getDivisionsByDistrict(e.target.value)[0];
|
|
269
|
+
if (firstDiv) setDivision(firstDiv.name_en);
|
|
270
|
+
}}
|
|
271
|
+
>
|
|
272
|
+
{districts.map((d) => <option key={d.value} value={d.value}>{d.label}</option>)}
|
|
273
|
+
</select>
|
|
274
|
+
|
|
275
|
+
{/* 3. DS Division */}
|
|
276
|
+
<select
|
|
277
|
+
value={division}
|
|
278
|
+
onChange={(e) => setDivision(e.target.value)}
|
|
279
|
+
>
|
|
280
|
+
{divisions.map((div) => <option key={div.value} value={div.value}>{div.label}</option>)}
|
|
281
|
+
</select>
|
|
282
|
+
|
|
283
|
+
{/* 4. Village (Grama Niladhari Division) */}
|
|
284
|
+
<select
|
|
285
|
+
value={selectedVillage}
|
|
286
|
+
onChange={(e) => setSelectedVillage(e.target.value)}
|
|
287
|
+
>
|
|
288
|
+
{villageOptions.map((v) => <option key={v.value} value={v.value}>{v.label}</option>)}
|
|
289
|
+
</select>
|
|
290
|
+
</form>
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
> [!TIP]
|
|
296
|
+
> **Autocomplete for Villages:** With 14,000+ villages, you can also use `searchVillages('Mattakkuliya', { district: 'Colombo', limit: 10 })` to power modern searchable comboboxes (Shadcn, Headless UI, Radix) across English, Sinhala (`මට්ටක්කුලිය`), and Tamil (`மட்டக்குளி`).
|
|
297
|
+
|
|
298
|
+
|
|
198
299
|
---
|
|
199
300
|
|
|
200
301
|
## 🏦 CBSL Bank & Branch Codes (`geo-sl/banks`)
|
|
@@ -209,7 +310,7 @@ const allBanks = getBanks();
|
|
|
209
310
|
|
|
210
311
|
// Find Bank of Ceylon
|
|
211
312
|
const boc = getBankByCode('7010');
|
|
212
|
-
console.log(boc
|
|
313
|
+
console.log(boc?.name); // "Bank of Ceylon"
|
|
213
314
|
|
|
214
315
|
// Get all branches for a bank
|
|
215
316
|
const branches = getBranches('7010');
|
|
@@ -222,12 +323,16 @@ const branches = getBranches('7010');
|
|
|
222
323
|
|
|
223
324
|
### Provinces
|
|
224
325
|
* `getProvinces(options?: { lang?: 'en' | 'si' | 'ta' }): Province[]`
|
|
225
|
-
* `
|
|
326
|
+
* `getProvince(codeOrId: string): Province | undefined` – primary lookup by code (e.g. `'WP'`), ID, or English name.
|
|
327
|
+
* `getProvinceName(codeOrId: string, lang?: Language): string | undefined`
|
|
328
|
+
* `getProvinceByCode(code: string, options?: { lang?: 'en' | 'si' | 'ta' }): Province | undefined` – alias for `getProvince`
|
|
226
329
|
|
|
227
330
|
### Districts
|
|
228
331
|
* `getDistricts(province?: string, options?: { lang?: 'en' | 'si' | 'ta' }): District[]`
|
|
229
|
-
* `
|
|
332
|
+
* `getDistrict(codeOrId: string): District | undefined` – primary lookup by abbreviation (e.g. `'CO'`), ID, or English name.
|
|
333
|
+
* `getDistrictName(codeOrId: string, lang?: Language): string | undefined`
|
|
230
334
|
* `getDistrictsByProvince(province: string, options?: { lang?: 'en' | 'si' | 'ta' }): District[]`
|
|
335
|
+
* `getDistrictByCode(code: string, options?: { lang?: 'en' | 'si' | 'ta' }): District | undefined` – alias for `getDistrict`
|
|
231
336
|
|
|
232
337
|
### Cities & Postal Codes
|
|
233
338
|
* `getCities(district?: string, options?: { lang?: 'en' | 'si' | 'ta' }): City[]`
|
|
@@ -235,36 +340,47 @@ const branches = getBranches('7010');
|
|
|
235
340
|
* `getCitiesByProvince(province: string, options?: { lang?: 'en' | 'si' | 'ta' }): City[]`
|
|
236
341
|
* `getPostalCode(cityName: string): string | undefined`
|
|
237
342
|
* `getCityByPostalCode(postalCode: string | number, lang?: 'en' | 'si' | 'ta'): City | undefined`
|
|
238
|
-
* `isValidPostalCode(postalCode: string | number): boolean`
|
|
239
|
-
* `lookupPostalCode(code: string | number, options?: { lang?: 'en' | 'si' | 'ta' }): City | undefined`
|
|
240
|
-
* `lookupAllByPostalCode(code: string | number, options?: { lang?: 'en' | 'si' | 'ta' }): City[]`
|
|
343
|
+
* `isValidPostalCode(postalCode: string | number): boolean` – checks existence against the Sri Lanka Post database.
|
|
344
|
+
* `lookupPostalCode(code: string | number, options?: { lang?: 'en' | 'si' | 'ta' }): City | undefined` – alias for `getCityByPostalCode`
|
|
345
|
+
* `lookupAllByPostalCode(code: string | number, options?: { lang?: 'en' | 'si' | 'ta' }): City[]` – returns all offices sharing a postal code.
|
|
241
346
|
* `search(query: string, options?: { limit?: number; lang?: 'en' | 'si' | 'ta'; district?: string; province?: string }): City[]`
|
|
242
347
|
|
|
243
|
-
### Cascading Hierarchy
|
|
244
|
-
* `getCascadingData(options?: { lang?: 'en' | 'si' | 'ta' }): CascadingProvince[]`
|
|
348
|
+
### Cascading Hierarchy & Form Helpers
|
|
349
|
+
* `getCascadingData(options?: { lang?: 'en' | 'si' | 'ta' }): CascadingProvince[]` – pre-nested tree (Province ➔ District ➔ Cities).
|
|
350
|
+
* `getAdministrativeCascadingData(options?: { lang?: 'en' | 'si' | 'ta' }): CascadingAdministrativeProvince[]` – pre-nested tree (Province ➔ District ➔ DS Divisions).
|
|
351
|
+
* `toSelectOptions<T>(items, labelKey, valueKey): SelectOption[]` – universal formatter converting data objects into `{ label, value }` pairs for UI dropdowns.
|
|
245
352
|
|
|
246
353
|
### Administrative Divisions (MOHA)
|
|
247
354
|
* `getDivisions(district?: string, options?: { lang?: 'en' | 'si' | 'ta' }): Division[]`
|
|
248
355
|
* `getDivisionsByDistrict(district: string, options?: { lang?: 'en' | 'si' | 'ta' }): Division[]`
|
|
356
|
+
* `getDivisionsByProvince(province: string, options?: { lang?: 'en' | 'si' | 'ta' }): Division[]`
|
|
249
357
|
|
|
250
358
|
### Financial Institutions (CBSL / LankaPay)
|
|
251
|
-
* `getBanks(): Bank[]`
|
|
252
|
-
* `
|
|
253
|
-
* `
|
|
359
|
+
* `getBanks(): readonly Bank[]`
|
|
360
|
+
* `getBank(codeOrId: string | number): Bank | undefined` – primary lookup by 4-digit CBSL code, ID, or bank name.
|
|
361
|
+
* `getBankByCode(codeOrId: string | number): Bank | undefined` – alias for `getBank`
|
|
362
|
+
* `getBranches(bankCode: string | number): readonly Branch[]`
|
|
254
363
|
* `getBranchByCode(bankCode: string | number, branchCode: string | number): Branch | undefined`
|
|
255
364
|
* `searchBranches(bankCode: string | number, query: string): Branch[]`
|
|
256
365
|
|
|
257
|
-
### Grama Niladhari (GN) Divisions (`geo-sl/gn`)
|
|
366
|
+
### Grama Niladhari (GN) & Village Divisions (`geo-sl/gn`)
|
|
258
367
|
* `getGNDivisions(options?: { lang?: 'en' | 'si' | 'ta' }): GNDivision[]`
|
|
259
368
|
* `getGNDivisionsByDSD(divisionName: string, options?: { lang?: 'en' | 'si' | 'ta' }): GNDivision[]`
|
|
260
369
|
* `getGNDivisionsByDistrict(districtName: string, options?: { lang?: 'en' | 'si' | 'ta' }): GNDivision[]`
|
|
261
370
|
* `findGNByCode(code: string, options?: { lang?: 'en' | 'si' | 'ta' }): GNDivision | undefined`
|
|
262
371
|
* `searchGN(query: string, options?: { limit?: number; lang?: 'en' | 'si' | 'ta'; district?: string; division?: string }): GNDivision[]`
|
|
372
|
+
* **Village Aliases:**
|
|
373
|
+
* `getVillages(options?)` – alias for `getGNDivisions`
|
|
374
|
+
* `getVillagesByDivision(divisionName, options?)` – alias for `getGNDivisionsByDSD`
|
|
375
|
+
* `getVillagesByDistrict(districtName, options?)` – alias for `getGNDivisionsByDistrict`
|
|
376
|
+
* `findVillageByCode(code, options?)` – alias for `findGNByCode`
|
|
377
|
+
* `searchVillages(query, options?)` – alias for `searchGN`
|
|
378
|
+
* `VILLAGES` – alias for `GN_DIVISIONS`
|
|
263
379
|
|
|
264
380
|
### Sri Lanka Validators & Parsers (`geo-sl/validators`)
|
|
265
381
|
* `validateNIC(nic: string): boolean`
|
|
266
382
|
* `parseNIC(nic: string): ParsedNIC | null` – parses birthdate, gender, age, voter eligibility from Old (9+V/X) and New (12 digits) NICs.
|
|
267
|
-
* `convertOldNICToNew(oldNic: string): string | null` – converts
|
|
383
|
+
* `convertOldNICToNew(oldNic: string): string | null` – converts 10-character old NIC (9 digits + V/X) to 12-digit new format.
|
|
268
384
|
* `validatePhone(phone: string): boolean`
|
|
269
385
|
* `parsePhone(phone: string): ParsedPhone | null` – parses operator (Dialog, Mobitel, Hutch, Airtel), type (mobile/fixed), and formats.
|
|
270
386
|
* `formatPhone(phone: string, style?: 'international' | 'local' | 'e164'): string | null`
|
|
@@ -281,15 +397,21 @@ import type {
|
|
|
281
397
|
Bank,
|
|
282
398
|
Branch,
|
|
283
399
|
GNDivision,
|
|
400
|
+
Village,
|
|
401
|
+
SelectOption,
|
|
284
402
|
CascadingProvince,
|
|
285
403
|
CascadingDistrict,
|
|
404
|
+
CascadingAdministrativeProvince,
|
|
405
|
+
CascadingAdministrativeDistrict,
|
|
406
|
+
CascadingAdministrativeDivision,
|
|
286
407
|
ParsedNIC,
|
|
287
408
|
ParsedPhone,
|
|
288
409
|
Language,
|
|
289
410
|
ProvinceCode,
|
|
290
411
|
DistrictCode,
|
|
291
412
|
QueryOptions,
|
|
292
|
-
SearchOptions
|
|
413
|
+
SearchOptions,
|
|
414
|
+
VillageSearchOptions
|
|
293
415
|
} from 'geo-sl';
|
|
294
416
|
```
|
|
295
417
|
|