places-autocomplete-svelte 2.2.12 → 2.2.13
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 +28 -0
- package/dist/PlaceAutocomplete.svelte +68 -25
- package/dist/PlaceAutocomplete.svelte.d.ts +5 -1
- package/dist/helpers.js +13 -49
- package/package.json +16 -16
package/README.md
CHANGED
|
@@ -20,6 +20,7 @@ Need this functionality for a non-Svelte project? Check out our companion vanill
|
|
|
20
20
|
* Automatically handles **session tokens** for cost management per Google's guidelines.
|
|
21
21
|
* **Debounced Input:** Limits API calls while the user is typing (configurable).
|
|
22
22
|
* **Suggestion Highlighting:** Automatically highlights the portion of text matching the user's input in the suggestions list.
|
|
23
|
+
* **Imperative API:** Exposes `clear()`, `focus()`, and `getRequestParams()` methods for direct control over the component.
|
|
23
24
|
* **Customizable Styling:** Easily override default styles or apply your own using the `options.classes` prop. Built with [Tailwind CSS](https://tailwindcss.com/) utility classes by default.
|
|
24
25
|
* **TypeScript Support:** Fully written in TypeScript with included type definitions.
|
|
25
26
|
* **Event Handling:** Provides `onResponse` and `onError` callbacks.
|
|
@@ -73,6 +74,7 @@ yarn add places-autocomplete-svelte
|
|
|
73
74
|
import { PlaceAutocomplete } from 'places-autocomplete-svelte';
|
|
74
75
|
import type { PlaceResult, ComponentOptions, RequestParams } from 'places-autocomplete-svelte/interfaces'; // Adjust path if needed
|
|
75
76
|
|
|
77
|
+
|
|
76
78
|
// Get API Key securely (e.g., from environment variables)
|
|
77
79
|
const PUBLIC_GOOGLE_MAPS_API_KEY = import.meta.env.VITE_PUBLIC_GOOGLE_MAPS_API_KEY;
|
|
78
80
|
let fullResponse: PlaceResult | null = $state(null);
|
|
@@ -164,7 +166,33 @@ const options: Partial<ComponentOptions> = $state({
|
|
|
164
166
|
| onResponse | (response: PlaceResult) => void | Yes | - | Callback function triggered with the selected place details (PlaceResult object) after fetchFields is complete. |
|
|
165
167
|
| onError | (error: string) => void | Yes | - | Callback function triggered when an error occurs (API loading, fetching suggestions, fetching details). |
|
|
166
168
|
|
|
169
|
+
Component Methods (Imperative API)
|
|
170
|
+
----------------------------------
|
|
171
|
+
|
|
172
|
+
For advanced use cases, you can get a reference to the component instance using `bind:this` and call its methods directly. This is useful for controlling the component from a parent, such as clearing the input from an external button.
|
|
173
|
+
|
|
174
|
+
**Example Setup:**
|
|
175
|
+
|
|
176
|
+
```svelte
|
|
177
|
+
<script lang="ts">
|
|
178
|
+
import PlaceAutocomplete from 'places-autocomplete-svelte';
|
|
179
|
+
let autocompleteComponent = $state(null); // This will hold the component instance
|
|
180
|
+
</script>
|
|
181
|
+
|
|
182
|
+
<PlaceAutocomplete bind:this={autocompleteComponent} ... />
|
|
183
|
+
|
|
184
|
+
<button onclick={() => autocompleteComponent.clear()}>Clear</button>
|
|
185
|
+
<button onclick={() => autocompleteComponent.focus()}>Focus Input</button>
|
|
186
|
+
<button onclick={() => console.log(JSON.stringify(autocompleteComponent.getRequestParams()))}>Get Request Params</button>
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
**vailable Methods:**
|
|
167
190
|
|
|
191
|
+
| Method | Signature | Description |
|
|
192
|
+
| :-- | :-- | :-- |
|
|
193
|
+
| `clear()` | `() => void` | Clears the input field, removes all suggestions, and resets the Google Places session token. |
|
|
194
|
+
| `focus()` | `() => void` | Programmatically sets focus on the text input field. |
|
|
195
|
+
| `getRequestParams()` | `() => RequestParams` | Returns the component's current internal `requestParams` object. Useful for debugging or state inspection. |
|
|
168
196
|
|
|
169
197
|
### Options
|
|
170
198
|
|
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
validateRequestParams,
|
|
8
8
|
formatDistance,
|
|
9
9
|
validateFetchFields,
|
|
10
|
-
componentOptions
|
|
11
10
|
} from './helpers.js';
|
|
12
11
|
const { Loader } = GMaps;
|
|
13
12
|
|
|
@@ -24,6 +23,8 @@
|
|
|
24
23
|
requestParams = {}
|
|
25
24
|
}: Props = $props();
|
|
26
25
|
|
|
26
|
+
const isDefaultOnResponse = onResponse.toString() === ((response: PlaceResult) => {}).toString();
|
|
27
|
+
|
|
27
28
|
// validate options
|
|
28
29
|
options = validateOptions(options);
|
|
29
30
|
//console.log(options);
|
|
@@ -32,13 +33,7 @@
|
|
|
32
33
|
fetchFields = validateFetchFields(fetchFields);
|
|
33
34
|
//console.log(fetchFields);
|
|
34
35
|
|
|
35
|
-
//
|
|
36
|
-
let cl = $state(options.classes ?? {});
|
|
37
|
-
// reset keyboard classes
|
|
38
|
-
const resetKbdClasses = () => {
|
|
39
|
-
cl.kbd_down = options.classes?.kbd_down ?? componentOptions.classes?.kbd_down ?? '';
|
|
40
|
-
cl.kbd_up = options.classes?.kbd_up ?? componentOptions.classes?.kbd_up ?? '';
|
|
41
|
-
};
|
|
36
|
+
let kbdAction = $state(''); // 'up', 'down', or 'escape'
|
|
42
37
|
|
|
43
38
|
// Local variables
|
|
44
39
|
let inputRef: HTMLInputElement;
|
|
@@ -64,12 +59,12 @@
|
|
|
64
59
|
*/
|
|
65
60
|
const reset = (placeData?: PlaceResult) => {
|
|
66
61
|
currentSuggestion = -1;
|
|
67
|
-
if(options?.clear_input == false){
|
|
62
|
+
if (options?.clear_input == false) {
|
|
68
63
|
if (placeData && placeData.formattedAddress) {
|
|
69
64
|
// set input to formatted address
|
|
70
65
|
request.input = placeData.formattedAddress;
|
|
71
66
|
}
|
|
72
|
-
}else{
|
|
67
|
+
} else {
|
|
73
68
|
request.input = '';
|
|
74
69
|
}
|
|
75
70
|
results = [];
|
|
@@ -77,6 +72,36 @@
|
|
|
77
72
|
//console.log('reset completed', results);
|
|
78
73
|
};
|
|
79
74
|
|
|
75
|
+
/**
|
|
76
|
+
* Clears the autocomplete input field and suggestions list.
|
|
77
|
+
* Refreshes the Google Places session token for a new session.
|
|
78
|
+
* Can be called imperatively on the component instance.
|
|
79
|
+
* @example
|
|
80
|
+
* let autocompleteComponent;
|
|
81
|
+
* autocompleteComponent.clear();
|
|
82
|
+
*/
|
|
83
|
+
export function clear() {
|
|
84
|
+
reset(); // The existing reset function already handles this
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Programmatically focuses the input element.
|
|
89
|
+
* @example
|
|
90
|
+
* autocompleteComponent.focus();
|
|
91
|
+
*/
|
|
92
|
+
export function focus() {
|
|
93
|
+
inputRef?.focus();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Returns the current internal request parameters.
|
|
98
|
+
* Useful for debugging or inspecting the component's state.
|
|
99
|
+
* @returns {RequestParams}
|
|
100
|
+
*/
|
|
101
|
+
export function getRequestParams() {
|
|
102
|
+
return request;
|
|
103
|
+
}
|
|
104
|
+
|
|
80
105
|
/**
|
|
81
106
|
* Debounce function to limit the rate at which a function can fire.
|
|
82
107
|
* @param func
|
|
@@ -204,8 +229,6 @@
|
|
|
204
229
|
(e.name || 'An error occurred') + ' - ' + (e.message || 'error fetching place details')
|
|
205
230
|
);
|
|
206
231
|
}
|
|
207
|
-
|
|
208
|
-
|
|
209
232
|
};
|
|
210
233
|
|
|
211
234
|
/**
|
|
@@ -223,6 +246,11 @@
|
|
|
223
246
|
* Initialize the Google Maps JavaScript API Loader.
|
|
224
247
|
*/
|
|
225
248
|
onMount(async (): Promise<void> => {
|
|
249
|
+
if (isDefaultOnResponse) {
|
|
250
|
+
console.warn(
|
|
251
|
+
'PlaceAutocomplete: The `onResponse` callback has not been provided. Selected place data will not be handled. See documentation for usage.'
|
|
252
|
+
);
|
|
253
|
+
}
|
|
226
254
|
if (options.autofocus) {
|
|
227
255
|
// focus on the input
|
|
228
256
|
inputRef.focus();
|
|
@@ -236,7 +264,7 @@
|
|
|
236
264
|
libraries: ['places']
|
|
237
265
|
});
|
|
238
266
|
|
|
239
|
-
const { AutocompleteSessionToken, AutocompleteSuggestion} =
|
|
267
|
+
const { AutocompleteSessionToken, AutocompleteSuggestion } =
|
|
240
268
|
await loader.importLibrary('places');
|
|
241
269
|
|
|
242
270
|
placesApi.AutocompleteSessionToken = AutocompleteSessionToken;
|
|
@@ -258,28 +286,27 @@
|
|
|
258
286
|
function onKeyDown(e: KeyboardEvent) {
|
|
259
287
|
if (e.key === 'ArrowDown') {
|
|
260
288
|
currentSuggestion = Math.min(currentSuggestion + 1, results.length - 1);
|
|
261
|
-
|
|
262
|
-
cl.kbd_down += ' ' + (cl?.kbd_active ?? 'bg-indigo-500 text-white');
|
|
289
|
+
kbdAction = 'down';
|
|
263
290
|
} else if (e.key === 'ArrowUp') {
|
|
264
291
|
currentSuggestion = Math.max(currentSuggestion - 1, 0);
|
|
265
|
-
|
|
266
|
-
cl.kbd_up += ' ' + (cl?.kbd_active ?? 'bg-indigo-500 text-white');
|
|
292
|
+
kbdAction = 'up';
|
|
267
293
|
} else if (e.key === 'Enter') {
|
|
268
294
|
e.preventDefault();
|
|
269
295
|
if (currentSuggestion >= 0) {
|
|
270
296
|
onPlaceSelected(results[currentSuggestion].place);
|
|
271
297
|
}
|
|
272
298
|
} else if (e.key === 'Escape') {
|
|
273
|
-
|
|
299
|
+
kbdAction = 'escape';
|
|
274
300
|
request.input = '';
|
|
275
301
|
reset();
|
|
276
302
|
}
|
|
277
303
|
// Optional: Scroll suggestion into view
|
|
278
304
|
const selectedElement = document.getElementById(`option-${currentSuggestion + 1}`);
|
|
279
305
|
selectedElement?.scrollIntoView({ block: 'nearest' });
|
|
306
|
+
// Reset the action state after a short delay to allow CSS transition to finish
|
|
280
307
|
setTimeout(() => {
|
|
281
|
-
|
|
282
|
-
},
|
|
308
|
+
kbdAction = '';
|
|
309
|
+
}, 200);
|
|
283
310
|
}
|
|
284
311
|
|
|
285
312
|
// Handle click outside the input
|
|
@@ -291,7 +318,7 @@
|
|
|
291
318
|
}
|
|
292
319
|
</script>
|
|
293
320
|
|
|
294
|
-
<svelte:window onkeydown={onKeyDown} onclick={handleClickOutside}/>
|
|
321
|
+
<svelte:window onkeydown={onKeyDown} onclick={handleClickOutside} />
|
|
295
322
|
|
|
296
323
|
<section class={options.classes?.section}>
|
|
297
324
|
{#if options?.label ?? ''}
|
|
@@ -326,11 +353,15 @@
|
|
|
326
353
|
|
|
327
354
|
{#if results.length > 0}
|
|
328
355
|
<div class={options.classes?.kbd_container ?? ''}>
|
|
329
|
-
<kbd class={options.classes?.kbd_escape ?? ''}
|
|
330
|
-
|
|
331
|
-
|
|
356
|
+
<kbd class={options.classes?.kbd_escape ?? ''} class:kbd-active={kbdAction === 'escape'}
|
|
357
|
+
>Esc</kbd
|
|
358
|
+
>
|
|
359
|
+
<kbd class={options.classes?.kbd_up ?? ''} class:kbd-active={kbdAction === 'up'}>⇑</kbd
|
|
360
|
+
>
|
|
361
|
+
<kbd class={options.classes?.kbd_down ?? ''} class:kbd-active={kbdAction === 'down'}
|
|
362
|
+
>⇓</kbd
|
|
363
|
+
>
|
|
332
364
|
</div>
|
|
333
|
-
|
|
334
365
|
<ul class={options.classes?.ul ?? ''} id="options" role="listbox">
|
|
335
366
|
{#each results as p, i}
|
|
336
367
|
<li
|
|
@@ -389,3 +420,15 @@
|
|
|
389
420
|
{/if}
|
|
390
421
|
</div>
|
|
391
422
|
</section>
|
|
423
|
+
|
|
424
|
+
<style>
|
|
425
|
+
/* Combining Tailwind CSS classes bg-indigo-500 text-white */
|
|
426
|
+
.kbd-active {
|
|
427
|
+
/* Use the active class from options if available, or a default */
|
|
428
|
+
background-color: var(--kbd-active-bg, #4f46e5); /* Indigo 500 */
|
|
429
|
+
color: var(--kbd-active-color, white);
|
|
430
|
+
transition:
|
|
431
|
+
background-color 0.1s ease-in-out,
|
|
432
|
+
color 0.1s ease-in-out;
|
|
433
|
+
}
|
|
434
|
+
</style>
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import type { Props } from './interfaces.js';
|
|
2
|
-
declare const PlaceAutocomplete: import("svelte").Component<Props, {
|
|
2
|
+
declare const PlaceAutocomplete: import("svelte").Component<Props, {
|
|
3
|
+
clear: () => void;
|
|
4
|
+
focus: () => void;
|
|
5
|
+
getRequestParams: () => import("./interfaces.js").RequestParams;
|
|
6
|
+
}, "onResponse" | "onError">;
|
|
3
7
|
type PlaceAutocomplete = ReturnType<typeof PlaceAutocomplete>;
|
|
4
8
|
export default PlaceAutocomplete;
|
package/dist/helpers.js
CHANGED
|
@@ -331,56 +331,20 @@ export const componentOptions = {
|
|
|
331
331
|
* @param options
|
|
332
332
|
*/
|
|
333
333
|
export const validateOptions = (options) => {
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
for (const key in validatedOptions) {
|
|
337
|
-
if (key in options) {
|
|
338
|
-
switch (key) {
|
|
339
|
-
case 'autofocus':
|
|
340
|
-
validatedOptions.autofocus = Boolean(options.autofocus);
|
|
341
|
-
break;
|
|
342
|
-
case 'autocomplete':
|
|
343
|
-
validatedOptions.autocomplete = String(options.autocomplete);
|
|
344
|
-
break;
|
|
345
|
-
case 'placeholder':
|
|
346
|
-
validatedOptions.placeholder = String(options.placeholder);
|
|
347
|
-
break;
|
|
348
|
-
case 'distance':
|
|
349
|
-
validatedOptions.distance = Boolean(options.distance);
|
|
350
|
-
break;
|
|
351
|
-
case 'label':
|
|
352
|
-
validatedOptions.label = String(options.label);
|
|
353
|
-
break;
|
|
354
|
-
case 'distance_units':
|
|
355
|
-
validatedOptions.distance_units = String(options.distance_units);
|
|
356
|
-
break;
|
|
357
|
-
case 'classes':
|
|
358
|
-
if (options.classes && typeof options.classes === 'object') {
|
|
359
|
-
validatedOptions.classes = {
|
|
360
|
-
...componentOptions.classes,
|
|
361
|
-
...options.classes ?? {}
|
|
362
|
-
};
|
|
363
|
-
}
|
|
364
|
-
break;
|
|
365
|
-
case 'debounce':
|
|
366
|
-
{
|
|
367
|
-
const debounceValue = Number(options.debounce);
|
|
368
|
-
if (!isNaN(debounceValue) && debounceValue >= 0) { // Allow 0 for debounce
|
|
369
|
-
validatedOptions.debounce = debounceValue;
|
|
370
|
-
}
|
|
371
|
-
break;
|
|
372
|
-
}
|
|
373
|
-
case 'clear_input':
|
|
374
|
-
validatedOptions.clear_input = Boolean(options.clear_input);
|
|
375
|
-
break;
|
|
376
|
-
default:
|
|
377
|
-
// Ignore any other keys that are not in the default options
|
|
378
|
-
break;
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
}
|
|
334
|
+
if (!options) {
|
|
335
|
+
return componentOptions;
|
|
382
336
|
}
|
|
383
|
-
|
|
337
|
+
// Perform a deep merge for the 'classes' object, inspired by the JS library
|
|
338
|
+
const mergedClasses = {
|
|
339
|
+
...componentOptions.classes,
|
|
340
|
+
...(options.classes ?? {})
|
|
341
|
+
};
|
|
342
|
+
const validated = {
|
|
343
|
+
...componentOptions, // Start with all defaults
|
|
344
|
+
...options, // Override with user-provided options
|
|
345
|
+
classes: mergedClasses // Apply the specifically merged classes
|
|
346
|
+
};
|
|
347
|
+
return validated;
|
|
384
348
|
};
|
|
385
349
|
/**
|
|
386
350
|
* Display distance in km or miles
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "places-autocomplete-svelte",
|
|
3
3
|
"license": "MIT",
|
|
4
|
-
"version": "2.2.
|
|
5
|
-
"description": "A flexible and
|
|
4
|
+
"version": "2.2.13",
|
|
5
|
+
"description": "A flexible and customisable Svelte component leveraging the Google Maps Places (New) Autocomplete API to provide a user-friendly way to search for and retrieve detailed address information within your SvelteKit applications.",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"svelte",
|
|
8
8
|
"sveltekit",
|
|
@@ -72,29 +72,29 @@
|
|
|
72
72
|
},
|
|
73
73
|
"devDependencies": {
|
|
74
74
|
"@sveltejs/adapter-auto": "^6.0.1",
|
|
75
|
-
"@sveltejs/adapter-cloudflare": "^7.0.
|
|
76
|
-
"@sveltejs/kit": "^2.
|
|
77
|
-
"@sveltejs/package": "^2.3.
|
|
75
|
+
"@sveltejs/adapter-cloudflare": "^7.0.4",
|
|
76
|
+
"@sveltejs/kit": "^2.22.2",
|
|
77
|
+
"@sveltejs/package": "^2.3.12",
|
|
78
78
|
"@sveltejs/vite-plugin-svelte": "^5.1.0",
|
|
79
|
-
"@tailwindcss/postcss": "^4.1.
|
|
79
|
+
"@tailwindcss/postcss": "^4.1.11",
|
|
80
80
|
"@tailwindcss/typography": "^0.5.16",
|
|
81
|
-
"@tailwindcss/vite": "^4.1.
|
|
81
|
+
"@tailwindcss/vite": "^4.1.11",
|
|
82
82
|
"@types/eslint": "^9.6.1",
|
|
83
83
|
"autoprefixer": "^10.4.21",
|
|
84
|
-
"eslint": "^9.
|
|
84
|
+
"eslint": "^9.30.1",
|
|
85
85
|
"eslint-config-prettier": "^10.1.5",
|
|
86
|
-
"eslint-plugin-svelte": "^3.
|
|
87
|
-
"globals": "^16.
|
|
88
|
-
"postcss": "^8.5.
|
|
89
|
-
"prettier": "^3.
|
|
86
|
+
"eslint-plugin-svelte": "^3.10.1",
|
|
87
|
+
"globals": "^16.3.0",
|
|
88
|
+
"postcss": "^8.5.6",
|
|
89
|
+
"prettier": "^3.6.2",
|
|
90
90
|
"prettier-plugin-svelte": "^3.4.0",
|
|
91
91
|
"publint": "^0.3.12",
|
|
92
|
-
"svelte": "^5.
|
|
93
|
-
"svelte-check": "^4.2.
|
|
94
|
-
"tailwindcss": "^4.1.
|
|
92
|
+
"svelte": "^5.35.2",
|
|
93
|
+
"svelte-check": "^4.2.2",
|
|
94
|
+
"tailwindcss": "^4.1.11",
|
|
95
95
|
"tslib": "^2.8.1",
|
|
96
96
|
"typescript": "^5.8.3",
|
|
97
|
-
"typescript-eslint": "^8.
|
|
97
|
+
"typescript-eslint": "^8.35.1",
|
|
98
98
|
"vite": "^6.3.5"
|
|
99
99
|
},
|
|
100
100
|
"svelte": "./dist/index.js",
|