@salla.sa/ui-address-autocomplete-widget 0.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.
Files changed (3) hide show
  1. package/README.md +501 -0
  2. package/dist/index.js +1597 -0
  3. package/package.json +41 -0
package/README.md ADDED
@@ -0,0 +1,501 @@
1
+ # @salla.sa/ui-address-autocomplete-widget
2
+
3
+ A framework-agnostic address autocomplete web component built with Lit and Google Places API. Works seamlessly with React, Vue, Angular, or vanilla JavaScript.
4
+
5
+ ## 🚀 Default Configuration
6
+
7
+ The component is **pre-configured to work with Salla's API (`api.salla.dev`)** out of the box. No additional proxy setup needed!
8
+
9
+ **Default Endpoints:**
10
+ - Autocomplete: `https://api.salla.dev/store/v1/checkout/address/autocomplete/query`
11
+ - Details: `https://api.salla.dev/store/v1/checkout/address/autocomplete/details`
12
+
13
+ **Expected Response Format:**
14
+
15
+ All responses must be wrapped in the standard Salla API format:
16
+
17
+ ```typescript
18
+ // Autocomplete response
19
+ {
20
+ "success": true,
21
+ "status": 200,
22
+ "data": [
23
+ {
24
+ "id": "ChIJOwg_06VPwokRYv534QaPC8g",
25
+ "formatted_address": "Empire State Building, New York, NY, USA",
26
+ "description": "Empire State Building",
27
+ "types": ["establishment", "point_of_interest"]
28
+ }
29
+ ]
30
+ }
31
+
32
+ // Details response
33
+ {
34
+ "success": true,
35
+ "status": 200,
36
+ "data": {
37
+ "id": "ChIJOwg_06VPwokRYv534QaPC8g",
38
+ "formatted_address": "20 W 34th St., New York, NY 10001, USA",
39
+ "address_components": [
40
+ {
41
+ "long_name": "20",
42
+ "short_name": "20",
43
+ "types": ["street_number"]
44
+ }
45
+ // ... more components
46
+ ],
47
+ "geometry": {
48
+ "location": { "lat": 40.748817, "lng": -73.985428 }
49
+ },
50
+ "types": ["street_address"]
51
+ }
52
+ }
53
+
54
+ // Error response
55
+ {
56
+ "success": false,
57
+ "status": 400,
58
+ "message": "Input is required"
59
+ }
60
+ ```
61
+
62
+ You can override the `proxy-url` prop if you want to use your own backend proxy (must follow the same response format).
63
+
64
+ ### Why Backend Proxy?
65
+ - ✅ No CORS issues - API calls from server-side
66
+ - ✅ API key hidden from users
67
+ - ✅ Full control over security & rate limiting
68
+ - ✅ No external SDK dependencies (~100KB saved)
69
+
70
+ **See [CORS_EXPLANATION.md](./CORS_EXPLANATION.md) for detailed explanation.**
71
+
72
+ ## Features
73
+
74
+ ✅ **Framework Agnostic** - Works with any framework or vanilla JS
75
+ ✅ **Full TypeScript Support** - Complete type definitions included
76
+ ✅ **No SDK Required** - Uses REST API via backend proxy (no Google SDK loading)
77
+ ✅ **Session Token Management** - Optimized billing with automatic session handling
78
+ ✅ **Address Validation Support** - Detects if validation is available for the country
79
+ ✅ **UX Best Practices** - Keyboard navigation, loading states, error handling
80
+ ✅ **Customizable** - Extensive props for location bias, country restrictions, and more
81
+ ✅ **Highlighted Matches** - Visual highlighting of matched text in suggestions
82
+ ✅ **Throttled Requests** - Efficient API usage with request throttling
83
+
84
+ ## Installation
85
+
86
+ ```bash
87
+ npm install @salla.sa/ui-address-autocomplete-widget
88
+ ```
89
+
90
+ ## Usage
91
+
92
+ ### Quick Start (Using Salla API)
93
+
94
+ The component works immediately with Salla's backend API:
95
+
96
+ ```html
97
+ <!DOCTYPE html>
98
+ <html>
99
+ <head>
100
+ <script type="module">
101
+ import '@salla.sa/ui-address-autocomplete-widget';
102
+
103
+ const autocomplete = document.querySelector('salla-address-autocomplete');
104
+
105
+ // Optional: Set auth token if required by your Salla integration
106
+ autocomplete.authToken = 'your-salla-auth-token';
107
+
108
+ autocomplete.addEventListener('place-select', (e) => {
109
+ console.log('Selected place:', e.detail.place);
110
+ console.log('Validation support:', e.detail.validationSupport);
111
+ });
112
+ </script>
113
+ </head>
114
+ <body>
115
+ <salla-address-autocomplete
116
+ placeholder="Enter an address..."
117
+ label="Delivery Address"
118
+ ></salla-address-autocomplete>
119
+ </body>
120
+ </html>
121
+ ```
122
+
123
+ ### Using Custom Backend Proxy
124
+
125
+ If you want to use your own backend proxy instead of Salla's API:
126
+
127
+ ```html
128
+ <salla-address-autocomplete
129
+ proxy-url="http://localhost:3001/api/places"
130
+ placeholder="Enter an address..."
131
+ label="Delivery Address"
132
+ ></salla-address-autocomplete>
133
+ ```
134
+
135
+ See [proxy-examples/](./proxy-examples/) for complete backend setup guide.
136
+
137
+ ### React
138
+
139
+ ```tsx
140
+ import '@salla.sa/ui-address-autocomplete-widget';
141
+
142
+ function CheckoutForm() {
143
+ const [authToken, setAuthToken] = useState('');
144
+
145
+ const handlePlaceSelect = (e: CustomEvent) => {
146
+ const { place, validationSupport } = e.detail;
147
+ console.log('Selected:', place);
148
+ console.log('Country:', validationSupport.countryCode);
149
+ };
150
+
151
+ return (
152
+ <salla-address-autocomplete
153
+ auth-token={authToken}
154
+ placeholder="Enter delivery address..."
155
+ label="Address"
156
+ included-region-codes={JSON.stringify(['US', 'CA'])}
157
+ onplace-select={handlePlaceSelect}
158
+ />
159
+ );
160
+ }
161
+ ```
162
+
163
+ ### Vue 3
164
+
165
+ ```vue
166
+ <template>
167
+ <salla-address-autocomplete
168
+ :auth-token="authToken"
169
+ placeholder="Enter address..."
170
+ label="Address"
171
+ :included-region-codes="['SA', 'AE']"
172
+ @place-select="handlePlaceSelect"
173
+ />
174
+ </template>
175
+
176
+ <script setup lang="ts">
177
+ import { ref } from 'vue';
178
+ import '@salla.sa/ui-address-autocomplete-widget';
179
+
180
+ const authToken = ref('');
181
+
182
+ const handlePlaceSelect = (e: CustomEvent) => {
183
+ console.log('Selected place:', e.detail.place);
184
+ };
185
+ </script>
186
+ ```
187
+
188
+ ### Angular
189
+
190
+ ```typescript
191
+ // app.module.ts
192
+ import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
193
+ import '@salla.sa/ui-address-autocomplete-widget';
194
+
195
+ @NgModule({
196
+ schemas: [CUSTOM_ELEMENTS_SCHEMA]
197
+ })
198
+ export class AppModule {}
199
+
200
+ // component.html
201
+ <salla-address-autocomplete
202
+ [attr.auth-token]="authToken"
203
+ placeholder="Enter address..."
204
+ label="Address"
205
+ (place-select)="handlePlaceSelect($event)"
206
+ ></salla-address-autocomplete>
207
+
208
+ // component.ts
209
+ authToken = '';
210
+
211
+ handlePlaceSelect(event: CustomEvent) {
212
+ console.log('Selected:', event.detail.place);
213
+ }
214
+ ```
215
+
216
+ ## Properties
217
+
218
+ | Property | Type | Default | Description |
219
+ |----------|------|---------|-------------|
220
+ | `proxyUrl` | `string` | `'https://api.salla.dev'` | Base URL for proxy server |
221
+ | `authToken` | `string\|null` | `null` | Optional bearer token for proxy authentication |
222
+ | `placeholder` | `string` | `''` | Input placeholder text |
223
+ | `value` | `string` | `''` | Initial/controlled value |
224
+ | `label` | `string\|null` | `null` | Label text above input |
225
+ | `className` | `string` | `''` | CSS class for wrapper element |
226
+ | `country` | `string\|null` | `null` | Inline country filter (e.g., `"SA"` or `"SA,AE"`). Sets `includedRegionCodes` |
227
+ | `includedRegionCodes` | `string[]\|null` | `null` | Restrict to specific countries (e.g., `['SA', 'AE']`). Sent as `country=SA,AE` query parameter |
228
+ | `locationBias` | `object\|null` | `null` | Bias results to location (radius + center) |
229
+ | `locationRestriction` | `object\|null` | `null` | Restrict results to location bounds |
230
+ | `types` | `string[]\|null` | `null` | Filter by place types (e.g., `['geocode', 'establishment']`) |
231
+ | `fields` | `string[]` | `['formatted_address', 'geometry']` | Place data fields to fetch (cost optimized) |
232
+ | `strictBounds` | `boolean` | `false` | Only return results within bounds |
233
+ | `useSessionToken` | `boolean` | `true` | Enable session tokens (UUID v4) for billing optimization |
234
+ | `onValidateAddress` | `function\|null` | `null` | Backend validation callback |
235
+ | `debounceDelay` | `number` | `300` | Milliseconds to wait after typing stops |
236
+ | `minChars` | `number` | `3` | Minimum characters before search |
237
+
238
+ ### Default Fields (Cost Optimized)
239
+
240
+ ```javascript
241
+ [
242
+ 'formatted_address', // Full address string
243
+ 'geometry' // Lat/lng coordinates
244
+ ]
245
+ ```
246
+
247
+ **Note:** Default configuration fetches **only essential fields** to minimize API costs (~$5/1000 requests).
248
+
249
+ To include country detection and validation support, add `address_components`:
250
+ ```javascript
251
+ autocomplete.fields = ['formatted_address', 'geometry', 'address_components'];
252
+ ```
253
+
254
+ ## Events
255
+
256
+ ### `place-select`
257
+
258
+ Fired when a user selects an address from the dropdown.
259
+
260
+ ```typescript
261
+ interface PlaceSelectEvent {
262
+ detail: {
263
+ place: PlaceDetails; // Complete place object with requested fields
264
+ validationSupport: {
265
+ isSupported: boolean | null; // true/false/null (if unknown)
266
+ countryCode: string | null; // ISO country code
267
+ };
268
+ };
269
+ }
270
+ ```
271
+
272
+ Example:
273
+ ```javascript
274
+ autocomplete.addEventListener('place-select', (e) => {
275
+ const { place, validationSupport } = e.detail;
276
+
277
+ if (validationSupport.isSupported) {
278
+ // Address Validation API is supported for this country
279
+ // You can use validateAddress() method
280
+ }
281
+
282
+ console.log('Address:', place.formatted_address);
283
+ console.log('Coordinates:', place.geometry.location);
284
+ console.log('Country:', validationSupport.countryCode);
285
+ });
286
+ ```
287
+
288
+ ### `place_changed`
289
+
290
+ Alias for `place-select` (for backward compatibility).
291
+
292
+ ### `input`
293
+
294
+ Fired on every input change.
295
+
296
+ ```typescript
297
+ interface InputEvent {
298
+ detail: {
299
+ value: string;
300
+ };
301
+ }
302
+ ```
303
+
304
+ ## Methods
305
+
306
+ ### `validateAddress(place: PlaceDetails)`
307
+
308
+ Validates an address using your backend API. Requires `onValidateAddress` callback.
309
+
310
+ ```javascript
311
+ const autocomplete = document.querySelector('salla-address-autocomplete');
312
+
313
+ autocomplete.onValidateAddress = async ({ place, sessionToken, validationSupport }) => {
314
+ // Call your backend API
315
+ const response = await fetch('/api/validate-address', {
316
+ method: 'POST',
317
+ headers: { 'Content-Type': 'application/json' },
318
+ body: JSON.stringify({
319
+ address: place.formatted_address,
320
+ sessionToken,
321
+ countryCode: validationSupport.countryCode
322
+ })
323
+ });
324
+ return response.json();
325
+ };
326
+
327
+ // Later, when user submits the form:
328
+ autocomplete.addEventListener('place-select', async (e) => {
329
+ const result = await autocomplete.validateAddress(e.detail.place);
330
+ console.log('Validation result:', result);
331
+ });
332
+ ```
333
+
334
+ ### `getValidationSupport(place: PlaceDetails)`
335
+
336
+ Returns validation support information for a place.
337
+
338
+ ```javascript
339
+ const support = autocomplete.getValidationSupport(place);
340
+ console.log(support); // { isSupported: true, countryCode: 'US' }
341
+ ```
342
+
343
+ ## Advanced Examples
344
+
345
+ ### Country-Restricted Search
346
+
347
+ ```html
348
+ <salla-address-autocomplete
349
+ placeholder="Enter address in Saudi Arabia..."
350
+ country="SA"
351
+ ></salla-address-autocomplete>
352
+
353
+ <!-- OR using JavaScript for multiple countries: -->
354
+ <salla-address-autocomplete
355
+ id="multi-country"
356
+ placeholder="Enter address in Saudi Arabia or UAE..."
357
+ ></salla-address-autocomplete>
358
+
359
+ <script>
360
+ const autocomplete = document.getElementById('multi-country');
361
+ autocomplete.includedRegionCodes = ['SA', 'AE'];
362
+ // OR: autocomplete.country = 'SA,AE';
363
+ </script>
364
+ ```
365
+
366
+ ### Location Bias (Prefer nearby results)
367
+
368
+ ```javascript
369
+ autocomplete.locationBias = {
370
+ radius: 50000, // 50km radius
371
+ center: { lat: 24.7136, lng: 46.6753 } // Riyadh coordinates
372
+ };
373
+ ```
374
+
375
+ ### Custom Fields (Cost Optimization)
376
+
377
+ ```javascript
378
+ // Minimal fields for cost savings
379
+ autocomplete.fields = ['formatted_address', 'geometry'];
380
+
381
+ // With address components for validation
382
+ autocomplete.fields = [
383
+ 'formatted_address',
384
+ 'address_components',
385
+ 'geometry',
386
+ 'types'
387
+ ];
388
+ ```
389
+
390
+ ### Address Validation with Backend
391
+
392
+ ```javascript
393
+ autocomplete.onValidateAddress = async ({ place, sessionToken, validationSupport }) => {
394
+ if (!validationSupport.isSupported) {
395
+ return { valid: true, message: 'Validation not available for this country' };
396
+ }
397
+
398
+ const response = await fetch('https://your-api.com/validate', {
399
+ method: 'POST',
400
+ headers: { 'Content-Type': 'application/json' },
401
+ body: JSON.stringify({
402
+ address: place.formatted_address,
403
+ sessionToken
404
+ })
405
+ });
406
+
407
+ return response.json();
408
+ };
409
+ ```
410
+
411
+ ### Using Bearer Token Authentication
412
+
413
+ If your proxy requires authentication, use the `authToken` prop:
414
+
415
+ ```html
416
+ <salla-address-autocomplete
417
+ proxy-url="https://your-api.com/places"
418
+ auth-token="your-bearer-token-here"
419
+ placeholder="Enter address..."
420
+ ></salla-address-autocomplete>
421
+ ```
422
+
423
+ JavaScript:
424
+ ```javascript
425
+ const autocomplete = document.querySelector('salla-address-autocomplete');
426
+ autocomplete.authToken = await getAuthToken(); // Get token from your auth system
427
+ ```
428
+
429
+ React:
430
+ ```tsx
431
+ <salla-address-autocomplete
432
+ proxy-url="https://your-api.com/places"
433
+ auth-token={authToken}
434
+ placeholder="Enter address..."
435
+ />
436
+ ```
437
+
438
+ ## Styling
439
+
440
+ The component uses Shadow DOM with default styles. You can customize via CSS custom properties:
441
+
442
+ ```css
443
+ salla-address-autocomplete {
444
+ --input-border-color: #d1d5db;
445
+ --input-focus-color: #3b82f6;
446
+ --dropdown-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
447
+ }
448
+ ```
449
+
450
+ Or provide custom styles via `className`:
451
+
452
+ ```html
453
+ <salla-address-autocomplete
454
+ class-name="my-custom-class"
455
+ ></salla-address-autocomplete>
456
+ ```
457
+
458
+ ## Google Places API Setup
459
+
460
+ 1. Go to [Google Cloud Console](https://console.cloud.google.com/)
461
+ 2. Enable **Places API** and **Geocoding API**
462
+ 3. Create an API key with restrictions
463
+ 4. Add HTTP referrer restrictions for security
464
+
465
+ ## Cost Optimization Tips
466
+
467
+ 1. **Use session tokens** (`useSessionToken: true`) - Groups requests for better pricing
468
+ 2. **Smart place details optimization** - Automatically skips details API call when location data is already in autocomplete response (saves 1 API call per selection when data is available)
469
+ 3. **Fetch only needed fields** - Each field tier has different pricing (default is cost-optimized)
470
+ 4. **Use address validation** - Free autocomplete in 53 countries when using validation API
471
+ 5. **Restrict by country** - Reduces irrelevant results
472
+ 6. **Debounce requests** - Built-in 300ms debouncing reduces API calls by ~93%
473
+ 7. **Minimum characters** - Default 3 characters before search starts
474
+
475
+ ## Browser Support
476
+
477
+ - Chrome/Edge 90+
478
+ - Firefox 88+
479
+ - Safari 14+
480
+ - All modern browsers with Custom Elements v1 support
481
+
482
+ ## TypeScript Support
483
+
484
+ Full TypeScript definitions included:
485
+
486
+ ```typescript
487
+ import type {
488
+ PlaceDetails,
489
+ AutocompletePrediction,
490
+ ValidationSupport,
491
+ LocationBias
492
+ } from '@salla.sa/ui-address-autocomplete-widget';
493
+ ```
494
+
495
+ ## License
496
+
497
+ MIT
498
+
499
+ ## Support
500
+
501
+ For issues and questions, please visit: [GitHub Issues](https://github.com/salla-sa/ui-address-autocomplete-widget/issues)