places-autocomplete-svelte 2.2.11 → 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 CHANGED
@@ -3,7 +3,7 @@
3
3
  [![npm version](https://badge.fury.io/js/places-autocomplete-svelte.svg)](https://badge.fury.io/js/places-autocomplete-svelte)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
5
 
6
- A flexible and customizable [Svelte](https://kit.svelte.dev) component leveraging the [Google Maps Places (New) Autocomplete API](https://developers.google.com/maps/documentation/javascript/place-autocomplete-overview) to provide a user-friendly way to search for and retrieve detailed address information within your [SvelteKit](https://kit.svelte.dev) applications.
6
+ A flexible and customizable [Svelte](https://kit.svelte.dev) component leveraging the [Google Maps Places Autocomplete API (New)](https://developers.google.com/maps/documentation/javascript/place-autocomplete-overview) to provide a user-friendly way to search for and retrieve detailed address information within your [SvelteKit](https://kit.svelte.dev) applications.
7
7
 
8
8
  This component handles API loading, session tokens, fetching suggestions, and requesting place details, allowing you to focus on integrating the results into your application. Includes features like debounced input, highlighting of matched suggestions, extensive customization via CSS classes, and full TypeScript support.
9
9
 
@@ -16,10 +16,11 @@ Need this functionality for a non-Svelte project? Check out our companion vanill
16
16
 
17
17
  ## Features
18
18
 
19
- * Integrates with the modern **Google Places (New) Autocomplete API**.
19
+ * Integrates with the modern **"Google Maps Places Autocomplete API (New)**.
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.
@@ -36,6 +37,9 @@ See a live demo of the component in action: [Basic Example](https://places-autoc
36
37
 
37
38
  [Customise request parameters](https://places-autocomplete-demo.pages.dev/examples/customise-request-parameters) - construct a `requestParams` object and control various aspects of the search, including language, region, and more.
38
39
 
40
+ [Retain Input Value After Selection](https://places-autocomplete-demo.pages.dev/examples/retain-input-value) -
41
+ This example demonstrates how to configure the Places (New) Autocomplete Svelte component to keep the selected address visible in the input field after a suggestion is chosen. It utilises the `options.clear_input = false` setting.
42
+
39
43
 
40
44
  <img src="places-autocomplete-svelte.gif" alt="A video demonstrating the Places Autocomplete Svelte component in action, showing address suggestions and selection.">
41
45
 
@@ -48,7 +52,7 @@ See a live demo of the component in action: [Basic Example](https://places-autoc
48
52
 
49
53
  ## Requirements
50
54
 
51
- - **Google Maps API Key** with the Places API (New) enabled. Refer to [Use API Keys](https://developers.google.com/maps/documentation/javascript/get-api-key) for detailed instructions.
55
+ - **Google Maps API Key** with the Google Maps Places API (New) enabled. Refer to [Use API Keys](https://developers.google.com/maps/documentation/javascript/get-api-key) for detailed instructions.
52
56
 
53
57
  ## Installation
54
58
 
@@ -65,11 +69,12 @@ yarn add places-autocomplete-svelte
65
69
  1. Replace `'___YOUR_API_KEY___'` with your actual **Google Maps API Key**.
66
70
  2. Use the `onResponse` callback to **handle the response**.
67
71
 
68
- ```js
72
+ ```svelte
69
73
  <script>
70
74
  import { PlaceAutocomplete } from 'places-autocomplete-svelte';
71
75
  import type { PlaceResult, ComponentOptions, RequestParams } from 'places-autocomplete-svelte/interfaces'; // Adjust path if needed
72
76
 
77
+
73
78
  // Get API Key securely (e.g., from environment variables)
74
79
  const PUBLIC_GOOGLE_MAPS_API_KEY = import.meta.env.VITE_PUBLIC_GOOGLE_MAPS_API_KEY;
75
80
  let fullResponse: PlaceResult | null = $state(null);
@@ -112,7 +117,7 @@ const options: Partial<ComponentOptions> = $state({
112
117
  input: 'my-custom-input-class border-blue-500',
113
118
  highlight: 'bg-yellow-200 text-black', // Customize suggestion highlighting
114
119
  },
115
- clear_input: false, // Keep the input value after selecting a suggestion. The value of the input will be the value of `formattedAddress`
120
+ clear_input: false, // Overriding the default value to keep the input value after selecting a suggestion. The value of the input will be the value of `formattedAddress`
116
121
  });
117
122
 
118
123
  </script>
@@ -151,7 +156,7 @@ const options: Partial<ComponentOptions> = $state({
151
156
  }
152
157
  </style>
153
158
  ```
154
- ## Component Properties
159
+ ## Props
155
160
  | Prop | Type | Required | Default | Description |
156
161
  |----------------------------|---------------------------------|----------|-------------------------------------------|-----------------------------------------------------------------------------------------------------------------|
157
162
  | PUBLIC_GOOGLE_MAPS_API_KEY | string | Yes | - | Your Google Maps API Key with Places API enabled. |
@@ -161,7 +166,33 @@ const options: Partial<ComponentOptions> = $state({
161
166
  | onResponse | (response: PlaceResult) => void | Yes | - | Callback function triggered with the selected place details (PlaceResult object) after fetchFields is complete. |
162
167
  | onError | (error: string) => void | Yes | - | Callback function triggered when an error occurs (API loading, fetching suggestions, fetching details). |
163
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:**
164
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. |
165
196
 
166
197
  ### Options
167
198
 
@@ -175,7 +206,7 @@ const options: Partial<ComponentOptions> = $state({
175
206
  | label | string | '' | Optional label text displayed above the input field. |
176
207
  | autofocus | boolean | false | Automatically focus the input field on mount. |
177
208
  | autocomplete | string | 'off' | Standard HTML autocomplete attribute for the input field. |
178
- | classes | Partial<ComponentClasses> | {} | Object to override default CSS classes. See Styling section. | |
209
+ | classes | Partial<ComponentClasses> | {} | Object to override default CSS classes for various component parts. See Styling (options.classes) section for keys. | |
179
210
  | clear_input | Boolean | true | If `true` (default), clears the input field after a suggestion is selected. If `false`, the input field retains the `formattedAddress` of the selected place. |
180
211
 
181
212
 
@@ -188,6 +219,7 @@ You can customize the appearance of the component by providing your own CSS clas
188
219
 
189
220
 
190
221
  **Available Class Keys:**
222
+ The following keys can be used within the `options.classes object to target specific parts of the component:
191
223
 
192
224
  - `section`: The main container section.
193
225
  - `container`: The div containing the input and suggestions list.
@@ -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
- // set classes as state
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,27 +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
- resetKbdClasses();
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
- resetKbdClasses();
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
- // reset srarch input and results
299
+ kbdAction = 'escape';
300
+ request.input = '';
274
301
  reset();
275
302
  }
276
303
  // Optional: Scroll suggestion into view
277
304
  const selectedElement = document.getElementById(`option-${currentSuggestion + 1}`);
278
305
  selectedElement?.scrollIntoView({ block: 'nearest' });
306
+ // Reset the action state after a short delay to allow CSS transition to finish
279
307
  setTimeout(() => {
280
- resetKbdClasses();
281
- }, 300);
308
+ kbdAction = '';
309
+ }, 200);
282
310
  }
283
311
 
284
312
  // Handle click outside the input
@@ -290,7 +318,7 @@
290
318
  }
291
319
  </script>
292
320
 
293
- <svelte:window onkeydown={onKeyDown} onclick={handleClickOutside}/>
321
+ <svelte:window onkeydown={onKeyDown} onclick={handleClickOutside} />
294
322
 
295
323
  <section class={options.classes?.section}>
296
324
  {#if options?.label ?? ''}
@@ -325,11 +353,15 @@
325
353
 
326
354
  {#if results.length > 0}
327
355
  <div class={options.classes?.kbd_container ?? ''}>
328
- <kbd class={options.classes?.kbd_escape ?? ''}>Esc</kbd>
329
- <kbd class={cl.kbd_up}>&uArr;</kbd>
330
- <kbd class={cl.kbd_down}>&dArr;</kbd>
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'}>&uArr;</kbd
360
+ >
361
+ <kbd class={options.classes?.kbd_down ?? ''} class:kbd-active={kbdAction === 'down'}
362
+ >&dArr;</kbd
363
+ >
331
364
  </div>
332
-
333
365
  <ul class={options.classes?.ul ?? ''} id="options" role="listbox">
334
366
  {#each results as p, i}
335
367
  <li
@@ -388,3 +420,15 @@
388
420
  {/if}
389
421
  </div>
390
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, {}, "onResponse" | "onError">;
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
- const validatedOptions = { ...componentOptions };
335
- if (options && typeof options === 'object') {
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
- return validatedOptions;
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.11",
5
- "description": "A flexible and customizable 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.",
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",
@@ -15,8 +15,7 @@
15
15
  "address",
16
16
  "address-input",
17
17
  "input",
18
- "search",
19
- "new api"
18
+ "search"
20
19
  ],
21
20
  "homepage": "https://places-autocomplete-demo.pages.dev",
22
21
  "repository": {
@@ -69,33 +68,33 @@
69
68
  "provenance": true
70
69
  },
71
70
  "peerDependencies": {
72
- "svelte": "^5.1.4"
71
+ "svelte": "^5.0.0"
73
72
  },
74
73
  "devDependencies": {
75
74
  "@sveltejs/adapter-auto": "^6.0.1",
76
- "@sveltejs/adapter-cloudflare": "^7.0.3",
77
- "@sveltejs/kit": "^2.21.2",
78
- "@sveltejs/package": "^2.3.11",
75
+ "@sveltejs/adapter-cloudflare": "^7.0.4",
76
+ "@sveltejs/kit": "^2.22.2",
77
+ "@sveltejs/package": "^2.3.12",
79
78
  "@sveltejs/vite-plugin-svelte": "^5.1.0",
80
- "@tailwindcss/postcss": "^4.1.8",
79
+ "@tailwindcss/postcss": "^4.1.11",
81
80
  "@tailwindcss/typography": "^0.5.16",
82
- "@tailwindcss/vite": "^4.1.8",
81
+ "@tailwindcss/vite": "^4.1.11",
83
82
  "@types/eslint": "^9.6.1",
84
83
  "autoprefixer": "^10.4.21",
85
- "eslint": "^9.28.0",
84
+ "eslint": "^9.30.1",
86
85
  "eslint-config-prettier": "^10.1.5",
87
- "eslint-plugin-svelte": "^3.9.1",
88
- "globals": "^16.2.0",
89
- "postcss": "^8.5.4",
90
- "prettier": "^3.5.3",
86
+ "eslint-plugin-svelte": "^3.10.1",
87
+ "globals": "^16.3.0",
88
+ "postcss": "^8.5.6",
89
+ "prettier": "^3.6.2",
91
90
  "prettier-plugin-svelte": "^3.4.0",
92
91
  "publint": "^0.3.12",
93
- "svelte": "^5.33.14",
94
- "svelte-check": "^4.2.1",
95
- "tailwindcss": "^4.1.8",
92
+ "svelte": "^5.35.2",
93
+ "svelte-check": "^4.2.2",
94
+ "tailwindcss": "^4.1.11",
96
95
  "tslib": "^2.8.1",
97
96
  "typescript": "^5.8.3",
98
- "typescript-eslint": "^8.33.1",
97
+ "typescript-eslint": "^8.35.1",
99
98
  "vite": "^6.3.5"
100
99
  },
101
100
  "svelte": "./dist/index.js",