@quantaroute/checkout 1.1.0 → 1.2.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/LICENSE +29 -0
- package/README.md +302 -175
- package/babel.config.js +16 -0
- package/dist/lib/index.d.ts +9 -7
- package/dist/lib/quantaroute-checkout.es.js +9 -3
- package/dist/lib/quantaroute-checkout.umd.js +9 -3
- package/expo-plugin.js +109 -0
- package/package.json +50 -10
- package/src/components/AddressForm.native.tsx +540 -0
- package/src/components/AddressForm.tsx +477 -0
- package/src/components/CheckoutWidget.native.tsx +218 -0
- package/src/components/CheckoutWidget.tsx +196 -0
- package/src/components/MapPinSelector.native.tsx +254 -0
- package/src/components/MapPinSelector.tsx +405 -0
- package/src/core/api.ts +150 -0
- package/src/core/digipin.ts +169 -0
- package/src/core/types.ts +150 -0
- package/src/hooks/useDigiPin.ts +20 -0
- package/src/hooks/useGeolocation.native.ts +55 -0
- package/src/hooks/useGeolocation.ts +48 -0
- package/src/index.ts +59 -0
- package/src/styles/checkout.css +1082 -0
- package/src/styles/checkout.native.ts +839 -0
- /package/dist/{lib/style.css → style.css} +0 -0
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Official DigiPin Algorithm (Offline, Zero-dependency)
|
|
3
|
+
*
|
|
4
|
+
* Source: India Post – Department of Posts, Government of India
|
|
5
|
+
* Repository: https://github.com/INDIAPOST-gov/digipin
|
|
6
|
+
* License: Apache 2.0
|
|
7
|
+
*
|
|
8
|
+
* Developed in collaboration with:
|
|
9
|
+
* – Indian Institute of Technology, Hyderabad
|
|
10
|
+
* – National Remote Sensing Centre, ISRO
|
|
11
|
+
*
|
|
12
|
+
* This is a TypeScript port vendored for offline DigiPin processing
|
|
13
|
+
* without any external dependencies.
|
|
14
|
+
*
|
|
15
|
+
* DigiPin provides a geo-coded addressing system for India, dividing
|
|
16
|
+
* the country into ~4m × 4m grids with unique 10-character codes.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/** DigiPin 4×4 grid character mapping */
|
|
20
|
+
const DIGIPIN_GRID: string[][] = [
|
|
21
|
+
['F', 'C', '9', '8'],
|
|
22
|
+
['J', '3', '2', '7'],
|
|
23
|
+
['K', '4', '5', '6'],
|
|
24
|
+
['L', 'M', 'P', 'T'],
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
/** Geographic bounds for India */
|
|
28
|
+
const BOUNDS = {
|
|
29
|
+
minLat: 2.5,
|
|
30
|
+
maxLat: 38.5,
|
|
31
|
+
minLon: 63.5,
|
|
32
|
+
maxLon: 99.5,
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export interface DigiPinCoordinates {
|
|
36
|
+
latitude: string;
|
|
37
|
+
longitude: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Convert lat/lng coordinates to a DigiPin code.
|
|
42
|
+
* @throws if coordinates are outside India's bounds
|
|
43
|
+
*/
|
|
44
|
+
export function getDigiPin(lat: number, lon: number): string {
|
|
45
|
+
if (lat < BOUNDS.minLat || lat > BOUNDS.maxLat) {
|
|
46
|
+
throw new Error(
|
|
47
|
+
`Latitude ${lat} out of range. Must be between ${BOUNDS.minLat} and ${BOUNDS.maxLat}`
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
if (lon < BOUNDS.minLon || lon > BOUNDS.maxLon) {
|
|
51
|
+
throw new Error(
|
|
52
|
+
`Longitude ${lon} out of range. Must be between ${BOUNDS.minLon} and ${BOUNDS.maxLon}`
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
let minLat = BOUNDS.minLat;
|
|
57
|
+
let maxLat = BOUNDS.maxLat;
|
|
58
|
+
let minLon = BOUNDS.minLon;
|
|
59
|
+
let maxLon = BOUNDS.maxLon;
|
|
60
|
+
|
|
61
|
+
let digiPin = '';
|
|
62
|
+
|
|
63
|
+
for (let level = 1; level <= 10; level++) {
|
|
64
|
+
const latDiv = (maxLat - minLat) / 4;
|
|
65
|
+
const lonDiv = (maxLon - minLon) / 4;
|
|
66
|
+
|
|
67
|
+
let row = 3 - Math.floor((lat - minLat) / latDiv);
|
|
68
|
+
let col = Math.floor((lon - minLon) / lonDiv);
|
|
69
|
+
|
|
70
|
+
row = Math.max(0, Math.min(row, 3));
|
|
71
|
+
col = Math.max(0, Math.min(col, 3));
|
|
72
|
+
|
|
73
|
+
digiPin += DIGIPIN_GRID[row]![col]!;
|
|
74
|
+
|
|
75
|
+
if (level === 3 || level === 6) digiPin += '-';
|
|
76
|
+
|
|
77
|
+
maxLat = minLat + latDiv * (4 - row);
|
|
78
|
+
minLat = minLat + latDiv * (3 - row);
|
|
79
|
+
minLon = minLon + lonDiv * col;
|
|
80
|
+
maxLon = minLon + lonDiv;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return digiPin;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Decode a DigiPin code back to center-point coordinates.
|
|
88
|
+
* @throws if the DigiPin format is invalid
|
|
89
|
+
*/
|
|
90
|
+
export function getLatLngFromDigiPin(digiPin: string): DigiPinCoordinates {
|
|
91
|
+
const pin = digiPin.replace(/-/g, '');
|
|
92
|
+
if (pin.length !== 10) {
|
|
93
|
+
throw new Error('Invalid DIGIPIN: must be 10 characters (excluding dashes)');
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
let minLat = BOUNDS.minLat;
|
|
97
|
+
let maxLat = BOUNDS.maxLat;
|
|
98
|
+
let minLon = BOUNDS.minLon;
|
|
99
|
+
let maxLon = BOUNDS.maxLon;
|
|
100
|
+
|
|
101
|
+
for (let i = 0; i < 10; i++) {
|
|
102
|
+
const char = pin[i]!;
|
|
103
|
+
let found = false;
|
|
104
|
+
let ri = -1;
|
|
105
|
+
let ci = -1;
|
|
106
|
+
|
|
107
|
+
for (let r = 0; r < 4; r++) {
|
|
108
|
+
for (let c = 0; c < 4; c++) {
|
|
109
|
+
if (DIGIPIN_GRID[r]![c] === char) {
|
|
110
|
+
ri = r;
|
|
111
|
+
ci = c;
|
|
112
|
+
found = true;
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (found) break;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (!found) {
|
|
120
|
+
throw new Error(`Invalid character in DIGIPIN: '${char}'`);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const latDiv = (maxLat - minLat) / 4;
|
|
124
|
+
const lonDiv = (maxLon - minLon) / 4;
|
|
125
|
+
|
|
126
|
+
const lat1 = maxLat - latDiv * (ri + 1);
|
|
127
|
+
const lat2 = maxLat - latDiv * ri;
|
|
128
|
+
const lon1 = minLon + lonDiv * ci;
|
|
129
|
+
const lon2 = minLon + lonDiv * (ci + 1);
|
|
130
|
+
|
|
131
|
+
minLat = lat1;
|
|
132
|
+
maxLat = lat2;
|
|
133
|
+
minLon = lon1;
|
|
134
|
+
maxLon = lon2;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
latitude: ((minLat + maxLat) / 2).toFixed(6),
|
|
139
|
+
longitude: ((minLon + maxLon) / 2).toFixed(6),
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Validate a DigiPin string (format + character check).
|
|
145
|
+
*/
|
|
146
|
+
export function isValidDigiPin(digiPin: string): boolean {
|
|
147
|
+
if (!digiPin || typeof digiPin !== 'string') return false;
|
|
148
|
+
if (!/^[A-Z0-9]{3}-[A-Z0-9]{3}-[A-Z0-9]{4}$/i.test(digiPin.trim())) return false;
|
|
149
|
+
try {
|
|
150
|
+
getLatLngFromDigiPin(digiPin.trim().toUpperCase());
|
|
151
|
+
return true;
|
|
152
|
+
} catch {
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Check if a lat/lng point is within India's DigiPin coverage area.
|
|
159
|
+
*/
|
|
160
|
+
export function isWithinIndia(lat: number, lon: number): boolean {
|
|
161
|
+
return (
|
|
162
|
+
lat >= BOUNDS.minLat &&
|
|
163
|
+
lat <= BOUNDS.maxLat &&
|
|
164
|
+
lon >= BOUNDS.minLon &&
|
|
165
|
+
lon <= BOUNDS.maxLon
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export { BOUNDS as DIGIPIN_BOUNDS };
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2
|
+
type AnyStyle = any; // CSSProperties on web · StyleProp<ViewStyle> on native
|
|
3
|
+
|
|
4
|
+
// ─── Coordinates ────────────────────────────────────────────────────────────
|
|
5
|
+
|
|
6
|
+
export interface Coordinates {
|
|
7
|
+
lat: number;
|
|
8
|
+
lng: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// ─── API Response Types ──────────────────────────────────────────────────────
|
|
12
|
+
|
|
13
|
+
export interface AdministrativeInfo {
|
|
14
|
+
country?: string;
|
|
15
|
+
state: string;
|
|
16
|
+
division: string;
|
|
17
|
+
locality: string;
|
|
18
|
+
pincode: string;
|
|
19
|
+
delivery: string;
|
|
20
|
+
district: string;
|
|
21
|
+
mean_population_density?: number;
|
|
22
|
+
min_population_density?: number;
|
|
23
|
+
max_population_density?: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface LocationAlternative {
|
|
27
|
+
pincode: string;
|
|
28
|
+
name: string; // Locality name
|
|
29
|
+
branchType: string;
|
|
30
|
+
deliveryStatus: string;
|
|
31
|
+
district: string;
|
|
32
|
+
state: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface LocationLookupData {
|
|
36
|
+
coordinates: { latitude: number; longitude: number };
|
|
37
|
+
digipin: string;
|
|
38
|
+
administrative_info: AdministrativeInfo;
|
|
39
|
+
alternatives?: LocationAlternative[];
|
|
40
|
+
confidence: number;
|
|
41
|
+
source: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface LocationLookupResponse {
|
|
45
|
+
success: boolean;
|
|
46
|
+
data: LocationLookupData;
|
|
47
|
+
error?: string;
|
|
48
|
+
message?: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// ─── Complete Address (output) ───────────────────────────────────────────────
|
|
52
|
+
|
|
53
|
+
export interface CompleteAddress {
|
|
54
|
+
/** Official India Post DigiPin code for this location (~4m x 4m precision) */
|
|
55
|
+
digipin: string;
|
|
56
|
+
/** Latitude of the confirmed pin location */
|
|
57
|
+
lat: number;
|
|
58
|
+
/** Longitude of the confirmed pin location */
|
|
59
|
+
lng: number;
|
|
60
|
+
/** Auto-filled: State */
|
|
61
|
+
state: string;
|
|
62
|
+
/** Auto-filled: District */
|
|
63
|
+
district: string;
|
|
64
|
+
/** Auto-filled: Postal division */
|
|
65
|
+
division: string;
|
|
66
|
+
/** Auto-filled: Locality / Post Office area */
|
|
67
|
+
locality: string;
|
|
68
|
+
/** Auto-filled: 6-digit pincode */
|
|
69
|
+
pincode: string;
|
|
70
|
+
/** Auto-filled: Delivery status ("Delivery" | "Non Delivery") */
|
|
71
|
+
delivery: string;
|
|
72
|
+
/** Auto-filled: Country */
|
|
73
|
+
country: string;
|
|
74
|
+
/** Manual: Flat / House number */
|
|
75
|
+
flatNumber: string;
|
|
76
|
+
/** Manual: Floor number */
|
|
77
|
+
floorNumber: string;
|
|
78
|
+
/** Manual: Building / Society name */
|
|
79
|
+
buildingName: string;
|
|
80
|
+
/** Manual: Street / Area */
|
|
81
|
+
streetName: string;
|
|
82
|
+
/** Combined formatted address string */
|
|
83
|
+
formattedAddress: string;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// ─── Component Props ─────────────────────────────────────────────────────────
|
|
87
|
+
|
|
88
|
+
export interface MapPinSelectorProps {
|
|
89
|
+
onLocationConfirm: (lat: number, lng: number, digipin: string) => void;
|
|
90
|
+
defaultLat?: number;
|
|
91
|
+
defaultLng?: number;
|
|
92
|
+
/** Map height. String on web ('380px'), number on native (380). */
|
|
93
|
+
mapHeight?: string | number;
|
|
94
|
+
theme?: 'light' | 'dark';
|
|
95
|
+
/**
|
|
96
|
+
* URL to an India boundary GeoJSON file (FeatureCollection).
|
|
97
|
+
* When provided, a thin grey outline of India's border is drawn on the map
|
|
98
|
+
* to satisfy Indian government map policy requirements.
|
|
99
|
+
* The file is fetched in parallel with map initialisation — no visible delay.
|
|
100
|
+
* Example: indiaBoundaryUrl="/geojson/india.geojson"
|
|
101
|
+
*/
|
|
102
|
+
indiaBoundaryUrl?: string;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export interface AddressFormProps {
|
|
106
|
+
digipin: string;
|
|
107
|
+
lat: number;
|
|
108
|
+
lng: number;
|
|
109
|
+
apiKey: string;
|
|
110
|
+
apiBaseUrl?: string;
|
|
111
|
+
onAddressComplete: (address: CompleteAddress) => void;
|
|
112
|
+
onBack: () => void;
|
|
113
|
+
onError?: (error: Error) => void;
|
|
114
|
+
theme?: 'light' | 'dark';
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export interface CheckoutWidgetProps {
|
|
118
|
+
/**
|
|
119
|
+
* Your QuantaRoute API key.
|
|
120
|
+
* Get one free at https://quantaroute.com
|
|
121
|
+
*/
|
|
122
|
+
apiKey: string;
|
|
123
|
+
/** API base URL. Defaults to https://api.quantaroute.com */
|
|
124
|
+
apiBaseUrl?: string;
|
|
125
|
+
/** Callback fired when user completes the full address entry. */
|
|
126
|
+
onComplete: (address: CompleteAddress) => void;
|
|
127
|
+
/** Optional error handler. */
|
|
128
|
+
onError?: (error: Error) => void;
|
|
129
|
+
/** Default map latitude (center). If provided, map opens at this location. */
|
|
130
|
+
defaultLat?: number;
|
|
131
|
+
/** Default map longitude (center). */
|
|
132
|
+
defaultLng?: number;
|
|
133
|
+
/** Color theme. Defaults to 'light'. */
|
|
134
|
+
theme?: 'light' | 'dark';
|
|
135
|
+
/** CSS class name(s) to add to the root element (web only). */
|
|
136
|
+
className?: string;
|
|
137
|
+
/** Inline styles for the root element (CSSProperties on web, StyleProp<ViewStyle> on native). */
|
|
138
|
+
style?: AnyStyle;
|
|
139
|
+
/** Map area height. String on web ('380px'), number on native (380). Defaults to '380px' / 380. */
|
|
140
|
+
mapHeight?: string | number;
|
|
141
|
+
/** Widget header title. Defaults to 'Add Delivery Address'. */
|
|
142
|
+
title?: string;
|
|
143
|
+
/**
|
|
144
|
+
* URL to an India boundary GeoJSON file (FeatureCollection).
|
|
145
|
+
* When provided, a thin grey outline is drawn on the map for legal compliance.
|
|
146
|
+
* Host the file in your app's public folder and pass its path here.
|
|
147
|
+
* Example: indiaBoundaryUrl="/geojson/india.geojson"
|
|
148
|
+
*/
|
|
149
|
+
indiaBoundaryUrl?: string;
|
|
150
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
|
+
import { getDigiPin, isWithinIndia } from '../core/digipin';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Compute a DigiPin code from lat/lng entirely offline (no API call).
|
|
6
|
+
* Returns null if coordinates are outside India's bounds.
|
|
7
|
+
*
|
|
8
|
+
* The calculation is pure math (~0.1ms), so no debouncing is needed
|
|
9
|
+
* even when called on every marker drag event.
|
|
10
|
+
*/
|
|
11
|
+
export function useDigiPin(lat: number, lng: number): string | null {
|
|
12
|
+
return useMemo(() => {
|
|
13
|
+
if (!isWithinIndia(lat, lng)) return null;
|
|
14
|
+
try {
|
|
15
|
+
return getDigiPin(lat, lng);
|
|
16
|
+
} catch {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
}, [lat, lng]);
|
|
20
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { useState, useCallback } from 'react';
|
|
2
|
+
import * as Location from 'expo-location';
|
|
3
|
+
|
|
4
|
+
interface GeoState {
|
|
5
|
+
loading: boolean;
|
|
6
|
+
error: string | null;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Native (iOS/Android) geolocation hook using expo-location.
|
|
11
|
+
* Identical public API to useGeolocation.ts (web version) —
|
|
12
|
+
* Metro resolves this file on native, the .ts file on web.
|
|
13
|
+
*/
|
|
14
|
+
export function useGeolocation() {
|
|
15
|
+
const [state, setState] = useState<GeoState>({ loading: false, error: null });
|
|
16
|
+
|
|
17
|
+
const locate = useCallback((onSuccess: (lat: number, lng: number) => void) => {
|
|
18
|
+
setState({ loading: true, error: null });
|
|
19
|
+
|
|
20
|
+
void (async () => {
|
|
21
|
+
try {
|
|
22
|
+
const { status } = await Location.requestForegroundPermissionsAsync();
|
|
23
|
+
|
|
24
|
+
if (status !== Location.PermissionStatus.GRANTED) {
|
|
25
|
+
setState({
|
|
26
|
+
loading: false,
|
|
27
|
+
error: 'Location permission denied. Please enable it in Settings.',
|
|
28
|
+
});
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const loc = await Location.getCurrentPositionAsync({
|
|
33
|
+
accuracy: Location.Accuracy.High,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
setState({ loading: false, error: null });
|
|
37
|
+
onSuccess(loc.coords.latitude, loc.coords.longitude);
|
|
38
|
+
} catch (err) {
|
|
39
|
+
let message = 'Unable to get location. Try again or place the pin manually.';
|
|
40
|
+
if (err instanceof Error) {
|
|
41
|
+
if (err.message.includes('timed out')) {
|
|
42
|
+
message = 'Location request timed out. Try again.';
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
setState({ loading: false, error: message });
|
|
46
|
+
}
|
|
47
|
+
})();
|
|
48
|
+
}, []);
|
|
49
|
+
|
|
50
|
+
const clearError = useCallback(() => {
|
|
51
|
+
setState((s) => ({ ...s, error: null }));
|
|
52
|
+
}, []);
|
|
53
|
+
|
|
54
|
+
return { ...state, locate, clearError };
|
|
55
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { useState, useCallback } from 'react';
|
|
2
|
+
|
|
3
|
+
interface GeoState {
|
|
4
|
+
loading: boolean;
|
|
5
|
+
error: string | null;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Hook to imperatively request the user's current GPS position.
|
|
10
|
+
* Returns a `locate(onSuccess)` callback to trigger the permission prompt.
|
|
11
|
+
*/
|
|
12
|
+
export function useGeolocation() {
|
|
13
|
+
const [state, setState] = useState<GeoState>({ loading: false, error: null });
|
|
14
|
+
|
|
15
|
+
const locate = useCallback((onSuccess: (lat: number, lng: number) => void) => {
|
|
16
|
+
if (!navigator.geolocation) {
|
|
17
|
+
setState({ loading: false, error: 'Geolocation is not supported by your browser.' });
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
setState({ loading: true, error: null });
|
|
22
|
+
|
|
23
|
+
navigator.geolocation.getCurrentPosition(
|
|
24
|
+
({ coords }) => {
|
|
25
|
+
setState({ loading: false, error: null });
|
|
26
|
+
onSuccess(coords.latitude, coords.longitude);
|
|
27
|
+
},
|
|
28
|
+
(err) => {
|
|
29
|
+
let message = 'Unable to get location.';
|
|
30
|
+
if (err.code === err.PERMISSION_DENIED) {
|
|
31
|
+
message = 'Location permission denied. Please enable it in browser settings.';
|
|
32
|
+
} else if (err.code === err.POSITION_UNAVAILABLE) {
|
|
33
|
+
message = 'Location unavailable. Try again or place the pin manually.';
|
|
34
|
+
} else if (err.code === err.TIMEOUT) {
|
|
35
|
+
message = 'Location request timed out. Try again.';
|
|
36
|
+
}
|
|
37
|
+
setState({ loading: false, error: message });
|
|
38
|
+
},
|
|
39
|
+
{ enableHighAccuracy: true, timeout: 12_000, maximumAge: 0 }
|
|
40
|
+
);
|
|
41
|
+
}, []);
|
|
42
|
+
|
|
43
|
+
const clearError = useCallback(() => {
|
|
44
|
+
setState((s) => ({ ...s, error: null }));
|
|
45
|
+
}, []);
|
|
46
|
+
|
|
47
|
+
return { ...state, locate, clearError };
|
|
48
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @quantaroute/checkout
|
|
3
|
+
*
|
|
4
|
+
* DigiPin-powered smart address checkout widget — one import, three platforms.
|
|
5
|
+
*
|
|
6
|
+
* ┌─────────────────────────────────────────────────────────────────┐
|
|
7
|
+
* │ Platform │ Resolved by │ Map engine │
|
|
8
|
+
* │──────────────────│────────────────────────────────────────────── │
|
|
9
|
+
* │ Web │ Vite / Webpack / Next.js │ MapLibre GL JS│
|
|
10
|
+
* │ iOS / Android │ Metro (Expo / React Native)│ expo-osm-sdk │
|
|
11
|
+
* └─────────────────────────────────────────────────────────────────┘
|
|
12
|
+
*
|
|
13
|
+
* Web usage:
|
|
14
|
+
* import { CheckoutWidget } from '@quantaroute/checkout';
|
|
15
|
+
* import '@quantaroute/checkout/style.css';
|
|
16
|
+
*
|
|
17
|
+
* Native usage (Expo / React Native):
|
|
18
|
+
* import { CheckoutWidget } from '@quantaroute/checkout';
|
|
19
|
+
* // No CSS import needed — styles are React Native StyleSheet objects
|
|
20
|
+
*
|
|
21
|
+
* @see https://quantaroute.com/docs/checkout
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
// ── Main component (default export as well) ───────────────────────────────────
|
|
25
|
+
export { default as CheckoutWidget } from './components/CheckoutWidget';
|
|
26
|
+
export { default } from './components/CheckoutWidget';
|
|
27
|
+
|
|
28
|
+
// ── Sub-components (for advanced composition) ─────────────────────────────────
|
|
29
|
+
export { default as MapPinSelector } from './components/MapPinSelector';
|
|
30
|
+
export { default as AddressForm } from './components/AddressForm';
|
|
31
|
+
|
|
32
|
+
// ── Core utilities (offline, zero-dependency) ─────────────────────────────────
|
|
33
|
+
export {
|
|
34
|
+
getDigiPin,
|
|
35
|
+
getLatLngFromDigiPin,
|
|
36
|
+
isValidDigiPin,
|
|
37
|
+
isWithinIndia,
|
|
38
|
+
DIGIPIN_BOUNDS,
|
|
39
|
+
} from './core/digipin';
|
|
40
|
+
|
|
41
|
+
// ── API client ────────────────────────────────────────────────────────────────
|
|
42
|
+
export { lookupLocation, reverseGeocode } from './core/api';
|
|
43
|
+
export type { AddressComponents, ReverseGeocodeResponse } from './core/api';
|
|
44
|
+
|
|
45
|
+
// ── Hooks ─────────────────────────────────────────────────────────────────────
|
|
46
|
+
export { useGeolocation } from './hooks/useGeolocation';
|
|
47
|
+
export { useDigiPin } from './hooks/useDigiPin';
|
|
48
|
+
|
|
49
|
+
// ── TypeScript types ──────────────────────────────────────────────────────────
|
|
50
|
+
export type {
|
|
51
|
+
CheckoutWidgetProps,
|
|
52
|
+
MapPinSelectorProps,
|
|
53
|
+
AddressFormProps,
|
|
54
|
+
CompleteAddress,
|
|
55
|
+
AdministrativeInfo,
|
|
56
|
+
LocationAlternative,
|
|
57
|
+
LocationLookupResponse,
|
|
58
|
+
Coordinates,
|
|
59
|
+
} from './core/types';
|