geo-sl 1.0.2 → 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 +135 -19
- 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
|
@@ -126,13 +126,17 @@ import { validateNIC, parseNIC, convertOldNICToNew, validatePhone, parsePhone, f
|
|
|
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`)
|
|
@@ -244,8 +345,10 @@ const branches = getBranches('7010');
|
|
|
244
345
|
* `lookupAllByPostalCode(code: string | number, options?: { lang?: 'en' | 'si' | 'ta' }): City[]` – returns all offices sharing a postal code.
|
|
245
346
|
* `search(query: string, options?: { limit?: number; lang?: 'en' | 'si' | 'ta'; district?: string; province?: string }): City[]`
|
|
246
347
|
|
|
247
|
-
### Cascading Hierarchy
|
|
248
|
-
* `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.
|
|
249
352
|
|
|
250
353
|
### Administrative Divisions (MOHA)
|
|
251
354
|
* `getDivisions(district?: string, options?: { lang?: 'en' | 'si' | 'ta' }): Division[]`
|
|
@@ -260,12 +363,19 @@ const branches = getBranches('7010');
|
|
|
260
363
|
* `getBranchByCode(bankCode: string | number, branchCode: string | number): Branch | undefined`
|
|
261
364
|
* `searchBranches(bankCode: string | number, query: string): Branch[]`
|
|
262
365
|
|
|
263
|
-
### Grama Niladhari (GN) Divisions (`geo-sl/gn`)
|
|
366
|
+
### Grama Niladhari (GN) & Village Divisions (`geo-sl/gn`)
|
|
264
367
|
* `getGNDivisions(options?: { lang?: 'en' | 'si' | 'ta' }): GNDivision[]`
|
|
265
368
|
* `getGNDivisionsByDSD(divisionName: string, options?: { lang?: 'en' | 'si' | 'ta' }): GNDivision[]`
|
|
266
369
|
* `getGNDivisionsByDistrict(districtName: string, options?: { lang?: 'en' | 'si' | 'ta' }): GNDivision[]`
|
|
267
370
|
* `findGNByCode(code: string, options?: { lang?: 'en' | 'si' | 'ta' }): GNDivision | undefined`
|
|
268
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`
|
|
269
379
|
|
|
270
380
|
### Sri Lanka Validators & Parsers (`geo-sl/validators`)
|
|
271
381
|
* `validateNIC(nic: string): boolean`
|
|
@@ -287,15 +397,21 @@ import type {
|
|
|
287
397
|
Bank,
|
|
288
398
|
Branch,
|
|
289
399
|
GNDivision,
|
|
400
|
+
Village,
|
|
401
|
+
SelectOption,
|
|
290
402
|
CascadingProvince,
|
|
291
403
|
CascadingDistrict,
|
|
404
|
+
CascadingAdministrativeProvince,
|
|
405
|
+
CascadingAdministrativeDistrict,
|
|
406
|
+
CascadingAdministrativeDivision,
|
|
292
407
|
ParsedNIC,
|
|
293
408
|
ParsedPhone,
|
|
294
409
|
Language,
|
|
295
410
|
ProvinceCode,
|
|
296
411
|
DistrictCode,
|
|
297
412
|
QueryOptions,
|
|
298
|
-
SearchOptions
|
|
413
|
+
SearchOptions,
|
|
414
|
+
VillageSearchOptions
|
|
299
415
|
} from 'geo-sl';
|
|
300
416
|
```
|
|
301
417
|
|